xref: /netbsd-src/lib/libedit/el.c (revision b5677b36047b601b9addaaa494a58ceae82c2a6c)
1 /*	$NetBSD: el.c,v 1.51 2009/03/31 17:38:27 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.51 2009/03/31 17:38:27 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 		char c = va_arg(ap, int);
161 
162 		rv = prompt_set(el, p, c, op);
163 		break;
164 	}
165 
166 	case EL_TERMINAL:
167 		rv = term_set(el, va_arg(ap, char *));
168 		break;
169 
170 	case EL_EDITOR:
171 		rv = map_set_editor(el, va_arg(ap, char *));
172 		break;
173 
174 	case EL_SIGNAL:
175 		if (va_arg(ap, int))
176 			el->el_flags |= HANDLE_SIGNALS;
177 		else
178 			el->el_flags &= ~HANDLE_SIGNALS;
179 		break;
180 
181 	case EL_BIND:
182 	case EL_TELLTC:
183 	case EL_SETTC:
184 	case EL_GETTC:
185 	case EL_ECHOTC:
186 	case EL_SETTY:
187 	{
188 		const char *argv[20];
189 		int i;
190 
191 		for (i = 1; i < 20; i++)
192 			if ((argv[i] = va_arg(ap, char *)) == NULL)
193 				break;
194 
195 		switch (op) {
196 		case EL_BIND:
197 			argv[0] = "bind";
198 			rv = map_bind(el, i, argv);
199 			break;
200 
201 		case EL_TELLTC:
202 			argv[0] = "telltc";
203 			rv = term_telltc(el, i, argv);
204 			break;
205 
206 		case EL_SETTC:
207 			argv[0] = "settc";
208 			rv = term_settc(el, i, argv);
209 			break;
210 
211 		case EL_ECHOTC:
212 			argv[0] = "echotc";
213 			rv = term_echotc(el, i, argv);
214 			break;
215 
216 		case EL_SETTY:
217 			argv[0] = "setty";
218 			rv = tty_stty(el, i, argv);
219 			break;
220 
221 		default:
222 			rv = -1;
223 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
224 			break;
225 		}
226 		break;
227 	}
228 
229 	case EL_ADDFN:
230 	{
231 		char *name = va_arg(ap, char *);
232 		char *help = va_arg(ap, char *);
233 		el_func_t func = va_arg(ap, el_func_t);
234 
235 		rv = map_addfunc(el, name, help, func);
236 		break;
237 	}
238 
239 	case EL_HIST:
240 	{
241 		hist_fun_t func = va_arg(ap, hist_fun_t);
242 		ptr_t ptr = va_arg(ap, char *);
243 
244 		rv = hist_set(el, func, ptr);
245 		break;
246 	}
247 
248 	case EL_EDITMODE:
249 		if (va_arg(ap, int))
250 			el->el_flags &= ~EDIT_DISABLED;
251 		else
252 			el->el_flags |= EDIT_DISABLED;
253 		rv = 0;
254 		break;
255 
256 	case EL_GETCFN:
257 	{
258 		el_rfunc_t rc = va_arg(ap, el_rfunc_t);
259 		rv = el_read_setfn(el, rc);
260 		break;
261 	}
262 
263 	case EL_CLIENTDATA:
264 		el->el_data = va_arg(ap, void *);
265 		break;
266 
267 	case EL_UNBUFFERED:
268 		rv = va_arg(ap, int);
269 		if (rv && !(el->el_flags & UNBUFFERED)) {
270 			el->el_flags |= UNBUFFERED;
271 			read_prepare(el);
272 		} else if (!rv && (el->el_flags & UNBUFFERED)) {
273 			el->el_flags &= ~UNBUFFERED;
274 			read_finish(el);
275 		}
276 		rv = 0;
277 		break;
278 
279 	case EL_PREP_TERM:
280 		rv = va_arg(ap, int);
281 		if (rv)
282 			(void) tty_rawmode(el);
283 		else
284 			(void) tty_cookedmode(el);
285 		rv = 0;
286 		break;
287 
288 	case EL_SETFP:
289 	{
290 		FILE *fp;
291 		int what;
292 
293 		what = va_arg(ap, int);
294 		fp = va_arg(ap, FILE *);
295 
296 		rv = 0;
297 		switch (what) {
298 		case 0:
299 			el->el_infile = fp;
300 			el->el_infd = fileno(fp);
301 			break;
302 		case 1:
303 			el->el_outfile = fp;
304 			break;
305 		case 2:
306 			el->el_errfile = fp;
307 			break;
308 		default:
309 			rv = -1;
310 			break;
311 		}
312 		break;
313 	}
314 
315 	case EL_REFRESH:
316 		re_clear_display(el);
317 		re_refresh(el);
318 		term__flush(el);
319 		break;
320 
321 	default:
322 		rv = -1;
323 		break;
324 	}
325 
326 	va_end(ap);
327 	return (rv);
328 }
329 
330 
331 /* el_get():
332  *	retrieve the editline parameters
333  */
334 public int
335 el_get(EditLine *el, int op, ...)
336 {
337 	va_list ap;
338 	int rv;
339 
340 	if (el == NULL)
341 		return -1;
342 
343 	va_start(ap, op);
344 
345 	switch (op) {
346 	case EL_PROMPT:
347 	case EL_RPROMPT: {
348 		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
349 		char *c = va_arg(ap, char *);
350 
351 		rv = prompt_get(el, p, c, op);
352 		break;
353 	}
354 
355 	case EL_EDITOR:
356 		rv = map_get_editor(el, va_arg(ap, const char **));
357 		break;
358 
359 	case EL_SIGNAL:
360 		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
361 		rv = 0;
362 		break;
363 
364 	case EL_EDITMODE:
365 		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
366 		rv = 0;
367 		break;
368 
369 	case EL_TERMINAL:
370 		term_get(el, va_arg(ap, const char **));
371 		rv = 0;
372 		break;
373 
374 	case EL_GETTC:
375 	{
376 		static char name[] = "gettc";
377 		char *argv[20];
378 		int i;
379 
380  		for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++)
381 			if ((argv[i] = va_arg(ap, char *)) == NULL)
382 				break;
383 
384 		switch (op) {
385 		case EL_GETTC:
386 			argv[0] = name;
387 			rv = term_gettc(el, i, argv);
388 			break;
389 
390 		default:
391 			rv = -1;
392 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
393 			break;
394 		}
395 		break;
396 	}
397 
398 #if 0 /* XXX */
399 	case EL_ADDFN:
400 	{
401 		char *name = va_arg(ap, char *);
402 		char *help = va_arg(ap, char *);
403 		el_func_t func = va_arg(ap, el_func_t);
404 
405 		rv = map_addfunc(el, name, help, func);
406 		break;
407 	}
408 
409 	case EL_HIST:
410 		{
411 			hist_fun_t func = va_arg(ap, hist_fun_t);
412 			ptr_t ptr = va_arg(ap, char *);
413 			rv = hist_set(el, func, ptr);
414 		}
415 		break;
416 #endif /* XXX */
417 
418 	case EL_GETCFN:
419 		*va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
420 		rv = 0;
421 		break;
422 
423 	case EL_CLIENTDATA:
424 		*va_arg(ap, void **) = el->el_data;
425 		rv = 0;
426 		break;
427 
428 	case EL_UNBUFFERED:
429 		*va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
430 		rv = 0;
431 		break;
432 
433 	case EL_GETFP:
434 	{
435 		int what;
436 		FILE **fpp;
437 
438 		what = va_arg(ap, int);
439 		fpp = va_arg(ap, FILE **);
440 		rv = 0;
441 		switch (what) {
442 		case 0:
443 			*fpp = el->el_infile;
444 			break;
445 		case 1:
446 			*fpp = el->el_outfile;
447 			break;
448 		case 2:
449 			*fpp = el->el_errfile;
450 			break;
451 		default:
452 			rv = -1;
453 			break;
454 		}
455 		break;
456 	}
457 	default:
458 		rv = -1;
459 		break;
460 	}
461 	va_end(ap);
462 
463 	return (rv);
464 }
465 
466 
467 /* el_line():
468  *	Return editing info
469  */
470 public const LineInfo *
471 el_line(EditLine *el)
472 {
473 
474 	return (const LineInfo *) (void *) &el->el_line;
475 }
476 
477 
478 /* el_source():
479  *	Source a file
480  */
481 public int
482 el_source(EditLine *el, const char *fname)
483 {
484 	FILE *fp;
485 	size_t len;
486 	char *ptr;
487 
488 	fp = NULL;
489 	if (fname == NULL) {
490 #ifdef HAVE_ISSETUGID
491 		static const char elpath[] = "/.editrc";
492 		char path[MAXPATHLEN];
493 
494 		if (issetugid())
495 			return (-1);
496 		if ((ptr = getenv("HOME")) == NULL)
497 			return (-1);
498 		if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
499 			return (-1);
500 		if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
501 			return (-1);
502 		fname = path;
503 #else
504 		/*
505 		 * If issetugid() is missing, always return an error, in order
506 		 * to keep from inadvertently opening up the user to a security
507 		 * hole.
508 		 */
509 		return (-1);
510 #endif
511 	}
512 	if (fp == NULL)
513 		fp = fopen(fname, "r");
514 	if (fp == NULL)
515 		return (-1);
516 
517 	while ((ptr = fgetln(fp, &len)) != NULL) {
518 		if (len > 0 && ptr[len - 1] == '\n')
519 			--len;
520 		ptr[len] = '\0';
521 		if (parse_line(el, ptr) == -1) {
522 			(void) fclose(fp);
523 			return (-1);
524 		}
525 	}
526 
527 	(void) fclose(fp);
528 	return (0);
529 }
530 
531 
532 /* el_resize():
533  *	Called from program when terminal is resized
534  */
535 public void
536 el_resize(EditLine *el)
537 {
538 	int lins, cols;
539 	sigset_t oset, nset;
540 
541 	(void) sigemptyset(&nset);
542 	(void) sigaddset(&nset, SIGWINCH);
543 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
544 
545 	/* get the correct window size */
546 	if (term_get_size(el, &lins, &cols))
547 		term_change_size(el, lins, cols);
548 
549 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
550 }
551 
552 
553 /* el_beep():
554  *	Called from the program to beep
555  */
556 public void
557 el_beep(EditLine *el)
558 {
559 
560 	term_beep(el);
561 }
562 
563 
564 /* el_editmode()
565  *	Set the state of EDIT_DISABLED from the `edit' command.
566  */
567 protected int
568 /*ARGSUSED*/
569 el_editmode(EditLine *el, int argc, const char **argv)
570 {
571 	const char *how;
572 
573 	if (argv == NULL || argc != 2 || argv[1] == NULL)
574 		return (-1);
575 
576 	how = argv[1];
577 	if (strcmp(how, "on") == 0) {
578 		el->el_flags &= ~EDIT_DISABLED;
579 		tty_rawmode(el);
580 	} else if (strcmp(how, "off") == 0) {
581 		tty_cookedmode(el);
582 		el->el_flags |= EDIT_DISABLED;
583 	}
584 	else {
585 		(void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
586 		return (-1);
587 	}
588 	return (0);
589 }
590