el.c revision 283084
1/*	$NetBSD: el.c,v 1.73 2014/06/18 18:12:28 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "config.h"
36#if !defined(lint) && !defined(SCCSID)
37#if 0
38static char sccsid[] = "@(#)el.c	8.2 (Berkeley) 1/3/94";
39#else
40__RCSID("$NetBSD: el.c,v 1.73 2014/06/18 18:12:28 christos Exp $");
41#endif
42#endif /* not lint && not SCCSID */
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD: head/lib/libedit/el.c 283084 2015-05-18 22:03:05Z bapt $");
45
46/*
47 * el.c: EditLine interface functions
48 */
49#include <sys/types.h>
50#include <sys/param.h>
51#include <string.h>
52#include <stdlib.h>
53#include <stdarg.h>
54#include <ctype.h>
55#include <locale.h>
56#include <langinfo.h>
57#include "el.h"
58
59/* el_init():
60 *	Initialize editline and set default parameters.
61 */
62public EditLine *
63el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
64{
65    return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout),
66	fileno(ferr));
67}
68
69public EditLine *
70el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
71    int fdin, int fdout, int fderr)
72{
73	EditLine *el = el_malloc(sizeof(*el));
74
75	if (el == NULL)
76		return NULL;
77
78	memset(el, 0, sizeof(EditLine));
79
80	el->el_infile = fin;
81	el->el_outfile = fout;
82	el->el_errfile = ferr;
83
84	el->el_infd = fdin;
85	el->el_outfd = fdout;
86	el->el_errfd = fderr;
87
88	el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch));
89	if (el->el_prog == NULL) {
90		el_free(el);
91		return NULL;
92	}
93
94	/*
95         * Initialize all the modules. Order is important!!!
96         */
97	el->el_flags = 0;
98#ifdef WIDECHAR
99	if (setlocale(LC_CTYPE, NULL) != NULL){
100		if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
101			el->el_flags |= CHARSET_IS_UTF8;
102	}
103#endif
104
105	if (terminal_init(el) == -1) {
106		el_free(el->el_prog);
107		el_free(el);
108		return NULL;
109	}
110	(void) keymacro_init(el);
111	(void) map_init(el);
112	if (tty_init(el) == -1)
113		el->el_flags |= NO_TTY;
114	(void) ch_init(el);
115	(void) search_init(el);
116	(void) hist_init(el);
117	(void) prompt_init(el);
118	(void) sig_init(el);
119	(void) read_init(el);
120
121	return el;
122}
123
124
125/* el_end():
126 *	Clean up.
127 */
128public void
129el_end(EditLine *el)
130{
131
132	if (el == NULL)
133		return;
134
135	el_reset(el);
136
137	terminal_end(el);
138	keymacro_end(el);
139	map_end(el);
140	tty_end(el);
141	ch_end(el);
142	search_end(el);
143	hist_end(el);
144	prompt_end(el);
145	sig_end(el);
146
147	el_free(el->el_prog);
148#ifdef WIDECHAR
149	el_free(el->el_scratch.cbuff);
150	el_free(el->el_scratch.wbuff);
151	el_free(el->el_lgcyconv.cbuff);
152	el_free(el->el_lgcyconv.wbuff);
153#endif
154	el_free(el);
155}
156
157
158/* el_reset():
159 *	Reset the tty and the parser
160 */
161public void
162el_reset(EditLine *el)
163{
164
165	tty_cookedmode(el);
166	ch_reset(el, 0);		/* XXX: Do we want that? */
167}
168
169
170/* el_set():
171 *	set the editline parameters
172 */
173public int
174FUN(el,set)(EditLine *el, int op, ...)
175{
176	va_list ap;
177	int rv = 0;
178
179	if (el == NULL)
180		return -1;
181	va_start(ap, op);
182
183	switch (op) {
184	case EL_PROMPT:
185	case EL_RPROMPT: {
186		el_pfunc_t p = va_arg(ap, el_pfunc_t);
187
188		rv = prompt_set(el, p, 0, op, 1);
189		break;
190	}
191
192	case EL_RESIZE: {
193		el_zfunc_t p = va_arg(ap, el_zfunc_t);
194		void *arg = va_arg(ap, void *);
195		rv = ch_resizefun(el, p, arg);
196		break;
197	}
198
199	case EL_ALIAS_TEXT: {
200		el_afunc_t p = va_arg(ap, el_afunc_t);
201		void *arg = va_arg(ap, void *);
202		rv = ch_aliasfun(el, p, arg);
203		break;
204	}
205
206	case EL_PROMPT_ESC:
207	case EL_RPROMPT_ESC: {
208		el_pfunc_t p = va_arg(ap, el_pfunc_t);
209		int c = va_arg(ap, int);
210
211		rv = prompt_set(el, p, c, op, 1);
212		break;
213	}
214
215	case EL_TERMINAL:
216		rv = terminal_set(el, va_arg(ap, char *));
217		break;
218
219	case EL_EDITOR:
220		rv = map_set_editor(el, va_arg(ap, Char *));
221		break;
222
223	case EL_SIGNAL:
224		if (va_arg(ap, int))
225			el->el_flags |= HANDLE_SIGNALS;
226		else
227			el->el_flags &= ~HANDLE_SIGNALS;
228		break;
229
230	case EL_BIND:
231	case EL_TELLTC:
232	case EL_SETTC:
233	case EL_ECHOTC:
234	case EL_SETTY:
235	{
236		const Char *argv[20];
237		int i;
238
239		for (i = 1; i < (int)__arraycount(argv); i++)
240			if ((argv[i] = va_arg(ap, Char *)) == NULL)
241				break;
242
243		switch (op) {
244		case EL_BIND:
245			argv[0] = STR("bind");
246			rv = map_bind(el, i, argv);
247			break;
248
249		case EL_TELLTC:
250			argv[0] = STR("telltc");
251			rv = terminal_telltc(el, i, argv);
252			break;
253
254		case EL_SETTC:
255			argv[0] = STR("settc");
256			rv = terminal_settc(el, i, argv);
257			break;
258
259		case EL_ECHOTC:
260			argv[0] = STR("echotc");
261			rv = terminal_echotc(el, i, argv);
262			break;
263
264		case EL_SETTY:
265			argv[0] = STR("setty");
266			rv = tty_stty(el, i, argv);
267			break;
268
269		default:
270			rv = -1;
271			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
272			break;
273		}
274		break;
275	}
276
277	case EL_ADDFN:
278	{
279		Char *name = va_arg(ap, Char *);
280		Char *help = va_arg(ap, Char *);
281		el_func_t func = va_arg(ap, el_func_t);
282
283		rv = map_addfunc(el, name, help, func);
284		break;
285	}
286
287	case EL_HIST:
288	{
289		hist_fun_t func = va_arg(ap, hist_fun_t);
290		void *ptr = va_arg(ap, void *);
291
292		rv = hist_set(el, func, ptr);
293		if (!(el->el_flags & CHARSET_IS_UTF8))
294			el->el_flags &= ~NARROW_HISTORY;
295		break;
296	}
297
298	case EL_EDITMODE:
299		if (va_arg(ap, int))
300			el->el_flags &= ~EDIT_DISABLED;
301		else
302			el->el_flags |= EDIT_DISABLED;
303		rv = 0;
304		break;
305
306	case EL_GETCFN:
307	{
308		el_rfunc_t rc = va_arg(ap, el_rfunc_t);
309		rv = el_read_setfn(el, rc);
310		el->el_flags &= ~NARROW_READ;
311		break;
312	}
313
314	case EL_CLIENTDATA:
315		el->el_data = va_arg(ap, void *);
316		break;
317
318	case EL_UNBUFFERED:
319		rv = va_arg(ap, int);
320		if (rv && !(el->el_flags & UNBUFFERED)) {
321			el->el_flags |= UNBUFFERED;
322			read_prepare(el);
323		} else if (!rv && (el->el_flags & UNBUFFERED)) {
324			el->el_flags &= ~UNBUFFERED;
325			read_finish(el);
326		}
327		rv = 0;
328		break;
329
330	case EL_PREP_TERM:
331		rv = va_arg(ap, int);
332		if (rv)
333			(void) tty_rawmode(el);
334		else
335			(void) tty_cookedmode(el);
336		rv = 0;
337		break;
338
339	case EL_SETFP:
340	{
341		FILE *fp;
342		int what;
343
344		what = va_arg(ap, int);
345		fp = va_arg(ap, FILE *);
346
347		rv = 0;
348		switch (what) {
349		case 0:
350			el->el_infile = fp;
351			el->el_infd = fileno(fp);
352			break;
353		case 1:
354			el->el_outfile = fp;
355			el->el_outfd = fileno(fp);
356			break;
357		case 2:
358			el->el_errfile = fp;
359			el->el_errfd = fileno(fp);
360			break;
361		default:
362			rv = -1;
363			break;
364		}
365		break;
366	}
367
368	case EL_REFRESH:
369		re_clear_display(el);
370		re_refresh(el);
371		terminal__flush(el);
372		break;
373
374	default:
375		rv = -1;
376		break;
377	}
378
379	va_end(ap);
380	return rv;
381}
382
383
384/* el_get():
385 *	retrieve the editline parameters
386 */
387public int
388FUN(el,get)(EditLine *el, int op, ...)
389{
390	va_list ap;
391	int rv;
392
393	if (el == NULL)
394		return -1;
395
396	va_start(ap, op);
397
398	switch (op) {
399	case EL_PROMPT:
400	case EL_RPROMPT: {
401		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
402		rv = prompt_get(el, p, 0, op);
403		break;
404	}
405	case EL_PROMPT_ESC:
406	case EL_RPROMPT_ESC: {
407		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
408		Char *c = va_arg(ap, Char *);
409
410		rv = prompt_get(el, p, c, op);
411		break;
412	}
413
414	case EL_EDITOR:
415		rv = map_get_editor(el, va_arg(ap, const Char **));
416		break;
417
418	case EL_SIGNAL:
419		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
420		rv = 0;
421		break;
422
423	case EL_EDITMODE:
424		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
425		rv = 0;
426		break;
427
428	case EL_TERMINAL:
429		terminal_get(el, va_arg(ap, const char **));
430		rv = 0;
431		break;
432
433	case EL_GETTC:
434	{
435		static char name[] = "gettc";
436		char *argv[20];
437		int i;
438
439 		for (i = 1; i < (int)__arraycount(argv); i++)
440			if ((argv[i] = va_arg(ap, char *)) == NULL)
441				break;
442
443		argv[0] = name;
444		rv = terminal_gettc(el, i, argv);
445		break;
446	}
447
448	case EL_GETCFN:
449		*va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
450		rv = 0;
451		break;
452
453	case EL_CLIENTDATA:
454		*va_arg(ap, void **) = el->el_data;
455		rv = 0;
456		break;
457
458	case EL_UNBUFFERED:
459		*va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0;
460		rv = 0;
461		break;
462
463	case EL_GETFP:
464	{
465		int what;
466		FILE **fpp;
467
468		what = va_arg(ap, int);
469		fpp = va_arg(ap, FILE **);
470		rv = 0;
471		switch (what) {
472		case 0:
473			*fpp = el->el_infile;
474			break;
475		case 1:
476			*fpp = el->el_outfile;
477			break;
478		case 2:
479			*fpp = el->el_errfile;
480			break;
481		default:
482			rv = -1;
483			break;
484		}
485		break;
486	}
487	default:
488		rv = -1;
489		break;
490	}
491	va_end(ap);
492
493	return rv;
494}
495
496
497/* el_line():
498 *	Return editing info
499 */
500public const TYPE(LineInfo) *
501FUN(el,line)(EditLine *el)
502{
503
504	return (const TYPE(LineInfo) *)(void *)&el->el_line;
505}
506
507
508/* el_source():
509 *	Source a file
510 */
511public int
512el_source(EditLine *el, const char *fname)
513{
514	FILE *fp;
515	size_t len;
516	char *ptr;
517	char *path = NULL;
518	const Char *dptr;
519	int error = 0;
520
521	fp = NULL;
522	if (fname == NULL) {
523#ifdef HAVE_ISSETUGID
524		static const char elpath[] = "/.editrc";
525		size_t plen = sizeof(elpath);
526
527		if (issetugid())
528			return -1;
529		if ((ptr = getenv("HOME")) == NULL)
530			return -1;
531		plen += strlen(ptr);
532		if ((path = el_malloc(plen * sizeof(*path))) == NULL)
533			return -1;
534		(void)snprintf(path, plen, "%s%s", ptr, elpath);
535		fname = path;
536#else
537		/*
538		 * If issetugid() is missing, always return an error, in order
539		 * to keep from inadvertently opening up the user to a security
540		 * hole.
541		 */
542		return -1;
543#endif
544	}
545	if (fp == NULL)
546		fp = fopen(fname, "r");
547	if (fp == NULL) {
548		el_free(path);
549		return -1;
550	}
551
552	while ((ptr = fgetln(fp, &len)) != NULL) {
553		if (*ptr == '\n')
554			continue;	/* Empty line. */
555		dptr = ct_decode_string(ptr, &el->el_scratch);
556		if (!dptr)
557			continue;
558		if (len > 0 && dptr[len - 1] == '\n')
559			--len;
560
561		/* loop until first non-space char or EOL */
562		while (*dptr != '\0' && Isspace(*dptr))
563			dptr++;
564		if (*dptr == '#')
565			continue;   /* ignore, this is a comment line */
566		if ((error = parse_line(el, dptr)) == -1)
567			break;
568	}
569
570	el_free(path);
571	(void) fclose(fp);
572	return error;
573}
574
575
576/* el_resize():
577 *	Called from program when terminal is resized
578 */
579public void
580el_resize(EditLine *el)
581{
582	int lins, cols;
583	sigset_t oset, nset;
584
585	(void) sigemptyset(&nset);
586	(void) sigaddset(&nset, SIGWINCH);
587	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
588
589	/* get the correct window size */
590	if (terminal_get_size(el, &lins, &cols))
591		terminal_change_size(el, lins, cols);
592
593	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
594}
595
596
597/* el_beep():
598 *	Called from the program to beep
599 */
600public void
601el_beep(EditLine *el)
602{
603
604	terminal_beep(el);
605}
606
607
608/* el_editmode()
609 *	Set the state of EDIT_DISABLED from the `edit' command.
610 */
611protected int
612/*ARGSUSED*/
613el_editmode(EditLine *el, int argc, const Char **argv)
614{
615	const Char *how;
616
617	if (argv == NULL || argc != 2 || argv[1] == NULL)
618		return -1;
619
620	how = argv[1];
621	if (Strcmp(how, STR("on")) == 0) {
622		el->el_flags &= ~EDIT_DISABLED;
623		tty_rawmode(el);
624	} else if (Strcmp(how, STR("off")) == 0) {
625		tty_cookedmode(el);
626		el->el_flags |= EDIT_DISABLED;
627	}
628	else {
629		(void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n",
630		    how);
631		return -1;
632	}
633	return 0;
634}
635