/** \ingroup rpmdep
 * \file lib/rpmal.c
 */

#include "system.h"

#include <rpmlib.h>

#include "rpmal.h"
#include "rpmds.h"
#include "rpmfi.h"

#include "debug.h"

typedef /*@abstract@*/ struct availablePackage_s * availablePackage;

/*@unchecked@*/
int _rpmal_debug = 0;

/*@access alKey @*/
/*@access alNum @*/
/*@access rpmal @*/
/*@access availablePackage @*/

/*@access fnpyKey @*/	/* XXX suggestedKeys array */

/** \ingroup rpmdep
 * Info about a single package to be installed.
 */
struct availablePackage_s {
/*@refcounted@*/ /*@null@*/
    rpmds provides;		/*!< Provides: dependencies. */
/*@refcounted@*/ /*@null@*/
    rpmfi fi;			/*!< File info set. */

    uint_32 tscolor;		/*!< Transaction color bits. */

/*@exposed@*/ /*@dependent@*/ /*@null@*/
    fnpyKey key;		/*!< Associated file name/python object */

};

typedef /*@abstract@*/ struct availableIndexEntry_s *	availableIndexEntry;
/*@access availableIndexEntry@*/

/** \ingroup rpmdep
 * A single available item (e.g. a Provides: dependency).
 */
struct availableIndexEntry_s {
/*@exposed@*/ /*@dependent@*/ /*@null@*/
    alKey pkgKey;		/*!< Containing package. */
/*@observer@*/
    const char * entry;		/*!< Dependency name. */
    unsigned short entryLen;	/*!< No. of bytes in name. */
    unsigned short entryIx;	/*!< Dependency index. */
    enum indexEntryType {
	IET_PROVIDES=1			/*!< A Provides: dependency. */
    } type;			/*!< Type of available item. */
};

typedef /*@abstract@*/ struct availableIndex_s *	availableIndex;
/*@access availableIndex@*/

/** \ingroup rpmdep
 * Index of all available items.
 */
struct availableIndex_s {
/*@null@*/
    availableIndexEntry index;	/*!< Array of available items. */
    int size;			/*!< No. of available items. */
    int k;			/*!< Current index. */
};

typedef /*@abstract@*/ struct fileIndexEntry_s *	fileIndexEntry;
/*@access fileIndexEntry@*/

/** \ingroup rpmdep
 * A file to be installed/removed.
 */
struct fileIndexEntry_s {
/*@dependent@*/ /*@relnull@*/
    const char * baseName;	/*!< File basename. */
    int baseNameLen;
    alNum pkgNum;		/*!< Containing package index. */
    uint_32 ficolor;
};

typedef /*@abstract@*/ struct dirInfo_s *		dirInfo;
/*@access dirInfo@*/

/** \ingroup rpmdep
 * A directory to be installed/removed.
 */
struct dirInfo_s {
/*@owned@*/ /*@relnull@*/
    const char * dirName;	/*!< Directory path (+ trailing '/'). */
    int dirNameLen;		/*!< No. bytes in directory path. */
/*@owned@*/
    fileIndexEntry files;	/*!< Array of files in directory. */
    int numFiles;		/*!< No. files in directory. */
};

/** \ingroup rpmdep
 * Set of available packages, items, and directories.
 */
struct rpmal_s {
/*@owned@*/ /*@null@*/
    availablePackage list;	/*!< Set of packages. */
    struct availableIndex_s index;	/*!< Set of available items. */
    int delta;			/*!< Delta for pkg list reallocation. */
    int size;			/*!< No. of pkgs in list. */
    int alloced;		/*!< No. of pkgs allocated for list. */
    uint_32 tscolor;		/*!< Transaction color. */
    int numDirs;		/*!< No. of directories. */
/*@owned@*/ /*@null@*/
    dirInfo dirs;		/*!< Set of directories. */
};

/**
 * Destroy available item index.
 * @param al		available list
 */
static void rpmalFreeIndex(rpmal al)
	/*@modifies al @*/
{
    availableIndex ai = &al->index;
    if (ai->size > 0) {
	ai->index = _free(ai->index);
	ai->size = 0;
    }
}

