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