Blame rpmio/rpmglob.c

2ff057
#include "system.h"
2ff057
2ff057
/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
2ff057
2ff057
   This library is free software; you can redistribute it and/or
2ff057
   modify it under the terms of the GNU Library General Public License as
2ff057
   published by the Free Software Foundation; either version 2 of the
2ff057
   License, or (at your option) any later version.
2ff057
2ff057
   This library is distributed in the hope that it will be useful,
2ff057
   but WITHOUT ANY WARRANTY; without even the implied warranty of
2ff057
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2ff057
   Library General Public License for more details.
2ff057
2ff057
   You should have received a copy of the GNU Library General Public
2ff057
   License along with this library; see the file COPYING.LIB.  If not,
2ff057
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2ff057
   Boston, MA 02111-1307, USA.  */
2ff057
2ff057
/* AIX requires this to be the first thing in the file.  */
2ff057
#if defined _AIX && !defined __GNUC__
2ff057
#pragma alloca
2ff057
#endif
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <stdlib.h>
2ff057
#include <string.h>
2ff057
#include <pwd.h>
2ff057
#include <assert.h>
2ff057
#include <sys/stat.h>		/* S_ISDIR */
2ff057
2ff057
/* Bits set in the FLAGS argument to `glob'.  */
2ff057
#define	GLOB_ERR	(1 << 0)	/* Return on read errors.  */
2ff057
#define	GLOB_MARK	(1 << 1)	/* Append a slash to each name.  */
2ff057
#define	GLOB_NOSORT	(1 << 2)	/* Don't sort the names.  */
2ff057
#define	GLOB_DOOFFS	(1 << 3)	/* Insert PGLOB->gl_offs NULLs.  */
2ff057
#define	GLOB_NOCHECK	(1 << 4)	/* If nothing matches, return the pattern.  */
2ff057
#define	GLOB_APPEND	(1 << 5)	/* Append to results of a previous call.  */
2ff057
#define	GLOB_NOESCAPE	(1 << 6)	/* Backslashes don't quote metacharacters.  */
2ff057
#define	GLOB_PERIOD	(1 << 7)	/* Leading `.' can be matched by metachars.  */
2ff057
2ff057
#define GLOB_MAGCHAR	 (1 << 8)	/* Set in gl_flags if any metachars seen.  */
2ff057
#define GLOB_ALTDIRFUNC (1 << 9)	/* Use gl_opendir et al functions.  */
2ff057
#define GLOB_BRACE	 (1 << 10)	/* Expand "{a,b}" to "a" "b".  */
2ff057
#define GLOB_NOMAGIC	 (1 << 11)	/* If no magic chars, return the pattern.  */
2ff057
#define GLOB_TILDE	 (1 << 12)	/* Expand ~user and ~ to home directories. */
2ff057
#define GLOB_ONLYDIR	 (1 << 13)	/* Match only directories.  */
2ff057
#define GLOB_TILDE_CHECK (1 << 14)	/* Like GLOB_TILDE but return an error
2ff057
					   if the user name is not available.  */
2ff057
#define __GLOB_FLAGS	(GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
2ff057
			 GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND|     \
2ff057
			 GLOB_PERIOD|GLOB_ALTDIRFUNC|GLOB_BRACE|     \
2ff057
			 GLOB_NOMAGIC|GLOB_TILDE|GLOB_ONLYDIR|GLOB_TILDE_CHECK)
2ff057
2ff057
/* Error returns from `glob'.  */
2ff057
#define	GLOB_NOSPACE	1	/* Ran out of memory.  */
2ff057
#define	GLOB_ABORTED	2	/* Read error.  */
2ff057
#define	GLOB_NOMATCH	3	/* No matches found.  */
2ff057
#define GLOB_NOSYS	4	/* Not implemented.  */
2ff057
2ff057
/* Structure describing a globbing run.  */
2ff057
typedef struct {
2ff057
    size_t gl_pathc;		/* Count of paths matched by the pattern.  */
2ff057
    char **gl_pathv;		/* List of matched pathnames.  */
2ff057
    size_t gl_offs;		/* Slots to reserve in `gl_pathv'.  */
2ff057
    int gl_flags;		/* Set to FLAGS, maybe | GLOB_MAGCHAR.  */
2ff057
2ff057
    /* If the GLOB_ALTDIRFUNC flag is set, the following functions
2ff057
       are used instead of the normal file access functions.  */
2ff057
    void (*gl_closedir)(void *);
2ff057
    struct dirent *(*gl_readdir)(void *);
2ff057
    void *(*gl_opendir)(const char *);
2ff057
    int (*gl_lstat)(const char *, struct stat *);
2ff057
    int (*gl_stat)(const char *, struct stat *);
2ff057
} glob_t;
2ff057
2ff057
#define	NAMLEN(_d)	NLENGTH(_d)
2ff057
2ff057
#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__
2ff057
/* Posix does not require that the d_ino field be present, and some
2ff057
   systems do not provide it. */