#ifdef	DYING
/**
 * Return number of packages in list.
 * @param al		available list
 * @return		no. of packages in list
 */
static int alGetSize(/*@null@*/ const rpmal al)
	/*@*/
{
    return (al != NULL ? al->size : 0);
}
#endif

static inline alNum alKey2Num(/*@unused@*/ /*@null@*/ const rpmal al,
		/*@null@*/ alKey pkgKey)
	/*@*/
{
    /*@-nullret -temptrans -retalias @*/
    return ((alNum)pkgKey);
    /*@=nullret =temptrans =retalias @*/
}

static inline alKey alNum2Key(/*@unused@*/ /*@null@*/ const rpmal al,
		/*@null@*/ alNum pkgNum)
	/*@*/
{
    /*@-nullret -temptrans -retalias @*/
    return ((alKey)pkgNum);
    /*@=nullret =temptrans =retalias @*/
}

#ifdef	DYING
/**
 * Return available package.
 * @param al		available list
 * @param pkgKey	available package key
 * @return		available package pointer
 */
/*@dependent@*/ /*@null@*/
static availablePackage alGetPkg(/*@null@*/ const rpmal al,
		/*@null@*/ alKey pkgKey)
	/*@*/
{
    alNum pkgNum = alKey2Num(al, pkgKey);
    availablePackage alp = NULL;

    if (al != NULL && pkgNum >= 0 && pkgNum < alGetSize(al)) {
	if (al->list != NULL)
	    alp = al->list + pkgNum;
    }
    return alp;
}
#endif

rpmal rpmalCreate(int delta)
{
    rpmal al = xcalloc(1, sizeof(*al));
    availableIndex ai = &al->index;

    al->delta = delta;
    al->size = 0;
    al->list = xcalloc(al->delta, sizeof(*al->list));
    al->alloced = al->delta;

    ai->index = NULL;
    ai->size = 0;

    al->numDirs = 0;
    al->dirs = NULL;
    return al;
}

rpmal rpmalFree(rpmal al)
{
    availablePackage alp;
    dirInfo die;
    int i;

    if (al == NULL)
	return NULL;

    if ((alp = al->list) != NULL)
    for (i = 0; i < al->size; i++, alp++) {
	alp->provides = rpmdsFree(alp->provides);
	alp->fi = rpmfiFree(alp->fi);
    }

    if ((die = al->dirs) != NULL)
    for (i = 0; i < al->numDirs; i++, die++) {
	die->dirName = _free(die->dirName);
	die->files = _free(die->files);
    }
    al->dirs = _free(al->dirs);
    al->numDirs = 0;

    al->list = _free(al->list);
    al->alloced = 0;
    rpmalFreeIndex(al);
    al = _free(al);
    return NULL;
}

/**
 * Compare two directory info entries by name (qsort/bsearch).
 * @param one		1st directory info
 * @param two		2nd directory info
 * @return		result of comparison
 */
static int dieCompare(const void * one, const void * two)
	/*@*/
{
    /*@-castexpose@*/
    const dirInfo a = (const dirInfo) one;
    const dirInfo b = (const dirInfo) two;
    /*@=castexpose@*/
    int lenchk = a->dirNameLen - b->dirNameLen;

    if (lenchk || a->dirNameLen == 0)
	return lenchk;

    if (a->dirName == NULL || b->dirName == NULL)
	return lenchk;

    /* XXX FIXME: this might do "backward" strcmp for speed */
    return strcmp(a->dirName, b->dirName);
}

/**
 * Compare two file info entries by name (qsort/bsearch).
 * @param one		1st directory info
 * @param two		2nd directory info
 * @return		result of comparison
 */
static int fieCompare(const void * one, const void * two)
	/*@*/
{
    /*@-castexpose@*/
    const fileIndexEntry a = (const fileIndexEntry) one;
    const fileIndexEntry b = (const fileIndexEntry) two;
    /*@=castexpose@*/
    int lenchk = a->baseNameLen - b->baseNameLen;

    if (lenchk)
	return lenchk;

    if (a->baseName == NULL || b->baseName == NULL)
	return lenchk;

#ifdef	NOISY
/*@-modfilesys@*/
if (_rpmal_debug) {
fprintf(stderr, "\t\tstrcmp(%p:%p, %p:%p)", a, a->baseName, b, b->baseName);
#if 0
fprintf(stderr, " a %s", a->baseName);
#endif
fprintf(stderr, " b %s", a->baseName);
fprintf(stderr, "\n");
}
/*@=modfilesys@*/
#endif

    return strcmp(a->baseName, b->baseName);
}

