1/*	$NetBSD: el.c,v 1.71 2012/09/11 11:58:53 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
38static char sccsid[] = "@(#)el.c	8.2 (Berkeley) 1/3/94";
39#else
40__RCSID("$NetBSD: el.c,v 1.71 2012/09/11 11:58:53 christos Exp $");
41#endif
42#endif /* not lint && not SCCSID */
43
44#ifndef MAXPATHLEN
45#define MAXPATHLEN 4096
46#endif
47
48/*
49 * el.c: EditLine interface functions
50 */
51#include <sys/types.h>
52#include <sys/param.h>
53#include <string.h>
54#include <stdlib.h>
55#include <stdarg.h>
56#include <ctype.h>
57#include <locale.h>
58#include <langinfo.h>
59#include "el.h"
60
61/* el_init():
62 *	Initialize editline and set default parameters.
63 */
64public EditLine *
65el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
66{
67	EditLine *el = el_malloc(sizeof(*el));
68
69	if (el == NULL)
70		return NULL;
71
72	memset(el, 0, sizeof(EditLine));
73
74	el->el_infile = fin;
75	el->el_outfile = fout;
76	el->el_errfile = ferr;
77
78	el->el_infd = fileno(fin);
79	el->el_outfd = fileno(fout);
80	el->el_errfd = fileno(ferr);
81
82	el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch));
83	if (el->el_prog == NULL) {
84		el_free(el);
85		return NULL;
86	}
87
88	/*
89         * Initialize all the modules. Order is important!!!
90         */
91	el->el_flags = 0;
92#ifdef WIDECHAR
93	char *loc = setlocale(LC_CTYPE, NULL);
94	if (loc != NULL && strcmp(loc, "C") == 0)
95		loc = setlocale(LC_CTYPE, "");
96	if (loc != NULL) {
97		if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
98			el->el_flags |= CHARSET_IS_UTF8;
99	}
100#endif
101
102	if (terminal_init(el) == -1) {
103		el_free(el->el_prog);
104		el_free(el);
105		return NULL;
106	}
107	(void) keymacro_init(el);
108	(void) map_init(el);
109	if (tty_init(el) == -1)
110		el->el_flags |= NO_TTY;
111	(void) ch_init(el);
112	(void) search_init(el);
113	(void) hist_init(el);
114	(void) prompt_init(el);
115	(void) sig_init(el);
116	(void) read_init(el);
117
118	return el;
119}
120
121
122/* el_end():
123 *	Clean up.
124 */
125public void
126el_end(EditLine *el)
127{
128
129	if (el == NULL)
130		return;
131
132	el_reset(el);
133
134	terminal_end(el);
135	keymacro_end(el);
136	map_end(el);
137	tty_end(el);
138	ch_end(el);
139	search_end(el);
140	hist_end(el);
141	prompt_end(el);
142	sig_end(el);
143
144	el_free(el->el_prog);
145#ifdef WIDECHAR
146	el_free(el->el_scratch.cbuff);
147	el_free(el->el_scratch.wbuff);
148	el_free(el->el_lgcyconv.cbuff);
149	el_free(el->el_lgcyconv.wbuff);
150#endif
151	el_free(el);
152}
153
154
155/* el_reset():
156 *	Reset the tty and the parser
157 */
158public void
159el_reset(EditLine *el)
160{
161
162	tty_cookedmode(el);
163	ch_reset(el, 0);		/* XXX: Do we want that? */
164}
165
166
167/* el_set():
168 *	set the editline parameters
169 */
170public int
171FUN(el,set)(EditLine *el, int op, ...)
172{
173	va_list ap;
174	int rv = 0;
175
176	if (el == NULL)
177		return -1;
178	va_start(ap, op);
179
180	switch (op) {
181	case EL_PROMPT:
182	case EL_RPROMPT: {
183		el_pfunc_t p = va_arg(ap, el_pfunc_t);
184
185		rv = prompt_set(el, p, 0, op, 1);
186		break;
187	}
188
189	case EL_RESIZE: {
190		el_zfunc_t p = va_arg(ap, el_zfunc_t);
191		void *arg = va_arg(ap, void *);
192		rv = ch_resizefun(el, p, arg);
193		break;
194	}
195
196	case EL_PROMPT_ESC:
197	case EL_RPROMPT_ESC: {
198		el_pfunc_t p = va_arg(ap, el_pfunc_t);
199		int c = va_arg(ap, int);
200
201		rv = prompt_set(el, p, c, op, 1);
202		break;
203	}
204
205	case EL_TERMINAL:
206		rv = terminal_set(el, va_arg(ap, char *));
207		break;
208
209	case EL_EDITOR:
210		rv = map_set_editor(el, va_arg(ap, Char *));
211		break;
212
213	case EL_SIGNAL:
214		if (va_arg(ap, int))
215			el->el_flags |= HANDLE_SIGNALS;
216		else
217			el->el_flags &= ~HANDLE_SIGNALS;
218		break;
219
220	case EL_BIND:
221	case EL_TELLTC:
222	case EL_SETTC:
223	case EL_ECHOTC:
224	case EL_SETTY:
225	{
226		const Char *argv[20];
227		int i;
228
229		for (i = 1; i < (int)__arraycount(argv); i++)
230			if ((argv[i] = va_arg(ap, Char *)) == NULL)
231				break;
232
233		switch (op) {
234		case EL_BIND:
235			argv[0] = STR("bind");
236			rv = map_bind(el, i, argv);
237			break;
238
239		case EL_TELLTC:
240			argv[0] = STR("telltc");
241			rv = terminal_telltc(el, i, argv);
242			break;
243
244		case EL_SETTC:
245			argv[0] = STR("settc");
246			rv = terminal_settc(el, i, argv);
247			break;
248
249		case EL_ECHOTC:
250			argv[0] = STR("echotc");
251			rv = terminal_echotc(el, i, argv);
252			break;
253
254		case EL_SETTY:
255			argv[0] = STR("setty");
256			rv = tty_stty(el, i, argv);
257			break;
258
259		default:
260			rv = -1;
261			EL_ABORT((el->el_errfile, "Bad op %d\n", op));
262			break;
263		}
264		break;
265	}
266
267	case EL_ADDFN:
268	{
269		Char *name = va_arg(ap, Char *);
270		Char *help = va_arg(ap, Char *);
271		el_func_t func = va_arg(ap, el_func_t);
272
273		rv = map_addfunc(el, name, help, func);
274		break;
275	}
276
277	case EL_HIST:
278	{
279		hist_fun_t func = va_arg(ap, hist_fun_t);
280		void *ptr = va_arg(ap, void *);
281
282		rv = hist_set(el, func, ptr);
283		el->el_flags &= ~NARROW_HISTORY;
284		break;
285	}
286
287	case EL_EDITMODE:
288		if (va_arg(ap, int))
289			el->el_flags &= ~EDIT_DISABLED;
290		else
291			el->el_flags |= EDIT_DISABLED;
292		rv = 0;
293		break;
294
295	case EL_GETCFN:
296	{
297		el_rfunc_t rc = va_arg(ap, el_rfunc_t);
298		rv = el_read_setfn(el, rc);
299		el->el_flags &= ~NARROW_READ;
300		break;
301	}
302
303	case EL_CLIENTDATA:
304		el->el_data = va_arg(ap, void *);
305		break;
306
307	case EL_UNBUFFERED:
308		rv = va_arg(ap, int);
309		if (rv && !(el->el_flags & UNBUFFERED)) {
310			el->el_flags |= UNBUFFERED;
311			read_prepare(el);
312		} else if (!rv && (el->el_flags & UNBUFFERED)) {
313			el->el_flags &= ~UNBUFFERED;
314			read_finish(el);
315		}
316		rv = 0;
317		break;
318
319	case EL_PREP_TERM:
320		rv = va_arg(ap, int);
321		if (rv)
322			(void) tty_rawmode(el);
323		else
324			(void) tty_cookedmode(el);
325		rv = 0;
326		break;
327
328	case EL_SETFP:
329	{
330		FILE *fp;
331		int what;
332
333		what = va_arg(ap, int);
334		fp = va_arg(ap, FILE *);
335
336		rv = 0;
337		switch (what) {
338		case 0:
339			el->el_infile = fp;
340			el->el_infd = fileno(fp);
341			break;
342		case 1:
343			el->el_outfile = fp;
344			el->el_outfd = fileno(fp);
345			break;
346		case 2:
347			el->el_errfile = fp;
348			el->el_errfd = fileno(fp);
349			break;
350		default:
351			rv = -1;
352			break;
353		}
354		break;
355	}
356
357	case EL_REFRESH:
358		re_clear_display(el);
359		re_refresh(el);
360		terminal__flush(el);
361		break;
362
363	default:
364		rv = -1;
365		break;
366	}
367
368	va_end(ap);
369	return rv;
370}
371
372
373/* el_get():
374 *	retrieve the editline parameters
375 */
376public int
377FUN(el,get)(EditLine *el, int op, ...)
378{
379	va_list ap;
380	int rv;
381
382	if (el == NULL)
383		return -1;
384
385	va_start(ap, op);
386
387	switch (op) {
388	case EL_PROMPT:
389	case EL_RPROMPT: {
390		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
391		rv = prompt_get(el, p, 0, op);
392		break;
393	}
394	case EL_PROMPT_ESC:
395	case EL_RPROMPT_ESC: {
396		el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
397		Char *c = va_arg(ap, Char *);
398
399		rv = prompt_get(el, p, c, op);
400		break;
401	}
402
403	case EL_EDITOR:
404		rv = map_get_editor(el, va_arg(ap, const Char **));
405		break;
406
407	case EL_SIGNAL:
408		*va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
409		rv = 0;
410		break;
411
412	case EL_EDITMODE:
413		*va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
414		rv = 0;
415		break;
416
417	case EL_TERMINAL:
418		terminal_get(el, va_arg(ap, const char **));
419		rv = 0;
420		break;
421
422	case EL_GETTC:
423	{
424		static char name[] = "gettc";
425		char *argv[20];
426		int i;
427
428 		for (i = 1; i < (int)__arraycount(argv); i++)
429			if ((argv[i] = va_arg(ap, char *)) == NULL)
430				break;
431
432		argv[0] = name;
433		rv = terminal_gettc(el, i, argv);
434		break;
435	}
436
437	case EL_GETCFN:
438		*va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
439		rv = 0;
440		break;
441
442	case EL_CLIENTDATA:
443		*va_arg(ap, void **) = el->el_data;
444		rv = 0;
445		break;
446
447	case EL_UNBUFFERED:
448		*va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0;
449		rv = 0;
450		break;
451
452	case EL_GETFP:
453	{
454		int what;
455		FILE **fpp;
456
457		what = va_arg(ap, int);
458		fpp = va_arg(ap, FILE **);
459		rv = 0;
460		switch (what) {
461		case 0:
462			*fpp = el->el_infile;
463			break;
464		case 1:
465			*fpp = el->el_outfile;
466			break;
467		case 2:
468			*fpp = el->el_errfile;
469			break;
470		default:
471			rv = -1;
472			break;
473		}
474		break;
475	}
476	default:
477		rv = -1;
478		break;
479	}
480	va_end(ap);
481
482	return rv;
483}
484
485
486/* el_line():
487 *	Return editing info
488 */
489public const TYPE(LineInfo) *
490FUN(el,line)(EditLine *el)
491{
492
493	return (const TYPE(LineInfo) *)(void *)&el->el_line;
494}
495
496
497/* el_source():
498 *	Source a file
499 */
500public int
501el_source(EditLine *el, const char *fname)
502{
503	FILE *fp;
504	size_t len;
505	char *ptr;
506	char *path = NULL;
507	const Char *dptr;
508	int error = 0;
509
510	fp = NULL;
511	if (fname == NULL) {
512		static const char elpath[] = "/.editrc";
513		size_t plen = sizeof(elpath);
514
515#ifdef HAVE_ISSETUGID
516		if (issetugid())
517			return -1;
518		if ((ptr = getenv("HOME")) == NULL)
519			return -1;
520		plen += strlen(ptr);
521		if ((path = el_malloc(plen * sizeof(*path))) == NULL)
522			return -1;
523		(void)snprintf(path, plen, "%s%s", ptr, elpath);
524		fname = path;
525#else
526		/*
527		 * If issetugid() is missing, always return an error, in order
528		 * to keep from inadvertently opening up the user to a security
529		 * hole.
530		 */
531		return -1;
532#endif
533	}
534	if (fp == NULL)
535		fp = fopen(fname, "r");
536	if (fp == NULL) {
537		el_free(path);
538		return -1;
539	}
540
541	while ((ptr = fgetln(fp, &len)) != NULL) {
542		if (*ptr == '\n')
543			continue;	/* Empty line. */
544		dptr = ct_decode_string(ptr, &el->el_scratch);
545		if (!dptr)
546			continue;
547		if (len > 0 && dptr[len - 1] == '\n')
548			--len;
549
550		/* loop until first non-space char or EOL */
551		while (*dptr != '\0' && Isspace(*dptr))
552			dptr++;
553		if (*dptr == '#')
554			continue;   /* ignore, this is a comment line */
555		if ((error = parse_line(el, dptr)) == -1)
556			break;
557	}
558
559	el_free(path);
560	(void) fclose(fp);
561	return error;
562}
563
564
565/* el_resize():
566 *	Called from program when terminal is resized
567 */
568public void
569el_resize(EditLine *el)
570{
571	int lins, cols;
572	sigset_t oset, nset;
573
574	(void) sigemptyset(&nset);
575	(void) sigaddset(&nset, SIGWINCH);
576	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
577
578	/* get the correct window size */
579	if (terminal_get_size(el, &lins, &cols))
580		terminal_change_size(el, lins, cols);
581
582	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
583}
584
585
586/* el_beep():
587 *	Called from the program to beep
588 */
589public void
590el_beep(EditLine *el)
591{
592
593	terminal_beep(el);
594}
595
596
597/* el_editmode()
598 *	Set the state of EDIT_DISABLED from the `edit' command.
599 */
600protected int
601/*ARGSUSED*/
602el_editmode(EditLine *el, int argc, const Char **argv)
603{
604	const Char *how;
605
606	if (argv == NULL || argc != 2 || argv[1] == NULL)
607		return -1;
608
609	how = argv[1];
610	if (Strcmp(how, STR("on")) == 0) {
611		el->el_flags &= ~EDIT_DISABLED;
612		tty_rawmode(el);
613	} else if (Strcmp(how, STR("off")) == 0) {
614		tty_cookedmode(el);
615		el->el_flags |= EDIT_DISABLED;
616	}
617	else {
618		(void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n",
619		    how);
620		return -1;
621	}
622	return 0;
623}
624