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