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