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