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/* el_data_get():
477 *	Set user private data.
478 */
479public void
480el_data_set (el, data)
481    EditLine *el;
482    void *data;
483{
484    el->el_data = data;
485
486    return;
487}
488
489/* el_data_get():
490 *	Return user private data.
491 */
492public void *
493el_data_get (el)
494    EditLine *el;
495{
496    if (el->el_data)
497	return (el->el_data);
498    return (NULL);
499}
500
501/* el_line():
502 *	Return editing info
503 */
504public const LineInfo *
505el_line(EditLine *el)
506{
507
508	return (const LineInfo *) (void *) &el->el_line;
509}
510
511
512/* el_source():
513 *	Source a file
514 */
515public int
516el_source(EditLine *el, const char *fname)
517{
518	FILE *fp;
519	size_t len;
520	char *ptr;
521#ifdef HAVE_ISSETUGID
522	char path[MAXPATHLEN];
523#endif
524
525	fp = NULL;
526	if (fname == NULL) {
527#ifdef HAVE_ISSETUGID
528		static const char elpath[] = "/.editrc";
529
530		if (issetugid())
531			return (-1);
532		if ((ptr = getenv("HOME")) == NULL)
533			return (-1);
534		if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
535			return (-1);
536		if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
537			return (-1);
538		fname = path;
539#else
540		/*
541		 * If issetugid() is missing, always return an error, in order
542		 * to keep from inadvertently opening up the user to a security
543		 * hole.
544		 */
545		return (-1);
546#endif
547	}
548	if (fp == NULL)
549		fp = fopen(fname, "r");
550	if (fp == NULL)
551		return (-1);
552
553	while ((ptr = fgetln(fp, &len)) != NULL) {
554		if (len > 0 && ptr[len - 1] == '\n')
555			--len;
556		ptr[len] = '\0';
557
558		/* loop until first non-space char or EOL */
559		while (*ptr != '\0' && isspace((unsigned char)*ptr))
560			ptr++;
561		if (*ptr == '#')
562			continue;   /* ignore, this is a comment line */
563
564		if (parse_line(el, ptr) == -1) {
565			(void) fclose(fp);
566			return (-1);
567		}
568	}
569
570	(void) fclose(fp);
571	return (0);
572}
573
574
575/* el_resize():
576 *	Called from program when terminal is resized
577 */
578public void
579el_resize(EditLine *el)
580{
581	int lins, cols;
582	sigset_t oset, nset;
583
584	(void) sigemptyset(&nset);
585	(void) sigaddset(&nset, SIGWINCH);
586	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
587
588	/* get the correct window size */
589	if (term_get_size(el, &lins, &cols))
590		term_change_size(el, lins, cols);
591
592	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
593}
594
595
596/* el_beep():
597 *	Called from the program to beep
598 */
599public void
600el_beep(EditLine *el)
601{
602
603	term_beep(el);
604}
605
606
607/* el_editmode()
608 *	Set the state of EDIT_DISABLED from the `edit' command.
609 */
610protected int
611/*ARGSUSED*/
612el_editmode(EditLine *el, int argc, const char **argv)
613{
614	const char *how;
615
616	if (argv == NULL || argc != 2 || argv[1] == NULL)
617		return (-1);
618
619	how = argv[1];
620	if (strcmp(how, "on") == 0) {
621		el->el_flags &= ~EDIT_DISABLED;
622		tty_rawmode(el);
623	} else if (strcmp(how, "off") == 0) {
624		tty_cookedmode(el);
625		el->el_flags |= EDIT_DISABLED;
626	}
627	else {
628		(void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
629		return (-1);
630	}
631	return (0);
632}
633