3 ** Main editing routines for editline library.
11 ** Manifest constants.
13 #define SCREEN_WIDTH 80
14 #define SCREEN_ROWS 24
17 #define CTL(x) ((x) & 0x1F)
18 #define ISCTL(x) ((x) && (x) < ' ')
19 #define UNCTL(x) ((x) + 64)
20 #define META(x) ((x) | 0x80)
21 #define ISMETA(x) ((x) & 0x80)
22 #define UNMETA(x) ((x) & 0x7F)
23 #if !defined(HIST_SIZE)
25 #endif /* !defined(HIST_SIZE) */
28 ** Command status codes.
30 typedef enum _STATUS
{
31 CSdone
, CSeof
, CSmove
, CSdispatch
, CSstay
, CSsignal
35 ** The type of case-changing to perform.
42 ** Key to command mapping.
44 typedef struct _KEYMAP
{
50 ** Command history structure.
52 typedef struct _HISTORY
{
55 CHAR
*Lines
[HIST_SIZE
];
67 STATIC CHAR NIL
[] = "";
68 STATIC CONST CHAR
*Input
= NIL
;
70 STATIC CONST
char *Prompt
;
73 STATIC
char NEWLINE
[]= CRLF
;
83 FORWARD KEYMAP Map
[33];
84 FORWARD KEYMAP MetaMap
[17];
86 STATIC SIZE_T ScreenCount
;
87 STATIC SIZE_T ScreenSize
;
88 STATIC
char *backspace
;
92 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
93 int rl_meta_chars
= 0;
98 STATIC CHAR
*editinput();
101 #if defined(USE_TERMCAP)
102 extern char *getenv();
103 extern char *tgetstr();
104 extern int tgetent();
105 #endif /* defined(USE_TERMCAP) */
108 ** TTY input/output functions.
115 (void)write(1, Screen
, ScreenCount
);
124 Screen
[ScreenCount
] = c
;
125 if (++ScreenCount
>= ScreenSize
- 1) {
126 ScreenSize
+= SCREEN_INC
;
127 RENEW(Screen
, char, ScreenSize
);
151 else if (rl_meta_chars
&& ISMETA(c
)) {
183 r
= read(0, &c
, (SIZE_T
)1);
184 } while (r
== -1 && errno
== EINTR
);
185 return r
== 1 ? c
: EOF
;
188 #define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
202 #if defined(USE_TERMCAP)
206 #endif /* defined(USE_TERMCAP) */
207 #if defined(TIOCGWINSZ)
209 #endif /* defined(TIOCGWINSZ) */
212 #if defined(TIOCGWINSZ)
213 /* Perhaps we got resized. */
214 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0
215 && W
.ws_col
> 0 && W
.ws_row
> 0) {
216 TTYwidth
= (int)W
.ws_col
;
217 TTYrows
= (int)W
.ws_row
;
219 #endif /* defined(TIOCGWINSZ) */
224 TTYwidth
= TTYrows
= 0;
225 #if defined(USE_TERMCAP)
227 if ((term
= getenv("TERM")) == NULL
)
229 if (tgetent(buff
, term
) < 0) {
230 TTYwidth
= SCREEN_WIDTH
;
231 TTYrows
= SCREEN_ROWS
;
234 if ((backspace
= tgetstr("le", &bp
)) != NULL
)
235 backspace
= strdup(backspace
);
236 TTYwidth
= tgetnum("co");
237 TTYrows
= tgetnum("li");
238 #endif /* defined(USE_TERMCAP) */
240 #if defined(TIOCGWINSZ)
241 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0) {
242 TTYwidth
= (int)W
.ws_col
;
243 TTYrows
= (int)W
.ws_row
;
245 #endif /* defined(TIOCGWINSZ) */
247 if (TTYwidth
<= 0 || TTYrows
<= 0) {
248 TTYwidth
= SCREEN_WIDTH
;
249 TTYrows
= SCREEN_ROWS
;
255 ** Print an array of words in columns.
271 /* Find longest name, determine column count from that. */
272 for (longest
= 0, i
= 0; i
< ac
; i
++)
273 if ((j
= strlen((char *)av
[i
])) > longest
)
275 cols
= TTYwidth
/ (longest
+ 3);
277 TTYputs((CHAR
*)NEWLINE
);
278 for (skip
= ac
/ cols
+ 1, i
= 0; i
< skip
; i
++) {
279 for (j
= i
; j
< ac
; j
+= skip
) {
280 for (p
= av
[j
], len
= strlen((char *)p
), k
= len
; --k
>= 0; p
++)
283 while (++len
< longest
+ 3)
286 TTYputs((CHAR
*)NEWLINE
);
297 TTYputs((CONST CHAR
*)Prompt
);
298 for (i
= Point
, p
= Line
; --i
>= 0; p
++)
308 if (ISCTL(Line
[Point
- 1]))
310 else if (rl_meta_chars
&& ISMETA(Line
[Point
- 1])) {
315 if (Change
== CSmove
)
323 TTYshow(Line
[Point
]);
324 if (Change
== CSmove
)
347 if ((Input
= (CHAR
*)getenv((char *)name
)) == NULL
) {
364 for ( ; Point
< End
&& (*p
== ' ' || !isalnum(*p
)); Point
++, p
++)
368 for (; Point
< End
&& isalnum(*p
); Point
++, p
++)
374 } while (++i
< Repeat
);
388 (void)do_forward(CSstay
);
389 if (OldPoint
!= Point
) {
390 if ((count
= Point
- OldPoint
) < 0)
393 if ((end
= Point
+ count
) > End
)
395 for (i
= Point
, p
= &Line
[i
]; i
< end
; i
++, p
++) {
396 if (type
== TOupper
) {
400 else if (isupper(*p
))
411 return do_case(TOlower
);
417 return do_case(TOupper
);
427 for (extras
= 0, i
= Point
, p
= &Line
[i
]; i
<= End
; i
++, p
++) {
433 else if (rl_meta_chars
&& ISMETA(*p
)) {
440 for (i
+= extras
; i
> Point
; i
--)
447 Point
= -strlen(Prompt
);
464 len
= strlen((char *)p
);
465 if (End
+ len
>= Length
) {
466 if ((new = NEW(CHAR
, Length
+ len
+ MEM_INC
)) == NULL
)
469 COPYFROMTO(new, Line
, Length
);
473 Length
+= len
+ MEM_INC
;
476 for (q
= &Line
[Point
], i
= End
- Point
; --i
>= 0; )
478 COPYFROMTO(&Line
[Point
], p
, len
);
481 TTYstring(&Line
[Point
]);
484 return Point
== End
? CSstay
: CSmove
;
490 TTYputs((CONST CHAR
*)NEWLINE
);
491 TTYputs((CONST CHAR
*)Prompt
);
499 rl_meta_chars
= ! rl_meta_chars
;
507 return H
.Pos
>= H
.Size
- 1 ? NULL
: H
.Lines
[++H
.Pos
];
513 return H
.Pos
== 0 ? NULL
: H
.Lines
[--H
.Pos
];
526 return insert_string(p
);
538 if ((p
= (*move
)()) == NULL
)
540 } while (++i
< Repeat
);
541 return do_insert_hist(p
);
547 return do_hist(next_hist
);
553 return do_hist(prev_hist
);
559 return do_insert_hist(H
.Lines
[H
.Pos
= 0]);
565 return do_insert_hist(H
.Lines
[H
.Pos
= H
.Size
- 1]);
569 ** Return zero if pat appears as a substring in text.
572 substrcmp(text
, pat
, len
)
579 if ((c
= *pat
) == '\0')
580 return *text
== '\0';
581 for ( ; *text
; text
++)
582 if (*text
== c
&& strncmp(text
, pat
, len
) == 0)
588 search_hist(search
, move
)
592 static CHAR
*old_search
;
598 /* Save or get remembered search pattern. */
599 if (search
&& *search
) {
602 old_search
= (CHAR
*)strdup((char *)search
);
605 if (old_search
== NULL
|| *old_search
== '\0')
610 /* Set up pattern-finder. */
611 if (*search
== '^') {
613 pat
= (char *)(search
+ 1);
617 pat
= (char *)search
;
621 for (pos
= H
.Pos
; (*move
)() != NULL
; )
622 if ((*match
)((char *)H
.Lines
[H
.Pos
], pat
, len
) == 0)
623 return H
.Lines
[H
.Pos
];
631 static int Searching
;
632 CONST
char *old_prompt
;
643 TTYputs((CONST CHAR
*)Prompt
);
644 move
= Repeat
== NO_ARG
? prev_hist
: next_hist
;
648 TTYputs((CONST CHAR
*)Prompt
);
649 if (p
== NULL
&& Signal
> 0) {
654 p
= search_hist(p
, move
);
660 return do_insert_hist(p
);
673 } while (++i
< Repeat
);
690 if ((Yanked
= NEW(CHAR
, (SIZE_T
)i
+ 1)) != NULL
) {
691 COPYFROMTO(Yanked
, &Line
[begin
], i
);
703 if (count
<= 0 || End
== Point
)
706 if (count
== 1 && Point
== End
- 1) {
707 /* Optimize common case of delete at end of line. */
716 else if (rl_meta_chars
&& ISMETA(*p
)) {
725 if (Point
+ count
> End
&& (count
= End
- Point
) <= 0)
729 save_yank(Point
, count
);
731 for (p
= &Line
[Point
], i
= End
- (Point
+ count
) + 1; --i
>= 0; p
++)
735 TTYstring(&Line
[Point
]);
749 } while (++i
< Repeat
);
764 } while (++i
< Repeat
);
766 return delete_string(i
);
774 if (Repeat
!= NO_ARG
) {
775 if (Repeat
< Point
) {
779 (void)delete_string(i
- Point
);
781 else if (Repeat
> Point
) {
783 (void)delete_string(Repeat
- Point
- 1);
788 save_yank(Point
, End
- Point
);
805 if (Repeat
== NO_ARG
|| Repeat
< 2) {
808 return insert_string(buff
);
811 if ((p
= NEW(CHAR
, Repeat
+ 1)) == NULL
)
813 for (i
= Repeat
, q
= p
; --i
>= 0; )
817 s
= insert_string(p
);
828 if ((c
= TTYget()) == EOF
)
830 #if defined(ANSI_ARROWS)
831 /* Also include VT-100 arrows. */
832 if (c
== '[' || c
== 'O')
833 switch (c
= TTYget()) {
834 default: return ring_bell();
835 case EOF
: return CSeof
;
836 case 'A': return h_prev();
837 case 'B': return h_next();
838 case 'C': return fd_char();
839 case 'D': return bk_char();
841 #endif /* defined(ANSI_ARROWS) */
844 for (Repeat
= c
- '0'; (c
= TTYget()) != EOF
&& isdigit(c
); )
845 Repeat
= Repeat
* 10 + c
- '0';
853 for (kp
= MetaMap
; kp
->Function
; kp
++)
855 return (*kp
->Function
)();
868 if (rl_meta_chars
&& ISMETA(c
)) {
870 PushBack
= UNMETA(c
);
873 for (kp
= Map
; kp
->Function
; kp
++)
876 s
= kp
->Function
? (*kp
->Function
)() : insert_char((int)c
);
878 /* No pushback means no repeat count; hacky, but true. */
890 if (c
== rl_erase
|| c
== DEL
)
891 return bk_del_char();
900 if (c
== rl_eof
&& Point
== 0 && End
== 0)
920 OldPoint
= Point
= Mark
= End
= 0;
924 while ((c
= TTYget()) != EOF
)
925 switch (TTYspecial(c
)) {
963 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
965 if (H
.Size
< HIST_SIZE
)
966 H
.Lines
[H
.Size
++] = p
;
969 for (i
= 0; i
< HIST_SIZE
- 1; i
++)
970 H
.Lines
[i
] = H
.Lines
[i
+ 1];
977 ** For compatibility with FSF readline.
1000 if ((Line
= NEW(CHAR
, Length
)) == NULL
)
1007 ScreenSize
= SCREEN_INC
;
1008 Screen
= NEW(char, ScreenSize
);
1009 Prompt
= prompt
? prompt
: (char *)NIL
;
1010 TTYputs((CONST CHAR
*)Prompt
);
1011 if ((line
= editinput()) != NULL
) {
1012 line
= (CHAR
*)strdup((char *)line
);
1013 TTYputs((CHAR
*)NEWLINE
);
1018 DISPOSE(H
.Lines
[--H
.Size
]);
1020 if (line
!= NULL
&& *line
!= '\0'
1021 #if defined(UNIQUE_HISTORY)
1022 && !(H
.Pos
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1023 #endif /* defined(UNIQUE_HISTORY) */
1024 && !(H
.Size
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1032 (void)kill(getpid(), s
);
1034 return (char *)line
;
1041 #ifdef obsolete /* Made part of readline(). -- kjb */
1042 if (p
== NULL
|| *p
== '\0')
1045 #if defined(UNIQUE_HISTORY)
1046 if (H
.Pos
&& strcmp(p
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1048 #endif /* defined(UNIQUE_HISTORY) */
1049 if (H
.Size
&& strcmp(p
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1051 hist_add((CHAR
*)p
);
1069 return delete_string(Repeat
== NO_ARG
? 1 : Repeat
);
1082 STATIC
char SEPS
[] = "\"#$&'()*:;<=>?[\\]^`{|}~\n\t ";
1085 ** Move back to the beginning of the current word and return an
1086 ** allocated copy of it.
1098 if (p
> Line
&& p
[-1] == '\\') {
1101 if (strchr(SEPS
, (char) *p
) != NULL
) {
1107 len
= Point
- (p
- Line
) + 1;
1108 if ((new = NEW(CHAR
, len
)) == NULL
)
1111 while (p
< &Line
[Point
]) {
1113 if (++p
== &Line
[Point
]) break;
1129 ac
= rl_list_possib((char *)word
, (char ***)&av
);
1152 p
= (CHAR
*)rl_complete((char *)word
, &unique
);
1156 len
= strlen((char *)p
);
1158 new = q
= NEW(CHAR
, 2 * len
+ 1);
1160 if ((*p
< ' ' || strchr(SEPS
, (char) *p
) != NULL
)
1161 && (!unique
|| p
[1] != 0)) {
1169 s
= insert_string(new);
1176 if (len
> 0) return s
;
1178 return c_possible();
1196 c
= Line
[Point
- 1];
1198 Line
[Point
- 1] = Line
[Point
];
1199 TTYshow(Line
[Point
- 1]);
1211 return (c
= TTYget()) == EOF
? CSeof
: insert_char((int)c
);
1229 return delete_string(Mark
- Point
);
1244 if ((c
= TTYget()) != CTL('X'))
1245 return c
== EOF
? CSeof
: ring_bell();
1247 if ((c
= Mark
) <= End
) {
1258 if (Yanked
&& *Yanked
)
1259 return insert_string(Yanked
);
1270 save_yank(Mark
, Point
- Mark
);
1272 save_yank(Point
, Mark
- Point
);
1284 if ((c
= TTYget()) == EOF
)
1286 for (i
= Point
+ 1, p
= &Line
[i
]; i
< End
; i
++, p
++)
1297 return do_forward(CSmove
);
1305 (void)do_forward(CSstay
);
1306 if (OldPoint
!= Point
) {
1307 i
= Point
- OldPoint
;
1309 return delete_string(i
);
1322 for (p
= &Line
[Point
]; p
> Line
&& !isalnum(p
[-1]); p
--)
1325 for (; p
> Line
&& p
[-1] != ' ' && isalnum(p
[-1]); p
--)
1330 } while (++i
< Repeat
);
1339 if (OldPoint
!= Point
)
1340 return delete_string(OldPoint
- Point
);
1356 if ((*avp
= p
= NEW(CHAR
*, i
))== NULL
)
1359 for (c
= line
; isspace(*c
); c
++)
1361 if (*c
== '\n' || *c
== '\0')
1364 for (ac
= 0, p
[ac
++] = c
; *c
&& *c
!= '\n'; ) {
1367 if (*c
&& *c
!= '\n') {
1369 new = NEW(CHAR
*, i
+ MEM_INC
);
1374 COPYFROMTO(new, p
, i
* sizeof (char **));
1398 if (H
.Size
== 1 || (p
= H
.Lines
[H
.Size
- 2]) == NULL
)
1401 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
1403 ac
= argify(p
, &av
);
1405 if (Repeat
!= NO_ARG
)
1406 s
= Repeat
< ac
? insert_string(av
[Repeat
]) : ring_bell();
1408 s
= ac
? insert_string(av
[ac
- 1]) : CSstay
;
1416 STATIC KEYMAP Map
[33] = {
1417 { CTL('@'), mk_set
},
1418 { CTL('A'), beg_line
},
1419 { CTL('B'), bk_char
},
1420 { CTL('D'), del_char
},
1421 { CTL('E'), end_line
},
1422 { CTL('F'), fd_char
},
1423 { CTL('G'), ring_bell
},
1424 { CTL('H'), bk_del_char
},
1425 { CTL('I'), c_complete
},
1426 { CTL('J'), accept_line
},
1427 { CTL('K'), kill_line
},
1428 { CTL('L'), redisplay
},
1429 { CTL('M'), accept_line
},
1430 { CTL('N'), h_next
},
1431 { CTL('O'), ring_bell
},
1432 { CTL('P'), h_prev
},
1433 { CTL('Q'), ring_bell
},
1434 { CTL('R'), h_search
},
1435 { CTL('S'), ring_bell
},
1436 { CTL('T'), transpose
},
1437 { CTL('U'), ring_bell
},
1438 { CTL('V'), quote
},
1439 { CTL('W'), bk_kill_word
},
1440 { CTL('X'), exchange
},
1442 { CTL('Z'), end_line
},
1444 { CTL(']'), move_to_char
},
1445 { CTL('^'), ring_bell
},
1446 { CTL('_'), ring_bell
},
1450 STATIC KEYMAP MetaMap
[17]= {
1454 { '.', last_argument
},
1457 { '?', c_possible
},
1459 { 'd', fd_kill_word
},
1461 { 'l', case_down_word
},
1462 { 'm', toggle_meta_mode
},
1463 { 'u', case_up_word
},
1465 { 'w', copy_region
},
1470 * $PchId: editline.c,v 1.4 1996/02/22 21:16:56 philip Exp $