Release 950122
[wine] / debugger / readline / editline.c
1 /*  $Revision: 1.4 $
2 **
3 **  Main editing routines for editline library.
4 */
5 #include "editline.h"
6 #include <ctype.h>
7 #include <stdlib.h>
8
9 /*
10 **  Manifest constants.
11 */
12 #define SCREEN_WIDTH    80
13 #define SCREEN_ROWS     24
14 #define NO_ARG          (-1)
15 #define DEL             127
16 #define CTL(x)          ((x) & 0x1F)
17 #define ISCTL(x)        ((x) && (x) < ' ')
18 #define UNCTL(x)        ((x) + 64)
19 #define META(x)         ((x) | 0x80)
20 #define ISMETA(x)       ((x) & 0x80)
21 #define UNMETA(x)       ((x) & 0x7F)
22 #if     !defined(HIST_SIZE)
23 #define HIST_SIZE       20
24 #endif  /* !defined(HIST_SIZE) */
25
26 /*
27 **  Command status codes.
28 */
29 typedef enum _STATUS {
30     CSdone, CSeof, CSmove, CSdispatch, CSstay
31 } STATUS;
32
33 /*
34 **  The type of case-changing to perform.
35 */
36 typedef enum _CASE {
37     TOupper, TOlower
38 } CASE;
39
40 /*
41 **  Key to command mapping.
42 */
43 typedef struct _KEYMAP {
44     CHAR        Key;
45     STATUS      (*Function)();
46 } KEYMAP;
47
48 /*
49 **  Command history structure.
50 */
51 typedef struct _HISTORY {
52     int         Size;
53     int         Pos;
54     CHAR        *Lines[HIST_SIZE];
55 } HISTORY;
56
57 /*
58 **  Globals.
59 */
60 int             rl_eof;
61 int             rl_erase;
62 int             rl_intr;
63 int             rl_kill;
64
65 STATIC CHAR             NIL[] = "";
66 STATIC CONST CHAR       *Input = NIL;
67 STATIC CHAR             *Line;
68 STATIC CONST char       *Prompt;
69 STATIC CHAR             *Yanked;
70 STATIC char             *Screen;
71 STATIC char             NEWLINE[]= CRLF;
72 STATIC HISTORY          H;
73 int             rl_quit;
74 STATIC int              Repeat;
75 STATIC int              End;
76 STATIC int              Mark;
77 STATIC int              OldPoint;
78 STATIC int              Point;
79 STATIC int              PushBack;
80 STATIC int              Pushed;
81 FORWARD KEYMAP          Map[33];
82 FORWARD KEYMAP          MetaMap[16];
83 STATIC SIZE_T           Length;
84 STATIC SIZE_T           ScreenCount;
85 STATIC SIZE_T           ScreenSize;
86 STATIC char             *backspace;
87 STATIC int              TTYwidth;
88 STATIC int              TTYrows;
89
90 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
91 int             rl_meta_chars = 1;
92
93 /*
94 **  Declarations.
95 */
96 STATIC CHAR     *editinput();
97 extern int      read();
98 extern int      write();
99 #if     defined(USE_TERMCAP)
100 extern char     *getenv();
101 extern char     *tgetstr();
102 extern int      tgetent();
103 #endif  /* defined(USE_TERMCAP) */
104 \f
105 /*
106 **  TTY input/output functions.
107 */
108
109 STATIC void
110 TTYflush()
111 {
112     if (ScreenCount) {
113         (void)write(1, Screen, ScreenCount);
114         ScreenCount = 0;
115     }
116 }
117
118 STATIC void
119 TTYput(c)
120     CHAR        c;
121 {
122     Screen[ScreenCount] = c;
123     if (++ScreenCount >= ScreenSize - 1) {
124         ScreenSize += SCREEN_INC;
125         RENEW(Screen, char, ScreenSize);
126     }
127 }
128
129 STATIC void
130 TTYputs(p)
131     CHAR        *p;
132 {
133     while (*p)
134         TTYput(*p++);
135 }
136
137 STATIC void
138 TTYshow(c)
139     CHAR        c;
140 {
141     if (c == DEL) {
142         TTYput('^');
143         TTYput('?');
144     }
145     else if (ISCTL(c)) {
146         TTYput('^');
147         TTYput(UNCTL(c));
148     }
149     else if (rl_meta_chars && ISMETA(c)) {
150         TTYput('M');
151         TTYput('-');
152         TTYput(UNMETA(c));
153     }
154     else
155         TTYput(c);
156 }
157
158 STATIC void
159 TTYstring(p)
160     CHAR        *p;
161 {
162     while (*p)
163         TTYshow(*p++);
164 }
165
166 STATIC unsigned int
167 TTYget()
168 {
169     CHAR        c;
170
171     TTYflush();
172     if (Pushed) {
173         Pushed = 0;
174         return PushBack;
175     }
176     if (*Input)
177         return *Input++;
178     return read(0, &c, (SIZE_T)1) == 1 ? c : EOF;
179 }
180
181 #define TTYback()       (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
182
183 STATIC void
184 TTYbackn(n)
185     int         n;
186 {
187     while (--n >= 0)
188         TTYback();
189 }
190
191 STATIC void
192 TTYinfo()
193 {
194     static int          init;
195 #if     defined(USE_TERMCAP)
196     char                *term;
197     char                buff[2048];
198     char                *bp;
199 #endif  /* defined(USE_TERMCAP) */
200 #if     defined(TIOCGWINSZ)
201     struct winsize      W;
202 #endif  /* defined(TIOCGWINSZ) */
203
204     if (init) {
205 #if     defined(TIOCGWINSZ)
206         /* Perhaps we got resized. */
207         if (ioctl(0, TIOCGWINSZ, &W) >= 0
208          && W.ws_col > 0 && W.ws_row > 0) {
209             TTYwidth = (int)W.ws_col;
210             TTYrows = (int)W.ws_row;
211         }
212 #endif  /* defined(TIOCGWINSZ) */
213         return;
214     }
215     init++;
216
217     TTYwidth = TTYrows = 0;
218 #if     defined(USE_TERMCAP)
219     bp = &buff[0];
220     if ((term = getenv("TERM")) == NULL)
221         term = "dumb";
222     if (tgetent(buff, term) < 0) {
223        TTYwidth = SCREEN_WIDTH;
224        TTYrows = SCREEN_ROWS;
225        return;
226     }
227     backspace = tgetstr("le", &bp);
228     TTYwidth = tgetnum("co");
229     TTYrows = tgetnum("li");
230 #endif  /* defined(USE_TERMCAP) */
231
232 #if     defined(TIOCGWINSZ)
233     if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
234         TTYwidth = (int)W.ws_col;
235         TTYrows = (int)W.ws_row;
236     }
237 #endif  /* defined(TIOCGWINSZ) */
238
239     if (TTYwidth <= 0 || TTYrows <= 0) {
240         TTYwidth = SCREEN_WIDTH;
241         TTYrows = SCREEN_ROWS;
242     }
243 }
244 \f
245
246 /*
247 **  Print an array of words in columns.
248 */
249 STATIC void
250 columns(ac, av)
251     int         ac;
252     CHAR        **av;
253 {
254     CHAR        *p;
255     int         i;
256     int         j;
257     int         k;
258     int         len;
259     int         skip;
260     int         longest;
261     int         cols;
262
263     /* Find longest name, determine column count from that. */
264     for (longest = 0, i = 0; i < ac; i++)
265         if ((j = strlen((char *)av[i])) > longest)
266             longest = j;
267     cols = TTYwidth / (longest + 3);
268
269     TTYputs((CHAR *)NEWLINE);
270     for (skip = ac / cols + 1, i = 0; i < skip; i++) {
271         for (j = i; j < ac; j += skip) {
272             for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++)
273                 TTYput(*p);
274             if (j + skip < ac)
275                 while (++len < longest + 3)
276                     TTYput(' ');
277         }
278         TTYputs((CHAR *)NEWLINE);
279     }
280 }
281
282 STATIC void
283 reposition()
284 {
285     int         i;
286     CHAR        *p;
287
288     TTYput('\r');
289     TTYputs((CHAR *)Prompt);
290     for (i = Point, p = Line; --i >= 0; p++)
291         TTYshow(*p);
292 }
293
294 STATIC void
295 left(Change)
296     STATUS      Change;
297 {
298     TTYback();
299     if (Point) {
300         if (ISCTL(Line[Point - 1]))
301             TTYback();
302         else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
303             TTYback();
304             TTYback();
305         }
306     }
307     if (Change == CSmove)
308         Point--;
309 }
310
311 STATIC void
312 right(Change)
313     STATUS      Change;
314 {
315     TTYshow(Line[Point]);
316     if (Change == CSmove)
317         Point++;
318 }
319
320 STATIC STATUS
321 ring_bell()
322 {
323     TTYput('\07');
324     TTYflush();
325     return CSstay;
326 }
327
328 STATIC STATUS
329 do_macro(c)
330     unsigned int        c;
331 {
332     CHAR                name[4];
333
334     name[0] = '_';
335     name[1] = c;
336     name[2] = '_';
337     name[3] = '\0';
338
339     if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
340         Input = NIL;
341         return ring_bell();
342     }
343     return CSstay;
344 }
345
346 STATIC STATUS
347 do_forward(move)
348     STATUS      move;
349 {
350     int         i;
351     CHAR        *p;
352
353     i = 0;
354     do {
355         p = &Line[Point];
356         for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
357             if (move == CSmove)
358                 right(CSstay);
359
360         for (; Point < End && isalnum(*p); Point++, p++)
361             if (move == CSmove)
362                 right(CSstay);
363
364         if (Point == End)
365             break;
366     } while (++i < Repeat);
367
368     return CSstay;
369 }
370
371 STATIC STATUS
372 do_case(type)
373     CASE        type;
374 {
375     int         i;
376     int         end;
377     int         count;
378     CHAR        *p;
379
380     (void)do_forward(CSstay);
381     if (OldPoint != Point) {
382         if ((count = Point - OldPoint) < 0)
383             count = -count;
384         Point = OldPoint;
385         if ((end = Point + count) > End)
386             end = End;
387         for (i = Point, p = &Line[i]; i < end; i++, p++) {
388             if (type == TOupper) {
389                 if (islower(*p))
390                     *p = toupper(*p);
391             }
392             else if (isupper(*p))
393                 *p = tolower(*p);
394             right(CSmove);
395         }
396     }
397     return CSstay;
398 }
399
400 STATIC STATUS
401 case_down_word()
402 {
403     return do_case(TOlower);
404 }
405
406 STATIC STATUS
407 case_up_word()
408 {
409     return do_case(TOupper);
410 }
411
412 STATIC void
413 ceol()
414 {
415     int         extras;
416     int         i;
417     CHAR        *p;
418
419     for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
420         TTYput(' ');
421         if (ISCTL(*p)) {
422             TTYput(' ');
423             extras++;
424         }
425         else if (rl_meta_chars && ISMETA(*p)) {
426             TTYput(' ');
427             TTYput(' ');
428             extras += 2;
429         }
430     }
431
432     for (i += extras; i > Point; i--)
433         TTYback();
434 }
435
436 STATIC void
437 clear_line()
438 {
439     Point = -strlen(Prompt);
440     TTYput('\r');
441     ceol();
442     Point = 0;
443     End = 0;
444     Line[0] = '\0';
445 }
446
447 STATIC STATUS
448 insert_string(p)
449     CHAR        *p;
450 {
451     SIZE_T      len;
452     int         i;
453     CHAR        *new;
454     CHAR        *q;
455
456     len = strlen((char *)p);
457     if (End + len >= Length) {
458         if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
459             return CSstay;
460         if (Length) {
461             COPYFROMTO(new, Line, Length);
462             DISPOSE(Line);
463         }
464         Line = new;
465         Length += len + MEM_INC;
466     }
467
468     for (q = &Line[Point], i = End - Point; --i >= 0; )
469         q[len + i] = q[i];
470     COPYFROMTO(&Line[Point], p, len);
471     End += len;
472     Line[End] = '\0';
473     TTYstring(&Line[Point]);
474     Point += len;
475
476     return Point == End ? CSstay : CSmove;
477 }
478 \f
479
480 STATIC CHAR *
481 next_hist()
482 {
483     return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
484 }
485
486 STATIC CHAR *
487 prev_hist()
488 {
489     return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
490 }
491
492 STATIC STATUS
493 do_insert_hist(p)
494     CHAR        *p;
495 {
496     if (p == NULL)
497         return ring_bell();
498     Point = 0;
499     reposition();
500     ceol();
501     End = 0;
502     return insert_string(p);
503 }
504
505 STATIC STATUS
506 do_hist(move)
507     CHAR        *(*move)();
508 {
509     CHAR        *p;
510     int         i;
511
512     i = 0;
513     do {
514         if ((p = (*move)()) == NULL)
515             return ring_bell();
516     } while (++i < Repeat);
517     return do_insert_hist(p);
518 }
519
520 STATIC STATUS
521 h_next()
522 {
523     return do_hist(next_hist);
524 }
525
526 STATIC STATUS
527 h_prev()
528 {
529     return do_hist(prev_hist);
530 }
531
532 STATIC STATUS
533 h_first()
534 {
535     return do_insert_hist(H.Lines[H.Pos = 0]);
536 }
537
538 STATIC STATUS
539 h_last()
540 {
541     return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
542 }
543
544 /*
545 **  Return zero if pat appears as a substring in text.
546 */
547 STATIC int
548 substrcmp(text, pat, len)
549     char        *text;
550     char        *pat;
551     int         len;
552 {
553     CHAR        c;
554
555     if ((c = *pat) == '\0')
556         return *text == '\0';
557     for ( ; *text; text++)
558         if ((CHAR)*text == c && strncmp(text, pat, len) == 0)
559             return 0;
560     return 1;
561 }
562
563 STATIC CHAR *
564 search_hist(search, move)
565     CHAR        *search;
566     CHAR        *(*move)();
567 {
568     static CHAR *old_search;
569     int         len;
570     int         pos;
571     int         (*match)();
572     char        *pat;
573
574     /* Save or get remembered search pattern. */
575     if (search && *search) {
576         if (old_search)
577             DISPOSE(old_search);
578         old_search = (CHAR *)strdup((char *)search);
579     }
580     else {
581         if (old_search == NULL || *old_search == '\0')
582             return NULL;
583         search = old_search;
584     }
585
586     /* Set up pattern-finder. */
587     if (*search == '^') {
588         match = strncmp;
589         pat = (char *)(search + 1);
590     }
591     else {
592         match = substrcmp;
593         pat = (char *)search;
594     }
595     len = strlen(pat);
596
597     for (pos = H.Pos; (*move)() != NULL; )
598         if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
599             return H.Lines[H.Pos];
600     H.Pos = pos;
601     return NULL;
602 }
603
604 STATIC STATUS
605 h_search()
606 {
607     static int  Searching;
608     CONST char  *old_prompt;
609     CHAR        *(*move)();
610     CHAR        *p;
611
612     if (Searching)
613         return ring_bell();
614     Searching = 1;
615
616     clear_line();
617     old_prompt = Prompt;
618     Prompt = "Search: ";
619     TTYputs((CHAR *)Prompt);
620     move = Repeat == NO_ARG ? prev_hist : next_hist;
621     p = search_hist(editinput(), move);
622     clear_line();
623     Prompt = old_prompt;
624     TTYputs((CHAR *)Prompt);
625
626     Searching = 0;
627     return do_insert_hist(p);
628 }
629
630 STATIC STATUS
631 fd_char()
632 {
633     int         i;
634
635     i = 0;
636     do {
637         if (Point >= End)
638             break;
639         right(CSmove);
640     } while (++i < Repeat);
641     return CSstay;
642 }
643
644 STATIC void
645 save_yank(begin, i)
646     int         begin;
647     int         i;
648 {
649     if (Yanked) {
650         DISPOSE(Yanked);
651         Yanked = NULL;
652     }
653
654     if (i < 1)
655         return;
656
657     if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
658         COPYFROMTO(Yanked, &Line[begin], i);
659         Yanked[i] = '\0';
660     }
661 }
662
663 STATIC STATUS
664 delete_string(count)
665     int         count;
666 {
667     int         i;
668     CHAR        *p;
669
670     if (count <= 0 || End == Point)
671         return ring_bell();
672
673     if (count == 1 && Point == End - 1) {
674         /* Optimize common case of delete at end of line. */
675         End--;
676         p = &Line[Point];
677         i = 1;
678         TTYput(' ');
679         if (ISCTL(*p)) {
680             i = 2;
681             TTYput(' ');
682         }
683         else if (rl_meta_chars && ISMETA(*p)) {
684             i = 3;
685             TTYput(' ');
686             TTYput(' ');
687         }
688         TTYbackn(i);
689         *p = '\0';
690         return CSmove;
691     }
692     if (Point + count > End && (count = End - Point) <= 0)
693         return CSstay;
694
695     if (count > 1)
696         save_yank(Point, count);
697
698     for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
699         p[0] = p[count];
700     ceol();
701     End -= count;
702     TTYstring(&Line[Point]);
703     return CSmove;
704 }
705
706 STATIC STATUS
707 bk_char()
708 {
709     int         i;
710
711     i = 0;
712     do {
713         if (Point == 0)
714             break;
715         left(CSmove);
716     } while (++i < Repeat);
717
718     return CSstay;
719 }
720
721 STATIC STATUS
722 bk_del_char()
723 {
724     int         i;
725
726     i = 0;
727     do {
728         if (Point == 0)
729             break;
730         left(CSmove);
731     } while (++i < Repeat);
732
733     return delete_string(i);
734 }
735
736 STATIC STATUS
737 redisplay()
738 {
739     TTYputs((CHAR *)NEWLINE);
740     TTYputs((CHAR *)Prompt);
741     TTYstring(Line);
742     return CSmove;
743 }
744
745 STATIC STATUS
746 kill_line()
747 {
748     int         i;
749
750     if (Repeat != NO_ARG) {
751         if (Repeat < Point) {
752             i = Point;
753             Point = Repeat;
754             reposition();
755             (void)delete_string(i - Point);
756         }
757         else if (Repeat > Point) {
758             right(CSmove);
759             (void)delete_string(Repeat - Point - 1);
760         }
761         return CSmove;
762     }
763
764     save_yank(Point, End - Point);
765     Line[Point] = '\0';
766     ceol();
767     End = Point;
768     return CSstay;
769 }
770
771 STATIC STATUS
772 insert_char(c)
773     int         c;
774 {
775     STATUS      s;
776     CHAR        buff[2];
777     CHAR        *p;
778     CHAR        *q;
779     int         i;
780
781     if (Repeat == NO_ARG || Repeat < 2) {
782         buff[0] = c;
783         buff[1] = '\0';
784         return insert_string(buff);
785     }
786
787     if ((p = NEW(CHAR, Repeat + 1)) == NULL)
788         return CSstay;
789     for (i = Repeat, q = p; --i >= 0; )
790         *q++ = c;
791     *q = '\0';
792     Repeat = 0;
793     s = insert_string(p);
794     DISPOSE(p);
795     return s;
796 }
797
798 STATIC STATUS
799 meta()
800 {
801     unsigned int        c;
802     KEYMAP              *kp;
803
804     if ((c = TTYget()) == EOF)
805         return CSeof;
806 #if     defined(ANSI_ARROWS)
807     /* Also include VT-100 arrows. */
808     if (c == '[' || c == 'O')
809         switch (c = TTYget()) {
810         default:        return ring_bell();
811         case EOF:       return CSeof;
812         case 'A':       return h_prev();
813         case 'B':       return h_next();
814         case 'C':       return fd_char();
815         case 'D':       return bk_char();
816         }
817 #endif  /* defined(ANSI_ARROWS) */
818
819     if (isdigit(c)) {
820         for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
821             Repeat = Repeat * 10 + c - '0';
822         Pushed = 1;
823         PushBack = c;
824         return CSstay;
825     }
826
827     if (isupper(c))
828         return do_macro(c);
829     for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
830         if (kp->Key == c)
831             return (*kp->Function)();
832
833     return ring_bell();
834 }
835
836 STATIC STATUS
837 emacs(c)
838     unsigned int        c;
839 {
840     STATUS              s;
841     KEYMAP              *kp;
842
843     if (ISMETA(c)) {
844         Pushed = 1;
845         PushBack = UNMETA(c);
846         return meta();
847     }
848     for (kp = Map; kp->Function; kp++)
849         if (kp->Key == c)
850             break;
851     s = kp->Function ? (*kp->Function)() : insert_char((int)c);
852     if (!Pushed)
853         /* No pushback means no repeat count; hacky, but true. */
854         Repeat = NO_ARG;
855     return s;
856 }
857
858 STATIC STATUS
859 TTYspecial(c)
860     unsigned int        c;
861 {
862     if (ISMETA(c))
863         return CSdispatch;
864
865     if (c == rl_erase || c == DEL)
866         return bk_del_char();
867     if (c == rl_kill) {
868         if (Point != 0) {
869             Point = 0;
870             reposition();
871         }
872         Repeat = NO_ARG;
873         return kill_line();
874     }
875     if (c == rl_intr || c == rl_quit) {
876         Point = End = 0;
877         Line[0] = '\0';
878         return redisplay();
879     }
880     if (c == rl_eof && Point == 0 && End == 0)
881         return CSeof;
882
883     return CSdispatch;
884 }
885
886 STATIC CHAR *
887 editinput()
888 {
889     unsigned int        c;
890
891     Repeat = NO_ARG;
892     OldPoint = Point = Mark = End = 0;
893     Line[0] = '\0';
894
895     while ((c = TTYget()) != EOF)
896         switch (TTYspecial(c)) {
897         case CSdone:
898             return Line;
899         case CSeof:
900             return NULL;
901         case CSmove:
902             reposition();
903             break;
904         case CSdispatch:
905             switch (emacs(c)) {
906             case CSdone:
907                 return Line;
908             case CSeof:
909                 return NULL;
910             case CSmove:
911                 reposition();
912                 break;
913             case CSdispatch:
914             case CSstay:
915                 break;
916             }
917             break;
918         case CSstay:
919             break;
920         }
921     return NULL;
922 }
923
924 STATIC void
925 hist_add(p)
926     CHAR        *p;
927 {
928     int         i;
929
930     if ((p = (CHAR *)strdup((char *)p)) == NULL)
931         return;
932     if (H.Size < HIST_SIZE)
933         H.Lines[H.Size++] = p;
934     else {
935         DISPOSE(H.Lines[0]);
936         for (i = 0; i < HIST_SIZE - 1; i++)
937             H.Lines[i] = H.Lines[i + 1];
938         H.Lines[i] = p;
939     }
940     H.Pos = H.Size - 1;
941 }
942
943 /*
944 **  For compatibility with FSF readline.
945 */
946 /* ARGSUSED0 */
947 void
948 rl_reset_terminal(p)
949     char        *p;
950 {
951 }
952
953 void
954 rl_initialize()
955 {
956 }
957
958 char *
959 readline(prompt)
960     CONST char  *prompt;
961 {
962     CHAR        *line;
963
964     if (Line == NULL) {
965         Length = MEM_INC;
966         if ((Line = NEW(CHAR, Length)) == NULL)
967             return NULL;
968     }
969
970     TTYinfo();
971     rl_ttyset(0);
972     hist_add(NIL);
973     ScreenSize = SCREEN_INC;
974     Screen = NEW(char, ScreenSize);
975     Prompt = prompt ? prompt : (char *)NIL;
976     TTYputs((CHAR *)Prompt);
977     if ((line = editinput()) != NULL) {
978         line = (CHAR *)strdup((char *)line);
979         TTYputs((CHAR *)NEWLINE);
980         TTYflush();
981     }
982     rl_ttyset(1);
983     DISPOSE(Screen);
984     DISPOSE(H.Lines[--H.Size]);
985     return (char *)line;
986 }
987
988 void
989 add_history(p)
990     char        *p;
991 {
992     if (p == NULL || *p == '\0')
993         return;
994
995 #if     defined(UNIQUE_HISTORY)
996     if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
997         return;
998 #endif  /* defined(UNIQUE_HISTORY) */
999     hist_add((CHAR *)p);
1000 }
1001 \f
1002
1003 STATIC STATUS
1004 beg_line()
1005 {
1006     if (Point) {
1007         Point = 0;
1008         return CSmove;
1009     }
1010     return CSstay;
1011 }
1012
1013 STATIC STATUS
1014 del_char()
1015 {
1016     return delete_string(Repeat == NO_ARG ? 1 : Repeat);
1017 }
1018
1019 STATIC STATUS
1020 end_line()
1021 {
1022     if (Point != End) {
1023         Point = End;
1024         return CSmove;
1025     }
1026     return CSstay;
1027 }
1028
1029 /*
1030 **  Move back to the beginning of the current word and return an
1031 **  allocated copy of it.
1032 */
1033 STATIC CHAR *
1034 find_word()
1035 {
1036     static char SEPS[] = "#;&|^$=`'{}()<>\n\t ";
1037     CHAR        *p;
1038     CHAR        *new;
1039     SIZE_T      len;
1040
1041     for (p = &Line[Point]; p > Line && strchr(SEPS, (char)p[-1]) == NULL; p--)
1042         continue;
1043     len = Point - (p - Line) + 1;
1044     if ((new = NEW(CHAR, len)) == NULL)
1045         return NULL;
1046     COPYFROMTO(new, p, len);
1047     new[len - 1] = '\0';
1048     return new;
1049 }
1050
1051 STATIC STATUS
1052 c_complete()
1053 {
1054     CHAR        *p;
1055     CHAR        *word;
1056     int         unique;
1057     STATUS      s;
1058
1059     word = find_word();
1060     p = (CHAR *)rl_complete((char *)word, &unique);
1061     if (word)
1062         DISPOSE(word);
1063     if (p && *p) {
1064         s = insert_string(p);
1065         if (!unique)
1066             (void)ring_bell();
1067         DISPOSE(p);
1068         return s;
1069     }
1070     return ring_bell();
1071 }
1072
1073 STATIC STATUS
1074 c_possible()
1075 {
1076     CHAR        **av;
1077     CHAR        *word;
1078     int         ac;
1079
1080     word = find_word();
1081     ac = rl_list_possib((char *)word, (char ***)&av);
1082     if (word)
1083         DISPOSE(word);
1084     if (ac) {
1085         columns(ac, av);
1086         while (--ac >= 0)
1087             DISPOSE(av[ac]);
1088         DISPOSE(av);
1089         return CSmove;
1090     }
1091     return ring_bell();
1092 }
1093
1094 STATIC STATUS
1095 accept_line()
1096 {
1097     Line[End] = '\0';
1098     return CSdone;
1099 }
1100
1101 STATIC STATUS
1102 transpose()
1103 {
1104     CHAR        c;
1105
1106     if (Point) {
1107         if (Point == End)
1108             left(CSmove);
1109         c = Line[Point - 1];
1110         left(CSstay);
1111         Line[Point - 1] = Line[Point];
1112         TTYshow(Line[Point - 1]);
1113         Line[Point++] = c;
1114         TTYshow(c);
1115     }
1116     return CSstay;
1117 }
1118
1119 STATIC STATUS
1120 quote()
1121 {
1122     unsigned int        c;
1123
1124     return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
1125 }
1126
1127 STATIC STATUS
1128 wipe()
1129 {
1130     int         i;
1131
1132     if (Mark > End)
1133         return ring_bell();
1134
1135     if (Point > Mark) {
1136         i = Point;
1137         Point = Mark;
1138         Mark = i;
1139         reposition();
1140     }
1141
1142     return delete_string(Mark - Point);
1143 }
1144
1145 STATIC STATUS
1146 mk_set()
1147 {
1148     Mark = Point;
1149     return CSstay;
1150 }
1151
1152 STATIC STATUS
1153 exchange()
1154 {
1155     unsigned int        c;
1156
1157     if ((c = TTYget()) != CTL('X'))
1158         return c == EOF ? CSeof : ring_bell();
1159
1160     if ((c = Mark) <= End) {
1161         Mark = Point;
1162         Point = c;
1163         return CSmove;
1164     }
1165     return CSstay;
1166 }
1167
1168 STATIC STATUS
1169 yank()
1170 {
1171     if (Yanked && *Yanked)
1172         return insert_string(Yanked);
1173     return CSstay;
1174 }
1175
1176 STATIC STATUS
1177 copy_region()
1178 {
1179     if (Mark > End)
1180         return ring_bell();
1181
1182     if (Point > Mark)
1183         save_yank(Mark, Point - Mark);
1184     else
1185         save_yank(Point, Mark - Point);
1186
1187     return CSstay;
1188 }
1189
1190 STATIC STATUS
1191 move_to_char()
1192 {
1193     unsigned int        c;
1194     int                 i;
1195     CHAR                *p;
1196
1197     if ((c = TTYget()) == EOF)
1198         return CSeof;
1199     for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1200         if (*p == c) {
1201             Point = i;
1202             return CSmove;
1203         }
1204     return CSstay;
1205 }
1206
1207 STATIC STATUS
1208 fd_word()
1209 {
1210     return do_forward(CSmove);
1211 }
1212
1213 STATIC STATUS
1214 fd_kill_word()
1215 {
1216     int         i;
1217
1218     (void)do_forward(CSstay);
1219     if (OldPoint != Point) {
1220         i = Point - OldPoint;
1221         Point = OldPoint;
1222         return delete_string(i);
1223     }
1224     return CSstay;
1225 }
1226
1227 STATIC STATUS
1228 bk_word()
1229 {
1230     int         i;
1231     CHAR        *p;
1232
1233     i = 0;
1234     do {
1235         for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1236             left(CSmove);
1237
1238         for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1239             left(CSmove);
1240
1241         if (Point == 0)
1242             break;
1243     } while (++i < Repeat);
1244
1245     return CSstay;
1246 }
1247
1248 STATIC STATUS
1249 bk_kill_word()
1250 {
1251     (void)bk_word();
1252     if (OldPoint != Point)
1253         return delete_string(OldPoint - Point);
1254     return CSstay;
1255 }
1256
1257 STATIC int
1258 argify(line, avp)
1259     CHAR        *line;
1260     CHAR        ***avp;
1261 {
1262     CHAR        *c;
1263     CHAR        **p;
1264     CHAR        **new;
1265     int         ac;
1266     int         i;
1267
1268     i = MEM_INC;
1269     if ((*avp = p = NEW(CHAR*, i))== NULL)
1270          return 0;
1271
1272     for (c = line; isspace(*c); c++)
1273         continue;
1274     if (*c == '\n' || *c == '\0')
1275         return 0;
1276
1277     for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1278         if (isspace(*c)) {
1279             *c++ = '\0';
1280             if (*c && *c != '\n') {
1281                 if (ac + 1 == i) {
1282                     new = NEW(CHAR*, i + MEM_INC);
1283                     if (new == NULL) {
1284                         p[ac] = NULL;
1285                         return ac;
1286                     }
1287                     COPYFROMTO(new, p, i * sizeof (char **));
1288                     i += MEM_INC;
1289                     DISPOSE(p);
1290                     *avp = p = new;
1291                 }
1292                 p[ac++] = c;
1293             }
1294         }
1295         else
1296             c++;
1297     }
1298     *c = '\0';
1299     p[ac] = NULL;
1300     return ac;
1301 }
1302
1303 STATIC STATUS
1304 last_argument()
1305 {
1306     CHAR        **av;
1307     CHAR        *p;
1308     STATUS      s;
1309     int         ac;
1310
1311     if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
1312         return ring_bell();
1313
1314     if ((p = (CHAR *)strdup((char *)p)) == NULL)
1315         return CSstay;
1316     ac = argify(p, &av);
1317
1318     if (Repeat != NO_ARG)
1319         s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1320     else
1321         s = ac ? insert_string(av[ac - 1]) : CSstay;
1322
1323     if (ac)
1324         DISPOSE(av);
1325     DISPOSE(p);
1326     return s;
1327 }
1328
1329 STATIC KEYMAP   Map[33] = {
1330     {   CTL('@'),       ring_bell       },
1331     {   CTL('A'),       beg_line        },
1332     {   CTL('B'),       bk_char         },
1333     {   CTL('D'),       del_char        },
1334     {   CTL('E'),       end_line        },
1335     {   CTL('F'),       fd_char         },
1336     {   CTL('G'),       ring_bell       },
1337     {   CTL('H'),       bk_del_char     },
1338     {   CTL('I'),       c_complete      },
1339     {   CTL('J'),       accept_line     },
1340     {   CTL('K'),       kill_line       },
1341     {   CTL('L'),       redisplay       },
1342     {   CTL('M'),       accept_line     },
1343     {   CTL('N'),       h_next          },
1344     {   CTL('O'),       ring_bell       },
1345     {   CTL('P'),       h_prev          },
1346     {   CTL('Q'),       ring_bell       },
1347     {   CTL('R'),       h_search        },
1348     {   CTL('S'),       ring_bell       },
1349     {   CTL('T'),       transpose       },
1350     {   CTL('U'),       ring_bell       },
1351     {   CTL('V'),       quote           },
1352     {   CTL('W'),       wipe            },
1353     {   CTL('X'),       exchange        },
1354     {   CTL('Y'),       yank            },
1355     {   CTL('Z'),       ring_bell       },
1356     {   CTL('['),       meta            },
1357     {   CTL(']'),       move_to_char    },
1358     {   CTL('^'),       ring_bell       },
1359     {   CTL('_'),       ring_bell       },
1360     {   0,              NULL            }
1361 };
1362
1363 STATIC KEYMAP   MetaMap[16]= {
1364     {   CTL('H'),       bk_kill_word    },
1365     {   DEL,            bk_kill_word    },
1366     {   ' ',            mk_set  },
1367     {   '.',            last_argument   },
1368     {   '<',            h_first         },
1369     {   '>',            h_last          },
1370     {   '?',            c_possible      },
1371     {   'b',            bk_word         },
1372     {   'd',            fd_kill_word    },
1373     {   'f',            fd_word         },
1374     {   'l',            case_down_word  },
1375     {   'u',            case_up_word    },
1376     {   'y',            yank            },
1377     {   'w',            copy_region     },
1378     {   0,              NULL            }
1379 };