void rpmalDel(rpmal al, alKey pkgKey)
{
    alNum pkgNum = alKey2Num(al, pkgKey);
    availablePackage alp;
    rpmfi fi;

    if (al == NULL || al->list == NULL)
	return;		/* XXX can't happen */

    alp = al->list + pkgNum;

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "*** del %p[%d]\n", al->list, pkgNum);
/*@=modfilesys@*/

    /* Delete directory/file info entries from added package list. */
    if ((fi = alp->fi) != NULL)
    if (rpmfiFC(fi) > 0) {
	int origNumDirs = al->numDirs;
	int dx;
	dirInfo dieNeedle =
		memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
	dirInfo die;
	int last;
	int i;

	/* XXX FIXME: We ought to relocate the directory list here */

	if (al->dirs != NULL)
	for (dx = rpmfiDC(fi) - 1; dx >= 0; dx--)
	{
	    fileIndexEntry fie;

	    (void) rpmfiSetDX(fi, dx);

	    /*@-assignexpose -dependenttrans -observertrans@*/
	    dieNeedle->dirName = (char *) rpmfiDN(fi);
	    /*@=assignexpose =dependenttrans =observertrans@*/
	    dieNeedle->dirNameLen = (dieNeedle->dirName != NULL
			? strlen(dieNeedle->dirName) : 0);
/*@-boundswrite@*/
	    die = bsearch(dieNeedle, al->dirs, al->numDirs,
			       sizeof(*dieNeedle), dieCompare);
/*@=boundswrite@*/
	    if (die == NULL)
		continue;

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "--- die[%5d] %p [%3d] %s\n", (die - al->dirs), die, die->dirNameLen, die->dirName);
/*@=modfilesys@*/

	    last = die->numFiles;
	    fie = die->files + last - 1;
	    for (i = last - 1; i >= 0; i--, fie--) {
		if (fie->pkgNum != pkgNum)
		    /*@innercontinue@*/ continue;
		die->numFiles--;

		if (i < die->numFiles) {
/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "\t%p[%3d] memmove(%p:%p,%p:%p,0x%x) %s <- %s\n", die->files, die->numFiles, fie, fie->baseName, fie+1, (fie+1)->baseName, ((die->numFiles - i) * sizeof(*fie)), fie->baseName, (fie+1)->baseName);
/*@=modfilesys@*/

/*@-bounds@*/
		    memmove(fie, fie+1, (die->numFiles - i) * sizeof(*fie));
/*@=bounds@*/
		}
/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "\t%p[%3d] memset(%p,0,0x%x) %p [%3d] %s\n", die->files, die->numFiles, die->files + die->numFiles, sizeof(*fie), fie->baseName, fie->baseNameLen, fie->baseName);
/*@=modfilesys@*/
		memset(die->files + die->numFiles, 0, sizeof(*fie)); /* overkill */

	    }
	    if (die->numFiles > 0) {
		if (last > i)
		    die->files = xrealloc(die->files,
					die->numFiles * sizeof(*die->files));
		continue;
	    }
	    die->files = _free(die->files);
	    die->dirName = _free(die->dirName);
	    al->numDirs--;
	    if ((die - al->dirs) < al->numDirs) {
/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "    die[%5d] memmove(%p,%p,0x%x)\n", (die - al->dirs), die, die+1, ((al->numDirs - (die - al->dirs)) * sizeof(*die)));
/*@=modfilesys@*/

/*@-bounds@*/
		memmove(die, die+1, (al->numDirs - (die - al->dirs)) * sizeof(*die));
