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