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