<commit_before>#include "system.h"

#include <sys/file.h>
#include <signal.h>
#include <sys/signal.h>

#include <rpmlib.h>
#include <rpmurl.h>
#include <rpmmacro.h>	/* XXX for rpmGetPath */

#include "dbindex.h"
/*@access dbiIndexSet@*/
/*@access dbiIndexRecord@*/
/*@access rpmdbMatchIterator@*/

#include "falloc.h"
#include "fprint.h"
#include "misc.h"
#include "rpmdb.h"

extern int _noDirTokens;
extern int _useDbiMajor;

#define	_DBI_FLAGS	0
#define	_DBI_PERMS	0644
#define	_DBI_MAJOR	-1

struct _dbiIndex rpmdbi[] = {
    { "packages.rpm", 0,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "nameindex.rpm", RPMTAG_NAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "fileindex.rpm", RPMTAG_BASENAMES,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "groupindex.rpm", RPMTAG_GROUP,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "requiredby.rpm", RPMTAG_REQUIRENAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "providesindex.rpm", RPMTAG_PROVIDENAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "conflictsindex.rpm", RPMTAG_CONFLICTNAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "triggerindex.rpm", RPMTAG_TRIGGERNAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { NULL }
#define	RPMDBI_MIN		0
#define	RPMDBI_MAX		8
};

/* XXX the signal handling in here is not thread safe */

/* the requiredbyIndex isn't stricly necessary. In a perfect world, we could
   have each header keep a list of packages that need it. However, we
   can't reserve space in the header for extra information so all of the
   required packages would move in the database every time a package was
   added or removed. Instead, each package (or virtual package) name
   keeps a list of package offsets of packages that might depend on this
   one. Version numbers still need verification, but it gets us in the
   right area w/o a linear search through the database. */

struct rpmdb_s {
    dbiIndex _dbi[RPMDBI_MAX];
#ifdef	DYING
#define	nameIndex		_dbi[RPMDBI_NAME]
#define	fileIndex		_dbi[RPMDBI_FILE]
#define	groupIndex		_dbi[RPMDBI_GROUP]
#define	requiredbyIndex		_dbi[RPMDBI_REQUIREDBY]
#define	providesIndex		_dbi[RPMDBI_PROVIDES]
#define	conflictsIndex		_dbi[RPMDBI_CONFLICTS]
#define	triggerIndex		_dbi[RPMDBI_TRIGGER]
#endif
};

static sigset_t signalMask;

static void blockSignals(void)
{
    sigset_t newMask;

    sigfillset(&newMask);		/* block all signals */
    sigprocmask(SIG_BLOCK, &newMask, &signalMask);
}

static void unblockSignals(void)
{
    sigprocmask(SIG_SETMASK, &signalMask, NULL);
}

static int openDbFile(const char * prefix, const char * dbpath,
	const dbiIndex dbi, int justCheck, int mode, dbiIndex * dbip)
{
    char * filename, * fn;
    int len;

    if (dbi == NULL || dbip == NULL)
	return 1;
    *dbip = NULL;

    len = (prefix ? strlen(prefix) : 0) +
		strlen(dbpath) + strlen(dbi->dbi_basename) + 1;
    fn = filename = alloca(len);
    *fn = '\0';
    switch (urlIsURL(dbpath)) {
    case URL_IS_UNKNOWN:
	if (prefix && *prefix &&
	    !(prefix[0] == '/' && prefix[1] == '\0' && dbpath[0] == '/'))
		fn = stpcpy(fn, prefix);
	break;
    default:
	break;
    }
    fn = stpcpy(fn, dbpath);
    if (fn > filename && !(fn[-1] == '/' || dbi->dbi_basename[0] == '/'))
	fn = stpcpy(fn, "/");
    fn = stpcpy(fn, dbi->dbi_basename);

    if (!justCheck || !rpmfileexists(filename)) {
	if ((*dbip = dbiOpenIndex(filename, mode, dbi)) == NULL)
	    return 1;
    }

    return 0;
}

static /*@only@*/ rpmdb newRpmdb(void)
{
    rpmdb db = xcalloc(sizeof(*db), 1);
    return db;
}

int openDatabase(const char * prefix, const char * dbpath, rpmdb *dbp,
		int mode, int perms, int flags)
{
    rpmdb db;
    int rc;
    int justcheck = flags & RPMDB_FLAG_JUSTCHECK;
    int minimal = flags & RPMDB_FLAG_MINIMAL;

    if (dbp)
	*dbp = NULL;
    if (mode & O_WRONLY) 
	return 1;

    if (!(perms & 0600))	/* XXX sanity */
	perms = 0644;

    /* we should accept NULL as a valid prefix */
    if (!prefix) prefix="";

    db = newRpmdb();

    {	int dbix;

	rc = 0;
	for (dbix = RPMDBI_MIN; rc == 0 && dbix < RPMDBI_MAX; dbix++) {
	    dbiIndex dbiTemplate;

	    dbiTemplate = rpmdbi + dbix;

	    rc = openDbFile(prefix, dbpath, dbiTemplate, justcheck, mode,
			&db->_dbi[dbix]);
	    if (rc)
		continue;

	    switch (dbix) {
	    case 1:
		if (minimal)
		    goto exit;
		break;
	    case 2:
	    {	void * keyp = NULL;

    /* We used to store the fileindexes as complete paths, rather then
       plain basenames. Let's see which version we are... */
    /*
     * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
     * XXX db1/db2 linkage) conditions.
     */
		if (!justcheck && !dbiGetNextKey(db->_dbi[RPMDBI_FILE], &keyp, NULL)) {
		    const char * akey;
		    akey = keyp;
		    if (strchr(akey, '/')) {
			rpmError(RPMERR_OLDDB, _("old format database is present; "
				"use --rebuilddb to generate a new format database"));
			rc |= 1;
		    }
		    dbiFreeCursor(db->_dbi[RPMDBI_FILE]);
		}
	    }	break;
	    default:
		break;
	    }
	}
    }

exit:
    if (!(rc || justcheck || dbp == NULL))
	*dbp = db;
    else
	rpmdbClose(db);

    return rc;
}

static int doRpmdbOpen (const char * prefix, /*@out@*/ rpmdb * dbp,
			int mode, int perms, int flags)
{
    const char * dbpath = rpmGetPath("%{_dbpath}", NULL);
    int rc;

    if (!(dbpath && dbpath[0] != '%')) {
	rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
	rc = 1;
    } else
    	rc = openDatabase(prefix, dbpath, dbp, mode, perms, flags);
    xfree(dbpath);
    return rc;
}

/* XXX called from python/upgrade.c */
int rpmdbOpenForTraversal(const char * prefix, rpmdb * dbp)
{
    return doRpmdbOpen(prefix, dbp, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL);
}

/* XXX called from python/rpmmodule.c */
int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
{
    return doRpmdbOpen(prefix, dbp, mode, perms, 0);
}

int rpmdbInit (const char * prefix, int perms)
{
    rpmdb db;
    return doRpmdbOpen(prefix, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK);
}

void rpmdbClose (rpmdb db)
{
    int dbix;

    for (dbix = RPMDBI_MAX; --dbix >= RPMDBI_MIN; ) {
	if (db->_dbi[dbix] == NULL)
	    continue;
    	dbiCloseIndex(db->_dbi[dbix]);
    	db->_dbi[dbix] = NULL;
    }
    free(db);
}

#ifdef	DYING
int rpmdbFirstRecNum(rpmdb db) {
    dbiIndex dbi = db->_dbi[RPMDBI_PACKAGES];
    unsigned int offset = 0;
    void * keyp = &offset;
    size_t keylen = sizeof(offset);
    int rc;

    /* XXX skip over instance 0 */
    do {
	rc = (*dbi->dbi_vec->cget) (dbi, &keyp, &keylen, NULL, NULL);
	if (rc)
	    return 0;
	memcpy(&offset, keyp, sizeof(offset));
    } while (offset == 0);
    return offset;
}

int rpmdbNextRecNum(rpmdb db, unsigned int lastOffset) {
    /* 0 at end */
    dbiIndex dbi = db->_dbi[RPMDBI_PACKAGES];
    void * keyp = &lastOffset;
    size_t keylen = sizeof(lastOffset);
    int rc;

    rc = (*dbi->dbi_vec->cget) (dbi, &keyp, &keylen, NULL, NULL);
    if (rc)
	return 0;
    memcpy(&lastOffset, keyp, sizeof(lastOffset));
    return lastOffset;
}
#endif	/* DYING */

Header rpmdbGetRecord(rpmdb db, unsigned int offset)
{
    dbiIndex dbi = db->_dbi[RPMDBI_PACKAGES];
    void * uh;
    size_t uhlen;
    void * keyp = &offset;
    size_t keylen = sizeof(offset);
    int rc;

    rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
    if (rc)
	return NULL;
    return headerLoad(uh);
}

int rpmdbFindByFile(rpmdb db, const char * filespec, dbiIndexSet * matches)
{
    const char * dirName;
    const char * baseName;
    fingerPrintCache fpc;
    fingerPrint fp1;
    dbiIndexSet allMatches = NULL;
    dbiIndexRecord rec = NULL;
    int i;
    int rc;

    *matches = NULL;
    if ((baseName = strrchr(filespec, '/')) != NULL) {
    	char * t;
	size_t len;

    	len = baseName - filespec + 1;
	t = strncpy(alloca(len + 1), filespec, len);
	t[len] = '\0';
	dirName = t;
	baseName++;
    } else {
	dirName = "";
	baseName = filespec;
    }

    fpc = fpCacheCreate(20);
    fp1 = fpLookup(fpc, dirName, baseName, 1);

    rc = dbiSearchIndex(db->_dbi[RPMDBI_FILE], baseName, 0, &allMatches);
    if (rc) {
	dbiFreeIndexSet(allMatches);
	allMatches = NULL;
	fpCacheFree(fpc);
	return rc;
    }

    *matches = dbiCreateIndexSet();
    rec = dbiReturnIndexRecordInstance(0, 0);
    i = 0;
    while (i < allMatches->count) {
	const char ** baseNames, ** dirNames;
	int_32 * dirIndexes;
	unsigned int offset = dbiIndexRecordOffset(allMatches, i);
	unsigned int prevoff;
	Header h;

	if ((h = rpmdbGetRecord(db, offset)) == NULL) {
	    i++;
	    continue;
	}

	headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
				(void **) &baseNames, NULL);
	headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
				(void **) &dirIndexes, NULL);
	headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
				(void **) &dirNames, NULL);

	do {
	    fingerPrint fp2;
	    int num = dbiIndexRecordFileNumber(allMatches, i);

	    fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
	    if (FP_EQUAL(fp1, fp2)) {
		rec->recOffset = dbiIndexRecordOffset(allMatches, i);
		rec->fileNumber = dbiIndexRecordFileNumber(allMatches, i);
		dbiAppendIndexRecord(*matches, rec);
	    }

	    prevoff = offset;
	    i++;
	    offset = dbiIndexRecordOffset(allMatches, i);
	} while (i < allMatches->count && 
		(i == 0 || offset == prevoff));

	free(baseNames);
	free(dirNames);
	headerFree(h);
    }

    if (rec) {
	dbiFreeIndexRecordInstance(rec);
	rec = NULL;
    }
    if (allMatches) {
	dbiFreeIndexSet(allMatches);
	allMatches = NULL;
    }

    fpCacheFree(fpc);

    if ((*matches)->count == 0) {
	dbiFreeIndexSet(*matches);
	*matches = NULL; 
	return 1;
    }

    return 0;
}

