2 ** Main editing routines for editline library.
12 ** Manifest constants.
14 #define SCREEN_WIDTH 80
15 #define SCREEN_ROWS 24
18 #define CTL(x) ((x) & 0x1F)
19 #define ISCTL(x) ((x) && (x) < ' ')
20 #define UNCTL(x) ((x) + 64)
21 #define META(x) ((x) | 0x80)
22 #define ISMETA(x) ((x) & 0x80)
23 #define UNMETA(x) ((x) & 0x7F)
24 #if !defined(HIST_SIZE)
26 #endif /* !defined(HIST_SIZE) */
29 ** Command status codes.
31 typedef enum _STATUS
{
32 CSdone
, CSeof
, CSmove
, CSdispatch
, CSstay
, CSsignal
36 ** The type of case-changing to perform.
43 ** Key to command mapping.
45 typedef struct _KEYMAP
{
51 ** Command history structure.
53 typedef struct _HISTORY
{
56 CHAR
*Lines
[HIST_SIZE
];
68 STATIC CHAR NIL
[] = "";
69 STATIC CONST CHAR
*Input
= NIL
;
71 STATIC CONST
char *Prompt
;
74 STATIC
char NEWLINE
[]= CRLF
;
84 static KEYMAP Map
[33];
85 static KEYMAP MetaMap
[17];
87 STATIC SIZE_T ScreenCount
;
88 STATIC SIZE_T ScreenSize
;
89 STATIC
char *backspace
;
93 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
94 int rl_meta_chars
= 0;
99 STATIC CHAR
*editinput();
100 #if defined(USE_TERMCAP)
101 extern char *tgetstr();
102 extern int tgetent();
103 #endif /* defined(USE_TERMCAP) */
106 ** TTY input/output functions.
113 (void)write(1, Screen
, ScreenCount
);
122 Screen
[ScreenCount
] = c
;
123 if (++ScreenCount
>= ScreenSize
- 1) {
124 ScreenSize
+= SCREEN_INC
;
125 RENEW(Screen
, char, ScreenSize
);
149 else if (rl_meta_chars
&& ISMETA(c
)) {
181 r
= read(0, &c
, (SIZE_T
)1);
182 } while (r
== -1 && errno
== EINTR
);
183 return r
== 1 ? c
: EOF
;
186 #define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b'))
200 #if defined(USE_TERMCAP)
204 #endif /* defined(USE_TERMCAP) */
205 #if defined(TIOCGWINSZ)
207 #endif /* defined(TIOCGWINSZ) */
210 #if defined(TIOCGWINSZ)
211 /* Perhaps we got resized. */
212 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0
213 && W
.ws_col
> 0 && W
.ws_row
> 0) {
214 TTYwidth
= (int)W
.ws_col
;
215 TTYrows
= (int)W
.ws_row
;
217 #endif /* defined(TIOCGWINSZ) */
222 TTYwidth
= TTYrows
= 0;
223 #if defined(USE_TERMCAP)
225 if ((term
= getenv("TERM")) == NULL
)
227 if (tgetent(buff
, term
) < 0) {
228 TTYwidth
= SCREEN_WIDTH
;
229 TTYrows
= SCREEN_ROWS
;
232 if ((backspace
= tgetstr("le", &bp
)) != NULL
)
233 backspace
= strdup(backspace
);
234 TTYwidth
= tgetnum("co");
235 TTYrows
= tgetnum("li");
236 #endif /* defined(USE_TERMCAP) */
238 #if defined(TIOCGWINSZ)
239 if (ioctl(0, TIOCGWINSZ
, &W
) >= 0) {
240 TTYwidth
= (int)W
.ws_col
;
241 TTYrows
= (int)W
.ws_row
;
243 #endif /* defined(TIOCGWINSZ) */
245 if (TTYwidth
<= 0 || TTYrows
<= 0) {
246 TTYwidth
= SCREEN_WIDTH
;
247 TTYrows
= SCREEN_ROWS
;
253 ** Print an array of words in columns.
269 /* Find longest name, determine column count from that. */
270 for (longest
= 0, i
= 0; i
< ac
; i
++)
271 if ((j
= strlen((char *)av
[i
])) > longest
)
273 cols
= TTYwidth
/ (longest
+ 3);
275 TTYputs((CHAR
*)NEWLINE
);
276 for (skip
= ac
/ cols
+ 1, i
= 0; i
< skip
; i
++) {
277 for (j
= i
; j
< ac
; j
+= skip
) {
278 for (p
= av
[j
], len
= strlen((char *)p
), k
= len
; --k
>= 0; p
++)
281 while (++len
< longest
+ 3)
284 TTYputs((CHAR
*)NEWLINE
);
295 TTYputs((CHAR
*)Prompt
);
296 for (i
= Point
, p
= Line
; --i
>= 0; p
++)
306 if (ISCTL(Line
[Point
- 1]))
308 else if (rl_meta_chars
&& ISMETA(Line
[Point
- 1])) {
313 if (Change
== CSmove
)
321 TTYshow(Line
[Point
]);
322 if (Change
== CSmove
)
345 if ((Input
= (CHAR
*)getenv((char *)name
)) == NULL
) {
362 for ( ; Point
< End
&& (*p
== ' ' || !isalnum(*p
)); Point
++, p
++)
366 for (; Point
< End
&& isalnum(*p
); Point
++, p
++)
372 } while (++i
< Repeat
);
386 (void)do_forward(CSstay
);
387 if (OldPoint
!= Point
) {
388 if ((count
= Point
- OldPoint
) < 0)
391 if ((end
= Point
+ count
) > End
)
393 for (i
= Point
, p
= &Line
[i
]; i
< end
; i
++, p
++) {
394 if (type
== TOupper
) {
398 else if (isupper(*p
))
409 return do_case(TOlower
);
415 return do_case(TOupper
);
425 for (extras
= 0, i
= Point
, p
= &Line
[i
]; i
<= End
; i
++, p
++) {
431 else if (rl_meta_chars
&& ISMETA(*p
)) {
438 for (i
+= extras
; i
> Point
; i
--)
445 Point
= -strlen(Prompt
);
462 len
= strlen((char *)p
);
463 if (End
+ len
>= Length
) {
464 if ((new = NEW(CHAR
, Length
+ len
+ MEM_INC
)) == NULL
)
467 COPYFROMTO(new, Line
, Length
);
471 Length
+= len
+ MEM_INC
;
474 for (q
= &Line
[Point
], i
= End
- Point
; --i
>= 0; )
476 COPYFROMTO(&Line
[Point
], p
, len
);
479 TTYstring(&Line
[Point
]);
482 return Point
== End
? CSstay
: CSmove
;
488 TTYputs((CHAR
*) NEWLINE
);
489 TTYputs((CHAR
*)Prompt
);
497 rl_meta_chars
= ! rl_meta_chars
;
505 return H
.Pos
>= H
.Size
- 1 ? NULL
: H
.Lines
[++H
.Pos
];
511 return H
.Pos
== 0 ? NULL
: H
.Lines
[--H
.Pos
];
524 return insert_string(p
);
536 if ((p
= (*move
)()) == NULL
)
538 } while (++i
< Repeat
);
539 return do_insert_hist(p
);
545 return do_hist(next_hist
);
551 return do_hist(prev_hist
);
557 return do_insert_hist(H
.Lines
[H
.Pos
= 0]);
563 return do_insert_hist(H
.Lines
[H
.Pos
= H
.Size
- 1]);
567 ** Return zero if pat appears as a substring in text.
570 substrcmp(text
, pat
, len
)
577 if ((c
= *pat
) == '\0')
578 return *text
== '\0';
579 for ( ; *text
; text
++)
580 if (*text
== c
&& strncmp(text
, pat
, len
) == 0)
586 search_hist(search
, move
)
590 static CHAR
*old_search
;
596 /* Save or get remembered search pattern. */
597 if (search
&& *search
) {
600 old_search
= (CHAR
*)strdup((char *)search
);
603 if (old_search
== NULL
|| *old_search
== '\0')
608 /* Set up pattern-finder. */
609 if (*search
== '^') {
611 pat
= (char *)(search
+ 1);
615 pat
= (char *)search
;
619 for (pos
= H
.Pos
; (*move
)() != NULL
; )
620 if ((*match
)((char *)H
.Lines
[H
.Pos
], pat
, len
) == 0)
621 return H
.Lines
[H
.Pos
];
629 static int Searching
;
630 CONST
char *old_prompt
;
641 TTYputs((CHAR
*)Prompt
);
642 move
= Repeat
== NO_ARG
? prev_hist
: next_hist
;
646 TTYputs((CHAR
*)Prompt
);
647 if (p
== NULL
&& Signal
> 0) {
652 p
= search_hist(p
, move
);
658 return do_insert_hist(p
);
671 } while (++i
< Repeat
);
688 if ((Yanked
= NEW(CHAR
, (SIZE_T
)i
+ 1)) != NULL
) {
689 COPYFROMTO(Yanked
, &Line
[begin
], i
);
701 if (count
<= 0 || End
== Point
)
704 if (count
== 1 && Point
== End
- 1) {
705 /* Optimize common case of delete at end of line. */
714 else if (rl_meta_chars
&& ISMETA(*p
)) {
723 if (Point
+ count
> End
&& (count
= End
- Point
) <= 0)
727 save_yank(Point
, count
);
729 for (p
= &Line
[Point
], i
= End
- (Point
+ count
) + 1; --i
>= 0; p
++)
733 TTYstring(&Line
[Point
]);
747 } while (++i
< Repeat
);
762 } while (++i
< Repeat
);
764 return delete_string(i
);
772 if (Repeat
!= NO_ARG
) {
773 if (Repeat
< Point
) {
777 (void)delete_string(i
- Point
);
779 else if (Repeat
> Point
) {
781 (void)delete_string(Repeat
- Point
- 1);
786 save_yank(Point
, End
- Point
);
803 if (Repeat
== NO_ARG
|| Repeat
< 2) {
806 return insert_string(buff
);
809 if ((p
= NEW(CHAR
, Repeat
+ 1)) == NULL
)
811 for (i
= Repeat
, q
= p
; --i
>= 0; )
815 s
= insert_string(p
);
826 if ((c
= TTYget()) == EOF
)
828 #if defined(ANSI_ARROWS)
829 /* Also include VT-100 arrows. */
830 if (c
== '[' || c
== 'O')
831 switch (c
= TTYget()) {
832 default: return ring_bell();
833 case EOF
: return CSeof
;
834 case 'A': return h_prev();
835 case 'B': return h_next();
836 case 'C': return fd_char();
837 case 'D': return bk_char();
839 #endif /* defined(ANSI_ARROWS) */
842 for (Repeat
= c
- '0'; (c
= TTYget()) != EOF
&& isdigit(c
); )
843 Repeat
= Repeat
* 10 + c
- '0';
851 for (kp
= MetaMap
; kp
->Function
; kp
++)
853 return (*kp
->Function
)();
866 if (rl_meta_chars
&& ISMETA(c
)) {
868 PushBack
= UNMETA(c
);
871 for (kp
= Map
; kp
->Function
; kp
++)
874 s
= kp
->Function
? (*kp
->Function
)() : insert_char((int)c
);
876 /* No pushback means no repeat count; hacky, but true. */
888 if (c
== rl_erase
|| c
== DEL
)
889 return bk_del_char();
898 if (c
== rl_eof
&& Point
== 0 && End
== 0)
918 OldPoint
= Point
= Mark
= End
= 0;
922 while ((c
= TTYget()) != EOF
)
923 switch (TTYspecial(c
)) {
961 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
963 if (H
.Size
< HIST_SIZE
)
964 H
.Lines
[H
.Size
++] = p
;
967 for (i
= 0; i
< HIST_SIZE
- 1; i
++)
968 H
.Lines
[i
] = H
.Lines
[i
+ 1];
975 ** For compatibility with FSF readline.
998 if ((Line
= NEW(CHAR
, Length
)) == NULL
)
1005 ScreenSize
= SCREEN_INC
;
1006 Screen
= NEW(char, ScreenSize
);
1007 Prompt
= prompt
? prompt
: (char *)NIL
;
1008 TTYputs((CHAR
*)Prompt
);
1009 if ((line
= editinput()) != NULL
) {
1010 line
= (CHAR
*)strdup((char *)line
);
1011 TTYputs((CHAR
*)NEWLINE
);
1016 DISPOSE(H
.Lines
[--H
.Size
]);
1018 if (line
!= NULL
&& *line
!= '\0'
1019 #if defined(UNIQUE_HISTORY)
1020 && !(H
.Pos
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1021 #endif /* defined(UNIQUE_HISTORY) */
1022 && !(H
.Size
&& strcmp((char *) line
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1030 (void)kill(getpid(), s
);
1032 return (char *)line
;
1039 #ifdef obsolete /* Made part of readline(). -- kjb */
1040 if (p
== NULL
|| *p
== '\0')
1043 #if defined(UNIQUE_HISTORY)
1044 if (H
.Pos
&& strcmp(p
, (char *) H
.Lines
[H
.Pos
- 1]) == 0)
1046 #endif /* defined(UNIQUE_HISTORY) */
1047 if (H
.Size
&& strcmp(p
, (char *) H
.Lines
[H
.Size
- 1]) == 0)
1049 hist_add((CHAR
*)p
);
1067 return delete_string(Repeat
== NO_ARG
? 1 : Repeat
);
1080 STATIC
char SEPS
[] = "\"#$&'()*:;<=>?[\\]^`{|}~\n\t ";
1083 ** Move back to the beginning of the current word and return an
1084 ** allocated copy of it.
1096 if (p
> Line
&& p
[-1] == '\\') {
1099 if (strchr(SEPS
, (char) *p
) != NULL
) {
1105 len
= Point
- (p
- Line
) + 1;
1106 if ((new = NEW(CHAR
, len
)) == NULL
)
1109 while (p
< &Line
[Point
]) {
1111 if (++p
== &Line
[Point
]) break;
1127 ac
= rl_list_possib((char *)word
, (char ***)&av
);
1150 p
= (CHAR
*)rl_complete((char *)word
, &unique
);
1154 len
= strlen((char *)p
);
1156 new = q
= NEW(CHAR
, 2 * len
+ 1);
1158 if ((*p
< ' ' || strchr(SEPS
, (char) *p
) != NULL
)
1159 && (!unique
|| p
[1] != 0)) {
1167 s
= insert_string(new);
1174 if (len
> 0) return s
;
1176 return c_possible();
1194 c
= Line
[Point
- 1];
1196 Line
[Point
- 1] = Line
[Point
];
1197 TTYshow(Line
[Point
- 1]);
1209 return (c
= TTYget()) == EOF
? CSeof
: insert_char((int)c
);
1227 return delete_string(Mark
- Point
);
1242 if ((c
= TTYget()) != CTL('X'))
1243 return c
== EOF
? CSeof
: ring_bell();
1245 if ((c
= Mark
) <= End
) {
1256 if (Yanked
&& *Yanked
)
1257 return insert_string(Yanked
);
1268 save_yank(Mark
, Point
- Mark
);
1270 save_yank(Point
, Mark
- Point
);
1282 if ((c
= TTYget()) == EOF
)
1284 for (i
= Point
+ 1, p
= &Line
[i
]; i
< End
; i
++, p
++)
1295 return do_forward(CSmove
);
1303 (void)do_forward(CSstay
);
1304 if (OldPoint
!= Point
) {
1305 i
= Point
- OldPoint
;
1307 return delete_string(i
);
1320 for (p
= &Line
[Point
]; p
> Line
&& !isalnum(p
[-1]); p
--)
1323 for (; p
> Line
&& p
[-1] != ' ' && isalnum(p
[-1]); p
--)
1328 } while (++i
< Repeat
);
1337 if (OldPoint
!= Point
)
1338 return delete_string(OldPoint
- Point
);
1354 if ((*avp
= p
= NEW(CHAR
*, i
))== NULL
)
1357 for (c
= line
; isspace(*c
); c
++)
1359 if (*c
== '\n' || *c
== '\0')
1362 for (ac
= 0, p
[ac
++] = c
; *c
&& *c
!= '\n'; ) {
1365 if (*c
&& *c
!= '\n') {
1367 new = NEW(CHAR
*, i
+ MEM_INC
);
1372 COPYFROMTO(new, p
, i
* sizeof (char **));
1396 if (H
.Size
== 1 || (p
= H
.Lines
[H
.Size
- 2]) == NULL
)
1399 if ((p
= (CHAR
*)strdup((char *)p
)) == NULL
)
1401 ac
= argify(p
, &av
);
1403 if (Repeat
!= NO_ARG
)
1404 s
= Repeat
< ac
? insert_string(av
[Repeat
]) : ring_bell();
1406 s
= ac
? insert_string(av
[ac
- 1]) : CSstay
;
1414 STATIC KEYMAP Map
[33] = {
1415 { CTL('@'), mk_set
},
1416 { CTL('A'), beg_line
},
1417 { CTL('B'), bk_char
},
1418 { CTL('D'), del_char
},
1419 { CTL('E'), end_line
},
1420 { CTL('F'), fd_char
},
1421 { CTL('G'), ring_bell
},
1422 { CTL('H'), bk_del_char
},
1423 { CTL('I'), c_complete
},
1424 { CTL('J'), accept_line
},
1425 { CTL('K'), kill_line
},
1426 { CTL('L'), redisplay
},
1427 { CTL('M'), accept_line
},
1428 { CTL('N'), h_next
},
1429 { CTL('O'), ring_bell
},
1430 { CTL('P'), h_prev
},
1431 { CTL('Q'), ring_bell
},
1432 { CTL('R'), h_search
},
1433 { CTL('S'), ring_bell
},
1434 { CTL('T'), transpose
},
1435 { CTL('U'), ring_bell
},
1436 { CTL('V'), quote
},
1437 { CTL('W'), bk_kill_word
},
1438 { CTL('X'), exchange
},
1440 { CTL('Z'), end_line
},
1442 { CTL(']'), move_to_char
},
1443 { CTL('^'), ring_bell
},
1444 { CTL('_'), ring_bell
},
1448 STATIC KEYMAP MetaMap
[17]= {
1452 { '.', last_argument
},
1455 { '?', c_possible
},
1457 { 'd', fd_kill_word
},
1459 { 'l', case_down_word
},
1460 { 'm', toggle_meta_mode
},
1461 { 'u', case_up_word
},
1463 { 'w', copy_region
},
1468 * $PchId: editline.c,v 1.4 1996/02/22 21:16:56 philip Exp $