1996-08-26 18:22:31 +00:00
|
|
|
|
/* File-name wildcard pattern matching for GNU.
|
|
|
|
|
|
Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
|
the Free Software Foundation; either version 1, or (at your option)
|
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
1996-12-23 17:02:34 +00:00
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
/* To whomever it may concern: I have never seen the code which most
|
|
|
|
|
|
Unix programs use to perform this function. I wrote this from scratch
|
|
|
|
|
|
based on specifications for the pattern matching. --RMS. */
|
|
|
|
|
|
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
|
|
|
|
|
|
#pragma alloca
|
|
|
|
|
|
#endif /* _AIX && RISC6000 && !__GNUC__ */
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
# include "bashtypes.h"
|
|
|
|
|
|
#else
|
|
|
|
|
|
# include <sys/types.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#if defined (HAVE_UNISTD_H)
|
|
|
|
|
|
# include <unistd.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
# include "bashansi.h"
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#else
|
1998-04-17 19:52:44 +00:00
|
|
|
|
# if defined (HAVE_STDLIB_H)
|
|
|
|
|
|
# include <stdlib.h>
|
|
|
|
|
|
# endif
|
|
|
|
|
|
# if defined (HAVE_STRING_H)
|
|
|
|
|
|
# include <string.h>
|
|
|
|
|
|
# else /* !HAVE_STRING_H */
|
|
|
|
|
|
# include <strings.h>
|
|
|
|
|
|
# endif /* !HAVE_STRING_H */
|
1996-08-26 18:22:31 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_DIRENT_H)
|
|
|
|
|
|
# include <dirent.h>
|
|
|
|
|
|
# define D_NAMLEN(d) strlen ((d)->d_name)
|
|
|
|
|
|
#else /* !HAVE_DIRENT_H */
|
|
|
|
|
|
# define D_NAMLEN(d) ((d)->d_namlen)
|
1996-12-23 17:02:34 +00:00
|
|
|
|
# if defined (HAVE_SYS_NDIR_H)
|
|
|
|
|
|
# include <sys/ndir.h>
|
|
|
|
|
|
# endif
|
|
|
|
|
|
# if defined (HAVE_SYS_DIR_H)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
# include <sys/dir.h>
|
1996-12-23 17:02:34 +00:00
|
|
|
|
# endif /* HAVE_SYS_DIR_H */
|
|
|
|
|
|
# if defined (HAVE_NDIR_H)
|
|
|
|
|
|
# include <ndir.h>
|
|
|
|
|
|
# endif
|
|
|
|
|
|
# if !defined (dirent)
|
|
|
|
|
|
# define dirent direct
|
|
|
|
|
|
# endif
|
1996-08-26 18:22:31 +00:00
|
|
|
|
#endif /* !HAVE_DIRENT_H */
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (_POSIX_SOURCE)
|
|
|
|
|
|
/* Posix does not require that the d_ino field be present, and some
|
|
|
|
|
|
systems do not provide it. */
|
|
|
|
|
|
# define REAL_DIR_ENTRY(dp) 1
|
|
|
|
|
|
#else
|
|
|
|
|
|
# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
|
|
|
|
|
|
#endif /* _POSIX_SOURCE */
|
|
|
|
|
|
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#if !defined (HAVE_BCOPY)
|
|
|
|
|
|
# define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
|
|
|
|
|
|
#endif /* !HAVE_BCOPY */
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
# include "posixstat.h"
|
|
|
|
|
|
#else /* !SHELL */
|
|
|
|
|
|
# include <sys/stat.h>
|
|
|
|
|
|
#endif /* !SHELL */
|
|
|
|
|
|
|
|
|
|
|
|
#include "filecntl.h"
|
|
|
|
|
|
#if !defined (F_OK)
|
|
|
|
|
|
# define F_OK 0
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
# include "memalloc.h"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#include "fnmatch.h"
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined (HAVE_STDLIB_H) && !defined (SHELL)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
extern char *malloc (), *realloc ();
|
|
|
|
|
|
extern void free ();
|
|
|
|
|
|
#endif /* !HAVE_STDLIB_H */
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined (NULL)
|
|
|
|
|
|
# if defined (__STDC__)
|
|
|
|
|
|
# define NULL ((void *) 0)
|
|
|
|
|
|
# else
|
|
|
|
|
|
# define NULL 0x0
|
|
|
|
|
|
# endif /* __STDC__ */
|
|
|
|
|
|
#endif /* !NULL */
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (SHELL)
|
1997-06-05 14:59:13 +00:00
|
|
|
|
extern void throw_to_top_level ();
|
1998-04-17 19:52:44 +00:00
|
|
|
|
extern int test_eaccess ();
|
1997-06-05 14:59:13 +00:00
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
extern int interrupt_state;
|
1998-04-17 19:52:44 +00:00
|
|
|
|
extern int extended_glob;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
#endif /* SHELL */
|
|
|
|
|
|
|
|
|
|
|
|
/* Global variable which controls whether or not * matches .*.
|
|
|
|
|
|
Non-zero means don't match .*. */
|
|
|
|
|
|
int noglob_dot_filenames = 1;
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
/* Global variable which controls whether or not filename globbing
|
|
|
|
|
|
is done without regard to case. */
|
|
|
|
|
|
int glob_ignore_case = 0;
|
|
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
/* Global variable to return to signify an error in globbing. */
|
|
|
|
|
|
char *glob_error_return;
|
|
|
|
|
|
|
|
|
|
|
|
/* Return nonzero if PATTERN has any special globbing chars in it. */
|
|
|
|
|
|
int
|
|
|
|
|
|
glob_pattern_p (pattern)
|
|
|
|
|
|
char *pattern;
|
|
|
|
|
|
{
|
1998-04-17 19:52:44 +00:00
|
|
|
|
register char *p;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
register char c;
|
1998-04-17 19:52:44 +00:00
|
|
|
|
int bopen;
|
|
|
|
|
|
|
|
|
|
|
|
p = pattern;
|
|
|
|
|
|
bopen = 0;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
|
|
|
|
|
while ((c = *p++) != '\0')
|
|
|
|
|
|
switch (c)
|
|
|
|
|
|
{
|
|
|
|
|
|
case '?':
|
|
|
|
|
|
case '*':
|
|
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
|
|
|
|
case '[': /* Only accept an open brace if there is a close */
|
1998-04-17 19:52:44 +00:00
|
|
|
|
bopen++; /* brace to match it. Bracket expressions must be */
|
1996-08-26 18:22:31 +00:00
|
|
|
|
continue; /* complete, according to Posix.2 */
|
|
|
|
|
|
case ']':
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (bopen)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
return (1);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
case '+': /* extended matching operators */
|
|
|
|
|
|
case '@':
|
|
|
|
|
|
case '!':
|
|
|
|
|
|
if (*p == '(') /*) */
|
|
|
|
|
|
return (1);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
case '\\':
|
|
|
|
|
|
if (*p++ == '\0')
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
|
|
|
|
|
|
static void
|
|
|
|
|
|
dequote_pathname (pathname)
|
|
|
|
|
|
char *pathname;
|
|
|
|
|
|
{
|
|
|
|
|
|
register int i, j;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = j = 0; pathname && pathname[i]; )
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pathname[i] == '\\')
|
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
|
|
pathname[j++] = pathname[i++];
|
|
|
|
|
|
|
|
|
|
|
|
if (!pathname[i - 1])
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
pathname[j] = '\0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
|
|
|
|
|
|
/* Test whether NAME exists. */
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (HAVE_LSTAT)
|
|
|
|
|
|
# define GLOB_TESTNAME(name) (lstat (name, &finfo))
|
|
|
|
|
|
#else /* !HAVE_LSTAT */
|
|
|
|
|
|
# if defined (SHELL) && !defined (AFS)
|
|
|
|
|
|
# define GLOB_TESTNAME(name) (test_eaccess (nextname, F_OK))
|
|
|
|
|
|
# else /* !SHELL || AFS */
|
|
|
|
|
|
# define GLOB_TESTNAME(name) (access (nextname, F_OK))
|
|
|
|
|
|
# endif /* !SHELL || AFS */
|
|
|
|
|
|
#endif /* !HAVE_LSTAT */
|
|
|
|
|
|
|
|
|
|
|
|
/* Return 0 if DIR is a directory, -1 otherwise. */
|
|
|
|
|
|
static int
|
|
|
|
|
|
glob_testdir (dir)
|
|
|
|
|
|
char *dir;
|
|
|
|
|
|
{
|
|
|
|
|
|
struct stat finfo;
|
|
|
|
|
|
|
|
|
|
|
|
if (stat (dir, &finfo) < 0)
|
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
|
|
if (S_ISDIR (finfo.st_mode) == 0)
|
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
/* Return a vector of names of files in directory DIR
|
|
|
|
|
|
whose names match glob pattern PAT.
|
|
|
|
|
|
The names are not in any particular order.
|
|
|
|
|
|
Wildcards at the beginning of PAT do not match an initial period.
|
|
|
|
|
|
|
|
|
|
|
|
The vector is terminated by an element that is a null pointer.
|
|
|
|
|
|
|
|
|
|
|
|
To free the space allocated, first free the vector's elements,
|
|
|
|
|
|
then free the vector.
|
|
|
|
|
|
|
|
|
|
|
|
Return 0 if cannot get enough memory to hold the pointer
|
|
|
|
|
|
and the names.
|
|
|
|
|
|
|
|
|
|
|
|
Return -1 if cannot access directory DIR.
|
|
|
|
|
|
Look in errno for more information. */
|
|
|
|
|
|
|
|
|
|
|
|
char **
|
|
|
|
|
|
glob_vector (pat, dir)
|
|
|
|
|
|
char *pat;
|
|
|
|
|
|
char *dir;
|
|
|
|
|
|
{
|
|
|
|
|
|
struct globval
|
|
|
|
|
|
{
|
|
|
|
|
|
struct globval *next;
|
|
|
|
|
|
char *name;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
DIR *d;
|
1996-12-23 17:02:34 +00:00
|
|
|
|
register struct dirent *dp;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
struct globval *lastlink;
|
|
|
|
|
|
register struct globval *nextlink;
|
|
|
|
|
|
register char *nextname;
|
|
|
|
|
|
unsigned int count;
|
|
|
|
|
|
int lose, skip;
|
|
|
|
|
|
register char **name_vector;
|
|
|
|
|
|
register unsigned int i;
|
1998-04-17 19:52:44 +00:00
|
|
|
|
int flags; /* Flags passed to fnmatch (). */
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
|
|
|
|
|
lastlink = 0;
|
1998-04-17 19:52:44 +00:00
|
|
|
|
count = lose = skip = 0;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
|
|
|
|
|
/* If PAT is empty, skip the loop, but return one (empty) filename. */
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (pat == 0 || *pat == '\0')
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (glob_testdir (dir) < 0)
|
|
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
nextlink = (struct globval *)alloca (sizeof (struct globval));
|
1998-04-17 19:52:44 +00:00
|
|
|
|
nextlink->next = (struct globval *)0;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
nextname = (char *) malloc (1);
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (nextname == 0)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
lose = 1;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
lastlink = nextlink;
|
|
|
|
|
|
nextlink->name = nextname;
|
|
|
|
|
|
nextname[0] = '\0';
|
1998-04-17 19:52:44 +00:00
|
|
|
|
count = 1;
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
1998-04-17 19:52:44 +00:00
|
|
|
|
|
1996-08-26 18:22:31 +00:00
|
|
|
|
skip = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
/* If the filename pattern (PAT) does not contain any globbing characters,
|
|
|
|
|
|
we can dispense with reading the directory, and just see if there is
|
|
|
|
|
|
a filename `DIR/PAT'. If there is, and we can access it, just make the
|
|
|
|
|
|
vector to return and bail immediately. */
|
|
|
|
|
|
if (skip == 0 && glob_pattern_p (pat) == 0)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
1998-04-17 19:52:44 +00:00
|
|
|
|
int dirlen;
|
|
|
|
|
|
struct stat finfo;
|
|
|
|
|
|
|
|
|
|
|
|
if (glob_testdir (dir) < 0)
|
|
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
|
|
|
|
|
|
dirlen = strlen (dir);
|
|
|
|
|
|
nextname = (char *)malloc (dirlen + strlen (pat) + 2);
|
|
|
|
|
|
if (nextname == 0)
|
|
|
|
|
|
lose = 1;
|
|
|
|
|
|
else
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
1998-04-17 19:52:44 +00:00
|
|
|
|
strcpy (nextname, dir);
|
|
|
|
|
|
nextname[dirlen++] = '/';
|
|
|
|
|
|
strcpy (nextname + dirlen, pat);
|
|
|
|
|
|
|
|
|
|
|
|
if (GLOB_TESTNAME (nextname) >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
free (nextname);
|
|
|
|
|
|
nextlink = (struct globval *)alloca (sizeof (struct globval));
|
|
|
|
|
|
nextlink->next = (struct globval *)0;
|
|
|
|
|
|
nextname = (char *) malloc (strlen (pat) + 1);
|
|
|
|
|
|
if (nextname == 0)
|
|
|
|
|
|
lose = 1;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
lastlink = nextlink;
|
|
|
|
|
|
nextlink->name = nextname;
|
|
|
|
|
|
strcpy (nextname, pat);
|
|
|
|
|
|
count = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
free (nextname);
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
skip = 1;
|
|
|
|
|
|
}
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (skip == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Open the directory, punting immediately if we cannot. If opendir
|
|
|
|
|
|
is not robust (i.e., it opens non-directories successfully), test
|
|
|
|
|
|
that DIR is a directory and punt if it's not. */
|
|
|
|
|
|
#if defined (OPENDIR_NOT_ROBUST)
|
|
|
|
|
|
if (glob_testdir (dir) < 0)
|
|
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
#endif
|
1996-08-26 18:22:31 +00:00
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
d = opendir (dir);
|
|
|
|
|
|
if (d == NULL)
|
|
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
|
|
|
|
|
|
/* Compute the flags that will be passed to fnmatch(). We don't
|
|
|
|
|
|
need to do this every time through the loop. */
|
1996-08-26 18:22:31 +00:00
|
|
|
|
flags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#ifdef FNM_CASEFOLD
|
|
|
|
|
|
if (glob_ignore_case)
|
|
|
|
|
|
flags |= FNM_CASEFOLD;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SHELL
|
|
|
|
|
|
if (extended_glob)
|
|
|
|
|
|
flags |= FNM_EXTMATCH;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* 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)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
/* Make globbing interruptible in the shell. */
|
|
|
|
|
|
if (interrupt_state)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
lose = 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
1998-04-17 19:52:44 +00:00
|
|
|
|
#endif /* SHELL */
|
|
|
|
|
|
|
|
|
|
|
|
dp = readdir (d);
|
|
|
|
|
|
if (dp == NULL)
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* If this directory entry is not to be used, try again. */
|
|
|
|
|
|
if (REAL_DIR_ENTRY (dp) == 0)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
/* If a dot must be explicity matched, check to see if they do. */
|
|
|
|
|
|
if (noglob_dot_filenames && dp->d_name[0] == '.' && pat[0] != '.' &&
|
|
|
|
|
|
(pat[0] != '\\' || pat[1] != '.'))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (fnmatch (pat, dp->d_name, flags) != FNM_NOMATCH)
|
|
|
|
|
|
{
|
|
|
|
|
|
nextlink = (struct globval *) alloca (sizeof (struct globval));
|
|
|
|
|
|
nextlink->next = lastlink;
|
|
|
|
|
|
nextname = (char *) malloc (D_NAMLEN (dp) + 1);
|
|
|
|
|
|
if (nextname == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
lose = 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
lastlink = nextlink;
|
|
|
|
|
|
nextlink->name = nextname;
|
|
|
|
|
|
bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
|
|
|
|
|
|
++count;
|
|
|
|
|
|
}
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
1998-04-17 19:52:44 +00:00
|
|
|
|
|
|
|
|
|
|
(void) closedir (d);
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
1998-04-17 19:52:44 +00:00
|
|
|
|
if (lose == 0)
|
1996-08-26 18:22:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
name_vector = (char **) malloc ((count + 1) * sizeof (char *));
|
|
|
|
|
|
lose |= name_vector == NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Have we run out of memory? */
|
|
|
|
|
|
if (lose)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Here free the strings we have got. */
|
|
|
|
|
|
while (lastlink)
|
|
|
|
|
|
{
|
|
|
|
|
|
free (lastlink->name);
|
|
|
|
|
|
lastlink = lastlink->next;
|
|
|
|
|
|
}
|
|
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
if (interrupt_state)
|
|
|
|
|
|
throw_to_top_level ();
|
|
|
|
|
|
#endif /* SHELL */
|
1998-04-17 19:52:44 +00:00
|
|
|
|
|
|
|
|
|
|
return ((char **)NULL);
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Copy the name pointers from the linked list into the vector. */
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
name_vector[i] = lastlink->name;
|
|
|
|
|
|
lastlink = lastlink->next;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
name_vector[count] = NULL;
|
|
|
|
|
|
return (name_vector);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return a new array which is the concatenation of each string in ARRAY
|
|
|
|
|
|
to DIR. This function expects you to pass in an allocated ARRAY, and
|
|
|
|
|
|
it takes care of free()ing that array. Thus, you might think of this
|
|
|
|
|
|
function as side-effecting ARRAY. */
|
|
|
|
|
|
static char **
|
|
|
|
|
|
glob_dir_to_array (dir, array)
|
|
|
|
|
|
char *dir, **array;
|
|
|
|
|
|
{
|
|
|
|
|
|
register unsigned int i, l;
|
|
|
|
|
|
int add_slash;
|
|
|
|
|
|
char **result;
|
|
|
|
|
|
|
|
|
|
|
|
l = strlen (dir);
|
|
|
|
|
|
if (l == 0)
|
|
|
|
|
|
return (array);
|
|
|
|
|
|
|
|
|
|
|
|
add_slash = dir[l - 1] != '/';
|
|
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
while (array[i] != NULL)
|
|
|
|
|
|
++i;
|
|
|
|
|
|
|
|
|
|
|
|
result = (char **) malloc ((i + 1) * sizeof (char *));
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; array[i] != NULL; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
result[i] = (char *) malloc (l + (add_slash ? 1 : 0)
|
|
|
|
|
|
+ strlen (array[i]) + 1);
|
|
|
|
|
|
if (result[i] == NULL)
|
|
|
|
|
|
return (NULL);
|
1996-12-23 17:02:34 +00:00
|
|
|
|
#if 1
|
|
|
|
|
|
strcpy (result[i], dir);
|
|
|
|
|
|
if (add_slash)
|
|
|
|
|
|
result[i][l] = '/';
|
|
|
|
|
|
strcpy (result[i] + l + add_slash, array[i]);
|
|
|
|
|
|
#else
|
|
|
|
|
|
(void)sprintf (result[i], "%s%s%s", dir, add_slash ? "/" : "", array[i]);
|
|
|
|
|
|
#endif
|
1996-08-26 18:22:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
result[i] = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Free the input array. */
|
|
|
|
|
|
for (i = 0; array[i] != NULL; i++)
|
|
|
|
|
|
free (array[i]);
|
|
|
|
|
|
free ((char *) array);
|
|
|
|
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Do globbing on PATHNAME. Return an array of pathnames that match,
|
|
|
|
|
|
marking the end of the array with a null-pointer as an element.
|
|
|
|
|
|
If no pathnames match, then the array is empty (first element is null).
|
|
|
|
|
|
If there isn't enough memory, then return NULL.
|
|
|
|
|
|
If a file system error occurs, return -1; `errno' has the error code. */
|
|
|
|
|
|
char **
|
|
|
|
|
|
glob_filename (pathname)
|
|
|
|
|
|
char *pathname;
|
|
|
|
|
|
{
|
|
|
|
|
|
char **result;
|
|
|
|
|
|
unsigned int result_size;
|
|
|
|
|
|
char *directory_name, *filename;
|
|
|
|
|
|
unsigned int directory_len;
|
|
|
|
|
|
|
|
|
|
|
|
result = (char **) malloc (sizeof (char *));
|
|
|
|
|
|
result_size = 1;
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
|
|
result[0] = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Find the filename. */
|
|
|
|
|
|
filename = strrchr (pathname, '/');
|
|
|
|
|
|
if (filename == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
filename = pathname;
|
|
|
|
|
|
directory_name = "";
|
|
|
|
|
|
directory_len = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
directory_len = (filename - pathname) + 1;
|
|
|
|
|
|
directory_name = (char *) alloca (directory_len + 1);
|
|
|
|
|
|
|
|
|
|
|
|
bcopy (pathname, directory_name, directory_len);
|
|
|
|
|
|
directory_name[directory_len] = '\0';
|
|
|
|
|
|
++filename;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* If directory_name contains globbing characters, then we
|
|
|
|
|
|
have to expand the previous levels. Just recurse. */
|
|
|
|
|
|
if (glob_pattern_p (directory_name))
|
|
|
|
|
|
{
|
|
|
|
|
|
char **directories;
|
|
|
|
|
|
register unsigned int i;
|
|
|
|
|
|
|
|
|
|
|
|
if (directory_name[directory_len - 1] == '/')
|
|
|
|
|
|
directory_name[directory_len - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
directories = glob_filename (directory_name);
|
|
|
|
|
|
|
|
|
|
|
|
if (directories == NULL)
|
|
|
|
|
|
goto memory_error;
|
|
|
|
|
|
else if (directories == (char **)&glob_error_return)
|
|
|
|
|
|
{
|
1996-12-23 17:02:34 +00:00
|
|
|
|
free ((char *) result);
|
1996-08-26 18:22:31 +00:00
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (*directories == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
free ((char *) directories);
|
|
|
|
|
|
free ((char *) result);
|
|
|
|
|
|
return ((char **) &glob_error_return);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* We have successfully globbed the preceding directory name.
|
|
|
|
|
|
For each name in DIRECTORIES, call glob_vector on it and
|
|
|
|
|
|
FILENAME. Concatenate the results together. */
|
|
|
|
|
|
for (i = 0; directories[i] != NULL; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
char **temp_results;
|
|
|
|
|
|
|
|
|
|
|
|
/* Scan directory even on a NULL pathname. That way, `*h/'
|
|
|
|
|
|
returns only directories ending in `h', instead of all
|
|
|
|
|
|
files ending in `h' with a `/' appended. */
|
|
|
|
|
|
temp_results = glob_vector (filename, directories[i]);
|
|
|
|
|
|
|
|
|
|
|
|
/* Handle error cases. */
|
|
|
|
|
|
if (temp_results == NULL)
|
|
|
|
|
|
goto memory_error;
|
|
|
|
|
|
else if (temp_results == (char **)&glob_error_return)
|
|
|
|
|
|
/* This filename is probably not a directory. Ignore it. */
|
|
|
|
|
|
;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
char **array;
|
|
|
|
|
|
register unsigned int l;
|
|
|
|
|
|
|
|
|
|
|
|
array = glob_dir_to_array (directories[i], temp_results);
|
|
|
|
|
|
l = 0;
|
|
|
|
|
|
while (array[l] != NULL)
|
|
|
|
|
|
++l;
|
|
|
|
|
|
|
|
|
|
|
|
result =
|
|
|
|
|
|
(char **)realloc (result, (result_size + l) * sizeof (char *));
|
|
|
|
|
|
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
|
goto memory_error;
|
|
|
|
|
|
|
|
|
|
|
|
for (l = 0; array[l] != NULL; ++l)
|
|
|
|
|
|
result[result_size++ - 1] = array[l];
|
|
|
|
|
|
|
|
|
|
|
|
result[result_size - 1] = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that the elements of ARRAY are not freed. */
|
|
|
|
|
|
free ((char *) array);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* Free the directories. */
|
|
|
|
|
|
for (i = 0; directories[i]; i++)
|
|
|
|
|
|
free (directories[i]);
|
|
|
|
|
|
|
|
|
|
|
|
free ((char *) directories);
|
|
|
|
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* If there is only a directory name, return it. */
|
|
|
|
|
|
if (*filename == '\0')
|
|
|
|
|
|
{
|
|
|
|
|
|
result = (char **) realloc ((char *) result, 2 * sizeof (char *));
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
result[0] = (char *) malloc (directory_len + 1);
|
|
|
|
|
|
if (result[0] == NULL)
|
|
|
|
|
|
goto memory_error;
|
|
|
|
|
|
bcopy (directory_name, result[0], directory_len + 1);
|
|
|
|
|
|
result[1] = NULL;
|
|
|
|
|
|
return (result);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
char **temp_results;
|
|
|
|
|
|
|
|
|
|
|
|
/* There are no unquoted globbing characters in DIRECTORY_NAME.
|
|
|
|
|
|
Dequote it before we try to open the directory since there may
|
|
|
|
|
|
be quoted globbing characters which should be treated verbatim. */
|
|
|
|
|
|
if (directory_len > 0)
|
|
|
|
|
|
dequote_pathname (directory_name);
|
|
|
|
|
|
|
|
|
|
|
|
/* We allocated a small array called RESULT, which we won't be using.
|
|
|
|
|
|
Free that memory now. */
|
|
|
|
|
|
free (result);
|
|
|
|
|
|
|
|
|
|
|
|
/* Just return what glob_vector () returns appended to the
|
|
|
|
|
|
directory name. */
|
|
|
|
|
|
temp_results =
|
|
|
|
|
|
glob_vector (filename, (directory_len == 0 ? "." : directory_name));
|
|
|
|
|
|
|
|
|
|
|
|
if (temp_results == NULL || temp_results == (char **)&glob_error_return)
|
|
|
|
|
|
return (temp_results);
|
|
|
|
|
|
|
|
|
|
|
|
return (glob_dir_to_array (directory_name, temp_results));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* We get to memory_error if the program has run out of memory, or
|
|
|
|
|
|
if this is the shell, and we have been interrupted. */
|
|
|
|
|
|
memory_error:
|
|
|
|
|
|
if (result != NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
register unsigned int i;
|
|
|
|
|
|
for (i = 0; result[i] != NULL; ++i)
|
|
|
|
|
|
free (result[i]);
|
|
|
|
|
|
free ((char *) result);
|
|
|
|
|
|
}
|
|
|
|
|
|
#if defined (SHELL)
|
|
|
|
|
|
if (interrupt_state)
|
|
|
|
|
|
throw_to_top_level ();
|
|
|
|
|
|
#endif /* SHELL */
|
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (TEST)
|
|
|
|
|
|
|
|
|
|
|
|
main (argc, argv)
|
|
|
|
|
|
int argc;
|
|
|
|
|
|
char **argv;
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < argc; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
char **value = glob_filename (argv[i]);
|
|
|
|
|
|
if (value == NULL)
|
|
|
|
|
|
puts ("Out of memory.");
|
|
|
|
|
|
else if (value == &glob_error_return)
|
|
|
|
|
|
perror (argv[i]);
|
|
|
|
|
|
else
|
|
|
|
|
|
for (i = 0; value[i] != NULL; i++)
|
|
|
|
|
|
puts (value[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exit (0);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif /* TEST. */
|