#include "config.h"
#include "bashtypes.h"
#include <stdio.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include "shell.h"
#include "pathexp.h"
#include "flags.h"
#include "shmbutil.h"
#include <glob/strmatch.h>
static int glob_name_is_acceptable __P((const char *));
static void ignore_globbed_names __P((char **, sh_ignore_func_t *));
#if defined (USE_POSIX_GLOB_LIBRARY)
# include <glob.h>
typedef int posix_glob_errfunc_t __P((const char *, int));
#else
# include <glob/glob.h>
#endif
int glob_dot_filenames;
int extended_glob = 0;
int
unquoted_glob_pattern_p (string)
register char *string;
{
register int c;
char *send;
int open;
DECLARE_MBSTATE;
open = 0;
send = string + strlen (string);
while (c = *string++)
{
switch (c)
{
case '?':
case '*':
return (1);
case '[':
open++;
continue;
case ']':
if (open)
return (1);
continue;
case '+':
case '@':
case '!':
if (*string == '(')
return (1);
continue;
case CTLESC:
case '\\':
if (*string++ == '\0')
return (0);
}
#ifdef HANDLE_MULTIBYTE
string--;
ADVANCE_CHAR_P (string, send - string);
string++;
#else
ADVANCE_CHAR_P (string, send - string);
#endif
}
return (0);
}
static inline int
ere_char (c)
int c;
{
switch (c)
{
case '.':
case '[':
case '\\':
case '(':
case ')':
case '*':
case '+':
case '?':
case '{':
case '|':
case '^':
case '$':
return 1;
default:
return 0;
}
return (0);
}
char *
quote_string_for_globbing (pathname, qflags)
const char *pathname;
int qflags;
{
char *temp;
register int i, j;
temp = (char *)xmalloc (strlen (pathname) + 1);
if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
{
temp[0] = '\0';
return temp;
}
for (i = j = 0; pathname[i]; i++)
{
if (pathname[i] == CTLESC)
{
if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
continue;
if ((qflags & QGLOB_REGEXP) && ere_char (pathname[i+1]) == 0)
continue;
temp[j++] = '\\';
i++;
if (pathname[i] == '\0')
break;
}
temp[j++] = pathname[i];
}
temp[j] = '\0';
return (temp);
}
char *
quote_globbing_chars (string)
char *string;
{
size_t slen;
char *temp, *s, *t, *send;
DECLARE_MBSTATE;
slen = strlen (string);
send = string + slen;
temp = (char *)xmalloc (slen * 2 + 1);
for (t = temp, s = string; *s; )
{
switch (*s)
{
case '*':
case '[':
case ']':
case '?':
case '\\':
*t++ = '\\';
break;
case '+':
case '@':
case '!':
if (s[1] == '(')
*t++ = '\\';
break;
}
COPY_CHAR_P (t, s, send);
}
*t = '\0';
return temp;
}
char **
shell_glob_filename (pathname)
const char *pathname;
{
#if defined (USE_POSIX_GLOB_LIBRARY)
register int i;
char *temp, **results;
glob_t filenames;
int glob_flags;
temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
filenames.gl_offs = 0;
# if defined (GLOB_PERIOD)
glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
# else
glob_flags = 0;
# endif
glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);
free (temp);
if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
return ((char **)NULL);
else if (i == GLOB_NOMATCH)
filenames.gl_pathv = (char **)NULL;
else if (i != 0)
filenames.gl_pathv = (char **)NULL;
results = filenames.gl_pathv;
if (results && ((GLOB_FAILED (results)) == 0))
{
if (should_ignore_glob_matches ())
ignore_glob_matches (results);
if (results && results[0])
strvec_sort (results);
else
{
FREE (results);
results = (char **)NULL;
}
}
return (results);
#else
char *temp, **results;
noglob_dot_filenames = glob_dot_filenames == 0;
temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
results = glob_filename (temp, 0);
free (temp);
if (results && ((GLOB_FAILED (results)) == 0))
{
if (should_ignore_glob_matches ())
ignore_glob_matches (results);
if (results && results[0])
strvec_sort (results);
else
{
FREE (results);
results = (char **)&glob_error_return;
}
}
return (results);
#endif
}
static struct ignorevar globignore =
{
"GLOBIGNORE",
(struct ign *)0,
0,
(char *)0,
(sh_iv_item_func_t *)0,
};
void
setup_glob_ignore (name)
char *name;
{
char *v;
v = get_string_value (name);
setup_ignore_patterns (&globignore);
if (globignore.num_ignores)
glob_dot_filenames = 1;
else if (v == 0)
glob_dot_filenames = 0;
}
int
should_ignore_glob_matches ()
{
return globignore.num_ignores;
}
static int
glob_name_is_acceptable (name)
const char *name;
{
struct ign *p;
int flags;
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
return (0);
flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
for (p = globignore.ignores; p->val; p++)
{
if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
return (0);
}
return (1);
}
static void
ignore_globbed_names (names, name_func)
char **names;
sh_ignore_func_t *name_func;
{
char **newnames;
int n, i;
for (i = 0; names[i]; i++)
;
newnames = strvec_create (i + 1);
for (n = i = 0; names[i]; i++)
{
if ((*name_func) (names[i]))
newnames[n++] = names[i];
else
free (names[i]);
}
newnames[n] = (char *)NULL;
if (n == 0)
{
names[0] = (char *)NULL;
free (newnames);
return;
}
for (n = 0; newnames[n]; n++)
names[n] = newnames[n];
names[n] = (char *)NULL;
free (newnames);
}
void
ignore_glob_matches (names)
char **names;
{
if (globignore.num_ignores == 0)
return;
ignore_globbed_names (names, glob_name_is_acceptable);
}
void
setup_ignore_patterns (ivp)
struct ignorevar *ivp;
{
int numitems, maxitems, ptr;
char *colon_bit, *this_ignoreval;
struct ign *p;
this_ignoreval = get_string_value (ivp->varname);
if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
(!this_ignoreval && !ivp->last_ignoreval))
return;
ivp->num_ignores = 0;
if (ivp->ignores)
{
for (p = ivp->ignores; p->val; p++)
free(p->val);
free (ivp->ignores);
ivp->ignores = (struct ign *)NULL;
}
if (ivp->last_ignoreval)
{
free (ivp->last_ignoreval);
ivp->last_ignoreval = (char *)NULL;
}
if (this_ignoreval == 0 || *this_ignoreval == '\0')
return;
ivp->last_ignoreval = savestring (this_ignoreval);
numitems = maxitems = ptr = 0;
while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
{
if (numitems + 1 >= maxitems)
{
maxitems += 10;
ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
}
ivp->ignores[numitems].val = colon_bit;
ivp->ignores[numitems].len = strlen (colon_bit);
ivp->ignores[numitems].flags = 0;
if (ivp->item_func)
(*ivp->item_func) (&ivp->ignores[numitems]);
numitems++;
}
ivp->ignores[numitems].val = (char *)NULL;
ivp->num_ignores = numitems;
}