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