Axe our internal and buggy glob() and fnmatch() copies

glibc 2.2 (or thereabouts) changed glob() and fnmatch() to not return
dangling symlinks as matches, which gravely affects rpmbuild in particular.
Because of this, rpm has carried a bundled copies of glibc 2.1 functions
for close to 22 years now (commit bed2a465fe).
glibc 2.27 in 2018 thankfully finally reverted that particular braindamage,
I think we've carried the compatibility babbage long enough to excuse
ourselves with a little shorter grace period in this case.
Nukes away, add a blurb about the version requirement on glibc
based systems.

This still leaves our internal glob_pattern_p() intact as unlike glob()
itself, that's not portable.

No functional changes as such.
This commit is contained in:
Panu Matilainen 2022-05-03 12:26:04 +03:00
parent 9bdf33719a
commit 66fa46c006
7 changed files with 6 additions and 1422 deletions

View File

@ -106,6 +106,10 @@ option to configure). For GCC, OpenMP 4.5 is fully supported since GCC 6.1,
which is available from
http://www.gnu.org/
If glibc is used, it needs to be of version 2.27 or newer. Older glibc
versions have a longstanding bug where glob() does not return dangling
symlinks as matches, which broadly affects rpmbuild.
Rpm requires a POSIX.1-2008 level operating system.
To compile RPM:

View File

