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