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