1 /* $OpenBSD: edit.c,v 1.66 2018/06/18 17:03:58 millert Exp $ */
4 * Command line editing - common code
10 #include <sys/ioctl.h>
27 static void x_sigwinch(int);
28 volatile sig_atomic_t got_sigwinch
;
29 static void check_sigwinch(void);
31 static int x_file_glob(int, const char *, int, char ***);
32 static int x_command_glob(int, const char *, int, char ***);
33 static int x_locate_word(const char *, int, int, int *, int *);
36 /* Called from main */
40 /* set to -2 to force initial binding */
41 edchars
.erase
= edchars
.kill
= edchars
.intr
= edchars
.quit
=
43 /* default value for deficient systems */
44 edchars
.werase
= 027; /* ^W */
46 if (setsig(&sigtraps
[SIGWINCH
], x_sigwinch
, SS_RESTORE_ORIG
|SS_SHTRAP
))
47 sigtraps
[SIGWINCH
].flags
|= TF_SHELL_USES
;
48 got_sigwinch
= 1; /* force initial check */
69 if (procpid
== kshpid
&& ioctl(tty_fd
, TIOCGWINSZ
, &ws
) >= 0) {
72 /* Do NOT export COLUMNS/LINES. Many applications
73 * check COLUMNS/LINES before checking ws.ws_col/row,
74 * so if the app is started with C/L in the environ
75 * and the window is then resized, the app won't
76 * see the change cause the environ doesn't change.
79 x_cols
= ws
.ws_col
< MIN_COLS
? MIN_COLS
:
82 if ((vp
= typeset("COLUMNS", 0, 0, 0, 0)))
83 setint(vp
, (int64_t) ws
.ws_col
);
85 if (ws
.ws_row
&& (vp
= typeset("LINES", 0, 0, 0, 0)))
86 setint(vp
, (int64_t) ws
.ws_row
);
92 * read an edited command line
95 x_read(char *buf
, size_t len
)
101 if (Flag(FEMACS
) || Flag(FGMACS
))
102 i
= x_emacs(buf
, len
);
110 i
= -1; /* internal error */
124 while ((n
= blocking_read(STDIN_FILENO
, &c
, 1)) < 0 && errno
== EINTR
)
132 return (int) (unsigned char) c
;
144 return shf_putc(c
, shl_out
);
148 x_puts(const char *s
)
151 shf_putc(*s
++, shl_out
);
157 static bool x_cur_mode
;
160 if (x_cur_mode
== onoff
)
172 edchars
.erase
= cb
.c_cc
[VERASE
];
173 edchars
.kill
= cb
.c_cc
[VKILL
];
174 edchars
.intr
= cb
.c_cc
[VINTR
];
175 edchars
.quit
= cb
.c_cc
[VQUIT
];
176 edchars
.eof
= cb
.c_cc
[VEOF
];
177 edchars
.werase
= cb
.c_cc
[VWERASE
];
178 cb
.c_iflag
&= ~(INLCR
|ICRNL
);
179 cb
.c_lflag
&= ~(ISIG
|ICANON
|ECHO
);
180 /* osf/1 processes lnext when ~icanon */
181 cb
.c_cc
[VLNEXT
] = _POSIX_VDISABLE
;
182 /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
183 cb
.c_cc
[VDISCARD
] = _POSIX_VDISABLE
;
187 tcsetattr(tty_fd
, TCSADRAIN
, &cb
);
189 /* Convert unset values to internal `unset' value */
190 if (edchars
.erase
== _POSIX_VDISABLE
)
192 if (edchars
.kill
== _POSIX_VDISABLE
)
194 if (edchars
.intr
== _POSIX_VDISABLE
)
196 if (edchars
.quit
== _POSIX_VDISABLE
)
198 if (edchars
.eof
== _POSIX_VDISABLE
)
200 if (edchars
.werase
== _POSIX_VDISABLE
)
202 if (memcmp(&edchars
, &oldchars
, sizeof(edchars
)) != 0) {
204 x_emacs_keys(&edchars
);
208 tcsetattr(tty_fd
, TCSADRAIN
, &tty_state
);
215 set_editmode(const char *ed
)
217 static const enum sh_flag edit_flags
[] = {
228 if ((rcp
= strrchr(ed
, '/')))
230 for (ele
= 0; ele
< NELEM(edit_flags
); ele
++)
231 if (strstr(ed
, sh_options
[(int) edit_flags
[ele
]].name
)) {
232 change_flag(edit_flags
[ele
], OF_SPECIAL
, 1);
237 /* ------------------------------------------------------------------------- */
238 /* Misc common code for vi/emacs */
240 /* Handle the commenting/uncommenting of a line.
242 * 1 if a carriage return is indicated (comment added)
243 * 0 if no return (comment removed)
244 * -1 if there is an error (not enough room for comment chars)
245 * If successful, *lenp contains the new length. Note: cursor should be
246 * moved to the start of the line after (un)commenting.
249 x_do_comment(char *buf
, int bsize
, int *lenp
)
255 return 1; /* somewhat arbitrary - it's what at&t ksh does */
257 /* Already commented? */
261 for (j
= 0, i
= 1; i
< len
; i
++) {
262 if (!saw_nl
|| buf
[i
] != '#')
264 saw_nl
= buf
[i
] == '\n';
271 /* See if there's room for the #'s - 1 per \n */
272 for (i
= 0; i
< len
; i
++)
275 if (len
+ n
>= bsize
)
277 /* Now add them... */
278 for (i
= len
, j
= len
+ n
; --i
>= 0; ) {
289 /* ------------------------------------------------------------------------- */
290 /* Common file/command completion code for vi/emacs */
293 static char *add_glob(const char *str
, int slen
);
294 static void glob_table(const char *pat
, XPtrV
*wp
, struct table
*tp
);
295 static void glob_path(int flags
, const char *pat
, XPtrV
*wp
,
299 x_print_expansions(int nwords
, char *const *words
, int is_command
)
303 /* Check if all matches are in the same directory (in this
304 * case, we want to omit the directory name)
307 (prefix_len
= x_longest_prefix(nwords
, words
)) > 0) {
310 /* Special case for 1 match (prefix is whole word) */
312 prefix_len
= x_basename(words
[0], NULL
);
313 /* Any (non-trailing) slashes in non-common word suffixes? */
314 for (i
= 0; i
< nwords
; i
++)
315 if (x_basename(words
[i
] + prefix_len
, NULL
) >
318 /* All in same directory? */
322 while (prefix_len
> 0 && words
[0][prefix_len
- 1] != '/')
324 XPinit(l
, nwords
+ 1);
325 for (i
= 0; i
< nwords
; i
++)
326 XPput(l
, words
[i
] + prefix_len
);
329 /* Enumerate expansions */
332 pr_list((char **) XPptrv(l
));
334 XPfree(l
); /* not x_free_words() */
339 /* Enumerate expansions */
347 * - appends * to (copy of) str if no globbing chars found
348 * - does expansion, checks for no match, etc.
349 * - sets *wordsp to array of matching strings
350 * - returns number of matching strings
353 x_file_glob(int flags
, const char *str
, int slen
, char ***wordsp
)
359 struct source
*s
, *sold
;
364 toglob
= add_glob(str
, slen
);
367 * Convert "foo*" (toglob) to an array of strings (words)
370 s
= pushs(SWSTR
, ATEMP
);
371 s
->start
= s
->str
= toglob
;
373 if (yylex(ONEWORD
|UNESCAPE
) != LWORD
) {
375 internal_warningf("%s: substitute error", __func__
);
380 expand(yylval
.cp
, &w
, DOGLOB
|DOTILDE
|DOMARKDIRS
);
382 words
= (char **) XPclose(w
);
384 for (nwords
= 0; words
[nwords
]; nwords
++)
389 /* Check if file exists, also, check for empty
390 * result - happens if we tried to glob something
391 * which evaluated to an empty string (e.g.,
392 * "$FOO" when there is no FOO, etc).
394 if ((lstat(words
[0], &statb
) < 0) ||
395 words
[0][0] == '\0') {
396 x_free_words(nwords
, words
);
401 afree(toglob
, ATEMP
);
406 x_free_words(nwords
, words
);
413 /* Data structure used in x_command_glob() */
414 struct path_order_info
{
420 static int path_order_cmp(const void *aa
, const void *bb
);
422 /* Compare routine used in x_command_glob() */
424 path_order_cmp(const void *aa
, const void *bb
)
426 const struct path_order_info
*a
= (const struct path_order_info
*) aa
;
427 const struct path_order_info
*b
= (const struct path_order_info
*) bb
;
430 t
= strcmp(a
->word
+ a
->base
, b
->word
+ b
->base
);
431 return t
? t
: a
->path_order
- b
->path_order
;
435 x_command_glob(int flags
, const char *str
, int slen
, char ***wordsp
)
447 toglob
= add_glob(str
, slen
);
449 /* Convert "foo*" (toglob) to a pattern for future use */
450 pat
= evalstr(toglob
, DOPAT
|DOTILDE
);
451 afree(toglob
, ATEMP
);
455 glob_table(pat
, &w
, &keywords
);
456 glob_table(pat
, &w
, &aliases
);
457 glob_table(pat
, &w
, &builtins
);
458 for (l
= genv
->loc
; l
; l
= l
->next
)
459 glob_table(pat
, &w
, &l
->funs
);
461 glob_path(flags
, pat
, &w
, search_path
);
462 if ((fpath
= str_val(global("FPATH"))) != null
)
463 glob_path(flags
, pat
, &w
, fpath
);
474 if (flags
& XCF_FULLPATH
) {
475 /* Sort by basename, then path order */
476 struct path_order_info
*info
;
477 struct path_order_info
*last_info
= NULL
;
478 char **words
= (char **) XPptrv(w
);
482 info
= areallocarray(NULL
, nwords
,
483 sizeof(struct path_order_info
), ATEMP
);
485 for (i
= 0; i
< nwords
; i
++) {
486 info
[i
].word
= words
[i
];
487 info
[i
].base
= x_basename(words
[i
], NULL
);
488 if (!last_info
|| info
[i
].base
!= last_info
->base
||
489 strncmp(words
[i
], last_info
->word
, info
[i
].base
) != 0) {
490 last_info
= &info
[i
];
493 info
[i
].path_order
= path_order
;
495 qsort(info
, nwords
, sizeof(struct path_order_info
),
497 for (i
= 0; i
< nwords
; i
++)
498 words
[i
] = info
[i
].word
;
501 /* Sort and remove duplicate entries */
502 char **words
= (char **) XPptrv(w
);
505 qsortp(XPptrv(w
), (size_t) nwords
, xstrcmp
);
507 for (i
= j
= 0; i
< nwords
- 1; i
++) {
508 if (strcmp(words
[i
], words
[i
+ 1]))
509 words
[j
++] = words
[i
];
511 afree(words
[i
], ATEMP
);
513 words
[j
++] = words
[i
];
515 w
.cur
= (void **) &words
[j
];
519 *wordsp
= (char **) XPclose(w
);
524 #define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' || \
525 (c) == '`' || (c) == '=' || (c) == ':' )
528 x_locate_word(const char *buf
, int buflen
, int pos
, int *startp
,
534 /* Bad call? Probably should report error */
535 if (pos
< 0 || pos
> buflen
) {
540 /* The case where pos == buflen happens to take care of itself... */
543 /* Keep going backwards to start of word (has effect of allowing
544 * one blank after the end of a word)
546 for (; (start
> 0 && IS_WORDC(buf
[start
- 1])) ||
547 (start
> 1 && buf
[start
-2] == '\\'); start
--)
549 /* Go forwards to end of word */
550 for (end
= start
; end
< buflen
&& IS_WORDC(buf
[end
]); end
++) {
551 if (buf
[end
] == '\\' && (end
+1) < buflen
)
558 /* Figure out if this is a command */
559 for (p
= start
- 1; p
>= 0 && isspace((unsigned char)buf
[p
]);
562 iscmd
= p
< 0 || strchr(";|&()`", buf
[p
]);
564 /* If command has a /, path, etc. is not searched;
565 * only current directory is searched, which is just
566 * like file globbing.
568 for (p
= start
; p
< end
; p
++)
573 *is_commandp
= iscmd
;
582 x_try_array(const char *buf
, int buflen
, const char *want
, int wantlen
,
583 int *nwords
, char ***words
)
585 const char *cmd
, *cp
;
586 int cmdlen
, n
, i
, slen
;
593 /* Walk back to find start of command. */
596 for (cmd
= want
; cmd
> buf
; cmd
--) {
597 if (strchr(";|&()`", cmd
[-1]) != NULL
)
600 while (cmd
< want
&& isspace((u_char
)*cmd
))
603 while (cmd
+ cmdlen
< want
&& !isspace((u_char
)cmd
[cmdlen
]))
605 for (i
= 0; i
< cmdlen
; i
++) {
606 if (!isalnum((u_char
)cmd
[i
]) && cmd
[i
] != '_')
610 /* Take a stab at argument count from here. */
612 for (cp
= cmd
+ cmdlen
+ 1; cp
< want
; cp
++) {
613 if (!isspace((u_char
)cp
[-1]) && isspace((u_char
)*cp
))
617 /* Try to find the array. */
618 if (asprintf(&name
, "complete_%.*s_%d", cmdlen
, cmd
, n
) < 0)
619 internal_errorf("unable to allocate memory");
622 if (~v
->flag
& (ISSET
|ARRAY
)) {
623 if (asprintf(&name
, "complete_%.*s", cmdlen
, cmd
) < 0)
624 internal_errorf("unable to allocate memory");
627 if (~v
->flag
& (ISSET
|ARRAY
))
631 /* Walk the array and build words list. */
632 for (vp
= v
; vp
; vp
= vp
->u
.array
) {
633 if (~vp
->flag
& ISSET
)
643 if (slen
!= 0 && strncmp(s
, want
, slen
) != 0)
646 *words
= areallocarray(*words
, (*nwords
) + 2, sizeof **words
,
648 (*words
)[(*nwords
)++] = str_save(s
, ATEMP
);
651 (*words
)[*nwords
] = NULL
;
657 x_cf_glob(int flags
, const char *buf
, int buflen
, int pos
, int *startp
,
658 int *endp
, char ***wordsp
, int *is_commandp
)
665 len
= x_locate_word(buf
, buflen
, pos
, startp
, &is_command
);
666 if (!(flags
& XCF_COMMAND
))
668 /* Don't do command globing on zero length strings - it takes too
669 * long and isn't very useful. File globs are more likely to be
670 * useful, so allow these.
672 if (len
== 0 && is_command
)
676 nwords
= x_command_glob(flags
, buf
+ *startp
, len
, &words
);
677 else if (!x_try_array(buf
, buflen
, buf
+ *startp
, len
, &nwords
, &words
))
678 nwords
= x_file_glob(flags
, buf
+ *startp
, len
, &words
);
685 *is_commandp
= is_command
;
687 *endp
= *startp
+ len
;
692 /* Given a string, copy it and possibly add a '*' to the end. The
693 * new string is returned.
696 add_glob(const char *str
, int slen
)
700 bool saw_slash
= false;
705 toglob
= str_nsave(str
, slen
+ 1, ATEMP
); /* + 1 for "*" */
709 * If the pathname contains a wildcard (an unquoted '*',
710 * '?', or '[') or parameter expansion ('$'), or a ~username
711 * with no trailing slash, then it is globbed based on that
712 * value (i.e., without the appended '*').
714 for (s
= toglob
; *s
; s
++) {
715 if (*s
== '\\' && s
[1])
717 else if (*s
== '*' || *s
== '[' || *s
== '?' || *s
== '$' ||
718 (s
[1] == '(' /*)*/ && strchr("+@!", *s
)))
723 if (!*s
&& (*toglob
!= '~' || saw_slash
)) {
725 toglob
[slen
+ 1] = '\0';
732 * Find longest common prefix
735 x_longest_prefix(int nwords
, char *const *words
)
744 prefix_len
= strlen(words
[0]);
745 for (i
= 1; i
< nwords
; i
++)
746 for (j
= 0, p
= words
[i
]; j
< prefix_len
; j
++)
747 if (p
[j
] != words
[0][j
]) {
755 x_free_words(int nwords
, char **words
)
759 for (i
= 0; i
< nwords
; i
++)
760 afree(words
[i
], ATEMP
);
764 /* Return the offset of the basename of string s (which ends at se - need not
765 * be null terminated). Trailing slashes are ignored. If s is just a slash,
766 * then the offset is 0 (actually, length - 1).
777 x_basename(const char *s
, const char *se
)
786 /* Skip trailing slashes */
787 for (p
= se
- 1; p
> s
&& *p
== '/'; p
--)
789 for (; p
> s
&& *p
!= '/'; p
--)
791 if (*p
== '/' && p
+ 1 < se
)
798 * Apply pattern matching to a table: all table entries that match a pattern
802 glob_table(const char *pat
, XPtrV
*wp
, struct table
*tp
)
807 for (ktwalk(&ts
, tp
); (te
= ktnext(&ts
)); ) {
808 if (gmatch(te
->name
, pat
, false))
809 XPput(*wp
, str_save(te
->name
, ATEMP
));
814 glob_path(int flags
, const char *pat
, XPtrV
*wp
, const char *path
)
821 int oldsize
, newsize
, i
, j
;
825 patlen
= strlen(pat
) + 1;
827 Xinit(xs
, xp
, patlen
+ 128, ATEMP
);
829 xp
= Xstring(xs
, xp
);
830 if (!(p
= strchr(sp
, ':')))
834 /* Copy sp into xp, stuffing any MAGIC characters
839 XcheckN(xs
, xp
, pathlen
* 2);
849 XcheckN(xs
, xp
, patlen
);
850 memcpy(xp
, pat
, patlen
);
852 oldsize
= XPsize(*wp
);
853 glob_str(Xstring(xs
, xp
), wp
, 1); /* mark dirs */
854 newsize
= XPsize(*wp
);
856 /* Check that each match is executable... */
857 words
= (char **) XPptrv(*wp
);
858 for (i
= j
= oldsize
; i
< newsize
; i
++) {
860 if ((search_access(words
[i
], X_OK
, &staterr
) >= 0) ||
861 (staterr
== EISDIR
)) {
863 if (!(flags
& XCF_FULLPATH
))
864 memmove(words
[j
], words
[j
] + pathlen
,
865 strlen(words
[j
] + pathlen
) + 1);
868 afree(words
[i
], ATEMP
);
870 wp
->cur
= (void **) &words
[j
];
879 * if argument string contains any special characters, they will
880 * be escaped and the result will be put into edit buffer by
881 * keybinding-specific function
884 x_escape(const char *s
, size_t len
, int (*putbuf_func
) (const char *, size_t))
887 const char *ifs
= str_val(local("IFS", 0));
890 for (add
= 0, wlen
= len
; wlen
- add
> 0; add
++) {
891 if (strchr("!\"#$&'()*:;<=>?[\\]`{|}", s
[add
]) ||
892 strchr(ifs
, s
[add
])) {
893 if (putbuf_func(s
, add
) != 0) {
898 putbuf_func("\\", 1);
899 putbuf_func(&s
[add
], 1);
904 add
= -1; /* after the increment it will go to 0 */
907 if (wlen
> 0 && rval
== 0)
908 rval
= putbuf_func(s
, wlen
);