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