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