2ff057
#define REAL_DIR_ENTRY(dp) 1
2ff057
#else
2ff057
#define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
2ff057
#endif				/* POSIX */
2ff057
2ff057
#include <errno.h>
2ff057
#ifndef __set_errno
2ff057
#define __set_errno(val) errno = (val)
2ff057
#endif
2ff057
2ff057
#include <popt.h>
2ff057
#include <rpm/rpmfileutil.h>
2ff057
#include <rpm/rpmurl.h>
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
/* Outcomment the following line for production quality code.  */
2ff057
/* #define NDEBUG 1 */
2ff057
2ff057
#define GLOB_INTERFACE_VERSION 1
2ff057
2ff057
static void globfree(glob_t * pglob);
2ff057
static inline const char *next_brace_sub(const char *begin);
2ff057
static int glob_in_dir(const char *pattern, const char *directory,
2ff057
			    int flags,
2ff057
			    int (*errfunc) (const char *, int),
2ff057
			    glob_t * pglob);
2ff057
static int prefix_array(const char *prefix, char **array, size_t n);
2ff057
static int collated_compare(const void *, const void *);
2ff057
2ff057
#ifndef HAVE_MEMPCPY
2ff057
static void * mempcpy(void *dest, const void *src, size_t n)
2ff057
{
2ff057
    return (char *) memcpy(dest, src, n) + n;
2ff057
}
2ff057
#endif
2ff057
2ff057
/* Find the end of the sub-pattern in a brace expression.  We define
2ff057
   this as an inline function if the compiler permits.  */
2ff057
static inline const char *next_brace_sub(const char *begin)
2ff057
{
2ff057
    unsigned int depth = 0;
2ff057
    const char *cp = begin;
2ff057
2ff057
    while (*cp != '\0') {
2ff057
	if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
2ff057
	    break;
2ff057
2ff057
	if (*cp++ == '{')
2ff057
	    depth++;
2ff057
    }
2ff057
2ff057
    return *cp != '\0' ? cp : NULL;
2ff057
}
2ff057
2ff057
static int __glob_pattern_p(const char *pattern, int quote);
2ff057
2ff057
/* Do glob searching for PATTERN, placing results in PGLOB.
2ff057
   The bits defined above may be set in FLAGS.
2ff057
   If a directory cannot be opened or read and ERRFUNC is not nil,
2ff057
   it is called with the pathname that caused the error, and the
2ff057
   `errno' value from the failing call; if it returns non-zero
2ff057
   `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
2ff057
   If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
2ff057
   Otherwise, `glob' returns zero.  */
2ff057
static int
2ff057
glob(const char *pattern, int flags,
2ff057
     int (*errfunc)(const char *, int), glob_t * pglob)
2ff057
{
2ff057
    const char *filename;
2ff057
    const char *dirname;
2ff057
    size_t dirlen;
2ff057
    int status;
2ff057
    int oldcount;
2ff057
2ff057
    if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) {
2ff057
	__set_errno(EINVAL);
2ff057
	return -1;
2ff057
    }