int rpmdbFindByProvides(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_PROVIDES], filespec, 0, matches);
}

int rpmdbFindByRequiredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_REQUIREDBY], filespec, 0, matches);
}

int rpmdbFindByConflicts(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_CONFLICTS], filespec, 0, matches);
}

int rpmdbFindByTriggeredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_TRIGGER], filespec, 0, matches);
}

int rpmdbFindByGroup(rpmdb db, const char * group, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_GROUP], group, 0, matches);
}

int rpmdbFindPackage(rpmdb db, const char * name, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, matches);
}

int rpmdbCountPackages(rpmdb db, const char * name)
{
    dbiIndexSet matches = NULL;
    int rc;

    rc = dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, &matches);

    switch (rc) {
    default:
    case -1:		/* error */
	rpmError(RPMERR_DBCORRUPT, _("cannot retrieve package \"%s\" from db"),
                name);
	rc = -1;
	break;
    case 1:		/* not found */
	rc = 0;
	break;
    case 0:		/* success */
	rc = dbiIndexSetCount(matches);
	break;
    }

    if (matches)
	dbiFreeIndexSet(matches);

    return rc;
}

struct _rpmdbMatchIterator {
    const void *	mi_key;
    size_t		mi_keylen;
    rpmdb		mi_db;
    dbiIndex		mi_dbi;
    int			mi_dbix;
    dbiIndexSet		mi_set;
    int			mi_setx;
    Header		mi_h;
    unsigned int	mi_offset;
};

