Blame lib/rpmal.c

2ff057
/** \ingroup rpmdep
2ff057
 * \file lib/rpmal.c
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
2ff057
#include <rpm/rpmte.h>
2ff057
#include <rpm/rpmfi.h>
2ff057
#include <rpm/rpmstrpool.h>
2ff057
2ff057
#include "lib/rpmal.h"
2ff057
#include "lib/misc.h"
2ff057
#include "lib/rpmte_internal.h"
2ff057
#include "lib/rpmds_internal.h"
2ff057
#include "lib/rpmfi_internal.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
typedef struct availablePackage_s * availablePackage;
2ff057
typedef int rpmalNum;
2ff057
2ff057
/** \ingroup rpmdep
2ff057
 * Info about a single package to be installed.
2ff057
 */
2ff057
struct availablePackage_s {
2ff057
    rpmte p;                    /*!< transaction member */
2ff057
    rpmds provides;		/*!< Provides: dependencies. */
2ff057
    rpmds obsoletes;		/*!< Obsoletes: dependencies. */
2ff057
    rpmfiles fi;		/*!< File info set. */
2ff057
};
2ff057
2ff057
/** \ingroup rpmdep
2ff057
 * A single available item (e.g. a Provides: dependency).
2ff057
 */
2ff057
typedef struct availableIndexEntry_s {
2ff057
    rpmalNum pkgNum;	        /*!< Containing package index. */
2ff057
    unsigned int entryIx;	/*!< Dependency index. */
2ff057
} * availableIndexEntry;
2ff057
2ff057
#undef HASHTYPE
2ff057
#undef HTKEYTYPE
2ff057
#undef HTDATATYPE
2ff057
#define HASHTYPE rpmalDepHash
2ff057
#define HTKEYTYPE rpmsid
2ff057
#define HTDATATYPE struct availableIndexEntry_s
2ff057
#include "lib/rpmhash.H"
2ff057
#include "lib/rpmhash.C"
2ff057
2ff057
typedef struct availableIndexFileEntry_s {
2ff057
    rpmsid dirName;
2ff057
    rpmalNum pkgNum;	        /*!< Containing package index. */
2ff057
    unsigned int entryIx;	/*!< Dependency index. */
2ff057
} * availableIndexFileEntry;
2ff057
2ff057
#undef HASHTYPE
2ff057
#undef HTKEYTYPE
2ff057
#undef HTDATATYPE
2ff057
#define HASHTYPE rpmalFileHash
2ff057
#define HTKEYTYPE rpmsid
2ff057
#define HTDATATYPE struct availableIndexFileEntry_s
2ff057
#include "lib/rpmhash.H"
2ff057
#include "lib/rpmhash.C"
2ff057
2ff057
/** \ingroup rpmdep
2ff057
 * Set of available packages, items, and directories.
2ff057
 */
2ff057
struct rpmal_s {
2ff057
    rpmstrPool pool;		/*!< String pool */
2ff057
    availablePackage list;	/*!< Set of packages. */
2ff057
    rpmalDepHash providesHash;
2ff057
    rpmalDepHash obsoletesHash;
2ff057
    rpmalFileHash fileHash;
2ff057
    int delta;			/*!< Delta for pkg list reallocation. */
2ff057
    int size;			/*!< No. of pkgs in list. */
2ff057
    int alloced;		/*!< No. of pkgs allocated for list. */
2ff057
    rpmtransFlags tsflags;	/*!< Transaction control flags. */
2ff057
    rpm_color_t tscolor;	/*!< Transaction color. */
2ff057
    rpm_color_t prefcolor;	/*!< Transaction preferred color. */
2ff057
    fingerPrintCache fpc;
2ff057
};
2ff057
2ff057
/**
2ff057
 * Destroy available item index.
2ff057
 * @param al		available list
2ff057
 */
2ff057
static void rpmalFreeIndex(rpmal al)
2ff057
{
2ff057
    al->providesHash = rpmalDepHashFree(al->providesHash);
2ff057
    al->obsoletesHash = rpmalDepHashFree(al->obsoletesHash);
2ff057
    al->fileHash = rpmalFileHashFree(al->fileHash);
2ff057
    al->fpc = fpCacheFree(al->fpc);
2ff057
}
2ff057
2ff057
rpmal rpmalCreate(rpmstrPool pool, int delta, rpmtransFlags tsflags,
2ff057
		  rpm_color_t tscolor, rpm_color_t prefcolor)