2ff057
2ff057
    if (flags & GLOB_BRACE) {
2ff057
	const char *begin = strchr(pattern, '{');
2ff057
	if (begin != NULL) {
2ff057
	    /* Allocate working buffer large enough for our work.  Note that
2ff057
	       we have at least an opening and closing brace.  */
2ff057
	    int firstc;
2ff057
	    char *alt_start;
2ff057
	    const char *p;
2ff057
	    const char *next;
2ff057
	    const char *rest;
2ff057
	    size_t rest_len;
2ff057
	    char onealt[strlen(pattern) - 1];
2ff057
2ff057
	    /* We know the prefix for all sub-patterns.  */
2ff057
	    alt_start = mempcpy(onealt, pattern, begin - pattern);
2ff057
2ff057
	    /* Find the first sub-pattern and at the same time find the
2ff057
	       rest after the closing brace.  */
2ff057
	    next = next_brace_sub(begin + 1);
2ff057
	    if (next == NULL) {
2ff057
		/* It is an illegal expression.  */
2ff057
		return glob(pattern, flags & ~GLOB_BRACE, errfunc, pglob);
2ff057
	    }
2ff057
2ff057
	    /* Now find the end of the whole brace expression.  */
2ff057
	    rest = next;
2ff057
	    while (*rest != '}') {
2ff057
		rest = next_brace_sub(rest + 1);
2ff057
		if (rest == NULL) {
2ff057
		    /* It is an illegal expression.  */
2ff057
		    return glob(pattern, flags & ~GLOB_BRACE, errfunc,
2ff057
				pglob);
2ff057
		}
2ff057
	    }
2ff057
	    /* Please note that we now can be sure the brace expression
2ff057
	       is well-formed.  */
2ff057
	    rest_len = strlen(++rest) + 1;
2ff057
2ff057
	    /* We have a brace expression.  BEGIN points to the opening {,
2ff057
	       NEXT points past the terminator of the first element, and END
2ff057
	       points past the final }.  We will accumulate result names from
2ff057
	       recursive runs for each brace alternative in the buffer using
2ff057
	       GLOB_APPEND.  */
2ff057
2ff057
	    if (!(flags & GLOB_APPEND)) {
2ff057
		/* This call is to set a new vector, so clear out the
2ff057
		   vector so we can append to it.  */
2ff057
		pglob->gl_pathc = 0;
2ff057
		pglob->gl_pathv = NULL;
2ff057
	    }
2ff057
	    firstc = pglob->gl_pathc;
2ff057
2ff057
	    p = begin + 1;
2ff057
	    while (1) {
2ff057
		int result;
2ff057
2ff057
		/* Construct the new glob expression.  */
2ff057
		mempcpy(mempcpy(alt_start, p, next - p), rest, rest_len);
2ff057
2ff057
		result = glob(onealt,
2ff057
			      ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
2ff057
			       | GLOB_APPEND), errfunc, pglob);
2ff057
2ff057
		/* If we got an error, return it.  */
2ff057
		if (result && result != GLOB_NOMATCH) {
2ff057
		    if (!(flags & GLOB_APPEND))
2ff057
			globfree(pglob);
2ff057
		    return result;
2ff057
		}
2ff057
2ff057
		if (*next == '}')
2ff057
		    /* We saw the last entry.  */
2ff057
		    break;
2ff057
2ff057
		p = next + 1;
2ff057
		next = next_brace_sub(p);
2ff057
		assert(next != NULL);
2ff057
	    }
2ff057
2ff057
	    if (pglob->gl_pathc != firstc)
2ff057
		/* We found some entries.  */
2ff057
		return 0;
2ff057
	    else if (!(flags & (GLOB_NOCHECK | GLOB_NOMAGIC)))
2ff057
		return GLOB_NOMATCH;
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* Find the filename.  */
2ff057
    filename = strrchr(pattern, '/');
2ff057
    if (filename == NULL) {
2ff057
	/* This can mean two things: a simple name or "~name".  The latter
2ff057
	   case is nothing but a notation for a directory.  */
2ff057
	if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && pattern[0] == '~') {
2ff057
	    dirname = pattern;
2ff057
	    dirlen = strlen(pattern);
2ff057
2ff057
	    /* Set FILENAME to NULL as a special flag.  This is ugly but
2ff057
	       other solutions would require much more code.  We test for
2ff057
	       this special case below.  */
2ff057
	    filename = NULL;
2ff057
	} else {
2ff057
	    filename = pattern;
2ff057
	    dirname = ".";
2ff057
	    dirlen = 0;
2ff057
	}
2ff057
    } else if (filename == pattern) {
2ff057
	/* "/pattern".  */
2ff057
	dirname = "/";
2ff057
	dirlen = 1;
2ff057
	++filename;
2ff057
    } else {
2ff057
	char *newp;
2ff057
	dirlen = filename - pattern;
2ff057
	newp = (char *) alloca(dirlen + 1);
2ff057
	*((char *) mempcpy(newp, pattern, dirlen)) = '\0';
2ff057
	dirname = newp;
2ff057
	++filename;
2ff057
2ff057
	if (filename[0] == '\0' && dirlen > 1) {
2ff057
	    /* "pattern/".  Expand "pattern", appending slashes.  */
2ff057
	    int val = glob(dirname, flags | GLOB_MARK, errfunc, pglob);
2ff057
	    if (val == 0)
2ff057
		pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
2ff057
				   | (flags & GLOB_MARK));
2ff057
	    return val;
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (!(flags & GLOB_APPEND)) {
2ff057
	pglob->gl_pathc = 0;
2ff057
	pglob->gl_pathv = NULL;
2ff057
    }
2ff057
2ff057
    oldcount = pglob->gl_pathc;
2ff057
2ff057
    if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && dirname[0] == '~') {
2ff057
	if (dirname[1] == '\0' || dirname[1] == '/') {
2ff057
	    /* Look up home directory.  */
2ff057
	    const char *home_dir = getenv("HOME");
2ff057
	    if (home_dir == NULL || home_dir[0] == '\0') {
2ff057
		int success;
2ff057
		char *name;
2ff057
		success = (name = getlogin()) != NULL;
2ff057
		if (success) {
2ff057
		    struct passwd *p;
2ff057
		    p = getpwnam(name);
2ff057
		    if (p != NULL)
2ff057
			home_dir = p->pw_dir;
2ff057
		}
2ff057
	    }
2ff057
	    if (home_dir == NULL || home_dir[0] == '\0') {
2ff057
		if (flags & GLOB_TILDE_CHECK)
2ff057
		    return GLOB_NOMATCH;
2ff057
		else
2ff057
		    home_dir = "~";	/* No luck.  */
2ff057
	    }
2ff057
	    /* Now construct the full directory.  */
2ff057
	    if (dirname[1] == '\0')
2ff057
		dirname = home_dir;
2ff057
	    else {
2ff057
		char *newp;
2ff057
		size_t home_len = strlen(home_dir);
2ff057
		newp = (char *) alloca(home_len + dirlen);
2ff057
		mempcpy(mempcpy(newp, home_dir, home_len),
2ff057
			&dirname[1], dirlen);
2ff057
		dirname = newp;
2ff057
	    }
2ff057
	}
2ff057
	else {
2ff057
	    char *end_name = strchr(dirname, '/');
2ff057
	    const char *user_name;
2ff057
	    const char *home_dir;
2ff057
2ff057
	    if (end_name == NULL)
2ff057
		user_name = dirname + 1;
2ff057
	    else {
2ff057
		char *newp;
2ff057
		newp = (char *) alloca(end_name - dirname + 1);
2ff057
		*((char *) mempcpy(newp, dirname + 1, end_name - dirname))
2ff057
		    = '\0';
2ff057
		user_name = newp;
2ff057
	    }
2ff057
2ff057
	    /* Look up specific user's home directory.  */
2ff057
	    {
2ff057
		struct passwd *p;
2ff057
		p = getpwnam(user_name);
2ff057
		if (p != NULL)
2ff057
		    home_dir = p->pw_dir;
2ff057
		else
2ff057
		    home_dir = NULL;
2ff057
	    }
2ff057
	    /* If we found a home directory use this.  */
2ff057
	    if (home_dir != NULL) {
2ff057
		char *newp;
2ff057
		size_t home_len = strlen(home_dir);
2ff057
		size_t rest_len = end_name == NULL ? 0 : strlen(end_name);
2ff057
		newp = (char *) alloca(home_len + rest_len + 1);
2ff057
		*((char *) mempcpy(mempcpy(newp, home_dir, home_len),
2ff057
				   end_name, rest_len)) = '\0';
2ff057
		dirname = newp;
2ff057
	    } else if (flags & GLOB_TILDE_CHECK)
2ff057
		/* We have to regard it as an error if we cannot find the
2ff057
		   home directory.  */
2ff057
		return GLOB_NOMATCH;
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* Now test whether we looked for "~" or "~NAME".  In this case we
2ff057
       can give the answer now.  */
2ff057
    if (filename == NULL) {
2ff057
	struct stat st;
2ff057
2ff057
	/* Return the directory if we don't check for error or if it exists.  */
2ff057
	if ((flags & GLOB_NOCHECK)
2ff057
	    || (((flags & GLOB_ALTDIRFUNC)
2ff057
		 ? (*pglob->gl_stat) (dirname, &st)
2ff057
		 : stat(dirname, &st)) == 0 && S_ISDIR(st.st_mode))) {
2ff057
	    pglob->gl_pathv
2ff057
		= (char **) xrealloc(pglob->gl_pathv,
2ff057
				     (pglob->gl_pathc +
2ff057
				      ((flags & GLOB_DOOFFS) ?
2ff057
				       pglob->gl_offs : 0) +
2ff057
				      1 + 1) * sizeof(char *));
2ff057
2ff057
	    if (flags & GLOB_DOOFFS)
2ff057
		while (pglob->gl_pathc < pglob->gl_offs)
2ff057
		    pglob->gl_pathv[pglob->gl_pathc++] = NULL;
2ff057
2ff057
	    pglob->gl_pathv[pglob->gl_pathc] = xstrdup(dirname);
2ff057
	    if (pglob->gl_pathv[pglob->gl_pathc] == NULL) {
2ff057
		free(pglob->gl_pathv);
2ff057
		return GLOB_NOSPACE;
2ff057
	    }
2ff057
	    pglob->gl_pathv[++pglob->gl_pathc] = NULL;
2ff057
	    pglob->gl_flags = flags;
2ff057
2ff057
	    return 0;
2ff057
	}
2ff057
2ff057
	/* Not found.  */
2ff057
	return GLOB_NOMATCH;
2ff057
    }
2ff057
2ff057
    if (__glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) {
2ff057
	/* The directory name contains metacharacters, so we
2ff057
	   have to glob for the directory, and then glob for
2ff057
	   the pattern in each directory found.  */
2ff057
	glob_t dirs;
2ff057
	register int i;
2ff057
2ff057
	if ((flags & GLOB_ALTDIRFUNC) != 0) {
2ff057
	    /* Use the alternative access functions also in the recursive
2ff057
	       call.  */
2ff057
	    dirs.gl_opendir = pglob->gl_opendir;
2ff057
	    dirs.gl_readdir = pglob->gl_readdir;
2ff057
	    dirs.gl_closedir = pglob->gl_closedir;
2ff057
	    dirs.gl_stat = pglob->gl_stat;
2ff057
	    dirs.gl_lstat = pglob->gl_lstat;
2ff057
	}
2ff057
2ff057
	status = glob(dirname,
2ff057
		      ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE
2ff057
				 | GLOB_ALTDIRFUNC))
2ff057
		       | GLOB_NOSORT | GLOB_ONLYDIR), errfunc, &dirs);