void rpmdbFreeIterator(rpmdbMatchIterator mi)
{
    if (mi->mi_h) {
	headerFree(mi->mi_h);
	mi->mi_h = NULL;
    }
    if (mi->mi_set) {
	dbiFreeIndexSet(mi->mi_set);
	mi->mi_set = NULL;
    } else {
	dbiIndex dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
	(void) (*dbi->dbi_vec->cclose) (dbi);
    }
    if (mi->mi_key) {
	xfree(mi->mi_key);
	mi->mi_key = NULL;
    }
    free(mi);
}

unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
    return mi->mi_offset;
}

Header rpmdbNextIterator(rpmdbMatchIterator mi)
{
    dbiIndex dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
    void * uh;
    size_t uhlen;
    void * keyp = &mi->mi_offset;
    size_t keylen = sizeof(mi->mi_offset);
    int rc;

    if (mi == NULL)
	return NULL;

    /* XXX skip over instances with 0 join key */
    do {
	if (mi->mi_set) {
	    if (!(mi->mi_setx < mi->mi_set->count))
		return NULL;
	    mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
	    mi->mi_setx++;
	} else {
	    rc = (*dbi->dbi_vec->cget) (dbi, &keyp, &keylen, NULL, NULL);
	    if (rc)
		return NULL;
	    memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
	}
    } while (mi->mi_offset == 0);

    /* Retrieve header */
    rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
    if (rc)
	return NULL;

    if (mi->mi_h) {
	headerFree(mi->mi_h);
	mi->mi_h = NULL;
    }
    mi->mi_h = headerLoad(uh);

    return mi->mi_h;
}

