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