2ff057
{
2ff057
    rpmal al = xcalloc(1, sizeof(*al));
2ff057
2ff057
    /* transition time safe-guard */
2ff057
    assert(pool != NULL);
2ff057
2ff057
    al->pool = rpmstrPoolLink(pool);
2ff057
    al->delta = delta;
2ff057
    al->size = 0;
2ff057
    al->alloced = al->delta;
2ff057
    al->list = xmalloc(sizeof(*al->list) * al->alloced);;
2ff057
2ff057
    al->providesHash = NULL;
2ff057
    al->obsoletesHash = NULL;
2ff057
    al->fileHash = NULL;
2ff057
    al->tsflags = tsflags;
2ff057
    al->tscolor = tscolor;
2ff057
    al->prefcolor = prefcolor;
2ff057
2ff057
    return al;
2ff057
}
2ff057
2ff057
rpmal rpmalFree(rpmal al)
2ff057
{
2ff057
    availablePackage alp;
2ff057
    int i;
2ff057
2ff057
    if (al == NULL)
2ff057
	return NULL;
2ff057
2ff057
    if ((alp = al->list) != NULL)
2ff057
    for (i = 0; i < al->size; i++, alp++) {
2ff057
	alp->obsoletes = rpmdsFree(alp->obsoletes);
2ff057
	alp->provides = rpmdsFree(alp->provides);
2ff057
	alp->fi = rpmfilesFree(alp->fi);
2ff057
    }
2ff057
    al->pool = rpmstrPoolFree(al->pool);
2ff057
    al->list = _free(al->list);
2ff057
    al->alloced = 0;
2ff057
2ff057
    rpmalFreeIndex(al);
2ff057
    al = _free(al);
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
static unsigned int sidHash(rpmsid sid)
2ff057
{
2ff057
    return sid;
2ff057
}
2ff057
2ff057
static int sidCmp(rpmsid a, rpmsid b)
2ff057
{
2ff057
    return (a != b);
2ff057
}
2ff057
2ff057
void rpmalDel(rpmal al, rpmte p)
2ff057
{
2ff057
    availablePackage alp;
2ff057
    rpmalNum pkgNum;
2ff057
2ff057
    if (al == NULL || al->list == NULL)
2ff057
	return;		/* XXX can't happen */
2ff057
2ff057
    // XXX use a search for self provide
2ff057
    for (pkgNum=0; pkgNum<al->size; pkgNum++) {
2ff057
	if (al->list[pkgNum].p == p) {
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
    if (pkgNum == al->size ) return; // Not found!
2ff057
2ff057
    alp = al->list + pkgNum;
2ff057
    // do not actually delete, just set p to NULL
2ff057
    // and later filter that out of the results
2ff057
    alp->p = NULL;
2ff057
}
2ff057
2ff057
static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfiles fi)
2ff057
{
2ff057
    struct availableIndexFileEntry_s fileEntry;
2ff057
    int fc = rpmfilesFC(fi);
2ff057
    rpm_color_t ficolor;
2ff057
    int skipdoc = (al->tsflags & RPMTRANS_FLAG_NODOCS);
2ff057
    int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS);
2ff057
2ff057
    fileEntry.pkgNum = pkgNum;
2ff057
2ff057
    for (int i = 0; i < fc; i++) {
2ff057
	/* Ignore colored provides not in our rainbow. */
2ff057
        ficolor = rpmfilesFColor(fi, i);
2ff057
        if (al->tscolor && ficolor && !(al->tscolor & ficolor))
2ff057
            continue;
2ff057
2ff057
	/* Ignore files that wont be installed */
2ff057
	if (skipdoc && (rpmfilesFFlags(fi, i) & RPMFILE_DOC))
2ff057
	    continue;
2ff057
	if (skipconf && (rpmfilesFFlags(fi, i) & RPMFILE_CONFIG))
2ff057
	    continue;
2ff057
2ff057
	fileEntry.dirName = rpmfilesDNId(fi, rpmfilesDI(fi, i));
2ff057
	fileEntry.entryIx = i;
2ff057
2ff057
	rpmalFileHashAddEntry(al->fileHash, rpmfilesBNId(fi, i), fileEntry);
2ff057
    }
2ff057
}
2ff057
2ff057
static void rpmalAddProvides(rpmal al, rpmalNum pkgNum, rpmds provides)
2ff057
{
2ff057
    struct availableIndexEntry_s indexEntry;
2ff057
    rpm_color_t dscolor;
2ff057
    int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS);
2ff057
    int dc = rpmdsCount(provides);
2ff057
2ff057
    indexEntry.pkgNum = pkgNum;
2ff057
2ff057
    for (int i = 0; i < dc; i++) {
2ff057
        /* Ignore colored provides not in our rainbow. */
2ff057
        dscolor = rpmdsColorIndex(provides, i);
2ff057
        if (al->tscolor && dscolor && !(al->tscolor & dscolor))
2ff057
            continue;
2ff057
2ff057
	/* Ignore config() provides if the files wont be installed */
2ff057
	if (skipconf & (rpmdsFlagsIndex(provides, i) & RPMSENSE_CONFIG))
2ff057
	    continue;
2ff057
2ff057
	indexEntry.entryIx = i;;
2ff057
	rpmalDepHashAddEntry(al->providesHash,
2ff057
				  rpmdsNIdIndex(provides, i), indexEntry);
2ff057
    }
2ff057
}
2ff057
2ff057
static void rpmalAddObsoletes(rpmal al, rpmalNum pkgNum, rpmds obsoletes)
2ff057
{
2ff057
    struct availableIndexEntry_s indexEntry;
2ff057
    rpm_color_t dscolor;
2ff057
    int dc = rpmdsCount(obsoletes);
2ff057
2ff057
    indexEntry.pkgNum = pkgNum;
2ff057
2ff057
    for (int i = 0; i < dc; i++) {
2ff057
	/* Obsoletes shouldn't be colored but just in case... */
2ff057
        dscolor = rpmdsColorIndex(obsoletes, i);
2ff057
        if (al->tscolor && dscolor && !(al->tscolor & dscolor))
2ff057
            continue;
2ff057
2ff057
	indexEntry.entryIx = i;;
2ff057
	rpmalDepHashAddEntry(al->obsoletesHash,
2ff057
				  rpmdsNIdIndex(obsoletes, i), indexEntry);
2ff057
    }
2ff057
}
2ff057
2ff057
void rpmalAdd(rpmal al, rpmte p)
2ff057
{
2ff057
    rpmalNum pkgNum;
2ff057
    availablePackage alp;
2ff057
2ff057
    if (al->size == al->alloced) {
2ff057
	al->alloced += al->delta;
2ff057
	al->list = xrealloc(al->list, sizeof(*al->list) * al->alloced);
2ff057
    }
2ff057
    pkgNum = al->size++;
2ff057
2ff057
    alp = al->list + pkgNum;
2ff057
2ff057
    alp->p = p;
2ff057
2ff057
    alp->provides = rpmdsLink(rpmteDS(p, RPMTAG_PROVIDENAME));
2ff057
    alp->obsoletes = rpmdsLink(rpmteDS(p, RPMTAG_OBSOLETENAME));
2ff057
    alp->fi = rpmteFiles(p);
2ff057
2ff057
    /*
2ff057
     * Transition-time safe-guard to catch private-pool uses.
2ff057
     * File sets with no files have NULL pool, that's fine. But WTF is up
2ff057
     * with the provides: every single package should have at least its
2ff057
     * own name as a provide, and thus never NULL, and normal use matches
2ff057
     * this expectation. However the test-suite is tripping up on NULL
2ff057
     * NULL pool from NULL alp->provides in numerous cases?
2ff057
     */
2ff057
    {
2ff057
	rpmstrPool fipool = rpmfilesPool(alp->fi);
2ff057
	rpmstrPool dspool = rpmdsPool(alp->provides);
2ff057
	
2ff057
	assert(fipool == NULL || fipool == al->pool);
2ff057
	assert(dspool == NULL || dspool == al->pool);
2ff057
    }
2ff057
2ff057
    /* Try to be lazy as delayed hash creation is cheaper */
2ff057
    if (al->providesHash != NULL)
2ff057
	rpmalAddProvides(al, pkgNum, alp->provides);
2ff057
    if (al->obsoletesHash != NULL)
2ff057
	rpmalAddObsoletes(al, pkgNum, alp->obsoletes);
2ff057
    if (al->fileHash != NULL)
2ff057
	rpmalAddFiles(al, pkgNum, alp->fi);
2ff057
2ff057
    assert(((rpmalNum)(alp - al->list)) == pkgNum);
2ff057
}
2ff057
2ff057
static void rpmalMakeFileIndex(rpmal al)
2ff057
{
2ff057
    availablePackage alp;
2ff057
    int i, fileCnt = 0;
2ff057
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	if (alp->fi != NULL)
2ff057
	    fileCnt += rpmfilesFC(alp->fi);
2ff057
    }
