]> git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
a498672f51ec668c266fb581a0a637b726b913c2
[thirdparty/bash.git] / pathexp.c
1 /* pathexp.c -- The shell interface to the globbing library. */
2
3 /* Copyright (C) 1995-2002 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
19 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
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 "shmbutil.h"
37
38 #include <glob/strmatch.h>
39
40 static int glob_name_is_acceptable __P((const char *));
41 static void ignore_globbed_names __P((char **, sh_ignore_func_t *));
42
43 #if defined (USE_POSIX_GLOB_LIBRARY)
44 # include <glob.h>
45 typedef int posix_glob_errfunc_t __P((const char *, int));
46 #else
47 # include <glob/glob.h>
48 #endif
49
50 /* Control whether * matches .files in globbing. */
51 int glob_dot_filenames;
52
53 /* Control whether the extended globbing features are enabled. */
54 int extended_glob = 0;
55
56 /* Return nonzero if STRING has any unquoted special globbing chars in it. */
57 int
58 unquoted_glob_pattern_p (string)
59 register char *string;
60 {
61 register int c;
62 char *send;
63 int open;
64
65 DECLARE_MBSTATE;
66
67 open = 0;
68 send = string + strlen (string);
69
70 while (c = *string++)
71 {
72 switch (c)
73 {
74 case '?':
75 case '*':
76 return (1);
77
78 case '[':
79 open++;
80 continue;
81
82 case ']':
83 if (open)
84 return (1);
85 continue;
86
87 case '+':
88 case '@':
89 case '!':
90 if (*string == '(') /*)*/
91 return (1);
92 continue;
93
94 case CTLESC:
95 case '\\':
96 if (*string++ == '\0')
97 return (0);
98 }
99
100 /* Advance one fewer byte than an entire multibyte character to
101 account for the auto-increment in the loop above. */
102 #ifdef HANDLE_MULTIBYTE
103 string--;
104 ADVANCE_CHAR_P (string, send - string);
105 string++;
106 #else
107 ADVANCE_CHAR_P (string, send - string);
108 #endif
109 }
110 return (0);
111 }
112
113 /* PATHNAME can contain characters prefixed by CTLESC; this indicates
114 that the character is to be quoted. We quote it here in the style
115 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
116 we change quoted null strings (pathname[0] == CTLNUL) into empty
117 strings (pathname[0] == 0). If this is called after quote removal
118 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
119 removal has not been done (for example, before attempting to match a
120 pattern while executing a case statement), flags should include
121 QGLOB_CVTNULL. If flags includes QGLOB_FILENAME, appropriate quoting
122 to match a filename should be performed. */
123 char *
124 quote_string_for_globbing (pathname, qflags)
125 const char *pathname;
126 int qflags;
127 {
128 char *temp;
129 register int i, j;
130
131 temp = (char *)xmalloc (strlen (pathname) + 1);
132
133 if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
134 {
135 temp[0] = '\0';
136 return temp;
137 }
138
139 for (i = j = 0; pathname[i]; i++)
140 {
141 if (pathname[i] == CTLESC)
142 {
143 if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
144 continue;
145 temp[j++] = '\\';
146 i++;
147 if (pathname[i] == '\0')
148 break;
149 }
150 temp[j++] = pathname[i];
151 }
152 temp[j] = '\0';
153
154 return (temp);
155 }
156
157 char *
158 quote_globbing_chars (string)
159 char *string;
160 {
161 size_t slen;
162 char *temp, *s, *t, *send;
163 DECLARE_MBSTATE;
164
165 slen = strlen (string);
166 send = string + slen;
167
168 temp = (char *)xmalloc (slen * 2 + 1);
169 for (t = temp, s = string; *s; )
170 {
171 switch (*s)
172 {
173 case '*':
174 case '[':
175 case ']':
176 case '?':
177 case '\\':
178 *t++ = '\\';
179 break;
180 case '+':
181 case '@':
182 case '!':
183 if (s[1] == '(') /*(*/
184 *t++ = '\\';
185 break;
186 }
187
188 /* Copy a single (possibly multibyte) character from s to t,
189 incrementing both. */
190 COPY_CHAR_P (t, s, send);
191 }
192 *t = '\0';
193 return temp;
194 }
195
196 /* Call the glob library to do globbing on PATHNAME. */
197 char **
198 shell_glob_filename (pathname)
199 const char *pathname;
200 {
201 #if defined (USE_POSIX_GLOB_LIBRARY)
202 register int i;
203 char *temp, **results;
204 glob_t filenames;
205 int glob_flags;
206
207 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
208
209 filenames.gl_offs = 0;
210
211 # if defined (GLOB_PERIOD)
212 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
213 # else
214 glob_flags = 0;
215 # endif /* !GLOB_PERIOD */
216
217 glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
218
219 i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
220
221 free (temp);
222
223 if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
224 return ((char **)NULL);
225 else if (i == GLOB_NOMATCH)
226 filenames.gl_pathv = (char **)NULL;
227 else if (i != 0) /* other error codes not in POSIX.2 */
228 filenames.gl_pathv = (char **)NULL;
229
230 results = filenames.gl_pathv;
231
232 if (results && ((GLOB_FAILED (results)) == 0))
233 {
234 if (should_ignore_glob_matches ())
235 ignore_glob_matches (results);
236 if (results && results[0])
237 strvec_sort (results);
238 else
239 {
240 FREE (results);
241 results = (char **)NULL;
242 }
243 }
244
245 return (results);
246
247 #else /* !USE_POSIX_GLOB_LIBRARY */
248
249 char *temp, **results;
250
251 noglob_dot_filenames = glob_dot_filenames == 0;
252
253 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
254 results = glob_filename (temp, 0);
255 free (temp);
256
257 if (results && ((GLOB_FAILED (results)) == 0))
258 {
259 if (should_ignore_glob_matches ())
260 ignore_glob_matches (results);
261 if (results && results[0])
262 strvec_sort (results);
263 else
264 {
265 FREE (results);
266 results = (char **)&glob_error_return;
267 }
268 }
269
270 return (results);
271 #endif /* !USE_POSIX_GLOB_LIBRARY */
272 }
273
274 /* Stuff for GLOBIGNORE. */
275
276 static struct ignorevar globignore =
277 {
278 "GLOBIGNORE",
279 (struct ign *)0,
280 0,
281 (char *)0,
282 (sh_iv_item_func_t *)0,
283 };
284
285 /* Set up to ignore some glob matches because the value of GLOBIGNORE
286 has changed. If GLOBIGNORE is being unset, we also need to disable
287 the globbing of filenames beginning with a `.'. */
288 void
289 setup_glob_ignore (name)
290 char *name;
291 {
292 char *v;
293
294 v = get_string_value (name);
295 setup_ignore_patterns (&globignore);
296
297 if (globignore.num_ignores)
298 glob_dot_filenames = 1;
299 else if (v == 0)
300 glob_dot_filenames = 0;
301 }
302
303 int
304 should_ignore_glob_matches ()
305 {
306 return globignore.num_ignores;
307 }
308
309 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
310 static int
311 glob_name_is_acceptable (name)
312 const char *name;
313 {
314 struct ign *p;
315 int flags;
316
317 /* . and .. are never matched */
318 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
319 return (0);
320
321 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
322 for (p = globignore.ignores; p->val; p++)
323 {
324 if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
325 return (0);
326 }
327 return (1);
328 }
329
330 /* Internal function to test whether filenames in NAMES should be
331 ignored. NAME_FUNC is a pointer to a function to call with each
332 name. It returns non-zero if the name is acceptable to the particular
333 ignore function which called _ignore_names; zero if the name should
334 be removed from NAMES. */
335
336 static void
337 ignore_globbed_names (names, name_func)
338 char **names;
339 sh_ignore_func_t *name_func;
340 {
341 char **newnames;
342 int n, i;
343
344 for (i = 0; names[i]; i++)
345 ;
346 newnames = strvec_create (i + 1);
347
348 for (n = i = 0; names[i]; i++)
349 {
350 if ((*name_func) (names[i]))
351 newnames[n++] = names[i];
352 else
353 free (names[i]);
354 }
355
356 newnames[n] = (char *)NULL;
357
358 if (n == 0)
359 {
360 names[0] = (char *)NULL;
361 free (newnames);
362 return;
363 }
364
365 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
366 new array end. */
367 for (n = 0; newnames[n]; n++)
368 names[n] = newnames[n];
369 names[n] = (char *)NULL;
370 free (newnames);
371 }
372
373 void
374 ignore_glob_matches (names)
375 char **names;
376 {
377 if (globignore.num_ignores == 0)
378 return;
379
380 ignore_globbed_names (names, glob_name_is_acceptable);
381 }
382
383 void
384 setup_ignore_patterns (ivp)
385 struct ignorevar *ivp;
386 {
387 int numitems, maxitems, ptr;
388 char *colon_bit, *this_ignoreval;
389 struct ign *p;
390
391 this_ignoreval = get_string_value (ivp->varname);
392
393 /* If nothing has changed then just exit now. */
394 if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
395 (!this_ignoreval && !ivp->last_ignoreval))
396 return;
397
398 /* Oops. The ignore variable has changed. Re-parse it. */
399 ivp->num_ignores = 0;
400
401 if (ivp->ignores)
402 {
403 for (p = ivp->ignores; p->val; p++)
404 free(p->val);
405 free (ivp->ignores);
406 ivp->ignores = (struct ign *)NULL;
407 }
408
409 if (ivp->last_ignoreval)
410 {
411 free (ivp->last_ignoreval);
412 ivp->last_ignoreval = (char *)NULL;
413 }
414
415 if (this_ignoreval == 0 || *this_ignoreval == '\0')
416 return;
417
418 ivp->last_ignoreval = savestring (this_ignoreval);
419
420 numitems = maxitems = ptr = 0;
421
422 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
423 {
424 if (numitems + 1 >= maxitems)
425 {
426 maxitems += 10;
427 ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
428 }
429 ivp->ignores[numitems].val = colon_bit;
430 ivp->ignores[numitems].len = strlen (colon_bit);
431 ivp->ignores[numitems].flags = 0;
432 if (ivp->item_func)
433 (*ivp->item_func) (&ivp->ignores[numitems]);
434 numitems++;
435 }
436 ivp->ignores[numitems].val = (char *)NULL;
437 ivp->num_ignores = numitems;
438 }