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