Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static char sccsid[] = "@(#)expand.c 5.6 (Berkeley) 6/1/90"; | |
36 | #endif /* not lint */ | |
37 | ||
38 | #include "defs.h" | |
39 | ||
40 | #define GAVSIZ NCARGS / 6 | |
41 | #define LC '{' | |
42 | #define RC '}' | |
43 | ||
44 | static char shchars[] = "${[*?"; | |
45 | ||
46 | int which; /* bit mask of types to expand */ | |
47 | int eargc; /* expanded arg count */ | |
48 | char **eargv; /* expanded arg vectors */ | |
49 | char *path; | |
50 | char *pathp; | |
51 | char *lastpathp; | |
52 | char *tilde; /* "~user" if not expanding tilde, else "" */ | |
53 | char *tpathp; | |
54 | int nleft; | |
55 | ||
56 | int expany; /* any expansions done? */ | |
57 | char *entp; | |
58 | char **sortbase; | |
59 | ||
60 | char *index(); | |
61 | int argcmp(); | |
62 | ||
63 | #define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \ | |
64 | sizeof(*sortbase), argcmp), sortbase = &eargv[eargc] | |
65 | ||
66 | /* | |
67 | * Take a list of names and expand any macros, etc. | |
68 | * wh = E_VARS if expanding variables. | |
69 | * wh = E_SHELL if expanding shell characters. | |
70 | * wh = E_TILDE if expanding `~'. | |
71 | * or any of these or'ed together. | |
72 | * | |
73 | * Major portions of this were snarfed from csh/sh.glob.c. | |
74 | */ | |
75 | struct namelist * | |
76 | expand(list, wh) | |
77 | struct namelist *list; | |
78 | int wh; | |
79 | { | |
80 | register struct namelist *nl, *prev; | |
81 | register int n; | |
82 | char pathbuf[BUFSIZ]; | |
83 | char *argvbuf[GAVSIZ]; | |
84 | ||
85 | if (debug) { | |
86 | printf("expand(%x, %d)\nlist = ", list, wh); | |
87 | prnames(list); | |
88 | } | |
89 | ||
90 | if (wh == 0) { | |
91 | register char *cp; | |
92 | ||
93 | for (nl = list; nl != NULL; nl = nl->n_next) | |
94 | for (cp = nl->n_name; *cp; cp++) | |
95 | *cp = *cp & TRIM; | |
96 | return(list); | |
97 | } | |
98 | ||
99 | which = wh; | |
100 | path = tpathp = pathp = pathbuf; | |
101 | *pathp = '\0'; | |
102 | lastpathp = &path[sizeof pathbuf - 2]; | |
103 | tilde = ""; | |
104 | eargc = 0; | |
105 | eargv = sortbase = argvbuf; | |
106 | *eargv = 0; | |
107 | nleft = NCARGS - 4; | |
108 | /* | |
109 | * Walk the name list and expand names into eargv[]; | |
110 | */ | |
111 | for (nl = list; nl != NULL; nl = nl->n_next) | |
112 | expstr(nl->n_name); | |
113 | /* | |
114 | * Take expanded list of names from eargv[] and build a new list. | |
115 | */ | |
116 | list = prev = NULL; | |
117 | for (n = 0; n < eargc; n++) { | |
118 | nl = makenl(NULL); | |
119 | nl->n_name = eargv[n]; | |
120 | if (prev == NULL) | |
121 | list = prev = nl; | |
122 | else { | |
123 | prev->n_next = nl; | |
124 | prev = nl; | |
125 | } | |
126 | } | |
127 | if (debug) { | |
128 | printf("expanded list = "); | |
129 | prnames(list); | |
130 | } | |
131 | return(list); | |
132 | } | |
133 | ||
134 | expstr(s) | |
135 | char *s; | |
136 | { | |
137 | register char *cp, *cp1; | |
138 | register struct namelist *tp; | |
139 | char *tail; | |
140 | char buf[BUFSIZ]; | |
141 | int savec, oeargc; | |
142 | extern char homedir[]; | |
143 | ||
144 | if (s == NULL || *s == '\0') | |
145 | return; | |
146 | ||
147 | if ((which & E_VARS) && (cp = index(s, '$')) != NULL) { | |
148 | *cp++ = '\0'; | |
149 | if (*cp == '\0') { | |
150 | yyerror("no variable name after '$'"); | |
151 | return; | |
152 | } | |
153 | if (*cp == LC) { | |
154 | cp++; | |
155 | if ((tail = index(cp, RC)) == NULL) { | |
156 | yyerror("unmatched '{'"); | |
157 | return; | |
158 | } | |
159 | *tail++ = savec = '\0'; | |
160 | if (*cp == '\0') { | |
161 | yyerror("no variable name after '$'"); | |
162 | return; | |
163 | } | |
164 | } else { | |
165 | tail = cp + 1; | |
166 | savec = *tail; | |
167 | *tail = '\0'; | |
168 | } | |
169 | tp = lookup(cp, NULL, 0); | |
170 | if (savec != '\0') | |
171 | *tail = savec; | |
172 | if (tp != NULL) { | |
173 | for (; tp != NULL; tp = tp->n_next) { | |
174 | sprintf(buf, "%s%s%s", s, tp->n_name, tail); | |
175 | expstr(buf); | |
176 | } | |
177 | return; | |
178 | } | |
179 | sprintf(buf, "%s%s", s, tail); | |
180 | expstr(buf); | |
181 | return; | |
182 | } | |
183 | if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) { | |
184 | Cat(s, ""); | |
185 | sort(); | |
186 | return; | |
187 | } | |
188 | if (*s == '~') { | |
189 | cp = ++s; | |
190 | if (*cp == '\0' || *cp == '/') { | |
191 | tilde = "~"; | |
192 | cp1 = homedir; | |
193 | } else { | |
194 | tilde = cp1 = buf; | |
195 | *cp1++ = '~'; | |
196 | do | |
197 | *cp1++ = *cp++; | |
198 | while (*cp && *cp != '/'); | |
199 | *cp1 = '\0'; | |
200 | if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) { | |
201 | if ((pw = getpwnam(buf+1)) == NULL) { | |
202 | strcat(buf, ": unknown user name"); | |
203 | yyerror(buf+1); | |
204 | return; | |
205 | } | |
206 | } | |
207 | cp1 = pw->pw_dir; | |
208 | s = cp; | |
209 | } | |
210 | for (cp = path; *cp++ = *cp1++; ) | |
211 | ; | |
212 | tpathp = pathp = cp - 1; | |
213 | } else { | |
214 | tpathp = pathp = path; | |
215 | tilde = ""; | |
216 | } | |
217 | *pathp = '\0'; | |
218 | if (!(which & E_SHELL)) { | |
219 | if (which & E_TILDE) | |
220 | Cat(path, s); | |
221 | else | |
222 | Cat(tilde, s); | |
223 | sort(); | |
224 | return; | |
225 | } | |
226 | oeargc = eargc; | |
227 | expany = 0; | |
228 | expsh(s); | |
229 | if (eargc == oeargc) | |
230 | Cat(s, ""); /* "nonomatch" is set */ | |
231 | sort(); | |
232 | } | |
233 | ||
234 | static | |
235 | argcmp(a1, a2) | |
236 | char **a1, **a2; | |
237 | { | |
238 | ||
239 | return (strcmp(*a1, *a2)); | |
240 | } | |
241 | ||
242 | /* | |
243 | * If there are any Shell meta characters in the name, | |
244 | * expand into a list, after searching directory | |
245 | */ | |
246 | expsh(s) | |
247 | char *s; | |
248 | { | |
249 | register char *cp; | |
250 | register char *spathp, *oldcp; | |
251 | struct stat stb; | |
252 | ||
253 | spathp = pathp; | |
254 | cp = s; | |
255 | while (!any(*cp, shchars)) { | |
256 | if (*cp == '\0') { | |
257 | if (!expany || stat(path, &stb) >= 0) { | |
258 | if (which & E_TILDE) | |
259 | Cat(path, ""); | |
260 | else | |
261 | Cat(tilde, tpathp); | |
262 | } | |
263 | goto endit; | |
264 | } | |
265 | addpath(*cp++); | |
266 | } | |
267 | oldcp = cp; | |
268 | while (cp > s && *cp != '/') | |
269 | cp--, pathp--; | |
270 | if (*cp == '/') | |
271 | cp++, pathp++; | |
272 | *pathp = '\0'; | |
273 | if (*oldcp == '{') { | |
274 | execbrc(cp, NULL); | |
275 | return; | |
276 | } | |
277 | matchdir(cp); | |
278 | endit: | |
279 | pathp = spathp; | |
280 | *pathp = '\0'; | |
281 | } | |
282 | ||
283 | matchdir(pattern) | |
284 | char *pattern; | |
285 | { | |
286 | struct stat stb; | |
287 | register struct direct *dp; | |
288 | DIR *dirp; | |
289 | ||
290 | dirp = opendir(path); | |
291 | if (dirp == NULL) { | |
292 | if (expany) | |
293 | return; | |
294 | goto patherr2; | |
295 | } | |
296 | if (fstat(dirp->dd_fd, &stb) < 0) | |
297 | goto patherr1; | |
298 | if (!ISDIR(stb.st_mode)) { | |
299 | errno = ENOTDIR; | |
300 | goto patherr1; | |
301 | } | |
302 | while ((dp = readdir(dirp)) != NULL) | |
303 | if (match(dp->d_name, pattern)) { | |
304 | if (which & E_TILDE) | |
305 | Cat(path, dp->d_name); | |
306 | else { | |
307 | strcpy(pathp, dp->d_name); | |
308 | Cat(tilde, tpathp); | |
309 | *pathp = '\0'; | |
310 | } | |
311 | } | |
312 | closedir(dirp); | |
313 | return; | |
314 | ||
315 | patherr1: | |
316 | closedir(dirp); | |
317 | patherr2: | |
318 | strcat(path, ": "); | |
319 | strcat(path, strerror(errno)); | |
320 | yyerror(path); | |
321 | } | |
322 | ||
323 | execbrc(p, s) | |
324 | char *p, *s; | |
325 | { | |
326 | char restbuf[BUFSIZ + 2]; | |
327 | register char *pe, *pm, *pl; | |
328 | int brclev = 0; | |
329 | char *lm, savec, *spathp; | |
330 | ||
331 | for (lm = restbuf; *p != '{'; *lm++ = *p++) | |
332 | continue; | |
333 | for (pe = ++p; *pe; pe++) | |
334 | switch (*pe) { | |
335 | ||
336 | case '{': | |
337 | brclev++; | |
338 | continue; | |
339 | ||
340 | case '}': | |
341 | if (brclev == 0) | |
342 | goto pend; | |
343 | brclev--; | |
344 | continue; | |
345 | ||
346 | case '[': | |
347 | for (pe++; *pe && *pe != ']'; pe++) | |
348 | continue; | |
349 | if (!*pe) | |
350 | yyerror("Missing ']'"); | |
351 | continue; | |
352 | } | |
353 | pend: | |
354 | if (brclev || !*pe) { | |
355 | yyerror("Missing '}'"); | |
356 | return (0); | |
357 | } | |
358 | for (pl = pm = p; pm <= pe; pm++) | |
359 | switch (*pm & (QUOTE|TRIM)) { | |
360 | ||
361 | case '{': | |
362 | brclev++; | |
363 | continue; | |
364 | ||
365 | case '}': | |
366 | if (brclev) { | |
367 | brclev--; | |
368 | continue; | |
369 | } | |
370 | goto doit; | |
371 | ||
372 | case ',': | |
373 | if (brclev) | |
374 | continue; | |
375 | doit: | |
376 | savec = *pm; | |
377 | *pm = 0; | |
378 | strcpy(lm, pl); | |
379 | strcat(restbuf, pe + 1); | |
380 | *pm = savec; | |
381 | if (s == 0) { | |
382 | spathp = pathp; | |
383 | expsh(restbuf); | |
384 | pathp = spathp; | |
385 | *pathp = 0; | |
386 | } else if (amatch(s, restbuf)) | |
387 | return (1); | |
388 | sort(); | |
389 | pl = pm + 1; | |
390 | continue; | |
391 | ||
392 | case '[': | |
393 | for (pm++; *pm && *pm != ']'; pm++) | |
394 | continue; | |
395 | if (!*pm) | |
396 | yyerror("Missing ']'"); | |
397 | continue; | |
398 | } | |
399 | return (0); | |
400 | } | |
401 | ||
402 | match(s, p) | |
403 | char *s, *p; | |
404 | { | |
405 | register int c; | |
406 | register char *sentp; | |
407 | char sexpany = expany; | |
408 | ||
409 | if (*s == '.' && *p != '.') | |
410 | return (0); | |
411 | sentp = entp; | |
412 | entp = s; | |
413 | c = amatch(s, p); | |
414 | entp = sentp; | |
415 | expany = sexpany; | |
416 | return (c); | |
417 | } | |
418 | ||
419 | amatch(s, p) | |
420 | register char *s, *p; | |
421 | { | |
422 | register int scc; | |
423 | int ok, lc; | |
424 | char *spathp; | |
425 | struct stat stb; | |
426 | int c, cc; | |
427 | ||
428 | expany = 1; | |
429 | for (;;) { | |
430 | scc = *s++ & TRIM; | |
431 | switch (c = *p++) { | |
432 | ||
433 | case '{': | |
434 | return (execbrc(p - 1, s - 1)); | |
435 | ||
436 | case '[': | |
437 | ok = 0; | |
438 | lc = 077777; | |
439 | while (cc = *p++) { | |
440 | if (cc == ']') { | |
441 | if (ok) | |
442 | break; | |
443 | return (0); | |
444 | } | |
445 | if (cc == '-') { | |
446 | if (lc <= scc && scc <= *p++) | |
447 | ok++; | |
448 | } else | |
449 | if (scc == (lc = cc)) | |
450 | ok++; | |
451 | } | |
452 | if (cc == 0) { | |
453 | yyerror("Missing ']'"); | |
454 | return (0); | |
455 | } | |
456 | continue; | |
457 | ||
458 | case '*': | |
459 | if (!*p) | |
460 | return (1); | |
461 | if (*p == '/') { | |
462 | p++; | |
463 | goto slash; | |
464 | } | |
465 | for (s--; *s; s++) | |
466 | if (amatch(s, p)) | |
467 | return (1); | |
468 | return (0); | |
469 | ||
470 | case '\0': | |
471 | return (scc == '\0'); | |
472 | ||
473 | default: | |
474 | if ((c & TRIM) != scc) | |
475 | return (0); | |
476 | continue; | |
477 | ||
478 | case '?': | |
479 | if (scc == '\0') | |
480 | return (0); | |
481 | continue; | |
482 | ||
483 | case '/': | |
484 | if (scc) | |
485 | return (0); | |
486 | slash: | |
487 | s = entp; | |
488 | spathp = pathp; | |
489 | while (*s) | |
490 | addpath(*s++); | |
491 | addpath('/'); | |
492 | if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) | |
493 | if (*p == '\0') { | |
494 | if (which & E_TILDE) | |
495 | Cat(path, ""); | |
496 | else | |
497 | Cat(tilde, tpathp); | |
498 | } else | |
499 | expsh(p); | |
500 | pathp = spathp; | |
501 | *pathp = '\0'; | |
502 | return (0); | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | smatch(s, p) | |
508 | register char *s, *p; | |
509 | { | |
510 | register int scc; | |
511 | int ok, lc; | |
512 | int c, cc; | |
513 | ||
514 | for (;;) { | |
515 | scc = *s++ & TRIM; | |
516 | switch (c = *p++) { | |
517 | ||
518 | case '[': | |
519 | ok = 0; | |
520 | lc = 077777; | |
521 | while (cc = *p++) { | |
522 | if (cc == ']') { | |
523 | if (ok) | |
524 | break; | |
525 | return (0); | |
526 | } | |
527 | if (cc == '-') { | |
528 | if (lc <= scc && scc <= *p++) | |
529 | ok++; | |
530 | } else | |
531 | if (scc == (lc = cc)) | |
532 | ok++; | |
533 | } | |
534 | if (cc == 0) { | |
535 | yyerror("Missing ']'"); | |
536 | return (0); | |
537 | } | |
538 | continue; | |
539 | ||
540 | case '*': | |
541 | if (!*p) | |
542 | return (1); | |
543 | for (s--; *s; s++) | |
544 | if (smatch(s, p)) | |
545 | return (1); | |
546 | return (0); | |
547 | ||
548 | case '\0': | |
549 | return (scc == '\0'); | |
550 | ||
551 | default: | |
552 | if ((c & TRIM) != scc) | |
553 | return (0); | |
554 | continue; | |
555 | ||
556 | case '?': | |
557 | if (scc == 0) | |
558 | return (0); | |
559 | continue; | |
560 | ||
561 | } | |
562 | } | |
563 | } | |
564 | ||
565 | Cat(s1, s2) | |
566 | register char *s1, *s2; | |
567 | { | |
568 | int len = strlen(s1) + strlen(s2) + 1; | |
569 | register char *s; | |
570 | ||
571 | nleft -= len; | |
572 | if (nleft <= 0 || ++eargc >= GAVSIZ) | |
573 | yyerror("Arguments too long"); | |
574 | eargv[eargc] = 0; | |
575 | eargv[eargc - 1] = s = malloc(len); | |
576 | if (s == NULL) | |
577 | fatal("ran out of memory\n"); | |
578 | while (*s++ = *s1++ & TRIM) | |
579 | ; | |
580 | s--; | |
581 | while (*s++ = *s2++ & TRIM) | |
582 | ; | |
583 | } | |
584 | ||
585 | addpath(c) | |
586 | char c; | |
587 | { | |
588 | ||
589 | if (pathp >= lastpathp) | |
590 | yyerror("Pathname too long"); | |
591 | else { | |
592 | *pathp++ = c & TRIM; | |
593 | *pathp = '\0'; | |
594 | } | |
595 | } | |
596 | ||
597 | /* | |
598 | * Expand file names beginning with `~' into the | |
599 | * user's home directory path name. Return a pointer in buf to the | |
600 | * part corresponding to `file'. | |
601 | */ | |
602 | char * | |
603 | exptilde(buf, file) | |
604 | char buf[]; | |
605 | register char *file; | |
606 | { | |
607 | register char *s1, *s2, *s3; | |
608 | extern char homedir[]; | |
609 | ||
610 | if (*file != '~') { | |
611 | strcpy(buf, file); | |
612 | return(buf); | |
613 | } | |
614 | if (*++file == '\0') { | |
615 | s2 = homedir; | |
616 | s3 = NULL; | |
617 | } else if (*file == '/') { | |
618 | s2 = homedir; | |
619 | s3 = file; | |
620 | } else { | |
621 | s3 = file; | |
622 | while (*s3 && *s3 != '/') | |
623 | s3++; | |
624 | if (*s3 == '/') | |
625 | *s3 = '\0'; | |
626 | else | |
627 | s3 = NULL; | |
628 | if (pw == NULL || strcmp(pw->pw_name, file) != 0) { | |
629 | if ((pw = getpwnam(file)) == NULL) { | |
630 | error("%s: unknown user name\n", file); | |
631 | if (s3 != NULL) | |
632 | *s3 = '/'; | |
633 | return(NULL); | |
634 | } | |
635 | } | |
636 | if (s3 != NULL) | |
637 | *s3 = '/'; | |
638 | s2 = pw->pw_dir; | |
639 | } | |
640 | for (s1 = buf; *s1++ = *s2++; ) | |
641 | ; | |
642 | s2 = --s1; | |
643 | if (s3 != NULL) { | |
644 | s2++; | |
645 | while (*s1++ = *s3++) | |
646 | ; | |
647 | } | |
648 | return(s2); | |
649 | } |