]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libedit/el.c
zfs: merge openzfs/zfs@ad0a55461
[FreeBSD/FreeBSD.git] / contrib / libedit / el.c
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 *
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);
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_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
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 *
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
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
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
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*/
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 }