• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$NetBSD: el.c,v 1.102 2025/01/03 00:40:08 rillig 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.102 2025/01/03 00:40:08 rillig 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 /* el_init():
88  *	Initialize editline and set default parameters.
89  */
90 EditLine *
el_init(const char * prog,FILE * fin,FILE * fout,FILE * ferr)91 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
92 {
93     return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout),
94 	fileno(ferr));
95 }
96 
97 libedit_private EditLine *
el_init_internal(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr,int flags)98 el_init_internal(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
99     int fdin, int fdout, int fderr, int flags)
100 {
101 	EditLine *el = el_calloc(1, sizeof(*el));
102 
103 	if (el == NULL)
104 		return NULL;
105 
106 	el->el_infile = fin;
107 	el->el_outfile = fout;
108 	el->el_errfile = ferr;
109 
110 	el->el_infd = fdin;
111 	el->el_outfd = fdout;
112 	el->el_errfd = fderr;
113 
114 	el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch));
115 	if (el->el_prog == NULL) {
116 		el_free(el);
117 		return NULL;
118 	}
119 
120 	/*
121          * Initialize all the modules. Order is important!!!
122          */
123 	el->el_flags = flags;
124 
125 	if (terminal_init(el) == -1) {
126 		el_free(el->el_prog);
127 		el_free(el);
128 		return NULL;
129 	}
130 	(void) keymacro_init(el);
131 	(void) map_init(el);
132 	if (tty_init(el) == -1)
133 		el->el_flags |= NO_TTY;
134 	(void) ch_init(el);
135 	(void) search_init(el);
136 	(void) hist_init(el);
137 	(void) prompt_init(el);
138 	(void) sig_init(el);
139 	(void) literal_init(el);
140 	if (read_init(el) == -1) {
141 		el_end(el);
142 		return NULL;
143 	}
144 	return el;
145 }
146 
147 EditLine *
el_init_fd(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr)148 el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
149     int fdin, int fdout, int fderr)
150 {
151 	return el_init_internal(prog, fin, fout, ferr, fdin, fdout, fderr, 0);
152 }
153 
154 /* el_end():
155  *	Clean up.
156  */
157 void
el_end(EditLine * el)158 el_end(EditLine *el)
159 {
160 
161 	if (el == NULL)
162 		return;
163 
164 	el_reset(el);
165 
166 	terminal_end(el);
167 	keymacro_end(el);
168 	map_end(el);
169 	if (!(el->el_flags & NO_TTY))
170 		tty_end(el, TCSAFLUSH);
171 	ch_end(el);
172 	read_end(el);
173 	search_end(el);
174 	hist_end(el);
175 	prompt_end(el);
176 	sig_end(el);
177 	literal_end(el);
178 
179 	el_free(el->el_prog);
180 	el_free(el->el_visual.cbuff);
181 	el_free(el->el_visual.wbuff);
182 	el_free(el->el_scratch.cbuff);
183 	el_free(el->el_scratch.wbuff);
184 	el_free(el->el_lgcyconv.cbuff);
185 	el_free(el->el_lgcyconv.wbuff);
186 	el_free(el);
187 }
188 
189 
190 /* el_reset():
191  *	Reset the tty and the parser
192  */
193 void
el_reset(EditLine * el)194 el_reset(EditLine *el)
195 {
196 
197 	tty_cookedmode(el);
198 	ch_reset(el);		/* XXX: Do we want that? */
199 }
200 
201 
202 /* el_set():
203  *	set the editline parameters
204  */
205 int
el_wset(EditLine * el,int op,...)206 el_wset(EditLine *el, int op, ...)
207 {
208 	va_list ap;
209 	int rv = 0;
210 
211 	if (el == NULL)
212 		return -1;
213 	va_start(ap, op);
214 
215 	switch (op) {
216 	case EL_PROMPT:
217 	case EL_RPROMPT: {
218 		el_pfunc_t p = va_arg(ap, el_pfunc_t);
219 
220 		rv = prompt_set(el, p, 0, op, 1);
221 		break;
222 	}
223 
224 	case EL_RESIZE: {
225 		el_zfunc_t p = va_arg(ap, el_zfunc_t);
226 		void *arg = va_arg(ap, void *);
227 		rv = ch_resizefun(el, p, arg);
228 		break;
229 	}
230 
231 	case EL_ALIAS_TEXT: {
232 		el_afunc_t p = va_arg(ap, el_afunc_t);
233 		void *arg = va_arg(ap, void *);
234 		rv = ch_aliasfun(el, p, arg);
235 		break;
236 	}
237 
238 	case EL_PROMPT_ESC:
239 	case EL_RPROMPT_ESC: {
240 		el_pfunc_t p = va_arg(ap, el_pfunc_t);
241 		int c = va_arg(ap, int);
242 
243 		rv = prompt_set(el, p, (wchar_t)c, op, 1);
244 		break;
245 	}
246 
247 	case EL_TERMINAL:
248 		rv = terminal_set(el, va_arg(ap, char *));
249 		break;
250 
251 	case EL_EDITOR:
252 		rv = map_set_editor(el, va_arg(ap, wchar_t *));
253 		break;
254 
255 	case EL_SIGNAL:
256 		if (va_arg(ap, int))
257 			el->el_flags |= HANDLE_SIGNALS;
258 		else
259 			el->el_flags &= ~HANDLE_SIGNALS;
260 		break;
261 
262 	case EL_BIND:
263 	case EL_TELLTC:
264 	case EL_SETTC:
265 	case EL_ECHOTC:
266 	case EL_SETTY:
267 	{
268 		const wchar_t *argv[20];
269 		int i;
270 
271 		for (i = 1; i < (int)__arraycount(argv); i++)
272 			if ((argv[i] = va_arg(ap, wchar_t *)) == NULL)
273 				break;
274 
275 		switch (op) {
276 		case EL_BIND:
277 			argv[0] = L"bind";
278 			rv = map_bind(el, i, argv);
279 			break;
280 
281 		case EL_TELLTC:
282 			argv[0] = L"telltc";
283 			rv = terminal_telltc(el, i, argv);
284 			break;
285 
286 		case EL_SETTC:
287 			argv[0] = L"settc";
288 			rv = terminal_settc(el, i, argv);
289 			break;
290 
291 		case EL_ECHOTC:
292 			argv[0] = L"echotc";
293 			rv = terminal_echotc(el, i, argv);
294 			break;
295 
296 		case EL_SETTY:
297 			argv[0] = L"setty";
298 			rv = tty_stty(el, i, argv);
299 			break;
300 
301 		default:
302 			rv = -1;
303 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
304 		}
305 		break;
306 	}
307 
308 	case EL_ADDFN:
309 	{
310 		wchar_t *name = va_arg(ap, wchar_t *);
311 		wchar_t *help = va_arg(ap, wchar_t *);
312 		el_func_t func = va_arg(ap, el_func_t);
313 
314 		rv = map_addfunc(el, name, help, func);
315 		break;
316 	}
317 
318 	case EL_HIST:
319 	{
320 		hist_fun_t func = va_arg(ap, hist_fun_t);
321 		void *ptr = va_arg(ap, void *);
322 
323 		rv = hist_set(el, func, ptr);
324 		if (MB_CUR_MAX == 1)
325 			el->el_flags &= ~NARROW_HISTORY;
326 		break;
327 	}
328 
329 	case EL_SAFEREAD:
330 		if (va_arg(ap, int))
331 			el->el_flags |= FIXIO;
332 		else
333 			el->el_flags &= ~FIXIO;
334 		rv = 0;
335 		break;
336 
337 	case EL_EDITMODE:
338 		if (va_arg(ap, int))
339 			el->el_flags &= ~EDIT_DISABLED;
340 		else
341 			el->el_flags |= EDIT_DISABLED;
342 		rv = 0;
343 		break;
344 
345 	case EL_GETCFN:
346 	{
347 		el_rfunc_t rc = va_arg(ap, el_rfunc_t);
348 		rv = el_read_setfn(el->el_read, rc);
349 		break;
350 	}
351 
352 	case EL_CLIENTDATA:
353 		el->el_data = va_arg(ap, void *);
354 		break;
355 
356 	case EL_UNBUFFERED:
357 		rv = va_arg(ap, int);
358 		if (rv && !(el->el_flags & UNBUFFERED)) {
359 			el->el_flags |= UNBUFFERED;
360 			read_prepare(el);
361 		} else if (!rv && (el->el_flags & UNBUFFERED)) {
362 			el->el_flags &= ~UNBUFFERED;
363 			read_finish(el);
364 		}
365 		rv = 0;
366 		break;
367 
368 	case EL_PREP_TERM:
369 		rv = va_arg(ap, int);
370 		if (rv)
371 			(void) tty_rawmode(el);
372 		else
373 			(void) tty_cookedmode(el);
374 		rv = 0;
375 		break;
376 
377 	case EL_SETFP:
378 	{
379 		FILE *fp;
380 		int what;
381 
382 		what = va_arg(ap, int);
383 		fp = va_arg(ap, FILE *);
384 
385 		rv = 0;
386 		switch (what) {
387 		case 0:
388 			el->el_infile = fp;
389 			el->el_infd = fileno(fp);
390 			break;
391 		case 1:
392 			el->el_outfile = fp;
393 			el->el_outfd = fileno(fp);
394 			break;
395 		case 2:
396 			el->el_errfile = fp;
397 			el->el_errfd = fileno(fp);
398 			break;
399 		default:
400 			rv = -1;
401 			break;
402 		}
403 		break;
404 	}
405 
406 	case EL_REFRESH:
407 		re_clear_display(el);
408 		re_refresh(el);
409 		terminal__flush(el);
410 		break;
411 
412 	default:
413 		rv = -1;
414 		break;
415 	}
416 
417 	va_end(ap);
418 	return rv;
419 }
420 
421 
422 /* el_get():
423  *	retrieve the editline parameters
424  */
425 int
el_wget(EditLine * el,int op,...)426 el_wget(EditLine *el, int op, ...)
427 {
428 	va_list ap;
429 	int rv;
430 
431 	if (el == NULL)
432 		return -1;
433 
434 	va_start(ap, op);
435 
436 	switch (op) {
437 	case EL_PROMPT:
438 	case EL_RPROMPT: {
439 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
440 		rv = prompt_get(el, p, 0, op);
441 		break;
442 	}
443 	case EL_PROMPT_ESC:
444 	case EL_RPROMPT_ESC: {
445 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
446 		wchar_t *c = va_arg(ap, wchar_t *);
447 
448 		rv = prompt_get(el, p, c, op);
449 		break;
450 	}
451 
452 	case EL_EDITOR:
453 		rv = map_get_editor(el, va_arg(ap, const wchar_t **));
454 		break;
455 
456 	case EL_SIGNAL:
457 		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
458 		rv = 0;
459 		break;
460 
461 	case EL_EDITMODE:
462 		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
463 		rv = 0;
464 		break;
465 
466 	case EL_SAFEREAD:
467 		*va_arg(ap, int *) = (el->el_flags & FIXIO);
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 
564 		/* secure_getenv is guaranteed to be defined and do the right thing here */
565 		/* because of the defines above which take into account issetugid, */
566 		/* secure_getenv and __secure_getenv availability. */
567 		if ((fname = secure_getenv("EDITRC")) == NULL) {
568 			static const char elpath[] = "/.editrc";
569 			size_t plen = sizeof(elpath);
570 
571 			if ((ptr = secure_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 
581 	}
582 	if (fname[0] == '\0')
583 		return -1;
584 
585 	if (fp == NULL)
586 		fp = fopen(fname, "r");
587 	if (fp == NULL) {
588 		el_free(path);
589 		return -1;
590 	}
591 
592 	ptr = NULL;
593 	len = 0;
594 	while ((slen = getline(&ptr, &len, fp)) != -1) {
595 		if (*ptr == '\n')
596 			continue;	/* Empty line. */
597 		if (slen > 0 && ptr[--slen] == '\n')
598 			ptr[slen] = '\0';
599 
600 		dptr = ct_decode_string(ptr, &el->el_scratch);
601 		if (!dptr)
602 			continue;
603 		/* loop until first non-space char or EOL */
604 		while (*dptr != '\0' && iswspace(*dptr))
605 			dptr++;
606 		if (*dptr == '#')
607 			continue;   /* ignore, this is a comment line */
608 		if ((error = parse_line(el, dptr)) == -1)
609 			break;
610 	}
611 	free(ptr);
612 
613 	el_free(path);
614 	(void) fclose(fp);
615 	return error;
616 }
617 
618 
619 /* el_resize():
620  *	Called from program when terminal is resized
621  */
622 void
el_resize(EditLine * el)623 el_resize(EditLine *el)
624 {
625 	int lins, cols;
626 	sigset_t oset, nset;
627 
628 	(void) sigemptyset(&nset);
629 	(void) sigaddset(&nset, SIGWINCH);
630 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
631 
632 	/* get the correct window size */
633 	if (terminal_get_size(el, &lins, &cols))
634 		terminal_change_size(el, lins, cols);
635 
636 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
637 }
638 
639 
640 /* el_beep():
641  *	Called from the program to beep
642  */
643 void
el_beep(EditLine * el)644 el_beep(EditLine *el)
645 {
646 
647 	terminal_beep(el);
648 }
649 
650 
651 /* el_editmode()
652  *	Set the state of EDIT_DISABLED from the `edit' command.
653  */
654 libedit_private int
655 /*ARGSUSED*/
el_editmode(EditLine * el,int argc,const wchar_t ** argv)656 el_editmode(EditLine *el, int argc, const wchar_t **argv)
657 {
658 	const wchar_t *how;
659 
660 	if (argv == NULL || argc != 2 || argv[1] == NULL)
661 		return -1;
662 
663 	how = argv[1];
664 	if (wcscmp(how, L"on") == 0) {
665 		el->el_flags &= ~EDIT_DISABLED;
666 		tty_rawmode(el);
667 	} else if (wcscmp(how, L"off") == 0) {
668 		tty_cookedmode(el);
669 		el->el_flags |= EDIT_DISABLED;
670 	}
671 	else {
672 		(void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n",
673 		    how);
674 		return -1;
675 	}
676 	return 0;
677 }
678