]>
git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
1 /* pathexp.c -- The shell interface to the globbing library. */
3 /* Copyright (C) 1995-2014 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
== '(') /*)*/
99 /* A pattern can't end with a backslash, but a backslash in the pattern
100 can be removed by the matching engine, so we have to run it through
103 if (*string
!= '\0' && *string
!= '/')
109 else if (*string
== 0)
113 if (*string
++ == '\0')
117 /* Advance one fewer byte than an entire multibyte character to
118 account for the auto-increment in the loop above. */
119 #ifdef HANDLE_MULTIBYTE
121 ADVANCE_CHAR_P (string
, send
- string
);
124 ADVANCE_CHAR_P (string
, send
- string
);
128 return (bsquote
? 2 : 0);
131 /* Return 1 if C is a character that is `special' in a POSIX ERE and needs to
132 be quoted to match itself. */
173 if (s
[1] == '(') /*(*/
180 /* PATHNAME can contain characters prefixed by CTLESC; this indicates
181 that the character is to be quoted. We quote it here in the style
182 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
183 we change quoted null strings (pathname[0] == CTLNUL) into empty
184 strings (pathname[0] == 0). If this is called after quote removal
185 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
186 removal has not been done (for example, before attempting to match a
187 pattern while executing a case statement), flags should include
188 QGLOB_CVTNULL. If flags includes QGLOB_CTLESC, we need to remove CTLESC
189 quoting CTLESC or CTLNUL (as if dequote_string were called). If flags
190 includes QGLOB_FILENAME, appropriate quoting to match a filename should be
191 performed. QGLOB_REGEXP means we're quoting for a Posix ERE (for
192 [[ string =~ pat ]]) and that requires some special handling. */
194 quote_string_for_globbing (pathname
, qflags
)
195 const char *pathname
;
200 int cclass
, collsym
, equiv
, c
, last_was_backslash
;
203 temp
= (char *)xmalloc (2 * strlen (pathname
) + 1);
205 if ((qflags
& QGLOB_CVTNULL
) && QUOTED_NULL (pathname
))
211 cclass
= collsym
= equiv
= last_was_backslash
= 0;
212 for (i
= j
= 0; pathname
[i
]; i
++)
214 /* Fix for CTLESC at the end of the string? */
215 if (pathname
[i
] == CTLESC
&& pathname
[i
+1] == '\0')
217 temp
[j
++] = pathname
[i
++];
220 /* If we are parsing regexp, turn CTLESC CTLESC into CTLESC. It's not an
221 ERE special character, so we should just be able to pass it through. */
222 else if ((qflags
& (QGLOB_REGEXP
|QGLOB_CTLESC
)) && pathname
[i
] == CTLESC
&& (pathname
[i
+1] == CTLESC
|| pathname
[i
+1] == CTLNUL
))
225 temp
[j
++] = pathname
[i
];
228 else if (pathname
[i
] == CTLESC
)
230 if ((qflags
& QGLOB_FILENAME
) && pathname
[i
+1] == '/')
232 /* What to do if preceding char is backslash? */
233 if (pathname
[i
+1] != CTLESC
&& (qflags
& QGLOB_REGEXP
) && ere_char (pathname
[i
+1]) == 0)
237 if (pathname
[i
] == '\0')
240 else if ((qflags
& QGLOB_REGEXP
) && (i
== 0 || pathname
[i
-1] != CTLESC
) && pathname
[i
] == '[') /*]*/
242 temp
[j
++] = pathname
[i
++]; /* open bracket */
245 c
= pathname
[i
++]; /* c == char after open bracket */
246 if (c
== '^') /* ignore pattern negation */
251 if (c
== ']') /* ignore right bracket if first char */
260 else if (c
== CTLESC
)
262 /* skip c, check for EOS, let assignment at end of loop */
263 /* pathname[i] == backslash-escaped character */
264 if (pathname
[i
] == 0)
266 temp
[j
++] = pathname
[i
++];
268 else if (c
== '[' && pathname
[i
] == ':')
271 temp
[j
++] = pathname
[i
++];
274 else if (cclass
&& c
== ':' && pathname
[i
] == ']')
277 temp
[j
++] = pathname
[i
++];
280 else if (c
== '[' && pathname
[i
] == '=')
283 temp
[j
++] = pathname
[i
++];
284 if (pathname
[i
] == ']')
285 temp
[j
++] = pathname
[i
++]; /* right brack can be in equiv */
288 else if (equiv
&& c
== '=' && pathname
[i
] == ']')
291 temp
[j
++] = pathname
[i
++];
294 else if (c
== '[' && pathname
[i
] == '.')
297 temp
[j
++] = pathname
[i
++];
298 if (pathname
[i
] == ']')
299 temp
[j
++] = pathname
[i
++]; /* right brack can be in collsym */
302 else if (collsym
&& c
== '.' && pathname
[i
] == ']')
305 temp
[j
++] = pathname
[i
++];
311 while (((c
= pathname
[i
++]) != ']') && c
!= 0);
313 /* If we don't find the closing bracket before we hit the end of
314 the string, rescan string without treating it as a bracket
315 expression (has implications for backslash and special ERE
319 i
= savei
- 1; /* -1 for autoincrement above */
324 temp
[j
++] = c
; /* closing right bracket */
325 i
--; /* increment will happen above in loop */
326 continue; /* skip double assignment below */
328 else if (pathname
[i
] == '\\' && (qflags
& QGLOB_REGEXP
) == 0)
330 /* XXX - if not quoting regexp, use backslash as quote char. Should
331 we just pass it through without treating it as special? That is
332 what ksh93 seems to do. */
334 /* If we want to pass through backslash unaltered, comment out these
339 if (pathname
[i
] == '\0')
341 /* If we are turning CTLESC CTLESC into CTLESC, we need to do that
342 even when the first CTLESC is preceded by a backslash. */
343 if ((qflags
& QGLOB_CTLESC
) && pathname
[i
] == CTLESC
&& (pathname
[i
+1] == CTLESC
|| pathname
[i
+1] == CTLNUL
))
344 i
++; /* skip over the CTLESC */
346 else if (pathname
[i
] == '\\' && (qflags
& QGLOB_REGEXP
))
347 last_was_backslash
= 1;
348 temp
[j
++] = pathname
[i
];
357 quote_globbing_chars (string
)
362 const char *s
, *send
;
365 slen
= strlen (string
);
366 send
= string
+ slen
;
368 temp
= (char *)xmalloc (slen
* 2 + 1);
369 for (t
= temp
, s
= string
; *s
; )
374 /* Copy a single (possibly multibyte) character from s to t,
375 incrementing both. */
376 COPY_CHAR_P (t
, s
, send
);
382 /* Call the glob library to do globbing on PATHNAME. */
384 shell_glob_filename (pathname
)
385 const char *pathname
;
387 #if defined (USE_POSIX_GLOB_LIBRARY)
389 char *temp
, **results
;
393 temp
= quote_string_for_globbing (pathname
, QGLOB_FILENAME
);
395 filenames
.gl_offs
= 0;
397 # if defined (GLOB_PERIOD)
398 glob_flags
= glob_dot_filenames
? GLOB_PERIOD
: 0;
401 # endif /* !GLOB_PERIOD */
403 glob_flags
|= (GLOB_ERR
| GLOB_DOOFFS
);
405 i
= glob (temp
, glob_flags
, (posix_glob_errfunc_t
*)NULL
, &filenames
);
409 if (i
== GLOB_NOSPACE
|| i
== GLOB_ABORTED
)
410 return ((char **)NULL
);
411 else if (i
== GLOB_NOMATCH
)
412 filenames
.gl_pathv
= (char **)NULL
;
413 else if (i
!= 0) /* other error codes not in POSIX.2 */
414 filenames
.gl_pathv
= (char **)NULL
;
416 results
= filenames
.gl_pathv
;
418 if (results
&& ((GLOB_FAILED (results
)) == 0))
420 if (should_ignore_glob_matches ())
421 ignore_glob_matches (results
);
422 if (results
&& results
[0])
423 strvec_sort (results
);
427 results
= (char **)NULL
;
433 #else /* !USE_POSIX_GLOB_LIBRARY */
435 char *temp
, **results
;
438 noglob_dot_filenames
= glob_dot_filenames
== 0;
440 temp
= quote_string_for_globbing (pathname
, QGLOB_FILENAME
);
441 gflags
= glob_star
? GX_GLOBSTAR
: 0;
442 results
= glob_filename (temp
, gflags
);
445 if (results
&& ((GLOB_FAILED (results
)) == 0))
447 if (should_ignore_glob_matches ())
448 ignore_glob_matches (results
);
449 if (results
&& results
[0])
450 strvec_sort (results
);
454 results
= (char **)&glob_error_return
;
459 #endif /* !USE_POSIX_GLOB_LIBRARY */
462 /* Stuff for GLOBIGNORE. */
464 static struct ignorevar globignore
=
470 (sh_iv_item_func_t
*)0,
473 /* Set up to ignore some glob matches because the value of GLOBIGNORE
474 has changed. If GLOBIGNORE is being unset, we also need to disable
475 the globbing of filenames beginning with a `.'. */
477 setup_glob_ignore (name
)
482 v
= get_string_value (name
);
483 setup_ignore_patterns (&globignore
);
485 if (globignore
.num_ignores
)
486 glob_dot_filenames
= 1;
488 glob_dot_filenames
= 0;
492 should_ignore_glob_matches ()
494 return globignore
.num_ignores
;
497 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
499 glob_name_is_acceptable (name
)
505 /* . and .. are never matched */
506 if (name
[0] == '.' && (name
[1] == '\0' || (name
[1] == '.' && name
[2] == '\0')))
509 flags
= FNM_PATHNAME
| FNMATCH_EXTFLAG
| FNMATCH_NOCASEGLOB
;
510 for (p
= globignore
.ignores
; p
->val
; p
++)
512 if (strmatch (p
->val
, (char *)name
, flags
) != FNM_NOMATCH
)
518 /* Internal function to test whether filenames in NAMES should be
519 ignored. NAME_FUNC is a pointer to a function to call with each
520 name. It returns non-zero if the name is acceptable to the particular
521 ignore function which called _ignore_names; zero if the name should
522 be removed from NAMES. */
525 ignore_globbed_names (names
, name_func
)
527 sh_ignore_func_t
*name_func
;
532 for (i
= 0; names
[i
]; i
++)
534 newnames
= strvec_create (i
+ 1);
536 for (n
= i
= 0; names
[i
]; i
++)
538 if ((*name_func
) (names
[i
]))
539 newnames
[n
++] = names
[i
];
544 newnames
[n
] = (char *)NULL
;
548 names
[0] = (char *)NULL
;
553 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
555 for (n
= 0; newnames
[n
]; n
++)
556 names
[n
] = newnames
[n
];
557 names
[n
] = (char *)NULL
;
562 ignore_glob_matches (names
)
565 if (globignore
.num_ignores
== 0)
568 ignore_globbed_names (names
, glob_name_is_acceptable
);
572 split_ignorespec (s
, ip
)
586 n
= skip_to_delim (s
, i
, ":", SD_NOJMP
|SD_EXTGLOB
|SD_GLOB
);
587 t
= substring (s
, i
, n
);
596 setup_ignore_patterns (ivp
)
597 struct ignorevar
*ivp
;
599 int numitems
, maxitems
, ptr
;
600 char *colon_bit
, *this_ignoreval
;
603 this_ignoreval
= get_string_value (ivp
->varname
);
605 /* If nothing has changed then just exit now. */
606 if ((this_ignoreval
&& ivp
->last_ignoreval
&& STREQ (this_ignoreval
, ivp
->last_ignoreval
)) ||
607 (!this_ignoreval
&& !ivp
->last_ignoreval
))
610 /* Oops. The ignore variable has changed. Re-parse it. */
611 ivp
->num_ignores
= 0;
615 for (p
= ivp
->ignores
; p
->val
; p
++)
618 ivp
->ignores
= (struct ign
*)NULL
;
621 if (ivp
->last_ignoreval
)
623 free (ivp
->last_ignoreval
);
624 ivp
->last_ignoreval
= (char *)NULL
;
627 if (this_ignoreval
== 0 || *this_ignoreval
== '\0')
630 ivp
->last_ignoreval
= savestring (this_ignoreval
);
632 numitems
= maxitems
= ptr
= 0;
635 while (colon_bit
= extract_colon_unit (this_ignoreval
, &ptr
))
637 while (colon_bit
= split_ignorespec (this_ignoreval
, &ptr
))
640 if (numitems
+ 1 >= maxitems
)
643 ivp
->ignores
= (struct ign
*)xrealloc (ivp
->ignores
, maxitems
* sizeof (struct ign
));
645 ivp
->ignores
[numitems
].val
= colon_bit
;
646 ivp
->ignores
[numitems
].len
= strlen (colon_bit
);
647 ivp
->ignores
[numitems
].flags
= 0;
649 (*ivp
->item_func
) (&ivp
->ignores
[numitems
]);
652 ivp
->ignores
[numitems
].val
= (char *)NULL
;
653 ivp
->num_ignores
= numitems
;