Initial revision
[ssr] / StraySrc / Utilities / c / glob
1 /*
2 * glob.c
3 *
4 * Full file wildcard matching
5 *
6 * © 1998 Straylight/Edgeware
7 */
8
9 /*----- Licensing note ----------------------------------------------------*
10 *
11 * This file is part of Straylight's core utilities (coreutils).
12 *
13 * Coreutils is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
16 * any later version.
17 *
18 * Coreutils is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with coreutils. If not, write to the Free Software Foundation,
25 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "swis.h"
36 #include "swiv.h"
37
38 #include "alloc.h"
39 #include "gf.h"
40 #include "glob.h"
41
42 /*----- Type definitions --------------------------------------------------*/
43
44 typedef struct glob_ctx {
45 char buf[1024]; /* Pointer to output buffer */
46 void (*proc)(const char *, void *); /* User procedure */
47 void *ctx; /* Context to pass the procedure */
48 int done; /* Number of matches found */
49 } glob_ctx;
50
51 /*----- Main code ---------------------------------------------------------*/
52
53 /* --- Wildcard syntax --- *
54 *
55 * The wildcards `*' and `#' do what they normally do: match zero-or-more
56 * and any-one characters respectively. Additionally, a `.' where a
57 * filename is expected will search recursively down the directory
58 * structure. Only globby matches with really existing things are counted;
59 * if a name contains nothing globbable then it matches nothing. This is
60 * done for speed: I expect the client to use a name literally if it fails
61 * to match anything. The syntax is a sort of blend between traditional
62 * RISC OS and kpathsea.
63 */
64
65 /* --- Hack note --- *
66 *
67 * This will fail gloriously given something like `adfs::ak*ha.$.foo.*'.
68 * Do I look like I care? This program has already taken five times longer
69 * than it should have done because I decided I wanted to do wildcarding.
70 */
71
72 /* --- Forward reference --- */
73
74 static void glob_do(glob_ctx *g, char *p, char *in);
75
76 /* --- @glob_got@ --- *
77 *
78 * Arguments: @glob_ctx *g@ = pointer to my context
79 * @char *p@ = pointer to current position in buffer
80 * @char *fn@ = filename to add
81 * @char *in@ = rest of wildcard pattern to match
82 *
83 * Returns: ---
84 *
85 * Use: Handles a matched filename in the glob matcher.
86 */
87
88 static void glob_got(glob_ctx *g, char *p, char *fn, char *in)
89 {
90 size_t sz;
91
92 /* --- Build the filename --- */
93
94 if (p != g->buf)
95 *p++ = '.';
96 sz = strlen(fn);
97 memcpy(p, fn, sz + 1);
98 p += sz;
99
100 /* --- See if this is the final element --- */
101
102 if (!in) {
103 int ty;
104
105 if (_swix(OS_File, _inr(0, 1) | _out(0), 17, g->buf, &ty) || !ty)
106 return;
107 g->done++;
108 g->proc(g->buf, g->ctx);
109 } else
110 glob_do(g, p, in);
111 }
112
113 /* --- @glob_match@ --- *
114 *
115 * Arguments: @const char *pat@ = pointer to pattern string
116 * @const char *s@ = pointer to candidate string
117 *
118 * Returns: Nonzero if pattern matches candidate.
119 *
120 * Use: Tries to match globbily. This is very simple stuff.
121 * I may add character classes later if I feel really eager.
122 */
123
124 static int glob_match(const char *pat, const char *s)
125 {
126 for (;;) {
127 if (!*pat && !*s)
128 return (1);
129 else if (!*pat)
130 return (0);
131 else if (*pat == '*') {
132 do pat++; while (*pat == '*');
133 do {
134 if (glob_match(pat, s))
135 return (1);
136 } while (*s++);
137 return (0);
138 } else if (!*s)
139 return (0);
140 else if (*pat != '#' && tolower(*pat) != tolower(*s))
141 return (0);
142 else
143 pat++, s++;
144 }
145 }
146
147 /* --- @glob_do@ --- *
148 *
149 * Arguments: @glob_ctx *g@ = pointer to my context
150 * @char *p@ = pointer to current position in buffer
151 * @char *in@ = rest of wildcard pattern to match
152 *
153 * Returns: ---
154 *
155 * Use: Main recursive glob matcher.
156 */
157
158 static void glob_do(glob_ctx *g, char *p, char *in)
159 {
160 char *q;
161 char *rec = 0;
162
163 /* --- Pick out the next component of the pathname --- */
164
165 if (*in == '.') {
166 do in++; while (*in == '.');
167 rec = in - 1;
168 if (!*in) in = "*";
169 }
170
171 q = strchr(in, '.');
172 if (q)
173 *q++ = 0;
174 else
175 q = 0;
176
177 /* --- See if this contains any wildcards --- */
178
179 if (rec) {
180 gf_ctx gx;
181 char *n;
182 char *qq = q ? q - 1 : 0;
183 for (gf_init(&gx, "*", g->buf); *p = 0, (n = gf_next(&gx)) != 0; ) {
184 if (qq) *qq = 0;
185 if (glob_match(in, n))
186 glob_got(g, p, n, q);
187 if (qq) *qq = '.';
188 glob_got(g, p, n, rec);
189 }
190 } else if (strpbrk(in, "*#")) {
191 gf_ctx gx;
192 char *n;
193 for (gf_init(&gx, in, g->buf); *p = 0, (n = gf_next(&gx)) != 0; )
194 glob_got(g, p, n, q);
195 } else
196 glob_got(g, p, in, q);
197 }
198
199 /* --- @glob@ --- *
200 *
201 * Arguments: @const char *pat@ = pointer to pattern string to match
202 * @void (*proc)(const char *, void *)@ = client function
203 * @void *ctx@ = context pointer to pass function
204 *
205 * Returns: Number of filenames matched.
206 *
207 * Use: Does filename globbing.
208 */
209
210 int glob(const char *pat, void (*proc)(const char *, void *), void *ctx)
211 {
212 glob_ctx gx;
213 char *in;
214
215 if (!strpbrk(pat, "*#") && !strstr(pat, ".."))
216 return (0);
217
218 gx.proc = proc;
219 gx.ctx = ctx;
220 gx.done = 0;
221 in = xstrdup(pat);
222 glob_do(&gx, gx.buf, in);
223 free(in);
224 return (gx.done);
225 }
226
227 /*----- That's all, folks -------------------------------------------------*/