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