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