rpmdbMatchIterator rpmdbInitIterator(rpmdb db, int dbix, const void * key, size_t keylen)
{
    rpmdbMatchIterator mi;
    dbiIndex dbi = NULL;
    dbiIndexSet set = NULL;

    dbi = db->_dbi[dbix];

    if (key) {
	int rc;
	rc = dbiSearchIndex(dbi, key, keylen, &set);
	switch (rc) {
	default:
	case -1:		/* error */
	case 1:		/* not found */
	    if (set)
		dbiFreeIndexSet(set);
	    return NULL;
	    /*@notreached@*/ break;
	case 0:		/* success */
	    break;
	}
    }

    mi = xcalloc(sizeof(*mi), 1);
    if (key) {
	if (keylen == 0)
	    keylen = strlen(key);

	{   char * k = xmalloc(keylen + 1);
	    memcpy(k, key, keylen);
	    k[keylen] = '\0';	/* XXX for strings */
	    mi->mi_key = k;
	}

	mi->mi_keylen = keylen;
    } else {
	mi->mi_key = NULL;
	mi->mi_keylen = 0;
    }
    mi->mi_db = db;
    mi->mi_dbi = dbi;
    mi->mi_dbix = dbix;
    mi->mi_set = set;
    mi->mi_setx = 0;
    mi->mi_h = NULL;
    mi->mi_offset = 0;
    return mi;
}

