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