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 0 : TTYflush()
170 : {
171 0 : if (ScreenCount) {
172 : /* Dummy assignment avoids GCC warning on
173 : * "attribute warn_unused_result" */
174 0 : ssize_t dummy = write(1, Screen, ScreenCount);
175 : (void)dummy;
176 0 : ScreenCount = 0;
177 : }
178 0 : }
179 :
180 : STATIC void
181 0 : TTYput(c)
182 : CHAR c;
183 : {
184 0 : Screen[ScreenCount] = c;
185 0 : if (++ScreenCount >= ScreenSize - 1) {
186 0 : ScreenSize += SCREEN_INC;
187 0 : RENEW(Screen, char, ScreenSize);
188 : }
189 0 : }
190 :
191 : STATIC void
192 0 : TTYputs(p)
193 : CONST CHAR *p;
194 : {
195 0 : while (*p)
196 0 : TTYput(*p++);
197 0 : }
198 :
199 : STATIC void
200 0 : TTYshow(c)
201 : CHAR c;
202 : {
203 0 : if (c == DEL) {
204 0 : TTYput('^');
205 0 : TTYput('?');
206 : }
207 0 : else if (ISCTL(c)) {
208 0 : TTYput('^');
209 0 : TTYput(UNCTL(c));
210 : }
211 0 : else if (rl_meta_chars && ISMETA(c)) {
212 0 : TTYput('M');
213 0 : TTYput('-');
214 0 : TTYput(UNMETA(c));
215 : }
216 : else
217 0 : TTYput(c);
218 0 : }
219 :
220 : STATIC void
221 0 : TTYstring(p)
222 : CHAR *p;
223 : {
224 0 : while (*p)
225 0 : TTYshow(*p++);
226 0 : }
227 :
228 : STATIC unsigned int
229 0 : TTYget()
230 : {
231 : CHAR c;
232 :
233 0 : TTYflush();
234 0 : if (Pushed) {
235 0 : Pushed = 0;
236 0 : return PushBack;
237 : }
238 0 : if (*Input)
239 0 : return *Input++;
240 0 : 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 0 : TTYbackn(n)
247 : int n;
248 : {
249 0 : while (--n >= 0)
250 0 : TTYback();
251 0 : }
252 :
253 : STATIC void
254 0 : 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 0 : 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 0 : return;
276 : }
277 0 : init++;
278 :
279 0 : 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 0 : if (TTYwidth <= 0 || TTYrows <= 0) {
303 0 : TTYwidth = SCREEN_WIDTH;
304 0 : TTYrows = SCREEN_ROWS;
305 : }
306 : }
307 :
308 :
309 : STATIC void
310 0 : reposition()
311 : {
312 : int i;
313 : CHAR *p;
314 :
315 0 : TTYput('\r');
316 0 : TTYputs((CONST CHAR *)Prompt);
317 0 : for (i = Point, p = Line; --i >= 0; p++)
318 0 : TTYshow(*p);
319 0 : }
320 :
321 : STATIC void
322 0 : left(Change)
323 : STATUS Change;
324 : {
325 0 : TTYback();
326 0 : if (Point) {
327 0 : if (ISCTL(Line[Point - 1]))
328 0 : TTYback();
329 0 : else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
330 0 : TTYback();
331 0 : TTYback();
332 : }
333 : }
334 0 : if (Change == CSmove)
335 0 : Point--;
336 0 : }
337 :
338 : STATIC void
339 0 : right(Change)
340 : STATUS Change;
341 : {
342 0 : TTYshow(Line[Point]);
343 0 : if (Change == CSmove)
344 0 : Point++;
345 0 : }
346 :
347 : STATIC STATUS
348 0 : ring_bell()
349 : {
350 0 : TTYput('\07');
351 0 : TTYflush();
352 0 : return CSstay;
353 : }
354 :
355 : STATIC STATUS
356 0 : do_macro(c)
357 : unsigned int c;
358 : {
359 : CHAR name[4];
360 :
361 0 : name[0] = '_';
362 0 : name[1] = c;
363 0 : name[2] = '_';
364 0 : name[3] = '\0';
365 :
366 0 : if ((Input = (CHAR *)getenv((char *)name)) == NULL) {
367 0 : Input = NIL;
368 0 : return ring_bell();
369 : }
370 0 : return CSstay;
371 : }
372 :
373 : STATIC STATUS
374 0 : do_forward(move)
375 : STATUS move;
376 : {
377 : int i;
378 : CHAR *p;
379 :
380 0 : i = 0;
381 : do {
382 0 : p = &Line[Point];
383 0 : for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++)
384 0 : if (move == CSmove)
385 0 : right(CSstay);
386 :
387 0 : for (; Point < End && isalnum(*p); Point++, p++)
388 0 : if (move == CSmove)
389 0 : right(CSstay);
390 :
391 0 : if (Point == End)
392 0 : break;
393 0 : } while (++i < Repeat);
394 :
395 0 : return CSstay;
396 : }
397 :
398 : STATIC STATUS
399 0 : do_case(type)
400 : CASE type;
401 : {
402 : int i;
403 : int end;
404 : int count;
405 : CHAR *p;
406 :
407 0 : (void)do_forward(CSstay);
408 0 : if (OldPoint != Point) {
409 0 : if ((count = Point - OldPoint) < 0)
410 0 : count = -count;
411 0 : Point = OldPoint;
412 0 : if ((end = Point + count) > End)
413 0 : end = End;
414 0 : for (i = Point, p = &Line[i]; i < end; i++, p++) {
415 0 : if (type == TOupper) {
416 0 : if (islower(*p))
417 0 : *p = toupper(*p);
418 : }
419 0 : else if (isupper(*p))
420 0 : *p = tolower(*p);
421 0 : right(CSmove);
422 : }
423 : }
424 0 : return CSstay;
425 : }
426 :
427 : STATIC STATUS
428 0 : case_down_word()
429 : {
430 0 : return do_case(TOlower);
431 : }
432 :
433 : STATIC STATUS
434 0 : case_up_word()
435 : {
436 0 : return do_case(TOupper);
437 : }
438 :
439 : STATIC void
440 0 : ceol()
441 : {
442 : int extras;
443 : int i;
444 : CHAR *p;
445 :
446 0 : for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) {
447 0 : TTYput(' ');
448 0 : if (ISCTL(*p)) {
449 0 : TTYput(' ');
450 0 : extras++;
451 : }
452 0 : else if (rl_meta_chars && ISMETA(*p)) {
453 0 : TTYput(' ');
454 0 : TTYput(' ');
455 0 : extras += 2;
456 : }
457 : }
458 :
459 0 : for (i += extras; i > Point; i--)
460 0 : TTYback();
461 0 : }
462 :
463 : STATIC void
464 0 : clear_line()
465 : {
466 0 : Point = -strlen(Prompt);
467 0 : TTYput('\r');
468 0 : ceol();
469 0 : Point = 0;
470 0 : End = 0;
471 0 : Line[0] = '\0';
472 0 : }
473 :
474 : STATIC STATUS
475 0 : insert_string(p)
476 : CHAR *p;
477 : {
478 : SIZE_T len;
479 : int i;
480 : CHAR *new;
481 : CHAR *q;
482 :
483 0 : len = strlen((char *)p);
484 0 : if (End + len >= Length) {
485 0 : if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL)
486 0 : return CSstay;
487 0 : if (Length) {
488 0 : COPYFROMTO(new, Line, Length);
489 0 : DISPOSE(Line);
490 : }
491 0 : Line = new;
492 0 : Length += len + MEM_INC;
493 : }
494 :
495 0 : for (q = &Line[Point], i = End - Point; --i >= 0; )
496 0 : q[len + i] = q[i];
497 0 : COPYFROMTO(&Line[Point], p, len);
498 0 : End += len;
499 0 : Line[End] = '\0';
500 0 : TTYstring(&Line[Point]);
501 0 : Point += len;
502 :
503 0 : return Point == End ? CSstay : CSmove;
504 : }
505 :
506 : STATIC STATUS
507 0 : redisplay()
508 : {
509 0 : TTYputs((CONST CHAR *)NEWLINE);
510 0 : TTYputs((CONST CHAR *)Prompt);
511 0 : TTYstring(Line);
512 0 : return CSmove;
513 : }
514 :
515 : STATIC STATUS
516 0 : toggle_meta_mode()
517 : {
518 0 : rl_meta_chars = ! rl_meta_chars;
519 0 : return redisplay();
520 : }
521 :
522 :
523 : STATIC CHAR *
524 0 : next_hist()
525 : {
526 0 : return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
527 : }
528 :
529 : STATIC CHAR *
530 0 : prev_hist()
531 : {
532 0 : return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
533 : }
534 :
535 : STATIC STATUS
536 0 : do_insert_hist(p)
537 : CHAR *p;
538 : {
539 0 : if (p == NULL)
540 0 : return ring_bell();
541 0 : Point = 0;
542 0 : reposition();
543 0 : ceol();
544 0 : End = 0;
545 0 : return insert_string(p);
546 : }
547 :
548 : STATIC STATUS
549 0 : do_hist(move)
550 : CHAR *(*move)();
551 : {
552 : CHAR *p;
553 : int i;
554 :
555 0 : i = 0;
556 : do {
557 0 : if ((p = (*move)()) == NULL)
558 0 : return ring_bell();
559 0 : } while (++i < Repeat);
560 0 : return do_insert_hist(p);
561 : }
562 :
563 : STATIC STATUS
564 0 : h_next()
565 : {
566 0 : return do_hist(next_hist);
567 : }
568 :
569 : STATIC STATUS
570 0 : h_prev()
571 : {
572 0 : return do_hist(prev_hist);
573 : }
574 :
575 : STATIC STATUS
576 0 : h_first()
577 : {
578 0 : return do_insert_hist(H.Lines[H.Pos = 0]);
579 : }
580 :
581 : STATIC STATUS
582 0 : h_last()
583 : {
584 0 : 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 0 : substrcmp(text, pat, len)
592 : char *text;
593 : char *pat;
594 : int len;
595 : {
596 : char c;
597 :
598 0 : if ((c = *pat) == '\0')
599 0 : return *text == '\0';
600 0 : for ( ; *text; text++)
601 0 : if (*text == c && strncmp(text, pat, len) == 0)
602 0 : return 0;
603 0 : return 1;
604 : }
605 :
606 : STATIC CHAR *
607 0 : 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 0 : if (search && *search) {
619 0 : if (old_search)
620 0 : DISPOSE(old_search);
621 0 : old_search = (CHAR *)strdup((char *)search);
622 : }
623 : else {
624 0 : if (old_search == NULL || *old_search == '\0')
625 0 : return NULL;
626 0 : search = old_search;
627 : }
628 :
629 : /* Set up pattern-finder. */
630 0 : if (*search == '^') {
631 0 : match = strncmp;
632 0 : pat = (char *)(search + 1);
633 : }
634 : else {
635 0 : match = substrcmp;
636 0 : pat = (char *)search;
637 : }
638 0 : len = strlen(pat);
639 :
640 0 : for (pos = H.Pos; (*move)() != NULL; )
641 0 : if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
642 0 : return H.Lines[H.Pos];
643 0 : H.Pos = pos;
644 0 : return NULL;
645 : }
646 :
647 : STATIC STATUS
648 0 : h_search()
649 : {
650 : static int Searching;
651 : CONST char *old_prompt;
652 : CHAR *(*move)();
653 : CHAR *p;
654 :
655 0 : if (Searching)
656 0 : return ring_bell();
657 0 : Searching = 1;
658 :
659 0 : clear_line();
660 0 : old_prompt = Prompt;
661 0 : Prompt = "Search: ";
662 0 : TTYputs((CONST CHAR *)Prompt);
663 0 : move = Repeat == NO_ARG ? prev_hist : next_hist;
664 0 : p = editinput();
665 0 : Prompt = old_prompt;
666 0 : Searching = 0;
667 0 : TTYputs((CONST CHAR *)Prompt);
668 0 : if (p == NULL && Signal > 0) {
669 0 : Signal = 0;
670 0 : clear_line();
671 0 : return redisplay();
672 : }
673 0 : p = search_hist(p, move);
674 0 : clear_line();
675 0 : if (p == NULL) {
676 0 : (void)ring_bell();
677 0 : return redisplay();
678 : }
679 0 : return do_insert_hist(p);
680 : }
681 :
682 : STATIC STATUS
683 0 : fd_char()
684 : {
685 : int i;
686 :
687 0 : i = 0;
688 : do {
689 0 : if (Point >= End)
690 0 : break;
691 0 : right(CSmove);
692 0 : } while (++i < Repeat);
693 0 : return CSstay;
694 : }
695 :
696 : STATIC void
697 0 : save_yank(begin, i)
698 : int begin;
699 : int i;
700 : {
701 0 : if (Yanked) {
702 0 : DISPOSE(Yanked);
703 0 : Yanked = NULL;
704 : }
705 :
706 0 : if (i < 1)
707 0 : return;
708 :
709 0 : if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) {
710 0 : COPYFROMTO(Yanked, &Line[begin], i);
711 0 : Yanked[i] = '\0';
712 : }
713 : }
714 :
715 : STATIC STATUS
716 0 : delete_string(count)
717 : int count;
718 : {
719 : int i;
720 : CHAR *p;
721 :
722 0 : if (count <= 0 || End == Point)
723 0 : return ring_bell();
724 :
725 0 : if (count == 1 && Point == End - 1) {
726 : /* Optimize common case of delete at end of line. */
727 0 : End--;
728 0 : p = &Line[Point];
729 0 : i = 1;
730 0 : TTYput(' ');
731 0 : if (ISCTL(*p)) {
732 0 : i = 2;
733 0 : TTYput(' ');
734 : }
735 0 : else if (rl_meta_chars && ISMETA(*p)) {
736 0 : i = 3;
737 0 : TTYput(' ');
738 0 : TTYput(' ');
739 : }
740 0 : TTYbackn(i);
741 0 : *p = '\0';
742 0 : return CSmove;
743 : }
744 0 : if (Point + count > End && (count = End - Point) <= 0)
745 0 : return CSstay;
746 :
747 0 : if (count > 1)
748 0 : save_yank(Point, count);
749 :
750 0 : for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++)
751 0 : p[0] = p[count];
752 0 : ceol();
753 0 : End -= count;
754 0 : TTYstring(&Line[Point]);
755 0 : return CSmove;
756 : }
757 :
758 : STATIC STATUS
759 0 : bk_char()
760 : {
761 : int i;
762 :
763 0 : i = 0;
764 : do {
765 0 : if (Point == 0)
766 0 : break;
767 0 : left(CSmove);
768 0 : } while (++i < Repeat);
769 :
770 0 : return CSstay;
771 : }
772 :
773 : STATIC STATUS
774 0 : bk_del_char()
775 : {
776 : int i;
777 :
778 0 : i = 0;
779 : do {
780 0 : if (Point == 0)
781 0 : break;
782 0 : left(CSmove);
783 0 : } while (++i < Repeat);
784 :
785 0 : return delete_string(i);
786 : }
787 :
788 : STATIC STATUS
789 0 : kill_line()
790 : {
791 : int i;
792 :
793 0 : if (Repeat != NO_ARG) {
794 0 : if (Repeat < Point) {
795 0 : i = Point;
796 0 : Point = Repeat;
797 0 : reposition();
798 0 : (void)delete_string(i - Point);
799 : }
800 0 : else if (Repeat > Point) {
801 0 : right(CSmove);
802 0 : (void)delete_string(Repeat - Point - 1);
803 : }
804 0 : return CSmove;
805 : }
806 :
807 0 : save_yank(Point, End - Point);
808 0 : Line[Point] = '\0';
809 0 : ceol();
810 0 : End = Point;
811 0 : return CSstay;
812 : }
813 :
814 : STATIC STATUS
815 0 : 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 0 : if (Repeat == NO_ARG || Repeat < 2) {
825 0 : buff[0] = c;
826 0 : buff[1] = '\0';
827 0 : return insert_string(buff);
828 : }
829 :
830 0 : if ((p = NEW(CHAR, Repeat + 1)) == NULL)
831 0 : return CSstay;
832 0 : for (i = Repeat, q = p; --i >= 0; )
833 0 : *q++ = c;
834 0 : *q = '\0';
835 0 : Repeat = 0;
836 0 : s = insert_string(p);
837 0 : DISPOSE(p);
838 0 : return s;
839 : }
840 :
841 : STATIC STATUS
842 0 : meta()
843 : {
844 : unsigned int c;
845 : KEYMAP *kp;
846 :
847 0 : if ((int)(c = TTYget()) == EOF)
848 0 : return CSeof;
849 : #if defined(ANSI_ARROWS)
850 : /* Also include VT-100 arrows. */
851 0 : if (c == '[' || c == 'O')
852 0 : switch (c = TTYget()) {
853 0 : default: return ring_bell();
854 0 : case EOF: return CSeof;
855 0 : case 'A': return h_prev();
856 0 : case 'B': return h_next();
857 0 : case 'C': return fd_char();
858 0 : case 'D': return bk_char();
859 : }
860 : #endif /* defined(ANSI_ARROWS) */
861 :
862 0 : if (isdigit(c)) {
863 0 : for (Repeat = c - '0'; (int)(c = TTYget()) != EOF && isdigit(c); )
864 0 : Repeat = Repeat * 10 + c - '0';
865 0 : Pushed = 1;
866 0 : PushBack = c;
867 0 : return CSstay;
868 : }
869 :
870 0 : if (isupper(c))
871 0 : return do_macro(c);
872 0 : for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
873 0 : if (kp->Key == c)
874 0 : return (*kp->Function)();
875 :
876 0 : return ring_bell();
877 : }
878 :
879 : STATIC STATUS
880 0 : emacs(c)
881 : unsigned int c;
882 : {
883 : STATUS s;
884 : KEYMAP *kp;
885 :
886 0 : if (rl_meta_chars && ISMETA(c)) {
887 0 : Pushed = 1;
888 0 : PushBack = UNMETA(c);
889 0 : return meta();
890 : }
891 0 : for (kp = Map; kp->Function; kp++)
892 0 : if (kp->Key == c)
893 0 : break;
894 0 : s = kp->Function ? (*kp->Function)() : insert_char((int)c);
895 0 : if (!Pushed)
896 : /* No pushback means no repeat count; hacky, but true. */
897 0 : Repeat = NO_ARG;
898 0 : return s;
899 : }
900 :
901 : STATIC STATUS
902 0 : TTYspecial(c)
903 : unsigned int c;
904 : {
905 0 : if (ISMETA(c))
906 0 : return CSdispatch;
907 :
908 0 : if (c == rl_erase || (int)c == DEL)
909 0 : return bk_del_char();
910 0 : if (c == rl_kill) {
911 0 : if (Point != 0) {
912 0 : Point = 0;
913 0 : reposition();
914 : }
915 0 : Repeat = NO_ARG;
916 0 : return kill_line();
917 : }
918 0 : if (c == rl_eof && Point == 0 && End == 0)
919 0 : return CSeof;
920 0 : if (c == rl_intr) {
921 0 : Signal = SIGINT;
922 0 : return CSsignal;
923 : }
924 0 : if (c == rl_quit) {
925 0 : Signal = SIGQUIT;
926 0 : return CSeof;
927 : }
928 :
929 0 : return CSdispatch;
930 : }
931 :
932 : STATIC CHAR *
933 0 : editinput()
934 : {
935 : unsigned int c;
936 :
937 0 : Repeat = NO_ARG;
938 0 : OldPoint = Point = Mark = End = 0;
939 0 : Line[0] = '\0';
940 :
941 0 : Signal = -1;
942 0 : while ((int)(c = TTYget()) != EOF)
943 0 : switch (TTYspecial(c)) {
944 : case CSdone:
945 0 : return Line;
946 : case CSeof:
947 0 : return NULL;
948 : case CSsignal:
949 0 : return (CHAR *)"";
950 : case CSmove:
951 0 : reposition();
952 0 : break;
953 : case CSdispatch:
954 0 : switch (emacs(c)) {
955 : case CSdone:
956 0 : return Line;
957 : case CSeof:
958 0 : return NULL;
959 : case CSsignal:
960 0 : return (CHAR *)"";
961 : case CSmove:
962 0 : reposition();
963 0 : break;
964 : case CSdispatch:
965 : case CSstay:
966 0 : break;
967 : }
968 0 : break;
969 : case CSstay:
970 0 : break;
971 : }
972 0 : if (strlen((char *)Line))
973 0 : return Line;
974 0 : free(Line);
975 0 : return NULL;
976 : }
977 :
978 : STATIC void
979 0 : hist_add(p)
980 : CHAR *p;
981 : {
982 : int i;
983 :
984 0 : if ((p = (CHAR *)strdup((char *)p)) == NULL)
985 0 : return;
986 0 : if (H.Size < HIST_SIZE)
987 0 : H.Lines[H.Size++] = p;
988 : else {
989 0 : DISPOSE(H.Lines[0]);
990 0 : for (i = 0; i < HIST_SIZE - 1; i++)
991 0 : H.Lines[i] = H.Lines[i + 1];
992 0 : H.Lines[i] = p;
993 : }
994 0 : H.Pos = H.Size - 1;
995 : }
996 :
997 : /*
998 : ** For compatibility with FSF readline.
999 : */
1000 : /* ARGSUSED0 */
1001 : void
1002 0 : rl_reset_terminal(p)
1003 : char *p;
1004 : {
1005 : (void)p;
1006 0 : }
1007 :
1008 : void
1009 0 : rl_initialize()
1010 : {
1011 0 : }
1012 :
1013 : char *
1014 0 : readline(prompt)
1015 : CONST char *prompt;
1016 : {
1017 : CHAR *line;
1018 : int s;
1019 :
1020 0 : if (Line == NULL) {
1021 0 : Length = MEM_INC;
1022 0 : if ((Line = NEW(CHAR, Length)) == NULL)
1023 0 : return NULL;
1024 : }
1025 :
1026 0 : TTYinfo();
1027 0 : rl_ttyset(0);
1028 0 : hist_add(NIL);
1029 0 : ScreenSize = SCREEN_INC;
1030 0 : Screen = NEW(char, ScreenSize);
1031 0 : Prompt = prompt ? prompt : (char *)NIL;
1032 0 : TTYputs((CONST CHAR *)Prompt);
1033 0 : if ((line = editinput()) != NULL) {
1034 0 : line = (CHAR *)strdup((char *)line);
1035 0 : TTYputs((CHAR *)NEWLINE);
1036 0 : TTYflush();
1037 : }
1038 0 : rl_ttyset(1);
1039 0 : DISPOSE(Screen);
1040 0 : DISPOSE(H.Lines[--H.Size]);
1041 0 : if (Signal > 0) {
1042 0 : s = Signal;
1043 0 : Signal = 0;
1044 0 : (void)kill(getpid(), s);
1045 : }
1046 0 : return (char *)line;
1047 : }
1048 :
1049 : void
1050 0 : add_history(p)
1051 : char *p;
1052 : {
1053 0 : if (p == NULL || *p == '\0')
1054 0 : return;
1055 :
1056 : #if defined(UNIQUE_HISTORY)
1057 0 : if (H.Size && strcmp(p, (char *)H.Lines[H.Size - 1]) == 0)
1058 0 : return;
1059 : #endif /* defined(UNIQUE_HISTORY) */
1060 0 : hist_add((CHAR *)p);
1061 : }
1062 :
1063 :
1064 : STATIC STATUS
1065 0 : beg_line()
1066 : {
1067 0 : if (Point) {
1068 0 : Point = 0;
1069 0 : return CSmove;
1070 : }
1071 0 : return CSstay;
1072 : }
1073 :
1074 : STATIC STATUS
1075 0 : del_char()
1076 : {
1077 0 : return delete_string(Repeat == NO_ARG ? 1 : Repeat);
1078 : }
1079 :
1080 : STATIC STATUS
1081 0 : end_line()
1082 : {
1083 0 : if (Point != End) {
1084 0 : Point = End;
1085 0 : return CSmove;
1086 : }
1087 0 : return CSstay;
1088 : }
1089 :
1090 : STATIC STATUS
1091 0 : accept_line()
1092 : {
1093 0 : Line[End] = '\0';
1094 0 : return CSdone;
1095 : }
1096 :
1097 : STATIC STATUS
1098 0 : transpose()
1099 : {
1100 : CHAR c;
1101 :
1102 0 : if (Point) {
1103 0 : if (Point == End)
1104 0 : left(CSmove);
1105 0 : c = Line[Point - 1];
1106 0 : left(CSstay);
1107 0 : Line[Point - 1] = Line[Point];
1108 0 : TTYshow(Line[Point - 1]);
1109 0 : Line[Point++] = c;
1110 0 : TTYshow(c);
1111 : }
1112 0 : return CSstay;
1113 : }
1114 :
1115 : STATIC STATUS
1116 0 : quote()
1117 : {
1118 : unsigned int c;
1119 :
1120 0 : return (int)(c = TTYget()) == EOF ? CSeof : insert_char((int)c);
1121 : }
1122 :
1123 : STATIC STATUS
1124 0 : wipe()
1125 : {
1126 : int i;
1127 :
1128 0 : if (Mark > End)
1129 0 : return ring_bell();
1130 :
1131 0 : if (Point > Mark) {
1132 0 : i = Point;
1133 0 : Point = Mark;
1134 0 : Mark = i;
1135 0 : reposition();
1136 : }
1137 :
1138 0 : return delete_string(Mark - Point);
1139 : }
1140 :
1141 : STATIC STATUS
1142 0 : mk_set()
1143 : {
1144 0 : Mark = Point;
1145 0 : return CSstay;
1146 : }
1147 :
1148 : STATIC STATUS
1149 0 : exchange()
1150 : {
1151 : unsigned int c;
1152 :
1153 0 : if ((c = TTYget()) != CTL('X'))
1154 0 : return (int)c == EOF ? CSeof : ring_bell();
1155 :
1156 0 : if ((int)(c = Mark) <= End) {
1157 0 : Mark = Point;
1158 0 : Point = c;
1159 0 : return CSmove;
1160 : }
1161 0 : return CSstay;
1162 : }
1163 :
1164 : STATIC STATUS
1165 0 : yank()
1166 : {
1167 0 : if (Yanked && *Yanked)
1168 0 : return insert_string(Yanked);
1169 0 : return CSstay;
1170 : }
1171 :
1172 : STATIC STATUS
1173 0 : copy_region()
1174 : {
1175 0 : if (Mark > End)
1176 0 : return ring_bell();
1177 :
1178 0 : if (Point > Mark)
1179 0 : save_yank(Mark, Point - Mark);
1180 : else
1181 0 : save_yank(Point, Mark - Point);
1182 :
1183 0 : return CSstay;
1184 : }
1185 :
1186 : STATIC STATUS
1187 0 : move_to_char()
1188 : {
1189 : unsigned int c;
1190 : int i;
1191 : CHAR *p;
1192 :
1193 0 : if ((int)(c = TTYget()) == EOF)
1194 0 : return CSeof;
1195 0 : for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1196 0 : if (*p == c) {
1197 0 : Point = i;
1198 0 : return CSmove;
1199 : }
1200 0 : return CSstay;
1201 : }
1202 :
1203 : STATIC STATUS
1204 0 : fd_word()
1205 : {
1206 0 : return do_forward(CSmove);
1207 : }
1208 :
1209 : STATIC STATUS
1210 0 : fd_kill_word()
1211 : {
1212 : int i;
1213 :
1214 0 : (void)do_forward(CSstay);
1215 0 : if (OldPoint != Point) {
1216 0 : i = Point - OldPoint;
1217 0 : Point = OldPoint;
1218 0 : return delete_string(i);
1219 : }
1220 0 : return CSstay;
1221 : }
1222 :
1223 : STATIC STATUS
1224 0 : bk_word()
1225 : {
1226 : int i;
1227 : CHAR *p;
1228 :
1229 0 : i = 0;
1230 : do {
1231 0 : for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1232 0 : left(CSmove);
1233 :
1234 0 : for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1235 0 : left(CSmove);
1236 :
1237 0 : if (Point == 0)
1238 0 : break;
1239 0 : } while (++i < Repeat);
1240 :
1241 0 : return CSstay;
1242 : }
1243 :
1244 : STATIC STATUS
1245 0 : bk_kill_word()
1246 : {
1247 0 : (void)bk_word();
1248 0 : if (OldPoint != Point)
1249 0 : return delete_string(OldPoint - Point);
1250 0 : return CSstay;
1251 : }
1252 :
1253 : STATIC int
1254 0 : argify(line, avp)
1255 : CHAR *line;
1256 : CHAR ***avp;
1257 : {
1258 : CHAR *c;
1259 : CHAR **p;
1260 : CHAR **new;
1261 : int ac;
1262 : int i;
1263 :
1264 0 : i = MEM_INC;
1265 0 : if ((*avp = p = NEW(CHAR*, i))== NULL)
1266 0 : return 0;
1267 :
1268 0 : for (c = line; isspace(*c); c++)
1269 0 : continue;
1270 0 : if (*c == '\n' || *c == '\0')
1271 0 : return 0;
1272 :
1273 0 : for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1274 0 : if (isspace(*c)) {
1275 0 : *c++ = '\0';
1276 0 : if (*c && *c != '\n') {
1277 0 : if (ac + 1 == i) {
1278 0 : new = NEW(CHAR*, i + MEM_INC);
1279 0 : if (new == NULL) {
1280 0 : p[ac] = NULL;
1281 0 : return ac;
1282 : }
1283 0 : COPYFROMTO(new, p, i * sizeof (char **));
1284 0 : i += MEM_INC;
1285 0 : DISPOSE(p);
1286 0 : *avp = p = new;
1287 : }
1288 0 : p[ac++] = c;
1289 : }
1290 : }
1291 : else
1292 0 : c++;
1293 : }
1294 0 : *c = '\0';
1295 0 : p[ac] = NULL;
1296 0 : return ac;
1297 : }
1298 :
1299 : STATIC STATUS
1300 0 : last_argument()
1301 : {
1302 : CHAR **av;
1303 : CHAR *p;
1304 : STATUS s;
1305 : int ac;
1306 :
1307 0 : if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
1308 0 : return ring_bell();
1309 :
1310 0 : if ((p = (CHAR *)strdup((char *)p)) == NULL)
1311 0 : return CSstay;
1312 0 : ac = argify(p, &av);
1313 :
1314 0 : if (Repeat != NO_ARG)
1315 0 : s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1316 : else
1317 0 : s = ac ? insert_string(av[ac - 1]) : CSstay;
1318 :
1319 0 : if (ac)
1320 0 : DISPOSE(av);
1321 0 : DISPOSE(p);
1322 0 : return s;
1323 : }
1324 :
1325 : STATIC KEYMAP Map[32] = {
1326 : { CTL('@'), ring_bell },
1327 : { CTL('A'), beg_line },
1328 : { CTL('B'), bk_char },
1329 : { CTL('D'), del_char },
1330 : { CTL('E'), end_line },
1331 : { CTL('F'), fd_char },
1332 : { CTL('G'), ring_bell },
1333 : { CTL('H'), bk_del_char },
1334 : { CTL('J'), accept_line },
1335 : { CTL('K'), kill_line },
1336 : { CTL('L'), redisplay },
1337 : { CTL('M'), accept_line },
1338 : { CTL('N'), h_next },
1339 : { CTL('O'), ring_bell },
1340 : { CTL('P'), h_prev },
1341 : { CTL('Q'), ring_bell },
1342 : { CTL('R'), h_search },
1343 : { CTL('S'), ring_bell },
1344 : { CTL('T'), transpose },
1345 : { CTL('U'), ring_bell },
1346 : { CTL('V'), quote },
1347 : { CTL('W'), wipe },
1348 : { CTL('X'), exchange },
1349 : { CTL('Y'), yank },
1350 : { CTL('Z'), ring_bell },
1351 : { CTL('['), meta },
1352 : { CTL(']'), move_to_char },
1353 : { CTL('^'), ring_bell },
1354 : { CTL('_'), ring_bell },
1355 : { 0, NULL }
1356 : };
1357 :
1358 : STATIC KEYMAP MetaMap[16]= {
1359 : { CTL('H'), bk_kill_word },
1360 : { DEL, bk_kill_word },
1361 : { ' ', mk_set },
1362 : { '.', last_argument },
1363 : { '<', h_first },
1364 : { '>', h_last },
1365 : { 'b', bk_word },
1366 : { 'd', fd_kill_word },
1367 : { 'f', fd_word },
1368 : { 'l', case_down_word },
1369 : { 'm', toggle_meta_mode },
1370 : { 'u', case_up_word },
1371 : { 'y', yank },
1372 : { 'w', copy_region },
1373 : { 0, NULL }
1374 : };
1375 :
|