Commit | Line | Data |
---|---|---|
ab72ea7b | 1 | #ifndef lint |
9592064e | 2 | static char *sccsid = "@(#)expand.c 4.11 (Berkeley) 84/12/06"; |
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); |
9592064e RC |
182 | if (eargc == oeargc) |
183 | Cat(s, ""); /* "nonomatch" is set */ | |
184 | sort(); | |
82572cb6 RC |
185 | } |
186 | ||
187 | /* | |
188 | * Bubble sort any new entries | |
189 | */ | |
190 | sort() | |
191 | { | |
192 | register char **p1, **p2, *c; | |
0fccdfef | 193 | char **ap = &eargv[eargc]; |
82572cb6 RC |
194 | |
195 | p1 = sortbase; | |
196 | while (p1 < ap-1) { | |
197 | p2 = p1; | |
198 | while (++p2 < ap) | |
199 | if (strcmp(*p1, *p2) > 0) | |
200 | c = *p1, *p1 = *p2, *p2 = c; | |
201 | p1++; | |
202 | } | |
203 | sortbase = ap; | |
204 | } | |
205 | ||
d6bccb44 RC |
206 | /* |
207 | * If there are any Shell meta characters in the name, | |
208 | * expand into a list, after searching directory | |
209 | */ | |
210 | expsh(s) | |
82572cb6 RC |
211 | char *s; |
212 | { | |
213 | register char *cp; | |
214 | register char *spathp, *oldcp; | |
215 | struct stat stb; | |
216 | ||
217 | spathp = pathp; | |
218 | cp = s; | |
219 | while (!any(*cp, shchars)) { | |
220 | if (*cp == '\0') { | |
d6bccb44 RC |
221 | if (!expany || stat(path, &stb) >= 0) { |
222 | if (which & E_TILDE) | |
223 | Cat(path, ""); | |
224 | else | |
225 | Cat(tilde, tpathp); | |
82572cb6 RC |
226 | } |
227 | goto endit; | |
228 | } | |
229 | addpath(*cp++); | |
230 | } | |
231 | oldcp = cp; | |
232 | while (cp > s && *cp != '/') | |
233 | cp--, pathp--; | |
234 | if (*cp == '/') | |
235 | cp++, pathp++; | |
236 | *pathp = '\0'; | |
237 | if (*oldcp == '{') { | |
238 | execbrc(cp, NULL); | |
239 | return; | |
240 | } | |
241 | matchdir(cp); | |
242 | endit: | |
243 | pathp = spathp; | |
244 | *pathp = '\0'; | |
245 | } | |
246 | ||
247 | matchdir(pattern) | |
248 | char *pattern; | |
249 | { | |
250 | struct stat stb; | |
251 | register struct direct *dp; | |
252 | DIR *dirp; | |
82572cb6 RC |
253 | |
254 | dirp = opendir(path); | |
255 | if (dirp == NULL) { | |
256 | if (expany) | |
257 | return; | |
258 | goto patherr2; | |
259 | } | |
260 | if (fstat(dirp->dd_fd, &stb) < 0) | |
261 | goto patherr1; | |
d1dee8e8 | 262 | if (!ISDIR(stb.st_mode)) { |
82572cb6 RC |
263 | errno = ENOTDIR; |
264 | goto patherr1; | |
265 | } | |
266 | while ((dp = readdir(dirp)) != NULL) | |
267 | if (match(dp->d_name, pattern)) { | |
d6bccb44 RC |
268 | if (which & E_TILDE) |
269 | Cat(path, dp->d_name); | |
270 | else { | |
271 | strcpy(pathp, dp->d_name); | |
272 | Cat(tilde, tpathp); | |
273 | *pathp = '\0'; | |
274 | } | |
82572cb6 RC |
275 | } |
276 | closedir(dirp); | |
277 | return; | |
278 | ||
279 | patherr1: | |
280 | closedir(dirp); | |
281 | patherr2: | |
d6bccb44 | 282 | error("%s: %s\n", path, sys_errlist[errno]); |
82572cb6 RC |
283 | } |
284 | ||
285 | execbrc(p, s) | |
286 | char *p, *s; | |
287 | { | |
288 | char restbuf[BUFSIZ + 2]; | |
289 | register char *pe, *pm, *pl; | |
290 | int brclev = 0; | |
291 | char *lm, savec, *spathp; | |
292 | ||
293 | for (lm = restbuf; *p != '{'; *lm++ = *p++) | |
294 | continue; | |
295 | for (pe = ++p; *pe; pe++) | |
296 | switch (*pe) { | |
297 | ||
298 | case '{': | |
299 | brclev++; | |
300 | continue; | |
301 | ||
302 | case '}': | |
303 | if (brclev == 0) | |
304 | goto pend; | |
305 | brclev--; | |
306 | continue; | |
307 | ||
308 | case '[': | |
309 | for (pe++; *pe && *pe != ']'; pe++) | |
310 | continue; | |
311 | if (!*pe) | |
d6bccb44 | 312 | error("Missing ]\n"); |
82572cb6 RC |
313 | continue; |
314 | } | |
315 | pend: | |
316 | if (brclev || !*pe) | |
317 | fatal("Missing }\n"); | |
318 | for (pl = pm = p; pm <= pe; pm++) | |
319 | switch (*pm & (QUOTE|TRIM)) { | |
320 | ||
321 | case '{': | |
322 | brclev++; | |
323 | continue; | |
324 | ||
325 | case '}': | |
326 | if (brclev) { | |
327 | brclev--; | |
328 | continue; | |
329 | } | |
330 | goto doit; | |
331 | ||
332 | case ',': | |
333 | if (brclev) | |
334 | continue; | |
335 | doit: | |
336 | savec = *pm; | |
337 | *pm = 0; | |
338 | strcpy(lm, pl); | |
339 | strcat(restbuf, pe + 1); | |
340 | *pm = savec; | |
341 | if (s == 0) { | |
342 | spathp = pathp; | |
d6bccb44 | 343 | expsh(restbuf); |
82572cb6 RC |
344 | pathp = spathp; |
345 | *pathp = 0; | |
346 | } else if (amatch(s, restbuf)) | |
347 | return (1); | |
348 | sort(); | |
349 | pl = pm + 1; | |
350 | continue; | |
351 | ||
352 | case '[': | |
353 | for (pm++; *pm && *pm != ']'; pm++) | |
354 | continue; | |
355 | if (!*pm) | |
d6bccb44 | 356 | error("Missing ]\n"); |
82572cb6 RC |
357 | continue; |
358 | } | |
359 | return (0); | |
360 | } | |
361 | ||
362 | match(s, p) | |
363 | char *s, *p; | |
364 | { | |
365 | register int c; | |
366 | register char *sentp; | |
367 | char sexpany = expany; | |
368 | ||
369 | if (*s == '.' && *p != '.') | |
370 | return (0); | |
371 | sentp = entp; | |
372 | entp = s; | |
373 | c = amatch(s, p); | |
374 | entp = sentp; | |
375 | expany = sexpany; | |
376 | return (c); | |
377 | } | |
378 | ||
379 | amatch(s, p) | |
380 | register char *s, *p; | |
381 | { | |
382 | register int scc; | |
383 | int ok, lc; | |
384 | char *spathp; | |
385 | struct stat stb; | |
386 | int c, cc; | |
387 | ||
388 | expany = 1; | |
389 | for (;;) { | |
390 | scc = *s++ & TRIM; | |
391 | switch (c = *p++) { | |
392 | ||
393 | case '{': | |
394 | return (execbrc(p - 1, s - 1)); | |
395 | ||
396 | case '[': | |
397 | ok = 0; | |
398 | lc = 077777; | |
399 | while (cc = *p++) { | |
400 | if (cc == ']') { | |
401 | if (ok) | |
402 | break; | |
403 | return (0); | |
404 | } | |
405 | if (cc == '-') { | |
406 | if (lc <= scc && scc <= *p++) | |
407 | ok++; | |
408 | } else | |
409 | if (scc == (lc = cc)) | |
410 | ok++; | |
411 | } | |
412 | if (cc == 0) | |
413 | fatal("Missing ]\n"); | |
414 | continue; | |
415 | ||
416 | case '*': | |
417 | if (!*p) | |
418 | return (1); | |
419 | if (*p == '/') { | |
420 | p++; | |
421 | goto slash; | |
422 | } | |
423 | for (s--; *s; s++) | |
424 | if (amatch(s, p)) | |
425 | return (1); | |
426 | return (0); | |
427 | ||
428 | case '\0': | |
429 | return (scc == '\0'); | |
430 | ||
431 | default: | |
432 | if (c != scc) | |
433 | return (0); | |
434 | continue; | |
435 | ||
436 | case '?': | |
437 | if (scc == '\0') | |
438 | return (0); | |
439 | continue; | |
440 | ||
441 | case '/': | |
442 | if (scc) | |
443 | return (0); | |
444 | slash: | |
445 | s = entp; | |
446 | spathp = pathp; | |
447 | while (*s) | |
448 | addpath(*s++); | |
449 | addpath('/'); | |
d1dee8e8 | 450 | if (stat(path, &stb) == 0 && ISDIR(stb.st_mode)) |
82572cb6 | 451 | if (*p == '\0') { |
d6bccb44 RC |
452 | if (which & E_TILDE) |
453 | Cat(path, ""); | |
454 | else | |
455 | Cat(tilde, tpathp); | |
82572cb6 | 456 | } else |
d6bccb44 | 457 | expsh(p); |
82572cb6 RC |
458 | pathp = spathp; |
459 | *pathp = '\0'; | |
460 | return (0); | |
461 | } | |
462 | } | |
463 | } | |
464 | ||
465 | smatch(s, p) | |
466 | register char *s, *p; | |
467 | { | |
468 | register int scc; | |
469 | int ok, lc; | |
470 | int c, cc; | |
471 | ||
472 | for (;;) { | |
473 | scc = *s++ & TRIM; | |
474 | switch (c = *p++) { | |
475 | ||
476 | case '[': | |
477 | ok = 0; | |
478 | lc = 077777; | |
479 | while (cc = *p++) { | |
480 | if (cc == ']') { | |
481 | if (ok) | |
482 | break; | |
483 | return (0); | |
484 | } | |
485 | if (cc == '-') { | |
486 | if (lc <= scc && scc <= *p++) | |
487 | ok++; | |
488 | } else | |
489 | if (scc == (lc = cc)) | |
490 | ok++; | |
491 | } | |
492 | if (cc == 0) | |
493 | fatal("Missing ]\n"); | |
494 | continue; | |
495 | ||
496 | case '*': | |
497 | if (!*p) | |
498 | return (1); | |
499 | for (s--; *s; s++) | |
500 | if (smatch(s, p)) | |
501 | return (1); | |
502 | return (0); | |
503 | ||
504 | case '\0': | |
505 | return (scc == '\0'); | |
506 | ||
507 | default: | |
508 | if ((c & TRIM) != scc) | |
509 | return (0); | |
510 | continue; | |
511 | ||
512 | case '?': | |
513 | if (scc == 0) | |
514 | return (0); | |
515 | continue; | |
516 | ||
517 | } | |
518 | } | |
519 | } | |
520 | ||
521 | Cat(s1, s2) | |
522 | register char *s1, *s2; | |
523 | { | |
524 | int len = strlen(s1) + strlen(s2) + 1; | |
3024eb6f | 525 | register char *s; |
82572cb6 RC |
526 | |
527 | nleft -= len; | |
0fccdfef | 528 | if (nleft <= 0 || ++eargc >= GAVSIZ) |
d6bccb44 | 529 | error("Arguments too long\n"); |
0fccdfef RC |
530 | eargv[eargc] = 0; |
531 | eargv[eargc - 1] = s = malloc(len); | |
82572cb6 RC |
532 | if (s == NULL) |
533 | fatal("ran out of memory\n"); | |
534 | while (*s++ = *s1++ & TRIM) | |
535 | ; | |
536 | s--; | |
537 | while (*s++ = *s2++ & TRIM) | |
538 | ; | |
539 | } | |
540 | ||
541 | addpath(c) | |
542 | char c; | |
543 | { | |
544 | ||
545 | if (pathp >= lastpathp) | |
546 | fatal("Pathname too long\n"); | |
547 | *pathp++ = c; | |
548 | *pathp = '\0'; | |
549 | } | |
550 | ||
551 | /* | |
552 | * Expand file names beginning with `~' into the | |
3024eb6f RC |
553 | * user's home directory path name. Return a pointer in buf to the |
554 | * part corresponding to `file'. | |
82572cb6 | 555 | */ |
3024eb6f | 556 | char * |
82572cb6 | 557 | exptilde(buf, file) |
ab72ea7b RC |
558 | char buf[]; |
559 | register char *file; | |
560 | { | |
561 | register char *s1, *s2, *s3; | |
e8109cf8 | 562 | extern char homedir[]; |
ab72ea7b RC |
563 | |
564 | if (*file != '~') { | |
565 | strcpy(buf, file); | |
3024eb6f | 566 | return(buf); |
ab72ea7b | 567 | } |
e8109cf8 RC |
568 | if (*++file == '\0') { |
569 | s2 = homedir; | |
570 | s3 = NULL; | |
571 | } else if (*file == '/') { | |
ab72ea7b RC |
572 | s2 = homedir; |
573 | s3 = file; | |
574 | } else { | |
e317e93f | 575 | s3 = file; |
e8109cf8 RC |
576 | while (*s3 && *s3 != '/') |
577 | s3++; | |
ab72ea7b RC |
578 | if (*s3 == '/') |
579 | *s3 = '\0'; | |
580 | else | |
581 | s3 = NULL; | |
e8109cf8 RC |
582 | if (pw == NULL || strcmp(pw->pw_name, file) != 0) { |
583 | if ((pw = getpwnam(file)) == NULL) { | |
584 | error("unknown user %s\n", file); | |
585 | if (s3 != NULL) | |
586 | *s3 = '/'; | |
587 | return(NULL); | |
588 | } | |
ab72ea7b RC |
589 | } |
590 | if (s3 != NULL) | |
591 | *s3 = '/'; | |
592 | s2 = pw->pw_dir; | |
593 | } | |
594 | for (s1 = buf; *s1++ = *s2++; ) | |
595 | ; | |
d1dee8e8 | 596 | s2 = --s1; |
e8109cf8 RC |
597 | if (s3 != NULL) { |
598 | s2++; | |
d1dee8e8 RC |
599 | while (*s1++ = *s3++) |
600 | ; | |
e8109cf8 | 601 | } |
d1dee8e8 | 602 | return(s2); |
ab72ea7b | 603 | } |