]>
Commit | Line | Data |
---|---|---|
ccc6cda3 JA |
1 | /* pathexp.c -- The shell interface to the globbing library. */ |
2 | ||
3 | /* Copyright (C) 1995 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
7 | Bash is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 2, or (at your option) any later | |
10 | version. | |
11 | ||
12 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License along | |
18 | with Bash; see the file COPYING. If not, write to the Free Software | |
bb70624e | 19 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ |
ccc6cda3 JA |
20 | |
21 | #include "config.h" | |
22 | ||
23 | #include "bashtypes.h" | |
24 | #include <stdio.h> | |
25 | ||
26 | #if defined (HAVE_UNISTD_H) | |
27 | # include <unistd.h> | |
28 | #endif | |
29 | ||
30 | #include "bashansi.h" | |
31 | ||
32 | #include "shell.h" | |
33 | #include "pathexp.h" | |
34 | #include "flags.h" | |
35 | ||
36 | #include <glob/fnmatch.h> | |
b72432fd JA |
37 | |
38 | #if defined (USE_POSIX_GLOB_LIBRARY) | |
39 | # include <glob.h> | |
40 | #else | |
41 | # include <glob/glob.h> | |
42 | #endif | |
ccc6cda3 JA |
43 | |
44 | /* Control whether * matches .files in globbing. */ | |
45 | int glob_dot_filenames; | |
46 | ||
cce855bc JA |
47 | /* Control whether the extended globbing features are enabled. */ |
48 | int extended_glob = 0; | |
49 | ||
ccc6cda3 JA |
50 | /* Return nonzero if STRING has any unquoted special globbing chars in it. */ |
51 | int | |
52 | unquoted_glob_pattern_p (string) | |
53 | register char *string; | |
54 | { | |
55 | register int c; | |
56 | int open; | |
57 | ||
58 | open = 0; | |
59 | while (c = *string++) | |
60 | { | |
61 | switch (c) | |
62 | { | |
63 | case '?': | |
64 | case '*': | |
65 | return (1); | |
66 | ||
67 | case '[': | |
68 | open++; | |
69 | continue; | |
70 | ||
71 | case ']': | |
72 | if (open) | |
73 | return (1); | |
74 | continue; | |
75 | ||
cce855bc JA |
76 | case '+': |
77 | case '@': | |
78 | case '!': | |
79 | if (*string == '(') /*)*/ | |
80 | return (1); | |
81 | continue; | |
82 | ||
ccc6cda3 JA |
83 | case CTLESC: |
84 | case '\\': | |
85 | if (*string++ == '\0') | |
86 | return (0); | |
87 | } | |
88 | } | |
89 | return (0); | |
90 | } | |
91 | ||
92 | /* PATHNAME can contain characters prefixed by CTLESC; this indicates | |
93 | that the character is to be quoted. We quote it here in the style | |
cce855bc | 94 | that the glob library recognizes. If flags includes QGLOB_CVTNULL, |
ccc6cda3 JA |
95 | we change quoted null strings (pathname[0] == CTLNUL) into empty |
96 | strings (pathname[0] == 0). If this is called after quote removal | |
cce855bc | 97 | is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote |
ccc6cda3 | 98 | removal has not been done (for example, before attempting to match a |
cce855bc JA |
99 | pattern while executing a case statement), flags should include |
100 | QGLOB_CVTNULL. If flags includes QGLOB_FILENAME, appropriate quoting | |
101 | to match a filename should be performed. */ | |
ccc6cda3 | 102 | char * |
cce855bc | 103 | quote_string_for_globbing (pathname, qflags) |
ccc6cda3 | 104 | char *pathname; |
cce855bc | 105 | int qflags; |
ccc6cda3 JA |
106 | { |
107 | char *temp; | |
cce855bc | 108 | register int i, j; |
ccc6cda3 | 109 | |
cce855bc | 110 | temp = xmalloc (strlen (pathname) + 1); |
ccc6cda3 | 111 | |
cce855bc | 112 | if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname)) |
ccc6cda3 JA |
113 | { |
114 | temp[0] = '\0'; | |
115 | return temp; | |
116 | } | |
117 | ||
cce855bc | 118 | for (i = j = 0; pathname[i]; i++) |
ccc6cda3 | 119 | { |
cce855bc JA |
120 | if (pathname[i] == CTLESC) |
121 | { | |
122 | if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/') | |
123 | continue; | |
124 | temp[j++] = '\\'; | |
125 | } | |
126 | else | |
127 | temp[j++] = pathname[i]; | |
ccc6cda3 | 128 | } |
cce855bc | 129 | temp[j] = '\0'; |
ccc6cda3 JA |
130 | |
131 | return (temp); | |
132 | } | |
133 | ||
134 | char * | |
135 | quote_globbing_chars (string) | |
136 | char *string; | |
137 | { | |
138 | char *temp, *s, *t; | |
139 | ||
140 | temp = xmalloc (strlen (string) * 2 + 1); | |
141 | for (t = temp, s = string; *s; ) | |
142 | { | |
143 | switch (*s) | |
144 | { | |
145 | case '*': | |
146 | case '[': | |
147 | case ']': | |
148 | case '?': | |
149 | case '\\': | |
150 | *t++ = '\\'; | |
151 | break; | |
cce855bc JA |
152 | case '+': |
153 | case '@': | |
154 | case '!': | |
155 | if (s[1] == '(') /*(*/ | |
156 | *t++ = '\\'; | |
157 | break; | |
ccc6cda3 JA |
158 | } |
159 | *t++ = *s++; | |
160 | } | |
161 | *t = '\0'; | |
162 | return temp; | |
163 | } | |
164 | ||
165 | /* Call the glob library to do globbing on PATHNAME. */ | |
166 | char ** | |
167 | shell_glob_filename (pathname) | |
168 | char *pathname; | |
169 | { | |
170 | #if defined (USE_POSIX_GLOB_LIBRARY) | |
171 | register int i; | |
172 | char *temp, **return_value; | |
173 | glob_t filenames; | |
174 | int glob_flags; | |
175 | ||
cce855bc | 176 | temp = quote_string_for_globbing (pathname, QGLOB_FILENAME); |
ccc6cda3 JA |
177 | |
178 | filenames.gl_offs = 0; | |
179 | ||
b72432fd | 180 | # if defined (GLOB_PERIOD) |
ccc6cda3 | 181 | glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0; |
b72432fd JA |
182 | # else |
183 | glob_flags = 0; | |
184 | # endif /* !GLOB_PERIOD */ | |
185 | ||
ccc6cda3 JA |
186 | glob_flags |= (GLOB_ERR | GLOB_DOOFFS); |
187 | ||
188 | i = glob (temp, glob_flags, (Function *)NULL, &filenames); | |
189 | ||
190 | free (temp); | |
191 | ||
192 | if (i == GLOB_NOSPACE || i == GLOB_ABEND) | |
193 | return ((char **)NULL); | |
b72432fd JA |
194 | else if (i == GLOB_NOMATCH) |
195 | filenames.gl_pathv = (char **)NULL; | |
196 | else if (i != 0) /* other error codes not in POSIX.2 */ | |
cce855bc | 197 | filenames.gl_pathv = (char **)NULL; |
ccc6cda3 | 198 | |
bb70624e JA |
199 | results = filenames.gl_pathv; |
200 | ||
201 | if (results && ((GLOB_FAILED (results)) == 0)) | |
202 | { | |
203 | if (should_ignore_glob_matches ()) | |
204 | ignore_glob_matches (results); | |
205 | if (results && results[0]) | |
206 | sort_char_array (results); | |
207 | else | |
208 | { | |
209 | FREE (results); | |
210 | results = (char **)NULL; | |
211 | } | |
212 | } | |
213 | ||
214 | return (results); | |
ccc6cda3 JA |
215 | |
216 | #else /* !USE_POSIX_GLOB_LIBRARY */ | |
217 | ||
218 | char *temp, **results; | |
219 | ||
220 | noglob_dot_filenames = glob_dot_filenames == 0; | |
221 | ||
cce855bc | 222 | temp = quote_string_for_globbing (pathname, QGLOB_FILENAME); |
ccc6cda3 JA |
223 | |
224 | results = glob_filename (temp); | |
225 | free (temp); | |
226 | ||
227 | if (results && ((GLOB_FAILED (results)) == 0)) | |
228 | { | |
229 | if (should_ignore_glob_matches ()) | |
230 | ignore_glob_matches (results); | |
231 | if (results && results[0]) | |
232 | sort_char_array (results); | |
233 | else | |
234 | { | |
235 | FREE (results); | |
236 | results = (char **)&glob_error_return; | |
237 | } | |
238 | } | |
239 | ||
240 | return (results); | |
241 | #endif /* !USE_POSIX_GLOB_LIBRARY */ | |
242 | } | |
243 | ||
244 | /* Stuff for GLOBIGNORE. */ | |
245 | ||
246 | static struct ignorevar globignore = | |
247 | { | |
248 | "GLOBIGNORE", | |
249 | (struct ign *)0, | |
250 | 0, | |
251 | (char *)0, | |
252 | (Function *)0, | |
253 | }; | |
254 | ||
255 | /* Set up to ignore some glob matches because the value of GLOBIGNORE | |
256 | has changed. If GLOBIGNORE is being unset, we also need to disable | |
257 | the globbing of filenames beginning with a `.'. */ | |
258 | void | |
259 | setup_glob_ignore (name) | |
260 | char *name; | |
261 | { | |
262 | char *v; | |
263 | ||
264 | v = get_string_value (name); | |
265 | setup_ignore_patterns (&globignore); | |
266 | ||
267 | if (globignore.num_ignores) | |
268 | glob_dot_filenames = 1; | |
269 | else if (v == 0) | |
270 | glob_dot_filenames = 0; | |
271 | } | |
272 | ||
273 | int | |
274 | should_ignore_glob_matches () | |
275 | { | |
276 | return globignore.num_ignores; | |
277 | } | |
278 | ||
279 | /* Return 0 if NAME matches a pattern in the globignore.ignores list. */ | |
280 | static int | |
281 | glob_name_is_acceptable (name) | |
282 | char *name; | |
283 | { | |
284 | struct ign *p; | |
cce855bc | 285 | int flags; |
ccc6cda3 JA |
286 | |
287 | /* . and .. are never matched */ | |
288 | if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) | |
289 | return (0); | |
290 | ||
cce855bc | 291 | flags = FNM_PATHNAME | FNMATCH_EXTFLAG; |
ccc6cda3 JA |
292 | for (p = globignore.ignores; p->val; p++) |
293 | { | |
cce855bc | 294 | if (fnmatch (p->val, name, flags) != FNM_NOMATCH) |
ccc6cda3 JA |
295 | return (0); |
296 | } | |
297 | return (1); | |
298 | } | |
299 | ||
300 | /* Internal function to test whether filenames in NAMES should be | |
301 | ignored. NAME_FUNC is a pointer to a function to call with each | |
302 | name. It returns non-zero if the name is acceptable to the particular | |
303 | ignore function which called _ignore_names; zero if the name should | |
304 | be removed from NAMES. */ | |
305 | ||
306 | static void | |
307 | ignore_globbed_names (names, name_func) | |
308 | char **names; | |
309 | Function *name_func; | |
310 | { | |
311 | char **newnames; | |
312 | int n, i; | |
313 | ||
314 | for (i = 0; names[i]; i++) | |
315 | ; | |
bb70624e | 316 | newnames = alloc_array (i + 1); |
ccc6cda3 JA |
317 | |
318 | for (n = i = 0; names[i]; i++) | |
319 | { | |
320 | if ((*name_func) (names[i])) | |
321 | newnames[n++] = names[i]; | |
322 | else | |
323 | free (names[i]); | |
324 | } | |
325 | ||
326 | newnames[n] = (char *)NULL; | |
327 | ||
328 | if (n == 0) | |
329 | { | |
330 | names[0] = (char *)NULL; | |
331 | free (newnames); | |
332 | return; | |
333 | } | |
334 | ||
335 | /* Copy the acceptable names from NEWNAMES back to NAMES and set the | |
336 | new array end. */ | |
337 | for (n = 0; newnames[n]; n++) | |
338 | names[n] = newnames[n]; | |
339 | names[n] = (char *)NULL; | |
d166f048 | 340 | free (newnames); |
ccc6cda3 JA |
341 | } |
342 | ||
343 | void | |
344 | ignore_glob_matches (names) | |
345 | char **names; | |
346 | { | |
347 | if (globignore.num_ignores == 0) | |
348 | return; | |
349 | ||
350 | ignore_globbed_names (names, glob_name_is_acceptable); | |
351 | } | |
352 | ||
353 | void | |
354 | setup_ignore_patterns (ivp) | |
355 | struct ignorevar *ivp; | |
356 | { | |
357 | int numitems, maxitems, ptr; | |
358 | char *colon_bit, *this_ignoreval; | |
359 | struct ign *p; | |
360 | ||
361 | this_ignoreval = get_string_value (ivp->varname); | |
362 | ||
363 | /* If nothing has changed then just exit now. */ | |
364 | if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) || | |
365 | (!this_ignoreval && !ivp->last_ignoreval)) | |
366 | return; | |
367 | ||
368 | /* Oops. The ignore variable has changed. Re-parse it. */ | |
369 | ivp->num_ignores = 0; | |
370 | ||
371 | if (ivp->ignores) | |
372 | { | |
373 | for (p = ivp->ignores; p->val; p++) | |
374 | free(p->val); | |
375 | free (ivp->ignores); | |
376 | ivp->ignores = (struct ign *)NULL; | |
377 | } | |
378 | ||
379 | if (ivp->last_ignoreval) | |
380 | { | |
381 | free (ivp->last_ignoreval); | |
382 | ivp->last_ignoreval = (char *)NULL; | |
383 | } | |
384 | ||
385 | if (this_ignoreval == 0 || *this_ignoreval == '\0') | |
386 | return; | |
387 | ||
388 | ivp->last_ignoreval = savestring (this_ignoreval); | |
389 | ||
390 | numitems = maxitems = ptr = 0; | |
391 | ||
392 | while (colon_bit = extract_colon_unit (this_ignoreval, &ptr)) | |
393 | { | |
394 | if (numitems + 1 >= maxitems) | |
395 | { | |
396 | maxitems += 10; | |
397 | ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign)); | |
398 | } | |
399 | ivp->ignores[numitems].val = colon_bit; | |
400 | ivp->ignores[numitems].len = strlen (colon_bit); | |
401 | ivp->ignores[numitems].flags = 0; | |
402 | if (ivp->item_func) | |
403 | (*ivp->item_func) (&ivp->ignores[numitems]); | |
404 | numitems++; | |
405 | } | |
406 | ivp->ignores[numitems].val = (char *)NULL; | |
407 | ivp->num_ignores = numitems; | |
408 | } |