1 /**	$MirOS: src/lib/libedit/el.c,v 1.4 2005/10/21 11:02:42 tg Exp $ */
2 /*	$NetBSD: el.c,v 1.39 2004/07/08 00:51:36 christos Exp $	*/
3 /*	$OpenBSD: el.c,v 1.14 2004/08/23 18:31:25 otto Exp $	*/
4 
5 /*-
6  * Copyright (c) 1992, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Christos Zoulas of Cornell University.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include "config.h"
38 
39 /*
40  * el.c: EditLine interface functions
41  */
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include "el.h"
48 
49 __SCCSID("@(#)el.c	8.2 (Berkeley) 1/3/94");
50 __RCSID("$MirOS: src/lib/libedit/el.c,v 1.4 2005/10/21 11:02:42 tg Exp $");
51 
52 /* el_init():
53  *	Initialize editline and set default parameters.
54  */
55 public EditLine *
el_init(const char * prog,FILE * fin,FILE * fout,FILE * ferr)56 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
57 {
58 
59 	EditLine *el = (EditLine *) el_malloc(sizeof(EditLine));
60 
61 	if (el == NULL)
62 		return (NULL);
63 
64 	memset(el, 0, sizeof(EditLine));
65 
66 	el->el_infd = fileno(fin);
67 	el->el_outfile = fout;
68 	el->el_errfile = ferr;
69 	if ((el->el_prog = el_strdup(prog)) == NULL) {
70 		el_free(el);
71 		return NULL;
72 	}
73 
74 	/*
75          * Initialize all the modules. Order is important!!!
76          */
77 	el->el_flags = 0;
78 
79 	if (term_init(el) == -1) {
80 		el_free(el->el_prog);
81 		el_free(el);
82 		return NULL;
83 	}
84 	(void) key_init(el);
85 	(void) map_init(el);
86 	if (tty_init(el) == -1)
87 		el->el_flags |= NO_TTY;
88 	(void) ch_init(el);
89 	(void) search_init(el);
90 	(void) hist_init(el);
91 	(void) prompt_init(el);
92 	(void) sig_init(el);
93 	(void) read_init(el);
94 
95 	return (el);
96 }
97 
98 
99 /* el_end():
100  *	Clean up.
101  */
102 public void
el_end(EditLine * el)103 el_end(EditLine *el)
104 {
105 
106 	if (el == NULL)
107 		return;
108 
109 	el_reset(el);
110 
111 	term_end(el);
112 	key_end(el);
113 	map_end(el);
114 	tty_end(el);
115 	ch_end(el);
116 	search_end(el);
117 	hist_end(el);
118 	prompt_end(el);
119 	sig_end(el);
120 
121 	el_free((ptr_t) el->el_prog);
122 	el_free((ptr_t) el);
123 }
124 
125 
126 /* el_reset():
127  *	Reset the tty and the parser
128  */
129 public void
el_reset(EditLine * el)130 el_reset(EditLine *el)
131 {
132 
133 	tty_cookedmode(el);
134 	ch_reset(el);		/* XXX: Do we want that? */
135 }
136 
137 
138 /* el_set():
139  *	set the editline parameters
140  */
141 public int
el_set(EditLine * el,int op,...)142 el_set(EditLine *el, int op, ...)
143 {
144 	va_list va;
145 	int rv = 0;
146 
147 	if (el == NULL)
148 		return (-1);
149 	va_start(va, op);
150 
151 	switch (op) {
152 	case EL_PROMPT:
153 	case EL_RPROMPT:
154 		rv = prompt_set(el, va_arg(va, el_pfunc_t), op);
155 		break;
156 
157 	case EL_TERMINAL:
158 		rv = term_set(el, va_arg(va, char *));
159 		break;
160 
161 	case EL_EDITOR:
162 		rv = map_set_editor(el, va_arg(va, char *));
163 		break;
164 
165 	case EL_SIGNAL:
166 		if (va_arg(va, int))
167 			el->el_flags |= HANDLE_SIGNALS;
168 		else
169 			el->el_flags &= ~HANDLE_SIGNALS;
170 		break;
171 
172 	case EL_BIND:
173 	case EL_TELLTC:
174 	case EL_SETTC:
175 	case EL_ECHOTC:
176 	case EL_SETTY:
177 	{
178 		const char *argv[20];
179 		int i;
180 
181 		for (i = 1; i < 20; i++)
182 			if ((argv[i] = va_arg(va, char *)) == NULL)
183 				break;
184 
185 		switch (op) {
186 		case EL_BIND:
187 			argv[0] = "bind";
188 			rv = map_bind(el, i, argv);
189 			break;
190 
191 		case EL_TELLTC:
192 			argv[0] = "telltc";
193 			rv = term_telltc(el, i, argv);
194 			break;
195 
196 		case EL_SETTC:
197 			argv[0] = "settc";
198 			rv = term_settc(el, i, argv);
199 			break;
200 
201 		case EL_ECHOTC:
202 			argv[0] = "echotc";
203 			rv = term_echotc(el, i, argv);
204 			break;
205 
206 		case EL_SETTY:
207 			argv[0] = "setty";
208 			rv = tty_stty(el, i, argv);
209 			break;
210 
211 		default:
212 			rv = -1;
213 			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
214 			break;
215 		}
216 		break;
217 	}
218 
219 	case EL_ADDFN:
220 	{
221 		char *name = va_arg(va, char *);
222 		char *help = va_arg(va, char *);
223 		el_func_t func = va_arg(va, el_func_t);
224 
225 		rv = map_addfunc(el, name, help, func);
226 		break;
227 	}
228 
229 	case EL_HIST:
230 	{
231 		hist_fun_t func = va_arg(va, hist_fun_t);
232 		ptr_t ptr = va_arg(va, char *);
233 
234 		rv = hist_set(el, func, ptr);
235 		break;
236 	}
237 
238 	case EL_EDITMODE:
239 		if (va_arg(va, int))
240 			el->el_flags &= ~EDIT_DISABLED;
241 		else
242 			el->el_flags |= EDIT_DISABLED;
243 		rv = 0;
244 		break;
245 
246 	case EL_GETCFN:
247 	{
248 		el_rfunc_t rc = va_arg(va, el_rfunc_t);
249 		rv = el_read_setfn(el, rc);
250 		break;
251 	}
252 
253 	case EL_CLIENTDATA:
254 		el->el_data = va_arg(va, void *);
255 		break;
256 
257 	case EL_UNBUFFERED:
258 		rv = va_arg(va, int);
259 		if (rv && !(el->el_flags & UNBUFFERED)) {
260 			el->el_flags |= UNBUFFERED;
261 			read_prepare(el);
262 		} else if (!rv && (el->el_flags & UNBUFFERED)) {
263 			el->el_flags &= ~UNBUFFERED;
264 			read_finish(el);
265 		}
266 		rv = 0;
267 		break;
268 
269 	case EL_PREP_TERM:
270 		rv = va_arg(va, int);
271 		if (rv)
272 			(void) tty_rawmode(el);
273 		else
274 			(void) tty_cookedmode(el);
275 		rv = 0;
276 		break;
277 
278 	default:
279 		rv = -1;
280 		break;
281 	}
282 
283 	va_end(va);
284 	return (rv);
285 }
286 
287 
288 /* el_get():
289  *	retrieve the editline parameters
290  */
291 public int
el_get(EditLine * el,int op,void * ret)292 el_get(EditLine *el, int op, void *ret)
293 {
294 	int rv;
295 
296 	if (el == NULL || ret == NULL)
297 		return (-1);
298 	switch (op) {
299 	case EL_PROMPT:
300 	case EL_RPROMPT:
301 		rv = prompt_get(el, (void *) &ret, op);
302 		break;
303 
304 	case EL_EDITOR:
305 		rv = map_get_editor(el, (void *) &ret);
306 		break;
307 
308 	case EL_SIGNAL:
309 		*((int *) ret) = (el->el_flags & HANDLE_SIGNALS);
310 		rv = 0;
311 		break;
312 
313 	case EL_EDITMODE:
314 		*((int *) ret) = (!(el->el_flags & EDIT_DISABLED));
315 		rv = 0;
316 		break;
317 
318 	case EL_TERMINAL:
319 		term_get(el, (const char **)ret);
320 		rv = 0;
321 		break;
322 
323 #if 0				/* XXX */
324 	case EL_BIND:
325 	case EL_TELLTC:
326 	case EL_SETTC:
327 	case EL_ECHOTC:
328 	case EL_SETTY:
329 	{
330 		const char *argv[20];
331 		int i;
332 
333  		for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++)
334 			if ((argv[i] = va_arg(va, char *)) == NULL)
335 				break;
336 
337 		switch (op) {
338 		case EL_BIND:
339 			argv[0] = "bind";
340 			rv = map_bind(el, i, argv);
341 			break;
342 
343 		case EL_TELLTC:
344 			argv[0] = "telltc";
345 			rv = term_telltc(el, i, argv);
346 			break;
347 
348 		case EL_SETTC:
349 			argv[0] = "settc";
350 			rv = term_settc(el, i, argv);
351 			break;
352 
353 		case EL_ECHOTC:
354 			argv[0] = "echotc";
355 			rv = term_echotc(el, i, argv);
356 			break;
357 
358 		case EL_SETTY:
359 			argv[0] = "setty";
360 			rv = tty_stty(el, i, argv);
361 			break;
362 
363 		default:
364 			rv = -1;
365 			EL_ABORT((el->errfile, "Bad op %d\n", op));
366 			break;
367 		}
368 		break;
369 	}
370 
371 	case EL_ADDFN:
372 	{
373 		char *name = va_arg(va, char *);
374 		char *help = va_arg(va, char *);
375 		el_func_t func = va_arg(va, el_func_t);
376 
377 		rv = map_addfunc(el, name, help, func);
378 		break;
379 	}
380 
381 	case EL_HIST:
382 		{
383 			hist_fun_t func = va_arg(va, hist_fun_t);
384 			ptr_t ptr = va_arg(va, char *);
385 			rv = hist_set(el, func, ptr);
386 		}
387 		break;
388 #endif /* XXX */
389 
390 	case EL_GETCFN:
391 		*((el_rfunc_t *)ret) = el_read_getfn(el);
392 		rv = 0;
393 		break;
394 
395 	case EL_CLIENTDATA:
396 		*((void **)ret) = el->el_data;
397 		rv = 0;
398 		break;
399 
400 	case EL_UNBUFFERED:
401 		*((int *) ret) = (!(el->el_flags & UNBUFFERED));
402 		rv = 0;
403 		break;
404 
405 	default:
406 		rv = -1;
407 	}
408 
409 	return (rv);
410 }
411 
412 
413 /* el_line():
414  *	Return editing info
415  */
416 public const LineInfo *
el_line(EditLine * el)417 el_line(EditLine *el)
418 {
419 
420 	return (const LineInfo *) (void *) &el->el_line;
421 }
422 
423 
424 /* el_source():
425  *	Source a file
426  */
427 public int
el_source(EditLine * el,const char * fname)428 el_source(EditLine *el, const char *fname)
429 {
430 	FILE *fp;
431 	size_t len;
432 	char *ptr, *lptr = NULL;
433 
434 	fp = NULL;
435 	if (fname == NULL) {
436 #ifdef HAVE_ISSETUGID
437 		char path[MAXPATHLEN];
438 
439 		if (issetugid())
440 			return (-1);
441 		if ((fp = fopen(".editrc", "r")) != NULL)
442 			goto found;
443 		if ((ptr = getenv("HOME")) == NULL)
444 			return (-1);
445 		if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
446 			return (-1);
447 		if (strlcat(path, "/.etc/editrc", sizeof(path)) >= sizeof(path))
448 			return (-1);
449 		fname = path;
450 	found:	;
451 #else
452 		/*
453 		 * If issetugid() is missing, always return an error, in order
454 		 * to keep from inadvertently opening up the user to a security
455 		 * hole.
456 		 */
457 		return (-1);
458 #endif
459 	}
460 	if (fp == NULL)
461 		fp = fopen(fname, "r");
462 	if (fp == NULL)
463 		return (-1);
464 
465 	while ((ptr = fgetln(fp, &len)) != NULL) {
466 		if (ptr[len - 1] == '\n')
467 			ptr[len - 1] = '\0';
468 		else {
469 			if ((lptr = (char *)malloc(len + 1)) == NULL) {
470 				(void) fclose(fp);
471 				return (-1);
472 			}
473 			memcpy(lptr, ptr, len);
474 			lptr[len] = '\0';
475 			ptr = lptr;
476 		}
477 		if (parse_line(el, ptr) == -1) {
478 			free(lptr);
479 			(void) fclose(fp);
480 			return (-1);
481 		}
482 	}
483 	free(lptr);
484 	(void) fclose(fp);
485 	return (0);
486 }
487 
488 
489 /* el_resize():
490  *	Called from program when terminal is resized
491  */
492 public void
el_resize(EditLine * el)493 el_resize(EditLine *el)
494 {
495 	int lins, cols;
496 	sigset_t oset, nset;
497 
498 	(void) sigemptyset(&nset);
499 	(void) sigaddset(&nset, SIGWINCH);
500 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
501 
502 	/* get the correct window size */
503 	if (term_get_size(el, &lins, &cols))
504 		term_change_size(el, lins, cols);
505 
506 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
507 }
508 
509 
510 /* el_beep():
511  *	Called from the program to beep
512  */
513 public void
el_beep(EditLine * el)514 el_beep(EditLine *el)
515 {
516 
517 	term_beep(el);
518 }
519 
520 
521 /* el_editmode()
522  *	Set the state of EDIT_DISABLED from the `edit' command.
523  */
524 protected int
525 /*ARGSUSED*/
el_editmode(EditLine * el,int argc,const char ** argv)526 el_editmode(EditLine *el, int argc, const char **argv)
527 {
528 	const char *how;
529 
530 	if (argv == NULL || argc != 2 || argv[1] == NULL)
531 		return (-1);
532 
533 	how = argv[1];
534 	if (strcmp(how, "on") == 0) {
535 		el->el_flags &= ~EDIT_DISABLED;
536 		tty_rawmode(el);
537 	} else if (strcmp(how, "off") == 0) {
538 		tty_cookedmode(el);
539 		el->el_flags |= EDIT_DISABLED;
540 	}
541 	else {
542 		(void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
543 		return (-1);
544 	}
545 	return (0);
546 }
547