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