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