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