2ff057
	if (status != 0)
2ff057
	    return status;
2ff057
2ff057
	/* We have successfully globbed the preceding directory name.
2ff057
	   For each name we found, call glob_in_dir on it and FILENAME,
2ff057
	   appending the results to PGLOB.  */
2ff057
	for (i = 0; i < dirs.gl_pathc; ++i) {
2ff057
	    int old_pathc = pglob->gl_pathc;
2ff057
	    status = glob_in_dir(filename, dirs.gl_pathv[i],
2ff057
				 ((flags | GLOB_APPEND)
2ff057
				  & ~(GLOB_NOCHECK | GLOB_ERR)),
2ff057
				 errfunc, pglob);
2ff057
	    if (status == GLOB_NOMATCH)
2ff057
		/* No matches in this directory.  Try the next.  */
2ff057
		continue;
2ff057
2ff057
	    if (status != 0) {
2ff057
		globfree(&dirs);
2ff057
		globfree(pglob);
2ff057
		return status;
2ff057
	    }
2ff057
2ff057
	    /* Stick the directory on the front of each name.  */
2ff057
	    if (prefix_array(dirs.gl_pathv[i],
2ff057
			     &pglob->gl_pathv[old_pathc],
2ff057
			     pglob->gl_pathc - old_pathc)) {
2ff057
		globfree(&dirs);
2ff057
		globfree(pglob);
2ff057
		return GLOB_NOSPACE;
2ff057
	    }
2ff057
	}
2ff057
2ff057
	flags |= GLOB_MAGCHAR;
2ff057
2ff057
	/* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls.
2ff057
	   But if we have not found any matching entry and thie GLOB_NOCHECK
2ff057
	   flag was set we must return the list consisting of the disrectory
2ff057
	   names followed by the filename.  */
