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