BSD 4_4_Lite1 development
[unix-history] / usr / src / contrib / rc-1.4 / glob.c
CommitLineData
825f5713
C
1/* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
2
3#include <sys/types.h>
4#include <sys/stat.h>
5#include "rc.h"
6#include <dirent.h>
7
8static List *dmatch(char *, char *, char *);
9static List *doglob(char *, char *);
10static List *lglob(List *, char *, char *, size_t);
11static List *sort(List *);
12
13/*
14 Matches a list of words s against a list of patterns p. Returns true iff
15 a pattern in p matches a word in s. () matches (), but otherwise null
16 patterns match nothing.
17*/
18
19extern bool lmatch(List *s, List *p) {
20 List *q;
21 int i;
22 bool okay;
23 if (s == NULL) {
24 if (p == NULL) /* null matches null */
25 return TRUE;
26 for (; p != NULL; p = p->n) { /* one or more stars match null */
27 if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */
28 okay = TRUE;
29 for (i = 0; p->w[i] != '\0'; i++)
30 if (p->w[i] != '*' || p->m[i] != 1) {
31 okay = FALSE;
32 break;
33 }
34 if (okay)
35 return TRUE;
36 }
37 }
38 return FALSE;
39 }
40 for (; s != NULL; s = s->n)
41 for (q = p; q != NULL; q = q->n)
42 if (match(q->w, q->m, s->w))
43 return TRUE;
44 return FALSE;
45}
46
47/*
48 Globs a list; checks to see if each element in the list has a metacharacter. If it
49 does, it is globbed, and the output is sorted.
50*/
51
52extern List *glob(List *s) {
53 List *top, *r;
54 bool meta;
55 for (r = s, meta = FALSE; r != NULL; r = r->n)
56 if (r->m != NULL)
57 meta = TRUE;
58 if (!meta)
59 return s; /* don't copy lists with no metacharacters in them */
60 for (top = r = NULL; s != NULL; s = s->n) {
61 if (s->m == NULL) { /* no metacharacters; just tack on to the return list */
62 if (top == NULL)
63 top = r = nnew(List);
64 else
65 r = r->n = nnew(List);
66 r->w = s->w;
67 } else {
68 if (top == NULL)
69 top = r = sort(doglob(s->w, s->m));
70 else
71 r->n = sort(doglob(s->w, s->m));
72 while (r->n != NULL)
73 r = r->n;
74 }
75 }
76 r->n = NULL;
77 return top;
78}
79
80/* Matches a pattern p against the contents of directory d */
81
82static List *dmatch(char *d, char *p, char *m) {
83 bool matched = FALSE;
84 List *top, *r;
85 static DIR *dirp;
86 static struct dirent *dp;
87 static struct stat s;
88 /* prototypes for XXXdir functions. comment out if necessary */
89 extern DIR *opendir(const char *);
90 extern struct dirent *readdir(DIR *);
91 /*extern int closedir(DIR *);*/
92
93 top = r = NULL;
94 /* opendir succeeds on regular files on some systems, so the stat() call is necessary (sigh) */
95 if (stat(d, &s) < 0 || (s.st_mode & S_IFMT) != S_IFDIR || (dirp = opendir(d)) == NULL)
96 return NULL;
97 while ((dp = readdir(dirp)) != NULL)
98 if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */
99 matched = TRUE;
100 if (top == NULL)
101 top = r = nnew(List);
102 else
103 r = r->n = nnew(List);
104 r->w = ncpy(dp->d_name);
105 r->m = NULL;
106 }
107 closedir(dirp);
108 if (!matched)
109 return NULL;
110 r->n = NULL;
111 return top;
112}
113
114/*
115 lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
116 will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
117 slashcount indicates the number of slashes to stick between the directory and the
118 matched name. e.g., for matching ////tmp/////foo*
119*/
120
121static List *lglob(List *s, char *p, char *m, size_t slashcount) {
122 List *q, *r, *top, foo;
123 static List slash;
124 static size_t slashsize = 0;
125 if (slashcount + 1 > slashsize) {
126 slash.w = ealloc(slashcount + 1);
127 slashsize = slashcount;
128 }
129 slash.w[slashcount] = '\0';
130 while (slashcount > 0)
131 slash.w[--slashcount] = '/';
132 for (top = r = NULL; s != NULL; s = s->n) {
133 q = dmatch(s->w, p, m);
134 if (q != NULL) {
135 foo.w = s->w;
136 foo.m = NULL;
137 foo.n = NULL;
138 if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */
139 q = concat(&slash, q); /* dir/name with slash */
140 q = concat(&foo, q);
141 if (r == NULL)
142 top = r = q;
143 else
144 r->n = q;
145 while (r->n != NULL)
146 r = r->n;
147 }
148 }
149 return top;
150}
151
152/*
153 Doglob globs a pathname in pattern form against a unix path. Returns the original
154 pattern (cleaned of metacharacters) on failure, or the globbed string(s).
155*/
156
157static List *doglob(char *w, char *m) {
158 static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL;
159 static size_t dsize = 0;
160 char *d, *p, *md, *mp;
161 size_t psize;
162 char *s = w;
163 List firstdir;
164 List *matched;
165 if ((psize = strlen(w) + 1) > dsize || dir == NULL) {
166 efree(dir); efree(pattern); efree(metadir); efree(metapattern);
167 dir = ealloc(psize);
168 pattern = ealloc(psize);
169 metadir = ealloc(psize);
170 metapattern = ealloc(psize);
171 dsize = psize;
172 }
173 d = dir;
174 p = pattern;
175 md = metadir;
176 mp = metapattern;
177 if (*s == '/')
178 while (*s == '/')
179 *d++ = *s++, *md++ = *m++;
180 else
181 while (*s != '/' && *s != '\0')
182 *d++ = *s++, *md++ = *m++; /* get first directory component */
183 *d = '\0';
184 /*
185 Special case: no slashes in the pattern, i.e., open the current directory.
186 Remember that w cannot consist of slashes alone (the other way *s could be
187 zero) since doglob gets called iff there's a metacharacter to be matched
188 */
189 if (*s == '\0') {
190 matched = dmatch(".", dir, metadir);
191 goto end;
192 }
193 if (*w == '/') {
194 firstdir.w = dir;
195 firstdir.m = metadir;
196 firstdir.n = NULL;
197 matched = &firstdir;
198 } else {
199 /*
200 we must glob against current directory,
201 since the first character is not a slash.
202 */
203 matched = dmatch(".", dir, metadir);
204 }
205 do {
206 size_t slashcount;
207 SIGCHK;
208 for (slashcount = 0; *s == '/'; s++, m++)
209 slashcount++; /* skip slashes */
210 while (*s != '/' && *s != '\0')
211 *p++ = *s++, *mp++ = *m++; /* get pattern */
212 *p = '\0';
213 matched = lglob(matched, pattern, metapattern, slashcount);
214 p = pattern, mp = metapattern;
215 } while (*s != '\0');
216end: if (matched == NULL) {
217 matched = nnew(List);
218 matched->w = w;
219 matched->m = NULL;
220 matched->n = NULL;
221 }
222 return matched;
223}
224
225static List *sort(List *s) {
226 size_t nel = listnel(s);
227 if (nel > 1) {
228 char **a;
229 List *t;
230 qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp);
231 for (t = s; t != NULL; t = t->n)
232 t->w = *a++;
233 }
234 return s;
235}