2ff057
    al->fileHash = rpmalFileHashCreate(fileCnt/4+128,
2ff057
				       sidHash, sidCmp, NULL, NULL);
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	rpmalAddFiles(al, i, alp->fi);
2ff057
    }
2ff057
}
2ff057
2ff057
static void rpmalMakeProvidesIndex(rpmal al)
2ff057
{
2ff057
    availablePackage alp;
2ff057
    int i, providesCnt = 0;
2ff057
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	providesCnt += rpmdsCount(alp->provides);
2ff057
    }
2ff057
2ff057
    al->providesHash = rpmalDepHashCreate(providesCnt/4+128,
2ff057
					       sidHash, sidCmp, NULL, NULL);
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	rpmalAddProvides(al, i, alp->provides);
2ff057
    }
2ff057
}
2ff057
2ff057
static void rpmalMakeObsoletesIndex(rpmal al)
2ff057
{
2ff057
    availablePackage alp;
2ff057
    int i, obsoletesCnt = 0;
2ff057
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	obsoletesCnt += rpmdsCount(alp->obsoletes);
2ff057
    }
2ff057
2ff057
    al->obsoletesHash = rpmalDepHashCreate(obsoletesCnt/4+128,
2ff057
					       sidHash, sidCmp, NULL, NULL);
