edit.c
1/* $OpenBSD: edit.c,v 1.69 2019/06/28 13:34:59 deraadt Exp $ */
2
3/*
4 * Command line editing - common code
5 *
6 */
7
8#include "config.h"
9
10#include <sys/ioctl.h>
11#include <sys/stat.h>
12
13#include <ctype.h>
14#include <errno.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19
20#include "sh.h"
21#include "edit.h"
22#include "tty.h"
23
24X_chars edchars;
25
26static void x_sigwinch(int);
27volatile sig_atomic_t got_sigwinch;
28static void check_sigwinch(void);
29
30static int x_file_glob(int, const char *, int, char ***);
31static int x_command_glob(int, const char *, int, char ***);
32static int x_locate_word(const char *, int, int, int *, int *);
33
34
35/* Called from main */
36void
37x_init(void)
38{
39 /* set to -2 to force initial binding */
40 edchars.erase = edchars.kill = edchars.intr = edchars.quit =
41 edchars.eof = -2;
42 /* default value for deficient systems */
43 edchars.werase = 027; /* ^W */
44
45 if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
46 sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
47 got_sigwinch = 1; /* force initial check */
48 check_sigwinch();
49
50#ifdef EMACS
51 x_init_emacs();
52#endif /* EMACS */
53}
54
55static void
56x_sigwinch(int sig)
57{
58 got_sigwinch = 1;
59}
60
61static void
62check_sigwinch(void)
63{
64 if (got_sigwinch) {
65 struct winsize ws;
66
67 got_sigwinch = 0;
68 if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) == 0) {
69 struct tbl *vp;
70
71 /* Do NOT export COLUMNS/LINES. Many applications
72 * check COLUMNS/LINES before checking ws.ws_col/row,
73 * so if the app is started with C/L in the environ
74 * and the window is then resized, the app won't
75 * see the change cause the environ doesn't change.
76 */
77 if (ws.ws_col) {
78 x_cols = ws.ws_col < MIN_COLS ? MIN_COLS :
79 ws.ws_col;
80
81 if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
82 setint(vp, (int64_t) ws.ws_col);
83 }
84 if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0)))
85 setint(vp, (int64_t) ws.ws_row);
86 }
87 }
88}
89
90/*
91 * read an edited command line
92 */
93int
94x_read(char *buf, size_t len)
95{
96 int i;
97
98 x_mode(true);
99#ifdef EMACS
100 if (Flag(FEMACS) || Flag(FGMACS))
101 i = x_emacs(buf, len);
102 else
103#endif
104#ifdef VI
105 if (Flag(FVI))
106 i = x_vi(buf, len);
107 else
108#endif
109 i = -1; /* internal error */
110 x_mode(false);
111 check_sigwinch();
112 return i;
113}
114
115/* tty I/O */
116
117int
118x_getc(void)
119{
120 char c;
121 int n;
122
123 while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
124 if (trap) {
125 x_mode(false);
126 runtraps(0);
127 x_mode(true);
128 }
129 if (n != 1)
130 return -1;
131 return (int) (unsigned char) c;
132}
133
134void
135x_flush(void)
136{
137 shf_flush(shl_out);
138}
139
140int
141x_putc(int c)
142{
143 return shf_putc(c, shl_out);
144}
145
146void
147x_puts(const char *s)
148{
149 while (*s != 0)
150 shf_putc(*s++, shl_out);
151}
152
153bool
154x_mode(bool onoff)
155{
156 static bool x_cur_mode;
157 bool prev;
158
159 if (x_cur_mode == onoff)
160 return x_cur_mode;
161 prev = x_cur_mode;
162 x_cur_mode = onoff;
163
164 if (onoff) {
165 struct termios cb;
166 X_chars oldchars;
167
168 oldchars = edchars;
169 cb = tty_state;
170
171 edchars.erase = cb.c_cc[VERASE];
172 edchars.kill = cb.c_cc[VKILL];
173 edchars.intr = cb.c_cc[VINTR];
174 edchars.quit = cb.c_cc[VQUIT];
175 edchars.eof = cb.c_cc[VEOF];
176 edchars.werase = cb.c_cc[VWERASE];
177 cb.c_iflag &= ~(INLCR|ICRNL);
178 cb.c_lflag &= ~(ISIG|ICANON|ECHO);
179 /* osf/1 processes lnext when ~icanon */
180 cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
181 /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
182 cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
183 cb.c_cc[VTIME] = 0;
184 cb.c_cc[VMIN] = 1;
185
186 tcsetattr(tty_fd, TCSADRAIN, &cb);
187
188 /* Convert unset values to internal `unset' value */
189 if (edchars.erase == _POSIX_VDISABLE)
190 edchars.erase = -1;
191 if (edchars.kill == _POSIX_VDISABLE)
192 edchars.kill = -1;
193 if (edchars.intr == _POSIX_VDISABLE)
194 edchars.intr = -1;
195 if (edchars.quit == _POSIX_VDISABLE)
196 edchars.quit = -1;
197 if (edchars.eof == _POSIX_VDISABLE)
198 edchars.eof = -1;
199 if (edchars.werase == _POSIX_VDISABLE)
200 edchars.werase = -1;
201 if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
202#ifdef EMACS
203 x_emacs_keys(&edchars);
204#endif
205 }
206 } else {
207 tcsetattr(tty_fd, TCSADRAIN, &tty_state);
208 }
209
210 return prev;
211}
212
213void
214set_editmode(const char *ed)
215{
216 static const enum sh_flag edit_flags[] = {
217#ifdef EMACS
218 FEMACS, FGMACS,
219#endif
220#ifdef VI
221 FVI,
222#endif
223 };
224 char *rcp;
225 unsigned int ele;
226
227 if ((rcp = strrchr(ed, '/')))
228 ed = ++rcp;
229 for (ele = 0; ele < NELEM(edit_flags); ele++)
230 if (strstr(ed, sh_options[(int) edit_flags[ele]].name)) {
231 change_flag(edit_flags[ele], OF_SPECIAL, 1);
232 return;
233 }
234}
235
236/* ------------------------------------------------------------------------- */
237/* Misc common code for vi/emacs */
238
239/* Handle the commenting/uncommenting of a line.
240 * Returns:
241 * 1 if a carriage return is indicated (comment added)
242 * 0 if no return (comment removed)
243 * -1 if there is an error (not enough room for comment chars)
244 * If successful, *lenp contains the new length. Note: cursor should be
245 * moved to the start of the line after (un)commenting.
246 */
247int
248x_do_comment(char *buf, int bsize, int *lenp)
249{
250 int i, j;
251 int len = *lenp;
252
253 if (len == 0)
254 return 1; /* somewhat arbitrary - it's what at&t ksh does */
255
256 /* Already commented? */
257 if (buf[0] == '#') {
258 int saw_nl = 0;
259
260 for (j = 0, i = 1; i < len; i++) {
261 if (!saw_nl || buf[i] != '#')
262 buf[j++] = buf[i];
263 saw_nl = buf[i] == '\n';
264 }
265 *lenp = j;
266 return 0;
267 } else {
268 int n = 1;
269
270 /* See if there's room for the #'s - 1 per \n */
271 for (i = 0; i < len; i++)
272 if (buf[i] == '\n')
273 n++;
274 if (len + n >= bsize)
275 return -1;
276 /* Now add them... */
277 for (i = len, j = len + n; --i >= 0; ) {
278 if (buf[i] == '\n')
279 buf[--j] = '#';
280 buf[--j] = buf[i];
281 }
282 buf[0] = '#';
283 *lenp += n;
284 return 1;
285 }
286}
287
288/* ------------------------------------------------------------------------- */
289/* Common file/command completion code for vi/emacs */
290
291
292static char *add_glob(const char *str, int slen);
293static void glob_table(const char *pat, XPtrV *wp, struct table *tp);
294static void glob_path(int flags, const char *pat, XPtrV *wp,
295 const char *path);
296
297void
298x_print_expansions(int nwords, char *const *words, int is_command)
299{
300 int prefix_len;
301
302 /* Check if all matches are in the same directory (in this
303 * case, we want to omit the directory name)
304 */
305 if (!is_command &&
306 (prefix_len = x_longest_prefix(nwords, words)) > 0) {
307 int i;
308
309 /* Special case for 1 match (prefix is whole word) */
310 if (nwords == 1)
311 prefix_len = x_basename(words[0], NULL);
312 /* Any (non-trailing) slashes in non-common word suffixes? */
313 for (i = 0; i < nwords; i++)
314 if (x_basename(words[i] + prefix_len, NULL) >
315 prefix_len)
316 break;
317 /* All in same directory? */
318 if (i == nwords) {
319 XPtrV l;
320
321 while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
322 prefix_len--;
323 XPinit(l, nwords + 1);
324 for (i = 0; i < nwords; i++)
325 XPput(l, words[i] + prefix_len);
326 XPput(l, NULL);
327
328 /* Enumerate expansions */
329 x_putc('\r');
330 x_putc('\n');
331 pr_list((char **) XPptrv(l));
332
333 XPfree(l); /* not x_free_words() */
334 return;
335 }
336 }
337
338 /* Enumerate expansions */
339 x_putc('\r');
340 x_putc('\n');
341 pr_list(words);
342}
343
344/*
345 * Do file globbing:
346 * - appends * to (copy of) str if no globbing chars found
347 * - does expansion, checks for no match, etc.
348 * - sets *wordsp to array of matching strings
349 * - returns number of matching strings
350 */
351static int
352x_file_glob(int flags, const char *str, int slen, char ***wordsp)
353{
354 char *toglob;
355 char **words;
356 int nwords;
357 XPtrV w;
358 struct source *s, *sold;
359
360 if (slen < 0)
361 return 0;
362
363 toglob = add_glob(str, slen);
364
365 /*
366 * Convert "foo*" (toglob) to an array of strings (words)
367 */
368 sold = source;
369 s = pushs(SWSTR, ATEMP);
370 s->start = s->str = toglob;
371 source = s;
372 if (yylex(ONEWORD|UNESCAPE) != LWORD) {
373 source = sold;
374 internal_warningf("%s: substitute error", __func__);
375 return 0;
376 }
377 source = sold;
378 XPinit(w, 32);
379 expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
380 XPput(w, NULL);
381 words = (char **) XPclose(w);
382
383 for (nwords = 0; words[nwords]; nwords++)
384 ;
385 if (nwords == 1) {
386 struct stat statb;
387
388 /* Check if file exists, also, check for empty
389 * result - happens if we tried to glob something
390 * which evaluated to an empty string (e.g.,
391 * "$FOO" when there is no FOO, etc).
392 */
393 if ((lstat(words[0], &statb) == -1) ||
394 words[0][0] == '\0') {
395 x_free_words(nwords, words);
396 words = NULL;
397 nwords = 0;
398 }
399 }
400 afree(toglob, ATEMP);
401
402 if (nwords) {
403 *wordsp = words;
404 } else if (words) {
405 x_free_words(nwords, words);
406 *wordsp = NULL;
407 }
408
409 return nwords;
410}
411
412/* Data structure used in x_command_glob() */
413struct path_order_info {
414 char *word;
415 int base;
416 int path_order;
417};
418
419static int path_order_cmp(const void *aa, const void *bb);
420
421/* Compare routine used in x_command_glob() */
422static int
423path_order_cmp(const void *aa, const void *bb)
424{
425 const struct path_order_info *a = (const struct path_order_info *) aa;
426 const struct path_order_info *b = (const struct path_order_info *) bb;
427 int t;
428
429 t = strcmp(a->word + a->base, b->word + b->base);
430 return t ? t : a->path_order - b->path_order;
431}
432
433static int
434x_command_glob(int flags, const char *str, int slen, char ***wordsp)
435{
436 char *toglob;
437 char *pat;
438 char *fpath;
439 int nwords;
440 XPtrV w;
441 struct block *l;
442
443 if (slen < 0)
444 return 0;
445
446 toglob = add_glob(str, slen);
447
448 /* Convert "foo*" (toglob) to a pattern for future use */
449 pat = evalstr(toglob, DOPAT|DOTILDE);
450 afree(toglob, ATEMP);
451
452 XPinit(w, 32);
453
454 glob_table(pat, &w, &keywords);
455 glob_table(pat, &w, &aliases);
456 glob_table(pat, &w, &builtins);
457 for (l = genv->loc; l; l = l->next)
458 glob_table(pat, &w, &l->funs);
459
460 glob_path(flags, pat, &w, search_path);
461 if ((fpath = str_val(global("FPATH"))) != null)
462 glob_path(flags, pat, &w, fpath);
463
464 nwords = XPsize(w);
465
466 if (!nwords) {
467 *wordsp = NULL;
468 XPfree(w);
469 return 0;
470 }
471
472 /* Sort entries */
473 if (flags & XCF_FULLPATH) {
474 /* Sort by basename, then path order */
475 struct path_order_info *info;
476 struct path_order_info *last_info = NULL;
477 char **words = (char **) XPptrv(w);
478 int path_order = 0;
479 int i;
480
481 info = areallocarray(NULL, nwords,
482 sizeof(struct path_order_info), ATEMP);
483
484 for (i = 0; i < nwords; i++) {
485 info[i].word = words[i];
486 info[i].base = x_basename(words[i], NULL);
487 if (!last_info || info[i].base != last_info->base ||
488 strncmp(words[i], last_info->word, info[i].base) != 0) {
489 last_info = &info[i];
490 path_order++;
491 }
492 info[i].path_order = path_order;
493 }
494 qsort(info, nwords, sizeof(struct path_order_info),
495 path_order_cmp);
496 for (i = 0; i < nwords; i++)
497 words[i] = info[i].word;
498 afree(info, ATEMP);
499 } else {
500 /* Sort and remove duplicate entries */
501 char **words = (char **) XPptrv(w);
502 int i, j;
503
504 qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
505
506 for (i = j = 0; i < nwords - 1; i++) {
507 if (strcmp(words[i], words[i + 1]))
508 words[j++] = words[i];
509 else
510 afree(words[i], ATEMP);
511 }
512 words[j++] = words[i];
513 nwords = j;
514 w.cur = (void **) &words[j];
515 }
516
517 XPput(w, NULL);
518 *wordsp = (char **) XPclose(w);
519
520 return nwords;
521}
522
523#define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' || \
524 (c) == '`' || (c) == '=' || (c) == ':' )
525
526static int
527x_locate_word(const char *buf, int buflen, int pos, int *startp,
528 int *is_commandp)
529{
530 int p;
531 int start, end;
532
533 /* Bad call? Probably should report error */
534 if (pos < 0 || pos > buflen) {
535 *startp = pos;
536 *is_commandp = 0;
537 return 0;
538 }
539 /* The case where pos == buflen happens to take care of itself... */
540
541 start = pos;
542 /* Keep going backwards to start of word (has effect of allowing
543 * one blank after the end of a word)
544 */
545 for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
546 (start > 1 && buf[start-2] == '\\'); start--)
547 ;
548 /* Go forwards to end of word */
549 for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
550 if (buf[end] == '\\' && (end+1) < buflen)
551 end++;
552 }
553
554 if (is_commandp) {
555 int iscmd;
556
557 /* Figure out if this is a command */
558 for (p = start - 1; p >= 0 && isspace((unsigned char)buf[p]);
559 p--)
560 ;
561 iscmd = p < 0 || strchr(";|&()`", buf[p]);
562 if (iscmd) {
563 /* If command has a /, path, etc. is not searched;
564 * only current directory is searched, which is just
565 * like file globbing.
566 */
567 for (p = start; p < end; p++)
568 if (buf[p] == '/')
569 break;
570 iscmd = p == end;
571 }
572 *is_commandp = iscmd;
573 }
574
575 *startp = start;
576
577 return end - start;
578}
579
580static int
581x_try_array(const char *buf, int buflen, const char *want, int wantlen,
582 int *nwords, char ***words)
583{
584 const char *cmd, *cp;
585 int cmdlen, n, i, slen;
586 char *name, *s;
587 struct tbl *v, *vp;
588
589 *nwords = 0;
590 *words = NULL;
591
592 /* Walk back to find start of command. */
593 if (want == buf)
594 return 0;
595 for (cmd = want; cmd > buf; cmd--) {
596 if (strchr(";|&()`", cmd[-1]) != NULL)
597 break;
598 }
599 while (cmd < want && isspace((u_char)*cmd))
600 cmd++;
601 cmdlen = 0;
602 while (cmd + cmdlen < want && !isspace((u_char)cmd[cmdlen]))
603 cmdlen++;
604 for (i = 0; i < cmdlen; i++) {
605 if (!isalnum((u_char)cmd[i]) && cmd[i] != '_')
606 return 0;
607 }
608
609 /* Take a stab at argument count from here. */
610 n = 1;
611 for (cp = cmd + cmdlen + 1; cp < want; cp++) {
612 if (!isspace((u_char)cp[-1]) && isspace((u_char)*cp))
613 n++;
614 }
615
616 /* Try to find the array. */
617 if (asprintf(&name, "complete_%.*s_%d", cmdlen, cmd, n) == -1)
618 internal_errorf("unable to allocate memory");
619 v = global(name);
620 free(name);
621 if (~v->flag & (ISSET|ARRAY)) {
622 if (asprintf(&name, "complete_%.*s", cmdlen, cmd) == -1)
623 internal_errorf("unable to allocate memory");
624 v = global(name);
625 free(name);
626 if (~v->flag & (ISSET|ARRAY))
627 return 0;
628 }
629
630 /* Walk the array and build words list. */
631 for (vp = v; vp; vp = vp->u.array) {
632 if (~vp->flag & ISSET)
633 continue;
634
635 s = str_val(vp);
636 slen = strlen(s);
637
638 if (slen < wantlen)
639 continue;
640 if (slen > wantlen)
641 slen = wantlen;
642 if (slen != 0 && strncmp(s, want, slen) != 0)
643 continue;
644
645 *words = areallocarray(*words, (*nwords) + 2, sizeof **words,
646 ATEMP);
647 (*words)[(*nwords)++] = str_save(s, ATEMP);
648 }
649 if (*nwords != 0)
650 (*words)[*nwords] = NULL;
651
652 return *nwords != 0;
653}
654
655int
656x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp,
657 int *endp, char ***wordsp, int *is_commandp)
658{
659 int len;
660 int nwords;
661 char **words = NULL;
662 int is_command;
663
664 len = x_locate_word(buf, buflen, pos, startp, &is_command);
665 if (!(flags & XCF_COMMAND))
666 is_command = 0;
667 /* Don't do command globing on zero length strings - it takes too
668 * long and isn't very useful. File globs are more likely to be
669 * useful, so allow these.
670 */
671 if (len == 0 && is_command)
672 return 0;
673
674 if (is_command)
675 nwords = x_command_glob(flags, buf + *startp, len, &words);
676 else if (!x_try_array(buf, buflen, buf + *startp, len, &nwords, &words))
677 nwords = x_file_glob(flags, buf + *startp, len, &words);
678 if (nwords == 0) {
679 *wordsp = NULL;
680 return 0;
681 }
682
683 if (is_commandp)
684 *is_commandp = is_command;
685 *wordsp = words;
686 *endp = *startp + len;
687
688 return nwords;
689}
690
691/* Given a string, copy it and possibly add a '*' to the end. The
692 * new string is returned.
693 */
694static char *
695add_glob(const char *str, int slen)
696{
697 char *toglob;
698 char *s;
699 bool saw_slash = false;
700
701 if (slen < 0)
702 return NULL;
703
704 toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
705 toglob[slen] = '\0';
706
707 /*
708 * If the pathname contains a wildcard (an unquoted '*',
709 * '?', or '[') or parameter expansion ('$'), or a ~username
710 * with no trailing slash, then it is globbed based on that
711 * value (i.e., without the appended '*').
712 */
713 for (s = toglob; *s; s++) {
714 if (*s == '\\' && s[1])
715 s++;
716 else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' ||
717 (s[1] == '(' /*)*/ && strchr("+@!", *s)))
718 break;
719 else if (*s == '/')
720 saw_slash = true;
721 }
722 if (!*s && (*toglob != '~' || saw_slash)) {
723 toglob[slen] = '*';
724 toglob[slen + 1] = '\0';
725 }
726
727 return toglob;
728}
729
730/*
731 * Find longest common prefix
732 */
733int
734x_longest_prefix(int nwords, char *const *words)
735{
736 int i, j;
737 int prefix_len;
738 char *p;
739
740 if (nwords <= 0)
741 return 0;
742
743 prefix_len = strlen(words[0]);
744 for (i = 1; i < nwords; i++)
745 for (j = 0, p = words[i]; j < prefix_len; j++)
746 if (p[j] != words[0][j]) {
747 prefix_len = j;
748 break;
749 }
750 return prefix_len;
751}
752
753void
754x_free_words(int nwords, char **words)
755{
756 int i;
757
758 for (i = 0; i < nwords; i++)
759 afree(words[i], ATEMP);
760 afree(words, ATEMP);
761}
762
763/* Return the offset of the basename of string s (which ends at se - need not
764 * be null terminated). Trailing slashes are ignored. If s is just a slash,
765 * then the offset is 0 (actually, length - 1).
766 * s Return
767 * /etc 1
768 * /etc/ 1
769 * /etc// 1
770 * /etc/fo 5
771 * foo 0
772 * /// 2
773 * 0
774 */
775int
776x_basename(const char *s, const char *se)
777{
778 const char *p;
779
780 if (se == NULL)
781 se = s + strlen(s);
782 if (s == se)
783 return 0;
784
785 /* Skip trailing slashes */
786 for (p = se - 1; p > s && *p == '/'; p--)
787 ;
788 for (; p > s && *p != '/'; p--)
789 ;
790 if (*p == '/' && p + 1 < se)
791 p++;
792
793 return p - s;
794}
795
796/*
797 * Apply pattern matching to a table: all table entries that match a pattern
798 * are added to wp.
799 */
800static void
801glob_table(const char *pat, XPtrV *wp, struct table *tp)
802{
803 struct tstate ts;
804 struct tbl *te;
805
806 for (ktwalk(&ts, tp); (te = ktnext(&ts)); ) {
807 if (gmatch(te->name, pat, false))
808 XPput(*wp, str_save(te->name, ATEMP));
809 }
810}
811
812static void
813glob_path(int flags, const char *pat, XPtrV *wp, const char *path)
814{
815 const char *sp, *p;
816 char *xp;
817 int staterr;
818 int pathlen;
819 int patlen;
820 int oldsize, newsize, i, j;
821 char **words;
822 XString xs;
823
824 patlen = strlen(pat) + 1;
825 sp = path;
826 Xinit(xs, xp, patlen + 128, ATEMP);
827 while (sp) {
828 xp = Xstring(xs, xp);
829 if (!(p = strchr(sp, ':')))
830 p = sp + strlen(sp);
831 pathlen = p - sp;
832 if (pathlen) {
833 /* Copy sp into xp, stuffing any MAGIC characters
834 * on the way
835 */
836 const char *s = sp;
837
838 XcheckN(xs, xp, pathlen * 2);
839 while (s < p) {
840 if (ISMAGIC(*s))
841 *xp++ = MAGIC;
842 *xp++ = *s++;
843 }
844 *xp++ = '/';
845 pathlen++;
846 }
847 sp = p;
848 XcheckN(xs, xp, patlen);
849 memcpy(xp, pat, patlen);
850
851 oldsize = XPsize(*wp);
852 glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
853 newsize = XPsize(*wp);
854
855 /* Check that each match is executable... */
856 words = (char **) XPptrv(*wp);
857 for (i = j = oldsize; i < newsize; i++) {
858 staterr = 0;
859 if ((search_access(words[i], X_OK, &staterr) >= 0) ||
860 (staterr == EISDIR)) {
861 words[j] = words[i];
862 if (!(flags & XCF_FULLPATH))
863 memmove(words[j], words[j] + pathlen,
864 strlen(words[j] + pathlen) + 1);
865 j++;
866 } else
867 afree(words[i], ATEMP);
868 }
869 wp->cur = (void **) &words[j];
870
871 if (!*sp++)
872 break;
873 }
874 Xfree(xs, xp);
875}
876
877/*
878 * if argument string contains any special characters, they will
879 * be escaped and the result will be put into edit buffer by
880 * keybinding-specific function
881 */
882int
883x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t))
884{
885 size_t add, wlen;
886 const char *ifs = str_val(local("IFS", 0));
887 int rval = 0;
888
889 for (add = 0, wlen = len; wlen - add > 0; add++) {
890 if (strchr("!\"#$&'()*:;<=>?[\\]`{|}", s[add]) ||
891 strchr(ifs, s[add])) {
892 if (putbuf_func(s, add) != 0) {
893 rval = -1;
894 break;
895 }
896
897 putbuf_func("\\", 1);
898 putbuf_func(&s[add], 1);
899
900 add++;
901 wlen -= add;
902 s += add;
903 add = -1; /* after the increment it will go to 0 */
904 }
905 }
906 if (wlen > 0 && rval == 0)
907 rval = putbuf_func(s, wlen);
908
909 return (rval);
910}