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