]>
git.ipfire.org Git - thirdparty/bash.git/blob - pathexp.c
1 /* pathexp.c -- The shell interface to the globbing library. */
3 /* Copyright (C) 1995-2020 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
PARAMS((const char *));
42 static void ignore_globbed_names
PARAMS((char **, sh_ignore_func_t
*));
43 static char *split_ignorespec
PARAMS((char *, int *));
45 #include <glob/glob.h>
47 /* Control whether * matches .files in globbing. */
48 int glob_dot_filenames
;
50 /* Control whether the extended globbing features are enabled. */
51 int extended_glob
= EXTGLOB_DEFAULT
;
53 /* Control enabling special handling of `**' */
56 /* Return nonzero if STRING has any unquoted special globbing chars in it.
57 This is supposed to be called when pathname expansion is performed, so
58 it implements the rules in Posix 2.13.3, specifically that an unquoted
59 slash cannot appear in a bracket expression. */
61 unquoted_glob_pattern_p (string
)
62 register char *string
;
71 send
= string
+ strlen (string
);
86 if (open
) /* XXX - if --open == 0? */
97 if (*string
== '(') /*)*/
101 /* A pattern can't end with a backslash, but a backslash in the pattern
102 can be special to the matching engine, so we note it in case we
105 if (*string
!= '\0' && *string
!= '/')
111 else if (open
&& *string
== '/')
113 string
++; /* quoted slashes in bracket expressions are ok */
116 else if (*string
== 0)
120 if (*string
++ == '\0')
124 /* Advance one fewer byte than an entire multibyte character to
125 account for the auto-increment in the loop above. */
126 #ifdef HANDLE_MULTIBYTE
128 ADVANCE_CHAR_P (string
, send
- string
);
131 ADVANCE_CHAR_P (string
, send
- string
);
136 return (bsquote
? 2 : 0);
142 /* Return 1 if C is a character that is `special' in a POSIX ERE and needs to
143 be quoted to match itself. */
169 /* This is only used to determine whether to backslash-quote a character. */
185 if (s
[1] == '(') /*(*/
192 /* PATHNAME can contain characters prefixed by CTLESC; this indicates
193 that the character is to be quoted. We quote it here in the style
194 that the glob library recognizes. If flags includes QGLOB_CVTNULL,
195 we change quoted null strings (pathname[0] == CTLNUL) into empty
196 strings (pathname[0] == 0). If this is called after quote removal
197 is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
198 removal has not been done (for example, before attempting to match a
199 pattern while executing a case statement), flags should include
200 QGLOB_CVTNULL. If flags includes QGLOB_CTLESC, we need to remove CTLESC
201 quoting CTLESC or CTLNUL (as if dequote_string were called). If flags
202 includes QGLOB_FILENAME, appropriate quoting to match a filename should be
203 performed. QGLOB_REGEXP means we're quoting for a Posix ERE (for
204 [[ string =~ pat ]]) and that requires some special handling. */
206 quote_string_for_globbing (pathname
, qflags
)
207 const char *pathname
;
212 int cclass
, collsym
, equiv
, c
, last_was_backslash
;
215 temp
= (char *)xmalloc (2 * strlen (pathname
) + 1);
217 if ((qflags
& QGLOB_CVTNULL
) && QUOTED_NULL (pathname
))
223 cclass
= collsym
= equiv
= last_was_backslash
= 0;
224 for (i
= j
= 0; pathname
[i
]; i
++)
226 /* Fix for CTLESC at the end of the string? */
227 if (pathname
[i
] == CTLESC
&& pathname
[i
+1] == '\0')
229 temp
[j
++] = pathname
[i
++];
232 /* If we are parsing regexp, turn CTLESC CTLESC into CTLESC. It's not an
233 ERE special character, so we should just be able to pass it through. */
234 else if ((qflags
& (QGLOB_REGEXP
|QGLOB_CTLESC
)) && pathname
[i
] == CTLESC
&& (pathname
[i
+1] == CTLESC
|| pathname
[i
+1] == CTLNUL
))
237 temp
[j
++] = pathname
[i
];
240 else if (pathname
[i
] == CTLESC
)
242 convert_to_backslash
:
243 if ((qflags
& QGLOB_FILENAME
) && pathname
[i
+1] == '/')
245 /* What to do if preceding char is backslash? */
246 if (pathname
[i
+1] != CTLESC
&& (qflags
& QGLOB_REGEXP
) && ere_char (pathname
[i
+1]) == 0)
250 if (pathname
[i
] == '\0')
253 else if ((qflags
& QGLOB_REGEXP
) && (i
== 0 || pathname
[i
-1] != CTLESC
) && pathname
[i
] == '[') /*]*/
255 temp
[j
++] = pathname
[i
++]; /* open bracket */
258 c
= pathname
[i
++]; /* c == char after open bracket */
259 if (c
== '^') /* ignore pattern negation */
264 if (c
== ']') /* ignore right bracket if first char */
273 else if (c
== CTLESC
)
275 /* skip c, check for EOS, let assignment at end of loop */
276 /* pathname[i] == backslash-escaped character */
277 if (pathname
[i
] == 0)
279 temp
[j
++] = pathname
[i
++];
281 else if (c
== '[' && pathname
[i
] == ':')
284 temp
[j
++] = pathname
[i
++];
287 else if (cclass
&& c
== ':' && pathname
[i
] == ']')
290 temp
[j
++] = pathname
[i
++];
293 else if (c
== '[' && pathname
[i
] == '=')
296 temp
[j
++] = pathname
[i
++];
297 if (pathname
[i
] == ']')
298 temp
[j
++] = pathname
[i
++]; /* right brack can be in equiv */
301 else if (equiv
&& c
== '=' && pathname
[i
] == ']')
304 temp
[j
++] = pathname
[i
++];
307 else if (c
== '[' && pathname
[i
] == '.')
310 temp
[j
++] = pathname
[i
++];
311 if (pathname
[i
] == ']')
312 temp
[j
++] = pathname
[i
++]; /* right brack can be in collsym */
315 else if (collsym
&& c
== '.' && pathname
[i
] == ']')
318 temp
[j
++] = pathname
[i
++];
324 while (((c
= pathname
[i
++]) != ']') && c
!= 0);
326 /* If we don't find the closing bracket before we hit the end of
327 the string, rescan string without treating it as a bracket
328 expression (has implications for backslash and special ERE
332 i
= savei
- 1; /* -1 for autoincrement above */
337 temp
[j
++] = c
; /* closing right bracket */
338 i
--; /* increment will happen above in loop */
339 continue; /* skip double assignment below */
341 else if (pathname
[i
] == '\\' && (qflags
& QGLOB_REGEXP
) == 0)
343 /* XXX - if not quoting regexp, use backslash as quote char. Should
344 We just pass it through without treating it as special? That is
345 what ksh93 seems to do. */
347 /* If we want to pass through backslash unaltered, comment out these
352 if (pathname
[i
] == '\0')
354 /* If we are turning CTLESC CTLESC into CTLESC, we need to do that
355 even when the first CTLESC is preceded by a backslash. */
356 if ((qflags
& QGLOB_CTLESC
) && pathname
[i
] == CTLESC
&& (pathname
[i
+1] == CTLESC
|| pathname
[i
+1] == CTLNUL
))
357 i
++; /* skip over the CTLESC */
358 else if ((qflags
& QGLOB_CTLESC
) && pathname
[i
] == CTLESC
)
359 /* A little more general: if there is an unquoted backslash in the
360 pattern and we are handling quoted characters in the pattern,
361 convert the CTLESC to backslash and add the next character on
362 the theory that the backslash will quote the next character
363 but it would be inconsistent not to replace the CTLESC with
364 another backslash here. We can't tell at this point whether the
365 CTLESC comes from a backslash or other form of quoting in the
367 goto convert_to_backslash
;
369 else if (pathname
[i
] == '\\' && (qflags
& QGLOB_REGEXP
))
370 last_was_backslash
= 1;
371 temp
[j
++] = pathname
[i
];
380 quote_globbing_chars (string
)
385 const char *s
, *send
;
388 slen
= strlen (string
);
389 send
= string
+ slen
;
391 temp
= (char *)xmalloc (slen
* 2 + 1);
392 for (t
= temp
, s
= string
; *s
; )
397 /* Copy a single (possibly multibyte) character from s to t,
398 incrementing both. */
399 COPY_CHAR_P (t
, s
, send
);
405 /* Call the glob library to do globbing on PATHNAME. */
407 shell_glob_filename (pathname
, qflags
)
408 const char *pathname
;
411 char *temp
, **results
;
412 int gflags
, quoted_pattern
;
414 noglob_dot_filenames
= glob_dot_filenames
== 0;
416 temp
= quote_string_for_globbing (pathname
, QGLOB_FILENAME
|qflags
);
417 gflags
= glob_star
? GX_GLOBSTAR
: 0;
418 results
= glob_filename (temp
, gflags
);
421 if (results
&& ((GLOB_FAILED (results
)) == 0))
423 if (should_ignore_glob_matches ())
424 ignore_glob_matches (results
);
425 if (results
&& results
[0])
426 strvec_sort (results
, 1); /* posix sort */
430 results
= (char **)&glob_error_return
;
437 /* Stuff for GLOBIGNORE. */
439 static struct ignorevar globignore
=
445 (sh_iv_item_func_t
*)0,
448 /* Set up to ignore some glob matches because the value of GLOBIGNORE
449 has changed. If GLOBIGNORE is being unset, we also need to disable
450 the globbing of filenames beginning with a `.'. */
452 setup_glob_ignore (name
)
457 v
= get_string_value (name
);
458 setup_ignore_patterns (&globignore
);
460 if (globignore
.num_ignores
)
461 glob_dot_filenames
= 1;
463 glob_dot_filenames
= 0;
467 should_ignore_glob_matches ()
469 return globignore
.num_ignores
;
472 /* Return 0 if NAME matches a pattern in the globignore.ignores list. */
474 glob_name_is_acceptable (name
)
481 /* . and .. are never matched. We extend this to the terminal component of a
483 n
= strrchr (name
, '/');
484 if (n
== 0 || n
[1] == 0)
489 if (n
[0] == '.' && (n
[1] == '\0' || (n
[1] == '.' && n
[2] == '\0')))
492 flags
= FNM_PATHNAME
| FNMATCH_EXTFLAG
| FNMATCH_NOCASEGLOB
;
493 for (p
= globignore
.ignores
; p
->val
; p
++)
495 if (strmatch (p
->val
, (char *)name
, flags
) != FNM_NOMATCH
)
501 /* Internal function to test whether filenames in NAMES should be
502 ignored. NAME_FUNC is a pointer to a function to call with each
503 name. It returns non-zero if the name is acceptable to the particular
504 ignore function which called _ignore_names; zero if the name should
505 be removed from NAMES. */
508 ignore_globbed_names (names
, name_func
)
510 sh_ignore_func_t
*name_func
;
515 for (i
= 0; names
[i
]; i
++)
517 newnames
= strvec_create (i
+ 1);
519 for (n
= i
= 0; names
[i
]; i
++)
521 if ((*name_func
) (names
[i
]))
522 newnames
[n
++] = names
[i
];
527 newnames
[n
] = (char *)NULL
;
531 names
[0] = (char *)NULL
;
536 /* Copy the acceptable names from NEWNAMES back to NAMES and set the
538 for (n
= 0; newnames
[n
]; n
++)
539 names
[n
] = newnames
[n
];
540 names
[n
] = (char *)NULL
;
545 ignore_glob_matches (names
)
548 if (globignore
.num_ignores
== 0)
551 ignore_globbed_names (names
, glob_name_is_acceptable
);
555 split_ignorespec (s
, ip
)
569 n
= skip_to_delim (s
, i
, ":", SD_NOJMP
|SD_EXTGLOB
|SD_GLOB
);
570 t
= substring (s
, i
, n
);
579 setup_ignore_patterns (ivp
)
580 struct ignorevar
*ivp
;
582 int numitems
, maxitems
, ptr
;
583 char *colon_bit
, *this_ignoreval
;
586 this_ignoreval
= get_string_value (ivp
->varname
);
588 /* If nothing has changed then just exit now. */
589 if ((this_ignoreval
&& ivp
->last_ignoreval
&& STREQ (this_ignoreval
, ivp
->last_ignoreval
)) ||
590 (!this_ignoreval
&& !ivp
->last_ignoreval
))
593 /* Oops. The ignore variable has changed. Re-parse it. */
594 ivp
->num_ignores
= 0;
598 for (p
= ivp
->ignores
; p
->val
; p
++)
601 ivp
->ignores
= (struct ign
*)NULL
;
604 if (ivp
->last_ignoreval
)
606 free (ivp
->last_ignoreval
);
607 ivp
->last_ignoreval
= (char *)NULL
;
610 if (this_ignoreval
== 0 || *this_ignoreval
== '\0')
613 ivp
->last_ignoreval
= savestring (this_ignoreval
);
615 numitems
= maxitems
= ptr
= 0;
618 while (colon_bit
= extract_colon_unit (this_ignoreval
, &ptr
))
620 while (colon_bit
= split_ignorespec (this_ignoreval
, &ptr
))
623 if (numitems
+ 1 >= maxitems
)
626 ivp
->ignores
= (struct ign
*)xrealloc (ivp
->ignores
, maxitems
* sizeof (struct ign
));
628 ivp
->ignores
[numitems
].val
= colon_bit
;
629 ivp
->ignores
[numitems
].len
= strlen (colon_bit
);
630 ivp
->ignores
[numitems
].flags
= 0;
632 (*ivp
->item_func
) (&ivp
->ignores
[numitems
]);
635 ivp
->ignores
[numitems
].val
= (char *)NULL
;
636 ivp
->num_ignores
= numitems
;