1 /*
2 **
3 ** Main editing routines for editline library.
4 */
5 #include <ctype.h>
6 #include "editline.h"
7 #include "edlproto.h"
8 #include "reader.h"
9 #include "readlib.h"
10
11 /*
12 ** Manifest constants.
13 */
14
15 #if 0
16 #define SCREEN_WIDTH 80
17 #define SCREEN_ROWS 24
18 #else
19 #define SCREEN_WIDTH SCREEN_COLS
20 #endif
21
22 #define NO_ARG (-1)
23 #define DEL 127
24 #define CTL(x) ((x) & 0x1F)
25 #define ISCTL(x) ((x) && (x) < ' ')
26 #define UNCTL(x) ((x) + 64)
27 #define META(x) ((x) | 0x80)
28
29 #ifdef NOMETA
30 #define ISMETA(x) ((x) & 0x80)
31 #else
32 #define ISMETA(x) (0)
33 #endif
34
35 #define UNMETA(x) ((x) & 0x7F)
36
37 #if !defined(HIST_SIZE)
38 #define HIST_SIZE 20
39 #endif
40
41 #ifndef ALVOID
42 #define ALVOID void
43 #endif
44
45 /*
46 ** Key to command mapping.
47 */
48 typedef struct _KEYMAP {
49 char Key;
50 STATUS(*Func) (ALVOID);
51 } KEYMAP;
52
53 STATIC KEYMAP MetaMap[16];
54 STATIC KEYMAP Map[33];
55
56 /*
57 ** Command history structure.
58 */
59 typedef struct _HISTORY {
60 int Size;
61 int Pos;
62 char *Lines[HIST_SIZE];
63 } HISTORY;
64
65 /*
66 * Globals.
67 */
68 static int rl_eof = 0x04 ; /* ^D */
69 static int rl_erase= 0x7f ; /* DEL */
70 static int rl_intr = 0x03 ; /* ^C */
71 static int rl_kill = 0x15 ; /* ^U */
72 static int rl_quit = 0x1c ; /* ^\ */
73
74 STATIC char NilStr[] = "";
75 STATIC char *Input = NilStr;
76 STATIC char *Line = NULL;
77 STATIC const char *Prompt = NULL;
78 STATIC char *Yanked = NULL;
79 STATIC char *Screen;
80 STATIC char NEWLINE[] = CRLF;
81 STATIC HISTORY H;
82 STATIC int Repeat;
83 STATIC int End;
84 STATIC int Mark;
85 STATIC int OldPoint;
86 STATIC int Point;
87 STATIC int PushBack;
88 STATIC int Pushed;
89 STATIC size_t Length;
90 STATIC size_t ScreenCount;
91 STATIC size_t ScreenSize;
92 static char *backspace = NULL;
93 /* NOTE: these MUST be NilStr and *not* NULL */
94 #if defined(HAVE_LIBTERMCAP)
95 char *rev_on = NilStr;
96 char *rev_off = NilStr;
97 char *blink_on = NilStr;
98 char *clear_attr = NilStr;
99 char *bold_on = NilStr;
100 char *clear_scr = NilStr;
101 static char *home = NilStr;
102 char *cur_right = NilStr;
103 char *del_eol = NilStr;
104 #endif
105
106 #if 0
107 STATIC int TTYwidth;
108 STATIC int TTYrows;
109 #endif
110
111 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
112 #ifdef NOMETA
113 STATIC int rl_meta_chars = 0;
114 #endif
115
116 /*
117 ** Declarations.
118 */
119 #if defined(_OS2) || defined(__MSDOS__)
120 STATIC int getakey(char *chr);
121 #endif
122
123 STATIC char *editinput(ALVOID);
124
125 /*
126 ** TTY input/output functions.
127 */
128
129 static void
TTYflush(void)130 TTYflush(void)
131 {
132 if (ScreenCount) {
133 (void) write(STDOUT_FILENO, Screen, ScreenCount);
134 ScreenCount = 0;
135 }
136 }
137
138 STATIC void
TTYput(const int c)139 TTYput(const int c)
140 {
141 Screen[ScreenCount] = (char)c;
142 if (++ScreenCount >= ScreenSize - 1) {
143 ScreenSize += SCREEN_INC;
144 Screen = (char *) realloc(Screen, (sizeof(char) * ScreenSize));
145 }
146 }
147
148 STATIC void
TTYputs(const char * p)149 TTYputs(const char *p)
150 {
151 fflush(stdout);
152 while (*p)
153 TTYput(*p++);
154 }
155
156 STATIC void
TTYshow(int c)157 TTYshow(int c)
158 {
159 if (c == DEL) {
160 TTYput('^');
161 TTYput('?');
162 } else if (ISCTL(c)) {
163 TTYput('^');
164 TTYput(UNCTL(c));
165 }
166 #ifdef NOMETA
167 else if (rl_meta_chars && ISMETA(c)) {
168 TTYput('M');
169 TTYput('-');
170 TTYput(UNMETA(c));
171 }
172 #endif
173 else
174 TTYput(c);
175 }
176
177 STATIC void
TTYstring(char * p)178 TTYstring(char *p)
179 {
180 while (*p)
181 TTYshow(*p++);
182 }
183
184 STATIC UNSI
TTYget(void)185 TTYget(void)
186 {
187 char c;
188
189 TTYflush();
190 if (Pushed) {
191 Pushed = 0;
192 return PushBack;
193 }
194 if (*Input)
195 return *Input++;
196 #if defined(_OS2) || defined(__MSDOS__)
197 return getakey(&c) == 1 ? c : EOF;
198 #else
199 return read(0, &c, (size_t) 1) == 1 ? c : EOF;
200 #endif
201 }
202
203 #define TTYback() ((backspace != NULL) ? TTYputs((char *)backspace) : TTYput('\b'))
204
205 STATIC void
TTYbackn(int n)206 TTYbackn(int n)
207 {
208 while (--n >= 0)
209 TTYback();
210 }
211
212 #ifdef HAVE_LIBTERMCAP
213 static char attr_buf[1024];
214 #endif
215
216 void
TTYinfo(void)217 TTYinfo(void)
218 {
219 static int init = FALSE;
220
221 #if defined(HAVE_LIBTERMCAP)
222 char *tttmp, *stand_out, *stand_end;
223 const char *term;
224 static char ebuff[2048]; /* !!!!! */
225 char *bp;
226 const int old_row = get_ScrnRows();
227 const int old_col = get_ScrnCols();
228 int tmp_row = 0, tmp_col = 0;
229 #endif
230
231 if (init)
232 return;
233 init = TRUE;
234 #if defined(TIOCGWINSZ) && defined(SIGWINCH)
235 getwinders(0);
236 #endif
237
238 #if defined(HAVE_LIBTERMCAP)
239 bp = attr_buf;
240 if ((term = getenv("TERM")) == NULL)
241 term = "dumb";
242
243 if (0 < tgetent(ebuff, term)) {
244 tmp_row = tgetnum("li");
245 tmp_col = tgetnum("co");
246 blink_on = tgetstr("mb", &bp);
247 rev_on = tgetstr("mr", &bp);
248 clear_attr = tgetstr("me", &bp);
249 bold_on = tgetstr("md", &bp);
250 clear_scr = tgetstr("cl", &bp);
251 home = tgetstr("ho", &bp);
252 backspace = tgetstr("le", &bp);
253 cur_right = tgetstr("nd", &bp);
254 del_eol = tgetstr("ce", &bp);
255 tttmp = tgetstr("pc", &bp);
256 stand_out = tgetstr("so", &bp);
257 stand_end = tgetstr("se", &bp);
258 PC = tttmp ? *tttmp : 0;
259 rev_off = clear_attr;
260 if (rev_on == NULL && stand_out != NULL) {
261 rev_on = stand_out;
262 rev_off = stand_end;
263 }
264 }
265 #ifdef TIOCGWINSZ
266 if (get_ScrnCols() < 1 || get_ScrnRows() < 1) /* invalid call to getwinders() */
267 #endif
268 {
269 set_ScrnCols((tmp_col < 2 ? old_col : tmp_col), ss_TTYinfo);
270 set_ScrnRows((tmp_row < 2 ? old_row : tmp_row), ss_TTYinfo);
271 }
272 #endif
273 /* default behavior for everyone */
274 if (get_ScrnCols() < 1 || get_ScrnRows() < 1) {
275 set_ScrnCols(SCREEN_COLS, ss_TTYinfo);
276 set_ScrnRows(SCREEN_ROWS, ss_TTYinfo);
277 }
278 }
279
280 /*
281 ** Print an array of words in columns.
282 */
283 static void
columns(int ac,char ** av)284 columns(int ac, char **av)
285 {
286 char *p;
287 int i;
288 int j;
289 int k;
290 int len;
291 int skip;
292 int longest;
293 int cols;
294
295 /* Find longest name, determine column count from that. */
296 for (longest = 0, i = 0; i < ac; i++)
297 if ((j = strlen((char *) av[i])) > longest)
298 longest = j;
299 #if 0
300 cols = TTYwidth / (longest + 3);
301 #endif
302 cols = get_ScrnCols() / (longest + 3);
303 TTYputs((char *) NEWLINE);
304 for (skip = ac / cols + 1, i = 0; i < skip; i++) {
305 for (j = i; j < ac; j += skip) {
306 for (p = av[j], len = strlen((char *) p), k = len - 1; k >= 0; k--, p++)
307 TTYput(*p);
308 if (j + skip < ac)
309 while (++len < longest + 3)
310 TTYput(' ');
311 }
312 TTYputs((char *) NEWLINE);
313 }
314 }
315
316 STATIC void
reposition(void)317 reposition(void)
318 {
319 int i;
320 char *p;
321
322 TTYput('\r');
323 TTYputs(Prompt);
324 for (i = Point - 1, p = Line; i >= 0; i--, p++)
325 TTYshow(*p);
326 }
327
328 STATIC void
left(STATUS Change)329 left(STATUS Change)
330 {
331 TTYback();
332 if (Point) {
333 if (ISCTL(Line[Point - 1]))
334 TTYback();
335 #ifdef NOMETA
336 else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
337 TTYback();
338 TTYback();
339 }
340 #endif
341 }
342 if (Change == CSmove)
343 Point--;
344 }
345
346 STATIC void
right(STATUS Change)347 right(STATUS Change)
348 {
349 TTYshow(Line[Point]);
350 if (Change == CSmove)
351 Point++;
352 }
353
354 STATIC STATUS
ring_bell(void)355 ring_bell(void)
356 {
357 /* extern int silent ; */
358
359 if (!silent) {
360 TTYput('\07');
361 TTYflush();
362 }
363 return CSstay;
364 }
365
366 STATIC STATUS
do_macro(int c)367 do_macro(int c)
368 {
369 char name[4];
370
371 name[0] = '_';
372 name[1] = (char) c;
373 name[2] = '_';
374 name[3] = '\0';
375
376 if ((Input = (char *) getenv((char *) name)) == NULL) {
377 Input = NilStr;
378 return ring_bell();
379 }
380 return CSstay;
381 }
382
383 STATIC STATUS
do_forward(STATUS move)384 do_forward(STATUS move)
385 {
386 int i;
387 char *p;
388
389 i = 0;
390 do {
391 p = &Line[Point];
392 for (; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
393 if (move == CSmove)
394 right(CSstay);
395
396 for (; Point < End && isalnum(*p); Point++, p++)
397 if (move == CSmove)
398 right(CSstay);
399
400 if (Point == End)
401 break;
402 } while (++i < Repeat);
403
404 return CSstay;
405 }
406
407 STATIC STATUS
do_case(CASE type)408 do_case(CASE type)
409 {
410 int i;
411 int end;
412 int count;
413 char *p;
414
415 (void) do_forward(CSstay);
416 if (OldPoint != Point) {
417 if ((count = Point - OldPoint) < 0)
418 count = -count;
419 Point = OldPoint;
420 if ((end = Point + count) > End)
421 end = End;
422 for (i = Point, p = &Line[i]; i < end; i++, p++) {
423 if (type == TOupper) {
424 if (islower(*p))
425 *p = toupper(*p);
426 } else if (isupper(*p))
427 *p = tolower(*p);
428 right(CSmove);
429 }
430 }
431 return CSstay;
432 }
433
434 STATIC STATUS
case_down_word(void)435 case_down_word(void)
436 {
437 return do_case(TOlower);
438 }
439
440 STATIC STATUS
case_up_word(void)441 case_up_word(void)
442 {
443 return do_case(TOupper);
444 }
445
446 STATIC void
ceol(void)447 ceol(void)
448 {
449 int extras;
450 int i;
451 char *p;
452
453 for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
454 TTYput(' ');
455 if (ISCTL(*p)) {
456 TTYput(' ');
457 extras++;
458 }
459 #ifdef NOMETA
460 else if (rl_meta_chars && ISMETA(*p)) {
461 TTYput(' ');
462 TTYput(' ');
463 extras += 2;
464 }
465 #endif
466 }
467
468 for (i += extras; i > Point; i--)
469 TTYback();
470 }
471
472 STATIC void
clear_line(void)473 clear_line(void)
474 {
475 Point = (0 - strlen(Prompt));
476 TTYput('\r');
477 ceol();
478 Point = 0;
479 End = 0;
480 Line[0] = '\0';
481 }
482
483 STATIC STATUS
insert_string(char * p)484 insert_string(char *p)
485 {
486 size_t len;
487 int i;
488 char *snew;
489 char *q;
490
491 len = strlen((char *) p);
492 if (End + len >= Length) {
493 if ((snew = (char *) malloc((Length + len + MEM_INC))) == NULL)
494 return CSstay;
495 if (Length) {
496 COPYFROMTO(snew, Line, Length);
497 free(Line);
498 }
499 Line = snew;
500 Length += len + MEM_INC;
501 }
502 for (q = &Line[Point], i = (End - Point) - 1; i >= 0; i--)
503 q[len + i] = q[i];
504 COPYFROMTO(&Line[Point], p, len);
505 End += len;
506 Line[End] = '\0';
507 TTYstring(&Line[Point]);
508 Point += len;
509
510 if (Point == End)
511 return CSstay;
512 else
513 return CSmove;
514 }
515
516
517 STATIC char *
next_hist(void)518 next_hist(void)
519 {
520 return H.Pos >= H.Size - 1 ? (char *) 0 : H.Lines[++H.Pos];
521 }
522
523 STATIC char *
prev_hist(void)524 prev_hist(void)
525 {
526 return H.Pos == 0 ? (char *) 0 : H.Lines[--H.Pos];
527 }
528
529 STATIC STATUS
do_insert_hist(char * p)530 do_insert_hist(char *p)
531 {
532 if (p == NULL)
533 return ring_bell();
534 Point = 0;
535 reposition();
536 ceol();
537 End = 0;
538 return insert_string(p);
539 }
540
541 STATIC STATUS
do_hist(char * (* move)(ALVOID))542 do_hist(char *(*move) (ALVOID))
543 {
544 char *p;
545 int i;
546
547 i = 0;
548 do {
549 if ((p = (*move) ()) == NULL)
550 return ring_bell();
551 } while (++i < Repeat);
552 return do_insert_hist(p);
553 }
554
555 STATIC STATUS
h_next(void)556 h_next(void)
557 {
558 return do_hist(next_hist);
559 }
560
561 STATIC STATUS
h_prev(void)562 h_prev(void)
563 {
564 return do_hist(prev_hist);
565 }
566
567 STATIC STATUS
h_first(void)568 h_first(void)
569 {
570 return do_insert_hist(H.Lines[H.Pos = 0]);
571 }
572
573 STATIC STATUS
h_last(void)574 h_last(void)
575 {
576 return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
577 }
578
579 /*
580 ** Return zero if pat appears as a substring in text.
581 */
582 STATIC int
substrcmp(const char * text,const char * pat,size_t len)583 substrcmp(const char *text, const char *pat, size_t len)
584 {
585 char c;
586
587 if ((c = *pat) == '\0')
588 return *text == '\0';
589 for (; *text; text++)
590 if ((char) *text == c && strncmp(text, pat, len) == 0)
591 return 0;
592 return 1;
593 }
594
595 STATIC char *
search_hist(char * search,char * (* move)(ALVOID))596 search_hist(char *search, char *(*move) (ALVOID))
597 {
598 static char *old_search = NULL;
599 size_t len;
600 int pos;
601 #ifdef no_proto
602 int (*match) ();
603 #else
604 int (*match) (const char *, const char *, size_t);
605 #endif
606 char *pat;
607
608 /* Save or get remembered search pattern. */
609 if (search && *search) {
610 if (old_search)
611 free(old_search);
612 old_search = (char *) strdup(search);
613 } else {
614 if (old_search == NULL || *old_search == '\0')
615 return NULL;
616 search = old_search;
617 }
618
619 /* Set up pattern-finder. */
620 if (*search == '^') {
621 match = strncmp;
622 pat = (char *) (search + 1);
623 } else {
624 match = substrcmp;
625 pat = (char *) search;
626 }
627 len = strlen(pat);
628
629 for (pos = H.Pos; (*move) () != NULL;)
630 if ((*match) ((char *) H.Lines[H.Pos], pat, len) == 0)
631 return H.Lines[H.Pos];
632 H.Pos = pos;
633 return NULL;
634 }
635
636 STATIC STATUS
h_search(void)637 h_search(void)
638 {
639 static int Searching = FALSE;
640 const char *old_prompt;
641 char *(*move) (ALVOID);
642 char *p;
643
644 if (Searching)
645 return ring_bell();
646 Searching = TRUE;
647
648 clear_line();
649 old_prompt = Prompt;
650 Prompt = "Search: ";
651 TTYputs(Prompt);
652 #ifdef no_proto
653 move = Repeat == NO_ARG ? prev_hist : next_hist;
654 #else
655 move = Repeat == NO_ARG ? (char *(*)(void)) prev_hist : (char *(*)(void)) next_hist;
656 #endif
657 p = search_hist(editinput(), move);
658 clear_line();
659 Prompt = old_prompt;
660 TTYputs(Prompt);
661
662 Searching = 0;
663 return do_insert_hist(p);
664 }
665
666 STATIC STATUS
fd_char(void)667 fd_char(void)
668 {
669 int i;
670
671 i = 0;
672 do {
673 if (Point >= End)
674 break;
675 right(CSmove);
676 } while (++i < Repeat);
677 return CSstay;
678 }
679
680 STATIC void
save_yank(int begin,int i)681 save_yank(int begin, int i)
682 {
683 if (Yanked) {
684 free(Yanked);
685 Yanked = NULL;
686 }
687 if (i < 1)
688 return;
689
690 if ((Yanked = (char *) malloc((size_t) (i + 1))) != NULL) {
691 COPYFROMTO(Yanked, &Line[begin], (size_t) i);
692 Yanked[i] = '\0';
693 }
694 }
695
696 STATIC STATUS
delete_string(int count)697 delete_string(int count)
698 {
699 int i;
700 char *p;
701
702 if (count <= 0 || End == Point)
703 return ring_bell();
704
705 if (count == 1 && Point == End - 1) {
706 /* Optimize common case of delete at end of line. */
707 End--;
708 p = &Line[Point];
709 i = 1;
710 TTYput(' ');
711 if (ISCTL(*p)) {
712 i = 2;
713 TTYput(' ');
714 }
715 #ifdef NOMETA
716 else if (rl_meta_chars && ISMETA(*p)) {
717 i = 3;
718 TTYput(' ');
719 TTYput(' ');
720 }
721 #endif
722 TTYbackn(i);
723 *p = '\0';
724 return CSmove;
725 }
726 if (Point + count > End && (count = End - Point) <= 0)
727 return CSstay;
728
729 if (count > 1)
730 save_yank(Point, count);
731
732 for (p = &Line[Point], i = End - (Point + count); i >= 0; i--, p++)
733 p[0] = p[count];
734 ceol();
735 End -= count;
736 TTYstring(&Line[Point]);
737 return CSmove;
738 }
739
740 STATIC STATUS
bk_char(void)741 bk_char(void)
742 {
743 int i;
744
745 i = 0;
746 do {
747 if (Point == 0)
748 break;
749 left(CSmove);
750 } while (++i < Repeat);
751
752 return CSstay;
753 }
754
755 STATIC STATUS
bk_del_char(void)756 bk_del_char(void)
757 {
758 int i;
759
760 i = 0;
761 do {
762 if (Point == 0)
763 break;
764 left(CSmove);
765 } while (++i < Repeat);
766
767 return delete_string(i);
768 }
769
770 STATIC STATUS
del_char(void)771 del_char(void)
772 {
773 return delete_string(Repeat == NO_ARG ? 1 : Repeat);
774 }
775
776 STATIC STATUS
redisplay(void)777 redisplay(void)
778 {
779 TTYputs((char *) NEWLINE);
780 TTYputs(Prompt);
781 TTYstring(Line);
782 return CSmove;
783 }
784
785 STATIC STATUS
kill_line(void)786 kill_line(void)
787 {
788 int i;
789
790 if (Repeat != NO_ARG) {
791 if (Repeat < Point) {
792 i = Point;
793 Point = Repeat;
794 reposition();
795 (void) delete_string(i - Point);
796 } else if (Repeat > Point) {
797 right(CSmove);
798 (void) delete_string(Repeat - Point - 1);
799 }
800 return CSmove;
801 }
802 save_yank(Point, End - Point);
803 Line[Point] = '\0';
804 ceol();
805 End = Point;
806 return CSstay;
807 }
808
809 STATIC STATUS
insert_char(int c)810 insert_char(int c)
811 {
812 STATUS s;
813 static char buff[2];
814 char *p;
815 char *q;
816 int i;
817
818 if (Repeat == NO_ARG || Repeat < 2) {
819 buff[0] = (char) c;
820 buff[1] = '\0';
821 return insert_string(buff);
822 }
823 if ((p = (char *) malloc((size_t) (Repeat + 1))) == NULL)
824 return CSstay;
825 for (i = Repeat - 1, q = p; i >= 0; i--)
826 *q++ = (char) c;
827 *q = '\0';
828 Repeat = 0;
829 s = insert_string(p);
830 free(p);
831 return s;
832 }
833
834 STATIC STATUS
meta(void)835 meta(void)
836 {
837 UNSI c;
838 KEYMAP *kp;
839
840 if ((c = TTYget()) == EOF)
841 return CSeof;
842 #if defined(ANSI_ARROWS)
843 /* Also include VT-100 arrows. */
844 if (c == '[' || c == 'O')
845 switch (c = TTYget()) {
846 default:
847 return ring_bell();
848 case EOF:
849 return CSeof;
850 case KUP:
851 return h_prev(); /* ansi arrow keys */
852 case KDN:
853 return h_next();
854 case KRT:
855 return fd_char();
856 case KLT:
857 return bk_char();
858 case KCE:
859 strcpy((char *) Line, "N"); /* center of keypad */
860 return CSdone;
861 case KF1:
862 strcpy((char *) Line, "help"); /* vt100 function keys f1 */
863 return CSdone;
864 case KF2:
865 strcpy((char *) Line, "tag help"); /* f2 */
866 return CSdone;
867 case KF3:
868 strcpy((char *) Line, "tag list"); /* f3 */
869 return CSdone;
870 case KF4:
871 strcpy((char *) Line, "qlist"); /* f4 */
872 return CSdone;
873 case KF5:
874 strcpy((char *) Line, "show terms"); /* f5 */
875 return CSdone;
876 case KF0:
877 strcpy((char *) Line, "next"); /* f10 */
878 return CSdone;
879 #ifndef SYS_UNIX /* non-unix */
880 case KPU:
881 strcpy((char *) Line, "-"); /* page up */
882 return CSdone;
883 case KPD:
884 strcpy((char *) Line, "+"); /* page down */
885 return CSdone;
886 case KHM:
887 strcpy((char *) Line, "1"); /* home */
888 return CSdone;
889 case KEN:
890 strcpy((char *) Line, "last"); /* end */
891 return CSdone;
892 case KIN:
893 return ring_bell();
894 case KDL:
895 return del_char();
896 #else /* linux, ibcs2, some other unixes */
897 case '2':
898 if ((c = TTYget()) != '1')
899 break;
900 if ((c = TTYget()) != '~')
901 break;
902 strcpy((char *) Line, "next"); /* f10 */
903 return CSdone;
904 case '3':
905 if ((c = TTYget()) != '\176') /* delete */
906 break;
907 return del_char();
908 case '5':
909 if ((c = TTYget()) != '\176') /* page up */
910 break;
911 strcpy(Line, "-");
912 return CSdone;
913 case '6':
914 if ((c = TTYget()) != '\176') /* page down */
915 break;
916 strcpy(Line, "+");
917 return CSdone;
918 case '1':
919 if ((c = TTYget()) != '\176') /* home */
920 break;
921 strcpy(Line, "1");
922 return CSdone;
923 case '4':
924 if ((c = TTYget()) != '\176') /* end */
925 break;
926 strcpy(Line, "last");
927 return CSdone;
928 case '[':
929 switch (c = TTYget()) { /* ansi function keys */
930 case 'A':
931 strcpy(Line, "help"); /* f1 */
932 return CSdone;
933 case 'B':
934 strcpy(Line, "tag help"); /* f2 */
935 return CSdone;
936 case 'C':
937 strcpy(Line, "tag list"); /* f3 */
938 return CSdone;
939 case 'D':
940 strcpy(Line, "qlist"); /* f4 */
941 return CSdone;
942 case 'E':
943 strcpy(Line, "show terms"); /* f5 */
944 return CSdone;
945 case 'J':
946 strcpy(Line, "next"); /* f10 */
947 return CSdone;
948 }
949 #endif
950 }
951 #endif /* defined(ANSI_ARROWS) */
952
953 if (isdigit(c)) {
954 for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c);)
955 Repeat = Repeat * 10 + c - '0';
956 Pushed = 1;
957 PushBack = c;
958 return CSstay;
959 }
960 if (isupper(c))
961 return do_macro(c);
962 for (OldPoint = Point, kp = MetaMap; kp->Func; kp++)
963 if (kp->Key == (char) c)
964 return (*kp->Func) ();
965
966 return ring_bell();
967 }
968
969 STATIC STATUS
emacs(int c)970 emacs(int c)
971 {
972 STATUS s;
973 KEYMAP *kp;
974 #ifdef NOMETA
975 if (ISMETA(c)) {
976 Pushed = 1;
977 PushBack = UNMETA(c);
978 return meta();
979 }
980 #endif
981 for (kp = Map; kp->Func; kp++)
982 if (kp->Key == (char) c)
983 break;
984 s = kp->Func ? (*kp->Func) () : insert_char(c);
985 if (!Pushed)
986 /* No pushback means no repeat count; hacky, but true. */
987 Repeat = NO_ARG;
988 return s;
989 }
990
991 STATIC STATUS
TTYspecial(int c)992 TTYspecial(int c)
993 {
994 #ifdef NOMETA
995 if (ISMETA(c))
996 return CSdispatch;
997 #endif
998 if (c == rl_erase || c == DEL)
999 return bk_del_char();
1000 if (c == rl_kill) {
1001 if (Point != 0) {
1002 Point = 0;
1003 reposition();
1004 }
1005 Repeat = NO_ARG;
1006 return kill_line();
1007 }
1008 if (c == rl_intr || c == rl_quit) {
1009 Point = End = 0;
1010 Line[0] = '\0';
1011 return redisplay();
1012 }
1013 if (c == rl_eof && Point == 0 && End == 0)
1014 return CSeof;
1015
1016 return CSdispatch;
1017 }
1018
1019 STATIC char *
editinput(void)1020 editinput(void)
1021 {
1022 /* extern const char *luxptr ; */
1023 UNSI c;
1024
1025 Repeat = NO_ARG;
1026 OldPoint = Point = Mark = End = 0;
1027 Line[0] = '\0';
1028 if (luxptr != NULL) { /* pre-load edit buffer with text */
1029 strcpy((char *) Line, luxptr);
1030 End = strlen((char *) Line);
1031 Point = End;
1032 reposition();
1033 Point = 0;
1034 reposition();
1035 }
1036 while ((c = TTYget()) != EOF)
1037 switch (TTYspecial(c)) {
1038 case CSdone:
1039 return Line;
1040 case CSeof:
1041 return NULL;
1042 case CSmove:
1043 reposition();
1044 break;
1045 case CSdispatch:
1046 switch (emacs(c)) {
1047 case CSdone:
1048 return Line;
1049 case CSeof:
1050 return NULL;
1051 case CSmove:
1052 reposition();
1053 break;
1054 case CSdispatch:
1055 case CSstay:
1056 break;
1057 }
1058 break;
1059 case CSstay:
1060 break;
1061 }
1062 return NULL;
1063 }
1064
1065 STATIC void
hist_add(const char * p)1066 hist_add(const char *p)
1067 {
1068 int i;
1069 char *tpr;
1070
1071 if ((tpr = strdup(p)) == NULL)
1072 return;
1073 if (H.Size < HIST_SIZE)
1074 H.Lines[H.Size++] = tpr;
1075 else {
1076 free(H.Lines[0]);
1077 for (i = 0; i < HIST_SIZE - 1; i++)
1078 H.Lines[i] = H.Lines[i + 1];
1079 H.Lines[i] = tpr;
1080 }
1081 H.Pos = H.Size - 1;
1082 }
1083
1084 /*
1085 ** For compatibility with FSF readline.
1086 */
1087 #if 0
1088 /* ARGSUSED0 */
1089 void
1090 rl_reset_terminal(char *p)
1091 {
1092 }
1093
1094 #endif
1095
1096 char *
readline(const char * prompt,scroll_command_t scrollflag)1097 readline(const char *prompt, scroll_command_t scrollflag)
1098 {
1099 char *line;
1100
1101 if (Line == NULL) {
1102 Length = MEM_INC;
1103 if ((Line = (char *) malloc(Length)) == NULL)
1104 return NULL;
1105 }
1106 TTYinfo();
1107 rl_ttyset(0);
1108 hist_add(NilStr);
1109 ScreenSize = SCREEN_INC;
1110 Screen = (char *) malloc(ScreenSize);
1111 if (prompt != NULL)
1112 Prompt = prompt;
1113 else
1114 Prompt = (char *) NilStr;
1115 /* Prompt = ((prompt != NULL) ? prompt : (char *) NilStr ) ; */
1116 TTYputs(Prompt);
1117 line = editinput();
1118 if (line != NULL) {
1119 line = (char *) strdup(line); /*???? */
1120 if (scrollflag == do_scroll)
1121 TTYputs((char *) NEWLINE);
1122 else if (scrollflag == no_scroll)
1123 TTYputs("\r");
1124 #ifdef ATPDBG
1125 else
1126 assert(scrollflag == no_scroll || scrollflag == do_scroll);
1127 #endif
1128 TTYflush();
1129 }
1130 rl_ttyset(1);
1131 free(Screen);
1132 free(H.Lines[--H.Size]);
1133 return (char *) line;
1134 }
1135
1136 void
add_history(const char * p)1137 add_history(const char *p)
1138 {
1139 if (p == NULL || *p == '\0')
1140 return;
1141
1142 #if defined(UNIQUE_HISTORY)
1143 if (H.Pos && strcmp(p, (char *) (H.Lines[H.Pos - 1])) == 0)
1144 return;
1145 #endif /* defined(UNIQUE_HISTORY) */
1146 hist_add( /* (char *) */ p);
1147 }
1148
1149
1150 STATIC STATUS
beg_line(void)1151 beg_line(void)
1152 {
1153 if (Point) {
1154 Point = 0;
1155 return CSmove;
1156 }
1157 return CSstay;
1158 }
1159
1160 STATIC STATUS
end_line(void)1161 end_line(void)
1162 {
1163 if (Point != End) {
1164 Point = End;
1165 return CSmove;
1166 }
1167 return CSstay;
1168 }
1169
1170 /*
1171 ** Move back to the beginning of the current word and return an
1172 ** allocated copy of it.
1173 */
1174 STATIC char *
find_word(void)1175 find_word(void)
1176 {
1177 static char SEPS[] = "#;&|^$=`'{}()<>\n\t ";
1178 char *p;
1179 char *mnew;
1180 size_t len;
1181
1182 for (p = &Line[Point]; p > Line && strchr(SEPS, (char) p[-1]) == NULL; p--)
1183 continue;
1184 len = (Point - (p - Line)) + 1;
1185 if ((mnew = (char *) malloc((len + 3))) == NULL) /* we add 3 for wild cards used by ms-dos */
1186 return NULL;
1187 COPYFROMTO(mnew, p, len);
1188 mnew[len - 1] = '\0';
1189 return mnew;
1190 }
1191
1192 STATIC STATUS
c_complete(void)1193 c_complete(void)
1194 {
1195 char *p;
1196 char *word;
1197 int unique;
1198 STATUS s;
1199
1200 word = find_word();
1201 p = (char *) rl_complete((char *) word, &unique);
1202 if (word)
1203 free(word);
1204 if (p && *p) {
1205 s = insert_string(p);
1206 if (!unique)
1207 (void) ring_bell();
1208 free(p);
1209 return s;
1210 }
1211 return ring_bell();
1212 }
1213
1214 STATIC STATUS
c_possible(void)1215 c_possible(void)
1216 {
1217 char **av;
1218 char *word;
1219 int ac;
1220
1221 word = find_word();
1222 ac = rl_list_possib((char *) word, (char ***) &av);
1223 if (word)
1224 free(word);
1225 if (ac) {
1226 columns(ac, av);
1227 while (--ac >= 0)
1228 free(av[ac]);
1229 free(av);
1230 return CSmove;
1231 }
1232 return ring_bell();
1233 }
1234
1235 STATIC STATUS
accept_line(void)1236 accept_line(void)
1237 {
1238 Line[End] = '\0';
1239 return CSdone;
1240 }
1241
1242 STATIC STATUS
transpose(void)1243 transpose(void)
1244 {
1245 char c;
1246
1247 if (Point) {
1248 if (Point == End)
1249 left(CSmove);
1250 c = Line[Point - 1];
1251 left(CSstay);
1252 Line[Point - 1] = Line[Point];
1253 TTYshow(Line[Point - 1]);
1254 Line[Point++] = c;
1255 TTYshow(c);
1256 }
1257 return CSstay;
1258 }
1259
1260 STATIC STATUS
quote(void)1261 quote(void)
1262 {
1263 UNSI c;
1264
1265 c = TTYget();
1266
1267 if (c == EOF)
1268 return CSeof;
1269 else
1270 return insert_char((int) c);
1271 }
1272
1273 STATIC STATUS
wipe(void)1274 wipe(void)
1275 {
1276 int i;
1277
1278 if (Mark > End)
1279 return ring_bell();
1280
1281 if (Point > Mark) {
1282 i = Point;
1283 Point = Mark;
1284 Mark = i;
1285 reposition();
1286 }
1287 return delete_string(Mark - Point);
1288 }
1289
1290 STATIC STATUS
mk_set(void)1291 mk_set(void)
1292 {
1293 Mark = Point;
1294 return CSstay;
1295 }
1296
1297 STATIC STATUS
exchange(void)1298 exchange(void)
1299 {
1300 UNSI c;
1301
1302 if ((c = TTYget()) != CTL('X')) {
1303 if (c == EOF)
1304 return CSeof;
1305 else
1306 return ring_bell();
1307 }
1308 if ((c = Mark) <= End) {
1309 Mark = Point;
1310 Point = c;
1311 return CSmove;
1312 }
1313 return CSstay;
1314 }
1315
1316 STATIC STATUS
yank(void)1317 yank(void)
1318 {
1319 if (Yanked && *Yanked)
1320 return insert_string(Yanked);
1321 return CSstay;
1322 }
1323
1324 STATIC STATUS
copy_region(void)1325 copy_region(void)
1326 {
1327 if (Mark > End)
1328 return ring_bell();
1329
1330 if (Point > Mark)
1331 save_yank(Mark, Point - Mark);
1332 else
1333 save_yank(Point, Mark - Point);
1334
1335 return CSstay;
1336 }
1337
1338 STATIC STATUS
move_to_char(void)1339 move_to_char(void)
1340 {
1341 UNSI c;
1342 int i;
1343 char *p;
1344
1345 if ((c = TTYget()) == EOF)
1346 return CSeof;
1347 for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1348 if (*p == (char) c) {
1349 Point = i;
1350 return CSmove;
1351 }
1352 return CSstay;
1353 }
1354
1355 STATIC STATUS
fd_word(void)1356 fd_word(void)
1357 {
1358 return do_forward(CSmove);
1359 }
1360
1361 STATIC STATUS
fd_kill_word(void)1362 fd_kill_word(void)
1363 {
1364 int i;
1365
1366 (void) do_forward(CSstay);
1367 if (OldPoint != Point) {
1368 i = Point - OldPoint;
1369 Point = OldPoint;
1370 return delete_string(i);
1371 }
1372 return CSstay;
1373 }
1374
1375 STATIC STATUS
bk_word(void)1376 bk_word(void)
1377 {
1378 int i;
1379 char *p;
1380
1381 i = 0;
1382 do {
1383 for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1384 left(CSmove);
1385
1386 for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1387 left(CSmove);
1388
1389 if (Point == 0)
1390 break;
1391 } while (++i < Repeat);
1392
1393 return CSstay;
1394 }
1395
1396 STATIC STATUS
bk_kill_word(void)1397 bk_kill_word(void)
1398 {
1399 (void) bk_word();
1400 if (OldPoint != Point)
1401 return delete_string(OldPoint - Point);
1402 return CSstay;
1403 }
1404
1405 STATIC int
argify(char * line,char *** avp)1406 argify(char *line, char ***avp)
1407 {
1408 char *c;
1409 char **p;
1410 char **snew;
1411 int ac;
1412 int i;
1413
1414 i = MEM_INC;
1415 /* if ((*avp = p = NEW(char*, i))== NULL) */
1416 if ((*avp = p = (char **) malloc((i * sizeof(c)))) == NULL)
1417 return 0;
1418
1419 for (c = line; isspace(*c); c++)
1420 continue;
1421 if (*c == '\n' || *c == '\0')
1422 return 0;
1423
1424 for (ac = 0, p[ac++] = c; *c && *c != '\n';) {
1425 if (isspace(*c)) {
1426 *c++ = '\0';
1427 if (*c && *c != '\n') {
1428 if (ac + 1 == i) {
1429 /* snew = NEW(char*, i + MEM_INC); */
1430 snew = (char **) malloc((i + MEM_INC) * sizeof(c));
1431 if (snew == NULL) {
1432 p[ac] = NULL;
1433 return ac;
1434 }
1435 COPYFROMTO(snew, p, i * sizeof(char **));
1436 i += MEM_INC;
1437 free(p);
1438 *avp = p = snew;
1439 }
1440 p[ac++] = c;
1441 }
1442 } else
1443 c++;
1444 }
1445 *c = '\0';
1446 p[ac] = NULL;
1447 return ac;
1448 }
1449
1450 STATIC STATUS
last_argument(void)1451 last_argument(void)
1452 {
1453 char **av;
1454 char *p;
1455 STATUS s;
1456 int ac;
1457
1458 if (H.Size == 1)
1459 return ring_bell();
1460
1461 p = H.Lines[H.Size - 2];
1462 if (p == NULL)
1463 return ring_bell();
1464
1465 if ((p = (char *) strdup((char *) p)) == NULL)
1466 return CSstay;
1467 ac = argify(p, &av);
1468
1469 if (Repeat != NO_ARG)
1470 s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1471 else
1472 s = ac ? insert_string(av[ac - 1]) : CSstay;
1473
1474 if (ac)
1475 free(av);
1476 free(p);
1477 return s;
1478 }
1479
1480 /*
1481
1482 * getakey() is primarily for reading the keyboard on non-unix systems
1483 * that don't understand termios or sgtty for setting raw key strokes mode.
1484 * Additionally, getakey() gets the raw keycode and builds the ANSI escape
1485 * sequences for special keys such as the arrow keys.
1486 *
1487 */
1488
1489 #if defined(_OS2) || defined(__MSDOS__)
1490
1491 STATIC int
getakey(char * chr)1492 getakey(char *chr)
1493 {
1494
1495 static int pushed = 0;
1496 static char c = 0;
1497 int t;
1498
1499 if (pushed > 1) {
1500 pushed--;
1501 *chr = '[';
1502 return 1;
1503 }
1504 if (pushed) {
1505 pushed = 0;
1506 *chr = c;
1507 return 1;
1508 }
1509 #if defined(DJGPP)
1510 t = getkey();
1511 if (t < 256) {
1512 *chr = (char) t;
1513 return 1;
1514 }
1515 t &= 0xFF; /* mask out high bits */
1516 #else
1517 #ifndef __EMX__
1518 while (!kbhit())
1519 A_DVPAUZ;
1520 #endif
1521 t = getch();
1522 if (t) {
1523 *chr = (char) t;
1524 return 1;
1525 }
1526 /* else extended character */
1527 t = getch();
1528 #endif
1529
1530 switch (t) {
1531 case 0x3b:
1532 c = KF1;
1533 break;
1534 case 0x3c:
1535 c = KF2;
1536 break;
1537 case 0x3d:
1538 c = KF3;
1539 break;
1540 case 0x3e:
1541 c = KF4;
1542 break;
1543 case 0x3f:
1544 c = KF5;
1545 break;
1546 case 0x40:
1547 c = KF6;
1548 break;
1549 case 0x41:
1550 c = KF7;
1551 break;
1552 case 0x42:
1553 c = KF8;
1554 break;
1555 case 0x43:
1556 c = KF9;
1557 break;
1558 case 0x44:
1559 c = KF0;
1560 break;
1561 case 0x47:
1562 c = KHM;
1563 break;
1564 case 0x48:
1565 c = KUP;
1566 break;
1567 case 0x49:
1568 c = KPU;
1569 break;
1570 case 0x4b:
1571 c = KLT;
1572 break;
1573 case 0x4c:
1574 c = KCE;
1575 break;
1576 case 0x4d:
1577 c = KRT;
1578 break;
1579 case 0x4f:
1580 c = KEN;
1581 break;
1582 case 0x50:
1583 c = KDN;
1584 break;
1585 case 0x51:
1586 c = KPD;
1587 break;
1588 case 0x52:
1589 c = KIN;
1590 break;
1591 case 0x53:
1592 c = KDL;
1593 break;
1594 default:
1595 c = 0;
1596 }
1597 if (c) {
1598 pushed = 2;
1599 *chr = ESC;
1600 return 1;
1601 } else
1602 return 0;
1603 }
1604 #endif
1605
1606 void
rl_initialize(void)1607 rl_initialize(void)
1608 {
1609 MetaMap[0].Key = CTL('H');
1610 MetaMap[0].Func = bk_kill_word;
1611 MetaMap[1].Key = DEL;
1612 MetaMap[1].Func = bk_kill_word;
1613 MetaMap[2].Key = ' ';
1614 MetaMap[2].Func = mk_set;
1615 MetaMap[3].Key = '.';
1616 MetaMap[3].Func = last_argument;
1617 MetaMap[4].Key = '<';
1618 MetaMap[4].Func = h_first;
1619 MetaMap[5].Key = '>';
1620 MetaMap[5].Func = h_last;
1621 MetaMap[6].Key = '?';
1622 MetaMap[6].Func = c_possible;
1623 MetaMap[7].Key = 'b';
1624 MetaMap[7].Func = bk_word;
1625 MetaMap[8].Key = 'd';
1626 MetaMap[8].Func = fd_kill_word;
1627 MetaMap[9].Key = 'f';
1628 MetaMap[9].Func = fd_word;
1629 MetaMap[10].Key = 'l';
1630 MetaMap[10].Func = case_down_word;
1631 MetaMap[11].Key = 'u';
1632 MetaMap[11].Func = case_up_word;
1633 MetaMap[12].Key = 'y';
1634 MetaMap[12].Func = yank;
1635 MetaMap[13].Key = 'w';
1636 MetaMap[13].Func = copy_region;
1637 MetaMap[14].Key = '\0';
1638 MetaMap[14].Func = 0; /* null pointer */
1639
1640 Map[0].Key = CTL('@');
1641 Map[0].Func = ring_bell;
1642 Map[1].Key = CTL('A');
1643 Map[1].Func = beg_line;
1644 Map[2].Key = CTL('B');
1645 Map[2].Func = bk_char;
1646 Map[3].Key = CTL('D');
1647 Map[3].Func = del_char;
1648 Map[4].Key = CTL('E');
1649 Map[4].Func = end_line;
1650 Map[5].Key = CTL('F');
1651 Map[5].Func = fd_char;
1652 Map[6].Key = CTL('G');
1653 Map[6].Func = ring_bell;
1654 Map[7].Key = CTL('H');
1655 Map[7].Func = bk_del_char;
1656 Map[8].Key = CTL('I');
1657 Map[8].Func = c_complete;
1658 Map[9].Key = CTL('J');
1659 Map[9].Func = accept_line;
1660 Map[10].Key = CTL('K');
1661 Map[10].Func = kill_line;
1662 Map[11].Key = CTL('L');
1663 Map[11].Func = redisplay;
1664 Map[12].Key = CTL('M');
1665 Map[12].Func = accept_line;
1666 Map[13].Key = CTL('N');
1667 Map[13].Func = h_next;
1668 Map[14].Key = CTL('O');
1669 Map[14].Func = ring_bell;
1670 Map[15].Key = CTL('P');
1671 Map[15].Func = h_prev;
1672 Map[16].Key = CTL('Q');
1673 Map[16].Func = ring_bell;
1674 Map[17].Key = CTL('R');
1675 Map[17].Func = h_search;
1676 Map[18].Key = CTL('S');
1677 Map[18].Func = ring_bell;
1678 Map[19].Key = CTL('T');
1679 Map[19].Func = transpose;
1680 Map[20].Key = CTL('U');
1681 Map[20].Func = ring_bell;
1682 Map[21].Key = CTL('V');
1683 Map[21].Func = quote;
1684 Map[22].Key = CTL('W');
1685 Map[22].Func = wipe;
1686 Map[23].Key = CTL('X');
1687 Map[23].Func = exchange;
1688 Map[24].Key = CTL('Y');
1689 Map[24].Func = yank;
1690 Map[25].Key = CTL('Z');
1691 Map[25].Func = ring_bell;
1692 Map[26].Key = CTL('[');
1693 Map[26].Func = meta;
1694 Map[27].Key = CTL(']');
1695 Map[27].Func = move_to_char;
1696 Map[28].Key = CTL('^');
1697 Map[28].Func = ring_bell;
1698 Map[29].Key = CTL('_');
1699 Map[29].Func = ring_bell;
1700 Map[30].Key = '\0';
1701 Map[30].Func = 0; /* null pointer */
1702 }
1703
1704
1705 /* call this before exit */
1706 void
rl_cleanup()1707 rl_cleanup()
1708 {
1709 int i = 0;
1710
1711 free(Line);
1712 while (i < H.Size) {
1713 free(H.Lines[i]);
1714 i++;
1715 }
1716 if (Yanked)
1717 free(Yanked);
1718 }
1719
1720
1721 #if !defined(_OS2) && !defined(__MSDOS__)
1722 void
rl_set_cntrl_char(int cquit,int cerase,int ceof,int cintr,int ckill)1723 rl_set_cntrl_char(int cquit, int cerase, int ceof, int cintr, int ckill)
1724 {
1725 rl_quit = cquit;
1726 rl_erase = cerase;
1727 rl_eof = ceof;
1728 rl_intr = cintr;
1729 rl_kill = ckill;
1730 }
1731 #endif
1732
1733