2ff057
	if (pglob->gl_pathc == oldcount) {
2ff057
	    /* No matches.  */
2ff057
	    if (flags & GLOB_NOCHECK) {
2ff057
		size_t filename_len = strlen(filename) + 1;
2ff057
		char **new_pathv;
2ff057
		struct stat st;
2ff057
2ff057
		/* This is an pessimistic guess about the size.  */
2ff057
		pglob->gl_pathv
2ff057
		    = (char **) xrealloc(pglob->gl_pathv,
2ff057
					 (pglob->gl_pathc +
2ff057
					  ((flags & GLOB_DOOFFS) ?
2ff057
					   pglob->gl_offs : 0) +
2ff057
					  dirs.gl_pathc + 1) *
2ff057
					 sizeof(char *));
2ff057
2ff057
		if (flags & GLOB_DOOFFS)
2ff057
		    while (pglob->gl_pathc < pglob->gl_offs)
2ff057
			pglob->gl_pathv[pglob->gl_pathc++] = NULL;
2ff057
2ff057
		for (i = 0; i < dirs.gl_pathc; ++i) {
2ff057
		    const char *dir = dirs.gl_pathv[i];
2ff057
		    size_t dir_len = strlen(dir);
2ff057
2ff057
		    /* First check whether this really is a directory.  */
2ff057
		    if (((flags & GLOB_ALTDIRFUNC)
2ff057
			 ? (*pglob->gl_stat) (dir, &st) : stat(dir,
2ff057
								 &st)) != 0
2ff057
			|| !S_ISDIR(st.st_mode))
2ff057
			/* No directory, ignore this entry.  */
2ff057
			continue;
2ff057
2ff057
		    pglob->gl_pathv[pglob->gl_pathc] = xmalloc(dir_len + 1
2ff057
							       +
2ff057
							       filename_len);
2ff057
		    mempcpy(mempcpy
2ff057
			    (mempcpy
2ff057
			     (pglob->gl_pathv[pglob->gl_pathc], dir,
2ff057
			      dir_len), "/", 1), filename, filename_len);
2ff057
		    ++pglob->gl_pathc;
2ff057
		}
2ff057
2ff057
		pglob->gl_pathv[pglob->gl_pathc] = NULL;
2ff057
		pglob->gl_flags = flags;
2ff057
2ff057
		/* Now we know how large the gl_pathv vector must be.  */
2ff057
		new_pathv = (char **) xrealloc(pglob->gl_pathv,
2ff057
					       ((pglob->gl_pathc + 1)
2ff057
						* sizeof(char *)));
2ff057
		pglob->gl_pathv = new_pathv;
2ff057
	    } else
2ff057
		return GLOB_NOMATCH;
2ff057
	}
2ff057
2ff057
	globfree(&dirs);
