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