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