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