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