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