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