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