2ff057
    for (i = 0; i < al->size; i++) {
2ff057
	alp = al->list + i;
2ff057
	rpmalAddObsoletes(al, i, alp->obsoletes);
2ff057
    }
2ff057
}
2ff057
2ff057
rpmte * rpmalAllObsoletes(rpmal al, rpmds ds)
2ff057
{
2ff057
    rpmte * ret = NULL;
2ff057
    rpmsid nameId;
2ff057
    availableIndexEntry result;
2ff057
    int resultCnt;
2ff057
2ff057
    if (al == NULL || ds == NULL || (nameId = rpmdsNId(ds)) == 0)
2ff057
	return ret;
2ff057
2ff057
    if (al->obsoletesHash == NULL)
2ff057
	rpmalMakeObsoletesIndex(al);
2ff057
2ff057
    rpmalDepHashGetEntry(al->obsoletesHash, nameId, &result, &resultCnt, NULL);
2ff057
2ff057
    if (resultCnt > 0) {
2ff057
	availablePackage alp;
2ff057
	int rc, found = 0;
2ff057
2ff057
	ret = xmalloc((resultCnt+1) * sizeof(*ret));
2ff057
2ff057
	for (int i = 0; i < resultCnt; i++) {
2ff057
	    alp = al->list + result[i].pkgNum;
2ff057
	    if (alp->p == NULL) // deleted
2ff057
		continue;
2ff057
2ff057
	    rc = rpmdsCompareIndex(alp->obsoletes, result[i].entryIx,
2ff057
				   ds, rpmdsIx(ds));
2ff057
2ff057
	    if (rc) {
2ff057
		rpmdsNotify(ds, "(added obsolete)", 0);
2ff057
		ret[found] = alp->p;
2ff057
		found++;
2ff057
	    }
2ff057
	}
2ff057
2ff057
	if (found)
2ff057
	    ret[found] = NULL;
2ff057
	else
2ff057
	    ret = _free(ret);
2ff057
    }
2ff057
2ff057
    return ret;
2ff057
}
2ff057
2ff057
static rpmte * rpmalAllFileSatisfiesDepend(const rpmal al, const char *fileName, const rpmds filterds)
2ff057
{
2ff057
    const char *slash; 
2ff057
    rpmte * ret = NULL;
2ff057
2ff057
    if (al == NULL || fileName == NULL || *fileName != '/')
2ff057
	return NULL;
2ff057
2ff057
    /* Split path into dirname and basename components for lookup */
2ff057
    if ((slash = strrchr(fileName, '/')) != NULL) {
2ff057
	availableIndexFileEntry result;
2ff057
	int resultCnt = 0;
2ff057
	size_t bnStart = (slash - fileName) + 1;
2ff057
	rpmsid baseName;
2ff057
2ff057
	if (al->fileHash == NULL)
2ff057
	    rpmalMakeFileIndex(al);
2ff057
2ff057
	baseName = rpmstrPoolId(al->pool, fileName + bnStart, 0);
2ff057
	if (!baseName)
2ff057
	    return NULL;	/* no match possible */
2ff057
2ff057
	rpmalFileHashGetEntry(al->fileHash, baseName, &result, &resultCnt, NULL);
2ff057
2ff057
	if (resultCnt > 0) {
2ff057
	    int i, found;
2ff057
	    ret = xmalloc((resultCnt+1) * sizeof(*ret));
2ff057
	    fingerPrint * fp = NULL;
2ff057
	    rpmsid dirName = rpmstrPoolIdn(al->pool, fileName, bnStart, 1);
2ff057
2ff057
	    if (!al->fpc)
2ff057
		al->fpc = fpCacheCreate(1001, NULL);
2ff057
	    fpLookup(al->fpc, rpmstrPoolStr(al->pool, dirName), fileName + bnStart, &fp);
2ff057
2ff057
	    for (found = i = 0; i < resultCnt; i++) {
2ff057
		availablePackage alp = al->list + result[i].pkgNum;
2ff057
		if (alp->p == NULL) /* deleted */
2ff057
		    continue;
2ff057
		/* ignore self-conflicts/obsoletes */
2ff057
		if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds)
2ff057
		    continue;
2ff057
		if (result[i].dirName != dirName &&
2ff057
		    !fpLookupEquals(al->fpc, fp, rpmstrPoolStr(al->pool, result[i].dirName), fileName + bnStart))
2ff057
		    continue;
2ff057
2ff057
		ret[found] = alp->p;
2ff057
		found++;
2ff057
	    }
2ff057
	    _free(fp);
2ff057
	    ret[found] = NULL;
2ff057
	}
2ff057
    }