static void removeIndexEntry(dbiIndex dbi, const char * key, dbiIndexRecord rec,
		             int tolerant, const char * idxName)
{
    dbiIndexSet matches = NULL;
    int rc;
    
    rc = dbiSearchIndex(dbi, key, 0, &matches);
    switch (rc) {
      case 0:
	if (dbiRemoveIndexRecord(matches, rec)) {
	    if (!tolerant)
		rpmError(RPMERR_DBCORRUPT, _("package not found with key \"%s\" in %s"),
				key, idxName);
	} else {<commit_msg>Make sure that match iterators can be called with mi = NULL. Remove DYING code.<commit_after>#include "system.h"

#include <sys/file.h>
#include <signal.h>
#include <sys/signal.h>

#include <rpmlib.h>
#include <rpmurl.h>
#include <rpmmacro.h>	/* XXX for rpmGetPath */

#include "dbindex.h"
/*@access dbiIndexSet@*/
/*@access dbiIndexRecord@*/
/*@access rpmdbMatchIterator@*/

#include "falloc.h"
#include "fprint.h"
#include "misc.h"
#include "rpmdb.h"

extern int _noDirTokens;
extern int _useDbiMajor;

#define	_DBI_FLAGS	0
#define	_DBI_PERMS	0644
#define	_DBI_MAJOR	-1

struct _dbiIndex rpmdbi[] = {
    { "packages.rpm", 0,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "nameindex.rpm", RPMTAG_NAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "fileindex.rpm", RPMTAG_BASENAMES,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "groupindex.rpm", RPMTAG_GROUP,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "requiredby.rpm", RPMTAG_REQUIRENAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "providesindex.rpm", RPMTAG_PROVIDENAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "conflictsindex.rpm", RPMTAG_CONFLICTNAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { "triggerindex.rpm", RPMTAG_TRIGGERNAME,
	DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
    { NULL }
#define	RPMDBI_MIN		0
#define	RPMDBI_MAX		8
};

/* XXX the signal handling in here is not thread safe */

/* the requiredbyIndex isn't stricly necessary. In a perfect world, we could
   have each header keep a list of packages that need it. However, we
   can't reserve space in the header for extra information so all of the
   required packages would move in the database every time a package was
   added or removed. Instead, each package (or virtual package) name
   keeps a list of package offsets of packages that might depend on this
   one. Version numbers still need verification, but it gets us in the
   right area w/o a linear search through the database. */

struct rpmdb_s {
    dbiIndex _dbi[RPMDBI_MAX];
#ifdef	DYING
#define	nameIndex		_dbi[RPMDBI_NAME]
#define	fileIndex		_dbi[RPMDBI_FILE]
#define	groupIndex		_dbi[RPMDBI_GROUP]
#define	requiredbyIndex		_dbi[RPMDBI_REQUIREDBY]
#define	providesIndex		_dbi[RPMDBI_PROVIDES]
#define	conflictsIndex		_dbi[RPMDBI_CONFLICTS]
#define	triggerIndex		_dbi[RPMDBI_TRIGGER]
#endif
};

static sigset_t signalMask;

static void blockSignals(void)
{
    sigset_t newMask;

    sigfillset(&newMask);		/* block all signals */
    sigprocmask(SIG_BLOCK, &newMask, &signalMask);
}

static void unblockSignals(void)
{
    sigprocmask(SIG_SETMASK, &signalMask, NULL);
}

static int openDbFile(const char * prefix, const char * dbpath,
	const dbiIndex dbi, int justCheck, int mode, dbiIndex * dbip)
{
    char * filename, * fn;
    int len;

    if (dbi == NULL || dbip == NULL)
	return 1;
    *dbip = NULL;

    len = (prefix ? strlen(prefix) : 0) +
		strlen(dbpath) + strlen(dbi->dbi_basename) + 1;
    fn = filename = alloca(len);
    *fn = '\0';
    switch (urlIsURL(dbpath)) {
    case URL_IS_UNKNOWN:
	if (prefix && *prefix &&
	    !(prefix[0] == '/' && prefix[1] == '\0' && dbpath[0] == '/'))
		fn = stpcpy(fn, prefix);
	break;
    default:
	break;
    }
    fn = stpcpy(fn, dbpath);
    if (fn > filename && !(fn[-1] == '/' || dbi->dbi_basename[0] == '/'))
	fn = stpcpy(fn, "/");
    fn = stpcpy(fn, dbi->dbi_basename);

    if (!justCheck || !rpmfileexists(filename)) {
	if ((*dbip = dbiOpenIndex(filename, mode, dbi)) == NULL)
	    return 1;
    }

    return 0;
}

static /*@only@*/ rpmdb newRpmdb(void)
{
    rpmdb db = xcalloc(sizeof(*db), 1);
    return db;
}

int openDatabase(const char * prefix, const char * dbpath, rpmdb *dbp,
		int mode, int perms, int flags)
{
    rpmdb db;
    int rc;
    int justcheck = flags & RPMDB_FLAG_JUSTCHECK;
    int minimal = flags & RPMDB_FLAG_MINIMAL;

    if (dbp)
	*dbp = NULL;
    if (mode & O_WRONLY) 
	return 1;

    if (!(perms & 0600))	/* XXX sanity */
	perms = 0644;

    /* we should accept NULL as a valid prefix */
    if (!prefix) prefix="";

    db = newRpmdb();

    {	int dbix;

	rc = 0;
	for (dbix = RPMDBI_MIN; rc == 0 && dbix < RPMDBI_MAX; dbix++) {
	    dbiIndex dbiTemplate;

	    dbiTemplate = rpmdbi + dbix;

	    rc = openDbFile(prefix, dbpath, dbiTemplate, justcheck, mode,
			&db->_dbi[dbix]);
	    if (rc)
		continue;

	    switch (dbix) {
	    case 1:
		if (minimal)
		    goto exit;
		break;
	    case 2:
	    {	void * keyp = NULL;

    /* We used to store the fileindexes as complete paths, rather then
       plain basenames. Let's see which version we are... */
    /*
     * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
     * XXX db1/db2 linkage) conditions.
     */
		if (!justcheck && !dbiGetNextKey(db->_dbi[RPMDBI_FILE], &keyp, NULL)) {
		    const char * akey;
		    akey = keyp;
		    if (strchr(akey, '/')) {
			rpmError(RPMERR_OLDDB, _("old format database is present; "
				"use --rebuilddb to generate a new format database"));
			rc |= 1;
		    }
		    dbiFreeCursor(db->_dbi[RPMDBI_FILE]);
		}
	    }	break;
	    default:
		break;
	    }
	}
    }

exit:
    if (!(rc || justcheck || dbp == NULL))
	*dbp = db;
    else
	rpmdbClose(db);

    return rc;
}

static int doRpmdbOpen (const char * prefix, /*@out@*/ rpmdb * dbp,
			int mode, int perms, int flags)
{
    const char * dbpath = rpmGetPath("%{_dbpath}", NULL);
    int rc;

    if (!(dbpath && dbpath[0] != '%')) {
	rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
	rc = 1;
    } else
    	rc = openDatabase(prefix, dbpath, dbp, mode, perms, flags);
    xfree(dbpath);
    return rc;
}

/* XXX called from python/upgrade.c */
int rpmdbOpenForTraversal(const char * prefix, rpmdb * dbp)
{
    return doRpmdbOpen(prefix, dbp, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL);
}

/* XXX called from python/rpmmodule.c */
int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
{
    return doRpmdbOpen(prefix, dbp, mode, perms, 0);
}

int rpmdbInit (const char * prefix, int perms)
{
    rpmdb db;
    return doRpmdbOpen(prefix, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK);
}

void rpmdbClose (rpmdb db)
{
    int dbix;

    for (dbix = RPMDBI_MAX; --dbix >= RPMDBI_MIN; ) {
	if (db->_dbi[dbix] == NULL)
	    continue;
    	dbiCloseIndex(db->_dbi[dbix]);
    	db->_dbi[dbix] = NULL;
    }
    free(db);
}

Header rpmdbGetRecord(rpmdb db, unsigned int offset)
{
    dbiIndex dbi = db->_dbi[RPMDBI_PACKAGES];
    void * uh;
    size_t uhlen;
    void * keyp = &offset;
    size_t keylen = sizeof(offset);
    int rc;

    rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
    if (rc)
	return NULL;
    return headerLoad(uh);
}

int rpmdbFindByFile(rpmdb db, const char * filespec, dbiIndexSet * matches)
{
    const char * dirName;
    const char * baseName;
    fingerPrintCache fpc;
    fingerPrint fp1;
    dbiIndexSet allMatches = NULL;
    dbiIndexRecord rec = NULL;
    int i;
    int rc;

    *matches = NULL;
    if ((baseName = strrchr(filespec, '/')) != NULL) {
    	char * t;
	size_t len;

    	len = baseName - filespec + 1;
	t = strncpy(alloca(len + 1), filespec, len);
	t[len] = '\0';
	dirName = t;
	baseName++;
    } else {
	dirName = "";
	baseName = filespec;
    }

    fpc = fpCacheCreate(20);
    fp1 = fpLookup(fpc, dirName, baseName, 1);

    rc = dbiSearchIndex(db->_dbi[RPMDBI_FILE], baseName, 0, &allMatches);
    if (rc) {
	dbiFreeIndexSet(allMatches);
	allMatches = NULL;
	fpCacheFree(fpc);
	return rc;
    }

    *matches = dbiCreateIndexSet();
    rec = dbiReturnIndexRecordInstance(0, 0);
    i = 0;
    while (i < allMatches->count) {
	const char ** baseNames, ** dirNames;
	int_32 * dirIndexes;
	unsigned int offset = dbiIndexRecordOffset(allMatches, i);
	unsigned int prevoff;
	Header h;

	if ((h = rpmdbGetRecord(db, offset)) == NULL) {
	    i++;
	    continue;
	}

	headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
				(void **) &baseNames, NULL);
	headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
				(void **) &dirIndexes, NULL);
	headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
				(void **) &dirNames, NULL);

	do {
	    fingerPrint fp2;
	    int num = dbiIndexRecordFileNumber(allMatches, i);

	    fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
	    if (FP_EQUAL(fp1, fp2)) {
		rec->recOffset = dbiIndexRecordOffset(allMatches, i);
		rec->fileNumber = dbiIndexRecordFileNumber(allMatches, i);
		dbiAppendIndexRecord(*matches, rec);
	    }

	    prevoff = offset;
	    i++;
	    offset = dbiIndexRecordOffset(allMatches, i);
	} while (i < allMatches->count && 
		(i == 0 || offset == prevoff));

	free(baseNames);
	free(dirNames);
	headerFree(h);
    }

    if (rec) {
	dbiFreeIndexRecordInstance(rec);
	rec = NULL;
    }
    if (allMatches) {
	dbiFreeIndexSet(allMatches);
	allMatches = NULL;
    }

    fpCacheFree(fpc);

    if ((*matches)->count == 0) {
	dbiFreeIndexSet(*matches);
	*matches = NULL; 
	return 1;
    }

    return 0;
}

int rpmdbFindByProvides(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_PROVIDES], filespec, 0, matches);
}

int rpmdbFindByRequiredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_REQUIREDBY], filespec, 0, matches);
}

int rpmdbFindByConflicts(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_CONFLICTS], filespec, 0, matches);
}

#ifdef	DYING
int rpmdbFindByTriggeredBy(rpmdb db, const char * filespec, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_TRIGGER], filespec, 0, matches);
}

int rpmdbFindByGroup(rpmdb db, const char * group, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_GROUP], group, 0, matches);
}
#endif

int rpmdbFindPackage(rpmdb db, const char * name, dbiIndexSet * matches) {
    return dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, matches);
}

int rpmdbCountPackages(rpmdb db, const char * name)
{
    dbiIndexSet matches = NULL;
    int rc;

    rc = dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, &matches);

    switch (rc) {
    default:
    case -1:		/* error */
	rpmError(RPMERR_DBCORRUPT, _("cannot retrieve package \"%s\" from db"),
                name);
	rc = -1;
	break;
    case 1:		/* not found */
	rc = 0;
	break;
    case 0:		/* success */
	rc = dbiIndexSetCount(matches);
	break;
    }

    if (matches)
	dbiFreeIndexSet(matches);

    return rc;
}

struct _rpmdbMatchIterator {
    const void *	mi_key;
    size_t		mi_keylen;
    rpmdb		mi_db;
    dbiIndex		mi_dbi;
    int			mi_dbix;
    dbiIndexSet		mi_set;
    int			mi_setx;
    Header		mi_h;
    unsigned int	mi_offset;
};

