How buggy this little piece of code could be? Repair strnvis() buffersize
[dragonfly.git] / lib / libedit / el.c
1 /*-
2  * Copyright (c) 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Christos Zoulas of Cornell University.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)el.c     8.2 (Berkeley) 1/3/94
33  * $NetBSD: el.c,v 1.45 2008/04/05 15:53:28 christos Exp $
34  * $DragonFly: src/lib/libedit/el.c,v 1.7 2008/05/17 22:48:04 pavalos Exp $
35  */
36
37 #include "config.h"
38
39 /*
40  * el.c: EditLine interface functions
41  */
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include "el.h"
48
49 /* el_init():
50  *      Initialize editline and set default parameters.
51  */
52 public EditLine *
53 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
54 {
55
56         EditLine *el = (EditLine *) el_malloc(sizeof(EditLine));
57
58         if (el == NULL)
59                 return (NULL);
60
61         memset(el, 0, sizeof(EditLine));
62
63         el->el_infile = fin;
64         el->el_outfile = fout;
65         el->el_errfile = ferr;
66
67         el->el_infd = fileno(fin);
68
69         if ((el->el_prog = el_strdup(prog)) == NULL) {
70                 el_free(el);
71                 return NULL;
72         }
73
74         /*
75          * Initialize all the modules. Order is important!!!
76          */
77         el->el_flags = 0;
78
79         if (term_init(el) == -1) {
80                 el_free(el->el_prog);
81                 el_free(el);
82                 return NULL;
83         }
84         (void) key_init(el);
85         (void) map_init(el);
86         if (tty_init(el) == -1)
87                 el->el_flags |= NO_TTY;
88         (void) ch_init(el);
89         (void) search_init(el);
90         (void) hist_init(el);
91         (void) prompt_init(el);
92         (void) sig_init(el);
93         (void) read_init(el);
94
95         return (el);
96 }
97
98
99 /* el_end():
100  *      Clean up.
101  */
102 public void
103 el_end(EditLine *el)
104 {
105
106         if (el == NULL)
107                 return;
108
109         el_reset(el);
110
111         term_end(el);
112         key_end(el);
113         map_end(el);
114         tty_end(el);
115         ch_end(el);
116         search_end(el);
117         hist_end(el);
118         prompt_end(el);
119         sig_end(el);
120
121         el_free((ptr_t) el->el_prog);
122         el_free((ptr_t) el);
123 }
124
125
126 /* el_reset():
127  *      Reset the tty and the parser
128  */
129 public void
130 el_reset(EditLine *el)
131 {
132
133         tty_cookedmode(el);
134         ch_reset(el, 0);                /* XXX: Do we want that? */
135 }
136
137
138 /* el_set():
139  *      set the editline parameters
140  */
141 public int
142 el_set(EditLine *el, int op, ...)
143 {
144         va_list ap;
145         int rv = 0;
146
147         if (el == NULL)
148                 return (-1);
149         va_start(ap, op);
150
151         switch (op) {
152         case EL_PROMPT:
153         case EL_RPROMPT:
154                 rv = prompt_set(el, va_arg(ap, el_pfunc_t), op);
155                 break;
156
157         case EL_TERMINAL:
158                 rv = term_set(el, va_arg(ap, char *));
159                 break;
160
161         case EL_EDITOR:
162                 rv = map_set_editor(el, va_arg(ap, char *));
163                 break;
164
165         case EL_SIGNAL:
166                 if (va_arg(ap, int))
167                         el->el_flags |= HANDLE_SIGNALS;
168                 else
169                         el->el_flags &= ~HANDLE_SIGNALS;
170                 break;
171
172         case EL_BIND:
173         case EL_TELLTC:
174         case EL_SETTC:
175         case EL_GETTC:
176         case EL_ECHOTC:
177         case EL_SETTY:
178         {
179                 const char *argv[20];
180                 int i;
181
182                 for (i = 1; i < 20; i++)
183                         if ((argv[i] = va_arg(ap, char *)) == NULL)
184                                 break;
185
186                 switch (op) {
187                 case EL_BIND:
188                         argv[0] = "bind";
189                         rv = map_bind(el, i, argv);
190                         break;
191
192                 case EL_TELLTC:
193                         argv[0] = "telltc";
194                         rv = term_telltc(el, i, argv);
195                         break;
196
197                 case EL_SETTC:
198                         argv[0] = "settc";
199                         rv = term_settc(el, i, argv);
200                         break;
201
202                 case EL_ECHOTC:
203                         argv[0] = "echotc";
204                         rv = term_echotc(el, i, argv);
205                         break;
206
207                 case EL_SETTY:
208                         argv[0] = "setty";
209                         rv = tty_stty(el, i, argv);
210                         break;
211
212                 default:
213                         rv = -1;
214                         EL_ABORT((el->el_errfile, "Bad op %d\n", op));
215                         break;
216                 }
217                 break;
218         }
219
220         case EL_ADDFN:
221         {
222                 char *name = va_arg(ap, char *);
223                 char *help = va_arg(ap, char *);
224                 el_func_t func = va_arg(ap, el_func_t);
225
226                 rv = map_addfunc(el, name, help, func);
227                 break;
228         }
229
230         case EL_HIST:
231         {
232                 hist_fun_t func = va_arg(ap, hist_fun_t);
233                 ptr_t ptr = va_arg(ap, char *);
234
235                 rv = hist_set(el, func, ptr);
236                 break;
237         }
238
239         case EL_EDITMODE:
240                 if (va_arg(ap, int))
241                         el->el_flags &= ~EDIT_DISABLED;
242                 else
243                         el->el_flags |= EDIT_DISABLED;
244                 rv = 0;
245                 break;
246
247         case EL_GETCFN:
248         {
249                 el_rfunc_t rc = va_arg(ap, el_rfunc_t);
250                 rv = el_read_setfn(el, rc);
251                 break;
252         }
253
254         case EL_CLIENTDATA:
255                 el->el_data = va_arg(ap, void *);
256                 break;
257
258         case EL_UNBUFFERED:
259                 rv = va_arg(ap, int);
260                 if (rv && !(el->el_flags & UNBUFFERED)) {
261                         el->el_flags |= UNBUFFERED;
262                         read_prepare(el);
263                 } else if (!rv && (el->el_flags & UNBUFFERED)) {
264                         el->el_flags &= ~UNBUFFERED;
265                         read_finish(el);
266                 }
267                 rv = 0;
268                 break;
269
270         case EL_PREP_TERM:
271                 rv = va_arg(ap, int);
272                 if (rv)
273                         (void) tty_rawmode(el);
274                 else
275                         (void) tty_cookedmode(el);
276                 rv = 0;
277                 break;
278
279         case EL_SETFP:
280         {
281                 FILE *fp;
282                 int what;
283
284                 what = va_arg(ap, int);
285                 fp = va_arg(ap, FILE *);
286
287                 rv = 0;
288                 switch (what) {
289                 case 0:
290                         el->el_infile = fp;
291                         el->el_infd = fileno(fp);
292                         break;
293                 case 1:
294                         el->el_outfile = fp;
295                         break;
296                 case 2:
297                         el->el_errfile = fp;
298                         break;
299                 default:
300                         rv = -1;
301                         break;
302                 }
303                 break;
304         }
305
306         case EL_REFRESH:
307                 re_clear_display(el);
308                 re_refresh(el);
309                 term__flush();
310                 break;
311
312         default:
313                 rv = -1;
314                 break;
315         }
316
317         va_end(ap);
318         return (rv);
319 }
320
321
322 /* el_get():
323  *      retrieve the editline parameters
324  */
325 public int
326 el_get(EditLine *el, int op, ...)
327 {
328         va_list ap;
329         int rv;
330
331         if (el == NULL)
332                 return -1;
333
334         va_start(ap, op);
335
336         switch (op) {
337         case EL_PROMPT:
338         case EL_RPROMPT:
339                 rv = prompt_get(el, va_arg(ap, el_pfunc_t *), op);
340                 break;
341
342         case EL_EDITOR:
343                 rv = map_get_editor(el, va_arg(ap, const char **));
344                 break;
345
346         case EL_SIGNAL:
347                 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS);
348                 rv = 0;
349                 break;
350
351         case EL_EDITMODE:
352                 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED);
353                 rv = 0;
354                 break;
355
356         case EL_TERMINAL:
357                 term_get(el, va_arg(ap, const char **));
358                 rv = 0;
359                 break;
360
361         case EL_GETTC:
362         {
363                 static char name[] = "gettc";
364                 char *argv[20];
365                 int i;
366
367                 for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++)
368                         if ((argv[i] = va_arg(ap, char *)) == NULL)
369                                 break;
370
371                 switch (op) {
372                 case EL_GETTC:
373                         argv[0] = name;
374                         rv = term_gettc(el, i, argv);
375                         break;
376
377                 default:
378                         rv = -1;
379                         EL_ABORT((el->el_errfile, "Bad op %d\n", op));
380                         break;
381                 }
382                 break;
383         }
384
385 #if 0 /* XXX */
386         case EL_ADDFN:
387         {
388                 char *name = va_arg(ap, char *);
389                 char *help = va_arg(ap, char *);
390                 el_func_t func = va_arg(ap, el_func_t);
391
392                 rv = map_addfunc(el, name, help, func);
393                 break;
394         }
395
396         case EL_HIST:
397                 {
398                         hist_fun_t func = va_arg(ap, hist_fun_t);
399                         ptr_t ptr = va_arg(ap, char *);
400                         rv = hist_set(el, func, ptr);
401                 }
402                 break;
403 #endif /* XXX */
404
405         case EL_GETCFN:
406                 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
407                 rv = 0;
408                 break;
409
410         case EL_CLIENTDATA:
411                 *va_arg(ap, void **) = el->el_data;
412                 rv = 0;
413                 break;
414
415         case EL_UNBUFFERED:
416                 *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
417                 rv = 0;
418                 break;
419
420         case EL_GETFP:
421         {
422                 int what;
423                 FILE **fpp;
424
425                 what = va_arg(ap, int);
426                 fpp = va_arg(ap, FILE **);
427                 rv = 0;
428                 switch (what) {
429                 case 0:
430                         *fpp = el->el_infile;
431                         break;
432                 case 1:
433                         *fpp = el->el_outfile;
434                         break;
435                 case 2:
436                         *fpp = el->el_errfile;
437                         break;
438                 default:
439                         rv = -1;
440                         break;
441                 }
442                 break;
443         }
444         default:
445                 rv = -1;
446                 break;
447         }
448         va_end(ap);
449
450         return (rv);
451 }
452
453
454 /* el_line():
455  *      Return editing info
456  */
457 public const LineInfo *
458 el_line(EditLine *el)
459 {
460
461         return (const LineInfo *) (void *) &el->el_line;
462 }
463
464
465 /* el_source():
466  *      Source a file
467  */
468 public int
469 el_source(EditLine *el, const char *fname)
470 {
471         FILE *fp;
472         size_t len;
473         char *ptr;
474
475         fp = NULL;
476         if (fname == NULL) {
477 #ifdef HAVE_ISSETUGID
478                 static const char elpath[] = "/.editrc";
479                 char path[MAXPATHLEN];
480
481                 if (issetugid())
482                         return (-1);
483                 if ((ptr = getenv("HOME")) == NULL)
484                         return (-1);
485                 if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
486                         return (-1);
487                 if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
488                         return (-1);
489                 fname = path;
490 #else
491                 /*
492                  * If issetugid() is missing, always return an error, in order
493                  * to keep from inadvertently opening up the user to a security
494                  * hole.
495                  */
496                 return (-1);
497 #endif
498         }
499         if (fp == NULL)
500                 fp = fopen(fname, "r");
501         if (fp == NULL)
502                 return (-1);
503
504         while ((ptr = fgetln(fp, &len)) != NULL) {
505                 if (len > 0 && ptr[len - 1] == '\n')
506                         --len;
507                 ptr[len] = '\0';
508                 if (parse_line(el, ptr) == -1) {
509                         (void) fclose(fp);
510                         return (-1);
511                 }
512         }
513
514         (void) fclose(fp);
515         return (0);
516 }
517
518
519 /* el_resize():
520  *      Called from program when terminal is resized
521  */
522 public void
523 el_resize(EditLine *el)
524 {
525         int lins, cols;
526         sigset_t oset, nset;
527
528         (void) sigemptyset(&nset);
529         (void) sigaddset(&nset, SIGWINCH);
530         (void) sigprocmask(SIG_BLOCK, &nset, &oset);
531
532         /* get the correct window size */
533         if (term_get_size(el, &lins, &cols))
534                 term_change_size(el, lins, cols);
535
536         (void) sigprocmask(SIG_SETMASK, &oset, NULL);
537 }
538
539
540 /* el_beep():
541  *      Called from the program to beep
542  */
543 public void
544 el_beep(EditLine *el)
545 {
546
547         term_beep(el);
548 }
549
550
551 /* el_editmode()
552  *      Set the state of EDIT_DISABLED from the `edit' command.
553  */
554 protected int
555 /*ARGSUSED*/
556 el_editmode(EditLine *el, int argc, const char **argv)
557 {
558         const char *how;
559
560         if (argv == NULL || argc != 2 || argv[1] == NULL)
561                 return (-1);
562
563         how = argv[1];
564         if (strcmp(how, "on") == 0) {
565                 el->el_flags &= ~EDIT_DISABLED;
566                 tty_rawmode(el);
567         } else if (strcmp(how, "off") == 0) {
568                 tty_cookedmode(el);
569                 el->el_flags |= EDIT_DISABLED;
570         }
571         else {
572                 (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
573                 return (-1);
574         }
575         return (0);
576 }