2ff057
    } else {
2ff057
	status = glob_in_dir(filename, dirname, flags, errfunc, pglob);
2ff057
	if (status != 0)
2ff057
	    return status;
2ff057
2ff057
	if (dirlen > 0) {
2ff057
	    /* Stick the directory on the front of each name.  */
2ff057
	    int ignore = oldcount;
2ff057
2ff057
	    if ((flags & GLOB_DOOFFS) && ignore < pglob->gl_offs)
2ff057
		ignore = pglob->gl_offs;
2ff057
2ff057
	    if (prefix_array(dirname,
2ff057
			     &pglob->gl_pathv[ignore],
2ff057
			     pglob->gl_pathc - ignore)) {
2ff057
		globfree(pglob);
2ff057
		return GLOB_NOSPACE;
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (flags & GLOB_MARK) {
2ff057
	/* Append slashes to directory names.  */
2ff057
	int i;
2ff057
	struct stat st;
2ff057
	for (i = oldcount; i < pglob->gl_pathc; ++i)
2ff057
	    if (((flags & GLOB_ALTDIRFUNC)
2ff057
		 ? (*pglob->gl_stat) (pglob->gl_pathv[i], &st)
2ff057
		 : stat(pglob->gl_pathv[i], &st)) == 0
2ff057
		&& S_ISDIR(st.st_mode)) {
2ff057
		size_t len = strlen(pglob->gl_pathv[i]) + 2;
2ff057
		char *new = xrealloc(pglob->gl_pathv[i], len);
2ff057
		strcpy(&new[len - 2], "/");
2ff057
		pglob->gl_pathv[i] = new;
2ff057
	    }
2ff057
    }
2ff057
2ff057
    if (!(flags & GLOB_NOSORT)) {
2ff057
	/* Sort the vector.  */
2ff057
	int non_sort = oldcount;
2ff057
2ff057
	if ((flags & GLOB_DOOFFS) && pglob->gl_offs > oldcount)
2ff057
	    non_sort = pglob->gl_offs;
2ff057
2ff057
	qsort(& pglob->gl_pathv[non_sort],
2ff057
	      pglob->gl_pathc - non_sort,
2ff057
	      sizeof(char *), collated_compare);
2ff057
    }
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
2ff057
/* Free storage allocated in PGLOB by a previous `glob' call.  */
2ff057
static void globfree(glob_t * pglob)
2ff057
{
2ff057
    if (pglob->gl_pathv != NULL) {
2ff057
	register int i;
2ff057
	for (i = 0; i < pglob->gl_pathc; ++i)
2ff057
	    if (pglob->gl_pathv[i] != NULL)
2ff057
		free(pglob->gl_pathv[i]);
2ff057
	free(pglob->gl_pathv);
2ff057
    }
2ff057
}
2ff057
2ff057
2ff057
/* Do a collated comparison of A and B.  */
2ff057
static int collated_compare(const void * a, const void * b)
2ff057
{
2ff057
    const char *const s1 = *(const char *const *const) a;
2ff057
    const char *const s2 = *(const char *const *const) b;
2ff057
2ff057
    if (s1 == s2)
2ff057
	return 0;
2ff057
    if (s1 == NULL)
2ff057
	return 1;
2ff057
    if (s2 == NULL)
2ff057
	return -1;
2ff057
    return strcoll(s1, s2);
2ff057
}
2ff057
2ff057
2ff057
/* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's
2ff057
   elements in place.  Return nonzero if out of memory, zero if successful.
2ff057
   A slash is inserted between DIRNAME and each elt of ARRAY,
2ff057
   unless DIRNAME is just "/".  Each old element of ARRAY is freed.  */
2ff057
static int prefix_array(const char *dirname, char **array, size_t n)
2ff057
{
2ff057
    register size_t i;
2ff057
    size_t dirlen = strlen(dirname);
2ff057
2ff057
    if (dirlen == 1 && dirname[0] == '/')
2ff057
	/* DIRNAME is just "/", so normal prepending would get us "//foo".
2ff057
	   We want "/foo" instead, so don't prepend any chars from DIRNAME.  */
2ff057
	dirlen = 0;
2ff057
2ff057
    for (i = 0; i < n; ++i) {
2ff057
	size_t eltlen = strlen(array[i]) + 1;
2ff057
	char *new = (char *) xmalloc(dirlen + 1 + eltlen);
2ff057
	{
2ff057
	    char *endp = (char *) mempcpy(new, dirname, dirlen);
2ff057
	    *endp++ = '/';
2ff057
	    mempcpy(endp, array[i], eltlen);
2ff057
	}
2ff057
	free(array[i]);
2ff057
	array[i] = new;
2ff057
    }
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
/* Return nonzero if PATTERN contains any metacharacters.
2ff057
   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
2ff057
static int __glob_pattern_p(const char *pattern, int quote)
2ff057
{
2ff057
    register const char *p;
2ff057
    int openBrackets = 0;
2ff057
2ff057
    for (p = pattern; *p != '\0'; ++p)
2ff057
	switch (*p) {
2ff057
	case '?':
2ff057
	case '*':
2ff057
	    return 1;
2ff057
2ff057
	case '\\':
2ff057
	    if (quote && p[1] != '\0')
2ff057
		++p;
2ff057
	    break;
2ff057
2ff057
	case '[':
2ff057
	    openBrackets = 1;
2ff057
	    break;
2ff057
2ff057
	case ']':
2ff057
	    if (openBrackets)
2ff057
		return 1;
2ff057
	    break;
2ff057
	}
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
/* Like `glob', but PATTERN is a final pathname component,
2ff057
   and matches are searched for in DIRECTORY.
2ff057
   The GLOB_NOSORT bit in FLAGS is ignored.  No sorting is ever done.
2ff057
   The GLOB_APPEND flag is assumed to be set (always appends).  */
2ff057
static int
2ff057
glob_in_dir(const char *pattern, const char *directory, int flags,
2ff057
	    int (*errfunc)(const char *, int), glob_t * pglob)
2ff057
{
2ff057
    void * stream = NULL;
2ff057
2ff057
    struct globlink {
2ff057
	struct globlink *next;
2ff057
	char *name;
2ff057
    };
2ff057
    struct globlink *names = NULL;
2ff057
    size_t nfound;
2ff057
    int meta;
2ff057
    int save;
2ff057
2ff057
    meta = __glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE));
2ff057
    if (meta == 0) {
2ff057
	if (flags & (GLOB_NOCHECK | GLOB_NOMAGIC))
2ff057
	    /* We need not do any tests.  The PATTERN contains no meta
2ff057
	       characters and we must not return an error therefore the
2ff057
	       result will always contain exactly one name.  */
2ff057
	    flags |= GLOB_NOCHECK;
2ff057
	else {
2ff057
	    /* Since we use the normal file functions we can also use stat()
2ff057
	       to verify the file is there.  */
2ff057
	    struct stat st;
2ff057
	    size_t patlen = strlen(pattern);
2ff057
	    size_t dirlen = strlen(directory);
2ff057
	    char *fullname = (char *) alloca(dirlen + 1 + patlen + 1);
2ff057
2ff057
	    mempcpy(mempcpy(mempcpy(fullname, directory, dirlen),
2ff057
			    "/", 1), pattern, patlen + 1);
2ff057
	    if (((flags & GLOB_ALTDIRFUNC)
2ff057
		 ? (*pglob->gl_stat) (fullname, &st)
2ff057
		 : stat(fullname, &st)) == 0)
2ff057
		/* We found this file to be existing.  Now tell the rest
2ff057
		   of the function to copy this name into the result.  */
2ff057
		flags |= GLOB_NOCHECK;
2ff057
	}
2ff057
2ff057
	nfound = 0;
2ff057
    } else {
2ff057
	if (pattern[0] == '\0') {
2ff057
	    /* This is a special case for matching directories like in
2ff057
	       "*a/".  */
2ff057
	    names = (struct globlink *) alloca(sizeof(struct globlink));
2ff057
	    names->name = (char *) xmalloc(1);
2ff057
	    names->name[0] = '\0';
2ff057
	    names->next = NULL;
2ff057
	    nfound = 1;
2ff057
	    meta = 0;
2ff057
	} else {
2ff057
	    stream = ((flags & GLOB_ALTDIRFUNC)
2ff057
		      ? (*pglob->gl_opendir) (directory)
2ff057
		      : opendir(directory));
2ff057
	    if (stream == NULL) {
2ff057
		if (errno != ENOTDIR
2ff057
		    && ((errfunc != NULL && (*errfunc) (directory, errno))
2ff057
			|| (flags & GLOB_ERR)))
2ff057
		    return GLOB_ABORTED;
2ff057
		nfound = 0;
2ff057
		meta = 0;
2ff057
	    } else {
2ff057
		int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) |
2ff057
				 ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
2ff057
		nfound = 0;
2ff057
		flags |= GLOB_MAGCHAR;
2ff057
2ff057
		while (1) {
2ff057
		    const char *name;
2ff057
		    size_t len;
2ff057
		    struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
2ff057
					? (*pglob->gl_readdir) (stream)
2ff057
					: readdir((DIR *) stream));
2ff057
		    if (d == NULL)
2ff057
			break;
2ff057
		    if (!REAL_DIR_ENTRY(d))
2ff057
			continue;
2ff057
2ff057
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
2ff057
		    /* If we shall match only directories use the information
2ff057
		       provided by the dirent call if possible.  */
2ff057
		    if ((flags & GLOB_ONLYDIR)
2ff057
			&& d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
2ff057
			continue;
2ff057
#endif
2ff057
2ff057
		    name = d->d_name;
2ff057
2ff057
		    if (fnmatch(pattern, name, fnm_flags) == 0) {
2ff057
			struct globlink *new = (struct globlink *)
2ff057
			    alloca(sizeof(struct globlink));
2ff057
			len = NAMLEN(d);
2ff057
			new->name = (char *) xmalloc(len + 1);
2ff057
			*((char *) mempcpy(new->name, name, len))
2ff057
			    = '\0';
2ff057
			new->next = names;
2ff057
			names = new;
2ff057
			++nfound;
2ff057
		    }
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (nfound == 0 && (flags & GLOB_NOCHECK)) {
2ff057
	size_t len = strlen(pattern);
2ff057
	nfound = 1;
2ff057
	names = (struct globlink *) alloca(sizeof(struct globlink));
2ff057
	names->next = NULL;
2ff057
	names->name = (char *) xmalloc(len + 1);
2ff057
	*((char *) mempcpy(names->name, pattern, len)) = '\0';
2ff057
    }
2ff057
2ff057
    if (nfound != 0) {
2ff057
	pglob->gl_pathv
2ff057
	    = (char **) xrealloc(pglob->gl_pathv,
2ff057
				 (pglob->gl_pathc +
2ff057
				  ((flags & GLOB_DOOFFS) ? pglob->
2ff057
				   gl_offs : 0) + nfound +
2ff057
				  1) * sizeof(char *));
2ff057
2ff057
	if (flags & GLOB_DOOFFS)
2ff057
	    while (pglob->gl_pathc < pglob->gl_offs)
2ff057
		pglob->gl_pathv[pglob->gl_pathc++] = NULL;
2ff057
2ff057
	for (; names != NULL; names = names->next)
2ff057
	    pglob->gl_pathv[pglob->gl_pathc++] = names->name;
2ff057
	pglob->gl_pathv[pglob->gl_pathc] = NULL;
2ff057
2ff057
	pglob->gl_flags = flags;
2ff057
    }
2ff057
2ff057
    save = errno;
2ff057
    if (stream != NULL) {
2ff057
	if (flags & GLOB_ALTDIRFUNC)
2ff057
	    (*pglob->gl_closedir) (stream);
2ff057
	else
2ff057
	    closedir((DIR *) stream);
2ff057
    }
2ff057
    __set_errno(save);
2ff057
2ff057
    return nfound == 0 ? GLOB_NOMATCH : 0;
2ff057
}
2ff057
2ff057
/* librpmio exported interfaces */
2ff057
2ff057
int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)
2ff057
{
2ff057
    int ac = 0;
2ff057
    const char ** av = NULL;
2ff057
    int argc = 0;
2ff057
    ARGV_t argv = NULL;
2ff057
    char * globRoot = NULL;
2ff057
    const char *home = getenv("HOME");
2ff057
    int gflags = 0;
2ff057
#ifdef ENABLE_NLS
2ff057
    char * old_collate = NULL;
2ff057
    char * old_ctype = NULL;
2ff057
    const char * t;
2ff057
#endif
2ff057
    size_t maxb, nb;
2ff057
    int i, j;
2ff057
    int rc;
2ff057
2ff057
    gflags |= GLOB_BRACE;
2ff057
2ff057
    if (home != NULL && strlen(home) > 0) 
2ff057
	gflags |= GLOB_TILDE;
2ff057
2ff057
    /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */
2ff057
    rc = poptParseArgvString(patterns, &ac, &av;;
2ff057
    if (rc)
2ff057
	return rc;
2ff057
2ff057
#ifdef ENABLE_NLS
2ff057
    t = setlocale(LC_COLLATE, NULL);
2ff057
    if (t)
2ff057
    	old_collate = xstrdup(t);
2ff057
    t = setlocale(LC_CTYPE, NULL);
2ff057
    if (t)
2ff057
    	old_ctype = xstrdup(t);
2ff057
    (void) setlocale(LC_COLLATE, "C");
2ff057
    (void) setlocale(LC_CTYPE, "C");
2ff057
#endif
2ff057
	
2ff057
    if (av != NULL)
2ff057
    for (j = 0; j < ac; j++) {
2ff057
	char * globURL;
2ff057
	const char * path;
2ff057
	int ut = urlPath(av[j], &path);
2ff057
	int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
2ff057
	size_t plen = strlen(path);
2ff057
	int flags = gflags;
2ff057
	int dir_only = (plen > 0 && path[plen-1] == '/');
2ff057
	glob_t gl;
2ff057
2ff057
	if (!local || (!rpmIsGlob(av[j], 0) && strchr(path, '~') == NULL)) {
2ff057
	    argvAdd(&argv, av[j]);
2ff057
	    continue;
2ff057
	}
2ff057
2ff057
	if (dir_only)
2ff057
	    flags |= GLOB_ONLYDIR;
2ff057
	
2ff057
	gl.gl_pathc = 0;
2ff057
	gl.gl_pathv = NULL;
2ff057
	
2ff057
	rc = glob(av[j], flags, NULL, &gl);
2ff057
	if (rc)
2ff057
	    goto exit;
2ff057
2ff057
	/* XXX Prepend the URL leader for globs that have stripped it off */
2ff057
	maxb = 0;
2ff057
	for (i = 0; i < gl.gl_pathc; i++) {
2ff057
	    if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
2ff057
		maxb = nb;
2ff057
	}
2ff057
	
2ff057
	nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
2ff057
	maxb += nb;
2ff057
	maxb += 1;
2ff057
	globURL = globRoot = xmalloc(maxb);
2ff057
2ff057
	switch (ut) {
2ff057
	case URL_IS_PATH:
2ff057
	case URL_IS_DASH:
2ff057
	    strncpy(globRoot, av[j], nb);
2ff057
	    break;
2ff057
	case URL_IS_HTTPS:
2ff057
	case URL_IS_HTTP:
2ff057
	case URL_IS_FTP:
2ff057
	case URL_IS_HKP:
2ff057
	case URL_IS_UNKNOWN:
2ff057
	default:
2ff057
	    break;
2ff057
	}
2ff057
	globRoot += nb;
2ff057
	*globRoot = '\0';
2ff057
2ff057
	for (i = 0; i < gl.gl_pathc; i++) {
2ff057
	    const char * globFile = &(gl.gl_pathv[i][0]);
2ff057
2ff057
	    if (dir_only) {
2ff057
		struct stat sb;
2ff057
		if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode))
2ff057
		    continue;
2ff057
	    }
2ff057
		
2ff057
	    if (globRoot > globURL && globRoot[-1] == '/')
2ff057
		while (*globFile == '/') globFile++;
2ff057
	    strcpy(globRoot, globFile);
2ff057
	    argvAdd(&argv, globURL);
2ff057
	}
2ff057
	globfree(&gl);
2ff057
	free(globURL);
2ff057
    }
2ff057
2ff057
    argc = argvCount(argv);
2ff057
    if (argc > 0) {
2ff057
	if (argvPtr)
2ff057
	    *argvPtr = argv;
2ff057
	if (argcPtr)
2ff057
	    *argcPtr = argc;
2ff057
	rc = 0;
2ff057
    } else
2ff057
	rc = 1;
2ff057
2ff057
2ff057
exit:
2ff057
#ifdef ENABLE_NLS	
2ff057
    if (old_collate) {
2ff057
	(void) setlocale(LC_COLLATE, old_collate);
2ff057
	free(old_collate);
2ff057
    }
2ff057
    if (old_ctype) {
2ff057
	(void) setlocale(LC_CTYPE, old_ctype);
2ff057
	free(old_ctype);
2ff057
    }
2ff057
#endif
2ff057
    av = _free(av);
2ff057
    if (rc || argvPtr == NULL) {
2ff057
	argvFree(argv);
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmIsGlob(const char * pattern, int quote)
2ff057
{
2ff057
    if (!__glob_pattern_p(pattern, quote)) {
2ff057
2ff057
	const char *begin;
2ff057
	const char *next;
2ff057
	const char *rest;
2ff057
2ff057
	begin = strchr(pattern, '{');
2ff057
	if (begin == NULL)
2ff057
	    return 0;
2ff057
	/*
2ff057
	 * Find the first sub-pattern and at the same time find the
2ff057
	 *  rest after the closing brace.
2ff057
	 */
2ff057
	next = next_brace_sub(begin + 1);
2ff057
	if (next == NULL)
2ff057
	    return 0;
2ff057
2ff057
	/* Now find the end of the whole brace expression.  */
2ff057
	rest = next;
2ff057
	while (*rest != '}') {
2ff057
	    rest = next_brace_sub(rest + 1);
2ff057
	    if (rest == NULL)
2ff057
		return 0;
2ff057
	}
2ff057
	/* Now we can be sure that brace expression is well-foermed. */
2ff057
    }
2ff057
2ff057
    return 1;
2ff057
}