Update libedit from NetBSD.
[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.39 2004/07/08 00:51:36 christos Exp $
34  * $DragonFly: src/lib/libedit/el.c,v 1.5 2005/11/13 11:58:30 corecode 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_infd = fileno(fin);
64         el->el_outfile = fout;
65         el->el_errfile = ferr;
66         if ((el->el_prog = el_strdup(prog)) == NULL) {
67                 el_free(el);
68                 return NULL;
69         }
70
71         /*
72          * Initialize all the modules. Order is important!!!
73          */
74         el->el_flags = 0;
75
76         if (term_init(el) == -1) {
77                 el_free(el->el_prog);
78                 el_free(el);
79                 return NULL;
80         }
81         (void) key_init(el);
82         (void) map_init(el);
83         if (tty_init(el) == -1)
84                 el->el_flags |= NO_TTY;
85         (void) ch_init(el);
86         (void) search_init(el);
87         (void) hist_init(el);
88         (void) prompt_init(el);
89         (void) sig_init(el);
90         (void) read_init(el);
91
92         return (el);
93 }
94
95
96 /* el_end():
97  *      Clean up.
98  */
99 public void
100 el_end(EditLine *el)
101 {
102
103         if (el == NULL)
104                 return;
105
106         el_reset(el);
107
108         term_end(el);
109         key_end(el);
110         map_end(el);
111         tty_end(el);
112         ch_end(el);
113         search_end(el);
114         hist_end(el);
115         prompt_end(el);
116         sig_end(el);
117
118         el_free((ptr_t) el->el_prog);
119         el_free((ptr_t) el);
120 }
121
122
123 /* el_reset():
124  *      Reset the tty and the parser
125  */
126 public void
127 el_reset(EditLine *el)
128 {
129
130         tty_cookedmode(el);
131         ch_reset(el);           /* XXX: Do we want that? */
132 }
133
134
135 /* el_set():
136  *      set the editline parameters
137  */
138 public int
139 el_set(EditLine *el, int op, ...)
140 {
141         va_list va;
142         int rv = 0;
143
144         if (el == NULL)
145                 return (-1);
146         va_start(va, op);
147
148         switch (op) {
149         case EL_PROMPT:
150         case EL_RPROMPT:
151                 rv = prompt_set(el, va_arg(va, el_pfunc_t), op);
152                 break;
153
154         case EL_TERMINAL:
155                 rv = term_set(el, va_arg(va, char *));
156                 break;
157
158         case EL_EDITOR:
159                 rv = map_set_editor(el, va_arg(va, char *));
160                 break;
161
162         case EL_SIGNAL:
163                 if (va_arg(va, int))
164                         el->el_flags |= HANDLE_SIGNALS;
165                 else
166                         el->el_flags &= ~HANDLE_SIGNALS;
167                 break;
168
169         case EL_BIND:
170         case EL_TELLTC:
171         case EL_SETTC:
172         case EL_ECHOTC:
173         case EL_SETTY:
174         {
175                 const char *argv[20];
176                 int i;
177
178                 for (i = 1; i < 20; i++)
179                         if ((argv[i] = va_arg(va, char *)) == NULL)
180                                 break;
181
182                 switch (op) {
183                 case EL_BIND:
184                         argv[0] = "bind";
185                         rv = map_bind(el, i, argv);
186                         break;
187
188                 case EL_TELLTC:
189                         argv[0] = "telltc";
190                         rv = term_telltc(el, i, argv);
191                         break;
192
193                 case EL_SETTC:
194                         argv[0] = "settc";
195                         rv = term_settc(el, i, argv);
196                         break;
197
198                 case EL_ECHOTC:
199                         argv[0] = "echotc";
200                         rv = term_echotc(el, i, argv);
201                         break;
202
203                 case EL_SETTY:
204                         argv[0] = "setty";
205                         rv = tty_stty(el, i, argv);
206                         break;
207
208                 default:
209                         rv = -1;
210                         EL_ABORT((el->el_errfile, "Bad op %d\n", op));
211                         break;
212                 }
213                 break;
214         }
215
216         case EL_ADDFN:
217         {
218                 char *name = va_arg(va, char *);
219                 char *help = va_arg(va, char *);
220                 el_func_t func = va_arg(va, el_func_t);
221
222                 rv = map_addfunc(el, name, help, func);
223                 break;
224         }
225
226         case EL_HIST:
227         {
228                 hist_fun_t func = va_arg(va, hist_fun_t);
229                 ptr_t ptr = va_arg(va, char *);
230
231                 rv = hist_set(el, func, ptr);
232                 break;
233         }
234
235         case EL_EDITMODE:
236                 if (va_arg(va, int))
237                         el->el_flags &= ~EDIT_DISABLED;
238                 else
239                         el->el_flags |= EDIT_DISABLED;
240                 rv = 0;
241                 break;
242
243         case EL_GETCFN:
244         {
245                 el_rfunc_t rc = va_arg(va, el_rfunc_t);
246                 rv = el_read_setfn(el, rc);
247                 break;
248         }
249
250         case EL_CLIENTDATA:
251                 el->el_data = va_arg(va, void *);
252                 break;
253
254         case EL_UNBUFFERED:
255                 rv = va_arg(va, int);
256                 if (rv && !(el->el_flags & UNBUFFERED)) {
257                         el->el_flags |= UNBUFFERED;
258                         read_prepare(el);
259                 } else if (!rv && (el->el_flags & UNBUFFERED)) {
260                         el->el_flags &= ~UNBUFFERED;
261                         read_finish(el);
262                 }
263                 rv = 0;
264                 break;
265
266         case EL_PREP_TERM:
267                 rv = va_arg(va, int);
268                 if (rv)
269                         (void) tty_rawmode(el);
270                 else
271                         (void) tty_cookedmode(el);
272                 rv = 0;
273                 break;
274
275         default:
276                 rv = -1;
277                 break;
278         }
279
280         va_end(va);
281         return (rv);
282 }
283
284
285 /* el_get():
286  *      retrieve the editline parameters
287  */
288 public int
289 el_get(EditLine *el, int op, void *ret)
290 {
291         int rv;
292
293         if (el == NULL || ret == NULL)
294                 return (-1);
295         switch (op) {
296         case EL_PROMPT:
297         case EL_RPROMPT:
298                 rv = prompt_get(el, (void *) &ret, op);
299                 break;
300
301         case EL_EDITOR:
302                 rv = map_get_editor(el, (void *) &ret);
303                 break;
304
305         case EL_SIGNAL:
306                 *((int *) ret) = (el->el_flags & HANDLE_SIGNALS);
307                 rv = 0;
308                 break;
309
310         case EL_EDITMODE:
311                 *((int *) ret) = (!(el->el_flags & EDIT_DISABLED));
312                 rv = 0;
313                 break;
314
315         case EL_TERMINAL:
316                 term_get(el, (const char **)ret);
317                 rv = 0;
318                 break;
319
320 #if 0                           /* XXX */
321         case EL_BIND:
322         case EL_TELLTC:
323         case EL_SETTC:
324         case EL_ECHOTC:
325         case EL_SETTY:
326         {
327                 const char *argv[20];
328                 int i;
329
330                 for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++)
331                         if ((argv[i] = va_arg(va, char *)) == NULL)
332                                 break;
333
334                 switch (op) {
335                 case EL_BIND:
336                         argv[0] = "bind";
337                         rv = map_bind(el, i, argv);
338                         break;
339
340                 case EL_TELLTC:
341                         argv[0] = "telltc";
342                         rv = term_telltc(el, i, argv);
343                         break;
344
345                 case EL_SETTC:
346                         argv[0] = "settc";
347                         rv = term_settc(el, i, argv);
348                         break;
349
350                 case EL_ECHOTC:
351                         argv[0] = "echotc";
352                         rv = term_echotc(el, i, argv);
353                         break;
354
355                 case EL_SETTY:
356                         argv[0] = "setty";
357                         rv = tty_stty(el, i, argv);
358                         break;
359
360                 default:
361                         rv = -1;
362                         EL_ABORT((el->errfile, "Bad op %d\n", op));
363                         break;
364                 }
365                 break;
366         }
367
368         case EL_ADDFN:
369         {
370                 char *name = va_arg(va, char *);
371                 char *help = va_arg(va, char *);
372                 el_func_t func = va_arg(va, el_func_t);
373
374                 rv = map_addfunc(el, name, help, func);
375                 break;
376         }
377
378         case EL_HIST:
379                 {
380                         hist_fun_t func = va_arg(va, hist_fun_t);
381                         ptr_t ptr = va_arg(va, char *);
382                         rv = hist_set(el, func, ptr);
383                 }
384                 break;
385 #endif /* XXX */
386
387         case EL_GETCFN:
388                 *((el_rfunc_t *)ret) = el_read_getfn(el);
389                 rv = 0;
390                 break;
391
392         case EL_CLIENTDATA:
393                 *((void **)ret) = el->el_data;
394                 rv = 0;
395                 break;
396
397         case EL_UNBUFFERED:
398                 *((int *) ret) = (!(el->el_flags & UNBUFFERED));
399                 rv = 0;
400                 break;
401
402         default:
403                 rv = -1;
404         }
405
406         return (rv);
407 }
408
409
410 /* el_line():
411  *      Return editing info
412  */
413 public const LineInfo *
414 el_line(EditLine *el)
415 {
416
417         return (const LineInfo *) (void *) &el->el_line;
418 }
419
420
421 /* el_source():
422  *      Source a file
423  */
424 public int
425 el_source(EditLine *el, const char *fname)
426 {
427         FILE *fp;
428         size_t len;
429         char *ptr;
430
431         fp = NULL;
432         if (fname == NULL) {
433 #ifdef HAVE_ISSETUGID
434                 static const char elpath[] = "/.editrc";
435                 char path[MAXPATHLEN];
436
437                 if (issetugid())
438                         return (-1);
439                 if ((ptr = getenv("HOME")) == NULL)
440                         return (-1);
441                 if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
442                         return (-1);
443                 if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
444                         return (-1);
445                 fname = path;
446 #else
447                 /*
448                  * If issetugid() is missing, always return an error, in order
449                  * to keep from inadvertently opening up the user to a security
450                  * hole.
451                  */
452                 return (-1);
453 #endif
454         }
455         if (fp == NULL)
456                 fp = fopen(fname, "r");
457         if (fp == NULL)
458                 return (-1);
459
460         while ((ptr = fgetln(fp, &len)) != NULL) {
461                 if (len > 0 && ptr[len - 1] == '\n')
462                         --len;
463                 ptr[len] = '\0';
464                 if (parse_line(el, ptr) == -1) {
465                         (void) fclose(fp);
466                         return (-1);
467                 }
468         }
469
470         (void) fclose(fp);
471         return (0);
472 }
473
474
475 /* el_resize():
476  *      Called from program when terminal is resized
477  */
478 public void
479 el_resize(EditLine *el)
480 {
481         int lins, cols;
482         sigset_t oset, nset;
483
484         (void) sigemptyset(&nset);
485         (void) sigaddset(&nset, SIGWINCH);
486         (void) sigprocmask(SIG_BLOCK, &nset, &oset);
487
488         /* get the correct window size */
489         if (term_get_size(el, &lins, &cols))
490                 term_change_size(el, lins, cols);
491
492         (void) sigprocmask(SIG_SETMASK, &oset, NULL);
493 }
494
495
496 /* el_beep():
497  *      Called from the program to beep
498  */
499 public void
500 el_beep(EditLine *el)
501 {
502
503         term_beep(el);
504 }
505
506
507 /* el_editmode()
508  *      Set the state of EDIT_DISABLED from the `edit' command.
509  */
510 protected int
511 /*ARGSUSED*/
512 el_editmode(EditLine *el, int argc, const char **argv)
513 {
514         const char *how;
515
516         if (argv == NULL || argc != 2 || argv[1] == NULL)
517                 return (-1);
518
519         how = argv[1];
520         if (strcmp(how, "on") == 0) {
521                 el->el_flags &= ~EDIT_DISABLED;
522                 tty_rawmode(el);
523         } else if (strcmp(how, "off") == 0) {
524                 tty_cookedmode(el);
525                 el->el_flags |= EDIT_DISABLED;
526         }
527         else {
528                 (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
529                 return (-1);
530         }
531         return (0);
532 }