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