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