1 /*
2 * Copyright (c) 1980 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 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 #if 0
19 #ifndef lint
20 static char sccsid[] = "@(#)glob.c 5.7 (Berkeley) 12/14/88";
21 #endif /* not lint */
22 #endif
23
24 #if HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 /*
29 * C-shell glob for random programs.
30 */
31
32
33 #include <sys/param.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <string.h>
40 #include <stdio.h>
41 #include <errno.h>
42 #include <pwd.h>
43
44 #if !defined(dirfd) && !defined(__GLIBC__) && !defined(__linux__) && \
45 !defined(__FreeBSD__) && !defined(__CYGWIN__) && !defined(__APPLE__) && \
46 !defined(__DragonFly__)
47 #define dirfd(dirp) ((dirp)->dd_fd)
48 #endif
49
50 #ifndef NCARGS
51 #define NCARGS 10240
52 #endif
53
54 #define QUOTE 0200
55 #define TRIM 0177
56 #define eq(a,b) (strcmp(a, b)==0)
57 #define GAVSIZ (NCARGS/6)
58 #define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
59
60 static char **gargv; /* Pointer to the (stack) arglist */
61 static int gargc; /* Number args in gargv */
62 static int gnleft;
63 static short gflag;
64 static int tglob();
65 char **glob();
66 static void rscan(char **, int ());
67 static void ginit(char **);
68 static void collect(char *);
69 static void acollect(char *);
70 static void sort();
71 static void expand(char *);
72 static void matchdir(char *);
73 static int execbrc(char *, char *);
74 static int match(char *, char *);
75 static int amatch(char *, char *);
76 #if 0
77 static int Gmatch(char *, char *);
78 #endif
79 static void Gcat(char *, char *);
80 static void addpath(char);
81 static void fatal(char *);
82 /*static void scan(char **, int ());*/
83 char *globerr;
84 char *home;
85 struct passwd *getpwnam();
86 extern int errno;
87 static char *strspl(), *strend();
88 /*char *malloc(), *strcpy(), *strcat(); */
89 char **copyblk();
90
91 static int globcnt;
92
93 char *globchars = "`{[*?";
94
95 static char *gpath, *gpathp, *lastgpathp;
96 static int globbed;
97 static char *entp;
98 static char **sortbas;
99
100 void blkfree();
101 int letter();
102 int digit();
103 int gethdir();
104 int any();
105
106
107 char **
glob(v)108 glob(v)
109 char *v;
110 {
111 char agpath[BUFSIZ];
112 char *agargv[GAVSIZ];
113 char *vv[2];
114 vv[0] = v;
115 vv[1] = 0;
116 gflag = 0;
117 rscan(vv, tglob);
118 if (gflag == 0)
119 return (copyblk(vv));
120
121 globerr = 0;
122 gpath = agpath; gpathp = gpath; *gpathp = 0;
123 lastgpathp = &gpath[sizeof agpath - 2];
124 ginit(agargv); globcnt = 0;
125 collect(v);
126 if (globcnt == 0 && (gflag&1)) {
127 blkfree(gargv), gargv = 0;
128 return (0);
129 } else
130 return (gargv = copyblk(gargv));
131 }
132
133 static void
ginit(agargv)134 ginit(agargv)
135 char **agargv;
136 {
137
138 agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
139 gnleft = NCARGS - 4;
140 }
141
142 static void
collect(as)143 collect(as)
144 char *as;
145 {
146 if (eq(as, "{") || eq(as, "{}")) {
147 Gcat(as, "");
148 sort();
149 } else
150 acollect(as);
151 }
152
153 static void
acollect(as)154 acollect(as)
155 char *as;
156 {
157 int ogargc = gargc;
158
159 gpathp = gpath; *gpathp = 0; globbed = 0;
160 expand(as);
161 if (gargc != ogargc)
162 sort();
163 }
164
165 static void
sort()166 sort()
167 {
168 char **p1, **p2, *c;
169 char **Gvp = &gargv[gargc];
170
171 p1 = sortbas;
172 while (p1 < Gvp-1) {
173 p2 = p1;
174 while (++p2 < Gvp)
175 if (strcmp(*p1, *p2) > 0)
176 c = *p1, *p1 = *p2, *p2 = c;
177 p1++;
178 }
179 sortbas = Gvp;
180 }
181
182 static void
expand(as)183 expand(as)
184 char *as;
185 {
186 char *cs;
187 char *sgpathp, *oldcs;
188 struct stat stb;
189
190 sgpathp = gpathp;
191 cs = as;
192 if (*cs == '~' && gpathp == gpath) {
193 addpath('~');
194 for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
195 addpath(*cs++);
196 if (!*cs || *cs == '/') {
197 if (gpathp != gpath + 1) {
198 *gpathp = 0;
199 if (gethdir(gpath + 1))
200 globerr = "Unknown user name after ~";
201 (void) strcpy(gpath, gpath + 1);
202 } else {
203 if(home == NULL) {
204 struct passwd *pp;
205 extern char *getenv();
206
207 home = getenv("HOME");
208 if(home == NULL) {
209 pp = getpwuid(getuid());
210 if(pp != NULL)
211 home = pp->pw_dir;
212 }
213 }
214 if(home == NULL) {
215 home = "";
216 globerr = "Can't determine own home directory";
217 } else {
218 (void) strcpy(gpath, home);
219 }
220 }
221 gpathp = strend(gpath);
222 }
223 }
224 while (!any(*cs, globchars)) {
225 if (*cs == 0) {
226 if (!globbed)
227 Gcat(gpath, "");
228 else if (stat(gpath, &stb) >= 0) {
229 Gcat(gpath, "");
230 globcnt++;
231 }
232 goto endit;
233 }
234 addpath(*cs++);
235 }
236 oldcs = cs;
237 while (cs > as && *cs != '/')
238 cs--, gpathp--;
239 if (*cs == '/')
240 cs++, gpathp++;
241 *gpathp = 0;
242 if (*oldcs == '{') {
243 (void) execbrc(cs, ((char *)0));
244 return;
245 }
246 matchdir(cs);
247 endit:
248 gpathp = sgpathp;
249 *gpathp = 0;
250 }
251
252 static void
matchdir(pattern)253 matchdir(pattern)
254 char *pattern;
255 {
256 struct stat stb;
257 struct dirent *dp;
258 DIR *dirp;
259
260 dirp = opendir(gpath[0] == '\0' ? "." : gpath);
261 if (dirp == NULL) {
262 if (globbed)
263 return;
264 goto patherr2;
265 }
266 if (fstat(dirfd(dirp), &stb) < 0)
267 goto patherr1;
268 if (!isdir(stb)) {
269 errno = ENOTDIR;
270 goto patherr1;
271 }
272 while ((dp = readdir(dirp)) != NULL) {
273 if (dp->d_ino == 0)
274 continue;
275 if (match(dp->d_name, pattern)) {
276 Gcat(gpath, dp->d_name);
277 globcnt++;
278 }
279 }
280 closedir(dirp);
281 return;
282
283 patherr1:
284 closedir(dirp);
285 patherr2:
286 globerr = "Bad directory components";
287 }
288
289 static int
execbrc(p,s)290 execbrc(p, s)
291 char *p, *s;
292 {
293 char restbuf[BUFSIZ + 2];
294 char *pe, *pm, *pl;
295 int brclev = 0;
296 char *lm, savec, *sgpathp;
297
298 for (lm = restbuf; *p != '{'; *lm++ = *p++)
299 continue;
300 for (pe = ++p; *pe; pe++)
301 switch (*pe) {
302
303 case '{':
304 brclev++;
305 continue;
306
307 case '}':
308 if (brclev == 0)
309 goto pend;
310 brclev--;
311 continue;
312
313 case '[':
314 for (pe++; *pe && *pe != ']'; pe++)
315 continue;
316 continue;
317 }
318 pend:
319 brclev = 0;
320 for (pl = pm = p; pm <= pe; pm++)
321 switch (*pm & (QUOTE|TRIM)) {
322
323 case '{':
324 brclev++;
325 continue;
326
327 case '}':
328 if (brclev) {
329 brclev--;
330 continue;
331 }
332 goto doit;
333
334 case ','|QUOTE:
335 case ',':
336 if (brclev)
337 continue;
338 doit:
339 savec = *pm;
340 *pm = 0;
341 (void) strcpy(lm, pl);
342 (void) strcat(restbuf, pe + 1);
343 *pm = savec;
344 if (s == 0) {
345 sgpathp = gpathp;
346 expand(restbuf);
347 gpathp = sgpathp;
348 *gpathp = 0;
349 } else if (amatch(s, restbuf))
350 return (1);
351 sort();
352 pl = pm + 1;
353 if (brclev)
354 return (0);
355 continue;
356
357 case '[':
358 for (pm++; *pm && *pm != ']'; pm++)
359 continue;
360 if (!*pm)
361 pm--;
362 continue;
363 }
364 if (brclev)
365 goto doit;
366 return (0);
367 }
368
369 static int
match(s,p)370 match(s, p)
371 char *s, *p;
372 {
373 int c;
374 char *sentp;
375 char sglobbed = globbed;
376
377 if (*s == '.' && *p != '.')
378 return (0);
379 sentp = entp;
380 entp = s;
381 c = amatch(s, p);
382 entp = sentp;
383 globbed = sglobbed;
384 return (c);
385 }
386
387 static int
amatch(s,p)388 amatch(s, p)
389 char *s, *p;
390 {
391 int scc;
392 int ok, lc;
393 char *sgpathp;
394 struct stat stb;
395 int c, cc;
396
397 globbed = 1;
398 for (;;) {
399 scc = *s++ & TRIM;
400 switch (c = *p++) {
401
402 case '{':
403 return (execbrc(p - 1, s - 1));
404
405 case '[':
406 ok = 0;
407 lc = 077777;
408 while ((cc = *p++)) {
409 if (cc == ']') {
410 if (ok)
411 break;
412 return (0);
413 }
414 if (cc == '-') {
415 if (lc <= scc && scc <= *p++)
416 ok++;
417 } else
418 if (scc == (lc = cc))
419 ok++;
420 }
421 if ((cc == 0)) {
422 if (ok) {
423 p--;
424 } else {
425 return 0;
426 }
427 }
428 continue;
429
430 case '*':
431 if (!*p)
432 return (1);
433 if (*p == '/') {
434 p++;
435 goto slash;
436 }
437 s--;
438 do {
439 if (amatch(s, p))
440 return (1);
441 } while (*s++);
442 return (0);
443
444 case 0:
445 return (scc == 0);
446
447 default:
448 if (c != scc)
449 return (0);
450 continue;
451
452 case '?':
453 if (scc == 0)
454 return (0);
455 continue;
456
457 case '/':
458 if (scc)
459 return (0);
460 slash:
461 s = entp;
462 sgpathp = gpathp;
463 while (*s)
464 addpath(*s++);
465 addpath('/');
466 if (stat(gpath, &stb) == 0 && isdir(stb)) {
467 if (*p == 0) {
468 Gcat(gpath, "");
469 globcnt++;
470 } else {
471 expand(p);
472 }
473 }
474 gpathp = sgpathp;
475 *gpathp = 0;
476 return (0);
477 }
478 }
479 }
480
481 #if 0
482 static int
483 Gmatch(s, p)
484 char *s, *p;
485 {
486 int scc;
487 int ok, lc;
488 int c, cc;
489
490 for (;;) {
491 scc = *s++ & TRIM;
492 switch (c = *p++) {
493
494 case '[':
495 ok = 0;
496 lc = 077777;
497 while ((cc = *p++)) {
498 if (cc == ']') {
499 if (ok)
500 break;
501 return (0);
502 }
503 if (cc == '-') {
504 if (lc <= scc && scc <= *p++)
505 ok++;
506 } else
507 if (scc == (lc = cc))
508 ok++;
509 }
510 if ((cc == 0)) {
511 if (ok) {
512 p--;
513 } else {
514 return 0;
515 }
516 }
517 continue;
518
519 case '*':
520 if (!*p)
521 return (1);
522 for (s--; *s; s++)
523 if (Gmatch(s, p))
524 return (1);
525 return (0);
526
527 case 0:
528 return (scc == 0);
529
530 default:
531 if ((c & TRIM) != scc)
532 return (0);
533 continue;
534
535 case '?':
536 if (scc == 0)
537 return (0);
538 continue;
539
540 }
541 }
542 }
543 #endif
544
545 static void
Gcat(s1,s2)546 Gcat(s1, s2)
547 char *s1, *s2;
548 {
549 int len = strlen(s1) + strlen(s2) + 1;
550
551 if (len >= gnleft || gargc >= GAVSIZ - 1)
552 globerr = "Arguments too long";
553 else {
554 gargc++;
555 gnleft -= len;
556 gargv[gargc] = 0;
557 gargv[gargc - 1] = strspl(s1, s2);
558 }
559 }
560
561 static void
addpath(char c)562 addpath(char c)
563 {
564
565 if (gpathp >= lastgpathp)
566 globerr = "Pathname too long";
567 else {
568 *gpathp++ = c;
569 *gpathp = 0;
570 }
571 }
572
573 static void
rscan(t,f)574 rscan(t, f)
575 char **t;
576 int (*f)();
577 {
578 char *p, c;
579
580 while ((p = *t++)) {
581 if (f == tglob) {
582 if (*p == '~') {
583 gflag |= 2;
584 } else if (eq(p, "{") || eq(p, "{}")) {
585 continue;
586 }
587 }
588 while ((c = *p++))
589 (*f)(c);
590 }
591 }
592 /*
593 static void
594 scan(t, f)
595 char **t;
596 int (*f)();
597 {
598 char *p, c;
599
600 while (p = *t++)
601 while (c = *p)
602 *p++ = (*f)(c);
603 } */
604
605 static int
tglob(c)606 tglob(c)
607 char c;
608 {
609
610 if (any(c, globchars))
611 gflag |= c == '{' ? 2 : 1;
612 return (c);
613 }
614 /*
615 static void
616 trim(c)
617 char c;
618 {
619
620 return (c & TRIM);
621 } */
622
623
624 int
letter(c)625 letter(c)
626 char c;
627 {
628
629 return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
630 }
631
632 int
digit(c)633 digit(c)
634 char c;
635 {
636
637 return (c >= '0' && c <= '9');
638 }
639
640 int
any(c,s)641 any(c, s)
642 int c;
643 char *s;
644 {
645
646 while (*s)
647 if (*s++ == c)
648 return(1);
649 return(0);
650 }
651
652 int
blklen(av)653 blklen(av)
654 char **av;
655 {
656 int i = 0;
657
658 while (*av++)
659 i++;
660 return (i);
661 }
662
663 char **
blkcpy(oav,bv)664 blkcpy(oav, bv)
665 char **oav;
666 char **bv;
667 {
668 char **av = oav;
669
670 while ((*av++ = *bv++))
671 continue;
672 return (oav);
673 }
674
675 void
blkfree(av0)676 blkfree(av0)
677 char **av0;
678 {
679 char **av = av0;
680
681 while (*av)
682 free(*av++);
683 }
684
685 static
686 char *
strspl(cp,dp)687 strspl(cp, dp)
688 char *cp, *dp;
689 {
690 char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
691
692 if (ep == (char *)0)
693 fatal("Out of memory");
694 (void) strcpy(ep, cp);
695 (void) strcat(ep, dp);
696 return (ep);
697 }
698
699 char **
copyblk(v)700 copyblk(v)
701 char **v;
702 {
703 char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
704 sizeof(char **)));
705 if (nv == (char **)0)
706 fatal("Out of memory");
707
708 return (blkcpy(nv, v));
709 }
710
711 static
712 char *
strend(cp)713 strend(cp)
714 char *cp;
715 {
716
717 while (*cp)
718 cp++;
719 return (cp);
720 }
721 /*
722 * Extract a home directory from the password file
723 * The argument points to a buffer where the name of the
724 * user whose home directory is sought is currently.
725 * We write the home directory of the user back there.
726 */
727 int
gethdir(home)728 gethdir(home)
729 char *home;
730 {
731 struct passwd *pp = getpwnam(home);
732
733 if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
734 return (1);
735 (void) strcpy(home, pp->pw_dir);
736 return (0);
737 }
738
739 static void
fatal(s)740 fatal(s)
741 char *s;
742 {
743 fprintf( stderr, "glob: %s\n", s );
744 exit(1);
745 }
746