/*@=bounds@*/
	    }

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "    die[%5d] memset(%p,0,0x%x)\n", al->numDirs, al->dirs + al->numDirs, sizeof(*die));
/*@=modfilesys@*/
	    memset(al->dirs + al->numDirs, 0, sizeof(*al->dirs)); /* overkill */
	}

	if (origNumDirs > al->numDirs) {
	    if (al->numDirs > 0)
		al->dirs = xrealloc(al->dirs, al->numDirs * sizeof(*al->dirs));
	    else
		al->dirs = _free(al->dirs);
	}
    }

    alp->provides = rpmdsFree(alp->provides);
    alp->fi = rpmfiFree(alp->fi);

/*@-boundswrite@*/
    memset(alp, 0, sizeof(*alp));	/* XXX trash and burn */
/*@=boundswrite@*/
    return;
}

/*@-bounds@*/
alKey rpmalAdd(rpmal * alistp, alKey pkgKey, fnpyKey key,
		rpmds provides, rpmfi fi, uint_32 tscolor)
{
    alNum pkgNum;
    rpmal al;
    availablePackage alp;

    /* If list doesn't exist yet, create. */
    if (*alistp == NULL)
	*alistp = rpmalCreate(5);
    al = *alistp;
    pkgNum = alKey2Num(al, pkgKey);

    if (pkgNum >= 0 && pkgNum < al->size) {
	rpmalDel(al, pkgKey);
    } else {
	if (al->size == al->alloced) {
	    al->alloced += al->delta;
	    al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
	}
	pkgNum = al->size++;
    }

    if (al->list == NULL)
	return RPMAL_NOMATCH;		/* XXX can't happen */

    alp = al->list + pkgNum;

    alp->key = key;
    alp->tscolor = tscolor;

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "*** add %p[%d] 0x%x\n", al->list, pkgNum, tscolor);
/*@=modfilesys@*/

    alp->provides = rpmdsLink(provides, "Provides (rpmalAdd)");
    alp->fi = rpmfiLink(fi, "Files (rpmalAdd)");

    fi = rpmfiLink(alp->fi, "Files index (rpmalAdd)");
    fi = rpmfiInit(fi, 0);
    if (rpmfiFC(fi) > 0) {
	dirInfo dieNeedle =
		memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
	dirInfo die;
	int dc = rpmfiDC(fi);
	int dx;
	int * dirMapping = alloca(sizeof(*dirMapping) * dc);
	int * dirUnique = alloca(sizeof(*dirUnique) * dc);
	const char * DN;
	int origNumDirs;
	int first;
	int i;

	/* XXX FIXME: We ought to relocate the directory list here */

	/* XXX enough space for all directories, late realloc to truncate. */
	al->dirs = xrealloc(al->dirs, (al->numDirs + dc) * sizeof(*al->dirs));

	/* Only previously allocated dirInfo is sorted and bsearch'able. */
	origNumDirs = al->numDirs;

	/* Package dirnames are not currently unique. Create unique mapping. */
	for (dx = 0; dx < dc; dx++) {
	    (void) rpmfiSetDX(fi, dx);
	    DN = rpmfiDN(fi);
	    if (DN != NULL)
	    for (i = 0; i < dx; i++) {
		const char * iDN;
		(void) rpmfiSetDX(fi, i);
		iDN = rpmfiDN(fi);
		if (iDN != NULL && !strcmp(DN, iDN))
		    /*@innerbreak@*/ break;
	    }
	    dirUnique[dx] = i;
	}

	/* Map package dirs into transaction dirInfo index. */
	for (dx = 0; dx < dc; dx++) {

	    /* Non-unique package dirs use the 1st entry mapping. */
	    if (dirUnique[dx] < dx) {
		dirMapping[dx] = dirMapping[dirUnique[dx]];
		continue;
	    }

	    /* Find global dirInfo mapping for first encounter. */
	    (void) rpmfiSetDX(fi, dx);

	    /*@-assignexpose -dependenttrans -observertrans@*/
	    {   DN = rpmfiDN(fi);

#if defined(__ia64__)
/* XXX Make sure that autorelocated file dependencies are satisfied. */
#define	DNPREFIX	"/emul/ia32-linux"
		if (!strncmp(DN, DNPREFIX, sizeof(DNPREFIX)-1))
		    DN += sizeof(DNPREFIX)-1;
#endif
		dieNeedle->dirName = DN;
	    }
	    /*@=assignexpose =dependenttrans =observertrans@*/

	    dieNeedle->dirNameLen = (dieNeedle->dirName != NULL
			? strlen(dieNeedle->dirName) : 0);
	    die = bsearch(dieNeedle, al->dirs, origNumDirs,
			       sizeof(*dieNeedle), dieCompare);
	    if (die) {
		dirMapping[dx] = die - al->dirs;
	    } else {
		dirMapping[dx] = al->numDirs;
		die = al->dirs + al->numDirs;
		if (dieNeedle->dirName != NULL)
		    die->dirName = xstrdup(dieNeedle->dirName);
		die->dirNameLen = dieNeedle->dirNameLen;
		die->files = NULL;
		die->numFiles = 0;
/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "+++ die[%5d] %p [%3d] %s\n", al->numDirs, die, die->dirNameLen, die->dirName);
/*@=modfilesys@*/

		al->numDirs++;
	    }
	}

	for (first = rpmfiNext(fi); first >= 0;) {
	    fileIndexEntry fie;
	    int next;

	    /* Find the first file of the next directory. */
	    dx = rpmfiDX(fi);
	    while ((next = rpmfiNext(fi)) >= 0) {
		if (dx != rpmfiDX(fi))
		    /*@innerbreak@*/ break;
	    }
	    if (next < 0) next = rpmfiFC(fi);	/* XXX reset end-of-list */

	    die = al->dirs + dirMapping[dx];
	    die->files = xrealloc(die->files,
			(die->numFiles + next - first) * sizeof(*die->files));

	    fie = die->files + die->numFiles;

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "    die[%5d] %p->files [%p[%d],%p) -> [%p[%d],%p)\n", dirMapping[dx], die,
die->files, die->numFiles, die->files+die->numFiles,
fie, (next - first), fie + (next - first));
/*@=modfilesys@*/

	    /* Rewind to first file, generate file index entry for each file. */
	    fi = rpmfiInit(fi, first);
	    while ((first = rpmfiNext(fi)) >= 0 && first < next) {
		/*@-assignexpose -dependenttrans -observertrans @*/
		fie->baseName = rpmfiBN(fi);
		/*@=assignexpose =dependenttrans =observertrans @*/
		fie->baseNameLen = (fie->baseName ? strlen(fie->baseName) : 0);
		fie->pkgNum = pkgNum;
		fie->ficolor = rpmfiFColor(fi);
/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "\t%p[%3d] %p:%p[%2d] %s\n", die->files, die->numFiles, fie, fie->baseName, fie->baseNameLen, rpmfiFN(fi));
/*@=modfilesys@*/

		die->numFiles++;
		fie++;
	    }
	    qsort(die->files, die->numFiles, sizeof(*die->files), fieCompare);
	}

	/* Resize the directory list. If any directories were added, resort. */
	al->dirs = xrealloc(al->dirs, al->numDirs * sizeof(*al->dirs));
	if (origNumDirs != al->numDirs)
	    qsort(al->dirs, al->numDirs, sizeof(*al->dirs), dieCompare);
    }
    fi = rpmfiUnlink(fi, "Files index (rpmalAdd)");

    rpmalFreeIndex(al);

