xref: /openbsd/lib/libedit/el.c (revision 8529ddd3)
1 /*	$OpenBSD: el.c,v 1.20 2015/01/16 16:48:51 deraadt 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 <string.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <ctype.h>
46 #include <locale.h>
47 #include <limits.h>
48 #include <langinfo.h>
49 #include "el.h"
50 
51 /* el_init():
52  *	Initialize editline and set default parameters.
53  */
54 public EditLine *
55 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
56 {
57 	EditLine *el = (EditLine *) malloc(sizeof(EditLine));
58 
59 	if (el == NULL)
60 		return (NULL);
61 
62 	memset(el, 0, sizeof(EditLine));
63 
64 	el->el_infile = fin;
65 	el->el_outfile = fout;
66 	el->el_errfile = ferr;
67 
68 	el->el_infd = fileno(fin);
69 	el->el_outfd = fileno(fout);
70 	el->el_errfd = fileno(ferr);
71 
72 	el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch));
73 	if (el->el_prog == NULL) {
74 		free(el);
75 		return NULL;
76 	}
77 
78 	/*
79          * Initialize all the modules. Order is important!!!
80          */
81 	el->el_flags = 0;
82 #ifdef WIDECHAR
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 #endif
88 
89 	if (term_init(el) == -1) {
90 		free(el->el_prog);
91 		free(el);
92 		return NULL;
93 	}
94 	(void) key_init(el);
95 	(void) map_init(el);
96 	if (tty_init(el) == -1)
97 		el->el_flags |= NO_TTY;
98 	(void) ch_init(el);
99 	(void) search_init(el);
100 	(void) hist_init(el);
101 	(void) prompt_init(el);
102 	(void) sig_init(el);
103 	(void) read_init(el);
104 
105 	return (el);
106 }
107 
108 
109 /* el_end():
110  *	Clean up.
111  */
112 public void
113 el_end(EditLine *el)
114 {
115 
116 	if (el == NULL)
117 		return;
118 
119 	el_reset(el);
120 
121 	term_end(el);
122 	key_end(el);
123 	map_end(el);
124 	tty_end(el);
125 	ch_end(el);
126 	search_end(el);
127 	hist_end(el);
128 	prompt_end(el);
129 	sig_end(el);
130 
131 	free((ptr_t) el->el_prog);
132 #ifdef WIDECHAR
133 	free((ptr_t) el->el_scratch.cbuff);
134 	free((ptr_t) el->el_scratch.wbuff);
135 	free((ptr_t) el->el_lgcyconv.cbuff);
136 	free((ptr_t) el->el_lgcyconv.wbuff);
137 #endif
138 	free((ptr_t) el);
139 }
140 
141 
142 /* el_reset():
143  *	Reset the tty and the parser
144  */
145 public void
146 el_reset(EditLine *el)
147 {
148 
149 	tty_cookedmode(el);
150 	ch_reset(el, 0);		/* XXX: Do we want that? */
151 }
152 
153 
154 /* el_set():
155  *	set the editline parameters
156  */
157 public int
158 FUN(el,set)(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 = term_set(el, va_arg(ap, char *));
194 		break;
195 
196 	case EL_EDITOR:
197 		rv = map_set_editor(el, va_arg(ap, Char *));
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 Char *argv[20];
214 		int i;
215 
216 		for (i = 1; i < 20; i++)
217 			if ((argv[i] = va_arg(ap, Char *)) == NULL)
218 				break;
219 
220 		switch (op) {
221 		case EL_BIND:
222 			argv[0] = STR("bind");
223 			rv = map_bind(el, i, argv);
224 			break;
225 
226 		case EL_TELLTC:
227 			argv[0] = STR("telltc");
228 			rv = term_telltc(el, i, argv);
229 			break;
230 
231 		case EL_SETTC:
232 			argv[0] = STR("settc");
233 			rv = term_settc(el, i, argv);
234 			break;
235 
236 		case EL_ECHOTC:
237 			argv[0] = STR("echotc");
238 			rv = term_echotc(el, i, argv);
239 			break;
240 
241 		case EL_SETTY:
242 			argv[0] = STR("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 		Char *name = va_arg(ap, Char *);
257 		Char *help = va_arg(ap, Char *);
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 		ptr_t ptr = va_arg(ap, ptr_t);
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, rc);
287 		el->el_flags &= ~NARROW_READ;
288 		break;
289 	}
290 
291 	case EL_CLIENTDATA:
292 		el->el_data = va_arg(ap, void *);
293 		break;
294 
295 	case EL_UNBUFFERED:
296 		rv = va_arg(ap, int);
297 		if (rv && !(el->el_flags & UNBUFFERED)) {
298 			el->el_flags |= UNBUFFERED;
299 			read_prepare(el);
300 		} else if (!rv && (el->el_flags & UNBUFFERED)) {
301 			el->el_flags &= ~UNBUFFERED;
302 			read_finish(el);
303 		}
304 		rv = 0;
305 		break;
306 
307 	case EL_PREP_TERM:
308 		rv = va_arg(ap, int);
309 		if (rv)
310 			(void) tty_rawmode(el);
311 		else
312 			(void) tty_cookedmode(el);
313 		rv = 0;
314 		break;
315 
316 	case EL_SETFP:
317 	{
318 		FILE *fp;
319 		int what;
320 
321 		what = va_arg(ap, int);
322 		fp = va_arg(ap, FILE *);
323 
324 		rv = 0;
325 		switch (what) {
326 		case 0:
327 			el->el_infile = fp;
328 			el->el_infd = fileno(fp);
329 			break;
330 		case 1:
331 			el->el_outfile = fp;
332 			el->el_outfd = fileno(fp);
333 			break;
334 		case 2:
335 			el->el_errfile = fp;
336 			el->el_errfd = fileno(fp);
337 			break;
338 		default:
339 			rv = -1;
340 			break;
341 		}
342 		break;
343 	}
344 
345 	case EL_REFRESH:
346 		re_clear_display(el);
347 		re_refresh(el);
348 		term__flush(el);
349 		break;
350 
351 	default:
352 		rv = -1;
353 		break;
354 	}
355 
356 	va_end(ap);
357 	return (rv);
358 }
359 
360 
361 /* el_get():
362  *	retrieve the editline parameters
363  */
364 public int
365 FUN(el,get)(EditLine *el, int op, ...)
366 {
367 	va_list ap;
368 	int rv;
369 
370 	if (el == NULL)
371 		return -1;
372 
373 	va_start(ap, op);
374 
375 	switch (op) {
376 	case EL_PROMPT:
377 	case EL_RPROMPT: {
378 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
379 		rv = prompt_get(el, p, 0, op);
380 		break;
381 	}
382 	case EL_PROMPT_ESC:
383 	case EL_RPROMPT_ESC: {
384 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
385 		Char *c = va_arg(ap, Char *);
386 
387 		rv = prompt_get(el, p, c, op);
388 		break;
389 	}
390 
391 	case EL_EDITOR:
392 		rv = map_get_editor(el, va_arg(ap, const Char **));
393 		break;
394 
395 	case EL_SIGNAL:
396 		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
397 		rv = 0;
398 		break;
399 
400 	case EL_EDITMODE:
401 		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
402 		rv = 0;
403 		break;
404 
405 	case EL_TERMINAL:
406 		term_get(el, va_arg(ap, const char **));
407 		rv = 0;
408 		break;
409 
410 	case EL_GETTC:
411 	{
412 		static char name[] = "gettc";
413 		char *argv[20];
414 		int i;
415 
416  		for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++)
417 			if ((argv[i] = va_arg(ap, char *)) == NULL)
418 				break;
419 
420 		switch (op) {
421 		case EL_GETTC:
422 			argv[0] = name;
423 			rv = term_gettc(el, i, argv);
424 			break;
425 
426 		default:
427 			rv = -1;
428 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
429 			break;
430 		}
431 		break;
432 	}
433 
434 	case EL_GETCFN:
435 		*va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
436 		rv = 0;
437 		break;
438 
439 	case EL_CLIENTDATA:
440 		*va_arg(ap, void **) = el->el_data;
441 		rv = 0;
442 		break;
443 
444 	case EL_UNBUFFERED:
445 		*va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
446 		rv = 0;
447 		break;
448 
449 	case EL_GETFP:
450 	{
451 		int what;
452 		FILE **fpp;
453 
454 		what = va_arg(ap, int);
455 		fpp = va_arg(ap, FILE **);
456 		rv = 0;
457 		switch (what) {
458 		case 0:
459 			*fpp = el->el_infile;
460 			break;
461 		case 1:
462 			*fpp = el->el_outfile;
463 			break;
464 		case 2:
465 			*fpp = el->el_errfile;
466 			break;
467 		default:
468 			rv = -1;
469 			break;
470 		}
471 		break;
472 	}
473 	default:
474 		rv = -1;
475 		break;
476 	}
477 	va_end(ap);
478 
479 	return (rv);
480 }
481 
482 
483 /* el_line():
484  *	Return editing info
485  */
486 public const TYPE(LineInfo) *
487 FUN(el,line)(EditLine *el)
488 {
489 
490 	return (const TYPE(LineInfo) *) (void *) &el->el_line;
491 }
492 
493 
494 /* el_source():
495  *	Source a file
496  */
497 public int
498 el_source(EditLine *el, const char *fname)
499 {
500 	FILE *fp;
501 	size_t len;
502 	char *ptr, *lptr = NULL;
503 #ifdef HAVE_ISSETUGID
504 	char path[PATH_MAX];
505 #endif
506 	const Char *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 	while ((ptr = fgetln(fp, &len)) != NULL) {
537 		if (ptr[len - 1] == '\n')
538 			ptr[len - 1] = '\0';
539 		else {
540 			if ((lptr = (char *)malloc(len + 1)) == NULL) {
541 				(void) fclose(fp);
542 				return (-1);
543 			}
544 			memcpy(lptr, ptr, len);
545 			lptr[len] = '\0';
546 			ptr = lptr;
547 		}
548 
549 		dptr = ct_decode_string(ptr, &el->el_scratch);
550 		if (!dptr)
551 			continue;
552 
553 		/* loop until first non-space char or EOL */
554 		while (*dptr != '\0' && Isspace(*dptr))
555 			dptr++;
556 		if (*dptr == '#')
557 			continue;   /* ignore, this is a comment line */
558 		if (parse_line(el, dptr) == -1) {
559 			free(lptr);
560 			(void) fclose(fp);
561 			return (-1);
562 		}
563 	}
564 	free(lptr);
565 	(void) fclose(fp);
566 	return (0);
567 }
568 
569 
570 /* el_resize():
571  *	Called from program when terminal is resized
572  */
573 public void
574 el_resize(EditLine *el)
575 {
576 	int lins, cols;
577 	sigset_t oset, nset;
578 
579 	(void) sigemptyset(&nset);
580 	(void) sigaddset(&nset, SIGWINCH);
581 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
582 
583 	/* get the correct window size */
584 	if (term_get_size(el, &lins, &cols))
585 		term_change_size(el, lins, cols);
586 
587 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
588 }
589 
590 
591 /* el_beep():
592  *	Called from the program to beep
593  */
594 public void
595 el_beep(EditLine *el)
596 {
597 
598 	term_beep(el);
599 }
600 
601 
602 /* el_editmode()
603  *	Set the state of EDIT_DISABLED from the `edit' command.
604  */
605 protected int
606 /*ARGSUSED*/
607 el_editmode(EditLine *el, int argc, const Char **argv)
608 {
609 	const Char *how;
610 
611 	if (argv == NULL || argc != 2 || argv[1] == NULL)
612 		return (-1);
613 
614 	how = argv[1];
615 	if (Strcmp(how, STR("on")) == 0) {
616 		el->el_flags &= ~EDIT_DISABLED;
617 		tty_rawmode(el);
618 	} else if (Strcmp(how, STR("off")) == 0) {
619 		tty_cookedmode(el);
620 		el->el_flags |= EDIT_DISABLED;
621 	}
622 	else {
623 		(void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n",
624 		    how);
625 		return (-1);
626 	}
627 	return (0);
628 }
629