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