edit.c revision 1.11 1 /* $NetBSD: edit.c,v 1.11 2003/06/23 11:38:54 agc Exp $ */
2
3 /*
4 * Command line editing - common code
5 *
6 */
7 #include <sys/cdefs.h>
8
9 #ifndef lint
10 __RCSID("$NetBSD: edit.c,v 1.11 2003/06/23 11:38:54 agc Exp $");
11 #endif
12
13
14 #include "config.h"
15 #ifdef EDIT
16
17 #include "sh.h"
18 #include "tty.h"
19 #define EXTERN
20 #include "edit.h"
21 #undef EXTERN
22 #ifdef OS_SCO /* SCO Unix 3.2v4.1 */
23 # include <sys/stream.h> /* needed for <sys/ptem.h> */
24 # include <sys/ptem.h> /* needed for struct winsize */
25 #endif /* OS_SCO */
26 #include <ctype.h>
27 #include <sys/ioctl.h>
28 #include "ksh_stat.h"
29
30
31 #if defined(TIOCGWINSZ)
32 static RETSIGTYPE x_sigwinch ARGS((int sig));
33 static int got_sigwinch;
34 static void check_sigwinch ARGS((void));
35 #endif /* TIOCGWINSZ */
36
37 static int x_file_glob ARGS((int flags, const char *str, int slen,
38 char ***wordsp));
39 static int x_command_glob ARGS((int flags, const char *str, int slen,
40 char ***wordsp));
41 static int x_locate_word ARGS((const char *buf, int buflen, int pos,
42 int *startp, int *is_command));
43 static int path_order_cmp ARGS((const void *, const void *));
44
45 static char vdisable_c;
46
47
48 /* Called from main */
49 void
50 x_init()
51 {
52 /* set to -2 to force initial binding */
53 edchars.erase = edchars.kill = edchars.intr = edchars.quit
54 = edchars.eof = -2;
55 /* default value for deficient systems */
56 edchars.werase = 027; /* ^W */
57
58 #ifdef TIOCGWINSZ
59 # ifdef SIGWINCH
60 if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP))
61 sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
62 # endif /* SIGWINCH */
63 got_sigwinch = 1; /* force initial check */
64 check_sigwinch();
65 #endif /* TIOCGWINSZ */
66
67 #ifdef EMACS
68 x_init_emacs();
69 #endif /* EMACS */
70
71 /* Bizarreness to figure out how to disable
72 * a struct termios.c_cc[] char
73 */
74 #ifdef _POSIX_VDISABLE
75 if (_POSIX_VDISABLE >= 0)
76 vdisable_c = (char) _POSIX_VDISABLE;
77 else
78 /* `feature not available' */
79 vdisable_c = (char) 0377;
80 #else
81 # if defined(HAVE_PATHCONF) && defined(_PC_VDISABLE)
82 vdisable_c = fpathconf(tty_fd, _PC_VDISABLE);
83 # else
84 vdisable_c = (char) 0377; /* default to old BSD value */
85 # endif
86 #endif /* _POSIX_VDISABLE */
87 }
88
89 #if defined(TIOCGWINSZ)
90 static RETSIGTYPE
91 x_sigwinch(sig)
92 int sig;
93 {
94 got_sigwinch = 1;
95 return RETSIGVAL;
96 }
97
98 static void
99 check_sigwinch ARGS((void))
100 {
101 if (got_sigwinch) {
102 struct winsize ws;
103
104 got_sigwinch = 0;
105 if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
106 struct tbl *vp;
107
108 /* Do NOT export COLUMNS/LINES. Many applications
109 * check COLUMNS/LINES before checking ws.ws_col/row,
110 * so if the app is started with C/L in the environ
111 * and the window is then resized, the app won't
112 * see the change cause the environ doesn't change.
113 */
114 if (ws.ws_col) {
115 x_cols = ws.ws_col < MIN_COLS ? MIN_COLS
116 : ws.ws_col;
117
118 if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
119 setint(vp, (long) ws.ws_col);
120 }
121 if (ws.ws_row
122 && (vp = typeset("LINES", 0, 0, 0, 0)))
123 setint(vp, (long) ws.ws_row);
124 }
125 }
126 }
127 #endif /* TIOCGWINSZ */
128
129 /*
130 * read an edited command line
131 */
132 int
133 x_read(buf, len)
134 char *buf;
135 size_t len;
136 {
137 int i;
138
139 #if defined(TIOCGWINSZ)
140 if (got_sigwinch)
141 check_sigwinch();
142 #endif /* TIOCGWINSZ */
143
144 x_mode(TRUE);
145 #ifdef EMACS
146 if (Flag(FEMACS) || Flag(FGMACS))
147 i = x_emacs(buf, len);
148 else
149 #endif
150 #ifdef VI
151 if (Flag(FVI))
152 i = x_vi(buf, len);
153 else
154 #endif
155 i = -1; /* internal error */
156 x_mode(FALSE);
157 return i;
158 }
159
160 /* tty I/O */
161
162 int
163 x_getc()
164 {
165 #ifdef OS2
166 unsigned char c = _read_kbd(0, 1, 0);
167 return c == 0 ? 0xE0 : c;
168 #else /* OS2 */
169 char c;
170 int n;
171
172 while ((n = blocking_read(0, &c, 1)) < 0 && errno == EINTR)
173 if (trap) {
174 x_mode(FALSE);
175 runtraps(0);
176 x_mode(TRUE);
177 }
178 if (n != 1)
179 return -1;
180 return (int) (unsigned char) c;
181 #endif /* OS2 */
182 }
183
184 void
185 x_flush()
186 {
187 shf_flush(shl_out);
188 }
189
190 void
191 x_putc(c)
192 int c;
193 {
194 shf_putc(c, shl_out);
195 }
196
197 void
198 x_puts(s)
199 const char *s;
200 {
201 while (*s != 0)
202 shf_putc(*s++, shl_out);
203 }
204
205 bool_t
206 x_mode(onoff)
207 bool_t onoff;
208 {
209 static bool_t x_cur_mode;
210 bool_t prev;
211
212 if (x_cur_mode == onoff)
213 return x_cur_mode;
214 prev = x_cur_mode;
215 x_cur_mode = onoff;
216
217 if (onoff) {
218 TTY_state cb;
219 X_chars oldchars;
220
221 oldchars = edchars;
222 cb = tty_state;
223
224 #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
225 edchars.erase = cb.c_cc[VERASE];
226 edchars.kill = cb.c_cc[VKILL];
227 edchars.intr = cb.c_cc[VINTR];
228 edchars.quit = cb.c_cc[VQUIT];
229 edchars.eof = cb.c_cc[VEOF];
230 # ifdef VWERASE
231 edchars.werase = cb.c_cc[VWERASE];
232 # endif
233 # ifdef _CRAY2 /* brain-damaged terminal handler */
234 cb.c_lflag &= ~(ICANON|ECHO);
235 /* rely on print routine to map '\n' to CR,LF */
236 # else
237 cb.c_iflag &= ~(INLCR|ICRNL);
238 # ifdef _BSD_SYSV /* need to force CBREAK instead of RAW (need CRMOD on output) */
239 cb.c_lflag &= ~(ICANON|ECHO);
240 # else
241 # ifdef SWTCH /* need CBREAK to handle swtch char */
242 cb.c_lflag &= ~(ICANON|ECHO);
243 cb.c_lflag |= ISIG;
244 cb.c_cc[VINTR] = vdisable_c;
245 cb.c_cc[VQUIT] = vdisable_c;
246 # else
247 cb.c_lflag &= ~(ISIG|ICANON|ECHO);
248 # endif
249 # endif
250 # ifdef VLNEXT
251 /* osf/1 processes lnext when ~icanon */
252 cb.c_cc[VLNEXT] = vdisable_c;
253 # endif /* VLNEXT */
254 # ifdef VDISCARD
255 /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */
256 cb.c_cc[VDISCARD] = vdisable_c;
257 # endif /* VDISCARD */
258 cb.c_cc[VTIME] = 0;
259 cb.c_cc[VMIN] = 1;
260 # endif /* _CRAY2 */
261 #else
262 /* Assume BSD tty stuff. */
263 edchars.erase = cb.sgttyb.sg_erase;
264 edchars.kill = cb.sgttyb.sg_kill;
265 cb.sgttyb.sg_flags &= ~ECHO;
266 cb.sgttyb.sg_flags |= CBREAK;
267 # ifdef TIOCGATC
268 edchars.intr = cb.lchars.tc_intrc;
269 edchars.quit = cb.lchars.tc_quitc;
270 edchars.eof = cb.lchars.tc_eofc;
271 edchars.werase = cb.lchars.tc_werasc;
272 cb.lchars.tc_suspc = -1;
273 cb.lchars.tc_dsuspc = -1;
274 cb.lchars.tc_lnextc = -1;
275 cb.lchars.tc_statc = -1;
276 cb.lchars.tc_intrc = -1;
277 cb.lchars.tc_quitc = -1;
278 cb.lchars.tc_rprntc = -1;
279 # else
280 edchars.intr = cb.tchars.t_intrc;
281 edchars.quit = cb.tchars.t_quitc;
282 edchars.eof = cb.tchars.t_eofc;
283 cb.tchars.t_intrc = -1;
284 cb.tchars.t_quitc = -1;
285 # ifdef TIOCGLTC
286 edchars.werase = cb.ltchars.t_werasc;
287 cb.ltchars.t_suspc = -1;
288 cb.ltchars.t_dsuspc = -1;
289 cb.ltchars.t_lnextc = -1;
290 cb.ltchars.t_rprntc = -1;
291 # endif
292 # endif /* TIOCGATC */
293 #endif /* HAVE_TERMIOS_H || HAVE_TERMIO_H */
294
295 set_tty(tty_fd, &cb, TF_WAIT);
296
297 #ifdef __CYGWIN__
298 if (edchars.eof == '\0')
299 edchars.eof = '\4';
300 #endif /* __CYGWIN__ */
301
302 /* Convert unset values to internal `unset' value */
303 if (edchars.erase == vdisable_c)
304 edchars.erase = -1;
305 if (edchars.kill == vdisable_c)
306 edchars.kill = -1;
307 if (edchars.intr == vdisable_c)
308 edchars.intr = -1;
309 if (edchars.quit == vdisable_c)
310 edchars.quit = -1;
311 if (edchars.eof == vdisable_c)
312 edchars.eof = -1;
313 if (edchars.werase == vdisable_c)
314 edchars.werase = -1;
315 if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) {
316 #ifdef EMACS
317 x_emacs_keys(&edchars);
318 #endif
319 }
320 } else {
321 /* TF_WAIT doesn't seem to be necessary when leaving xmode */
322 set_tty(tty_fd, &tty_state, TF_NONE);
323 }
324
325 return prev;
326 }
327
328 /* NAME:
329 * promptlen - calculate the length of PS1 etc.
330 *
331 * DESCRIPTION:
332 * This function is based on a fix from guy (at) demon.co.uk
333 * It fixes a bug in that if PS1 contains '!', the length
334 * given by strlen() is probably wrong.
335 *
336 * RETURN VALUE:
337 * length
338 */
339 int
340 promptlen(cp, spp)
341 const char *cp;
342 const char **spp;
343 {
344 int count = 0;
345 const char *sp = cp;
346 char delimiter = 0;
347 int indelimit = 0;
348
349 /* Undocumented AT&T ksh feature:
350 * If the second char in the prompt string is \r then the first char
351 * is taken to be a non-printing delimiter and any chars between two
352 * instances of the delimiter are not considered to be part of the
353 * prompt length
354 */
355 if (*cp && cp[1] == '\r') {
356 delimiter = *cp;
357 cp += 2;
358 }
359
360 for (; *cp; cp++) {
361 if (indelimit && *cp != delimiter)
362 ;
363 else if (*cp == '\n' || *cp == '\r') {
364 count = 0;
365 sp = cp + 1;
366 } else if (*cp == '\t') {
367 count = (count | 7) + 1;
368 } else if (*cp == '\b') {
369 if (count > 0)
370 count--;
371 } else if (*cp == delimiter)
372 indelimit = !indelimit;
373 else
374 count++;
375 }
376 if (spp)
377 *spp = sp;
378 return count;
379 }
380
381 void
382 set_editmode(ed)
383 const char *ed;
384 {
385 static const enum sh_flag edit_flags[] = {
386 #ifdef EMACS
387 FEMACS, FGMACS,
388 #endif
389 #ifdef VI
390 FVI,
391 #endif
392 };
393 char *rcp;
394 int i;
395
396 if ((rcp = ksh_strrchr_dirsep(ed)))
397 ed = ++rcp;
398 for (i = 0; i < NELEM(edit_flags); i++)
399 if (strstr(ed, options[(int) edit_flags[i]].name)) {
400 change_flag(edit_flags[i], OF_SPECIAL, 1);
401 return;
402 }
403 }
404
405 /* ------------------------------------------------------------------------- */
406 /* Misc common code for vi/emacs */
407
408 /* Handle the commenting/uncommenting of a line.
409 * Returns:
410 * 1 if a carriage return is indicated (comment added)
411 * 0 if no return (comment removed)
412 * -1 if there is an error (not enough room for comment chars)
413 * If successful, *lenp contains the new length. Note: cursor should be
414 * moved to the start of the line after (un)commenting.
415 */
416 int
417 x_do_comment(buf, bsize, lenp)
418 char *buf;
419 int bsize;
420 int *lenp;
421 {
422 int i, j;
423 int len = *lenp;
424
425 if (len == 0)
426 return 1; /* somewhat arbitrary - it's what at&t ksh does */
427
428 /* Already commented? */
429 if (buf[0] == '#') {
430 int saw_nl = 0;
431
432 for (j = 0, i = 1; i < len; i++) {
433 if (!saw_nl || buf[i] != '#')
434 buf[j++] = buf[i];
435 saw_nl = buf[i] == '\n';
436 }
437 *lenp = j;
438 return 0;
439 } else {
440 int n = 1;
441
442 /* See if there's room for the #'s - 1 per \n */
443 for (i = 0; i < len; i++)
444 if (buf[i] == '\n')
445 n++;
446 if (len + n >= bsize)
447 return -1;
448 /* Now add them... */
449 for (i = len, j = len + n; --i >= 0; ) {
450 if (buf[i] == '\n')
451 buf[--j] = '#';
452 buf[--j] = buf[i];
453 }
454 buf[0] = '#';
455 *lenp += n;
456 return 1;
457 }
458 }
459
460 /* ------------------------------------------------------------------------- */
461 /* Common file/command completion code for vi/emacs */
462
463
464 static char *add_glob ARGS((const char *str, int slen));
465 static void glob_table ARGS((const char *pat, XPtrV *wp, struct table *tp));
466 static void glob_path ARGS((int flags, const char *pat, XPtrV *wp,
467 const char *path));
468
469 #if 0 /* not used... */
470 int x_complete_word ARGS((const char *str, int slen, int is_command,
471 int *multiple, char **ret));
472 int
473 x_complete_word(str, slen, is_command, nwordsp, ret)
474 const char *str;
475 int slen;
476 int is_command;
477 int *nwordsp;
478 char **ret;
479 {
480 int nwords;
481 int prefix_len;
482 char **words;
483
484 nwords = (is_command ? x_command_glob : x_file_glob)(XCF_FULLPATH,
485 str, slen, &words);
486 *nwordsp = nwords;
487 if (nwords == 0) {
488 *ret = (char *) 0;
489 return -1;
490 }
491
492 prefix_len = x_longest_prefix(nwords, words);
493 *ret = str_nsave(words[0], prefix_len, ATEMP);
494 x_free_words(nwords, words);
495 return prefix_len;
496 }
497 #endif /* 0 */
498
499 void
500 x_print_expansions(nwords, words, is_command)
501 int nwords;
502 char *const *words;
503 int is_command;
504 {
505 int use_copy = 0;
506 int prefix_len;
507 XPtrV l;
508
509 /* Check if all matches are in the same directory (in this
510 * case, we want to omit the directory name)
511 */
512 if (!is_command
513 && (prefix_len = x_longest_prefix(nwords, words)) > 0)
514 {
515 int i;
516
517 /* Special case for 1 match (prefix is whole word) */
518 if (nwords == 1)
519 prefix_len = x_basename(words[0], (char *) 0);
520 /* Any (non-trailing) slashes in non-common word suffixes? */
521 for (i = 0; i < nwords; i++)
522 if (x_basename(words[i] + prefix_len, (char *) 0)
523 > prefix_len)
524 break;
525 /* All in same directory? */
526 if (i == nwords) {
527 while (prefix_len > 0
528 && !ISDIRSEP(words[0][prefix_len - 1]))
529 prefix_len--;
530 use_copy = 1;
531 XPinit(l, nwords + 1);
532 for (i = 0; i < nwords; i++)
533 XPput(l, words[i] + prefix_len);
534 XPput(l, (char *) 0);
535 }
536 }
537
538 /*
539 * Enumerate expansions
540 */
541 x_putc('\r');
542 x_putc('\n');
543 pr_list(use_copy ? (char **) XPptrv(l) : words);
544
545 if (use_copy)
546 XPfree(l); /* not x_free_words() */
547 }
548
549 /*
550 * Do file globbing:
551 * - appends * to (copy of) str if no globbing chars found
552 * - does expansion, checks for no match, etc.
553 * - sets *wordsp to array of matching strings
554 * - returns number of matching strings
555 */
556 static int
557 x_file_glob(flags, str, slen, wordsp)
558 int flags;
559 const char *str;
560 int slen;
561 char ***wordsp;
562 {
563 char *toglob;
564 char **words;
565 int nwords, i, idx, escaping;
566 XPtrV w;
567 struct source *s, *sold;
568
569 if (slen < 0)
570 return 0;
571
572 toglob = add_glob(str, slen);
573
574 /* remove all escaping backward slashes */
575 escaping = 0;
576 for(i = 0, idx = 0; toglob[i]; i++) {
577 if (toglob[i] == '\\' && !escaping) {
578 escaping = 1;
579 continue;
580 }
581
582 toglob[idx] = toglob[i];
583 idx++;
584 if (escaping) escaping = 0;
585 }
586 toglob[idx] = '\0';
587
588 /*
589 * Convert "foo*" (toglob) to an array of strings (words)
590 */
591 sold = source;
592 s = pushs(SWSTR, ATEMP);
593 s->start = s->str = toglob;
594 source = s;
595 if (yylex(ONEWORD) != LWORD) {
596 source = sold;
597 internal_errorf(0, "fileglob: substitute error");
598 return 0;
599 }
600 source = sold;
601 XPinit(w, 32);
602 expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS);
603 XPput(w, NULL);
604 words = (char **) XPclose(w);
605
606 for (nwords = 0; words[nwords]; nwords++)
607 ;
608 if (nwords == 1) {
609 struct stat statb;
610
611 /* Check if globbing failed (returned glob pattern),
612 * but be careful (E.g. toglob == "ab*" when the file
613 * "ab*" exists is not an error).
614 * Also, check for empty result - happens if we tried
615 * to glob something which evaluated to an empty
616 * string (e.g., "$FOO" when there is no FOO, etc).
617 */
618 if ((strcmp(words[0], toglob) == 0
619 && stat(words[0], &statb) < 0)
620 || words[0][0] == '\0')
621 {
622 x_free_words(nwords, words);
623 nwords = 0;
624 }
625 }
626 afree(toglob, ATEMP);
627
628 *wordsp = nwords ? words : (char **) 0;
629
630 return nwords;
631 }
632
633 /* Data structure used in x_command_glob() */
634 struct path_order_info {
635 char *word;
636 int base;
637 int path_order;
638 };
639
640 /* Compare routine used in x_command_glob() */
641 static int
642 path_order_cmp(aa, bb)
643 const void *aa;
644 const void *bb;
645 {
646 const struct path_order_info *a = (const struct path_order_info *) aa;
647 const struct path_order_info *b = (const struct path_order_info *) bb;
648 int t;
649
650 t = FILECMP(a->word + a->base, b->word + b->base);
651 return t ? t : a->path_order - b->path_order;
652 }
653
654 static int
655 x_command_glob(flags, str, slen, wordsp)
656 int flags;
657 const char *str;
658 int slen;
659 char ***wordsp;
660 {
661 char *toglob;
662 char *pat;
663 char *fpath;
664 int nwords;
665 XPtrV w;
666 struct block *l;
667
668 if (slen < 0)
669 return 0;
670
671 toglob = add_glob(str, slen);
672
673 /* Convert "foo*" (toglob) to a pattern for future use */
674 pat = evalstr(toglob, DOPAT|DOTILDE);
675 afree(toglob, ATEMP);
676
677 XPinit(w, 32);
678
679 glob_table(pat, &w, &keywords);
680 glob_table(pat, &w, &aliases);
681 glob_table(pat, &w, &builtins);
682 for (l = e->loc; l; l = l->next)
683 glob_table(pat, &w, &l->funs);
684
685 glob_path(flags, pat, &w, path);
686 if ((fpath = str_val(global("FPATH"))) != null)
687 glob_path(flags, pat, &w, fpath);
688
689 nwords = XPsize(w);
690
691 if (!nwords) {
692 *wordsp = (char **) 0;
693 XPfree(w);
694 return 0;
695 }
696
697 /* Sort entries */
698 if (flags & XCF_FULLPATH) {
699 /* Sort by basename, then path order */
700 struct path_order_info *info;
701 struct path_order_info *last_info = 0;
702 char **words = (char **) XPptrv(w);
703 int path_order = 0;
704 int i;
705
706 info = (struct path_order_info *)
707 alloc(sizeof(struct path_order_info) * nwords, ATEMP);
708 for (i = 0; i < nwords; i++) {
709 info[i].word = words[i];
710 info[i].base = x_basename(words[i], (char *) 0);
711 if (!last_info || info[i].base != last_info->base
712 || FILENCMP(words[i],
713 last_info->word, info[i].base) != 0)
714 {
715 last_info = &info[i];
716 path_order++;
717 }
718 info[i].path_order = path_order;
719 }
720 qsort(info, nwords, sizeof(struct path_order_info),
721 path_order_cmp);
722 for (i = 0; i < nwords; i++)
723 words[i] = info[i].word;
724 afree((void *) info, ATEMP);
725 } else {
726 /* Sort and remove duplicate entries */
727 char **words = (char **) XPptrv(w);
728 int i, j;
729
730 qsortp(XPptrv(w), (size_t) nwords, xstrcmp);
731
732 for (i = j = 0; i < nwords - 1; i++) {
733 if (strcmp(words[i], words[i + 1]))
734 words[j++] = words[i];
735 else
736 afree(words[i], ATEMP);
737 }
738 words[j++] = words[i];
739 nwords = j;
740 w.cur = (void **) &words[j];
741 }
742
743 XPput(w, NULL);
744 *wordsp = (char **) XPclose(w);
745
746 return nwords;
747 }
748
749 #define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' \
750 || (c) == '`' || (c) == '=' || (c) == ':' )
751
752 static int
753 x_locate_word(buf, buflen, pos, startp, is_commandp)
754 const char *buf;
755 int buflen;
756 int pos;
757 int *startp;
758 int *is_commandp;
759 {
760 int p;
761 int start, end;
762
763 /* Bad call? Probably should report error */
764 if (pos < 0 || pos > buflen) {
765 *startp = pos;
766 *is_commandp = 0;
767 return 0;
768 }
769 /* The case where pos == buflen happens to take care of itself... */
770
771 start = pos;
772 /* Keep going backwards to start of word (has effect of allowing
773 * one blank after the end of a word)
774 */
775 for (; (start > 0 && IS_WORDC(buf[start - 1]))
776 || (start > 1 && buf[start-2] == '\\'); start--)
777 ;
778 /* Go forwards to end of word */
779 for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
780 if (buf[end] == '\\' && (end+1) < buflen && buf[end+1] == ' ')
781 end++;
782 }
783
784 if (is_commandp) {
785 int iscmd;
786
787 /* Figure out if this is a command */
788 for (p = start - 1; p >= 0 && isspace((unsigned char)buf[p]); p--)
789 ;
790 iscmd = p < 0 || strchr(";|&()`", buf[p]);
791 if (iscmd) {
792 /* If command has a /, path, etc. is not searched;
793 * only current directory is searched, which is just
794 * like file globbing.
795 */
796 for (p = start; p < end; p++)
797 if (ISDIRSEP(buf[p]))
798 break;
799 iscmd = p == end;
800 }
801 *is_commandp = iscmd;
802 }
803
804 *startp = start;
805
806 return end - start;
807 }
808
809 int
810 x_cf_glob(flags, buf, buflen, pos, startp, endp, wordsp, is_commandp)
811 int flags;
812 const char *buf;
813 int buflen;
814 int pos;
815 int *startp;
816 int *endp;
817 char ***wordsp;
818 int *is_commandp;
819 {
820 int len;
821 int nwords;
822 char **words;
823 int is_command;
824
825 len = x_locate_word(buf, buflen, pos, startp, &is_command);
826 if (!(flags & XCF_COMMAND))
827 is_command = 0;
828 /* Don't do command globing on zero length strings - it takes too
829 * long and isn't very useful. File globs are more likely to be
830 * useful, so allow these.
831 */
832 if (len == 0 && is_command)
833 return 0;
834
835 nwords = (is_command ? x_command_glob : x_file_glob)(flags,
836 buf + *startp, len, &words);
837 if (nwords == 0) {
838 *wordsp = (char **) 0;
839 return 0;
840 }
841
842 if (is_commandp)
843 *is_commandp = is_command;
844 *wordsp = words;
845 *endp = *startp + len;
846
847 return nwords;
848 }
849
850
851 static char *
852 find_match(const char *s, int have)
853 {
854 int want = 0;
855 int nest = 1;
856 int c;
857
858 switch (have) {
859 case '(': want = ')'; break;
860 case '{': want = '}'; break;
861 case '[': want = ']'; break;
862 case '\'':
863 case '"': want = have; break;
864 }
865 if (want == 0 || s == NULL)
866 return NULL;
867
868 while (nest > 0 && (c = *s)) {
869 if (c == '\\') {
870 s++;
871 s++;
872 continue;
873 }
874 if (c == want)
875 nest--;
876 else if (c == have)
877 nest++;
878 if (nest > 0)
879 s++;
880 }
881 return (nest == 0) ? (char *) s : NULL;
882 }
883
884 /* Given a string, copy it and possibly add a '*' to the end. The
885 * new string is returned.
886 */
887 static char *
888 add_glob(str, slen)
889 const char *str;
890 int slen;
891 {
892 char *toglob;
893 char *s;
894 bool_t saw_slash = FALSE;
895
896 if (slen < 0)
897 return (char *) 0;
898
899 toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */
900 toglob[slen] = '\0';
901
902 /*
903 * If the pathname contains a wildcard (an unquoted '*', '?',
904 * or '[') or parameter expansion ('$') with nothing following
905 * it, or a ~username with no trailing slash, then it is
906 * globbed based on that value (i.e., without the appended
907 * '*').
908 */
909 for (s = toglob; *s; s++) {
910 if (*s == '\\' && s[1])
911 s++;
912 else if (*s == '*' || *s == '[' || *s == '?'
913 || (s[1] == '(' /*)*/ && strchr("*+?@!", *s)))
914 break;
915 else if (ISDIRSEP(*s))
916 saw_slash = TRUE;
917 else if (*s == '$') {
918 if (*++s == '{') {
919 char *cp;
920
921 if ((cp = find_match(&s[1], '{')))
922 s = ++cp;
923 }
924 if (*s)
925 s += strcspn(s,
926 ".,/?-<>[]{}()'\";:\\|=+*&^%$#@!`~");
927 if (!*s)
928 return toglob;
929 }
930 }
931 if (!*s && (*toglob != '~' || saw_slash)) {
932 toglob[slen] = '*';
933 toglob[slen + 1] = '\0';
934 }
935
936 return toglob;
937 }
938
939 /*
940 * Find longest common prefix
941 */
942 int
943 x_longest_prefix(nwords, words)
944 int nwords;
945 char *const *words;
946 {
947 int i, j;
948 int prefix_len;
949 char *p;
950
951 if (nwords <= 0)
952 return 0;
953
954 prefix_len = strlen(words[0]);
955 for (i = 1; i < nwords; i++)
956 for (j = 0, p = words[i]; j < prefix_len; j++)
957 if (FILECHCONV(p[j]) != FILECHCONV(words[0][j])) {
958 prefix_len = j;
959 break;
960 }
961 return prefix_len;
962 }
963
964 void
965 x_free_words(nwords, words)
966 int nwords;
967 char **words;
968 {
969 int i;
970
971 for (i = 0; i < nwords; i++)
972 if (words[i])
973 afree(words[i], ATEMP);
974 afree(words, ATEMP);
975 }
976
977 /* Return the offset of the basename of string s (which ends at se - need not
978 * be null terminated). Trailing slashes are ignored. If s is just a slash,
979 * then the offset is 0 (actually, length - 1).
980 * s Return
981 * /etc 1
982 * /etc/ 1
983 * /etc// 1
984 * /etc/fo 5
985 * foo 0
986 * /// 2
987 * 0
988 */
989 int
990 x_basename(s, se)
991 const char *s;
992 const char *se;
993 {
994 const char *p;
995
996 if (se == (char *) 0)
997 se = s + strlen(s);
998 if (s == se)
999 return 0;
1000
1001 /* Skip trailing slashes */
1002 for (p = se - 1; p > s && ISDIRSEP(*p); p--)
1003 ;
1004 for (; p > s && !ISDIRSEP(*p); p--)
1005 ;
1006 if (ISDIRSEP(*p) && p + 1 < se)
1007 p++;
1008
1009 return p - s;
1010 }
1011
1012 /*
1013 * Apply pattern matching to a table: all table entries that match a pattern
1014 * are added to wp.
1015 */
1016 static void
1017 glob_table(pat, wp, tp)
1018 const char *pat;
1019 XPtrV *wp;
1020 struct table *tp;
1021 {
1022 struct tstate ts;
1023 struct tbl *te;
1024
1025 for (twalk(&ts, tp); (te = tnext(&ts)); ) {
1026 if (gmatch(te->name, pat, FALSE))
1027 XPput(*wp, str_save(te->name, ATEMP));
1028 }
1029 }
1030
1031 static void
1032 glob_path(flags, pat, wp, path)
1033 int flags;
1034 const char *pat;
1035 XPtrV *wp;
1036 const char *path;
1037 {
1038 const char *sp, *p;
1039 char *xp;
1040 int staterr;
1041 int pathlen;
1042 int patlen;
1043 int oldsize, newsize, i, j;
1044 char **words;
1045 XString xs;
1046
1047 patlen = strlen(pat) + 1;
1048 sp = path;
1049 Xinit(xs, xp, patlen + 128, ATEMP);
1050 while (sp) {
1051 xp = Xstring(xs, xp);
1052 if (!(p = strchr(sp, PATHSEP)))
1053 p = sp + strlen(sp);
1054 pathlen = p - sp;
1055 if (pathlen) {
1056 /* Copy sp into xp, stuffing any MAGIC characters
1057 * on the way
1058 */
1059 const char *s = sp;
1060
1061 XcheckN(xs, xp, pathlen * 2);
1062 while (s < p) {
1063 if (ISMAGIC(*s))
1064 *xp++ = MAGIC;
1065 *xp++ = *s++;
1066 }
1067 *xp++ = DIRSEP;
1068 pathlen++;
1069 }
1070 sp = p;
1071 XcheckN(xs, xp, patlen);
1072 memcpy(xp, pat, patlen);
1073
1074 oldsize = XPsize(*wp);
1075 glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */
1076 newsize = XPsize(*wp);
1077
1078 /* Check that each match is executable... */
1079 words = (char **) XPptrv(*wp);
1080 for (i = j = oldsize; i < newsize; i++) {
1081 staterr = 0;
1082 if ((search_access(words[i], X_OK, &staterr) >= 0)
1083 || (staterr == EISDIR)) {
1084 words[j] = words[i];
1085 if (!(flags & XCF_FULLPATH))
1086 memmove(words[j], words[j] + pathlen,
1087 strlen(words[j] + pathlen) + 1);
1088 j++;
1089 } else
1090 afree(words[i], ATEMP);
1091 }
1092 wp->cur = (void **) &words[j];
1093
1094 if (!*sp++)
1095 break;
1096 }
1097 Xfree(xs, xp);
1098 }
1099
1100 /*
1101 * if argument string contains any special characters, they will
1102 * be escaped and the result will be put into edit buffer by
1103 * keybinding-specific function
1104 */
1105 int
1106 x_escape(s, len, putbuf_func)
1107 const char *s;
1108 size_t len;
1109 int putbuf_func ARGS((const char *s, size_t len));
1110 {
1111 size_t add, wlen;
1112 const char *ifs = str_val(local("IFS", 0));
1113 int rval=0;
1114
1115 for (add = 0, wlen = len; wlen - add > 0; add++) {
1116 if (strchr("\\$(){}*&;|<>\"'`#", s[add]) || strchr(ifs, s[add])) {
1117 if (putbuf_func(s, add) != 0) {
1118 rval = -1;
1119 break;
1120 }
1121
1122 putbuf_func("\\", 1);
1123 putbuf_func(&s[add], 1);
1124
1125 add++;
1126 wlen -= add;
1127 s += add;
1128 add = -1; /* after the increment it will go to 0 */
1129 }
1130 }
1131 if (wlen > 0 && rval == 0)
1132 rval = putbuf_func(s, wlen);
1133
1134 return (rval);
1135 }
1136 #endif /* EDIT */
1137