1 /*
2  * Copyright (c) 1992, 1993  Simmule Turner and Rich Salz
3  * All rights reserved.
4  *
5  * This software is not subject to any license of the American Telephone
6  * and Telegraph Company or of the Regents of the University of California.
7  *
8  * Permission is granted to anyone to use this software for any purpose on
9  * any computer system, and to alter it and redistribute it freely, subject
10  * to the following restrictions:
11  * 1. The authors are not responsible for the consequences of use of this
12  *    software, no matter how awful, even if they arise from flaws in it.
13  * 2. The origin of this software must not be misrepresented, either by
14  *    explicit claim or by omission.  Since few users ever read sources,
15  *    credits must appear in the documentation.
16  * 3. Altered versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.  Since few users
18  *    ever read sources, credits must appear in the documentation.
19  * 4. This notice may not be removed or altered.
20  */
21 
22 #include <errno.h>
23 #include <ctype.h>
24 #include <signal.h>
25 #include <sys/ioctl.h>
26 
27 #include "editline.h"
28 
29 /*
30 **  Manifest constants.
31 */
32 #define SCREEN_COLS     80
33 #define SCREEN_ROWS     24
34 #define EL_STDIN        0
35 #define EL_STDOUT       1
36 #define NO_ARG          (-1)
37 #define DEL             127
38 #define SEPS "\"#$&'()*:;<=>?[\\]^`{|}~\n\t "
39 
40 /*
41 **  The type of case-changing to perform.
42 */
43 typedef enum {
44     TOupper, TOlower, TOcapitalize
45 } el_case_t;
46 
47 /*
48 **  Key to command mapping.
49 */
50 typedef struct {
51     int         Key;
52     el_status_t (*Function)(void);
53 } el_keymap_t;
54 
55 /*
56 **  Command history structure.
57 */
58 typedef struct {
59     int         Size;
60     int         Pos;
61     char      **Lines;
62 } el_hist_t;
63 
64 /* User definable callbacks. */
65 rl_getc_func_t *rl_getc_function = rl_getc;
66 rl_hook_func_t *rl_event_hook;
67 rl_vintfunc_t  *rl_prep_term_function   = rl_prep_terminal;
68 rl_voidfunc_t  *rl_deprep_term_function = rl_deprep_terminal;
69 
70 /*
71 **  Globals.
72 */
73 int               rl_eof;
74 int               rl_erase;
75 int               rl_intr;
76 int               rl_kill;
77 int               rl_quit;
78 #ifdef CONFIG_SIGSTOP
79 int               rl_susp;
80 #endif
81 
82 int               el_hist_size = 15;
83 static el_hist_t  H = {
84     .Size  = 0,
85     .Pos   = 0,
86     .Lines = NULL,
87 };
88 
89 static char        NILSTR[] = "";
90 static const char *el_input = NILSTR;
91 static char       *Yanked;
92 static char       *Screen;
93 static char       NEWLINE[]= CRLF;
94 static char       CLEAR[]= "\ec";
95 static const char *el_term = "dumb";
96 static int        Repeat;
97 static int        old_point;
98 static int        el_push_back;
99 static int        el_pushed;
100 static int        el_intr_pending;
101 static int        el_infd  = EL_STDIN;
102 static int        el_outfd = EL_STDOUT;
103 static el_keymap_t Map[];
104 static el_keymap_t MetaMap[];
105 static size_t     Length = 0;
106 static size_t     ScreenCount;
107 static size_t     ScreenSize;
108 static char       *backspace = "\b";
109 static char       *old_search = NULL;
110 static int        tty_cols = SCREEN_COLS;
111 static int        tty_rows = SCREEN_ROWS;
112 static int        Searching = 0;
113 static const char *(*search_move)(void);
114 static const char *old_prompt = NULL;
115 static rl_vcpfunc_t *line_handler = NULL;
116 static char       *line_up = "\x1b[A";
117 static char       *line_down = "\x1b[B";
118 int prompt_len = 0;
119 
120 int               el_no_echo = 0; /* e.g., under Emacs */
121 int               el_no_hist = 0;
122 int               rl_point;
123 int               rl_mark;
124 int               rl_end;
125 int               rl_meta_chars = 0; /* Display 8-bit chars as the actual char(0) or as `M-x'(1)? */
126 int               rl_inhibit_complete = 0;
127 char             *rl_line_buffer = NULL;
128 const char       *rl_prompt = NULL;
129 const char       *rl_readline_name = NULL; /* Set by calling program, for conditional parsing of ~/.inputrc - Not supported yet! */
130 FILE             *rl_instream = NULL;  /* The stdio stream from which input is read. Defaults to stdin if NULL */
131 FILE             *rl_outstream = NULL; /* The stdio stream to which output is flushed. Defaults to stdout if NULL */
132 
133 /* Declarations. */
134 static char     *editinput(int complete);
135 #ifdef CONFIG_USE_TERMCAP
136 extern char     *tgetstr(const char *, char **);
137 extern int      tgetent(char *, const char *);
138 extern int      tgetnum(const char *);
139 #endif
140 
141 /*
142 **  Misc. local helper functions.
143 */
is_alpha_num(unsigned char c)144 static int is_alpha_num(unsigned char c)
145 {
146     if (isalnum(c))
147         return 1;
148     if (ISMETA(c))
149         return 1;
150     if (ISCTL(c))
151         return 1;
152 
153     return 0;
154 }
155 
156 /*
157 **  TTY input/output functions.
158 */
159 
tty_flush(void)160 static void tty_flush(void)
161 {
162     ssize_t res;
163 
164     if (!ScreenCount)
165         return;
166 
167     if (!el_no_echo) {
168         res = write(el_outfd, Screen, ScreenCount);
169         if (res > 0)
170             ScreenCount = 0;
171     }
172 }
173 
tty_put(const char c)174 static void tty_put(const char c)
175 {
176     if (el_no_echo)
177         return;
178 
179     Screen[ScreenCount] = c;
180     if (++ScreenCount >= ScreenSize) {
181 	char *ptr;
182 
183         ScreenSize += SCREEN_INC;
184         ptr = realloc(Screen, sizeof(char) * ScreenSize);
185 	if (ptr)
186 	    Screen = ptr;
187     }
188 }
189 
tty_puts(const char * p)190 static void tty_puts(const char *p)
191 {
192     while (*p)
193         tty_put(*p++);
194 }
195 
tty_show(unsigned char c)196 static void tty_show(unsigned char c)
197 {
198     if (c == DEL) {
199         tty_put('^');
200         tty_put('?');
201     } else if (ISCTL(c)) {
202         tty_put('^');
203         tty_put(UNCTL(c));
204     } else if (rl_meta_chars && ISMETA(c)) {
205         tty_put('M');
206         tty_put('-');
207         tty_put(UNMETA(c));
208     } else {
209         tty_put(c);
210     }
211 }
212 
tty_string(char * p)213 static void tty_string(char *p)
214 {
215     int i = rl_point + prompt_len + 1;
216 
217     while (*p) {
218         tty_show(*p++);
219         if ((i++) % tty_cols == 0) {
220             tty_put(' ');
221             tty_put('\b');
222         }
223     }
224 }
225 
tty_push(int c)226 static void tty_push(int c)
227 {
228     el_pushed = 1;
229     el_push_back = c;
230 }
231 
rl_getc(void)232 int rl_getc(void)
233 {
234     int r;
235     char c;
236 
237     do {
238         r = read(el_infd, &c, 1);
239     } while (r == -1 && errno == EINTR);
240 
241     return r == 1 ? c : EOF;
242 }
243 
tty_get(void)244 static int tty_get(void)
245 {
246     tty_flush();
247 
248     if (el_pushed) {
249         el_pushed = 0;
250         return el_push_back;
251     }
252 
253     if (*el_input)
254         return *el_input++;
255 
256     return rl_getc_function();
257 }
258 
259 #define tty_back()  tty_puts(backspace)
260 
tty_backn(int n)261 static void tty_backn(int n)
262 {
263     while (--n >= 0)
264         tty_back();
265 }
266 
tty_forwardn(int n)267 static void tty_forwardn(int n)
268 {
269     char buf[12];
270 
271     snprintf(buf, sizeof(buf), "\x1b[%dC", n);
272     tty_puts(buf);
273 }
274 
tty_info(void)275 static void tty_info(void)
276 {
277     rl_reset_terminal(NULL);
278 }
279 
280 /*
281 **  Glue routines to rl_ttyset()
282 */
rl_prep_terminal(int meta_flag)283 void rl_prep_terminal(int meta_flag)
284 {
285     rl_meta_chars = !meta_flag;
286     rl_ttyset(0);
287 }
288 
rl_deprep_terminal(void)289 void rl_deprep_terminal(void)
290 {
291     rl_ttyset(1);
292 }
293 
294 /*
295 **  Print an array of words in columns.
296 */
el_print_columns(int ac,char ** av)297 void el_print_columns(int ac, char **av)
298 {
299     char        *p;
300     int         i;
301     int         j;
302     int         k;
303     int         len;
304     int         skip;
305     int         longest;
306     int         cols;
307     int         colwidth;
308 
309     /* Find longest name, determine column count from that. */
310     for (longest = 0, i = 0; i < ac; i++) {
311         if ((j = strlen((char *)av[i])) > longest)
312             longest = j;
313     }
314 
315     colwidth = longest + 3;
316     if (colwidth > tty_cols)
317         colwidth = tty_cols;
318     cols = tty_cols / colwidth;
319 
320     tty_puts(NEWLINE);
321     for (skip = ac / cols + 1, i = 0; i < skip; i++) {
322         for (j = i; j < ac; j += skip) {
323             for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++)
324                 tty_put(*p);
325 
326             if (j + skip < ac) {
327                 while (++len < colwidth)
328                     tty_put(' ');
329             }
330         }
331 
332         tty_puts(NEWLINE);
333     }
334 }
335 
reposition(int key)336 static void reposition(int key)
337 {
338     int len_with_prompt = prompt_len + rl_end;
339     int n =  len_with_prompt / tty_cols;                  /* determine the number of lines */
340     int i = 0;
341 
342     tty_put('\r');
343 
344     if (n > 0) {
345         int line;
346 
347 	/* determine num of current line */
348         if (key == CTL('A') || key == CTL('E') || key == rl_kill)
349             line = (prompt_len + old_point) / tty_cols;
350         else
351             line = len_with_prompt / tty_cols;
352 
353 	/* move to end of line(s) */
354         if (key == CTL('E')) {
355 	    int k;
356 
357             for (k = line; k < n; k++)
358                 tty_puts(line_down);
359 
360 	    /* determine reminder of last line and redraw only it */
361             i = rl_point - (len_with_prompt % tty_cols);
362         } else {
363 	    int k;
364 
365 	    /* CTRL-A, CTRL-U, insert (end, middle), remove (end, middle) */
366             for (k = line; k > 0; k--)
367                 tty_puts(line_up); /* redraw characters until changed data */
368 
369             tty_puts(rl_prompt);
370         }
371     } else if (n == 0) {
372         tty_puts(rl_prompt);
373     }
374 
375     for (; i < rl_point; i++) {
376         tty_show(rl_line_buffer[i]);
377 
378 	/* move to the next line */
379         if ((i + prompt_len + 1) % tty_cols == 0)
380             tty_put('\n');
381     }
382 }
383 
left(el_status_t Change)384 static void left(el_status_t Change)
385 {
386     if (rl_point) {
387         if ((rl_point + prompt_len) % tty_cols == 0) {
388             tty_puts(line_up);
389             tty_forwardn(tty_cols);
390         } else {
391             tty_back();
392         }
393 
394         if (ISMETA(rl_line_buffer[rl_point - 1])) {
395             if (rl_meta_chars) {
396                 tty_back();
397                 tty_back();
398             }
399         } else if (ISCTL(rl_line_buffer[rl_point - 1])) {
400             tty_back();
401         }
402     }
403 
404     if (Change == CSmove)
405         rl_point--;
406 }
407 
right(el_status_t Change)408 static void right(el_status_t Change)
409 {
410     if ((rl_point + prompt_len + 1) % tty_cols == 0)
411         tty_put('\n');
412     else
413         tty_show(rl_line_buffer[rl_point]);
414 
415     if (Change == CSmove)
416         rl_point++;
417 }
418 
el_ring_bell(void)419 el_status_t el_ring_bell(void)
420 {
421     tty_put('\07');
422     tty_flush();
423 
424     return CSstay;
425 }
426 
do_macro(int c)427 static el_status_t do_macro(int c)
428 {
429     char name[4];
430 
431     name[0] = '_';
432     name[1] = c;
433     name[2] = '_';
434     name[3] = '\0';
435 
436     if ((el_input = (char *)getenv((char *)name)) == NULL) {
437         el_input = NILSTR;
438         return el_ring_bell();
439     }
440 
441     return CSstay;
442 }
443 
444 /* Skip forward to start of next word. If @move is set we also move the cursor. */
do_forward(el_status_t move)445 static el_status_t do_forward(el_status_t move)
446 {
447     int         i;
448     char        *p;
449 
450     i = 0;
451     do {
452         p = &rl_line_buffer[rl_point];
453 
454         /* Skip leading whitespace, like FSF Readline */
455         for ( ; rl_point < rl_end && (p[0] == ' ' || !is_alpha_num(p[0])); rl_point++, p++) {
456             if (move == CSmove)
457                 right(CSstay);
458         }
459 
460         /* Skip to end of word, if inside a word. */
461         for (; rl_point < rl_end && is_alpha_num(p[0]); rl_point++, p++) {
462             if (move == CSmove)
463                 right(CSstay);
464         }
465 
466         /* Skip to next word, or skip leading white space if outside a word. */
467         for ( ; rl_point < rl_end && (p[0] == ' ' || !is_alpha_num(p[0])); rl_point++, p++) {
468             if (move == CSmove)
469                 right(CSstay);
470         }
471 
472         if (rl_point == rl_end)
473             break;
474     } while (++i < Repeat);
475 
476     return CSstay;
477 }
478 
do_case(el_case_t type)479 static el_status_t do_case(el_case_t type)
480 {
481     int         i;
482     int         end;
483     int         count;
484     char        *p;
485 
486     do_forward(CSstay);
487     if (old_point != rl_point) {
488         if ((count = rl_point - old_point) < 0)
489             count = -count;
490 
491         rl_point = old_point;
492         if ((end = rl_point + count) > rl_end)
493             end = rl_end;
494 
495         for (i = rl_point, p = &rl_line_buffer[i]; rl_point < end; p++) {
496             if ((type == TOupper) || (type == TOcapitalize && rl_point == i)) {
497                 if (islower(*p))
498                     *p = toupper(*p);
499             } else if (isupper(*p)) {
500                 *p = tolower(*p);
501             }
502             right(CSmove);
503         }
504     }
505 
506     return CSstay;
507 }
508 
case_down_word(void)509 static el_status_t case_down_word(void)
510 {
511     return do_case(TOlower);
512 }
513 
case_up_word(void)514 static el_status_t case_up_word(void)
515 {
516     return do_case(TOupper);
517 }
518 
case_cap_word(void)519 static el_status_t case_cap_word(void)
520 {
521     return do_case(TOcapitalize);
522 }
523 
ceol(void)524 static void ceol(void)
525 {
526     int         extras = 0;
527     int         i;
528     char        *p;
529 
530     while (rl_point < 0) {
531         tty_put(' ');
532         rl_point++;
533     }
534 
535     for (i = rl_point, p = &rl_line_buffer[i]; i <= rl_end; i++, p++) {
536         if ((i + prompt_len + 1) % tty_cols == 0){
537             tty_put(' ');
538             tty_put('\n');
539         }
540         else
541             tty_put(' ');
542         if (ISMETA(*p)) {
543             if (rl_meta_chars) {
544                 tty_put(' ');
545                 tty_put(' ');
546                 extras += 2;
547             }
548         } else if (ISCTL(*p)) {
549             tty_put(' ');
550             extras++;
551         }
552     }
553 
554     for (i += extras; i > rl_point; i--) {
555         if ((i + prompt_len) % tty_cols == 0) {
556             tty_puts(line_up);
557             tty_forwardn(tty_cols);
558         } else {
559             tty_back();
560         }
561     }
562 }
563 
clear_line(void)564 static void clear_line(void)
565 {
566     int n = (rl_point + prompt_len) / tty_cols;
567     rl_point = -(int)strlen(rl_prompt);
568 
569     if (n > 0) {
570         for(int k = 0; k < n; k++)
571             tty_puts(line_up);
572         tty_put('\r');
573     }
574     else {
575         tty_put('\r');
576     }
577 
578     ceol();
579 
580     rl_point = 0;
581     rl_end = 0;
582     rl_line_buffer[0] = '\0';
583 }
584 
insert_string(const char * p)585 static el_status_t insert_string(const char *p)
586 {
587     size_t      len;
588     int         i;
589     char        *line;
590     char        *q;
591 
592     len = strlen(p);
593     if (rl_end + len >= Length) {
594         line = malloc(sizeof(char) * (Length + len + MEM_INC));
595         if (!line)
596             return CSstay;
597 
598         if (Length) {
599             memcpy(line, rl_line_buffer, Length);
600             free(rl_line_buffer);
601         }
602 
603         rl_line_buffer = line;
604         Length += len + MEM_INC;
605     }
606 
607     for (q = &rl_line_buffer[rl_point], i = rl_end - rl_point; --i >= 0; )
608         q[len + i] = q[i];
609 
610     memcpy(&rl_line_buffer[rl_point], p, len);
611     rl_end += len;
612     rl_line_buffer[rl_end] = '\0';
613     tty_string(&rl_line_buffer[rl_point]);
614     rl_point += len;
615 
616     return rl_point == rl_end ? CSstay : CSmove;
617 }
618 
rl_insert_text(const char * text)619 int rl_insert_text(const char *text)
620 {
621     int mark = rl_point;
622 
623     insert_string(text);
624     ceol();
625 
626     return rl_point - mark;
627 }
628 
redisplay(int cls)629 static el_status_t redisplay(int cls)
630 {
631     if (cls)
632         tty_puts(CLEAR);
633     else
634         tty_puts("\r\e[K");
635 
636     tty_puts(rl_prompt);
637     rl_point = 0;
638     tty_string(rl_line_buffer);
639     rl_point = rl_end;
640     return CSmove;
641 }
642 
refresh(void)643 static el_status_t refresh(void)
644 {
645     return redisplay(1);
646 }
647 
rl_refresh_line(int ignore1,int ignore2)648 int rl_refresh_line(int ignore1 __attribute__((unused)), int ignore2 __attribute__((unused)))
649 {
650     redisplay(0);
651     return 0;
652 }
653 
toggle_meta_mode(void)654 static el_status_t toggle_meta_mode(void)
655 {
656     rl_meta_chars = ! rl_meta_chars;
657     return redisplay(0);
658 }
659 
el_next_hist(void)660 const char *el_next_hist(void)
661 {
662     return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
663 }
664 
el_prev_hist(void)665 const char *el_prev_hist(void)
666 {
667     return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
668 }
669 
do_insert_hist(const char * p)670 static el_status_t do_insert_hist(const char *p)
671 {
672     if (p == NULL)
673         return el_ring_bell();
674 
675     clear_line();
676 
677     rl_point = 0;
678     reposition(EOF);
679     rl_end = 0;
680 
681     return insert_string(p);
682 }
683 
do_hist(const char * (* move)(void))684 static el_status_t do_hist(const char *(*move)(void))
685 {
686     const char *p;
687     int i = 0;
688 
689     do {
690         if ((p = move()) == NULL)
691             return el_ring_bell();
692     } while (++i < Repeat);
693 
694     return do_insert_hist(p);
695 }
696 
h_next(void)697 static el_status_t h_next(void)
698 {
699     if (el_no_hist)
700         return CSstay;
701 
702     return do_hist(el_next_hist);
703 }
704 
h_prev(void)705 static el_status_t h_prev(void)
706 {
707     if (el_no_hist)
708         return CSstay;
709 
710     return do_hist(el_prev_hist);
711 }
712 
h_first(void)713 static el_status_t h_first(void)
714 {
715     return do_insert_hist(H.Lines[H.Pos = 0]);
716 }
717 
h_last(void)718 static el_status_t h_last(void)
719 {
720     return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
721 }
722 
723 /*
724 **  Return zero if pat appears as a substring in text.
725 */
substrcmp(const char * text,const char * pat,size_t len)726 static int substrcmp(const char *text, const char *pat, size_t len)
727 {
728     char c;
729 
730     if ((c = *pat) == '\0')
731         return *text == '\0';
732 
733     for ( ; *text; text++) {
734         if (*text == c && strncmp(text, pat, len) == 0)
735             return 0;
736     }
737 
738     return 1;
739 }
740 
search_hist(const char * search,const char * (* move)(void))741 static const char *search_hist(const char *search, const char *(*move)(void))
742 {
743     int         len;
744     int         pos;
745     int         (*match)(const char *s1, const char *s2, size_t n);
746     const char *pat;
747 
748     /* Save or get remembered search pattern. */
749     if (search && *search) {
750         if (old_search)
751             free(old_search);
752         old_search = strdup(search);
753     } else {
754         if (old_search == NULL || *old_search == '\0')
755             return NULL;
756         search = old_search;
757     }
758 
759     /* Set up pattern-finder. */
760     if (*search == '^') {
761         match = strncmp;
762         pat = search + 1;
763     } else {
764         match = substrcmp;
765         pat = search;
766     }
767     len = strlen(pat);
768 
769     pos = H.Pos;		/* Save H.Pos */
770     while (move()) {
771         if (match(H.Lines[H.Pos], pat, len) == 0)
772             return H.Lines[H.Pos];
773     }
774     H.Pos = pos;		/* Restore H.Pos */
775 
776     return NULL;
777 }
778 
h_search_end(const char * p)779 static el_status_t h_search_end(const char *p)
780 {
781     rl_prompt = old_prompt;
782     Searching = 0;
783 
784     if (el_intr_pending > 0) {
785         el_intr_pending = 0;
786         clear_line();
787         return redisplay(0);
788     }
789 
790     p = search_hist(p, search_move);
791     if (p == NULL) {
792         el_ring_bell();
793         clear_line();
794         return redisplay(0);
795     }
796 
797     return do_insert_hist(p);
798 }
799 
h_search(void)800 static el_status_t h_search(void)
801 {
802     if (Searching)
803         return el_ring_bell();
804     Searching = 1;
805 
806     clear_line();
807     old_prompt = rl_prompt;
808     rl_prompt = "Search: ";
809     tty_puts(rl_prompt);
810 
811     search_move = Repeat == NO_ARG ? el_prev_hist : el_next_hist;
812     if (line_handler) {
813         editinput(0);
814         return CSstay;
815     }
816 
817     return h_search_end(editinput(1));
818 }
819 
fd_char(void)820 static el_status_t fd_char(void)
821 {
822     int i = 0;
823 
824     do {
825         if (rl_point >= rl_end)
826             break;
827         right(CSmove);
828     } while (++i < Repeat);
829     return CSstay;
830 }
831 
save_yank(int begin,int i)832 static void save_yank(int begin, int i)
833 {
834     if (Yanked) {
835         free(Yanked);
836         Yanked = NULL;
837     }
838 
839     if (i < 1)
840         return;
841 
842     Yanked = malloc(sizeof(char) * (i + 1));
843     if (Yanked) {
844         memcpy(Yanked, &rl_line_buffer[begin], i);
845         Yanked[i] = '\0';
846     }
847 }
848 
delete_string(int count)849 static el_status_t delete_string(int count)
850 {
851     int i;
852     char *p;
853 
854     if (count <= 0 || rl_end == rl_point)
855         return el_ring_bell();
856 
857     if (count == 1 && rl_point == rl_end - 1) {
858         /* Optimize common case of delete at end of line. */
859         rl_end--;
860         p = &rl_line_buffer[rl_point];
861         i = 1;
862         tty_put(' ');
863         if (ISCTL(*p)) {
864             i = 2;
865             tty_put(' ');
866         } else if (rl_meta_chars && ISMETA(*p)) {
867             i = 3;
868             tty_put(' ');
869             tty_put(' ');
870         }
871         tty_backn(i);
872         *p = '\0';
873         return CSmove;
874     }
875 
876     if (rl_point + count > rl_end && (count = rl_end - rl_point) <= 0)
877         return CSstay;
878 
879     if (count > 1)
880         save_yank(rl_point, count);
881 
882     for (p = &rl_line_buffer[rl_point], i = rl_end - (rl_point + count) + 1; --i >= 0; p++)
883         p[0] = p[count];
884     ceol();
885 
886     rl_end -= count;
887     tty_string(&rl_line_buffer[rl_point]);
888 
889     return CSmove;
890 }
891 
bk_char(void)892 static el_status_t bk_char(void)
893 {
894     int i = 0;
895 
896     do {
897         if (rl_point == 0)
898             break;
899         left(CSmove);
900     } while (++i < Repeat);
901 
902     return CSstay;
903 }
904 
bk_del_char(void)905 static el_status_t bk_del_char(void)
906 {
907     int i = 0;
908 
909     do {
910         if (rl_point == 0)
911             break;
912         left(CSmove);
913     } while (++i < Repeat);
914 
915     return delete_string(i);
916 }
917 
kill_line(void)918 static el_status_t kill_line(void)
919 {
920     int i;
921 
922     if (Repeat != NO_ARG) {
923         if (Repeat < rl_point) {
924             i = rl_point;
925             rl_point = Repeat;
926             reposition(EOF);
927             delete_string(i - rl_point);
928         } else if (Repeat > rl_point) {
929             right(CSmove);
930             delete_string(Repeat - rl_point - 1);
931         }
932 
933         return CSmove;
934     }
935 
936     save_yank(rl_point, rl_end - rl_point);
937     rl_line_buffer[rl_point] = '\0';
938     ceol();
939     rl_end = rl_point;
940 
941     return CSstay;
942 }
943 
insert_char(int c)944 static el_status_t insert_char(int c)
945 {
946     el_status_t      s;
947     char        buff[2];
948     char        *p;
949     char        *q;
950     int         i;
951 
952     if (Repeat == NO_ARG || Repeat < 2) {
953         buff[0] = c;
954         buff[1] = '\0';
955 
956         return insert_string(buff);
957     }
958 
959     p = malloc(sizeof(char) * (Repeat + 1));
960     if (!p)
961         return CSstay;
962 
963     for (i = Repeat, q = p; --i >= 0; )
964         *q++ = c;
965     *q = '\0';
966     Repeat = 0;
967     s = insert_string(p);
968     free(p);
969 
970     return s;
971 }
972 
beg_line(void)973 static el_status_t beg_line(void)
974 {
975     if (rl_point) {
976         rl_point = 0;
977         return CSmove;
978     }
979 
980     return CSstay;
981 }
982 
end_line(void)983 static el_status_t end_line(void)
984 {
985     if (rl_point != rl_end) {
986         rl_point = rl_end;
987         return CSmove;
988     }
989 
990     return CSstay;
991 }
992 
del_char(void)993 static el_status_t del_char(void)
994 {
995     return delete_string(Repeat == NO_ARG ? CSeof : Repeat);
996 }
997 
el_del_char(void)998 el_status_t el_del_char(void)
999 {
1000     return del_char();
1001 }
1002 
fd_word(void)1003 static el_status_t fd_word(void)
1004 {
1005     return do_forward(CSmove);
1006 }
1007 
bk_word(void)1008 static el_status_t bk_word(void)
1009 {
1010     int         i;
1011     char        *p;
1012 
1013     i = 0;
1014     do {
1015         for (p = &rl_line_buffer[rl_point]; p > rl_line_buffer && !is_alpha_num(p[-1]); p--)
1016             left(CSmove);
1017 
1018         for (; p > rl_line_buffer && !isblank(p[-1]) && is_alpha_num(p[-1]); p--)
1019             left(CSmove);
1020 
1021         if (rl_point == 0)
1022             break;
1023     } while (++i < Repeat);
1024 
1025     return CSstay;
1026 }
1027 
meta(void)1028 static el_status_t meta(void)
1029 {
1030     int c;
1031     el_keymap_t *kp;
1032 
1033     if ((c = tty_get()) == EOF)
1034         return CSeof;
1035 
1036 #ifdef CONFIG_ANSI_ARROWS
1037     /* Also include VT-100 arrows. */
1038     if (c == '[' || c == 'O') {
1039         switch (tty_get()) {
1040         case EOF:  return CSeof;
1041         case '1':
1042         {
1043             char seq[4] = { 0 };
1044 
1045             for (c = 0; c < 3; c++)
1046                 seq[c] = tty_get();
1047 
1048             if (!strncmp(seq, ";5C", 3))
1049                 return fd_word(); /* Ctrl+Right */
1050             if (!strncmp(seq, ";5D", 3))
1051                 return bk_word(); /* Ctrl+Left */
1052 
1053             break;
1054         }
1055         case '2':  tty_get(); return CSstay;     /* Insert */
1056         case '3':  tty_get(); return del_char(); /* Delete */
1057         case '5':  tty_get(); return CSstay;     /* PgUp */
1058         case '6':  tty_get(); return CSstay;     /* PgDn */
1059         case 'A':  return h_prev();              /* Up */
1060         case 'B':  return h_next();              /* Down */
1061         case 'C':  return fd_char();             /* Left */
1062         case 'D':  return bk_char();             /* Right */
1063         case 'F':  return end_line();            /* End */
1064         case 'H':  return beg_line();            /* Home */
1065         default:                                 /* Fall through */
1066             break;
1067         }
1068 
1069         return el_ring_bell();
1070     }
1071 #endif /* CONFIG_ANSI_ARROWS */
1072 
1073     if (isdigit(c)) {
1074         for (Repeat = c - '0'; (c = tty_get()) != EOF && isdigit(c); )
1075             Repeat = Repeat * 10 + c - '0';
1076         tty_push(c);
1077 
1078         return CSstay;
1079     }
1080 
1081     if (isupper(c))
1082         return do_macro(c);
1083 
1084     for (kp = MetaMap; kp->Function; kp++) {
1085         if (kp->Key == c)
1086             return kp->Function();
1087     }
1088 
1089     return el_ring_bell();
1090 }
1091 
emacs(int c)1092 static el_status_t emacs(int c)
1093 {
1094     el_status_t  s;
1095     el_keymap_t *kp;
1096 
1097     /* Save point before interpreting input character 'c'. */
1098     old_point = rl_point;
1099 
1100     if (rl_meta_chars && ISMETA(c)) {
1101         tty_push(UNMETA(c));
1102         return meta();
1103     }
1104 
1105     for (kp = Map; kp->Function; kp++) {
1106         if (kp->Key == c)
1107             break;
1108     }
1109 
1110     if (kp->Function) {
1111         s = kp->Function();
1112         if (s == CSdispatch)	/* If Function is inhibited. */
1113             s = insert_char(c);
1114     } else {
1115         s = insert_char(c);
1116     }
1117 
1118     if (!el_pushed) {
1119         /* No pushback means no repeat count; hacky, but true. */
1120         Repeat = NO_ARG;
1121     }
1122 
1123     return s;
1124 }
1125 
tty_special(int c)1126 static el_status_t tty_special(int c)
1127 {
1128 #ifdef CONFIG_SIGINT
1129     if (c == rl_intr) {
1130         el_intr_pending = SIGINT;
1131         return CSsignal;
1132     }
1133 #endif
1134     if (c == rl_quit) {
1135         el_intr_pending = SIGQUIT;
1136         return CSeof;
1137     }
1138 #ifdef CONFIG_SIGSTOP
1139     if (c == rl_susp) {
1140         el_intr_pending = SIGTSTP;
1141         return CSsignal;
1142     }
1143 #endif
1144 
1145     if (rl_meta_chars && ISMETA(c))
1146         return CSdispatch;
1147 
1148     if (c == rl_erase || c == DEL)
1149         return bk_del_char();
1150 
1151     if (c == rl_kill) {
1152         if (rl_point != 0) {
1153             old_point = rl_point;
1154             rl_point = 0;
1155             reposition(c);
1156         }
1157         Repeat = NO_ARG;
1158 
1159         return kill_line();
1160     }
1161 
1162 #ifdef CONFIG_EOF
1163     if (c == rl_eof && rl_point == 0 && rl_end == 0)
1164         return CSeof;
1165 #endif
1166 
1167     return CSdispatch;
1168 }
1169 
editinput(int complete)1170 static char *editinput(int complete)
1171 {
1172     int c;
1173 
1174     do {
1175         c = tty_get();
1176         if (c == EOF)
1177             break;
1178 
1179         switch (tty_special(c)) {
1180         case CSdone:
1181             return rl_line_buffer;
1182 
1183         case CSeof:
1184             return NULL;
1185 
1186         case CSsignal:
1187             return (char *)"";
1188 
1189         case CSmove:
1190             reposition(c);
1191             break;
1192 
1193         case CSdispatch:
1194             switch (emacs(c)) {
1195             case CSdone:
1196                 return rl_line_buffer;
1197 
1198             case CSeof:
1199                 return NULL;
1200 
1201             case CSsignal:
1202                 return (char *)"";
1203 
1204             case CSmove:
1205                 reposition(c);
1206                 break;
1207 
1208             case CSdispatch:
1209             case CSstay:
1210                 break;
1211             }
1212             break;
1213 
1214         case CSstay:
1215             break;
1216         }
1217     } while (complete);
1218 
1219     return NULL;
1220 }
1221 
hist_alloc(void)1222 static void hist_alloc(void)
1223 {
1224     if (!H.Lines)
1225         H.Lines = calloc(el_hist_size, sizeof(char *));
1226 }
1227 
hist_add(const char * p)1228 static void hist_add(const char *p)
1229 {
1230     int i;
1231     char *s;
1232 
1233 #ifdef CONFIG_UNIQUE_HISTORY
1234     if (H.Size && strcmp(p, H.Lines[H.Size - 1]) == 0)
1235         return;
1236 #endif
1237 
1238     s = strdup(p);
1239     if (s == NULL)
1240         return;
1241 
1242     if (H.Size < el_hist_size) {
1243         H.Lines[H.Size++] = s;
1244     } else {
1245         free(H.Lines[0]);
1246         for (i = 0; i < el_hist_size - 1; i++)
1247             H.Lines[i] = H.Lines[i + 1];
1248         H.Lines[i] = s;
1249     }
1250     H.Pos = H.Size - 1;
1251 }
1252 
read_redirected(void)1253 static char *read_redirected(void)
1254 {
1255     int         size = MEM_INC;
1256     char        *p;
1257     char        *line;
1258     char        *end;
1259 
1260     p = line = malloc(sizeof(char) * size);
1261     if (!p)
1262         return NULL;
1263 
1264     end = p + size;
1265     while (1) {
1266         if (p == end) {
1267             int oldpos = end - line;
1268 
1269             size += MEM_INC;
1270             p = realloc(line, sizeof(char) * size);
1271             if (!p) {
1272 		free(line);
1273                 return NULL;
1274 	    }
1275 	    line = p;
1276             end = p + size;
1277 
1278             p += oldpos;        /* Continue where we left off... */
1279         }
1280 
1281         if (read(el_infd, p, 1) <= 0) {
1282             /* Ignore "incomplete" lines at EOF, just like we do for a tty. */
1283             free(line);
1284             return NULL;
1285         }
1286 
1287         if (*p == '\n')
1288             break;
1289         p++;
1290     }
1291     *p = '\0';
1292 
1293     return line;
1294 }
1295 
1296 /* For compatibility with FSF readline. */
rl_reset_terminal(const char * terminal_name)1297 void rl_reset_terminal(const char *terminal_name)
1298 {
1299 #ifdef CONFIG_USE_TERMCAP
1300     char                buf[1024];
1301     char               *bp;
1302 #endif
1303 #ifdef TIOCGWINSZ
1304     struct winsize      W;
1305 #endif
1306 
1307     if (terminal_name) {
1308         el_term = terminal_name;
1309     } else if ((el_term = getenv("TERM")) == NULL) {
1310         el_term = "dumb";
1311     }
1312 
1313     /* Initialize to faulty values to trigger fallback if nothing else works. */
1314     tty_cols = tty_rows = -1;
1315 
1316 #ifdef CONFIG_USE_TERMCAP
1317     bp = buf;
1318     if (-1 != tgetent(buf, el_term)) {
1319         if ((backspace = tgetstr("le", &bp)) != NULL)
1320             backspace = strdup(backspace);
1321         tty_cols = tgetnum("co");
1322         tty_rows = tgetnum("li");
1323     }
1324     /* Make sure to check width & rows and fallback to TIOCGWINSZ if available. */
1325 #endif
1326 
1327     if (tty_cols <= 0 || tty_rows <= 0) {
1328 #ifdef TIOCGWINSZ
1329         if (ioctl(el_outfd, TIOCGWINSZ, &W) >= 0 && W.ws_col > 0 && W.ws_row > 0) {
1330             tty_cols = (int)W.ws_col;
1331             tty_rows = (int)W.ws_row;
1332             return;
1333         }
1334 #endif
1335         tty_cols = SCREEN_COLS;
1336         tty_rows = SCREEN_ROWS;
1337     }
1338 }
1339 
rl_initialize(void)1340 void rl_initialize(void)
1341 {
1342     if (!rl_prompt)
1343         rl_prompt = "? ";
1344 
1345     hist_alloc();
1346 
1347     /* Setup I/O descriptors */
1348     if (!rl_instream)  el_infd  = EL_STDIN;
1349     else               el_infd  = fileno(rl_instream);
1350     if (el_infd < 0)   el_infd  = EL_STDIN;
1351     if (!rl_outstream) el_outfd = EL_STDOUT;
1352     else               el_outfd = fileno(rl_outstream);
1353     if (el_outfd < 0)  el_outfd = EL_STDOUT;
1354 }
1355 
rl_uninitialize(void)1356 void rl_uninitialize(void)
1357 {
1358     int i;
1359 
1360     /* Uninitialize the history */
1361     if (H.Lines) {
1362         for (i = 0; i < el_hist_size; i++) {
1363             if (H.Lines[i])
1364                 free(H.Lines[i]);
1365             H.Lines[i] = NULL;
1366         }
1367         free(H.Lines);
1368         H.Lines = NULL;
1369     }
1370     H.Size = 0;
1371     H.Pos = 0;
1372 
1373     if (old_search)
1374         free(old_search);
1375     old_search = NULL;
1376 
1377     /* Uninitialize the line buffer */
1378     if (rl_line_buffer)
1379         free(rl_line_buffer);
1380     rl_line_buffer = NULL;
1381     Length = 0;
1382 }
1383 
1384 static const char *rl_saved_prompt = NULL;
rl_save_prompt(void)1385 void rl_save_prompt(void)
1386 {
1387     rl_saved_prompt = rl_prompt;
1388 }
1389 
rl_restore_prompt(void)1390 void rl_restore_prompt(void)
1391 {
1392     if (rl_saved_prompt)
1393         rl_prompt = rl_saved_prompt;
1394 }
1395 
rl_set_prompt(const char * prompt)1396 void rl_set_prompt(const char *prompt)
1397 {
1398     rl_prompt = prompt;
1399 }
1400 
rl_clear_message(void)1401 void rl_clear_message(void)
1402 {
1403     /* Nothing to do atm. */
1404 }
1405 
rl_forced_update_display()1406 void rl_forced_update_display()
1407 {
1408     redisplay(0);
1409     tty_flush();
1410 }
1411 
el_prep(const char * prompt)1412 static int el_prep(const char *prompt)
1413 {
1414     rl_initialize();
1415 
1416     if (!rl_line_buffer) {
1417         Length = MEM_INC;
1418         rl_line_buffer = malloc(sizeof(char) * Length);
1419         if (!rl_line_buffer)
1420             return -1;
1421     }
1422 
1423     tty_info();
1424     rl_prep_term_function(!rl_meta_chars);
1425     hist_add(NILSTR);
1426     ScreenSize = SCREEN_INC;
1427     Screen = malloc(sizeof(char) * ScreenSize);
1428     if (!Screen)
1429         return -1;
1430 
1431     rl_prompt = prompt ? prompt : NILSTR;
1432     prompt_len = strlen(rl_prompt);
1433 
1434     if (el_no_echo) {
1435         int old = el_no_echo;
1436 
1437         el_no_echo = 0;
1438         tty_puts(rl_prompt);
1439         tty_flush();
1440         el_no_echo = old;
1441     } else {
1442         tty_puts(rl_prompt);
1443     }
1444 
1445     Repeat = NO_ARG;
1446     old_point = rl_point = rl_mark = rl_end = 0;
1447     rl_line_buffer[0] = '\0';
1448     el_intr_pending = -1;
1449 
1450     return 0;
1451 }
1452 
el_deprep(char * line)1453 static char *el_deprep(char *line)
1454 {
1455     if (line) {
1456         line = strdup(line);
1457         tty_puts(NEWLINE);
1458         tty_flush();
1459     }
1460 
1461     rl_deprep_term_function();
1462     if (Screen) {
1463         free(Screen);
1464         Screen = NULL;
1465     }
1466 
1467     free(H.Lines[--H.Size]);
1468     H.Lines[H.Size] = NULL;
1469 
1470     /* Add to history, unless no-echo or no-history mode ... */
1471     if (!el_no_echo && !el_no_hist) {
1472         if (line != NULL && *line != '\0')
1473             hist_add(line);
1474     }
1475 
1476     if (el_intr_pending > 0) {
1477         int signo = el_intr_pending;
1478 
1479         el_intr_pending = 0;
1480         kill(getpid(), signo);
1481     }
1482 
1483     return line;
1484 }
1485 
rl_callback_handler_install(const char * prompt,rl_vcpfunc_t * lhandler)1486 void rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *lhandler)
1487 {
1488     if (!lhandler)
1489         return;
1490     line_handler = lhandler;
1491 
1492     /*
1493      * Any error from el_prep() is handled by the lhandler callbck as
1494      * soon as the user calls rl_callback_read_char().
1495      */
1496     el_prep(prompt);
1497     tty_flush();
1498 }
1499 
1500 /*
1501  * Reads one character at a time, when a complete line has been received
1502  * the lhandler from rl_callback_handler_install() is called with the
1503  * line as argument.
1504  *
1505  * If the callback returns the terminal is prepped for reading a new
1506  * line.
1507  *
1508  * If any error occurs, either in the _install() phase, or while reading
1509  * one character, this function restores the terminal and calls lhandler
1510  * with a NULL argument.
1511  */
rl_callback_read_char(void)1512 void rl_callback_read_char(void)
1513 {
1514     char *line;
1515 
1516     if (!line_handler) {
1517         errno = EINVAL;
1518         return;
1519     }
1520 
1521     /*
1522      * Check if rl_callback_handler_install() failed
1523      * This is the only point where we can tell user
1524      */
1525     if (!Screen || !rl_line_buffer) {
1526         errno = ENOMEM;
1527         line_handler(el_deprep(NULL));
1528         return;
1529     }
1530 
1531     line = editinput(0);
1532     if (line) {
1533         char *l;
1534 
1535         if (Searching) {
1536             h_search_end(line);
1537             tty_flush();
1538             return;
1539         }
1540 
1541         l = el_deprep(line);
1542         line_handler(l);
1543 
1544         if (el_prep(rl_prompt))
1545             line_handler(NULL);
1546     }
1547     tty_flush();
1548 }
1549 
rl_callback_handler_remove(void)1550 void rl_callback_handler_remove(void)
1551 {
1552     if (!line_handler)
1553         return;
1554 
1555     el_deprep(NULL);
1556     line_handler = NULL;
1557 }
1558 
readline(const char * prompt)1559 char *readline(const char *prompt)
1560 {
1561     /* Unless called by the user already. */
1562     rl_initialize();
1563 
1564     if (!isatty(el_infd)) {
1565         tty_flush();
1566 
1567         return read_redirected();
1568     }
1569 
1570     if (el_prep(prompt))
1571         return NULL;
1572 
1573     return el_deprep(editinput(1));
1574 }
1575 
1576 /*
1577  * Even though readline() itself adds history automatically, the user
1578  * can also add lines.  This is for compatibility with GNU Readline.
1579  */
add_history(const char * p)1580 void add_history(const char *p)
1581 {
1582     if (p == NULL || *p == '\0')
1583         return;
1584 
1585     hist_add(p);
1586 }
1587 
1588 
read_history(const char * filename)1589 int read_history(const char *filename)
1590 {
1591     FILE *fp;
1592     char buf[SCREEN_INC];
1593 
1594     hist_alloc();
1595 
1596     fp = fopen(filename, "r");
1597     if (!fp)
1598 	return EOF;
1599 
1600     H.Size = 0;
1601     while (H.Size < el_hist_size) {
1602 	if (!fgets(buf, SCREEN_INC, fp))
1603 	    break;
1604 
1605 	buf[strlen(buf) - 1] = 0; /* Remove '\n' */
1606 	add_history(buf);
1607     }
1608 
1609     return fclose(fp);
1610 }
1611 
write_history(const char * filename)1612 int write_history(const char *filename)
1613 {
1614     FILE *fp;
1615     int i = 0;
1616 
1617     hist_alloc();
1618 
1619     fp = fopen(filename, "w");
1620     if (!fp)
1621 	return EOF;
1622 
1623     while (i < H.Size)
1624 	fprintf(fp, "%s\n", H.Lines[i++]);
1625 
1626     return fclose(fp);
1627 }
1628 
1629 /*
1630 **  Move back to the beginning of the current word and return an
1631 **  allocated copy of it.
1632 */
el_find_word(void)1633 char *el_find_word(void)
1634 {
1635     char        *p, *q;
1636     char        *word;
1637     size_t      len;
1638 
1639     p = &rl_line_buffer[rl_point];
1640     while (p > rl_line_buffer) {
1641         p--;
1642         if (p > rl_line_buffer && p[-1] == '\\') {
1643             p--;
1644         } else {
1645             if (strchr(SEPS, (char) *p) != NULL) {
1646                 p++;
1647                 break;
1648             }
1649         }
1650     }
1651 
1652     len = rl_point - (p - rl_line_buffer) + 1;
1653     word = malloc(sizeof(char) * len);
1654     if (!word)
1655         return NULL;
1656 
1657     q = word;
1658     while (p < &rl_line_buffer[rl_point]) {
1659         if (*p == '\\') {
1660             if (++p == &rl_line_buffer[rl_point])
1661                 break;
1662         }
1663         *q++ = *p++;
1664     }
1665     *q = '\0';
1666 
1667     return word;
1668 }
1669 
c_possible(void)1670 static el_status_t c_possible(void)
1671 {
1672     char        **av;
1673     char        *word;
1674     int         ac;
1675 
1676     word = el_find_word();
1677     ac = rl_list_possib(word, &av);
1678     if (word)
1679         free(word);
1680     if (ac) {
1681         el_print_columns(ac, av);
1682         while (--ac >= 0)
1683             free(av[ac]);
1684         free(av);
1685 
1686         return CSmove;
1687     }
1688 
1689     return el_ring_bell();
1690 }
1691 
c_complete(void)1692 static el_status_t c_complete(void)
1693 {
1694     char        *p, *q;
1695     char        *word, *string;
1696     size_t      len;
1697     int         unique;
1698     el_status_t s = CSdone;
1699 
1700     if (rl_inhibit_complete)
1701         return CSdispatch;
1702 
1703     word = el_find_word();
1704     p = rl_complete(word, &unique);
1705     if (word)
1706         free(word);
1707     if (p) {
1708         len = strlen(p);
1709         word = p;
1710 
1711         string = q = malloc(sizeof(char) * (2 * len + 1));
1712         if (!string) {
1713             free(word);
1714             return CSstay;
1715         }
1716 
1717         while (*p) {
1718             if ((*p < ' ' || strchr(SEPS, *p) != NULL)
1719                 && (!unique || p[1] != 0)) {
1720                 *q++ = '\\';
1721             }
1722             *q++ = *p++;
1723         }
1724         *q = '\0';
1725         free(word);
1726 
1727         if (len > 0) {
1728             s = insert_string(string);
1729 #ifdef CONFIG_TERMINAL_BELL
1730             if (!unique)
1731                 el_ring_bell();
1732 #endif
1733         }
1734         free(string);
1735 
1736         if (len > 0)
1737             return s;
1738     }
1739 
1740     return c_possible();
1741 }
1742 
accept_line(void)1743 static el_status_t accept_line(void)
1744 {
1745     rl_line_buffer[rl_end] = '\0';
1746     return CSdone;
1747 }
1748 
1749 #ifdef SYSTEM_IS_WIN32
end_of_input(void)1750 static el_status_t end_of_input(void)
1751 {
1752     rl_line_buffer[rl_end] = '\0';
1753     return CSeof;
1754 }
1755 #endif
1756 
transpose(void)1757 static el_status_t transpose(void)
1758 {
1759     char        c;
1760 
1761     if (rl_point) {
1762         if (rl_point == rl_end)
1763             left(CSmove);
1764         c = rl_line_buffer[rl_point - 1];
1765         left(CSstay);
1766         rl_line_buffer[rl_point - 1] = rl_line_buffer[rl_point];
1767         tty_show(rl_line_buffer[rl_point - 1]);
1768         rl_line_buffer[rl_point++] = c;
1769         tty_show(c);
1770     }
1771 
1772     return CSstay;
1773 }
1774 
quote(void)1775 static el_status_t quote(void)
1776 {
1777     int c;
1778 
1779     return (c = tty_get()) == EOF ? CSeof : insert_char((int)c);
1780 }
1781 
mk_set(void)1782 static el_status_t mk_set(void)
1783 {
1784     rl_mark = rl_point;
1785     return CSstay;
1786 }
1787 
exchange(void)1788 static el_status_t exchange(void)
1789 {
1790     int c;
1791 
1792     if ((c = tty_get()) != CTL('X'))
1793         return c == EOF ? CSeof : el_ring_bell();
1794 
1795     if ((c = rl_mark) <= rl_end) {
1796         rl_mark = rl_point;
1797         rl_point = c;
1798         return CSmove;
1799     }
1800 
1801     return CSstay;
1802 }
1803 
yank(void)1804 static el_status_t yank(void)
1805 {
1806     if (Yanked && *Yanked)
1807         return insert_string(Yanked);
1808 
1809     return CSstay;
1810 }
1811 
copy_region(void)1812 static el_status_t copy_region(void)
1813 {
1814     if (rl_mark > rl_end)
1815         return el_ring_bell();
1816 
1817     if (rl_point > rl_mark)
1818         save_yank(rl_mark, rl_point - rl_mark);
1819     else
1820         save_yank(rl_point, rl_mark - rl_point);
1821 
1822     return CSstay;
1823 }
1824 
move_to_char(void)1825 static el_status_t move_to_char(void)
1826 {
1827     int i, c;
1828     char *p;
1829 
1830     if ((c = tty_get()) == EOF)
1831         return CSeof;
1832 
1833     for (i = rl_point + 1, p = &rl_line_buffer[i]; i < rl_end; i++, p++) {
1834         if (*p == c) {
1835             rl_point = i;
1836             return CSmove;
1837         }
1838     }
1839 
1840     return CSstay;
1841 }
1842 
fd_kill_word(void)1843 static el_status_t fd_kill_word(void)
1844 {
1845     int i;
1846 
1847     do_forward(CSstay);
1848     if (old_point != rl_point) {
1849         i = rl_point - old_point - 1;
1850         rl_point = old_point;
1851         return delete_string(i);
1852     }
1853 
1854     return CSstay;
1855 }
1856 
bk_kill_word(void)1857 static el_status_t bk_kill_word(void)
1858 {
1859     bk_word();
1860     if (old_point != rl_point)
1861         return delete_string(old_point - rl_point);
1862 
1863     return CSstay;
1864 }
1865 
argify(char * line,char *** avp)1866 static int argify(char *line, char ***avp)
1867 {
1868     char       *c;
1869     char      **p;
1870     char      **arg;
1871     int         ac;
1872     int         i;
1873 
1874     i = MEM_INC;
1875     *avp = p = malloc(sizeof(char *) * i);
1876     if (!p)
1877         return 0;
1878 
1879     for (c = line; isspace(*c); c++)
1880         continue;
1881 
1882     if (*c == '\n' || *c == '\0')
1883         return 0;
1884 
1885     for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1886         if (!isspace(*c)) {
1887             c++;
1888             continue;
1889         }
1890 
1891         *c++ = '\0';
1892         if (*c && *c != '\n') {
1893             if (ac + 1 == i) {
1894                 arg = malloc(sizeof(char *) * (i + MEM_INC));
1895                 if (!arg) {
1896                     p[ac] = NULL;
1897                     return ac;
1898                 }
1899 
1900                 memcpy(arg, p, i * sizeof(char *));
1901                 i += MEM_INC;
1902                 free(p);
1903                 *avp = p = arg;
1904             }
1905             p[ac++] = c;
1906         }
1907     }
1908 
1909     *c = '\0';
1910     p[ac] = NULL;
1911 
1912     return ac;
1913 }
1914 
last_argument(void)1915 static el_status_t last_argument(void)
1916 {
1917     char      **av = NULL;
1918     char       *p;
1919     el_status_t s;
1920     int         ac;
1921 
1922     if (H.Size == 1 || (p = (char *)H.Lines[H.Size - 2]) == NULL)
1923         return el_ring_bell();
1924 
1925     p = strdup(p);
1926     if (!p)
1927         return CSstay;
1928 
1929     ac = argify(p, &av);
1930     if (Repeat != NO_ARG)
1931         s = Repeat < ac ? insert_string(av[Repeat]) : el_ring_bell();
1932     else
1933         s = ac ? insert_string(av[ac - 1]) : CSstay;
1934 
1935     if (av)
1936         free(av);
1937     free(p);
1938 
1939     return s;
1940 }
1941 
1942 static el_keymap_t Map[64] = {
1943     {   CTL('@'),       mk_set          },
1944     {   CTL('A'),       beg_line        },
1945     {   CTL('B'),       bk_char         },
1946     {   CTL('D'),       del_char        },
1947     {   CTL('E'),       end_line        },
1948     {   CTL('F'),       fd_char         },
1949     {   CTL('G'),       el_ring_bell    },
1950     {   CTL('H'),       bk_del_char     },
1951     {   CTL('I'),       c_complete      },
1952     {   CTL('J'),       accept_line     },
1953     {   CTL('K'),       kill_line       },
1954     {   CTL('L'),       refresh         },
1955     {   CTL('M'),       accept_line     },
1956     {   CTL('N'),       h_next          },
1957     {   CTL('O'),       el_ring_bell    },
1958     {   CTL('P'),       h_prev          },
1959     {   CTL('Q'),       el_ring_bell    },
1960     {   CTL('R'),       h_search        },
1961     {   CTL('S'),       el_ring_bell    },
1962     {   CTL('T'),       transpose       },
1963     {   CTL('U'),       el_ring_bell    },
1964     {   CTL('V'),       quote           },
1965     {   CTL('W'),       bk_kill_word    },
1966     {   CTL('X'),       exchange        },
1967     {   CTL('Y'),       yank            },
1968 #ifdef SYSTEM_IS_WIN32
1969     {   CTL('Z'),       end_of_input    },
1970 #else
1971     {   CTL('Z'),       el_ring_bell    },
1972 #endif
1973     {   CTL('['),       meta            },
1974     {   CTL(']'),       move_to_char    },
1975     {   CTL('^'),       el_ring_bell    },
1976     {   CTL('_'),       el_ring_bell    },
1977     {   0,              NULL            }
1978 };
1979 
1980 static el_keymap_t   MetaMap[64]= {
1981     {   CTL('H'),       bk_kill_word    },
1982     {   DEL,            bk_kill_word    },
1983     {   ' ',            mk_set          },
1984     {   '.',            last_argument   },
1985     {   '<',            h_first         },
1986     {   '>',            h_last          },
1987     {   '?',            c_possible      },
1988     {   'b',            bk_word         },
1989     {   'c',            case_cap_word   },
1990     {   'd',            fd_kill_word    },
1991     {   'f',            fd_word         },
1992     {   'l',            case_down_word  },
1993     {   'm',            toggle_meta_mode },
1994     {   'u',            case_up_word    },
1995     {   'y',            yank            },
1996     {   'w',            copy_region     },
1997     {   0,              NULL            }
1998 };
1999 
find_key_in_map(int key,el_keymap_t map[],size_t mapsz)2000 static size_t find_key_in_map(int key, el_keymap_t map[], size_t mapsz)
2001 {
2002     size_t i;
2003 
2004     for (i = 0; i < mapsz && map[i].Function; i++) {
2005         if (map[i].Key == key)
2006             return i;
2007     }
2008 
2009     if (i < mapsz)
2010         return i;
2011 
2012     return mapsz;
2013 }
2014 
el_bind_key_in_map(int key,el_keymap_func_t function,el_keymap_t map[],size_t mapsz)2015 static el_status_t el_bind_key_in_map(int key, el_keymap_func_t function, el_keymap_t map[], size_t mapsz)
2016 {
2017     size_t creat, pos = find_key_in_map(key, map, mapsz);
2018 
2019     /* Must check that pos is not the next to last array position,
2020      * otherwise we will write out-of-bounds to terminate the list. */
2021     if (pos + 1 >= mapsz) {
2022         errno = ENOMEM;
2023         return CSeof;
2024     }
2025 
2026     /* Add at end, create new? */
2027     creat = map[pos].Function == NULL;
2028 
2029     /* A new key so have to add it to end */
2030     map[pos].Key      = key;
2031     map[pos].Function = function;
2032 
2033     /* Terminate list */
2034     if (creat) {
2035         map[pos + 1].Key      = 0;
2036         map[pos + 1].Function = NULL;
2037     }
2038 
2039     return CSdone;
2040 }
2041 
el_bind_key(int key,el_keymap_func_t function)2042 el_status_t el_bind_key(int key, el_keymap_func_t function)
2043 {
2044     return el_bind_key_in_map(key, function, Map, NELEMS(Map));
2045 }
2046 
el_bind_key_in_metamap(int key,el_keymap_func_t function)2047 el_status_t el_bind_key_in_metamap(int key, el_keymap_func_t function)
2048 {
2049     return el_bind_key_in_map(key, function, MetaMap, NELEMS(MetaMap));
2050 }
2051 
rl_set_getc_func(rl_getc_func_t * func)2052 rl_getc_func_t *rl_set_getc_func(rl_getc_func_t *func)
2053 {
2054     rl_getc_func_t *old = rl_getc_function;
2055     rl_getc_function = func;
2056     return old;
2057 }
2058 
2059 /**
2060  * Local Variables:
2061  *  c-file-style: "k&r"
2062  *  c-basic-offset: 4
2063  * End:
2064  */
2065