|
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 |
|
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 |
|
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 |
|
158 TTYputs(p) |
|
159 CONST CHAR *p; |
|
160 { |
|
161 while (*p) |
|
162 TTYput(*p++); |
|
163 } |
|
164 |
|
165 STATIC void |
|
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 |
|
187 TTYstring(p) |
|
188 CHAR *p; |
|
189 { |
|
190 while (*p) |
|
191 TTYshow(*p++); |
|
192 } |
|
193 |
|
194 STATIC unsigned int |
|
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 |
|
212 TTYbackn(n) |
|
213 int n; |
|
214 { |
|
215 while (--n >= 0) |
|
216 TTYback(); |
|
217 } |
|
218 |
|
219 STATIC void |
|
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 |
|
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 |
|
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 |
|
305 right(Change) |
|
306 STATUS Change; |
|
307 { |
|
308 TTYshow(Line[Point]); |
|
309 if (Change == CSmove) |
|
310 Point++; |
|
311 } |
|
312 |
|
313 STATIC STATUS |
|
314 ring_bell() |
|
315 { |
|
316 TTYput('\07'); |
|
317 TTYflush(); |
|
318 return CSstay; |
|
319 } |
|
320 |
|
321 STATIC STATUS |
|
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 |
|
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 |
|
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 |
|
394 case_down_word() |
|
395 { |
|
396 return do_case(TOlower); |
|
397 } |
|
398 |
|
399 STATIC STATUS |
|
400 case_up_word() |
|
401 { |
|
402 return do_case(TOupper); |
|
403 } |
|
404 |
|
405 STATIC void |
|
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 |
|
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 |
|
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 |
|
473 redisplay() |
|
474 { |
|
475 TTYputs((CONST CHAR *)NEWLINE); |
|
476 TTYputs((CONST CHAR *)Prompt); |
|
477 TTYstring(Line); |
|
478 return CSmove; |
|
479 } |
|
480 |
|
481 STATIC STATUS |
|
482 toggle_meta_mode() |
|
483 { |
|
484 rl_meta_chars = ! rl_meta_chars; |
|
485 return redisplay(); |
|
486 } |
|
487 |
|
488 |
|
489 STATIC CHAR * |
|
490 next_hist() |
|
491 { |
|
492 return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos]; |
|
493 } |
|
494 |
|
495 STATIC CHAR * |
|
496 prev_hist() |
|
497 { |
|
498 return H.Pos == 0 ? NULL : H.Lines[--H.Pos]; |
|
499 } |
|
500 |
|
501 STATIC STATUS |
|
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 |
|
530 h_next() |
|
531 { |
|
532 return do_hist(next_hist); |
|
533 } |
|
534 |
|
535 STATIC STATUS |
|
536 h_prev() |
|
537 { |
|
538 return do_hist(prev_hist); |
|
539 } |
|
540 |
|
541 STATIC STATUS |
|
542 h_first() |
|
543 { |
|
544 return do_insert_hist(H.Lines[H.Pos = 0]); |
|
545 } |
|
546 |
|
547 STATIC STATUS |
|
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 |
|
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 * |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 * |
|
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 |
|
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 |
|
968 rl_reset_terminal(p) |
|
969 char *p; |
|
970 { |
|
971 (void)p; |
|
972 } |
|
973 |
|
974 void |
|
975 rl_initialize() |
|
976 { |
|
977 } |
|
978 |
|
979 char * |
|
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 |
|
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 |
|
1031 beg_line() |
|
1032 { |
|
1033 if (Point) { |
|
1034 Point = 0; |
|
1035 return CSmove; |
|
1036 } |
|
1037 return CSstay; |
|
1038 } |
|
1039 |
|
1040 STATIC STATUS |
|
1041 del_char() |
|
1042 { |
|
1043 return delete_string(Repeat == NO_ARG ? 1 : Repeat); |
|
1044 } |
|
1045 |
|
1046 STATIC STATUS |
|
1047 end_line() |
|
1048 { |
|
1049 if (Point != End) { |
|
1050 Point = End; |
|
1051 return CSmove; |
|
1052 } |
|
1053 return CSstay; |
|
1054 } |
|
1055 |
|
1056 STATIC STATUS |
|
1057 accept_line() |
|
1058 { |
|
1059 Line[End] = '\0'; |
|
1060 return CSdone; |
|
1061 } |
|
1062 |
|
1063 STATIC STATUS |
|
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 |
|
1082 quote() |
|
1083 { |
|
1084 unsigned int c; |
|
1085 |
|
1086 return (int)(c = TTYget()) == EOF ? CSeof : insert_char((int)c); |
|
1087 } |
|
1088 |
|
1089 STATIC STATUS |
|
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 |
|
1108 mk_set() |
|
1109 { |
|
1110 Mark = Point; |
|
1111 return CSstay; |
|
1112 } |
|
1113 |
|
1114 STATIC STATUS |
|
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 |
|
1131 yank() |
|
1132 { |
|
1133 if (Yanked && *Yanked) |
|
1134 return insert_string(Yanked); |
|
1135 return CSstay; |
|
1136 } |
|
1137 |
|
1138 STATIC STATUS |
|
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 |
|
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 |
|
1170 fd_word() |
|
1171 { |
|
1172 return do_forward(CSmove); |
|
1173 } |
|
1174 |
|
1175 STATIC STATUS |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |