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