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