]>
git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
1 /* pathexp.c -- The shell interface to the globbing library. */
3 /* Copyright (C) 1995-2009 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
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/>.
23 #include "bashtypes.h"
26 #if defined (HAVE_UNISTD_H)
39 #include <glob/strmatch.h>
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 *));
45 #if defined (USE_POSIX_GLOB_LIBRARY)
47 typedef int posix_glob_errfunc_t
__P((const char *, int));
49 # include <glob/glob.h>
52 /* Control whether * matches .files in globbing. */
53 int glob_dot_filenames
;
55 /* Control whether the extended globbing features are enabled. */
56 int extended_glob
= EXTGLOB_DEFAULT
;
58 /* Control enabling special handling of `**' */
61 /* Return nonzero if STRING has any unquoted special globbing chars in it. */
63 unquoted_glob_pattern_p (string
)
64 register char *string
;
73 send
= string
+ strlen (string
);
95 if (*string
== '(') /*)*/
101 if (*string
++ == '\0')
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
109 ADVANCE_CHAR_P (string
, send
- string
);
112 ADVANCE_CHAR_P (string
, send
- string
);
118 /* Return 1 if C is a character that is `special' in a POSIX ERE and needs to
119 be quoted to match itself. */
160 if (s
[1] == '(') /*(*/
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. */
180 quote_string_for_globbing (pathname
, qflags
)
181 const char *pathname
;
186 int brack
, cclass
, collsym
, equiv
, c
;
188 temp
= (char *)xmalloc (strlen (pathname
) + 1);
190 if ((qflags
& QGLOB_CVTNULL
) && QUOTED_NULL (pathname
))
196 brack
= cclass
= collsym
= equiv
= 0;
197 for (i
= j
= 0; pathname
[i
]; i
++)
199 if (pathname
[i
] == CTLESC
)
201 if ((qflags
& QGLOB_FILENAME
) && pathname
[i
+1] == '/')
203 if (pathname
[i
+1] != CTLESC
&& (qflags
& QGLOB_REGEXP
) && ere_char (pathname
[i
+1]) == 0)
207 if (pathname
[i
] == '\0')
210 else if ((qflags
& QGLOB_REGEXP
) && (i
== 0 || pathname
[i
-1] != CTLESC
) && pathname
[i
] == '[') /*]*/
213 temp
[j
++] = pathname
[i
++]; /* open bracket */
214 c
= pathname
[i
++]; /* c == char after open bracket */
219 else if (c
== CTLESC
)
221 /* skip c, check for EOS, let assignment at end of loop */
222 /* pathname[i] == backslash-escaped character */
223 if (pathname
[i
] == 0)
225 temp
[j
++] = pathname
[i
++];
227 else if (c
== '[' && pathname
[i
] == ':')
230 temp
[j
++] = pathname
[i
++];
233 else if (cclass
&& c
== ':' && pathname
[i
] == ']')
236 temp
[j
++] = pathname
[i
++];
239 else if (c
== '[' && pathname
[i
] == '=')
242 temp
[j
++] = pathname
[i
++];
243 if (pathname
[i
] == ']')
244 temp
[j
++] = pathname
[i
++]; /* right brack can be in equiv */
247 else if (equiv
&& c
== '=' && pathname
[i
] == ']')
250 temp
[j
++] = pathname
[i
++];
253 else if (c
== '[' && pathname
[i
] == '.')
256 temp
[j
++] = pathname
[i
++];
257 if (pathname
[i
] == ']')
258 temp
[j
++] = pathname
[i
++]; /* right brack can be in collsym */
261 else if (collsym
&& c
== '.' && pathname
[i
] == ']')
264 temp
[j
++] = pathname
[i
++];
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 */
275 else if (pathname
[i
] == '\\')
279 if (pathname
[i
] == '\0')
282 temp
[j
++] = pathname
[i
];
291 quote_globbing_chars (string
)
295 char *temp
, *s
, *t
, *send
;
298 slen
= strlen (string
);
299 send
= string
+ slen
;
301 temp
= (char *)xmalloc (slen
* 2 + 1);
302 for (t
= temp
, s
= string
; *s
; )
307 /* Copy a single (possibly multibyte) character from s to t,
308 incrementing both. */
309 COPY_CHAR_P (t
, s
, send
);
315 /* Call the glob library to do globbing on PATHNAME. */
317 shell_glob_filename (pathname
)
318 const char *pathname
;
320 #if defined (USE_POSIX_GLOB_LIBRARY)
322 char *temp
, **results
;
326 temp
= quote_string_for_globbing (pathname
, QGLOB_FILENAME
);
328 filenames
.gl_offs
= 0;
330 # if defined (GLOB_PERIOD)
331 glob_flags
= glob_dot_filenames
? GLOB_PERIOD
: 0;
334 # endif /* !GLOB_PERIOD */
336 glob_flags
|= (GLOB_ERR
| GLOB_DOOFFS
);
338 i
= glob (temp
, glob_flags
, (posix_glob_errfunc_t
*)NULL
, &filenames
);
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
;
349 results
= filenames
.gl_pathv
;
351 if (results
&& ((GLOB_FAILED (results
)) == 0))
353 if (should_ignore_glob_matches ())
354 ignore_glob_matches (results
);
355 if (results
&& results
[0])
356 strvec_sort (results
);
360 results
= (char **)NULL
;
366 #else /* !USE_POSIX_GLOB_LIBRARY */
368 char *temp
, **results
;
370 noglob_dot_filenames
= glob_dot_filenames
== 0;
372 temp
= quote_string_for_globbing (pathname
, QGLOB_FILENAME
);
373 results
= glob_filename (temp
, glob_star
? GX_GLOBSTAR
: 0);
376 if (results
&& ((GLOB_FAILED (results
)) == 0))
378 if (should_ignore_glob_matches ())
379 ignore_glob_matches (results
);
380 if (results
&& results
[0])
381 strvec_sort (results
);
385 results
= (char **)&glob_error_return
;
390 #endif /* !USE_POSIX_GLOB_LIBRARY */
393 /* Stuff for GLOBIGNORE. */
395 static struct ignorevar globignore
=
401 (sh_iv_item_func_t
*)0,
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 `.'. */
408 setup_glob_ignore (name
)
413 v
= get_string_value (name
);
414 setup_ignore_patterns (&globignore
);
416 if (globignore
.num_ignores
)
417 glob_dot_filenames
= 1;
419 glob_dot_filenames
= 0;
423 should_ignore_glob_matches ()
425 return globignore
.num_ignores
;
428 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
430 glob_name_is_acceptable (name
)
436 /* . and .. are never matched */
437 if (name
[0] == '.' && (name
[1] == '\0' || (name
[1] == '.' && name
[2] == '\0')))
440 flags
= FNM_PATHNAME
| FNMATCH_EXTFLAG
;
441 for (p
= globignore
.ignores
; p
->val
; p
++)
443 if (strmatch (p
->val
, (char *)name
, flags
) != FNM_NOMATCH
)
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. */
456 ignore_globbed_names (names
, name_func
)
458 sh_ignore_func_t
*name_func
;
463 for (i
= 0; names
[i
]; i
++)
465 newnames
= strvec_create (i
+ 1);
467 for (n
= i
= 0; names
[i
]; i
++)
469 if ((*name_func
) (names
[i
]))
470 newnames
[n
++] = names
[i
];
475 newnames
[n
] = (char *)NULL
;
479 names
[0] = (char *)NULL
;
484 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
486 for (n
= 0; newnames
[n
]; n
++)
487 names
[n
] = newnames
[n
];
488 names
[n
] = (char *)NULL
;
493 ignore_glob_matches (names
)
496 if (globignore
.num_ignores
== 0)
499 ignore_globbed_names (names
, glob_name_is_acceptable
);
503 split_ignorespec (s
, ip
)
517 n
= skip_to_delim (s
, i
, ":", SD_NOJMP
|SD_EXTGLOB
);
518 t
= substring (s
, i
, n
);
527 setup_ignore_patterns (ivp
)
528 struct ignorevar
*ivp
;
530 int numitems
, maxitems
, ptr
;
531 char *colon_bit
, *this_ignoreval
;
534 this_ignoreval
= get_string_value (ivp
->varname
);
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
))
541 /* Oops. The ignore variable has changed. Re-parse it. */
542 ivp
->num_ignores
= 0;
546 for (p
= ivp
->ignores
; p
->val
; p
++)
549 ivp
->ignores
= (struct ign
*)NULL
;
552 if (ivp
->last_ignoreval
)
554 free (ivp
->last_ignoreval
);
555 ivp
->last_ignoreval
= (char *)NULL
;
558 if (this_ignoreval
== 0 || *this_ignoreval
== '\0')
561 ivp
->last_ignoreval
= savestring (this_ignoreval
);
563 numitems
= maxitems
= ptr
= 0;
566 while (colon_bit
= extract_colon_unit (this_ignoreval
, &ptr
))
568 while (colon_bit
= split_ignorespec (this_ignoreval
, &ptr
))
571 if (numitems
+ 1 >= maxitems
)
574 ivp
->ignores
= (struct ign
*)xrealloc (ivp
->ignores
, maxitems
* sizeof (struct ign
));
576 ivp
->ignores
[numitems
].val
= colon_bit
;
577 ivp
->ignores
[numitems
].len
= strlen (colon_bit
);
578 ivp
->ignores
[numitems
].flags
= 0;
580 (*ivp
->item_func
) (&ivp
->ignores
[numitems
]);
583 ivp
->ignores
[numitems
].val
= (char *)NULL
;
584 ivp
->num_ignores
= numitems
;