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