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