1 /*	$NetBSD: el.c,v 1.99 2019/07/23 10:18:52 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
38 static char sccsid[] = "@(#)el.c	8.2 (Berkeley) 1/3/94";
39 #else
40 __RCSID("$NetBSD: el.c,v 1.99 2019/07/23 10:18:52 christos Exp $");
41 #endif
42 #endif /* not lint && not SCCSID */
43 
44 #ifndef MAXPATHLEN
45 #define MAXPATHLEN 4096
46 #endif
47 
48 /*
49  * el.c: EditLine interface functions
50  */
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <ctype.h>
54 #include <langinfo.h>
55 #include <locale.h>
56 #include <stdarg.h>
57 #include <stdlib.h>
58 #include <string.h>
59 
60 #include "el.h"
61 #include "parse.h"
62 #include "read.h"
63 
64 #ifndef HAVE_SECURE_GETENV
65 #	ifdef HAVE___SECURE_GETENV
66 #		define secure_getenv __secure_getenv
67 #		define HAVE_SECURE_GETENV 1
68 #	else
69 #		ifdef HAVE_ISSETUGID
70 #			include <unistd.h>
71 #		else
72 #			undef issetugid
73 #			define issetugid() 1
74 #		endif
75 #	endif
76 #endif
77 
78 #ifndef HAVE_SECURE_GETENV
secure_getenv(char const * name)79 char *secure_getenv(char const *name)
80 {
81 	if (issetugid())
82 		return 0;
83 	return getenv(name);
84 }
85 #endif
86 
87 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID) && \
88     defined(HAVE_GETGID) && defined(HAVE_GETEGID)
89 #define HAVE_IDENTITY_FUNCS 1
90 
91 #ifndef HAVE_ISSETUGID
issetugid(void)92 static int issetugid(void) {
93   return (getuid() != geteuid() || getgid() != getegid());
94 }
95 #endif
96 
97 #endif
98 
99 /* el_init():
100  *	Initialize editline and set default parameters.
101  */
102 EditLine *
el_init(const char * prog,FILE * fin,FILE * fout,FILE * ferr)103 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
104 {
105     return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout),
106 	fileno(ferr));
107 }
108 
109 libedit_private EditLine *
el_init_internal(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr,int flags)110 el_init_internal(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
111     int fdin, int fdout, int fderr, int flags)
112 {
113 	EditLine *el = el_calloc(1, sizeof(*el));
114 
115 	if (el == NULL)
116 		return NULL;
117 
118 	el->el_infile = fin;
119 	el->el_outfile = fout;
120 	el->el_errfile = ferr;
121 
122 	el->el_infd = fdin;
123 	el->el_outfd = fdout;
124 	el->el_errfd = fderr;
125 
126 	el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch));
127 	if (el->el_prog == NULL) {
128 		el_free(el);
129 		return NULL;
130 	}
131 
132 	/*
133          * Initialize all the modules. Order is important!!!
134          */
135 	el->el_flags = flags;
136 
137 	if (terminal_init(el) == -1) {
138 		el_free(el->el_prog);
139 		el_free(el);
140 		return NULL;
141 	}
142 	(void) keymacro_init(el);
143 	(void) map_init(el);
144 	if (tty_init(el) == -1)
145 		el->el_flags |= NO_TTY;
146 	(void) ch_init(el);
147 	(void) search_init(el);
148 	(void) hist_init(el);
149 	(void) prompt_init(el);
150 	(void) sig_init(el);
151 	(void) literal_init(el);
152 	if (read_init(el) == -1) {
153 		el_end(el);
154 		return NULL;
155 	}
156 	return el;
157 }
158 
159 EditLine *
el_init_fd(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr)160 el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
161     int fdin, int fdout, int fderr)
162 {
163 	return el_init_internal(prog, fin, fout, ferr, fdin, fdout, fderr, 0);
164 }
165 
166 /* el_end():
167  *	Clean up.
168  */
169 void
el_end(EditLine * el)170 el_end(EditLine *el)
171 {
172 
173 	if (el == NULL)
174 		return;
175 
176 	el_reset(el);
177 
178 	terminal_end(el);
179 	keymacro_end(el);
180 	map_end(el);
181 	if (!(el->el_flags & NO_TTY))
182 		tty_end(el, TCSAFLUSH);
183 	ch_end(el);
184 	read_end(el->el_read);
185 	search_end(el);
186 	hist_end(el);
187 	prompt_end(el);
188 	sig_end(el);
189 	literal_end(el);
190 
191 	el_free(el->el_prog);
192 	el_free(el->el_visual.cbuff);
193 	el_free(el->el_visual.wbuff);
194 	el_free(el->el_scratch.cbuff);
195 	el_free(el->el_scratch.wbuff);
196 	el_free(el->el_lgcyconv.cbuff);
197 	el_free(el->el_lgcyconv.wbuff);
198 	el_free(el);
199 }
200 
201 
202 /* el_reset():
203  *	Reset the tty and the parser
204  */
205 void
el_reset(EditLine * el)206 el_reset(EditLine *el)
207 {
208 
209 	tty_cookedmode(el);
210 	ch_reset(el);		/* XXX: Do we want that? */
211 }
212 
213 
214 /* el_set():
215  *	set the editline parameters
216  */
217 int
el_wset(EditLine * el,int op,...)218 el_wset(EditLine *el, int op, ...)
219 {
220 	va_list ap;
221 	int rv = 0;
222 
223 	if (el == NULL)
224 		return -1;
225 	va_start(ap, op);
226 
227 	switch (op) {
228 	case EL_PROMPT:
229 	case EL_RPROMPT: {
230 		el_pfunc_t p = va_arg(ap, el_pfunc_t);
231 
232 		rv = prompt_set(el, p, 0, op, 1);
233 		break;
234 	}
235 
236 	case EL_RESIZE: {
237 		el_zfunc_t p = va_arg(ap, el_zfunc_t);
238 		void *arg = va_arg(ap, void *);
239 		rv = ch_resizefun(el, p, arg);
240 		break;
241 	}
242 
243 	case EL_ALIAS_TEXT: {
244 		el_afunc_t p = va_arg(ap, el_afunc_t);
245 		void *arg = va_arg(ap, void *);
246 		rv = ch_aliasfun(el, p, arg);
247 		break;
248 	}
249 
250 	case EL_PROMPT_ESC:
251 	case EL_RPROMPT_ESC: {
252 		el_pfunc_t p = va_arg(ap, el_pfunc_t);
253 		int c = va_arg(ap, int);
254 
255 		rv = prompt_set(el, p, (wchar_t)c, op, 1);
256 		break;
257 	}
258 
259 	case EL_TERMINAL:
260 		rv = terminal_set(el, va_arg(ap, char *));
261 		break;
262 
263 	case EL_EDITOR:
264 		rv = map_set_editor(el, va_arg(ap, wchar_t *));
265 		break;
266 
267 	case EL_SIGNAL:
268 		if (va_arg(ap, int))
269 			el->el_flags |= HANDLE_SIGNALS;
270 		else
271 			el->el_flags &= ~HANDLE_SIGNALS;
272 		break;
273 
274 	case EL_BIND:
275 	case EL_TELLTC:
276 	case EL_SETTC:
277 	case EL_ECHOTC:
278 	case EL_SETTY:
279 	{
280 		const wchar_t *argv[20];
281 		int i;
282 
283 		for (i = 1; i < (int)__arraycount(argv); i++)
284 			if ((argv[i] = va_arg(ap, wchar_t *)) == NULL)
285 				break;
286 
287 		switch (op) {
288 		case EL_BIND:
289 			argv[0] = L"bind";
290 			rv = map_bind(el, i, argv);
291 			break;
292 
293 		case EL_TELLTC:
294 			argv[0] = L"telltc";
295 			rv = terminal_telltc(el, i, argv);
296 			break;
297 
298 		case EL_SETTC:
299 			argv[0] = L"settc";
300 			rv = terminal_settc(el, i, argv);
301 			break;
302 
303 		case EL_ECHOTC:
304 			argv[0] = L"echotc";
305 			rv = terminal_echotc(el, i, argv);
306 			break;
307 
308 		case EL_SETTY:
309 			argv[0] = L"setty";
310 			rv = tty_stty(el, i, argv);
311 			break;
312 
313 		default:
314 			rv = -1;
315 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
316 			break;
317 		}
318 		break;
319 	}
320 
321 	case EL_ADDFN:
322 	{
323 		wchar_t *name = va_arg(ap, wchar_t *);
324 		wchar_t *help = va_arg(ap, wchar_t *);
325 		el_func_t func = va_arg(ap, el_func_t);
326 
327 		rv = map_addfunc(el, name, help, func);
328 		break;
329 	}
330 
331 	case EL_HIST:
332 	{
333 		hist_fun_t func = va_arg(ap, hist_fun_t);
334 		void *ptr = va_arg(ap, void *);
335 
336 		rv = hist_set(el, func, ptr);
337 		if (MB_CUR_MAX == 1)
338 			el->el_flags &= ~NARROW_HISTORY;
339 		break;
340 	}
341 
342 	case EL_EDITMODE:
343 		if (va_arg(ap, int))
344 			el->el_flags &= ~EDIT_DISABLED;
345 		else
346 			el->el_flags |= EDIT_DISABLED;
347 		rv = 0;
348 		break;
349 
350 	case EL_GETCFN:
351 	{
352 		el_rfunc_t rc = va_arg(ap, el_rfunc_t);
353 		rv = el_read_setfn(el->el_read, rc);
354 		break;
355 	}
356 
357 	case EL_CLIENTDATA:
358 		el->el_data = va_arg(ap, void *);
359 		break;
360 
361 	case EL_UNBUFFERED:
362 		rv = va_arg(ap, int);
363 		if (rv && !(el->el_flags & UNBUFFERED)) {
364 			el->el_flags |= UNBUFFERED;
365 			read_prepare(el);
366 		} else if (!rv && (el->el_flags & UNBUFFERED)) {
367 			el->el_flags &= ~UNBUFFERED;
368 			read_finish(el);
369 		}
370 		rv = 0;
371 		break;
372 
373 	case EL_PREP_TERM:
374 		rv = va_arg(ap, int);
375 		if (rv)
376 			(void) tty_rawmode(el);
377 		else
378 			(void) tty_cookedmode(el);
379 		rv = 0;
380 		break;
381 
382 	case EL_SETFP:
383 	{
384 		FILE *fp;
385 		int what;
386 
387 		what = va_arg(ap, int);
388 		fp = va_arg(ap, FILE *);
389 
390 		rv = 0;
391 		switch (what) {
392 		case 0:
393 			el->el_infile = fp;
394 			el->el_infd = fileno(fp);
395 			break;
396 		case 1:
397 			el->el_outfile = fp;
398 			el->el_outfd = fileno(fp);
399 			break;
400 		case 2:
401 			el->el_errfile = fp;
402 			el->el_errfd = fileno(fp);
403 			break;
404 		default:
405 			rv = -1;
406 			break;
407 		}
408 		break;
409 	}
410 
411 	case EL_REFRESH:
412 		re_clear_display(el);
413 		re_refresh(el);
414 		terminal__flush(el);
415 		break;
416 
417 	default:
418 		rv = -1;
419 		break;
420 	}
421 
422 	va_end(ap);
423 	return rv;
424 }
425 
426 
427 /* el_get():
428  *	retrieve the editline parameters
429  */
430 int
el_wget(EditLine * el,int op,...)431 el_wget(EditLine *el, int op, ...)
432 {
433 	va_list ap;
434 	int rv;
435 
436 	if (el == NULL)
437 		return -1;
438 
439 	va_start(ap, op);
440 
441 	switch (op) {
442 	case EL_PROMPT:
443 	case EL_RPROMPT: {
444 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
445 		rv = prompt_get(el, p, 0, op);
446 		break;
447 	}
448 	case EL_PROMPT_ESC:
449 	case EL_RPROMPT_ESC: {
450 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
451 		wchar_t *c = va_arg(ap, wchar_t *);
452 
453 		rv = prompt_get(el, p, c, op);
454 		break;
455 	}
456 
457 	case EL_EDITOR:
458 		rv = map_get_editor(el, va_arg(ap, const wchar_t **));
459 		break;
460 
461 	case EL_SIGNAL:
462 		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
463 		rv = 0;
464 		break;
465 
466 	case EL_EDITMODE:
467 		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
468 		rv = 0;
469 		break;
470 
471 	case EL_TERMINAL:
472 		terminal_get(el, va_arg(ap, const char **));
473 		rv = 0;
474 		break;
475 
476 	case EL_GETTC:
477 	{
478 		static char name[] = "gettc";
479 		char *argv[3];
480 		argv[0] = name;
481 		argv[1] = va_arg(ap, char *);
482 		argv[2] = va_arg(ap, void *);
483 		rv = terminal_gettc(el, 3, argv);
484 		break;
485 	}
486 
487 	case EL_GETCFN:
488 		*va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read);
489 		rv = 0;
490 		break;
491 
492 	case EL_CLIENTDATA:
493 		*va_arg(ap, void **) = el->el_data;
494 		rv = 0;
495 		break;
496 
497 	case EL_UNBUFFERED:
498 		*va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0;
499 		rv = 0;
500 		break;
501 
502 	case EL_GETFP:
503 	{
504 		int what;
505 		FILE **fpp;
506 
507 		what = va_arg(ap, int);
508 		fpp = va_arg(ap, FILE **);
509 		rv = 0;
510 		switch (what) {
511 		case 0:
512 			*fpp = el->el_infile;
513 			break;
514 		case 1:
515 			*fpp = el->el_outfile;
516 			break;
517 		case 2:
518 			*fpp = el->el_errfile;
519 			break;
520 		default:
521 			rv = -1;
522 			break;
523 		}
524 		break;
525 	}
526 	default:
527 		rv = -1;
528 		break;
529 	}
530 	va_end(ap);
531 
532 	return rv;
533 }
534 
535 
536 /* el_line():
537  *	Return editing info
538  */
539 const LineInfoW *
el_wline(EditLine * el)540 el_wline(EditLine *el)
541 {
542 
543 	return (const LineInfoW *)(void *)&el->el_line;
544 }
545 
546 
547 /* el_source():
548  *	Source a file
549  */
550 int
el_source(EditLine * el,const char * fname)551 el_source(EditLine *el, const char *fname)
552 {
553 	FILE *fp;
554 	size_t len;
555 	ssize_t slen;
556 	char *ptr;
557 	char *path = NULL;
558 	const wchar_t *dptr;
559 	int error = 0;
560 
561 	fp = NULL;
562 	if (fname == NULL) {
563 #if defined(HAVE_ISSETUGID) || defined(HAVE_IDENTITY_FUNCS)
564 		if (issetugid())
565 			return -1;
566 
567 		if ((fname = getenv("EDITRC")) == NULL) {
568 			static const char elpath[] = "/.editrc";
569 			size_t plen = sizeof(elpath);
570 
571 			if ((ptr = getenv("HOME")) == NULL)
572 				return -1;
573 			plen += strlen(ptr);
574 			if ((path = el_calloc(plen, sizeof(*path))) == NULL)
575 				return -1;
576 			(void)snprintf(path, plen, "%s%s", ptr,
577 				elpath + (*ptr == '\0'));
578 			fname = path;
579 		}
580 #else
581 		/*
582 		 * If issetugid() is missing, always return an error, in order
583 		 * to keep from inadvertently opening up the user to a security
584 		 * hole.
585 		 */
586 		return -1;
587 #endif
588 	}
589 	if (fname[0] == '\0')
590 		return -1;
591 
592 	if (fp == NULL)
593 		fp = fopen(fname, "r");
594 	if (fp == NULL) {
595 		el_free(path);
596 		return -1;
597 	}
598 
599 	ptr = NULL;
600 	len = 0;
601 	while ((slen = getline(&ptr, &len, fp)) != -1) {
602 		if (*ptr == '\n')
603 			continue;	/* Empty line. */
604 		if (slen > 0 && ptr[--slen] == '\n')
605 			ptr[slen] = '\0';
606 
607 		dptr = ct_decode_string(ptr, &el->el_scratch);
608 		if (!dptr)
609 			continue;
610 		/* loop until first non-space char or EOL */
611 		while (*dptr != '\0' && iswspace(*dptr))
612 			dptr++;
613 		if (*dptr == '#')
614 			continue;   /* ignore, this is a comment line */
615 		if ((error = parse_line(el, dptr)) == -1)
616 			break;
617 	}
618 	free(ptr);
619 
620 	el_free(path);
621 	(void) fclose(fp);
622 	return error;
623 }
624 
625 
626 /* el_resize():
627  *	Called from program when terminal is resized
628  */
629 void
el_resize(EditLine * el)630 el_resize(EditLine *el)
631 {
632 	int lins, cols;
633 	sigset_t oset, nset;
634 
635 	(void) sigemptyset(&nset);
636 	(void) sigaddset(&nset, SIGWINCH);
637 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
638 
639 	/* get the correct window size */
640 	if (terminal_get_size(el, &lins, &cols))
641 		terminal_change_size(el, lins, cols);
642 
643 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
644 }
645 
646 
647 /* el_beep():
648  *	Called from the program to beep
649  */
650 void
el_beep(EditLine * el)651 el_beep(EditLine *el)
652 {
653 
654 	terminal_beep(el);
655 }
656 
657 
658 /* el_editmode()
659  *	Set the state of EDIT_DISABLED from the `edit' command.
660  */
661 libedit_private int
662 /*ARGSUSED*/
el_editmode(EditLine * el,int argc,const wchar_t ** argv)663 el_editmode(EditLine *el, int argc, const wchar_t **argv)
664 {
665 	const wchar_t *how;
666 
667 	if (argv == NULL || argc != 2 || argv[1] == NULL)
668 		return -1;
669 
670 	how = argv[1];
671 	if (wcscmp(how, L"on") == 0) {
672 		el->el_flags &= ~EDIT_DISABLED;
673 		tty_rawmode(el);
674 	} else if (wcscmp(how, L"off") == 0) {
675 		tty_cookedmode(el);
676 		el->el_flags |= EDIT_DISABLED;
677 	}
678 	else {
679 		(void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n",
680 		    how);
681 		return -1;
682 	}
683 	return 0;
684 }
685