Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / editline / editline.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Simmule Turner and Rich Salz.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * Copyright 1992,1993 Simmule Turner and Rich Salz.  All rights reserved.
42  *
43  * This software is not subject to any license of the American Telephone
44  * and Telegraph Company or of the Regents of the University of California.
45  *
46  * Permission is granted to anyone to use this software for any purpose on
47  * any computer system, and to alter it and redistribute it freely, subject
48  * to the following restrictions:
49  * 1. The authors are not responsible for the consequences of use of this
50  *    software, no matter how awful, even if they arise from flaws in it.
51  * 2. The origin of this software must not be misrepresented, either by
52  *    explicit claim or by omission.  Since few users ever read sources,
53  *    credits must appear in the documentation.
54  * 3. Altered versions must be plainly marked as such, and must not be
55  *    misrepresented as being the original software.  Since few users
56  *    ever read sources, credits must appear in the documentation.
57  * 4. This notice may not be removed or altered.
58  */
59
60
61 /*
62 **  Main editing routines for editline library.
63 */
64 #include "editline.h"
65 #include <signal.h>
66 #include <ctype.h>
67 #include <unistd.h>
68
69 /*
70 **  Manifest constants.
71 */
72 #define SCREEN_WIDTH    80
73 #define SCREEN_ROWS     24
74 #define NO_ARG          (-1)
75 #define DEL             127
76 #define CTL(x)          ((x) & 0x1F)
77 #define ISCTL(x)        ((x) && (x) < ' ')
78 #define UNCTL(x)        ((x) + 64)
79 #define META(x)         ((x) | 0x80)
80 #define ISMETA(x)       ((x) & 0x80)
81 #define UNMETA(x)       ((x) & 0x7F)
82 #if     !defined(HIST_SIZE)
83 #define HIST_SIZE       20
84 #endif  /* !defined(HIST_SIZE) */
85
86 /*
87 **  Command status codes.
88 */
89 typedef enum _STATUS {
90     CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal
91 } STATUS;
92
93 /*
94 **  The type of case-changing to perform.
95 */
96 typedef enum _CASE {
97     TOupper, TOlower
98 } CASE;
99
100 /*
101 **  Key to command mapping.
102 */
103 typedef struct _KEYMAP {
104     CHAR        Key;
105     STATUS      (*Function)();
106 } KEYMAP;
107
108 /*
109 **  Command history structure.
110 */
111 typedef struct _HISTORY {
112     int         Size;
113     int         Pos;
114     CHAR        *Lines[HIST_SIZE];
115 } HISTORY;
116
117 /*
118 **  Globals.
119 */
120 unsigned        rl_eof;
121 unsigned        rl_erase;
122 unsigned        rl_intr;
123 unsigned        rl_kill;
124 unsigned        rl_quit;
125
126 STATIC CHAR             NIL[] = "";
127 STATIC CONST CHAR       *Input = NIL;
128 STATIC CHAR             *Line;
129 STATIC CONST char       *Prompt;
130 STATIC CHAR             *Yanked;
131 STATIC char             *Screen;
132 STATIC char             NEWLINE[]= CRLF;
133 STATIC HISTORY          H;
134 STATIC int              Repeat;
135 STATIC int              End;
136 STATIC int              Mark;
137 STATIC int              OldPoint;
138 STATIC int              Point;
139 STATIC int              PushBack;
140 STATIC int              Pushed;
141 STATIC int              Signal;
142 FORWARD KEYMAP          Map[32];
143 FORWARD KEYMAP          MetaMap[16];
144 STATIC SIZE_T           Length;
145 STATIC SIZE_T           ScreenCount;
146 STATIC SIZE_T           ScreenSize;
147 STATIC char             *backspace;
148 STATIC int              TTYwidth;
149 STATIC int              TTYrows;
150
151 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
152 int             rl_meta_chars = 0;
153
154 /*
155 **  Declarations.
156 */
157 STATIC CHAR     *editinput();
158 #if     defined(USE_TERMCAP)
159 #include <stdlib.h>
160 #include <curses.h>
161 #include <term.h>
162 #endif  /* defined(USE_TERMCAP) */
163
164 /*
165 **  TTY input/output functions.
166 */
167
168 STATIC void
169 TTYflush()
170 {
171     if (ScreenCount) {
172         /* Dummy assignment avoids GCC warning on
173          * "attribute warn_unused_result" */
174         ssize_t dummy = write(1, Screen, ScreenCount);
175         (void)dummy;
176         ScreenCount = 0;
177     }
178 }
179
180 STATIC void
181 TTYput(c)
182     CHAR        c;
183 {
184     Screen[ScreenCount] = c;
185     if (++ScreenCount >= ScreenSize - 1) {
186         ScreenSize += SCREEN_INC;
187         RENEW(Screen, char, ScreenSize);
188     }
189 }
190
191 STATIC void
192 TTYputs(p)
193     CHAR        *p;
194 {
195     while (*p)
196         TTYput(*p++);
197 }
198
199 STATIC void
200 TTYshow(c)
201     CHAR        c;
202 {
203     if (c == DEL) {
204         TTYput('^');
205         TTYput('?');
206     }
207     else if (ISCTL(c)) {
208         TTYput('^');
209         TTYput(UNCTL(c));
210     }
211     else if (rl_meta_chars && ISMETA(c)) {
212         TTYput('M');
213         TTYput('-');
214         TTYput(UNMETA(c));
215     }
216     else
217         TTYput(c);
218 }
219
220 STATIC void
221 TTYstring(p)
222     CHAR        *p;
223 {
224     while (*p)
225         TTYshow(*p++);
226 }
227
228 STATIC unsigned int
229 TTYget()
230 {
231     CHAR        c;
232
233     TTYflush();
234     if (Pushed) {
235         Pushed = 0;
236         return PushBack;
237     }
238     if (*Input)
239         return *Input++;
240     return read(0, &c, (SIZE_T)1) == 1 ? c : EOF;
241 }
242
243 #define TTYback()       (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
244
245 STATIC void
246 TTYbackn(n)
247     int         n;
248 {
249     while (--n >= 0)
250         TTYback();
251 }
252
253 STATIC void
254 TTYinfo()
255 {
256     static int          init;
257 #if     defined(USE_TERMCAP)
258     char                *term;
259     char                buff[2048];
260     char                *bp, *p;
261 #endif  /* defined(USE_TERMCAP) */
262 #if     defined(TIOCGWINSZ)
263     struct winsize      W;
264 #endif  /* defined(TIOCGWINSZ) */
265
266     if (init) {
267 #if     defined(TIOCGWINSZ)
268         /* Perhaps we got resized. */
269         if (ioctl(0, TIOCGWINSZ, &W) >= 0
270          && W.ws_col > 0 && W.ws_row > 0) {
271             TTYwidth = (int)W.ws_col;
272             TTYrows = (int)W.ws_row;
273         }
274 #endif  /* defined(TIOCGWINSZ) */
275         return;
276     }
277     init++;
278
279     TTYwidth = TTYrows = 0;
280 #if     defined(USE_TERMCAP)
281     bp = &buff[0];
282     if ((term = getenv("TERM")) == NULL)
283         term = "dumb";
284     if (tgetent(buff, term) < 0) {
285        TTYwidth = SCREEN_WIDTH;
286        TTYrows = SCREEN_ROWS;
287        return;
288     }
289     p = tgetstr("le", &bp);
290     backspace = p ? strdup(p) : NULL;
291     TTYwidth = tgetnum("co");
292     TTYrows = tgetnum("li");
293 #endif  /* defined(USE_TERMCAP) */
294
295 #if     defined(TIOCGWINSZ)
296     if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
297         TTYwidth = (int)W.ws_col;
298         TTYrows = (int)W.ws_row;
299     }
300 #endif  /* defined(TIOCGWINSZ) */
301
302     if (TTYwidth <= 0 || TTYrows <= 0) {
303         TTYwidth = SCREEN_WIDTH;
304         TTYrows = SCREEN_ROWS;
305     }
306 }
307
308
309 STATIC void
310 reposition()
311 {
312     int         i;
313     CHAR        *p;
314
315     TTYput('\r');
316     TTYputs((CONST CHAR *)Prompt);
317     for (i = Point, p = Line; --i >= 0; p++)
318         TTYshow(*p);
319 }
320
321 STATIC void
322 left(Change)
323     STATUS      Change;
324 {
325     TTYback();
326     if (Point) {
327         if (ISCTL(Line[Point - 1]))
328             TTYback();
329         else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
330             TTYback();
331             TTYback();
332         }
333     }
334     if (Change == CSmove)
335         Point--;
336 }
337
338 STATIC void
339 right(Change)
340     STATUS      Change;
341 {
342     TTYshow(Line[Point]);
343     if (Change == CSmove)
344         Point++;
345 }
346
347 STATIC STATUS
348 ring_bell()
349 {
350     TTYput('\07');
351     TTYflush();
352     return CSstay;
353 }
354
355 STATIC STATUS
356 do_macro(c)
357     unsigned int        c;
358 {
359     CHAR                name[4];
360
361     name[0] = '_';
362     name[1] = c;
363     name[2] = '_';
364     name[3] = '\0';
365
366     if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
367         Input = NIL;
368         return ring_bell();
369     }
370     return CSstay;
371 }
372
373 STATIC STATUS
374 do_forward(move)
375     STATUS      move;
376 {
377     int         i;
378     CHAR        *p;
379
380     i = 0;
381     do {
382         p = &Line[Point];
383         for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
384             if (move == CSmove)
385                 right(CSstay);
386
387         for (; Point < End && isalnum(*p); Point++, p++)
388             if (move == CSmove)
389                 right(CSstay);
390
391         if (Point == End)
392             break;
393     } while (++i < Repeat);
394
395     return CSstay;
396 }
397
398 STATIC STATUS
399 do_case(type)
400     CASE        type;
401 {
402     int         i;
403     int         end;
404     int         count;
405     CHAR        *p;
406
407     (void)do_forward(CSstay);
408     if (OldPoint != Point) {
409         if ((count = Point - OldPoint) < 0)
410             count = -count;
411         Point = OldPoint;
412         if ((end = Point + count) > End)
413             end = End;
414         for (i = Point, p = &Line[i]; i < end; i++, p++) {
415             if (type == TOupper) {
416                 if (islower(*p))
417                     *p = toupper(*p);
418             }
419             else if (isupper(*p))
420                 *p = tolower(*p);
421             right(CSmove);
422         }
423     }
424     return CSstay;
425 }
426
427 STATIC STATUS
428 case_down_word()
429 {
430     return do_case(TOlower);
431 }
432
433 STATIC STATUS
434 case_up_word()
435 {
436     return do_case(TOupper);
437 }
438
439 STATIC void
440 ceol()
441 {
442     int         extras;
443     int         i;
444     CHAR        *p;
445
446     for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
447         TTYput(' ');
448         if (ISCTL(*p)) {
449             TTYput(' ');
450             extras++;
451         }
452         else if (rl_meta_chars && ISMETA(*p)) {
453             TTYput(' ');
454             TTYput(' ');
455             extras += 2;
456         }
457     }
458
459     for (i += extras; i > Point; i--)
460         TTYback();
461 }
462
463 STATIC void
464 clear_line()
465 {
466     Point = -strlen(Prompt);
467     TTYput('\r');
468     ceol();
469     Point = 0;
470     End = 0;
471     Line[0] = '\0';
472 }
473
474 STATIC STATUS
475 insert_string(p)
476     CHAR        *p;
477 {
478     SIZE_T      len;
479     int         i;
480     CHAR        *new;
481     CHAR        *q;
482
483     len = strlen((char *)p);
484     if (End + len >= Length) {
485         if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
486             return CSstay;
487         if (Length) {
488             COPYFROMTO(new, Line, Length);
489             DISPOSE(Line);
490         }
491         Line = new;
492         Length += len + MEM_INC;
493     }
494
495     for (q = &Line[Point], i = End - Point; --i >= 0; )
496         q[len + i] = q[i];
497     COPYFROMTO(&Line[Point], p, len);
498     End += len;
499     Line[End] = '\0';
500     TTYstring(&Line[Point]);
501     Point += len;
502
503     return Point == End ? CSstay : CSmove;
504 }
505
506 STATIC STATUS
507 redisplay()
508 {
509     TTYputs((CONST CHAR *)NEWLINE);
510     TTYputs((CONST CHAR *)Prompt);
511     TTYstring(Line);
512     return CSmove;
513 }
514
515 STATIC STATUS
516 toggle_meta_mode()
517 {
518     rl_meta_chars = ! rl_meta_chars;
519     return redisplay();
520 }
521
522
523 STATIC CHAR *
524 next_hist()
525 {
526     return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
527 }
528
529 STATIC CHAR *
530 prev_hist()
531 {
532     return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
533 }
534
535 STATIC STATUS
536 do_insert_hist(p)
537     CHAR        *p;
538 {
539     if (p == NULL)
540         return ring_bell();
541     Point = 0;
542     reposition();
543     ceol();
544     End = 0;
545     return insert_string(p);
546 }
547
548 STATIC STATUS
549 do_hist(move)
550     CHAR        *(*move)();
551 {
552     CHAR        *p;
553     int         i;
554
555     i = 0;
556     do {
557         if ((p = (*move)()) == NULL)
558             return ring_bell();
559     } while (++i < Repeat);
560     return do_insert_hist(p);
561 }
562
563 STATIC STATUS
564 h_next()
565 {
566     return do_hist(next_hist);
567 }
568
569 STATIC STATUS
570 h_prev()
571 {
572     return do_hist(prev_hist);
573 }
574
575 STATIC STATUS
576 h_first()
577 {
578     return do_insert_hist(H.Lines[H.Pos = 0]);
579 }
580
581 STATIC STATUS
582 h_last()
583 {
584     return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
585 }
586
587 /*
588 **  Return zero if pat appears as a substring in text.
589 */
590 STATIC int
591 substrcmp(text, pat, len)
592     char        *text;
593     char        *pat;
594     int         len;
595 {
596     char        c;
597
598     if ((c = *pat) == '\0')
599         return *text == '\0';
600     for ( ; *text; text++)
601         if (*text == c && strncmp(text, pat, len) == 0)
602             return 0;
603     return 1;
604 }
605
606 STATIC CHAR *
607 search_hist(search, move)
608     CHAR        *search;
609     CHAR        *(*move)();
610 {
611     static CHAR *old_search;
612     int         len;
613     int         pos;
614     int         (*match)();
615     char        *pat;
616
617     /* Save or get remembered search pattern. */
618     if (search && *search) {
619         if (old_search)
620             DISPOSE(old_search);
621         old_search = (CHAR *)strdup((char *)search);
622     }
623     else {
624         if (old_search == NULL || *old_search == '\0')
625             return NULL;
626         search = old_search;
627     }
628
629     /* Set up pattern-finder. */
630     if (*search == '^') {
631         match = strncmp;
632         pat = (char *)(search + 1);
633     }
634     else {
635         match = substrcmp;
636         pat = (char *)search;
637     }
638     len = strlen(pat);
639
640     for (pos = H.Pos; (*move)() != NULL; )
641         if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
642             return H.Lines[H.Pos];
643     H.Pos = pos;
644     return NULL;
645 }
646
647 STATIC STATUS
648 h_search()
649 {
650     static int  Searching;
651     CONST char  *old_prompt;
652     CHAR        *(*move)();
653     CHAR        *p;
654
655     if (Searching)
656         return ring_bell();
657     Searching = 1;
658
659     clear_line();
660     old_prompt = Prompt;
661     Prompt = "Search: ";
662     TTYputs((CONST CHAR *)Prompt);
663     move = Repeat == NO_ARG ? prev_hist : next_hist;
664     p = editinput();
665     Prompt = old_prompt;
666     Searching = 0;
667     TTYputs((CONST CHAR *)Prompt);
668     if (p == NULL && Signal > 0) {
669         Signal = 0;
670         clear_line();
671         return redisplay();
672     }
673     p = search_hist(p, move);
674     clear_line();
675     if (p == NULL) {
676         (void)ring_bell();
677         return redisplay();
678     }
679     return do_insert_hist(p);
680 }
681
682 STATIC STATUS
683 fd_char()
684 {
685     int         i;
686
687     i = 0;
688     do {
689         if (Point >= End)
690             break;
691         right(CSmove);
692     } while (++i < Repeat);
693     return CSstay;
694 }
695
696 STATIC void
697 save_yank(begin, i)
698     int         begin;
699     int         i;
700 {
701     if (Yanked) {
702         DISPOSE(Yanked);
703         Yanked = NULL;
704     }
705
706     if (i < 1)
707         return;
708
709     if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
710         COPYFROMTO(Yanked, &Line[begin], i);
711         Yanked[i] = '\0';
712     }
713 }
714
715 STATIC STATUS
716 delete_string(count)
717     int         count;
718 {
719     int         i;
720     CHAR        *p;
721
722     if (count <= 0 || End == Point)
723         return ring_bell();
724
725     if (count == 1 && Point == End - 1) {
726         /* Optimize common case of delete at end of line. */
727         End--;
728         p = &Line[Point];
729         i = 1;
730         TTYput(' ');
731         if (ISCTL(*p)) {
732             i = 2;
733             TTYput(' ');
734         }
735         else if (rl_meta_chars && ISMETA(*p)) {
736             i = 3;
737             TTYput(' ');
738             TTYput(' ');
739         }
740         TTYbackn(i);
741         *p = '\0';
742         return CSmove;
743     }
744     if (Point + count > End && (count = End - Point) <= 0)
745         return CSstay;
746
747     if (count > 1)
748         save_yank(Point, count);
749
750     for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
751         p[0] = p[count];
752     ceol();
753     End -= count;
754     TTYstring(&Line[Point]);
755     return CSmove;
756 }
757
758 STATIC STATUS
759 bk_char()
760 {
761     int         i;
762
763     i = 0;
764     do {
765         if (Point == 0)
766             break;
767         left(CSmove);
768     } while (++i < Repeat);
769
770     return CSstay;
771 }
772
773 STATIC STATUS
774 bk_del_char()
775 {
776     int         i;
777
778     i = 0;
779     do {
780         if (Point == 0)
781             break;
782         left(CSmove);
783     } while (++i < Repeat);
784
785     return delete_string(i);
786 }
787
788 STATIC STATUS
789 kill_line()
790 {
791     int         i;
792
793     if (Repeat != NO_ARG) {
794         if (Repeat < Point) {
795             i = Point;
796             Point = Repeat;
797             reposition();
798             (void)delete_string(i - Point);
799         }
800         else if (Repeat > Point) {
801             right(CSmove);
802             (void)delete_string(Repeat - Point - 1);
803         }
804         return CSmove;
805     }
806
807     save_yank(Point, End - Point);
808     Line[Point] = '\0';
809     ceol();
810     End = Point;
811     return CSstay;
812 }
813
814 STATIC STATUS
815 insert_char(c)
816     int         c;
817 {
818     STATUS      s;
819     CHAR        buff[2];
820     CHAR        *p;
821     CHAR        *q;
822     int         i;
823
824     if (Repeat == NO_ARG || Repeat < 2) {
825         buff[0] = c;
826         buff[1] = '\0';
827         return insert_string(buff);
828     }
829
830     if ((p = NEW(CHAR, Repeat + 1)) == NULL)
831         return CSstay;
832     for (i = Repeat, q = p; --i >= 0; )
833         *q++ = c;
834     *q = '\0';
835     Repeat = 0;
836     s = insert_string(p);
837     DISPOSE(p);
838     return s;
839 }
840
841 STATIC STATUS
842 meta()
843 {
844     unsigned int        c;
845     KEYMAP              *kp;
846
847     if ((int)(c = TTYget()) == EOF)
848         return CSeof;
849 #if     defined(ANSI_ARROWS)
850     /* Also include VT-100 arrows. */
851     if (c == '[' || c == 'O')
852         switch (c = TTYget()) {
853         default:        return ring_bell();
854         case EOF:       return CSeof;
855         case 'A':       return h_prev();
856         case 'B':       return h_next();
857         case 'C':       return fd_char();
858         case 'D':       return bk_char();
859         }
860 #endif  /* defined(ANSI_ARROWS) */
861
862     if (isdigit(c)) {
863         for (Repeat = c - '0'; (int)(c = TTYget()) != EOF && isdigit(c); )
864             Repeat = Repeat * 10 + c - '0';
865         Pushed = 1;
866         PushBack = c;
867         return CSstay;
868     }
869
870     if (isupper(c))
871         return do_macro(c);
872     for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
873         if (kp->Key == c)
874             return (*kp->Function)();
875
876     return ring_bell();
877 }
878
879 STATIC STATUS
880 emacs(c)
881     unsigned int        c;
882 {
883     STATUS              s;
884     KEYMAP              *kp;
885
886     if (rl_meta_chars && ISMETA(c)) {
887         Pushed = 1;
888         PushBack = UNMETA(c);
889         return meta();
890     }
891     for (kp = Map; kp->Function; kp++)
892         if (kp->Key == c)
893             break;
894     s = kp->Function ? (*kp->Function)() : insert_char((int)c);
895     if (!Pushed)
896         /* No pushback means no repeat count; hacky, but true. */
897         Repeat = NO_ARG;
898     return s;
899 }
900
901 STATIC STATUS
902 TTYspecial(c)
903     unsigned int        c;
904 {
905     if (ISMETA(c))
906         return CSdispatch;
907
908     if (c == rl_erase || (int)c == DEL)
909         return bk_del_char();
910     if (c == rl_kill) {
911         if (Point != 0) {
912             Point = 0;
913             reposition();
914         }
915         Repeat = NO_ARG;
916         return kill_line();
917     }
918     if (c == rl_eof && Point == 0 && End == 0)
919         return CSeof;
920     if (c == rl_intr) {
921         Signal = SIGINT;
922         return CSsignal;
923     }
924     if (c == rl_quit) {
925         Signal = SIGQUIT;
926         return CSeof;
927     }
928
929     return CSdispatch;
930 }
931
932 STATIC CHAR *
933 editinput()
934 {
935     unsigned int        c;
936
937     Repeat = NO_ARG;
938     OldPoint = Point = Mark = End = 0;
939     Line[0] = '\0';
940
941     Signal = -1;
942     while ((int)(c = TTYget()) != EOF)
943         switch (TTYspecial(c)) {
944         case CSdone:
945             return Line;
946         case CSeof:
947             return NULL;
948         case CSsignal:
949             return (CHAR *)"";
950         case CSmove:
951             reposition();
952             break;
953         case CSdispatch:
954             switch (emacs(c)) {
955             case CSdone:
956                 return Line;
957             case CSeof:
958                 return NULL;
959             case CSsignal:
960                 return (CHAR *)"";
961             case CSmove:
962                 reposition();
963                 break;
964             case CSdispatch:
965             case CSstay:
966                 break;
967             }
968             break;
969         case CSstay:
970             break;
971         }
972     if (strlen((char *)Line))
973         return Line;
974     free(Line);
975     return NULL;
976 }
977
978 STATIC void
979 hist_add(p)
980     CHAR        *p;
981 {
982     int         i;
983
984     if ((p = (CHAR *)strdup((char *)p)) == NULL)
985         return;
986     if (H.Size < HIST_SIZE)
987         H.Lines[H.Size++] = p;
988     else {
989         DISPOSE(H.Lines[0]);
990         for (i = 0; i < HIST_SIZE - 1; i++)
991             H.Lines[i] = H.Lines[i + 1];
992         H.Lines[i] = p;
993     }
994     H.Pos = H.Size - 1;
995 }
996
997 /*
998 **  For compatibility with FSF readline.
999 */
1000 /* ARGSUSED0 */
1001 void
1002 rl_reset_terminal(p)
1003     char        *p;
1004 {
1005 }
1006
1007 void
1008 rl_initialize()
1009 {
1010 }
1011
1012 char *
1013 readline(prompt)
1014     CONST char  *prompt;
1015 {
1016     CHAR        *line;
1017     int         s;
1018
1019     if (Line == NULL) {
1020         Length = MEM_INC;
1021         if ((Line = NEW(CHAR, Length)) == NULL)
1022             return NULL;
1023     }
1024
1025     TTYinfo();
1026     rl_ttyset(0);
1027     hist_add(NIL);
1028     ScreenSize = SCREEN_INC;
1029     Screen = NEW(char, ScreenSize);
1030     Prompt = prompt ? prompt : (char *)NIL;
1031     TTYputs((CONST CHAR *)Prompt);
1032     if ((line = editinput()) != NULL) {
1033         line = (CHAR *)strdup((char *)line);
1034         TTYputs((CHAR *)NEWLINE);
1035         TTYflush();
1036     }
1037     rl_ttyset(1);
1038     DISPOSE(Screen);
1039     DISPOSE(H.Lines[--H.Size]);
1040     if (Signal > 0) {
1041         s = Signal;
1042         Signal = 0;
1043         (void)kill(getpid(), s);
1044     }
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.Size && strcmp(p, (char *)H.Lines[H.Size - 1]) == 0)
1057         return;
1058 #endif  /* defined(UNIQUE_HISTORY) */
1059     hist_add((CHAR *)p);
1060 }
1061
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 (int)(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 (int)c == EOF ? CSeof : ring_bell();
1154
1155     if ((int)(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 ((int)(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[32] = {
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('J'),       accept_line     },
1334     {   CTL('K'),       kill_line       },
1335     {   CTL('L'),       redisplay       },
1336     {   CTL('M'),       accept_line     },
1337     {   CTL('N'),       h_next          },
1338     {   CTL('O'),       ring_bell       },
1339     {   CTL('P'),       h_prev          },
1340     {   CTL('Q'),       ring_bell       },
1341     {   CTL('R'),       h_search        },
1342     {   CTL('S'),       ring_bell       },
1343     {   CTL('T'),       transpose       },
1344     {   CTL('U'),       ring_bell       },
1345     {   CTL('V'),       quote           },
1346     {   CTL('W'),       wipe            },
1347     {   CTL('X'),       exchange        },
1348     {   CTL('Y'),       yank            },
1349     {   CTL('Z'),       ring_bell       },
1350     {   CTL('['),       meta            },
1351     {   CTL(']'),       move_to_char    },
1352     {   CTL('^'),       ring_bell       },
1353     {   CTL('_'),       ring_bell       },
1354     {   0,              NULL            }
1355 };
1356
1357 STATIC KEYMAP   MetaMap[16]= {
1358     {   CTL('H'),       bk_kill_word    },
1359     {   DEL,            bk_kill_word    },
1360     {   ' ',            mk_set          },
1361     {   '.',            last_argument   },
1362     {   '<',            h_first         },
1363     {   '>',            h_last          },
1364     {   'b',            bk_word         },
1365     {   'd',            fd_kill_word    },
1366     {   'f',            fd_word         },
1367     {   'l',            case_down_word  },
1368     {   'm',            toggle_meta_mode },
1369     {   'u',            case_up_word    },
1370     {   'y',            yank            },
1371     {   'w',            copy_region     },
1372     {   0,              NULL            }
1373 };
1374