void rpmdbFreeIterator(rpmdbMatchIterator mi)
{
    if (mi == NULL)
	return;

    if (mi->mi_h) {
	headerFree(mi->mi_h);
	mi->mi_h = NULL;
    }
    if (mi->mi_set) {
	dbiFreeIndexSet(mi->mi_set);
	mi->mi_set = NULL;
    } else {
	dbiIndex dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
	(void) (*dbi->dbi_vec->cclose) (dbi);
    }
    if (mi->mi_key) {
	xfree(mi->mi_key);
	mi->mi_key = NULL;
    }
    free(mi);
}

unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
    if (mi == NULL)
	return 0;
    return mi->mi_offset;
}

int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
    if (!(mi && mi->mi_set))
	return 0;	/* XXX W2DO? */
    return mi->mi_set->count;
}

Header rpmdbNextIterator(rpmdbMatchIterator mi)
{
    dbiIndex dbi;
    void * uh;
    size_t uhlen;
    void * keyp;
    size_t keylen;
    int rc;

    if (mi == NULL)
	return NULL;

    dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
    keyp = &mi->mi_offset;
    keylen = sizeof(mi->mi_offset);

    /* XXX skip over instances with 0 join key */
    do {
	if (mi->mi_set) {
	    if (!(mi->mi_setx < mi->mi_set->count))
		return NULL;
	    mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
	    mi->mi_setx++;
	} else {
	    rc = (*dbi->dbi_vec->cget) (dbi, &keyp, &keylen, NULL, NULL);
	    if (rc)
		return NULL;
	    memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
	}
    } while (mi->mi_offset == 0);

    /* Retrieve header */
    rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
    if (rc)
	return NULL;

    if (mi->mi_h) {
	headerFree(mi->mi_h);
	mi->mi_h = NULL;
    }
    mi->mi_h = headerLoad(uh);

    return mi->mi_h;
}

