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