Release 950727
[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 STATIC KEYMAP           Map[33];
82 STATIC 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 STATIC void
248 reposition()
249 {
250     int         i;
251     CHAR        *p;
252
253     TTYput('\r');
254     TTYputs((CHAR *)Prompt);
255     for (i = Point, p = Line; --i >= 0; p++)
256         TTYshow(*p);
257 }
258
259 STATIC void
260 left(Change)
261     STATUS      Change;
262 {
263     TTYback();
264     if (Point) {
265         if (ISCTL(Line[Point - 1]))
266             TTYback();
267         else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
268             TTYback();
269             TTYback();
270         }
271     }
272     if (Change == CSmove)
273         Point--;
274 }
275
276 STATIC void
277 right(Change)
278     STATUS      Change;
279 {
280     TTYshow(Line[Point]);
281     if (Change == CSmove)
282         Point++;
283 }
284
285 STATIC STATUS
286 ring_bell()
287 {
288     TTYput('\07');
289     TTYflush();
290     return CSstay;
291 }
292
293 STATIC STATUS
294 do_macro(c)
295     unsigned int        c;
296 {
297     CHAR                name[4];
298
299     name[0] = '_';
300     name[1] = c;
301     name[2] = '_';
302     name[3] = '\0';
303
304     if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
305         Input = NIL;
306         return ring_bell();
307     }
308     return CSstay;
309 }
310
311 STATIC STATUS
312 do_forward(move)
313     STATUS      move;
314 {
315     int         i;
316     CHAR        *p;
317
318     i = 0;
319     do {
320         p = &Line[Point];
321         for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
322             if (move == CSmove)
323                 right(CSstay);
324
325         for (; Point < End && isalnum(*p); Point++, p++)
326             if (move == CSmove)
327                 right(CSstay);
328
329         if (Point == End)
330             break;
331     } while (++i < Repeat);
332
333     return CSstay;
334 }
335
336 STATIC STATUS
337 do_case(type)
338     CASE        type;
339 {
340     int         i;
341     int         end;
342     int         count;
343     CHAR        *p;
344
345     (void)do_forward(CSstay);
346     if (OldPoint != Point) {
347         if ((count = Point - OldPoint) < 0)
348             count = -count;
349         Point = OldPoint;
350         if ((end = Point + count) > End)
351             end = End;
352         for (i = Point, p = &Line[i]; i < end; i++, p++) {
353             if (type == TOupper) {
354                 if (islower(*p))
355                     *p = toupper(*p);
356             }
357             else if (isupper(*p))
358                 *p = tolower(*p);
359             right(CSmove);
360         }
361     }
362     return CSstay;
363 }
364
365 STATIC STATUS
366 case_down_word()
367 {
368     return do_case(TOlower);
369 }
370
371 STATIC STATUS
372 case_up_word()
373 {
374     return do_case(TOupper);
375 }
376
377 STATIC void
378 ceol()
379 {
380     int         extras;
381     int         i;
382     CHAR        *p;
383
384     for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
385         TTYput(' ');
386         if (ISCTL(*p)) {
387             TTYput(' ');
388             extras++;
389         }
390         else if (rl_meta_chars && ISMETA(*p)) {
391             TTYput(' ');
392             TTYput(' ');
393             extras += 2;
394         }
395     }
396
397     for (i += extras; i > Point; i--)
398         TTYback();
399 }
400
401 STATIC void
402 clear_line()
403 {
404     Point = -strlen(Prompt);
405     TTYput('\r');
406     ceol();
407     Point = 0;
408     End = 0;
409     Line[0] = '\0';
410 }
411
412 STATIC STATUS
413 insert_string(p)
414     CHAR        *p;
415 {
416     SIZE_T      len;
417     int         i;
418     CHAR        *new;
419     CHAR        *q;
420
421     len = strlen((char *)p);
422     if (End + len >= Length) {
423         if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
424             return CSstay;
425         if (Length) {
426             COPYFROMTO(new, Line, Length);
427             DISPOSE(Line);
428         }
429         Line = new;
430         Length += len + MEM_INC;
431     }
432
433     for (q = &Line[Point], i = End - Point; --i >= 0; )
434         q[len + i] = q[i];
435     COPYFROMTO(&Line[Point], p, len);
436     End += len;
437     Line[End] = '\0';
438     TTYstring(&Line[Point]);
439     Point += len;
440
441     return Point == End ? CSstay : CSmove;
442 }
443 \f
444
445 STATIC CHAR *
446 next_hist()
447 {
448     return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
449 }
450
451 STATIC CHAR *
452 prev_hist()
453 {
454     return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
455 }
456
457 STATIC STATUS
458 do_insert_hist(p)
459     CHAR        *p;
460 {
461     if (p == NULL)
462         return ring_bell();
463     Point = 0;
464     reposition();
465     ceol();
466     End = 0;
467     return insert_string(p);
468 }
469
470 STATIC STATUS
471 do_hist(move)
472     CHAR        *(*move)();
473 {
474     CHAR        *p;
475     int         i;
476
477     i = 0;
478     do {
479         if ((p = (*move)()) == NULL)
480             return ring_bell();
481     } while (++i < Repeat);
482     return do_insert_hist(p);
483 }
484
485 STATIC STATUS
486 h_next()
487 {
488     return do_hist(next_hist);
489 }
490
491 STATIC STATUS
492 h_prev()
493 {
494     return do_hist(prev_hist);
495 }
496
497 STATIC STATUS
498 h_first()
499 {
500     return do_insert_hist(H.Lines[H.Pos = 0]);
501 }
502
503 STATIC STATUS
504 h_last()
505 {
506     return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
507 }
508
509 /*
510 **  Return zero if pat appears as a substring in text.
511 */
512 STATIC int
513 substrcmp(text, pat, len)
514     char        *text;
515     char        *pat;
516     int         len;
517 {
518     CHAR        c;
519
520     if ((c = *pat) == '\0')
521         return *text == '\0';
522     for ( ; *text; text++)
523         if ((CHAR)*text == c && strncmp(text, pat, len) == 0)
524             return 0;
525     return 1;
526 }
527
528 STATIC CHAR *
529 search_hist(search, move)
530     CHAR        *search;
531     CHAR        *(*move)();
532 {
533     static CHAR *old_search;
534     int         len;
535     int         pos;
536     int         (*match)();
537     char        *pat;
538
539     /* Save or get remembered search pattern. */
540     if (search && *search) {
541         if (old_search)
542             DISPOSE(old_search);
543         old_search = (CHAR *)strdup((char *)search);
544     }
545     else {
546         if (old_search == NULL || *old_search == '\0')
547             return NULL;
548         search = old_search;
549     }
550
551     /* Set up pattern-finder. */
552     if (*search == '^') {
553         match = strncmp;
554         pat = (char *)(search + 1);
555     }
556     else {
557         match = substrcmp;
558         pat = (char *)search;
559     }
560     len = strlen(pat);
561
562     for (pos = H.Pos; (*move)() != NULL; )
563         if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
564             return H.Lines[H.Pos];
565     H.Pos = pos;
566     return NULL;
567 }
568
569 STATIC STATUS
570 h_search()
571 {
572     static int  Searching;
573     CONST char  *old_prompt;
574     CHAR        *(*move)();
575     CHAR        *p;
576
577     if (Searching)
578         return ring_bell();
579     Searching = 1;
580
581     clear_line();
582     old_prompt = Prompt;
583     Prompt = "Search: ";
584     TTYputs((CHAR *)Prompt);
585     move = Repeat == NO_ARG ? prev_hist : next_hist;
586     p = search_hist(editinput(), move);
587     clear_line();
588     Prompt = old_prompt;
589     TTYputs((CHAR *)Prompt);
590
591     Searching = 0;
592     return do_insert_hist(p);
593 }
594
595 STATIC STATUS
596 fd_char()
597 {
598     int         i;
599
600     i = 0;
601     do {
602         if (Point >= End)
603             break;
604         right(CSmove);
605     } while (++i < Repeat);
606     return CSstay;
607 }
608
609 STATIC void
610 save_yank(begin, i)
611     int         begin;
612     int         i;
613 {
614     if (Yanked) {
615         DISPOSE(Yanked);
616         Yanked = NULL;
617     }
618
619     if (i < 1)
620         return;
621
622     if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
623         COPYFROMTO(Yanked, &Line[begin], i);
624         Yanked[i] = '\0';
625     }
626 }
627
628 STATIC STATUS
629 delete_string(count)
630     int         count;
631 {
632     int         i;
633     CHAR        *p;
634
635     if (count <= 0 || End == Point)
636         return ring_bell();
637
638     if (count == 1 && Point == End - 1) {
639         /* Optimize common case of delete at end of line. */
640         End--;
641         p = &Line[Point];
642         i = 1;
643         TTYput(' ');
644         if (ISCTL(*p)) {
645             i = 2;
646             TTYput(' ');
647         }
648         else if (rl_meta_chars && ISMETA(*p)) {
649             i = 3;
650             TTYput(' ');
651             TTYput(' ');
652         }
653         TTYbackn(i);
654         *p = '\0';
655         return CSmove;
656     }
657     if (Point + count > End && (count = End - Point) <= 0)
658         return CSstay;
659
660     if (count > 1)
661         save_yank(Point, count);
662
663     for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
664         p[0] = p[count];
665     ceol();
666     End -= count;
667     TTYstring(&Line[Point]);
668     return CSmove;
669 }
670
671 STATIC STATUS
672 bk_char()
673 {
674     int         i;
675
676     i = 0;
677     do {
678         if (Point == 0)
679             break;
680         left(CSmove);
681     } while (++i < Repeat);
682
683     return CSstay;
684 }
685
686 STATIC STATUS
687 bk_del_char()
688 {
689     int         i;
690
691     i = 0;
692     do {
693         if (Point == 0)
694             break;
695         left(CSmove);
696     } while (++i < Repeat);
697
698     return delete_string(i);
699 }
700
701 STATIC STATUS
702 redisplay()
703 {
704     TTYputs((CHAR *)NEWLINE);
705     TTYputs((CHAR *)Prompt);
706     TTYstring(Line);
707     return CSmove;
708 }
709
710 STATIC STATUS
711 kill_line()
712 {
713     int         i;
714
715     if (Repeat != NO_ARG) {
716         if (Repeat < Point) {
717             i = Point;
718             Point = Repeat;
719             reposition();
720             (void)delete_string(i - Point);
721         }
722         else if (Repeat > Point) {
723             right(CSmove);
724             (void)delete_string(Repeat - Point - 1);
725         }
726         return CSmove;
727     }
728
729     save_yank(Point, End - Point);
730     Line[Point] = '\0';
731     ceol();
732     End = Point;
733     return CSstay;
734 }
735
736 STATIC STATUS
737 insert_char(c)
738     int         c;
739 {
740     STATUS      s;
741     CHAR        buff[2];
742     CHAR        *p;
743     CHAR        *q;
744     int         i;
745
746     if (Repeat == NO_ARG || Repeat < 2) {
747         buff[0] = c;
748         buff[1] = '\0';
749         return insert_string(buff);
750     }
751
752     if ((p = NEW(CHAR, Repeat + 1)) == NULL)
753         return CSstay;
754     for (i = Repeat, q = p; --i >= 0; )
755         *q++ = c;
756     *q = '\0';
757     Repeat = 0;
758     s = insert_string(p);
759     DISPOSE(p);
760     return s;
761 }
762
763 STATIC STATUS
764 meta()
765 {
766     unsigned int        c;
767     KEYMAP              *kp;
768
769     if ((c = TTYget()) == EOF)
770         return CSeof;
771 #if     defined(ANSI_ARROWS)
772     /* Also include VT-100 arrows. */
773     if (c == '[' || c == 'O')
774         switch (c = TTYget()) {
775         default:        return ring_bell();
776         case EOF:       return CSeof;
777         case 'A':       return h_prev();
778         case 'B':       return h_next();
779         case 'C':       return fd_char();
780         case 'D':       return bk_char();
781         }
782 #endif  /* defined(ANSI_ARROWS) */
783
784     if (isdigit(c)) {
785         for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
786             Repeat = Repeat * 10 + c - '0';
787         Pushed = 1;
788         PushBack = c;
789         return CSstay;
790     }
791
792     if (isupper(c))
793         return do_macro(c);
794     for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
795         if (kp->Key == c)
796             return (*kp->Function)();
797
798     return ring_bell();
799 }
800
801 STATIC STATUS
802 emacs(c)
803     unsigned int        c;
804 {
805     STATUS              s;
806     KEYMAP              *kp;
807
808     if (ISMETA(c)) {
809         Pushed = 1;
810         PushBack = UNMETA(c);
811         return meta();
812     }
813     for (kp = Map; kp->Function; kp++)
814         if (kp->Key == c)
815             break;
816     s = kp->Function ? (*kp->Function)() : insert_char((int)c);
817     if (!Pushed)
818         /* No pushback means no repeat count; hacky, but true. */
819         Repeat = NO_ARG;
820     return s;
821 }
822
823 STATIC STATUS
824 TTYspecial(c)
825     unsigned int        c;
826 {
827     if (ISMETA(c))
828         return CSdispatch;
829
830     if (c == rl_erase || c == DEL)
831         return bk_del_char();
832     if (c == rl_kill) {
833         if (Point != 0) {
834             Point = 0;
835             reposition();
836         }
837         Repeat = NO_ARG;
838         return kill_line();
839     }
840     if (c == rl_intr || c == rl_quit) {
841         Point = End = 0;
842         Line[0] = '\0';
843         return redisplay();
844     }
845     if (c == rl_eof && Point == 0 && End == 0)
846         return CSeof;
847
848     return CSdispatch;
849 }
850
851 STATIC CHAR *
852 editinput()
853 {
854     unsigned int        c;
855
856     Repeat = NO_ARG;
857     OldPoint = Point = Mark = End = 0;
858     Line[0] = '\0';
859
860     while ((c = TTYget()) != EOF)
861         switch (TTYspecial(c)) {
862         case CSdone:
863             return Line;
864         case CSeof:
865             return NULL;
866         case CSmove:
867             reposition();
868             break;
869         case CSdispatch:
870             switch (emacs(c)) {
871             case CSdone:
872                 return Line;
873             case CSeof:
874                 return NULL;
875             case CSmove:
876                 reposition();
877                 break;
878             case CSdispatch:
879             case CSstay:
880                 break;
881             }
882             break;
883         case CSstay:
884             break;
885         }
886     return NULL;
887 }
888
889 STATIC void
890 hist_add(p)
891     CHAR        *p;
892 {
893     int         i;
894
895     if ((p = (CHAR *)strdup((char *)p)) == NULL)
896         return;
897     if (H.Size < HIST_SIZE)
898         H.Lines[H.Size++] = p;
899     else {
900         DISPOSE(H.Lines[0]);
901         for (i = 0; i < HIST_SIZE - 1; i++)
902             H.Lines[i] = H.Lines[i + 1];
903         H.Lines[i] = p;
904     }
905     H.Pos = H.Size - 1;
906 }
907
908 /*
909 **  For compatibility with FSF readline.
910 */
911 /* ARGSUSED0 */
912 void
913 rl_reset_terminal(p)
914     char        *p;
915 {
916 }
917
918 void
919 rl_initialize()
920 {
921 }
922
923 char *
924 readline(prompt)
925     CONST char  *prompt;
926 {
927     CHAR        *line;
928
929     if (Line == NULL) {
930         Length = MEM_INC;
931         if ((Line = NEW(CHAR, Length)) == NULL)
932             return NULL;
933     }
934
935     TTYinfo();
936     rl_ttyset(0);
937     hist_add(NIL);
938     ScreenSize = SCREEN_INC;
939     Screen = NEW(char, ScreenSize);
940     Prompt = prompt ? prompt : (char *)NIL;
941     TTYputs((CHAR *)Prompt);
942     if ((line = editinput()) != NULL) {
943         line = (CHAR *)strdup((char *)line);
944         TTYputs((CHAR *)NEWLINE);
945         TTYflush();
946     }
947     rl_ttyset(1);
948     DISPOSE(Screen);
949     DISPOSE(H.Lines[--H.Size]);
950     return (char *)line;
951 }
952
953 void
954 add_history(p)
955     char        *p;
956 {
957     if (p == NULL || *p == '\0')
958         return;
959
960 #if     defined(UNIQUE_HISTORY)
961     if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
962         return;
963 #endif  /* defined(UNIQUE_HISTORY) */
964     hist_add((CHAR *)p);
965 }
966 \f
967
968 STATIC STATUS
969 beg_line()
970 {
971     if (Point) {
972         Point = 0;
973         return CSmove;
974     }
975     return CSstay;
976 }
977
978 STATIC STATUS
979 del_char()
980 {
981     return delete_string(Repeat == NO_ARG ? 1 : Repeat);
982 }
983
984 STATIC STATUS
985 end_line()
986 {
987     if (Point != End) {
988         Point = End;
989         return CSmove;
990     }
991     return CSstay;
992 }
993
994 STATIC STATUS
995 accept_line()
996 {
997     Line[End] = '\0';
998     return CSdone;
999 }
1000
1001 STATIC STATUS
1002 transpose()
1003 {
1004     CHAR        c;
1005
1006     if (Point) {
1007         if (Point == End)
1008             left(CSmove);
1009         c = Line[Point - 1];
1010         left(CSstay);
1011         Line[Point - 1] = Line[Point];
1012         TTYshow(Line[Point - 1]);
1013         Line[Point++] = c;
1014         TTYshow(c);
1015     }
1016     return CSstay;
1017 }
1018
1019 STATIC STATUS
1020 quote()
1021 {
1022     unsigned int        c;
1023
1024     return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
1025 }
1026
1027 STATIC STATUS
1028 wipe()
1029 {
1030     int         i;
1031
1032     if (Mark > End)
1033         return ring_bell();
1034
1035     if (Point > Mark) {
1036         i = Point;
1037         Point = Mark;
1038         Mark = i;
1039         reposition();
1040     }
1041
1042     return delete_string(Mark - Point);
1043 }
1044
1045 STATIC STATUS
1046 mk_set()
1047 {
1048     Mark = Point;
1049     return CSstay;
1050 }
1051
1052 STATIC STATUS
1053 exchange()
1054 {
1055     unsigned int        c;
1056
1057     if ((c = TTYget()) != CTL('X'))
1058         return c == EOF ? CSeof : ring_bell();
1059
1060     if ((c = Mark) <= End) {
1061         Mark = Point;
1062         Point = c;
1063         return CSmove;
1064     }
1065     return CSstay;
1066 }
1067
1068 STATIC STATUS
1069 yank()
1070 {
1071     if (Yanked && *Yanked)
1072         return insert_string(Yanked);
1073     return CSstay;
1074 }
1075
1076 STATIC STATUS
1077 copy_region()
1078 {
1079     if (Mark > End)
1080         return ring_bell();
1081
1082     if (Point > Mark)
1083         save_yank(Mark, Point - Mark);
1084     else
1085         save_yank(Point, Mark - Point);
1086
1087     return CSstay;
1088 }
1089
1090 STATIC STATUS
1091 move_to_char()
1092 {
1093     unsigned int        c;
1094     int                 i;
1095     CHAR                *p;
1096
1097     if ((c = TTYget()) == EOF)
1098         return CSeof;
1099     for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1100         if (*p == c) {
1101             Point = i;
1102             return CSmove;
1103         }
1104     return CSstay;
1105 }
1106
1107 STATIC STATUS
1108 fd_word()
1109 {
1110     return do_forward(CSmove);
1111 }
1112
1113 STATIC STATUS
1114 fd_kill_word()
1115 {
1116     int         i;
1117
1118     (void)do_forward(CSstay);
1119     if (OldPoint != Point) {
1120         i = Point - OldPoint;
1121         Point = OldPoint;
1122         return delete_string(i);
1123     }
1124     return CSstay;
1125 }
1126
1127 STATIC STATUS
1128 bk_word()
1129 {
1130     int         i;
1131     CHAR        *p;
1132
1133     i = 0;
1134     do {
1135         for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1136             left(CSmove);
1137
1138         for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1139             left(CSmove);
1140
1141         if (Point == 0)
1142             break;
1143     } while (++i < Repeat);
1144
1145     return CSstay;
1146 }
1147
1148 STATIC STATUS
1149 bk_kill_word()
1150 {
1151     (void)bk_word();
1152     if (OldPoint != Point)
1153         return delete_string(OldPoint - Point);
1154     return CSstay;
1155 }
1156
1157 STATIC int
1158 argify(line, avp)
1159     CHAR        *line;
1160     CHAR        ***avp;
1161 {
1162     CHAR        *c;
1163     CHAR        **p;
1164     CHAR        **new;
1165     int         ac;
1166     int         i;
1167
1168     i = MEM_INC;
1169     if ((*avp = p = NEW(CHAR*, i))== NULL)
1170          return 0;
1171
1172     for (c = line; isspace(*c); c++)
1173         continue;
1174     if (*c == '\n' || *c == '\0')
1175         return 0;
1176
1177     for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1178         if (isspace(*c)) {
1179             *c++ = '\0';
1180             if (*c && *c != '\n') {
1181                 if (ac + 1 == i) {
1182                     new = NEW(CHAR*, i + MEM_INC);
1183                     if (new == NULL) {
1184                         p[ac] = NULL;
1185                         return ac;
1186                     }
1187                     COPYFROMTO(new, p, i * sizeof (char **));
1188                     i += MEM_INC;
1189                     DISPOSE(p);
1190                     *avp = p = new;
1191                 }
1192                 p[ac++] = c;
1193             }
1194         }
1195         else
1196             c++;
1197     }
1198     *c = '\0';
1199     p[ac] = NULL;
1200     return ac;
1201 }
1202
1203 STATIC STATUS
1204 last_argument()
1205 {
1206     CHAR        **av;
1207     CHAR        *p;
1208     STATUS      s;
1209     int         ac;
1210
1211     if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
1212         return ring_bell();
1213
1214     if ((p = (CHAR *)strdup((char *)p)) == NULL)
1215         return CSstay;
1216     ac = argify(p, &av);
1217
1218     if (Repeat != NO_ARG)
1219         s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1220     else
1221         s = ac ? insert_string(av[ac - 1]) : CSstay;
1222
1223     if (ac)
1224         DISPOSE(av);
1225     DISPOSE(p);
1226     return s;
1227 }
1228
1229 STATIC KEYMAP   Map[33] = {
1230     {   CTL('@'),       ring_bell       },
1231     {   CTL('A'),       beg_line        },
1232     {   CTL('B'),       bk_char         },
1233     {   CTL('D'),       del_char        },
1234     {   CTL('E'),       end_line        },
1235     {   CTL('F'),       fd_char         },
1236     {   CTL('G'),       ring_bell       },
1237     {   CTL('H'),       bk_del_char     },
1238     {   CTL('I'),       ring_bell       },
1239     {   CTL('J'),       accept_line     },
1240     {   CTL('K'),       kill_line       },
1241     {   CTL('L'),       redisplay       },
1242     {   CTL('M'),       accept_line     },
1243     {   CTL('N'),       h_next          },
1244     {   CTL('O'),       ring_bell       },
1245     {   CTL('P'),       h_prev          },
1246     {   CTL('Q'),       ring_bell       },
1247     {   CTL('R'),       h_search        },
1248     {   CTL('S'),       ring_bell       },
1249     {   CTL('T'),       transpose       },
1250     {   CTL('U'),       ring_bell       },
1251     {   CTL('V'),       quote           },
1252     {   CTL('W'),       wipe            },
1253     {   CTL('X'),       exchange        },
1254     {   CTL('Y'),       yank            },
1255     {   CTL('Z'),       ring_bell       },
1256     {   CTL('['),       meta            },
1257     {   CTL(']'),       move_to_char    },
1258     {   CTL('^'),       ring_bell       },
1259     {   CTL('_'),       ring_bell       },
1260     {   0,              NULL            }
1261 };
1262
1263 STATIC KEYMAP   MetaMap[16]= {
1264     {   CTL('H'),       bk_kill_word    },
1265     {   DEL,            bk_kill_word    },
1266     {   ' ',            mk_set  },
1267     {   '.',            last_argument   },
1268     {   '<',            h_first         },
1269     {   '>',            h_last          },
1270     {   '?',            ring_bell       },
1271     {   'b',            bk_word         },
1272     {   'd',            fd_kill_word    },
1273     {   'f',            fd_word         },
1274     {   'l',            case_down_word  },
1275     {   'u',            case_up_word    },
1276     {   'y',            yank            },
1277     {   'w',            copy_region     },
1278     {   0,              NULL            }
1279 };