assert(((alNum)(alp - al->list)) == pkgNum);
    return ((alKey)(alp - al->list));
}
/*@=bounds@*/

/**
 * Compare two available index entries by name (qsort/bsearch).
 * @param one		1st available index entry
 * @param two		2nd available index entry
 * @return		result of comparison
 */
static int indexcmp(const void * one, const void * two)
	/*@*/
{
    /*@-castexpose@*/
    const availableIndexEntry a = (const availableIndexEntry) one;
    const availableIndexEntry b = (const availableIndexEntry) two;
    /*@=castexpose@*/
    int lenchk;

    lenchk = a->entryLen - b->entryLen;
    if (lenchk)
	return lenchk;

    return strcmp(a->entry, b->entry);
}

void rpmalAddProvides(rpmal al, alKey pkgKey, rpmds provides, uint_32 tscolor)
{
    uint_32 dscolor;
    const char * Name;
    alNum pkgNum = alKey2Num(al, pkgKey);
    availableIndex ai = &al->index;
    availableIndexEntry aie;
    int ix;

    if (provides == NULL || pkgNum < 0 || pkgNum >= al->size)
	return;
    if (ai->index == NULL || ai->k < 0 || ai->k >= ai->size)
	return;

    if (rpmdsInit(provides) != NULL)
    while (rpmdsNext(provides) >= 0) {

	if ((Name = rpmdsN(provides)) == NULL)
	    continue;	/* XXX can't happen */

	/* Ignore colored provides not in our rainbow. */
	dscolor = rpmdsColor(provides);
	if (tscolor && dscolor && !(tscolor & dscolor))
	    continue;

	aie = ai->index + ai->k;
	ai->k++;

	aie->pkgKey = pkgKey;
	aie->entry = Name;
	aie->entryLen = strlen(Name);
	ix = rpmdsIx(provides);

/* XXX make sure that element index fits in unsigned short */
assert(ix < 0x10000);

	aie->entryIx = ix;
	aie->type = IET_PROVIDES;
    }
}