rpmdbMatchIterator rpmdbInitIterator(rpmdb db, int dbix, const void * key, size_t keylen)
{
    rpmdbMatchIterator mi;
    dbiIndex dbi = NULL;
    dbiIndexSet set = NULL;

    dbi = db->_dbi[dbix];

    if (key) {
	int rc;
	rc = dbiSearchIndex(dbi, key, keylen, &set);
	switch (rc) {
	default:
	case -1:		/* error */
	case 1:		/* not found */
	    if (set)
		dbiFreeIndexSet(set);
	    return NULL;
	    /*@notreached@*/ break;
	case 0:		/* success */
	    break;
	}
    }

    mi = xcalloc(sizeof(*mi), 1);
    if (key) {
	if (keylen == 0)
	    keylen = strlen(key);

	{   char * k = xmalloc(keylen + 1);
	    memcpy(k, key, keylen);
	    k[keylen] = '\0';	/* XXX for strings */
	    mi->mi_key = k;
	}

	mi->mi_keylen = keylen;
    } else {
	mi->mi_key = NULL;
	mi->mi_keylen = 0;
    }
    mi->mi_db = db;
    mi->mi_dbi = dbi;
    assert(dbi->dbi_dbcursor == NULL);
    mi->mi_dbix = dbix;
    mi->mi_set = set;
    mi->mi_setx = 0;
    mi->mi_h = NULL;
    mi->mi_offset = 0;
    return mi;
}

static void removeIndexEntry(dbiIndex dbi, const char * key, dbiIndexRecord rec,
		             int tolerant, const char * idxName)
{
    dbiIndexSet matches = NULL;
    int rc;
    
    rc = dbiSearchIndex(dbi, key, 0, &matches);
    switch (rc) {
      case 0:
	if (dbiRemoveIndexRecord(matches, rec)) {
	    if (!tolerant)
		rpmError(RPMERR_DBCORRUPT, _("package not found with key \"%s\" in %s"),
				key, idxName);
	} else {
{'fraction_non_alphanumeric': 0.1393419865343236, 'fraction_numerical': 0.007392239883390018, 'mean_word_length': 3.2065693430656936, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 181, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2164218', 'n_tokens_mistral': 12075, 'n_tokens_neox': 11313, 'n_words': 2817}