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