2ff057
2ff057
    return ret;
2ff057
}
2ff057
2ff057
rpmte * rpmalAllSatisfiesDepend(const rpmal al, const rpmds ds)
2ff057
{
2ff057
    rpmte * ret = NULL;
2ff057
    int i, ix, found;
2ff057
    rpmsid nameId;
2ff057
    const char *name;
2ff057
    availableIndexEntry result;
2ff057
    int resultCnt;
2ff057
    int obsolete;
2ff057
    rpmTagVal dtag;
2ff057
    rpmds filterds = NULL;
2ff057
2ff057
    availablePackage alp;
2ff057
    int rc;
2ff057
2ff057
    if (al == NULL || ds == NULL || (nameId = rpmdsNId(ds)) == 0)
2ff057
	return ret;
2ff057
2ff057
    dtag = rpmdsTagN(ds);
2ff057
    obsolete = (dtag == RPMTAG_OBSOLETENAME);
2ff057
    if (dtag == RPMTAG_OBSOLETENAME || dtag == RPMTAG_CONFLICTNAME)
2ff057
	filterds = ds;
2ff057
    name = rpmstrPoolStr(al->pool, nameId);
2ff057
    if (!obsolete && *name == '/') {
2ff057
	/* First, look for files "contained" in package ... */
2ff057
	ret = rpmalAllFileSatisfiesDepend(al, name, filterds);
2ff057
	if (ret != NULL && *ret != NULL) {
2ff057
	    rpmdsNotify(ds, "(added files)", 0);
2ff057
	    return ret;
2ff057
	}
2ff057
	/* ... then, look for files "provided" by package. */
2ff057
	ret = _free(ret);
2ff057
    }
2ff057
2ff057
    if (al->providesHash == NULL)
2ff057
	rpmalMakeProvidesIndex(al);
2ff057
2ff057
    rpmalDepHashGetEntry(al->providesHash, nameId, &result,
2ff057
			      &resultCnt, NULL);
2ff057
2ff057
    if (resultCnt==0) return NULL;
2ff057
2ff057
    ret = xmalloc((resultCnt+1) * sizeof(*ret));
2ff057
2ff057
    for (found=i=0; i
2ff057
	alp = al->list + result[i].pkgNum;
2ff057
	if (alp->p == NULL) /* deleted */
2ff057
	    continue;
2ff057
	/* ignore self-conflicts/obsoletes */
2ff057
	if (filterds && rpmteDS(alp->p, rpmdsTagN(filterds)) == filterds)
2ff057
	    continue;
2ff057
	ix = result[i].entryIx;
2ff057
2ff057
	if (obsolete) {
2ff057
	    /* Obsoletes are on package NEVR only */
2ff057
	    rpmds thisds;
2ff057
	    if (!rstreq(rpmdsNIndex(alp->provides, ix), rpmteN(alp->p)))
2ff057
		continue;
2ff057
	    thisds = rpmteDS(alp->p, RPMTAG_NAME);
2ff057
	    rc = rpmdsCompareIndex(thisds, rpmdsIx(thisds), ds, rpmdsIx(ds));
2ff057
	} else {
2ff057
	    rc = rpmdsCompareIndex(alp->provides, ix, ds, rpmdsIx(ds));
2ff057
	}
2ff057
2ff057
	if (rc)
2ff057
	    ret[found++] = alp->p;
2ff057
    }
2ff057
2ff057
    if (found) {
2ff057
	rpmdsNotify(ds, "(added provide)", 0);
2ff057
	ret[found] = NULL;
2ff057
    } else {
2ff057
	ret = _free(ret);
2ff057
    }
2ff057
2ff057
    return ret;
2ff057
}
2ff057
2ff057
rpmte
2ff057
rpmalSatisfiesDepend(const rpmal al, const rpmte te, const rpmds ds)
2ff057
{
2ff057
    rpmte *providers = rpmalAllSatisfiesDepend(al, ds);
2ff057
    rpmte best = NULL;
2ff057
    int bestscore = 0;
2ff057
2ff057
    if (providers) {
2ff057
	rpm_color_t dscolor = rpmdsColor(ds);
2ff057
	for (rpmte *p = providers; *p; p++) {
2ff057
	    int score = 0;
2ff057
2ff057
	    /*
2ff057
	     * For colored dependencies, prefer a matching colored provider.
2ff057
	     * Otherwise prefer provider of ts preferred color.
2ff057
	     */
2ff057
	    if (al->tscolor) {
2ff057
		rpm_color_t tecolor = rpmteColor(*p);
2ff057
		if (dscolor) {
2ff057
		    if (dscolor == tecolor) score += 2;
2ff057
		} else if (al->prefcolor) {
2ff057
		    if (al->prefcolor == tecolor) score += 2;
2ff057
		}
2ff057
	    }
2ff057
2ff057
	    /* Being self-provided is a bonus */
2ff057
	    if (*p == te)
2ff057
		score += 1;
2ff057
2ff057
	    if (score > bestscore) {
2ff057
		bestscore = score;
2ff057
		best = *p;
2ff057
	    }
2ff057
	}
2ff057
	/* if not decided by now, just pick first match */
2ff057
	if (!best) best = providers[0];
2ff057
	free(providers);
2ff057
    }
2ff057
    return best;
2ff057
}
2ff057
2ff057
unsigned int
2ff057
rpmalLookupTE(const rpmal al, const rpmte te)
2ff057
{
2ff057
    rpmalNum pkgNum;
2ff057
    for (pkgNum=0; pkgNum < al->size; pkgNum++)
2ff057
	if (al->list[pkgNum].p == te)
2ff057
	    break;
2ff057
    return pkgNum < al->size ? pkgNum : (unsigned int)-1;
2ff057
}
2ff057