void rpmalMakeIndex(rpmal al)
{
    availableIndex ai;
    availablePackage alp;
    int i;

    if (al == NULL || al->list == NULL) return;
    ai = &al->index;

    ai->size = 0;
    for (i = 0; i < al->size; i++) {
	alp = al->list + i;
	if (alp->provides != NULL)
	    ai->size += rpmdsCount(alp->provides);
    }
    if (ai->size == 0) return;

    ai->index = xrealloc(ai->index, ai->size * sizeof(*ai->index));
    ai->k = 0;
    for (i = 0; i < al->size; i++) {
	alp = al->list + i;
	rpmalAddProvides(al, (alKey)i, alp->provides, alp->tscolor);
    }

    /* Reset size to the no. of provides added. */
    ai->size = ai->k;
    qsort(ai->index, ai->size, sizeof(*ai->index), indexcmp);
}

fnpyKey *
rpmalAllFileSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
{
    uint_32 tscolor;
    uint_32 ficolor;
    int found = 0;
    const char * dirName;
    const char * baseName;
    dirInfo dieNeedle =
		memset(alloca(sizeof(*dieNeedle)), 0, sizeof(*dieNeedle));
    dirInfo die;
    fileIndexEntry fieNeedle =
		memset(alloca(sizeof(*fieNeedle)), 0, sizeof(*fieNeedle));
    fileIndexEntry fie;
    availablePackage alp;
    fnpyKey * ret = NULL;
    const char * fileName;

    if (keyp) *keyp = RPMAL_NOMATCH;

    if (al == NULL || (fileName = rpmdsN(ds)) == NULL || *fileName != '/')
	return NULL;

    /* Solaris 2.6 bsearch sucks down on this. */
    if (al->numDirs == 0 || al->dirs == NULL || al->list == NULL)
	return NULL;

    {	char * t;
	dirName = t = xstrdup(fileName);
	if ((t = strrchr(t, '/')) != NULL) {
	    t++;		/* leave the trailing '/' */
	    *t = '\0';
	}
    }

    dieNeedle->dirName = (char *) dirName;
    dieNeedle->dirNameLen = strlen(dirName);
    die = bsearch(dieNeedle, al->dirs, al->numDirs,
		       sizeof(*dieNeedle), dieCompare);
    if (die == NULL)
	goto exit;

    /* rewind to the first match */
    while (die > al->dirs && dieCompare(die-1, dieNeedle) == 0)
	die--;

    if ((baseName = strrchr(fileName, '/')) == NULL)
	goto exit;
    baseName++;

    /*@-branchstate@*/ /* FIX: ret is a problem */
    for (found = 0, ret = NULL;
	 die <= al->dirs + al->numDirs && dieCompare(die, dieNeedle) == 0;
	 die++)
    {

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "==> die %p %s\n", die, (die->dirName ? die->dirName : "(nil)"));
/*@=modfilesys@*/

/*@-observertrans@*/
	fieNeedle->baseName = baseName;
/*@=observertrans@*/
	fieNeedle->baseNameLen = strlen(fieNeedle->baseName);
	fie = bsearch(fieNeedle, die->files, die->numFiles,
		       sizeof(*fieNeedle), fieCompare);
	if (fie == NULL)
	    continue;	/* XXX shouldn't happen */

/*@-modfilesys@*/
if (_rpmal_debug)
fprintf(stderr, "==> fie %p %s\n", fie, (fie->baseName ? fie->baseName : "(nil)"));
/*@=modfilesys@*/

	alp = al->list + fie->pkgNum;

        /* Ignore colored files not in our rainbow. */
	tscolor = alp->tscolor;
	ficolor = fie->ficolor;
        if (tscolor && ficolor && !(tscolor & ficolor))
            continue;

	rpmdsNotify(ds, _("(added files)"), 0);

	ret = xrealloc(ret, (found+2) * sizeof(*ret));
	if (ret)	/* can't happen */
	    ret[found] = alp->key;
	if (keyp)
	    *keyp = alNum2Key(al, fie->pkgNum);
	found++;
    }
    /*@=branchstate@*/

