Commit | Line | Data |
---|---|---|
ab72ea7b | 1 | #ifndef lint |
d1dee8e8 | 2 | static char *sccsid = "@(#)expand.c 4.4 (Berkeley) 83/10/20"; |
ab72ea7b RC |
3 | #endif |
4 | ||
5 | #include "defs.h" | |
6 | ||
82572cb6 | 7 | char shchars[] = "{[*?"; |
ab72ea7b | 8 | |
82572cb6 RC |
9 | int argc; |
10 | char **argv; | |
11 | char *path, *pathp, *lastpathp; | |
12 | int nleft; | |
ab72ea7b | 13 | |
82572cb6 RC |
14 | int argcnt; |
15 | int expany; /* any expansions done? */ | |
16 | char *entp; | |
17 | char **sortbase; | |
ab72ea7b RC |
18 | |
19 | char *index(); | |
3024eb6f | 20 | struct block *copy(); |
ab72ea7b RC |
21 | |
22 | /* | |
82572cb6 | 23 | * Take a list of names and expand any macros, etc. |
ab72ea7b RC |
24 | */ |
25 | struct block * | |
82572cb6 | 26 | expand(list, noshexp) |
ab72ea7b | 27 | struct block *list; |
82572cb6 | 28 | int noshexp; |
ab72ea7b RC |
29 | { |
30 | register struct block *prev, *bp, *tp; | |
3024eb6f | 31 | register char *cp; |
ab72ea7b | 32 | register int n; |
3024eb6f | 33 | char *tail; |
ab72ea7b | 34 | int c; |
82572cb6 RC |
35 | char pathbuf[BUFSIZ]; |
36 | char *argvbuf[GAVSIZ]; | |
ab72ea7b | 37 | |
3024eb6f RC |
38 | if (debug) { |
39 | printf("expand(%x, %d)\nlist = ", list, noshexp); | |
40 | prnames(list); | |
41 | } | |
42 | ||
ab72ea7b RC |
43 | for (prev = NULL, bp = list; bp != NULL; prev = bp, bp = bp->b_next) { |
44 | again: | |
45 | cp = index(bp->b_name, '$'); | |
3024eb6f | 46 | if (cp == NULL) |
ab72ea7b RC |
47 | continue; |
48 | *cp++ = '\0'; | |
49 | if (*cp == '\0') | |
50 | fatal("no variable name after '$'"); | |
51 | if (*cp == '{') { | |
52 | cp++; | |
53 | if ((tail = index(cp, '}')) == NULL) | |
54 | fatal("missing '}'"); | |
55 | *tail++ = c = '\0'; | |
56 | if (*cp == '\0') | |
57 | fatal("no variable name after '$'"); | |
58 | } else { | |
59 | tail = cp + 1; | |
60 | c = *tail; | |
61 | *tail = '\0'; | |
62 | } | |
82572cb6 | 63 | tp = lookup(cp, NULL, 0); |
3024eb6f RC |
64 | if (c != '\0') |
65 | *tail = c; | |
ab72ea7b | 66 | if ((tp = tp->b_args) != NULL) { |
3024eb6f RC |
67 | struct block *first = bp; |
68 | ||
69 | for (bp = prev; tp != NULL; tp = tp->b_next) { | |
70 | if (bp == NULL) | |
71 | list = bp = copy(tp, first->b_name, tail); | |
72 | else { | |
73 | bp->b_next = copy(tp, first->b_name, tail); | |
74 | bp = bp->b_next; | |
75 | } | |
76 | } | |
77 | bp->b_next = first->b_next; | |
78 | free(first->b_name); | |
79 | free(first); | |
ab72ea7b | 80 | if (prev == NULL) |
3024eb6f | 81 | bp = list; |
ab72ea7b | 82 | else |
3024eb6f | 83 | bp = prev->b_next; |
ab72ea7b RC |
84 | goto again; |
85 | } else { | |
86 | if (prev == NULL) | |
82572cb6 | 87 | list = tp = bp->b_next; |
ab72ea7b RC |
88 | else |
89 | prev->b_next = tp = bp->b_next; | |
90 | free(bp->b_name); | |
91 | free(bp); | |
92 | if (tp != NULL) { | |
93 | bp = tp; | |
94 | goto again; | |
95 | } | |
96 | break; | |
97 | } | |
98 | } | |
82572cb6 RC |
99 | |
100 | if (noshexp) | |
101 | return(list); | |
102 | ||
3024eb6f RC |
103 | if (debug) { |
104 | printf("shexpand "); | |
105 | prnames(list); | |
106 | } | |
107 | ||
82572cb6 RC |
108 | path = pathp = pathbuf; |
109 | *pathp = '\0'; | |
110 | lastpathp = &path[sizeof pathbuf - 2]; | |
111 | argc = 0; | |
112 | argv = sortbase = argvbuf; | |
113 | *argv = 0; | |
114 | nleft = NCARGS - 4; | |
115 | argcnt = 0; | |
116 | for (bp = list; bp != NULL; bp = bp->b_next) | |
117 | expsh(bp->b_name); | |
118 | for (bp = list; bp != NULL; bp = tp) { | |
119 | tp = bp->b_next; | |
120 | free(bp->b_name); | |
121 | free(bp); | |
122 | } | |
123 | prev = NULL; | |
124 | for (n = 0; n < argc; n++) { | |
125 | bp = ALLOC(block); | |
126 | if (bp == NULL) | |
127 | fatal("ran out of memory\n"); | |
128 | bp->b_type = NAME; | |
129 | bp->b_next = bp->b_args = NULL; | |
130 | bp->b_name = argv[n]; | |
131 | if (prev == NULL) | |
132 | list = prev = bp; | |
133 | else { | |
134 | prev->b_next = bp; | |
135 | prev = bp; | |
136 | } | |
137 | } | |
ab72ea7b RC |
138 | return(list); |
139 | } | |
140 | ||
141 | /* | |
3024eb6f | 142 | * Return a new NAME block named "head, bp->b_name, tail" |
ab72ea7b | 143 | */ |
3024eb6f RC |
144 | struct block * |
145 | copy(bp, head, tail) | |
ab72ea7b RC |
146 | struct block *bp; |
147 | char *head, *tail; | |
148 | { | |
149 | register int n; | |
150 | register char *cp; | |
3024eb6f | 151 | register struct block *np; |
ab72ea7b | 152 | |
3024eb6f RC |
153 | np = ALLOC(block); |
154 | if (np == NULL) | |
155 | fatal("ran out of memory\n"); | |
156 | np->b_type = NAME; | |
157 | np->b_next = bp->b_args = NULL; | |
ab72ea7b | 158 | n = strlen(bp->b_name) + strlen(head) + strlen(tail) + 1; |
3024eb6f | 159 | np->b_name = cp = malloc(n); |
ab72ea7b RC |
160 | if (cp == NULL) |
161 | fatal("ran out of memory"); | |
162 | sprintf(cp, "%s%s%s", head, bp->b_name, tail); | |
3024eb6f | 163 | return(np); |
ab72ea7b RC |
164 | } |
165 | ||
166 | /* | |
167 | * If there are any Shell meta characters in the name, | |
168 | * expand into a list, after searching directory | |
ab72ea7b | 169 | */ |
82572cb6 RC |
170 | expsh(s) |
171 | register char *s; | |
172 | { | |
82572cb6 RC |
173 | register int oargc = argc; |
174 | ||
175 | if (!strcmp(s, "{") || !strcmp(s, "{}")) { | |
176 | Cat(s, ""); | |
177 | sort(); | |
178 | return; | |
179 | } | |
180 | ||
181 | pathp = path; | |
182 | *pathp = 0; | |
183 | expany = 0; | |
184 | expstr(s); | |
185 | if (argc != oargc) | |
186 | sort(); | |
187 | } | |
188 | ||
189 | /* | |
190 | * Bubble sort any new entries | |
191 | */ | |
192 | sort() | |
193 | { | |
194 | register char **p1, **p2, *c; | |
195 | char **ap = &argv[argc]; | |
196 | ||
197 | p1 = sortbase; | |
198 | while (p1 < ap-1) { | |
199 | p2 = p1; | |
200 | while (++p2 < ap) | |
201 | if (strcmp(*p1, *p2) > 0) | |
202 | c = *p1, *p1 = *p2, *p2 = c; | |
203 | p1++; | |
204 | } | |
205 | sortbase = ap; | |
206 | } | |
207 | ||
208 | expstr(s) | |
209 | char *s; | |
210 | { | |
211 | register char *cp; | |
212 | register char *spathp, *oldcp; | |
213 | struct stat stb; | |
214 | ||
215 | spathp = pathp; | |
216 | cp = s; | |
217 | while (!any(*cp, shchars)) { | |
218 | if (*cp == '\0') { | |
219 | if (!expany) | |
220 | Cat(path, ""); | |
221 | else if (stat(path, &stb) >= 0) { | |
222 | Cat(path, ""); | |
223 | argcnt++; | |
224 | } | |
225 | goto endit; | |
226 | } | |
227 | addpath(*cp++); | |
228 | } | |
229 | oldcp = cp; | |
230 | while (cp > s && *cp != '/') | |
231 | cp--, pathp--; | |
232 | if (*cp == '/') | |
233 | cp++, pathp++; | |
234 | *pathp = '\0'; | |
235 | if (*oldcp == '{') { | |
236 | execbrc(cp, NULL); | |
237 | return; | |
238 | } | |
239 | matchdir(cp); | |
240 | endit: | |
241 | pathp = spathp; | |
242 | *pathp = '\0'; | |
243 | } | |
244 | ||
245 | matchdir(pattern) | |
246 | char *pattern; | |
247 | { | |
248 | struct stat stb; | |
249 | register struct direct *dp; | |
250 | DIR *dirp; | |
82572cb6 RC |
251 | |
252 | dirp = opendir(path); | |
253 | if (dirp == NULL) { | |
254 | if (expany) | |
255 | return; | |
256 | goto patherr2; | |
257 | } | |
258 | if (fstat(dirp->dd_fd, &stb) < 0) | |
259 | goto patherr1; | |
d1dee8e8 | 260 | if (!ISDIR(stb.st_mode)) { |
82572cb6 RC |
261 | errno = ENOTDIR; |
262 | goto patherr1; | |
263 | } | |
264 | while ((dp = readdir(dirp)) != NULL) | |
265 | if (match(dp->d_name, pattern)) { | |
266 | Cat(path, dp->d_name); | |
267 | argcnt++; | |
268 | } | |
269 | closedir(dirp); | |
270 | return; | |
271 | ||
272 | patherr1: | |
273 | closedir(dirp); | |
274 | patherr2: | |
275 | fatal("%s: %s\n", path, sys_errlist[errno]); | |
276 | } | |
277 | ||
278 | execbrc(p, s) | |
279 | char *p, *s; | |
280 | { | |
281 | char restbuf[BUFSIZ + 2]; | |
282 | register char *pe, *pm, *pl; | |
283 | int brclev = 0; | |
284 | char *lm, savec, *spathp; | |
285 | ||
286 | for (lm = restbuf; *p != '{'; *lm++ = *p++) | |
287 | continue; | |
288 | for (pe = ++p; *pe; pe++) | |
289 | switch (*pe) { | |
290 | ||
291 | case '{': | |
292 | brclev++; | |
293 | continue; | |
294 | ||
295 | case '}': | |
296 | if (brclev == 0) | |
297 | goto pend; | |
298 | brclev--; | |
299 | continue; | |
300 | ||
301 | case '[': | |
302 | for (pe++; *pe && *pe != ']'; pe++) | |
303 | continue; | |
304 | if (!*pe) | |
305 | fatal("Missing ]\n"); | |
306 | continue; | |
307 | } | |
308 | pend: | |
309 | if (brclev || !*pe) | |
310 | fatal("Missing }\n"); | |
311 | for (pl = pm = p; pm <= pe; pm++) | |
312 | switch (*pm & (QUOTE|TRIM)) { | |
313 | ||
314 | case '{': | |
315 | brclev++; | |
316 | continue; | |
317 | ||
318 | case '}': | |
319 | if (brclev) { | |
320 | brclev--; | |
321 | continue; | |
322 | } | |
323 | goto doit; | |
324 | ||
325 | case ',': | |
326 | if (brclev) | |
327 | continue; | |
328 | doit: | |
329 | savec = *pm; | |
330 | *pm = 0; | |
331 | strcpy(lm, pl); | |
332 | strcat(restbuf, pe + 1); | |
333 | *pm = savec; | |
334 | if (s == 0) { | |
335 | spathp = pathp; | |
336 | expstr(restbuf); | |
337 | pathp = spathp; | |
338 | *pathp = 0; | |
339 | } else if (amatch(s, restbuf)) | |
340 | return (1); | |
341 | sort(); | |
342 | pl = pm + 1; | |
343 | continue; | |
344 | ||
345 | case '[': | |
346 | for (pm++; *pm && *pm != ']'; pm++) | |
347 | continue; | |
348 | if (!*pm) | |
349 | fatal("Missing ]\n"); | |
350 | continue; | |
351 | } | |
352 | return (0); | |
353 | } | |
354 | ||
355 | match(s, p) | |
356 | char *s, *p; | |
357 | { | |
358 | register int c; | |
359 | register char *sentp; | |
360 | char sexpany = expany; | |
361 | ||
362 | if (*s == '.' && *p != '.') | |
363 | return (0); | |
364 | sentp = entp; | |
365 | entp = s; | |
366 | c = amatch(s, p); | |
367 | entp = sentp; | |
368 | expany = sexpany; | |
369 | return (c); | |
370 | } | |
371 | ||
372 | amatch(s, p) | |
373 | register char *s, *p; | |
374 | { | |
375 | register int scc; | |
376 | int ok, lc; | |
377 | char *spathp; | |
378 | struct stat stb; | |
379 | int c, cc; | |
380 | ||
381 | expany = 1; | |
382 | for (;;) { | |
383 | scc = *s++ & TRIM; | |
384 | switch (c = *p++) { | |
385 | ||
386 | case '{': | |
387 | return (execbrc(p - 1, s - 1)); | |
388 | ||
389 | case '[': | |
390 | ok = 0; | |
391 | lc = 077777; | |
392 | while (cc = *p++) { | |
393 | if (cc == ']') { | |
394 | if (ok) | |
395 | break; | |
396 | return (0); | |
397 | } | |
398 | if (cc == '-') { | |
399 | if (lc <= scc && scc <= *p++) | |
400 | ok++; | |
401 | } else | |
402 | if (scc == (lc = cc)) | |
403 | ok++; | |
404 | } | |
405 | if (cc == 0) | |
406 | fatal("Missing ]\n"); | |
407 | continue; | |
408 | ||
409 | case '*': | |
410 | if (!*p) | |
411 | return (1); | |
412 | if (*p == '/') { | |
413 | p++; | |
414 | goto slash; | |
415 | } | |
416 | for (s--; *s; s++) | |
417 | if (amatch(s, p)) | |
418 | return (1); | |
419 | return (0); | |
420 | ||
421 | case '\0': | |
422 | return (scc == '\0'); | |
423 | ||
424 | default: | |
425 | if (c != scc) | |
426 | return (0); | |
427 | continue; | |
428 | ||
429 | case '?': | |
430 | if (scc == '\0') | |
431 | return (0); | |
432 | continue; | |
433 | ||
434 | case '/': | |
435 | if (scc) | |
436 | return (0); | |
437 | slash: | |
438 | s = entp; | |
439 | spathp = pathp; | |
440 | while (*s) | |
441 | addpath(*s++); | |
442 | addpath('/'); | |
d1dee8e8 | 443 | if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) |
82572cb6 RC |
444 | if (*p == '\0') { |
445 | Cat(path, ""); | |
446 | argcnt++; | |
447 | } else | |
448 | expstr(p); | |
449 | pathp = spathp; | |
450 | *pathp = '\0'; | |
451 | return (0); | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | smatch(s, p) | |
457 | register char *s, *p; | |
458 | { | |
459 | register int scc; | |
460 | int ok, lc; | |
461 | int c, cc; | |
462 | ||
463 | for (;;) { | |
464 | scc = *s++ & TRIM; | |
465 | switch (c = *p++) { | |
466 | ||
467 | case '[': | |
468 | ok = 0; | |
469 | lc = 077777; | |
470 | while (cc = *p++) { | |
471 | if (cc == ']') { | |
472 | if (ok) | |
473 | break; | |
474 | return (0); | |
475 | } | |
476 | if (cc == '-') { | |
477 | if (lc <= scc && scc <= *p++) | |
478 | ok++; | |
479 | } else | |
480 | if (scc == (lc = cc)) | |
481 | ok++; | |
482 | } | |
483 | if (cc == 0) | |
484 | fatal("Missing ]\n"); | |
485 | continue; | |
486 | ||
487 | case '*': | |
488 | if (!*p) | |
489 | return (1); | |
490 | for (s--; *s; s++) | |
491 | if (smatch(s, p)) | |
492 | return (1); | |
493 | return (0); | |
494 | ||
495 | case '\0': | |
496 | return (scc == '\0'); | |
497 | ||
498 | default: | |
499 | if ((c & TRIM) != scc) | |
500 | return (0); | |
501 | continue; | |
502 | ||
503 | case '?': | |
504 | if (scc == 0) | |
505 | return (0); | |
506 | continue; | |
507 | ||
508 | } | |
509 | } | |
510 | } | |
511 | ||
512 | Cat(s1, s2) | |
513 | register char *s1, *s2; | |
514 | { | |
515 | int len = strlen(s1) + strlen(s2) + 1; | |
3024eb6f | 516 | register char *s; |
82572cb6 RC |
517 | |
518 | nleft -= len; | |
519 | if (nleft <= 0 || ++argc >= GAVSIZ) | |
520 | fatal("Arguments too long\n"); | |
521 | argv[argc] = 0; | |
3024eb6f | 522 | argv[argc - 1] = s = malloc(len); |
82572cb6 RC |
523 | if (s == NULL) |
524 | fatal("ran out of memory\n"); | |
525 | while (*s++ = *s1++ & TRIM) | |
526 | ; | |
527 | s--; | |
528 | while (*s++ = *s2++ & TRIM) | |
529 | ; | |
530 | } | |
531 | ||
532 | addpath(c) | |
533 | char c; | |
534 | { | |
535 | ||
536 | if (pathp >= lastpathp) | |
537 | fatal("Pathname too long\n"); | |
538 | *pathp++ = c; | |
539 | *pathp = '\0'; | |
540 | } | |
541 | ||
542 | /* | |
543 | * Expand file names beginning with `~' into the | |
3024eb6f RC |
544 | * user's home directory path name. Return a pointer in buf to the |
545 | * part corresponding to `file'. | |
82572cb6 | 546 | */ |
3024eb6f | 547 | char * |
82572cb6 | 548 | exptilde(buf, file) |
ab72ea7b RC |
549 | char buf[]; |
550 | register char *file; | |
551 | { | |
552 | register char *s1, *s2, *s3; | |
553 | register struct passwd *pw; | |
554 | extern char *homedir; | |
555 | ||
556 | if (*file != '~') { | |
557 | strcpy(buf, file); | |
3024eb6f | 558 | return(buf); |
ab72ea7b RC |
559 | } |
560 | file++; | |
561 | if (*file == '\0' || *file == '/') { | |
562 | s2 = homedir; | |
563 | s3 = file; | |
564 | } else { | |
565 | for (s3 = file; *s3 && *s3 != '/'; s3++) | |
566 | ; | |
567 | if (*s3 == '/') | |
568 | *s3 = '\0'; | |
569 | else | |
570 | s3 = NULL; | |
ab72ea7b RC |
571 | pw = getpwnam(file); |
572 | if (pw == NULL) { | |
3024eb6f | 573 | error("unknown user %s\n", file); |
ab72ea7b RC |
574 | if (s3 != NULL) |
575 | *s3 = '/'; | |
3024eb6f | 576 | return(NULL); |
ab72ea7b RC |
577 | } |
578 | if (s3 != NULL) | |
579 | *s3 = '/'; | |
580 | s2 = pw->pw_dir; | |
581 | } | |
582 | for (s1 = buf; *s1++ = *s2++; ) | |
583 | ; | |
d1dee8e8 RC |
584 | s2 = --s1; |
585 | if (s3 != NULL) | |
586 | while (*s1++ = *s3++) | |
587 | ; | |
588 | return(s2); | |
ab72ea7b | 589 | } |