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