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