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