exit:
    dirName = _free(dirName);
    if (ret)
	ret[found] = NULL;
    return ret;
}

fnpyKey *
rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
{
    availableIndex ai;
    availableIndexEntry needle;
    availableIndexEntry match;
    fnpyKey * ret = NULL;
    int found = 0;
    const char * KName;
    availablePackage alp;
    int rc;

    if (keyp) *keyp = RPMAL_NOMATCH;

    if (al == NULL || ds == NULL || (KName = rpmdsN(ds)) == NULL)
	return ret;

    if (*KName == '/') {
	/* First, look for files "contained" in package ... */
	ret = rpmalAllFileSatisfiesDepend(al, ds, keyp);
	if (ret != NULL && *ret != NULL)
	    return ret;
	/* ... then, look for files "provided" by package. */
    }

    ai = &al->index;
    if (ai->index == NULL || ai->size <= 0)
	return NULL;

    needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
    /*@-assignexpose -temptrans@*/
    needle->entry = KName;
    /*@=assignexpose =temptrans@*/
    needle->entryLen = strlen(needle->entry);

    match = bsearch(needle, ai->index, ai->size, sizeof(*ai->index), indexcmp);
    if (match == NULL)
	return NULL;

    /* rewind to the first match */
    while (match > ai->index && indexcmp(match-1, needle) == 0)
	match--;

    if (al->list != NULL)	/* XXX always true */
    for (ret = NULL, found = 0;
	 match < ai->index + ai->size && indexcmp(match, needle) == 0;
	 match++)
    {
	alp = al->list + alKey2Num(al, match->pkgKey);

	rc = 0;
	if (alp->provides != NULL)	/* XXX can't happen */
	switch (match->type) {
	case IET_PROVIDES:
	    /* XXX single step on rpmdsNext to regenerate DNEVR string */
	    (void) rpmdsSetIx(alp->provides, match->entryIx - 1);
	    if (rpmdsNext(alp->provides) >= 0)
		rc = rpmdsCompare(alp->provides, ds);

	    if (rc)
		rpmdsNotify(ds, _("(added provide)"), 0);

	    /*@switchbreak@*/ break;
	}

	/*@-branchstate@*/
	if (rc) {
	    ret = xrealloc(ret, (found + 2) * sizeof(*ret));
	    if (ret)	/* can't happen */
		ret[found] = alp->key;
/*@-dependenttrans@*/
	    if (keyp)
		*keyp = match->pkgKey;
/*@=dependenttrans@*/
	    found++;
	}
	/*@=branchstate@*/
    }

    if (ret)
	ret[found] = NULL;

/*@-nullstate@*/ /* FIX: *keyp may be NULL */
    return ret;
/*@=nullstate@*/
}

fnpyKey
rpmalSatisfiesDepend(const rpmal al, const rpmds ds, alKey * keyp)
{
    fnpyKey * tmp = rpmalAllSatisfiesDepend(al, ds, keyp);

    if (tmp) {
	fnpyKey ret = tmp[0];
	free(tmp);
	return ret;
    }
    return NULL;
}
