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 *tgetstr();
103 extern int tgetent();
104 #endif /* defined(USE_TERMCAP) */
107 ** TTY input/output functions.
114 (void)write(1, Screen
, ScreenCount
);
123 Screen
[ScreenCount
] = c
;
124 if (++ScreenCount
>= ScreenSize
- 1) {
125 ScreenSize
+= SCREEN_INC
;
126 RENEW(Screen
, char, ScreenSize
);
150 else if (rl_meta_chars
&& ISMETA(c
)) {
182 r
= read(0, &c
, (SIZE_T
)1);
183 } while (r
== -1 && errno
== EINTR
);
184 return r
== 1 ? c
: EOF
;
187 #define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
201 #if defined(USE_TERMCAP)
205 #endif /* defined(USE_TERMCAP) */
206 #if defined(TIOCGWINSZ)
208 #endif /* defined(TIOCGWINSZ) */
211 #if defined(TIOCGWINSZ)
212 /* Perhaps we got resized. */
213 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0
214 && W
.ws_col
> 0 && W
.ws_row
> 0) {
215 TTYwidth
= (int)W
.ws_col
;
216 TTYrows
= (int)W
.ws_row
;
218 #endif /* defined(TIOCGWINSZ) */
223 TTYwidth
= TTYrows
= 0;
224 #if defined(USE_TERMCAP)
226 if ((term
= getenv("TERM")) == NULL
)
228 if (tgetent(buff
, term
) < 0) {
229 TTYwidth
= SCREEN_WIDTH
;
230 TTYrows
= SCREEN_ROWS
;
233 if ((backspace
= tgetstr("le", &bp
)) != NULL
)
234 backspace
= strdup(backspace
);
235 TTYwidth
= tgetnum("co");
236 TTYrows
= tgetnum("li");
237 #endif /* defined(USE_TERMCAP) */
239 #if defined(TIOCGWINSZ)
240 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0) {
241 TTYwidth
= (int)W
.ws_col
;
242 TTYrows
= (int)W
.ws_row
;
244 #endif /* defined(TIOCGWINSZ) */
246 if (TTYwidth
<= 0 || TTYrows
<= 0) {
247 TTYwidth
= SCREEN_WIDTH
;
248 TTYrows
= SCREEN_ROWS
;
254 ** Print an array of words in columns.
270 /* Find longest name, determine column count from that. */
271 for (longest
= 0, i
= 0; i
< ac
; i
++)
272 if ((j
= strlen((char *)av
[i
])) > longest
)
274 cols
= TTYwidth
/ (longest
+ 3);
276 TTYputs((CHAR
*)NEWLINE
);
277 for (skip
= ac
/ cols
+ 1, i
= 0; i
< skip
; i
++) {
278 for (j
= i
; j
< ac
; j
+= skip
) {
279 for (p
= av
[j
], len
= strlen((char *)p
), k
= len
; --k
>= 0; p
++)
282 while (++len
< longest
+ 3)
285 TTYputs((CHAR
*)NEWLINE
);
296 TTYputs((CONST CHAR
*)Prompt
);
297 for (i
= Point
, p
= Line
; --i
>= 0; p
++)
307 if (ISCTL(Line
[Point
- 1]))
309 else if (rl_meta_chars
&& ISMETA(Line
[Point
- 1])) {
314 if (Change
== CSmove
)
322 TTYshow(Line
[Point
]);
323 if (Change
== CSmove
)
346 if ((Input
= (CHAR
*)getenv((char *)name
)) == NULL
) {
363 for ( ; Point
< End
&& (*p
== ' ' || !isalnum(*p
)); Point
++, p
++)
367 for (; Point
< End
&& isalnum(*p
); Point
++, p
++)
373 } while (++i
< Repeat
);
387 (void)do_forward(CSstay
);
388 if (OldPoint
!= Point
) {
389 if ((count
= Point
- OldPoint
) < 0)
392 if ((end
= Point
+ count
) > End
)
394 for (i
= Point
, p
= &Line
[i
]; i
< end
; i
++, p
++) {
395 if (type
== TOupper
) {
399 else if (isupper(*p
))
410 return do_case(TOlower
);
416 return do_case(TOupper
);
426 for (extras
= 0, i
= Point
, p
= &Line
[i
]; i
<= End
; i
++, p
++) {
432 else if (rl_meta_chars
&& ISMETA(*p
)) {
439 for (i
+= extras
; i
> Point
; i
--)
446 Point
= -strlen(Prompt
);
463 len
= strlen((char *)p
);
464 if (End
+ len
>= Length
) {
465 if ((new = NEW(CHAR
, Length
+ len
+ MEM_INC
)) == NULL
)
468 COPYFROMTO(new, Line
, Length
);
472 Length
+= len
+ MEM_INC
;
475 for (q
= &Line
[Point
], i
= End
- Point
; --i
>= 0; )
477 COPYFROMTO(&Line
[Point
], p
, len
);
480 TTYstring(&Line
[Point
]);
483 return Point
== End
? CSstay
: CSmove
;
489 TTYputs((CONST CHAR
*)NEWLINE
);
490 TTYputs((CONST CHAR
*)Prompt
);
498 rl_meta_chars
= ! rl_meta_chars
;
506 return H
.Pos
>= H
.Size
- 1 ? NULL
: H
.Lines
[++H
.Pos
];
512 return H
.Pos
== 0 ? NULL
: H
.Lines
[--H
.Pos
];
525 return insert_string(p
);
537 if ((p
= (*move
)()) == NULL
)
539 } while (++i
< Repeat
);
540 return do_insert_hist(p
);
546 return do_hist(next_hist
);
552 return do_hist(prev_hist
);
558 return do_insert_hist(H
.Lines
[H
.Pos
= 0]);
564 return do_insert_hist(H
.Lines
[H
.Pos
= H
.Size
- 1]);
568 ** Return zero if pat appears as a substring in text.
571 substrcmp(text
, pat
, len
)
578 if ((c
= *pat
) == '\0')
579 return *text
== '\0';
580 for ( ; *text
; text
++)
581 if (*text
== c
&& strncmp(text
, pat
, len
) == 0)
587 search_hist(search
, move
)
591 static CHAR
*old_search
;
597 /* Save or get remembered search pattern. */
598 if (search
&& *search
) {
601 old_search
= (CHAR
*)strdup((char *)search
);
604 if (old_search
== NULL
|| *old_search
== '\0')
609 /* Set up pattern-finder. */
610 if (*search
== '^') {
612 pat
= (char *)(search
+ 1);
616 pat
= (char *)search
;
620 for (pos
= H
.Pos
; (*move
)() != NULL
; )
621 if ((*match
)((char *)H
.Lines
[H
.Pos
], pat
, len
) == 0)
622 return H
.Lines
[H
.Pos
];
630 static int Searching
;
631 CONST
char *old_prompt
;
642 TTYputs((CONST CHAR
*)Prompt
);
643 move
= Repeat
== NO_ARG
? prev_hist
: next_hist
;
647 TTYputs((CONST CHAR
*)Prompt
);
648 if (p
== NULL
&& Signal
> 0) {
653 p
= search_hist(p
, move
);
659 return do_insert_hist(p
);
672 } while (++i
< Repeat
);
689 if ((Yanked
= NEW(CHAR
, (SIZE_T
)i
+ 1)) != NULL
) {
690 COPYFROMTO(Yanked
, &Line
[begin
], i
);
702 if (count
<= 0 || End
== Point
)
705 if (count
== 1 && Point
== End
- 1) {
706 /* Optimize common case of delete at end of line. */
715 else if (rl_meta_chars
&& ISMETA(*p
)) {
724 if (Point
+ count
> End
&& (count
= End
- Point
) <= 0)
728 save_yank(Point
, count
);
730 for (p
= &Line
[Point
], i
= End
- (Point
+ count
) + 1; --i
>= 0; p
++)
734 TTYstring(&Line
[Point
]);
748 } while (++i
< Repeat
);
763 } while (++i
< Repeat
);
765 return delete_string(i
);
773 if (Repeat
!= NO_ARG
) {
774 if (Repeat
< Point
) {
778 (void)delete_string(i
- Point
);
780 else if (Repeat
> Point
) {
782 (void)delete_string(Repeat
- Point
- 1);
787 save_yank(Point
, End
- Point
);
804 if (Repeat
== NO_ARG
|| Repeat
< 2) {
807 return insert_string(buff
);
810 if ((p
= NEW(CHAR
, Repeat
+ 1)) == NULL
)
812 for (i
= Repeat
, q
= p
; --i
>= 0; )
816 s
= insert_string(p
);
827 if ((c
= TTYget()) == EOF
)
829 #if defined(ANSI_ARROWS)
830 /* Also include VT-100 arrows. */
831 if (c
== '[' || c
== 'O')
832 switch (c
= TTYget()) {
833 default: return ring_bell();
834 case EOF
: return CSeof
;
835 case 'A': return h_prev();
836 case 'B': return h_next();
837 case 'C': return fd_char();
838 case 'D': return bk_char();
840 #endif /* defined(ANSI_ARROWS) */
843 for (Repeat
= c
- '0'; (c
= TTYget()) != EOF
&& isdigit(c
); )
844 Repeat
= Repeat
* 10 + c
- '0';
852 for (kp
= MetaMap
; kp
->Function
; kp
++)
854 return (*kp
->Function
)();
867 if (rl_meta_chars
&& ISMETA(c
)) {
869 PushBack
= UNMETA(c
);
872 for (kp
= Map
; kp
->Function
; kp
++)
875 s
= kp
->Function
? (*kp
->Function
)() : insert_char((int)c
);
877 /* No pushback means no repeat count; hacky, but true. */
889 if (c
== rl_erase
|| c
== DEL
)
890 return bk_del_char();
899 if (c
== rl_eof
&& Point
== 0 && End
== 0)
919 OldPoint
= Point
= Mark
= End
= 0;
923 while ((c
= TTYget()) != EOF
)
924 switch (TTYspecial(c
)) {
962 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
964 if (H
.Size
< HIST_SIZE
)
965 H
.Lines
[H
.Size
++] = p
;
968 for (i
= 0; i
< HIST_SIZE
- 1; i
++)
969 H
.Lines
[i
] = H
.Lines
[i
+ 1];
976 ** For compatibility with FSF readline.
999 if ((Line
= NEW(CHAR
, Length
)) == NULL
)
1006 ScreenSize
= SCREEN_INC
;
1007 Screen
= NEW(char, ScreenSize
);
1008 Prompt
= prompt
? prompt
: (char *)NIL
;
1009 TTYputs((CONST CHAR
*)Prompt
);
1010 if ((line
= editinput()) != NULL
) {
1011 line
= (CHAR
*)strdup((char *)line
);
1012 TTYputs((CHAR
*)NEWLINE
);
1017 DISPOSE(H
.Lines
[--H
.Size
]);
1019 if (line
!= NULL
&& *line
!= '\0'
1020 #if defined(UNIQUE_HISTORY)
1021 && !(H
.Pos
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1022 #endif /* defined(UNIQUE_HISTORY) */
1023 && !(H
.Size
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1031 (void)kill(getpid(), s
);
1033 return (char *)line
;
1040 #ifdef obsolete /* Made part of readline(). -- kjb */
1041 if (p
== NULL
|| *p
== '\0')
1044 #if defined(UNIQUE_HISTORY)
1045 if (H
.Pos
&& strcmp(p
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1047 #endif /* defined(UNIQUE_HISTORY) */
1048 if (H
.Size
&& strcmp(p
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1050 hist_add((CHAR
*)p
);
1068 return delete_string(Repeat
== NO_ARG
? 1 : Repeat
);
1081 STATIC
char SEPS
[] = "\"#$&'()*:;<=>?[\\]^`{|}~\n\t ";
1084 ** Move back to the beginning of the current word and return an
1085 ** allocated copy of it.
1097 if (p
> Line
&& p
[-1] == '\\') {
1100 if (strchr(SEPS
, (char) *p
) != NULL
) {
1106 len
= Point
- (p
- Line
) + 1;
1107 if ((new = NEW(CHAR
, len
)) == NULL
)
1110 while (p
< &Line
[Point
]) {
1112 if (++p
== &Line
[Point
]) break;
1128 ac
= rl_list_possib((char *)word
, (char ***)&av
);
1151 p
= (CHAR
*)rl_complete((char *)word
, &unique
);
1155 len
= strlen((char *)p
);
1157 new = q
= NEW(CHAR
, 2 * len
+ 1);
1159 if ((*p
< ' ' || strchr(SEPS
, (char) *p
) != NULL
)
1160 && (!unique
|| p
[1] != 0)) {
1168 s
= insert_string(new);
1175 if (len
> 0) return s
;
1177 return c_possible();
1195 c
= Line
[Point
- 1];
1197 Line
[Point
- 1] = Line
[Point
];
1198 TTYshow(Line
[Point
- 1]);
1210 return (c
= TTYget()) == EOF
? CSeof
: insert_char((int)c
);
1228 return delete_string(Mark
- Point
);
1243 if ((c
= TTYget()) != CTL('X'))
1244 return c
== EOF
? CSeof
: ring_bell();
1246 if ((c
= Mark
) <= End
) {
1257 if (Yanked
&& *Yanked
)
1258 return insert_string(Yanked
);
1269 save_yank(Mark
, Point
- Mark
);
1271 save_yank(Point
, Mark
- Point
);
1283 if ((c
= TTYget()) == EOF
)
1285 for (i
= Point
+ 1, p
= &Line
[i
]; i
< End
; i
++, p
++)
1296 return do_forward(CSmove
);
1304 (void)do_forward(CSstay
);
1305 if (OldPoint
!= Point
) {
1306 i
= Point
- OldPoint
;
1308 return delete_string(i
);
1321 for (p
= &Line
[Point
]; p
> Line
&& !isalnum(p
[-1]); p
--)
1324 for (; p
> Line
&& p
[-1] != ' ' && isalnum(p
[-1]); p
--)
1329 } while (++i
< Repeat
);
1338 if (OldPoint
!= Point
)
1339 return delete_string(OldPoint
- Point
);
1355 if ((*avp
= p
= NEW(CHAR
*, i
))== NULL
)
1358 for (c
= line
; isspace(*c
); c
++)
1360 if (*c
== '\n' || *c
== '\0')
1363 for (ac
= 0, p
[ac
++] = c
; *c
&& *c
!= '\n'; ) {
1366 if (*c
&& *c
!= '\n') {
1368 new = NEW(CHAR
*, i
+ MEM_INC
);
1373 COPYFROMTO(new, p
, i
* sizeof (char **));
1397 if (H
.Size
== 1 || (p
= H
.Lines
[H
.Size
- 2]) == NULL
)
1400 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
1402 ac
= argify(p
, &av
);
1404 if (Repeat
!= NO_ARG
)
1405 s
= Repeat
< ac
? insert_string(av
[Repeat
]) : ring_bell();
1407 s
= ac
? insert_string(av
[ac
- 1]) : CSstay
;
1415 STATIC KEYMAP Map
[33] = {
1416 { CTL('@'), mk_set
},
1417 { CTL('A'), beg_line
},
1418 { CTL('B'), bk_char
},
1419 { CTL('D'), del_char
},
1420 { CTL('E'), end_line
},
1421 { CTL('F'), fd_char
},
1422 { CTL('G'), ring_bell
},
1423 { CTL('H'), bk_del_char
},
1424 { CTL('I'), c_complete
},
1425 { CTL('J'), accept_line
},
1426 { CTL('K'), kill_line
},
1427 { CTL('L'), redisplay
},
1428 { CTL('M'), accept_line
},
1429 { CTL('N'), h_next
},
1430 { CTL('O'), ring_bell
},
1431 { CTL('P'), h_prev
},
1432 { CTL('Q'), ring_bell
},
1433 { CTL('R'), h_search
},
1434 { CTL('S'), ring_bell
},
1435 { CTL('T'), transpose
},
1436 { CTL('U'), ring_bell
},
1437 { CTL('V'), quote
},
1438 { CTL('W'), bk_kill_word
},
1439 { CTL('X'), exchange
},
1441 { CTL('Z'), end_line
},
1443 { CTL(']'), move_to_char
},
1444 { CTL('^'), ring_bell
},
1445 { CTL('_'), ring_bell
},
1449 STATIC KEYMAP MetaMap
[17]= {
1453 { '.', last_argument
},
1456 { '?', c_possible
},
1458 { 'd', fd_kill_word
},
1460 { 'l', case_down_word
},
1461 { 'm', toggle_meta_mode
},
1462 { 'u', case_up_word
},
1464 { 'w', copy_region
},
1469 * $PchId: editline.c,v 1.4 1996/02/22 21:16:56 philip Exp $