1 /* $NetBSD: el.c,v 1.101 2022/10/30 19:11:31 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.101 2022/10/30 19:11:31 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 *
el_init(const char * prog,FILE * fin,FILE * fout,FILE * ferr)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 *
el_init_internal(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr,int flags)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 *
el_init_fd(const char * prog,FILE * fin,FILE * fout,FILE * ferr,int fdin,int fdout,int fderr)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
el_end(EditLine * el)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);
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
el_reset(EditLine * el)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
el_wset(EditLine * el,int op,...)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_SAFEREAD:
304 if (va_arg(ap, int))
305 el->el_flags |= FIXIO;
306 else
307 el->el_flags &= ~FIXIO;
308 rv = 0;
309 break;
310
311 case EL_EDITMODE:
312 if (va_arg(ap, int))
313 el->el_flags &= ~EDIT_DISABLED;
314 else
315 el->el_flags |= EDIT_DISABLED;
316 rv = 0;
317 break;
318
319 case EL_GETCFN:
320 {
321 el_rfunc_t rc = va_arg(ap, el_rfunc_t);
322 rv = el_read_setfn(el->el_read, rc);
323 break;
324 }
325
326 case EL_CLIENTDATA:
327 el->el_data = va_arg(ap, void *);
328 break;
329
330 case EL_UNBUFFERED:
331 rv = va_arg(ap, int);
332 if (rv && !(el->el_flags & UNBUFFERED)) {
333 el->el_flags |= UNBUFFERED;
334 read_prepare(el);
335 } else if (!rv && (el->el_flags & UNBUFFERED)) {
336 el->el_flags &= ~UNBUFFERED;
337 read_finish(el);
338 }
339 rv = 0;
340 break;
341
342 case EL_PREP_TERM:
343 rv = va_arg(ap, int);
344 if (rv)
345 (void) tty_rawmode(el);
346 else
347 (void) tty_cookedmode(el);
348 rv = 0;
349 break;
350
351 case EL_SETFP:
352 {
353 FILE *fp;
354 int what;
355
356 what = va_arg(ap, int);
357 fp = va_arg(ap, FILE *);
358
359 rv = 0;
360 switch (what) {
361 case 0:
362 el->el_infile = fp;
363 el->el_infd = fileno(fp);
364 break;
365 case 1:
366 el->el_outfile = fp;
367 el->el_outfd = fileno(fp);
368 break;
369 case 2:
370 el->el_errfile = fp;
371 el->el_errfd = fileno(fp);
372 break;
373 default:
374 rv = -1;
375 break;
376 }
377 break;
378 }
379
380 case EL_REFRESH:
381 re_clear_display(el);
382 re_refresh(el);
383 terminal__flush(el);
384 break;
385
386 default:
387 rv = -1;
388 break;
389 }
390
391 va_end(ap);
392 return rv;
393 }
394
395
396 /* el_get():
397 * retrieve the editline parameters
398 */
399 int
el_wget(EditLine * el,int op,...)400 el_wget(EditLine *el, int op, ...)
401 {
402 va_list ap;
403 int rv;
404
405 if (el == NULL)
406 return -1;
407
408 va_start(ap, op);
409
410 switch (op) {
411 case EL_PROMPT:
412 case EL_RPROMPT: {
413 el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
414 rv = prompt_get(el, p, 0, op);
415 break;
416 }
417 case EL_PROMPT_ESC:
418 case EL_RPROMPT_ESC: {
419 el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
420 wchar_t *c = va_arg(ap, wchar_t *);
421
422 rv = prompt_get(el, p, c, op);
423 break;
424 }
425
426 case EL_EDITOR:
427 rv = map_get_editor(el, va_arg(ap, const wchar_t **));
428 break;
429
430 case EL_SIGNAL:
431 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
432 rv = 0;
433 break;
434
435 case EL_EDITMODE:
436 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
437 rv = 0;
438 break;
439
440 case EL_SAFEREAD:
441 *va_arg(ap, int *) = (el->el_flags & FIXIO);
442 rv = 0;
443 break;
444
445 case EL_TERMINAL:
446 terminal_get(el, va_arg(ap, const char **));
447 rv = 0;
448 break;
449
450 case EL_GETTC:
451 {
452 static char name[] = "gettc";
453 char *argv[3];
454 argv[0] = name;
455 argv[1] = va_arg(ap, char *);
456 argv[2] = va_arg(ap, void *);
457 rv = terminal_gettc(el, 3, argv);
458 break;
459 }
460
461 case EL_GETCFN:
462 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read);
463 rv = 0;
464 break;
465
466 case EL_CLIENTDATA:
467 *va_arg(ap, void **) = el->el_data;
468 rv = 0;
469 break;
470
471 case EL_UNBUFFERED:
472 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0;
473 rv = 0;
474 break;
475
476 case EL_GETFP:
477 {
478 int what;
479 FILE **fpp;
480
481 what = va_arg(ap, int);
482 fpp = va_arg(ap, FILE **);
483 rv = 0;
484 switch (what) {
485 case 0:
486 *fpp = el->el_infile;
487 break;
488 case 1:
489 *fpp = el->el_outfile;
490 break;
491 case 2:
492 *fpp = el->el_errfile;
493 break;
494 default:
495 rv = -1;
496 break;
497 }
498 break;
499 }
500 default:
501 rv = -1;
502 break;
503 }
504 va_end(ap);
505
506 return rv;
507 }
508
509
510 /* el_line():
511 * Return editing info
512 */
513 const LineInfoW *
el_wline(EditLine * el)514 el_wline(EditLine *el)
515 {
516
517 return (const LineInfoW *)(void *)&el->el_line;
518 }
519
520
521 /* el_source():
522 * Source a file
523 */
524 int
el_source(EditLine * el,const char * fname)525 el_source(EditLine *el, const char *fname)
526 {
527 FILE *fp;
528 size_t len;
529 ssize_t slen;
530 char *ptr;
531 char *path = NULL;
532 const wchar_t *dptr;
533 int error = 0;
534
535 fp = NULL;
536 if (fname == NULL) {
537 #ifdef HAVE_ISSETUGID
538 if (issetugid())
539 return -1;
540
541 if ((fname = getenv("EDITRC")) == NULL) {
542 static const char elpath[] = "/.editrc";
543 size_t plen = sizeof(elpath);
544
545 if ((ptr = getenv("HOME")) == NULL)
546 return -1;
547 plen += strlen(ptr);
548 if ((path = el_calloc(plen, sizeof(*path))) == NULL)
549 return -1;
550 (void)snprintf(path, plen, "%s%s", ptr,
551 elpath + (*ptr == '\0'));
552 fname = path;
553 }
554 #else
555 /*
556 * If issetugid() is missing, always return an error, in order
557 * to keep from inadvertently opening up the user to a security
558 * hole.
559 */
560 return -1;
561 #endif
562 }
563 if (fname[0] == '\0')
564 return -1;
565
566 if (fp == NULL)
567 fp = fopen(fname, "r");
568 if (fp == NULL) {
569 el_free(path);
570 return -1;
571 }
572
573 ptr = NULL;
574 len = 0;
575 while ((slen = getline(&ptr, &len, fp)) != -1) {
576 if (*ptr == '\n')
577 continue; /* Empty line. */
578 if (slen > 0 && ptr[--slen] == '\n')
579 ptr[slen] = '\0';
580
581 dptr = ct_decode_string(ptr, &el->el_scratch);
582 if (!dptr)
583 continue;
584 /* loop until first non-space char or EOL */
585 while (*dptr != '\0' && iswspace(*dptr))
586 dptr++;
587 if (*dptr == '#')
588 continue; /* ignore, this is a comment line */
589 if ((error = parse_line(el, dptr)) == -1)
590 break;
591 }
592 free(ptr);
593
594 el_free(path);
595 (void) fclose(fp);
596 return error;
597 }
598
599
600 /* el_resize():
601 * Called from program when terminal is resized
602 */
603 void
el_resize(EditLine * el)604 el_resize(EditLine *el)
605 {
606 int lins, cols;
607 sigset_t oset, nset;
608
609 (void) sigemptyset(&nset);
610 (void) sigaddset(&nset, SIGWINCH);
611 (void) sigprocmask(SIG_BLOCK, &nset, &oset);
612
613 /* get the correct window size */
614 if (terminal_get_size(el, &lins, &cols))
615 terminal_change_size(el, lins, cols);
616
617 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
618 }
619
620
621 /* el_beep():
622 * Called from the program to beep
623 */
624 void
el_beep(EditLine * el)625 el_beep(EditLine *el)
626 {
627
628 terminal_beep(el);
629 }
630
631
632 /* el_editmode()
633 * Set the state of EDIT_DISABLED from the `edit' command.
634 */
635 libedit_private int
636 /*ARGSUSED*/
el_editmode(EditLine * el,int argc,const wchar_t ** argv)637 el_editmode(EditLine *el, int argc, const wchar_t **argv)
638 {
639 const wchar_t *how;
640
641 if (argv == NULL || argc != 2 || argv[1] == NULL)
642 return -1;
643
644 how = argv[1];
645 if (wcscmp(how, L"on") == 0) {
646 el->el_flags &= ~EDIT_DISABLED;
647 tty_rawmode(el);
648 } else if (wcscmp(how, L"off") == 0) {
649 tty_cookedmode(el);
650 el->el_flags |= EDIT_DISABLED;
651 }
652 else {
653 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n",
654 how);
655 return -1;
656 }
657 return 0;
658 }
659