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