@ -613,8 +613,6 @@ AC_CHECK_FUNCS(
renameat utimensat fchmodat fchownat ],
[], [AC_MSG_ERROR([function required by rpm])])
AC_LIBOBJ(fnmatch)
dnl check if python is requested
AC_ARG_ENABLE(python, [AS_HELP_STRING([--enable-python],[build rpm python bindings])],
[case "$enable_python" in

View File

@ -4,7 +4,6 @@ AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_srcdir)/include
AM_CPPFLAGS += -I$(top_srcdir)/misc
EXTRA_DIST = \
fnmatch.c fnmatch.h \
stpcpy.c stpncpy.c
noinst_LTLIBRARIES = libmisc.la

View File

@ -1,613 +0,0 @@
/* Copyright (C) 1991-1993, 1996-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
# include "system.h"
# include <ctype.h>
# include <stdlib.h>
# include <string.h>
/* Find the first occurrence of C in S or the final NUL byte. */
static inline char *
__strchrnul (const char *s, int c)
{
const unsigned char *char_ptr;
const unsigned long int *longword_ptr;
unsigned long int longword, magic_bits, charmask;
c = (unsigned char) c;
/* Handle the first few characters by reading one character at a time.
Do this until CHAR_PTR is aligned on a longword boundary. */
for (char_ptr = (const unsigned char *)s; ((unsigned long int) char_ptr
& (sizeof (longword) - 1)) != 0;
++char_ptr)
if (*char_ptr == c || *char_ptr == '\0')
return (void *) char_ptr;
/* All these elucidatory comments refer to 4-byte longwords,
but the theory applies equally well to 8-byte longwords. */
longword_ptr = (unsigned long int *) char_ptr;
/* Bits 31, 24, 16, and 8 of this number are zero. Call these bits
the "holes." Note that there is a hole just to the left of
each byte, with an extra at the end:
bits: 01111110 11111110 11111110 11111111
bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
The 1-bits make sure that carries propagate to the next 0-bit.
The 0-bits provide holes for carries to fall into. */
switch (sizeof (longword))
{
case 4: magic_bits = 0x7efefeffL; break;
case 8: magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL; break;
default:
abort ();
}
/* Set up a longword, each of whose bytes is C. */
charmask = c | (c << 8);
charmask |= charmask << 16;
if (sizeof (longword) > 4)
/* Do the shift in two steps to avoid a warning if long has 32 bits. */
charmask |= (charmask << 16) << 16;
if (sizeof (longword) > 8)
abort ();
/* Instead of the traditional loop which tests each character,
we will test a longword at a time. The tricky part is testing
if *any of the four* bytes in the longword in question are zero. */
for (;;)
{
/* We tentatively exit the loop if adding MAGIC_BITS to
LONGWORD fails to change any of the hole bits of LONGWORD.
1) Is this safe? Will it catch all the zero bytes?
Suppose there is a byte with all zeros. Any carry bits
propagating from its left will fall into the hole at its
least significant bit and stop. Since there will be no
carry from its most significant bit, the LSB of the
byte to the left will be unchanged, and the zero will be
detected.
2) Is this worthwhile? Will it ignore everything except
zero bytes? Suppose every byte of LONGWORD has a bit set
somewhere. There will be a carry into bit 8. If bit 8
is set, this will carry into bit 16. If bit 8 is clear,
one of bits 9-15 must be set, so there will be a carry
into bit 16. Similarly, there will be a carry into bit
24. If one of bits 24-30 is set, there will be a carry
into bit 31, so all of the hole bits will be changed.
The one misfire occurs when bits 24-30 are clear and bit
31 is set; in this case, the hole at bit 31 is not
changed. If we had access to the processor carry flag,
we could close this loophole by putting the fourth hole
at bit 32!
So it ignores everything except 128's, when they're aligned
properly.
3) But wait! Aren't we looking for C as well as zero?
Good point. So what we do is XOR LONGWORD with a longword,
each of whose bytes is C. This turns each byte that is C
into a zero. */
longword = *longword_ptr++;
/* Add MAGIC_BITS to LONGWORD. */
if ((((longword + magic_bits)
/* Set those bits that were unchanged by the addition. */
^ ~longword)
/* Look at only the hole bits. If any of the hole bits
are unchanged, most likely one of the bytes was a
zero. */
& ~magic_bits) != 0 ||
/* That caught zeroes. Now test for C. */
((((longword ^ charmask) + magic_bits) ^ ~(longword ^ charmask))
& ~magic_bits) != 0)
{
/* Which of the bytes was C or zero?
If none of them were, it was a misfire; continue the search. */
const unsigned char *cp = (const unsigned char *) (longword_ptr - 1);
if (*cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (sizeof (longword) > 4)
{
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
if (*++cp == c || *cp == '\0')
return (char *) cp;
}
}
}
/* This should never happen. */
return NULL;
}
/* For platform which support the ISO C amendement 1 functionality we
support user defined character classes. */
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
# include <wchar.h>
# include <wctype.h>
#endif
/* Comment out all this code if we are using the GNU C Library, and are not
actually compiling the library itself. This code is part of the GNU C
Library, but also included in many other GNU distributions. Compiling
and linking in this code is a waste when using the GNU C library
(especially if it is a shared library). Rather than having every GNU
program understand `configure --with-gnu-libc' and omit the object files,
it is simpler to just do this in the source for each such file. */
#if defined _LIBC || !defined __GNU_LIBRARY__
# if defined STDC_HEADERS || !defined isascii
# define ISASCII(c) 1
# else
# define ISASCII(c) isascii(c)
# endif
#ifdef isblank
# define ISBLANK(c) (ISASCII (c) && isblank (c))
#else
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
#endif
#ifdef isgraph
# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
#else
# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
#endif
#define ISPRINT(c) (ISASCII (c) && isprint (c))
#define ISDIGIT(c) (ISASCII (c) && isdigit (c))
#define ISALNUM(c) (ISASCII (c) && isalnum (c))
#define ISALPHA(c) (ISASCII (c) && isalpha (c))
#define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
#define ISLOWER(c) (ISASCII (c) && islower (c))
#define ISPUNCT(c) (ISASCII (c) && ispunct (c))
#define ISSPACE(c) (ISASCII (c) && isspace (c))
#define ISUPPER(c) (ISASCII (c) && isupper (c))
#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* The GNU C library provides support for user-defined character classes
and the functions from ISO C amendement 1. */
# ifdef CHARCLASS_NAME_MAX
# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
# else
/* This shouldn't happen but some implementation might still have this
problem. Use a reasonable default value. */
# define CHAR_CLASS_MAX_LENGTH 256
# endif
# ifdef _LIBC
# define IS_CHAR_CLASS(string) __wctype (string)
# else
# define IS_CHAR_CLASS(string) wctype (string)
# endif
# else
# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
# define IS_CHAR_CLASS(string) \
(STREQ (string, "alpha") || STREQ (string, "upper") \
|| STREQ (string, "lower") || STREQ (string, "digit") \
|| STREQ (string, "alnum") || STREQ (string, "xdigit") \
|| STREQ (string, "space") || STREQ (string, "print") \
|| STREQ (string, "punct") || STREQ (string, "graph") \
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
# endif
/* Avoid depending on library functions or files
whose names are inconsistent. */
# if !defined _LIBC && !defined getenv
extern char *getenv ();
# endif
# ifndef errno
extern int errno;
# endif
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, nonzero if not. */
static int
#ifdef _LIBC
internal_function
#endif
internal_fnmatch (const char *pattern, const char *string,
int no_leading_period, int flags)
{
register const char *p = pattern, *n = string;
register unsigned char c;
/* Note that this evaluates C many times. */
# ifdef _LIBC
# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
# else
# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
# endif
while ((c = *p++) != '\0')
{
c = FOLD (c);
switch (c)
{
case '?':
if (*n == '\0')
return FNM_NOMATCH;
else if (*n == '/' && (flags & FNM_FILE_NAME))
return FNM_NOMATCH;
else if (*n == '.' && no_leading_period
&& (n == string
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
return FNM_NOMATCH;
break;
case '\\':
if (!(flags & FNM_NOESCAPE))
{
c = *p++;
if (c == '\0')
/* Trailing \ loses. */
return FNM_NOMATCH;
c = FOLD (c);
}
if (FOLD ((unsigned char) *n) != c)
return FNM_NOMATCH;
break;
case '*':
if (*n == '.' && no_leading_period
&& (n == string
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
return FNM_NOMATCH;
for (c = *p++; c == '?' || c == '*'; c = *p++)
{
if (*n == '/' && (flags & FNM_FILE_NAME))
/* A slash does not match a wildcard under FNM_FILE_NAME. */
return FNM_NOMATCH;
else if (c == '?')
{
/* A ? needs to match one character. */
if (*n == '\0')
/* There isn't another character; no match. */
return FNM_NOMATCH;
else
/* One character of the string is consumed in matching
this ? wildcard, so *??? won't match if there are
less than three characters. */
++n;
}
}
if (c == '\0')
/* The wildcard(s) is/are the last element of the pattern.
If the name is a file name and contains another slash
this does mean it cannot match. If the FNM_LEADING_DIR
flag is set and exactly one slash is following, we have
a match. */
{
int result = (flags & FNM_FILE_NAME) == 0 ? 0 : FNM_NOMATCH;
if (flags & FNM_FILE_NAME)
{
const char *slashp = strchr (n, '/');
if (flags & FNM_LEADING_DIR)
{
if (slashp != NULL
&& strchr (slashp + 1, '/') == NULL)
result = 0;
}
else
{
if (slashp == NULL)
result = 0;
}
}
return result;
}
else
{
const char *endp;
endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
if (c == '[')
{
int flags2 = ((flags & FNM_FILE_NAME)
? flags : (flags & ~FNM_PERIOD));
for (--p; n < endp; ++n)
if (internal_fnmatch (p, n,
(no_leading_period
&& (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME)))),
flags2)
== 0)
return 0;
}
else if (c == '/' && (flags & FNM_FILE_NAME))
{
while (*n != '\0' && *n != '/')
++n;
if (*n == '/'
&& (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
flags) == 0))
return 0;
}
else
{
int flags2 = ((flags & FNM_FILE_NAME)
? flags : (flags & ~FNM_PERIOD));
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *p;
c = FOLD (c);
for (--p; n < endp; ++n)
if (FOLD ((unsigned char) *n) == c
&& (internal_fnmatch (p, n,
(no_leading_period
&& (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME)))),
flags2) == 0))
return 0;
}
}
/* If we come here no match is possible with the wildcard. */
return FNM_NOMATCH;
case '[':
{
/* Nonzero if the sense of the character class is inverted. */
static int posixly_correct;
register int not;
char cold;
if (posixly_correct == 0)
posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
if (*n == '\0')
return FNM_NOMATCH;
if (*n == '.' && no_leading_period && (n == string
|| (n[-1] == '/'
&& (flags
& FNM_FILE_NAME))))
return FNM_NOMATCH;
if (*n == '/' && (flags & FNM_FILE_NAME))
/* `/' cannot be matched. */
return FNM_NOMATCH;
not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
if (not)
++p;
c = *p++;
for (;;)
{
unsigned char fn = FOLD ((unsigned char) *n);
if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return FNM_NOMATCH;
c = FOLD ((unsigned char) *p);
++p;
if (c == fn)
goto matched;
}
else if (c == '[' && *p == ':')
{
/* Leave room for the null. */
char str[CHAR_CLASS_MAX_LENGTH + 1];
size_t c1 = 0;
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
wctype_t wt;
# endif
const char *startp = p;
for (;;)
{
if (c1 == CHAR_CLASS_MAX_LENGTH)
/* The name is too long and therefore the pattern
is ill-formed. */
return FNM_NOMATCH;
c = *++p;
if (c == ':' && p[1] == ']')
{
p += 2;
break;
}
if (c < 'a' || c >= 'z')
{
/* This cannot possibly be a character class name.
Match it as a normal range. */
p = startp;
c = '[';
goto normal_bracket;
}
str[c1++] = c;
}
str[c1] = '\0';
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
wt = IS_CHAR_CLASS (str);
if (wt == 0)
/* Invalid character class name. */
return FNM_NOMATCH;
if (__iswctype (__btowc ((unsigned char) *n), wt))
goto matched;
# else
if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
|| (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
|| (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
|| (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
|| (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
|| (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
|| (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
|| (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
|| (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
|| (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
|| (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
|| (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
goto matched;
# endif
}
else if (c == '\0')
/* [ (unterminated) loses. */
return FNM_NOMATCH;
else
{
c = FOLD (c);
normal_bracket:
if (c == fn)
goto matched;
cold = c;
c = *p++;
if (c == '-' && *p != ']')
{
/* It is a range. */
char lo[2];
char fc[2];
unsigned char cend = *p++;
if (!(flags & FNM_NOESCAPE) && cend == '\\')
cend = *p++;
if (cend == '\0')
return FNM_NOMATCH;
lo[0] = cold;
lo[1] = '\0';
fc[0] = fn;
fc[1] = '\0';
if (strcoll (lo, fc) <= 0)
{
char hi[2];
hi[0] = FOLD (cend);
hi[1] = '\0';
if (strcoll (fc, hi) <= 0)
goto matched;
}
c = *p++;
}
}
if (c == ']')
break;
}
if (!not)
return FNM_NOMATCH;
break;
matched:
/* Skip the rest of the [...] that already matched. */
while (c != ']')
{
if (c == '\0')
/* [... (unterminated) loses. */
return FNM_NOMATCH;
c = *p++;
if (!(flags & FNM_NOESCAPE) && c == '\\')
{
if (*p == '\0')
return FNM_NOMATCH;
/* XXX 1003.2d11 is unclear if this is right. */
++p;
}
else if (c == '[' && *p == ':')
{
do
if (*++p == '\0')
return FNM_NOMATCH;
while (*p != ':' || p[1] == ']');
p += 2;
c = *p;
}
}
if (not)
return FNM_NOMATCH;
}
break;
default:
if (c != FOLD ((unsigned char) *n))
return FNM_NOMATCH;
}
++n;
}
if (*n == '\0')
return 0;
if ((flags & FNM_LEADING_DIR) && *n == '/')
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
return 0;
return FNM_NOMATCH;
# undef FOLD
}
int
fnmatch (const char *pattern, const char *string, int flags)
{
return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
}
#endif /* _LIBC or not __GNU_LIBRARY__. */

View File

@ -1,85 +0,0 @@
/* Copyright (C) 1991,92,93,96,97,98,99,2001 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#ifndef _FNMATCH_H
#define _FNMATCH_H 1
#ifdef __cplusplus
extern "C" {
#endif
#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__ || !defined __P
# undef __P
# define __P(protos) protos
# endif
#else /* Not C++ or ANSI C. */
# undef __P
# define __P(protos) ()
/* We can get away without defining `const' here only because in this file
it is used only inside the prototype for `fnmatch', which is elided in
non-ANSI C where `const' is problematical. */
#endif /* C++ or ANSI C. */
#ifndef const
# if (defined __STDC__ && __STDC__) || defined __cplusplus
# define __const const
# else
# define __const
# endif
#endif
/* We #undef these before defining them because some losing systems
(HP-UX A.08.07 for example) define these in <unistd.h>. */
#undef FNM_PATHNAME
#undef FNM_NOESCAPE
#undef FNM_PERIOD
/* Bits set in the FLAGS argument to `fnmatch'. */
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */
#endif
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
#define FNM_NOMATCH 1
/* This value is returned if the implementation does not support
`fnmatch'. Since this is not the case here it will never be
returned but the conformance test suites still require the symbol
to be defined. */
#ifdef _XOPEN_SOURCE
# define FNM_NOSYS (-1)
#endif
/* Match NAME against the filename pattern PATTERN,
returning zero if it matches, FNM_NOMATCH if not. */
extern int fnmatch __P ((__const char *__pattern, __const char *__name,
int __flags));
#ifdef __cplusplus
}
#endif
#endif /* fnmatch.h */

View File

@ -17,11 +17,6 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* AIX requires this to be the first thing in the file. */
#if defined _AIX && !defined __GNUC__
#pragma alloca
#endif
#include "system.h"
#include <stdlib.h>
@ -30,56 +25,8 @@
#include <pwd.h>
#include <assert.h>
#include <sys/stat.h> /* S_ISDIR */
/* Bits set in the FLAGS argument to `glob'. */
#define GLOB_ERR (1 << 0) /* Return on read errors. */
#define GLOB_MARK (1 << 1) /* Append a slash to each name. */
#define GLOB_NOSORT (1 << 2) /* Don't sort the names. */
#define GLOB_DOOFFS (1 << 3) /* Insert PGLOB->gl_offs NULLs. */
#define GLOB_NOCHECK (1 << 4) /* If nothing matches, return the pattern. */
#define GLOB_APPEND (1 << 5) /* Append to results of a previous call. */
#define GLOB_NOESCAPE (1 << 6) /* Backslashes don't quote metacharacters. */
#define GLOB_PERIOD (1 << 7) /* Leading `.' can be matched by metachars. */
#define GLOB_MAGCHAR (1 << 8) /* Set in gl_flags if any metachars seen. */
#define GLOB_ALTDIRFUNC (1 << 9) /* Use gl_opendir et al functions. */
#define GLOB_BRACE (1 << 10) /* Expand "{a,b}" to "a" "b". */
#define GLOB_NOMAGIC (1 << 11) /* If no magic chars, return the pattern. */
#define GLOB_TILDE (1 << 12) /* Expand ~user and ~ to home directories. */
#define GLOB_ONLYDIR (1 << 13) /* Match only directories. */
#define GLOB_TILDE_CHECK (1 << 14) /* Like GLOB_TILDE but return an error
if the user name is not available. */
#define __GLOB_FLAGS (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND| \
GLOB_PERIOD|GLOB_ALTDIRFUNC|GLOB_BRACE| \
GLOB_NOMAGIC|GLOB_TILDE|GLOB_ONLYDIR|GLOB_TILDE_CHECK)
/* Error returns from `glob'. */
#define GLOB_NOSPACE 1 /* Ran out of memory. */
#define GLOB_ABORTED 2 /* Read error. */
#define GLOB_NOMATCH 3 /* No matches found. */
#define GLOB_NOSYS 4 /* Not implemented. */
/* Structure describing a globbing run. */
typedef struct {
size_t gl_pathc; /* Count of paths matched by the pattern. */
char **gl_pathv; /* List of matched pathnames. */
size_t gl_offs; /* Slots to reserve in `gl_pathv'. */
int gl_flags; /* Set to FLAGS, maybe | GLOB_MAGCHAR. */
/* If the GLOB_ALTDIRFUNC flag is set, the following functions
are used instead of the normal file access functions. */
void (*gl_closedir)(void *);
struct dirent *(*gl_readdir)(void *);
void *(*gl_opendir)(const char *);
int (*gl_lstat)(const char *, struct stat *);
int (*gl_stat)(const char *, struct stat *);
} glob_t;
#include <errno.h>
#ifndef __set_errno
#define __set_errno(val) errno = (val)
#endif
#include <fnmatch.h>
#include <glob.h>
#include <popt.h>
#include <rpm/rpmfileutil.h>
@ -87,27 +34,6 @@ typedef struct {
#include "debug.h"
/* Outcomment the following line for production quality code. */
/* #define NDEBUG 1 */
#define GLOB_INTERFACE_VERSION 1
static void globfree(glob_t * pglob);
static inline const char *next_brace_sub(const char *begin);
static int glob_in_dir(const char *pattern, const char *directory,
int flags,
int (*errfunc) (const char *, int),
glob_t * pglob);
static int prefix_array(const char *prefix, char **array, size_t n);
static int collated_compare(const void *, const void *);
#ifndef HAVE_MEMPCPY
static void * mempcpy(void *dest, const void *src, size_t n)
{
return (char *) memcpy(dest, src, n) + n;
}
#endif
/* Find the end of the sub-pattern in a brace expression. We define
this as an inline function if the compiler permits. */
static inline const char *next_brace_sub(const char *begin)
@ -126,500 +52,6 @@ static inline const char *next_brace_sub(const char *begin)
return *cp != '\0' ? cp : NULL;
}
static int __glob_pattern_p(const char *pattern, int quote);
/* Do glob searching for PATTERN, placing results in PGLOB.
The bits defined above may be set in FLAGS.
If a directory cannot be opened or read and ERRFUNC is not nil,
it is called with the pathname that caused the error, and the
`errno' value from the failing call; if it returns non-zero
`glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
Otherwise, `glob' returns zero. */
static int
glob(const char *pattern, int flags,
int (*errfunc)(const char *, int), glob_t * pglob)
{
const char *filename;
const char *dirname;
size_t dirlen;
int status;
int oldcount;
if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) {
__set_errno(EINVAL);
return -1;
}
if (flags & GLOB_BRACE) {
const char *begin = strchr(pattern, '{');
if (begin != NULL) {
/* Allocate working buffer large enough for our work. Note that
we have at least an opening and closing brace. */
int firstc;
char *alt_start;
const char *p;
const char *next;
const char *rest;
size_t rest_len;
char onealt[strlen(pattern) - 1];
/* We know the prefix for all sub-patterns. */
alt_start = mempcpy(onealt, pattern, begin - pattern);
/* Find the first sub-pattern and at the same time find the
rest after the closing brace. */
next = next_brace_sub(begin + 1);
if (next == NULL) {
/* It is an illegal expression. */
return glob(pattern, flags & ~GLOB_BRACE, errfunc, pglob);
}
/* Now find the end of the whole brace expression. */
rest = next;
while (*rest != '}') {
rest = next_brace_sub(rest + 1);
if (rest == NULL) {
/* It is an illegal expression. */
return glob(pattern, flags & ~GLOB_BRACE, errfunc,
pglob);
}
}
/* Please note that we now can be sure the brace expression
is well-formed. */
rest_len = strlen(++rest) + 1;
/* We have a brace expression. BEGIN points to the opening {,
NEXT points past the terminator of the first element, and END
points past the final }. We will accumulate result names from
recursive runs for each brace alternative in the buffer using
GLOB_APPEND. */
if (!(flags & GLOB_APPEND)) {
/* This call is to set a new vector, so clear out the
vector so we can append to it. */
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
}
firstc = pglob->gl_pathc;
p = begin + 1;
while (1) {
int result;
/* Construct the new glob expression. */
mempcpy(mempcpy(alt_start, p, next - p), rest, rest_len);
result = glob(onealt,
((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
| GLOB_APPEND), errfunc, pglob);
/* If we got an error, return it. */
if (result && result != GLOB_NOMATCH) {
if (!(flags & GLOB_APPEND))
globfree(pglob);
return result;
}
if (*next == '}')
/* We saw the last entry. */
break;
p = next + 1;
next = next_brace_sub(p);
assert(next != NULL);
}
if (pglob->gl_pathc != firstc)
/* We found some entries. */
return 0;
else if (!(flags & (GLOB_NOCHECK | GLOB_NOMAGIC)))
return GLOB_NOMATCH;
}
}
/* Find the filename. */
filename = strrchr(pattern, '/');
if (filename == NULL) {
/* This can mean two things: a simple name or "~name". The latter
case is nothing but a notation for a directory. */
if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && pattern[0] == '~') {
dirname = pattern;
dirlen = strlen(pattern);
/* Set FILENAME to NULL as a special flag. This is ugly but
other solutions would require much more code. We test for
this special case below. */
filename = NULL;
} else {
filename = pattern;
dirname = ".";
dirlen = 0;
}
} else if (filename == pattern) {
/* "/pattern". */
dirname = "/";
dirlen = 1;
++filename;
} else {
char *newp;
dirlen = filename - pattern;
newp = (char *) alloca(dirlen + 1);
*((char *) mempcpy(newp, pattern, dirlen)) = '\0';
dirname = newp;
++filename;
if (filename[0] == '\0' && dirlen > 1) {
/* "pattern/". Expand "pattern", appending slashes. */
int val = glob(dirname, flags | GLOB_MARK, errfunc, pglob);
if (val == 0)
pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
| (flags & GLOB_MARK));
return val;
}
}
if (!(flags & GLOB_APPEND)) {
pglob->gl_pathc = 0;
pglob->gl_pathv = NULL;
}
oldcount = pglob->gl_pathc;
if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && dirname[0] == '~') {
if (dirname[1] == '\0' || dirname[1] == '/') {
/* Look up home directory. */
const char *home_dir = getenv("HOME");
if (home_dir == NULL || home_dir[0] == '\0') {
int success;
char *name;
success = (name = getlogin()) != NULL;
if (success) {
struct passwd *p;
p = getpwnam(name);
if (p != NULL)
home_dir = p->pw_dir;
}
}
if (home_dir == NULL || home_dir[0] == '\0') {
if (flags & GLOB_TILDE_CHECK)
return GLOB_NOMATCH;
else
home_dir = "~"; /* No luck. */
}
/* Now construct the full directory. */
if (dirname[1] == '\0')
dirname = home_dir;
else {
char *newp;
size_t home_len = strlen(home_dir);
newp = (char *) alloca(home_len + dirlen + 1);
mempcpy(mempcpy(newp, home_dir, home_len),
&dirname[1], dirlen);
dirname = newp;
}
}
else {
char *end_name = strchr(dirname, '/');
const char *user_name;
const char *home_dir;
if (end_name == NULL)
user_name = dirname + 1;
else {
char *newp;
newp = (char *) alloca(end_name - dirname + 1);
*((char *) mempcpy(newp, dirname + 1, end_name - dirname))
= '\0';
user_name = newp;
}
/* Look up specific user's home directory. */
{
struct passwd *p;
p = getpwnam(user_name);
if (p != NULL)
home_dir = p->pw_dir;
else
home_dir = NULL;
}
/* If we found a home directory use this. */
if (home_dir != NULL) {
char *newp;
size_t home_len = strlen(home_dir);
size_t rest_len = end_name == NULL ? 0 : strlen(end_name);
newp = (char *) alloca(home_len + rest_len + 1);
*((char *) mempcpy(mempcpy(newp, home_dir, home_len),
end_name, rest_len)) = '\0';
dirname = newp;
} else if (flags & GLOB_TILDE_CHECK)
/* We have to regard it as an error if we cannot find the
home directory. */
return GLOB_NOMATCH;
}
}
/* Now test whether we looked for "~" or "~NAME". In this case we
can give the answer now. */
if (filename == NULL) {
struct stat st;
/* Return the directory if we don't check for error or if it exists. */
if ((flags & GLOB_NOCHECK)
|| (((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_stat) (dirname, &st)
: stat(dirname, &st)) == 0 && S_ISDIR(st.st_mode))) {
pglob->gl_pathv
= (char **) xrealloc(pglob->gl_pathv,
(pglob->gl_pathc +
((flags & GLOB_DOOFFS) ?
pglob->gl_offs : 0) +
1 + 1) * sizeof(char *));
if (flags & GLOB_DOOFFS)
while (pglob->gl_pathc < pglob->gl_offs)
pglob->gl_pathv[pglob->gl_pathc++] = NULL;
pglob->gl_pathv[pglob->gl_pathc] = xstrdup(dirname);
if (pglob->gl_pathv[pglob->gl_pathc] == NULL) {
free(pglob->gl_pathv);
return GLOB_NOSPACE;
}
pglob->gl_pathv[++pglob->gl_pathc] = NULL;
pglob->gl_flags = flags;
return 0;
}
/* Not found. */
return GLOB_NOMATCH;
}
if (__glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) {
/* The directory name contains metacharacters, so we
have to glob for the directory, and then glob for
the pattern in each directory found. */
glob_t dirs;
register int i;
if ((flags & GLOB_ALTDIRFUNC) != 0) {
/* Use the alternative access functions also in the recursive
call. */
dirs.gl_opendir = pglob->gl_opendir;
dirs.gl_readdir = pglob->gl_readdir;
dirs.gl_closedir = pglob->gl_closedir;
dirs.gl_stat = pglob->gl_stat;
dirs.gl_lstat = pglob->gl_lstat;
}
status = glob(dirname,
((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE
| GLOB_ALTDIRFUNC))
| GLOB_NOSORT | GLOB_ONLYDIR), errfunc, &dirs);
if (status != 0)
return status;
/* We have successfully globbed the preceding directory name.
For each name we found, call glob_in_dir on it and FILENAME,
appending the results to PGLOB. */
for (i = 0; i < dirs.gl_pathc; ++i) {
int old_pathc = pglob->gl_pathc;
status = glob_in_dir(filename, dirs.gl_pathv[i],
((flags | GLOB_APPEND)
& ~(GLOB_NOCHECK | GLOB_ERR)),
errfunc, pglob);
if (status == GLOB_NOMATCH)
/* No matches in this directory. Try the next. */
continue;
if (status != 0) {
globfree(&dirs);
globfree(pglob);
return status;
}
/* Stick the directory on the front of each name. */
if (prefix_array(dirs.gl_pathv[i],
&pglob->gl_pathv[old_pathc],
pglob->gl_pathc - old_pathc)) {
globfree(&dirs);
globfree(pglob);
return GLOB_NOSPACE;
}
}
flags |= GLOB_MAGCHAR;
/* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls.
But if we have not found any matching entry and thie GLOB_NOCHECK
flag was set we must return the list consisting of the disrectory
names followed by the filename. */
if (pglob->gl_pathc == oldcount) {
/* No matches. */
if (flags & GLOB_NOCHECK) {
size_t filename_len = strlen(filename) + 1;
char **new_pathv;
struct stat st;
/* This is an pessimistic guess about the size. */
pglob->gl_pathv
= (char **) xrealloc(pglob->gl_pathv,
(pglob->gl_pathc +
((flags & GLOB_DOOFFS) ?
pglob->gl_offs : 0) +
dirs.gl_pathc + 1) *
sizeof(char *));
if (flags & GLOB_DOOFFS)
while (pglob->gl_pathc < pglob->gl_offs)
pglob->gl_pathv[pglob->gl_pathc++] = NULL;
for (i = 0; i < dirs.gl_pathc; ++i) {
const char *dir = dirs.gl_pathv[i];
size_t dir_len = strlen(dir);
/* First check whether this really is a directory. */
if (((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_stat) (dir, &st) : stat(dir,
&st)) != 0
|| !S_ISDIR(st.st_mode))
/* No directory, ignore this entry. */
continue;
pglob->gl_pathv[pglob->gl_pathc] = xmalloc(dir_len + 1
+
filename_len);
mempcpy(mempcpy
(mempcpy
(pglob->gl_pathv[pglob->gl_pathc], dir,
dir_len), "/", 1), filename, filename_len);
++pglob->gl_pathc;
}
pglob->gl_pathv[pglob->gl_pathc] = NULL;
pglob->gl_flags = flags;
/* Now we know how large the gl_pathv vector must be. */
new_pathv = (char **) xrealloc(pglob->gl_pathv,
((pglob->gl_pathc + 1)
* sizeof(char *)));
pglob->gl_pathv = new_pathv;
} else
return GLOB_NOMATCH;
}
globfree(&dirs);
} else {
status = glob_in_dir(filename, dirname, flags, errfunc, pglob);
if (status != 0)
return status;
if (dirlen > 0) {
/* Stick the directory on the front of each name. */
int ignore = oldcount;
if ((flags & GLOB_DOOFFS) && ignore < pglob->gl_offs)
ignore = pglob->gl_offs;
if (prefix_array(dirname,
&pglob->gl_pathv[ignore],
pglob->gl_pathc - ignore)) {
globfree(pglob);
return GLOB_NOSPACE;
}
}
}
if (flags & GLOB_MARK) {
/* Append slashes to directory names. */
int i;
struct stat st;
for (i = oldcount; i < pglob->gl_pathc; ++i)
if (((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_stat) (pglob->gl_pathv[i], &st)
: stat(pglob->gl_pathv[i], &st)) == 0
&& S_ISDIR(st.st_mode)) {
size_t len = strlen(pglob->gl_pathv[i]) + 2;
char *new = xrealloc(pglob->gl_pathv[i], len);
strcpy(&new[len - 2], "/");
pglob->gl_pathv[i] = new;
}
}
if (!(flags & GLOB_NOSORT)) {
/* Sort the vector. */
int non_sort = oldcount;
if ((flags & GLOB_DOOFFS) && pglob->gl_offs > oldcount)
non_sort = pglob->gl_offs;
qsort(& pglob->gl_pathv[non_sort],
pglob->gl_pathc - non_sort,
sizeof(char *), collated_compare);
}
return 0;
}
/* Free storage allocated in PGLOB by a previous `glob' call. */
static void globfree(glob_t * pglob)
{
if (pglob->gl_pathv != NULL) {
register int i;
for (i = 0; i < pglob->gl_pathc; ++i)
if (pglob->gl_pathv[i] != NULL)
free(pglob->gl_pathv[i]);
free(pglob->gl_pathv);
}
}
/* Do a collated comparison of A and B. */
static int collated_compare(const void * a, const void * b)
{
const char *const s1 = *(const char *const *const) a;
const char *const s2 = *(const char *const *const) b;
if (s1 == s2)
return 0;
if (s1 == NULL)
return 1;
if (s2 == NULL)
return -1;
return strcoll(s1, s2);
}
/* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's
elements in place. Return nonzero if out of memory, zero if successful.
A slash is inserted between DIRNAME and each elt of ARRAY,
unless DIRNAME is just "/". Each old element of ARRAY is freed. */
static int prefix_array(const char *dirname, char **array, size_t n)
{
register size_t i;
size_t dirlen = strlen(dirname);
if (dirlen == 1 && dirname[0] == '/')
/* DIRNAME is just "/", so normal prepending would get us "//foo".
We want "/foo" instead, so don't prepend any chars from DIRNAME. */
dirlen = 0;
for (i = 0; i < n; ++i) {
size_t eltlen = strlen(array[i]) + 1;
char *new = (char *) xmalloc(dirlen + 1 + eltlen);
{
char *endp = (char *) mempcpy(new, dirname, dirlen);
*endp++ = '/';
mempcpy(endp, array[i], eltlen);
}
free(array[i]);
array[i] = new;
}
return 0;
}
/* Return nonzero if PATTERN contains any metacharacters.
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
static int __glob_pattern_p(const char *pattern, int quote)
@ -651,153 +83,6 @@ static int __glob_pattern_p(const char *pattern, int quote)
return 0;
}
/* Like `glob', but PATTERN is a final pathname component,
and matches are searched for in DIRECTORY.
The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done.
The GLOB_APPEND flag is assumed to be set (always appends). */
static int
glob_in_dir(const char *pattern, const char *directory, int flags,
int (*errfunc)(const char *, int), glob_t * pglob)
{
void * stream = NULL;
struct globlink {
struct globlink *next;
char *name;
};
struct globlink *names = NULL;
size_t nfound;
int meta;
int save;
meta = __glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE));
if (meta == 0) {
if (flags & (GLOB_NOCHECK | GLOB_NOMAGIC))
/* We need not do any tests. The PATTERN contains no meta
characters and we must not return an error therefore the
result will always contain exactly one name. */
flags |= GLOB_NOCHECK;
else {
/* Since we use the normal file functions we can also use stat()
to verify the file is there. */
struct stat st;
size_t patlen = strlen(pattern);
size_t dirlen = strlen(directory);
char *fullname = (char *) alloca(dirlen + 1 + patlen + 1);
mempcpy(mempcpy(mempcpy(fullname, directory, dirlen),
"/", 1), pattern, patlen + 1);
if (((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_stat) (fullname, &st)
: stat(fullname, &st)) == 0)
/* We found this file to be existing. Now tell the rest
of the function to copy this name into the result. */
flags |= GLOB_NOCHECK;
}
nfound = 0;
} else {
if (pattern[0] == '\0') {
/* This is a special case for matching directories like in
"*a/". */
names = (struct globlink *) alloca(sizeof(struct globlink));
names->name = (char *) xmalloc(1);
names->name[0] = '\0';
names->next = NULL;
nfound = 1;
meta = 0;
} else {
stream = ((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_opendir) (directory)
: opendir(directory));
if (stream == NULL) {
if (errno != ENOTDIR
&& ((errfunc != NULL && (*errfunc) (directory, errno))
|| (flags & GLOB_ERR)))
return GLOB_ABORTED;
nfound = 0;
meta = 0;
} else {
int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
nfound = 0;
flags |= GLOB_MAGCHAR;
while (1) {
const char *name;
size_t len;
struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
? (*pglob->gl_readdir) (stream)
: readdir((DIR *) stream));
if (d == NULL)
break;
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
/* If we shall match only directories use the information
provided by the dirent call if possible. */
if ((flags & GLOB_ONLYDIR)
&& d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
continue;
#endif
name = d->d_name;
if (fnmatch(pattern, name, fnm_flags) == 0) {
struct globlink *new = (struct globlink *)
alloca(sizeof(struct globlink));
len = strlen(d->d_name);
new->name = (char *) xmalloc(len + 1);
*((char *) mempcpy(new->name, name, len))
= '\0';
new->next = names;
names = new;
++nfound;
}
}
}
}
}
if (nfound == 0 && (flags & GLOB_NOCHECK)) {
size_t len = strlen(pattern);
nfound = 1;
names = (struct globlink *) alloca(sizeof(struct globlink));
names->next = NULL;
names->name = (char *) xmalloc(len + 1);
*((char *) mempcpy(names->name, pattern, len)) = '\0';
}
if (nfound != 0) {
pglob->gl_pathv
= (char **) xrealloc(pglob->gl_pathv,
(pglob->gl_pathc +
((flags & GLOB_DOOFFS) ? pglob->
gl_offs : 0) + nfound +
1) * sizeof(char *));
if (flags & GLOB_DOOFFS)
while (pglob->gl_pathc < pglob->gl_offs)
pglob->gl_pathv[pglob->gl_pathc++] = NULL;
for (; names != NULL; names = names->next)
pglob->gl_pathv[pglob->gl_pathc++] = names->name;
pglob->gl_pathv[pglob->gl_pathc] = NULL;
pglob->gl_flags = flags;
}
save = errno;
if (stream != NULL) {
if (flags & GLOB_ALTDIRFUNC)
(*pglob->gl_closedir) (stream);
else
closedir((DIR *) stream);
}
__set_errno(save);
return nfound == 0 ? GLOB_NOMATCH : 0;
}
/* librpmio exported interfaces */
int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)

View File

@ -100,8 +100,4 @@ extern int fdatasync(int fildes);
#define N_(Text) Text
/* ============== from misc/miscfn.h */
#include "misc/fnmatch.h"
#endif /* H_SYSTEM */