]> git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
commit bash-20121005 snapshot
[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. QGLOB_REGEXP means we're
177 quoting for a Posix ERE (for [[ string =~ pat ]]) and that requires
178 some special handling. */
179 char *
180 quote_string_for_globbing (pathname, qflags)
181 const char *pathname;
182 int qflags;
183 {
184 char *temp;
185 register int i, j;
186 int brack, cclass, collsym, equiv, c;
187
188 temp = (char *)xmalloc (strlen (pathname) + 1);
189
190 if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
191 {
192 temp[0] = '\0';
193 return temp;
194 }
195
196 brack = cclass = collsym = equiv = 0;
197 for (i = j = 0; pathname[i]; i++)
198 {
199 if (pathname[i] == CTLESC)
200 {
201 if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
202 continue;
203 if (pathname[i+1] != CTLESC && (qflags & QGLOB_REGEXP) && ere_char (pathname[i+1]) == 0)
204 continue;
205 temp[j++] = '\\';
206 i++;
207 if (pathname[i] == '\0')
208 break;
209 }
210 else if ((qflags & QGLOB_REGEXP) && (i == 0 || pathname[i-1] != CTLESC) && pathname[i] == '[') /*]*/
211 {
212 brack = 1;
213 temp[j++] = pathname[i++]; /* open bracket */
214 c = pathname[i++]; /* c == char after open bracket */
215 do
216 {
217 if (c == 0)
218 goto endpat;
219 else if (c == CTLESC)
220 {
221 /* skip c, check for EOS, let assignment at end of loop */
222 /* pathname[i] == backslash-escaped character */
223 if (pathname[i] == 0)
224 goto endpat;
225 temp[j++] = pathname[i++];
226 }
227 else if (c == '[' && pathname[i] == ':')
228 {
229 temp[j++] = c;
230 temp[j++] = pathname[i++];
231 cclass = 1;
232 }
233 else if (cclass && c == ':' && pathname[i] == ']')
234 {
235 temp[j++] = c;
236 temp[j++] = pathname[i++];
237 cclass = 0;
238 }
239 else if (c == '[' && pathname[i] == '=')
240 {
241 temp[j++] = c;
242 temp[j++] = pathname[i++];
243 if (pathname[i] == ']')
244 temp[j++] = pathname[i++]; /* right brack can be in equiv */
245 equiv = 1;
246 }
247 else if (equiv && c == '=' && pathname[i] == ']')
248 {
249 temp[j++] = c;
250 temp[j++] = pathname[i++];
251 equiv = 0;
252 }
253 else if (c == '[' && pathname[i] == '.')
254 {
255 temp[j++] = c;
256 temp[j++] = pathname[i++];
257 if (pathname[i] == ']')
258 temp[j++] = pathname[i++]; /* right brack can be in collsym */
259 collsym = 1;
260 }
261 else if (collsym && c == '.' && pathname[i] == ']')
262 {
263 temp[j++] = c;
264 temp[j++] = pathname[i++];
265 collsym = 0;
266 }
267 else
268 temp[j++] = c;
269 }
270 while ((c = pathname[i++]) != ']');
271 temp[j++] = c; /* closing right bracket */
272 i--; /* increment will happen above in loop */
273 continue; /* skip double assignment below */
274 }
275 else if (pathname[i] == '\\')
276 {
277 temp[j++] = '\\';
278 i++;
279 if (pathname[i] == '\0')
280 break;
281 }
282 temp[j++] = pathname[i];
283 }
284 endpat:
285 temp[j] = '\0';
286
287 return (temp);
288 }
289
290 char *
291 quote_globbing_chars (string)
292 char *string;
293 {
294 size_t slen;
295 char *temp, *s, *t, *send;
296 DECLARE_MBSTATE;
297
298 slen = strlen (string);
299 send = string + slen;
300
301 temp = (char *)xmalloc (slen * 2 + 1);
302 for (t = temp, s = string; *s; )
303 {
304 if (glob_char_p (s))
305 *t++ = '\\';
306
307 /* Copy a single (possibly multibyte) character from s to t,
308 incrementing both. */
309 COPY_CHAR_P (t, s, send);
310 }
311 *t = '\0';
312 return temp;
313 }
314
315 /* Call the glob library to do globbing on PATHNAME. */
316 char **
317 shell_glob_filename (pathname)
318 const char *pathname;
319 {
320 #if defined (USE_POSIX_GLOB_LIBRARY)
321 register int i;
322 char *temp, **results;
323 glob_t filenames;
324 int glob_flags;
325
326 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
327
328 filenames.gl_offs = 0;
329
330 # if defined (GLOB_PERIOD)
331 glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
332 # else
333 glob_flags = 0;
334 # endif /* !GLOB_PERIOD */
335
336 glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
337
338 i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
339
340 free (temp);
341
342 if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
343 return ((char **)NULL);
344 else if (i == GLOB_NOMATCH)
345 filenames.gl_pathv = (char **)NULL;
346 else if (i != 0) /* other error codes not in POSIX.2 */
347 filenames.gl_pathv = (char **)NULL;
348
349 results = filenames.gl_pathv;
350
351 if (results && ((GLOB_FAILED (results)) == 0))
352 {
353 if (should_ignore_glob_matches ())
354 ignore_glob_matches (results);
355 if (results && results[0])
356 strvec_sort (results);
357 else
358 {
359 FREE (results);
360 results = (char **)NULL;
361 }
362 }
363
364 return (results);
365
366 #else /* !USE_POSIX_GLOB_LIBRARY */
367
368 char *temp, **results;
369
370 noglob_dot_filenames = glob_dot_filenames == 0;
371
372 temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
373 results = glob_filename (temp, glob_star ? GX_GLOBSTAR : 0);
374 free (temp);
375
376 if (results && ((GLOB_FAILED (results)) == 0))
377 {
378 if (should_ignore_glob_matches ())
379 ignore_glob_matches (results);
380 if (results && results[0])
381 strvec_sort (results);
382 else
383 {
384 FREE (results);
385 results = (char **)&glob_error_return;
386 }
387 }
388
389 return (results);
390 #endif /* !USE_POSIX_GLOB_LIBRARY */
391 }
392
393 /* Stuff for GLOBIGNORE. */
394
395 static struct ignorevar globignore =
396 {
397 "GLOBIGNORE",
398 (struct ign *)0,
399 0,
400 (char *)0,
401 (sh_iv_item_func_t *)0,
402 };
403
404 /* Set up to ignore some glob matches because the value of GLOBIGNORE
405 has changed. If GLOBIGNORE is being unset, we also need to disable
406 the globbing of filenames beginning with a `.'. */
407 void
408 setup_glob_ignore (name)
409 char *name;
410 {
411 char *v;
412
413 v = get_string_value (name);
414 setup_ignore_patterns (&globignore);
415
416 if (globignore.num_ignores)
417 glob_dot_filenames = 1;
418 else if (v == 0)
419 glob_dot_filenames = 0;
420 }
421
422 int
423 should_ignore_glob_matches ()
424 {
425 return globignore.num_ignores;
426 }
427
428 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
429 static int
430 glob_name_is_acceptable (name)
431 const char *name;
432 {
433 struct ign *p;
434 int flags;
435
436 /* . and .. are never matched */
437 if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
438 return (0);
439
440 flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
441 for (p = globignore.ignores; p->val; p++)
442 {
443 if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
444 return (0);
445 }
446 return (1);
447 }
448
449 /* Internal function to test whether filenames in NAMES should be
450 ignored. NAME_FUNC is a pointer to a function to call with each
451 name. It returns non-zero if the name is acceptable to the particular
452 ignore function which called _ignore_names; zero if the name should
453 be removed from NAMES. */
454
455 static void
456 ignore_globbed_names (names, name_func)
457 char **names;
458 sh_ignore_func_t *name_func;
459 {
460 char **newnames;
461 int n, i;
462
463 for (i = 0; names[i]; i++)
464 ;
465 newnames = strvec_create (i + 1);
466
467 for (n = i = 0; names[i]; i++)
468 {
469 if ((*name_func) (names[i]))
470 newnames[n++] = names[i];
471 else
472 free (names[i]);
473 }
474
475 newnames[n] = (char *)NULL;
476
477 if (n == 0)
478 {
479 names[0] = (char *)NULL;
480 free (newnames);
481 return;
482 }
483
484 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
485 new array end. */
486 for (n = 0; newnames[n]; n++)
487 names[n] = newnames[n];
488 names[n] = (char *)NULL;
489 free (newnames);
490 }
491
492 void
493 ignore_glob_matches (names)
494 char **names;
495 {
496 if (globignore.num_ignores == 0)
497 return;
498
499 ignore_globbed_names (names, glob_name_is_acceptable);
500 }
501
502 static char *
503 split_ignorespec (s, ip)
504 char *s;
505 int *ip;
506 {
507 char *t;
508 int n, i;
509
510 if (s == 0)
511 return 0;
512
513 i = *ip;
514 if (s[i] == 0)
515 return 0;
516
517 n = skip_to_delim (s, i, ":", SD_NOJMP|SD_EXTGLOB);
518 t = substring (s, i, n);
519
520 if (s[n] == ':')
521 n++;
522 *ip = n;
523 return t;
524 }
525
526 void
527 setup_ignore_patterns (ivp)
528 struct ignorevar *ivp;
529 {
530 int numitems, maxitems, ptr;
531 char *colon_bit, *this_ignoreval;
532 struct ign *p;
533
534 this_ignoreval = get_string_value (ivp->varname);
535
536 /* If nothing has changed then just exit now. */
537 if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
538 (!this_ignoreval && !ivp->last_ignoreval))
539 return;
540
541 /* Oops. The ignore variable has changed. Re-parse it. */
542 ivp->num_ignores = 0;
543
544 if (ivp->ignores)
545 {
546 for (p = ivp->ignores; p->val; p++)
547 free(p->val);
548 free (ivp->ignores);
549 ivp->ignores = (struct ign *)NULL;
550 }
551
552 if (ivp->last_ignoreval)
553 {
554 free (ivp->last_ignoreval);
555 ivp->last_ignoreval = (char *)NULL;
556 }
557
558 if (this_ignoreval == 0 || *this_ignoreval == '\0')
559 return;
560
561 ivp->last_ignoreval = savestring (this_ignoreval);
562
563 numitems = maxitems = ptr = 0;
564
565 #if 0
566 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
567 #else
568 while (colon_bit = split_ignorespec (this_ignoreval, &ptr))
569 #endif
570 {
571 if (numitems + 1 >= maxitems)
572 {
573 maxitems += 10;
574 ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
575 }
576 ivp->ignores[numitems].val = colon_bit;
577 ivp->ignores[numitems].len = strlen (colon_bit);
578 ivp->ignores[numitems].flags = 0;
579 if (ivp->item_func)
580 (*ivp->item_func) (&ivp->ignores[numitems]);
581 numitems++;
582 }
583 ivp->ignores[numitems].val = (char *)NULL;
584 ivp->num_ignores = numitems;
585 }