/* glob.c -- file-name wildcard pattern matching for Bash.
- Copyright (C) 1985-2020 Free Software Foundation, Inc.
+ Copyright (C) 1985-2021 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne-Again SHell.
/* Global variable controlling whether globbing ever returns . or ..
regardless of the pattern. If set to 1, no glob pattern will ever
match `.' or `..'. Disabled by default. */
-int glob_always_skip_dot_and_dotdot = 0;
+int glob_always_skip_dot_and_dotdot = 1;
/* Global variable to return to signify an error in globbing. */
char *glob_error_return;
#if HANDLE_MULTIBYTE
void wcdequote_pathname PARAMS((wchar_t *));
static void wdequote_pathname PARAMS((char *));
+static void dequote_pathname PARAMS((char *));
#else
-# define dequote_pathname udequote_pathname
+# define dequote_pathname(p) udequote_pathname(p)
#endif
-static void dequote_pathname PARAMS((char *));
static int glob_testdir PARAMS((char *, int));
static char **glob_dir_to_array PARAMS((char *, char **, int));
}
#if EXTENDED_GLOB
+
+#if defined (HANDLE_MULTIBYTE)
+# define XSKIPNAME(p, d, f) mbskipname(p, d, f)
+#else
+# define XSKIPNAME(p, d, f) skipname(p, d, f)
+#endif
+
/* Return 1 if all subpatterns in the extended globbing pattern PAT indicate
that the name should be skipped. XXX - doesn't handle pattern negation,
not sure if it should */
int flags;
{
char *pp, *pe, *t, *se;
- int n, r, negate, wild, nullpat;
+ int n, r, negate, wild, nullpat, xflags;
negate = *pat == '!';
wild = *pat == '*' || *pat == '?';
pp = pat + 2;
- se = pp + strlen (pp) - 1; /* end of string */
- pe = glob_patscan (pp, se, 0); /* end of extglob pattern (( */
- /* we should check for invalid extglob pattern here */
+ se = pp + strlen (pp); /* end of pattern string */
+ pe = glob_patscan (pp, se, 0); /* end of extglob pattern */
+
+ /* if pe == 0, this is an invalid extglob pattern */
if (pe == 0)
return 0;
+ xflags = flags | ( negate ? GX_NEGATE : 0);
+
/* if pe != se we have more of the pattern at the end of the extglob
pattern. Check the easy case first ( */
- if (pe == se && *pe == ')' && (t = strchr (pp, '|')) == 0)
+ if (pe == se && *pe == 0 && pe[-1] == ')' && (t = strchr (pp, '|')) == 0)
{
- *pe = '\0';
-#if defined (HANDLE_MULTIBYTE)
- r = mbskipname (pp, dname, flags);
-#else
- r = skipname (pp, dname, flags); /*(*/
-#endif
- *pe = ')';
- if (wild && pe[1]) /* if we can match zero instances, check further */
- return (skipname (pe+1, dname, flags));
+ pe[-1] = '\0';
+ /* This is where we check whether the pattern is being negated and
+ match all files beginning with `.' if the pattern begins with a
+ literal `.'. */
+ r = XSKIPNAME (pp, dname, xflags); /*(*/
+ pe[-1] = ')';
return r;
}
/* check every subpattern */
while (t = glob_patscan (pp, pe, '|'))
{
+ /* If T == PE and *T == 0 (&& PE[-1] == RPAREN), we have hit the end
+ of a pattern with no trailing characters. */
n = t[-1]; /* ( */
- if (extglob_pattern_p (pp) && n == ')')
+ if (extglob_pattern_p (pp) && n == ')') /* nested extglob? */
t[-1] = n; /* no-op for now */
else
t[-1] = '\0';
-#if defined (HANDLE_MULTIBYTE)
- r = mbskipname (pp, dname, flags);
-#else
- r = skipname (pp, dname, flags);
-#endif
+ r = XSKIPNAME (pp, dname, xflags);
t[-1] = n;
if (r == 0) /* if any pattern says not skip, we don't skip */
return r;
pp = t;
- } /*(*/
+ if (pp == pe)
+ break;
+ }
/* glob_patscan might find end of string */
if (pp == se)
/* but if it doesn't then we didn't match a leading dot */
if (wild && *pe) /* if we can match zero instances, check further */
- return (skipname (pe, dname, flags));
+ return (XSKIPNAME (pe, dname, flags));
+
return 1;
}
#endif
char *dname;
int flags;
{
+ int i;
+
#if EXTENDED_GLOB
if (extglob_pattern_p (pat)) /* XXX */
return (extglob_skipname (pat, dname, flags));
DOT_OR_DOTDOT (dname))
return 1;
+#if 0
+ /* This is where we check whether the pattern is being negated and
+ match all files beginning with `.' if the pattern begins with a
+ literal `.'. This is the negation of the next clause. */
+ else if ((flags & GX_NEGATE) && noglob_dot_filenames == 0 &&
+ dname[0] == '.' &&
+ (pat[0] == '.' || (pat[0] == '\\' && pat[1] == '.')))
+ return 0;
+#endif
+
/* If a dot must be explicitly matched, check to see if they do. */
- else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
- (pat[0] != '\\' || pat[1] != '.'))
+ else if (noglob_dot_filenames && dname[0] == '.' &&
+ pat[0] != '.' && (pat[0] != '\\' || pat[1] != '.'))
return 1;
return 0;
wchar_t *pat, *dname;
int flags;
{
+ int i;
+
if (glob_always_skip_dot_and_dotdot && WDOT_OR_DOTDOT (dname))
return 1;
WDOT_OR_DOTDOT (dname))
return 1;
+#if 0
+ /* This is where we check whether the pattern is being negated and
+ match all files beginning with `.' if the pattern begins with a
+ literal `.'. This is the negation of the next clause. */
+ else if ((flags & GX_NEGATE) && noglob_dot_filenames == 0 &&
+ dname[0] == L'.' &&
+ (pat[0] == L'.' || (pat[0] == L'\\' && pat[1] == L'.')))
+ return 0;
+#endif
+
/* If a leading dot must be explicitly matched, check to see if the
pattern and dirname both have one. */
- else if (noglob_dot_filenames && dname[0] == L'.' &&
- pat[0] != L'.' &&
- (pat[0] != L'\\' || pat[1] != L'.'))
+ else if (noglob_dot_filenames && dname[0] == L'.' &&
+ pat[0] != L'.' && (pat[0] != L'\\' || pat[1] != L'.'))
return 1;
return 0;
int flags;
{
#if EXTENDED_GLOB
- wchar_t *pp, *pe, *t, n, *se;
- int r, negate, wild, nullpat;
+ wchar_t *pp, *pe, *t, *se, n;
+ int r, negate, wild, nullpat, xflags;
negate = *pat == L'!';
wild = *pat == L'*' || *pat == L'?';
pp = pat + 2;
- se = pp + wcslen (pp) - 1; /*(*/
+ se = pp + wcslen (pp);
pe = glob_patscan_wc (pp, se, 0);
- if (pe == se && *pe == ')' && (t = wcschr (pp, L'|')) == 0)
+ /* if pe == 0, this is an invalid extglob pattern */
+ if (pe == 0)
+ return 0;
+
+ xflags = flags | ( negate ? GX_NEGATE : 0);
+
+ /* if pe != se we have more of the pattern at the end of the extglob
+ pattern. Check the easy case first ( */
+ if (pe == se && *pe == L'\0' && pe[-1] == L')' && (t = wcschr (pp, L'|')) == 0)
{
- *pe = L'\0';
- r = wskipname (pp, dname, flags); /*(*/
- *pe = L')';
- if (wild && pe[1] != L'\0')
- return (wskipname (pe+1, dname, flags));
+ pe[-1] = L'\0';
+ r = wskipname (pp, dname, xflags); /*(*/
+ pe[-1] = L')';
return r;
}
while (t = glob_patscan_wc (pp, pe, '|'))
{
n = t[-1]; /* ( */
- if (wextglob_pattern_p (pp) && n == L')')
+ if (wextglob_pattern_p (pp) && n == L')') /* nested extglob? */
t[-1] = n; /* no-op for now */
else
t[-1] = L'\0';
- r = wskipname (pp, dname, flags);
+ r = wskipname (pp, dname, xflags);
t[-1] = n;
if (r == 0)
return 0;
pp = t;
+ if (pp == pe)
+ break;
}
- if (pp == pe) /* glob_patscan_wc might find end of pattern */
+ /* glob_patscan_wc might find end of string */
+ if (pp == se)
return r;
/* but if it doesn't then we didn't match a leading dot */
if (wild && *pe != L'\0')
return (wskipname (pe, dname, flags));
+
return 1;
#else
return (wskipname (pat, dname, flags));
/* Compute the flags that will be passed to strmatch(). We don't
need to do this every time through the loop. */
- mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
+ mflags = (noglob_dot_filenames ? FNM_PERIOD : FNM_DOTDOT) | FNM_PATHNAME;
#ifdef FNM_CASEFOLD
if (glob_ignore_case)
add_current = ((flags & (GX_ALLDIRS|GX_ADDCURDIR)) == (GX_ALLDIRS|GX_ADDCURDIR));
- /* Scan the directory, finding all names that match.
- For each name that matches, allocate a struct globval
+ /* Scan the directory, finding all names that match For each name that matches, allocate a struct globval
on the stack and store the name in it.
Chain those structs together; lastlink is the front of the chain. */
while (1)
nextname = (char *) malloc (sdlen + 1);
if (nextlink == 0 || nextname == 0)
{
+ if (firstmalloc && firstmalloc == nextlink)
+ firstmalloc = 0;
+ /* If we reset FIRSTMALLOC we can free this here. */
FREE (nextlink);
FREE (nextname);
free (subdir);
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
if (nextlink == 0 || nextname == 0)
{
- FREE (nextlink);
+ /* We free NEXTLINK here, since it won't be added to the
+ LASTLINK chain. If we used malloc, and it returned non-
+ NULL, firstmalloc will be set to something valid. If it's
+ NEXTLINK, reset it before we free NEXTLINK to avoid
+ duplicate frees. If not, it will be taken care of by the
+ loop below with TMPLINK. */
+ if (firstmalloc)
+ {
+ if (firstmalloc == nextlink)
+ firstmalloc = 0;
+ FREE (nextlink);
+ }
FREE (nextname);
lose = 1;
break;
/* compat: if GX_ADDCURDIR, add the passed directory also. Add an empty
directory name as a placeholder if GX_NULLDIR (in which case the passed
directory name is "."). */
- if (add_current)
+ if (add_current && lose == 0)
{
sdlen = strlen (dir);
nextname = (char *)malloc (sdlen + 1);
lose |= name_vector == NULL;
}
- /* Have we run out of memory? */
+ /* Have we run out of memory or been interrupted? */
if (lose)
{
tmplink = 0;
{
if (free_dirname)
free (directory_name);
+ free ((char *) result);
return ((char **)&glob_error_return);
}
}