Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

lib/rpmdb.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 static int _debug = 0;
00008 #define INLINE
00009 
00010 #include <sys/file.h>
00011 #include <signal.h>
00012 #include <sys/signal.h>
00013 
00014 #include <rpmlib.h>
00015 #include <rpmmacro.h>   /* XXX for rpmGetPath/rpmGenPath */
00016 
00017 #include "rpmdb.h"
00018 #include "fprint.h"
00019 #include "misc.h"
00020 #include "debug.h"
00021 
00022 /*@access dbiIndexSet@*/
00023 /*@access dbiIndexItem@*/
00024 /*@access Header@*/             /* XXX compared with NULL */
00025 /*@access rpmdbMatchIterator@*/
00026 
00027 extern int _noDirTokens;
00028 static int _rebuildinprogress = 0;
00029 static int _db_filter_dups = 0;
00030 
00031 int _filterDbDups = 0;  /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
00032 
00033 #define _DBI_FLAGS      0
00034 #define _DBI_PERMS      0644
00035 #define _DBI_MAJOR      -1
00036 
00037 static int dbiTagsMax = 0;
00038 /*@only@*/ static int *dbiTags = NULL;
00039 
00045 static int dbiTagToDbix(int rpmtag)
00046 {
00047     int dbix;
00048 
00049     if (!(dbiTagsMax > 0 && dbiTags))
00050         return -1;
00051     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00052         if (rpmtag == dbiTags[dbix])
00053             return dbix;
00054     }
00055     return -1;
00056 }
00057 
00061 static void dbiTagsInit(void)
00062 {
00063 /*@observer@*/ static const char * const _dbiTagStr_default =
00064         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Removetid";
00065     char * dbiTagStr = NULL;
00066     char * o, * oe;
00067     int rpmtag;
00068 
00069     dbiTagStr = rpmExpand("%{_dbi_tags}", NULL);
00070     if (!(dbiTagStr && *dbiTagStr && *dbiTagStr != '%')) {
00071         free((void *)dbiTagStr);
00072         dbiTagStr = xstrdup(_dbiTagStr_default);
00073     }
00074 
00075     if (dbiTagsMax || dbiTags) {
00076         free(dbiTags);
00077         dbiTags = NULL;
00078         dbiTagsMax = 0;
00079     }
00080 
00081     /* Always allocate package index */
00082     dbiTagsMax = 1;
00083     dbiTags = xcalloc(1, dbiTagsMax * sizeof(*dbiTags));
00084 
00085     for (o = dbiTagStr; o && *o; o = oe) {
00086         while (*o && isspace(*o))
00087             o++;
00088         if (*o == '\0')
00089             break;
00090         for (oe = o; oe && *oe; oe++) {
00091             if (isspace(*oe))
00092                 break;
00093             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
00094                 break;
00095         }
00096         if (oe && *oe)
00097             *oe++ = '\0';
00098         rpmtag = tagValue(o);
00099         if (rpmtag < 0) {
00100 
00101             fprintf(stderr, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
00102             continue;
00103         }
00104         if (dbiTagToDbix(rpmtag) >= 0)
00105             continue;
00106 
00107         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
00108         dbiTags[dbiTagsMax++] = rpmtag;
00109     }
00110 
00111     if (dbiTagStr)
00112         free(dbiTagStr);
00113 }
00114 
00115 #if USE_DB1
00116 extern struct _dbiVec db1vec;
00117 #define DB1vec          &db1vec
00118 #else
00119 #define DB1vec          NULL
00120 #endif
00121 
00122 #if USE_DB2
00123 extern struct _dbiVec db2vec;
00124 #define DB2vec          &db2vec
00125 #else
00126 #define DB2vec          NULL
00127 #endif
00128 
00129 #if USE_DB3
00130 extern struct _dbiVec db3vec;
00131 #define DB3vec          &db3vec
00132 #else
00133 #define DB3vec          NULL
00134 #endif
00135 
00136 static struct _dbiVec *mydbvecs[] = {
00137     DB1vec, DB1vec, DB2vec, DB3vec, NULL
00138 };
00139 
00140 INLINE int dbiSync(dbiIndex dbi, unsigned int flags) {
00141 if (_debug < 0 || dbi->dbi_debug)
00142 fprintf(stderr, "    Sync %s\n", tagName(dbi->dbi_rpmtag));
00143     return (*dbi->dbi_vec->sync) (dbi, flags);
00144 }
00145 
00146 INLINE int dbiByteSwapped(dbiIndex dbi) {
00147     return (*dbi->dbi_vec->byteswapped) (dbi);
00148 }
00149 
00150 INLINE int XdbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags,
00151         const char * f, unsigned int l)
00152 {
00153 if (_debug < 0 || dbi->dbi_debug)
00154 fprintf(stderr, "+++ RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
00155     return (*dbi->dbi_vec->copen) (dbi, dbcp, flags);
00156 }
00157 
00158 INLINE int XdbiCclose(dbiIndex dbi, /*@only@*/ DBC * dbcursor, unsigned int flags,
00159         const char * f, unsigned int l)
00160 {
00161 if (_debug < 0 || dbi->dbi_debug)
00162 fprintf(stderr, "--- RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
00163     return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags);
00164 }
00165 
00166 INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, unsigned int flags)
00167 {
00168     int NULkey;
00169     int rc;
00170 
00171     /* XXX make sure that keylen is correct for "" lookup */
00172     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00173     if (NULkey) keylen++;
00174     rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags);
00175     if (NULkey) keylen--;
00176 
00177 if (_debug < 0 || dbi->dbi_debug)
00178 fprintf(stderr, "    Del %s key (%p,%ld) %s rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), rc);
00179 
00180     return rc;
00181 }
00182 
00183 INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp,
00184         void ** datapp, size_t * datalenp, unsigned int flags)
00185 {
00186     int NULkey;
00187     int rc;
00188 
00189     /* XXX make sure that keylen is correct for "" lookup */
00190     NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0' && keylenp && *keylenp == 0);
00191     if (NULkey) (*keylenp)++;
00192     rc = (*dbi->dbi_vec->cget) (dbi, dbcursor, keypp, keylenp, datapp, datalenp, flags);
00193     if (NULkey) (*keylenp)--;
00194 
00195 if (_debug < 0 || dbi->dbi_debug) {
00196 char keyval[32];
00197 int dataval = 0xdeadbeef;
00198 if (dbi->dbi_rpmtag == RPMDBI_PACKAGES && keypp && *keypp && keylenp && *keylenp >= sizeof(keyval)) {
00199     int keyint;
00200     memcpy(&keyint, *keypp, sizeof(keyint));
00201     sprintf(keyval, "%d", keyint);
00202 } else keyval[0] = '\0';
00203 if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval))
00204     memcpy(&dataval, *datapp, sizeof(dataval));
00205 fprintf(stderr, "    Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n",
00206     tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp,
00207     (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)*keypp : keyval), (unsigned)dataval, rc);
00208 }
00209     return rc;
00210 }
00211 
00212 INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen,
00213         const void * datap, size_t datalen, unsigned int flags)
00214 {
00215     int NULkey;
00216     int rc;
00217 
00218     /* XXX make sure that keylen is correct for "" lookup */
00219     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00220     if (NULkey) keylen++;
00221     rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags);
00222     if (NULkey) keylen--;
00223 
00224 if (_debug < 0 || dbi->dbi_debug) {
00225 int dataval = 0xdeadbeef;
00226 if (datap) memcpy(&dataval, datap, sizeof(dataval));
00227 fprintf(stderr, "    Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, datap, (long)datalen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), (unsigned)dataval, rc);
00228 }
00229 
00230     return rc;
00231 }
00232 
00233 INLINE int dbiClose(dbiIndex dbi, unsigned int flags) {
00234 if (_debug < 0 || dbi->dbi_debug)
00235 fprintf(stderr, "    %s Close\n", tagName(dbi->dbi_rpmtag));
00236     return (*dbi->dbi_vec->close) (dbi, flags);
00237 }
00238 
00239 dbiIndex dbiOpen(rpmdb rpmdb, int rpmtag, /*@unused@*/ unsigned int flags)
00240 {
00241     int dbix;
00242     dbiIndex dbi = NULL;
00243     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
00244     int rc = 0;
00245 
00246     dbix = dbiTagToDbix(rpmtag);
00247     if (dbix < 0 || dbix >= dbiTagsMax)
00248         return NULL;
00249 
00250     /* Is this index already open ? */
00251     if ((dbi = rpmdb->_dbi[dbix]) != NULL)
00252         return dbi;
00253 
00254     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
00255     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
00256         _dbapi_rebuild = 3;
00257     _dbapi_wanted = (_rebuildinprogress ? -1 : rpmdb->db_api);
00258 
00259     switch (_dbapi_wanted) {
00260     default:
00261         _dbapi = _dbapi_wanted;
00262         if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
00263             return NULL;
00264         }
00265         errno = 0;
00266         dbi = NULL;
00267         rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
00268         if (rc) {
00269             static int _printed[32];
00270             if (!_printed[dbix & 0x1f]++)
00271                 rpmError(RPMERR_DBOPEN,
00272                         _("cannot open %s index using db%d - %s (%d)\n"),
00273                         tagName(rpmtag), _dbapi,
00274                         (rc > 0 ? strerror(rc) : ""), rc);
00275             _dbapi = -1;
00276         }
00277         break;
00278     case -1:
00279         _dbapi = 4;
00280         while (_dbapi-- > 1) {
00281             if (mydbvecs[_dbapi] == NULL)
00282                 continue;
00283             errno = 0;
00284             dbi = NULL;
00285             rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
00286             if (rc == 0 && dbi)
00287                 break;
00288         }
00289         if (_dbapi <= 0) {
00290             static int _printed[32];
00291             if (!_printed[dbix & 0x1f]++)
00292                 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
00293                         tagName(rpmtag));
00294             rc = 1;
00295             goto exit;
00296         }
00297         if (rpmdb->db_api == -1 && _dbapi > 0)
00298             rpmdb->db_api = _dbapi;
00299         break;
00300     }
00301 
00302     /* Require conversion. */
00303     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
00304         rc = (_rebuildinprogress ? 0 : 1);
00305         goto exit;
00306     }
00307 
00308     /* Suggest possible configuration */
00309     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
00310         rc = 1;
00311         goto exit;
00312     }
00313 
00314     /* Suggest possible configuration */
00315     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
00316         rc = (_rebuildinprogress ? 0 : 1);
00317         goto exit;
00318     }
00319 
00320 exit:
00321     if (rc == 0 && dbi) {
00322         rpmdb->_dbi[dbix] = dbi;
00323     } else if (dbi) {
00324         db3Free(dbi);
00325         dbi = NULL;
00326     }
00327 
00328     return dbi;
00329 }
00330 
00337 static INLINE dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum) {
00338     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
00339     rec->hdrNum = hdrNum;
00340     rec->tagNum = tagNum;
00341     return rec;
00342 }
00343 
00344 union _dbswap {
00345     unsigned int ui;
00346     unsigned char uc[4];
00347 };
00348 
00349 #define _DBSWAP(_a) \
00350   { unsigned char _b, *_c = (_a).uc; \
00351     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
00352     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
00353   }
00354 
00364 static int dbiSearch(dbiIndex dbi, DBC * dbcursor,
00365         const char * keyp, size_t keylen, dbiIndexSet * setp)
00366 {
00367     void * datap = NULL;
00368     size_t datalen = 0;
00369     int rc;
00370 
00371     if (setp) *setp = NULL;
00372     if (keylen == 0) keylen = strlen(keyp);
00373 
00374     rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen, 0);
00375 
00376     if (rc > 0) {
00377         rpmError(RPMERR_DBGETINDEX,
00378                 _("error(%d) getting \"%s\" records from %s index\n"),
00379                 rc, keyp, tagName(dbi->dbi_rpmtag));
00380     } else
00381     if (rc == 0 && setp) {
00382         int _dbbyteswapped = dbiByteSwapped(dbi);
00383         const char * sdbir = datap;
00384         dbiIndexSet set;
00385         int i;
00386 
00387         set = xmalloc(sizeof(*set));
00388 
00389         /* Convert to database internal format */
00390         switch (dbi->dbi_jlen) {
00391         default:
00392         case 2*sizeof(int_32):
00393             set->count = datalen / (2*sizeof(int_32));
00394             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00395             for (i = 0; i < set->count; i++) {
00396                 union _dbswap hdrNum, tagNum;
00397 
00398                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00399                 sdbir += sizeof(hdrNum.ui);
00400                 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
00401                 sdbir += sizeof(tagNum.ui);
00402                 if (_dbbyteswapped) {
00403                     _DBSWAP(hdrNum);
00404                     _DBSWAP(tagNum);
00405                 }
00406                 set->recs[i].hdrNum = hdrNum.ui;
00407                 set->recs[i].tagNum = tagNum.ui;
00408                 set->recs[i].fpNum = 0;
00409                 set->recs[i].dbNum = 0;
00410             }
00411             break;
00412         case 1*sizeof(int_32):
00413             set->count = datalen / (1*sizeof(int_32));
00414             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00415             for (i = 0; i < set->count; i++) {
00416                 union _dbswap hdrNum;
00417 
00418                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00419                 sdbir += sizeof(hdrNum.ui);
00420                 if (_dbbyteswapped) {
00421                     _DBSWAP(hdrNum);
00422                 }
00423                 set->recs[i].hdrNum = hdrNum.ui;
00424                 set->recs[i].tagNum = 0;
00425                 set->recs[i].fpNum = 0;
00426                 set->recs[i].dbNum = 0;
00427             }
00428             break;
00429         }
00430         *setp = set;
00431     }
00432     return rc;
00433 }
00434 
00444 /*@-compmempass@*/
00445 static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor,
00446         const void * keyp, size_t keylen, dbiIndexSet set)
00447 {
00448     void * datap;
00449     size_t datalen;
00450     int rc;
00451 
00452     if (set->count) {
00453         char * tdbir;
00454         int i;
00455         int _dbbyteswapped = dbiByteSwapped(dbi);
00456 
00457         /* Convert to database internal format */
00458 
00459         switch (dbi->dbi_jlen) {
00460         default:
00461         case 2*sizeof(int_32):
00462             datalen = set->count * (2 * sizeof(int_32));
00463             datap = tdbir = alloca(datalen);
00464             for (i = 0; i < set->count; i++) {
00465                 union _dbswap hdrNum, tagNum;
00466 
00467                 hdrNum.ui = set->recs[i].hdrNum;
00468                 tagNum.ui = set->recs[i].tagNum;
00469                 if (_dbbyteswapped) {
00470                     _DBSWAP(hdrNum);
00471                     _DBSWAP(tagNum);
00472                 }
00473                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00474                 tdbir += sizeof(hdrNum.ui);
00475                 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
00476                 tdbir += sizeof(tagNum.ui);
00477             }
00478             break;
00479         case 1*sizeof(int_32):
00480             datalen = set->count * (1 * sizeof(int_32));
00481             datap = tdbir = alloca(datalen);
00482             for (i = 0; i < set->count; i++) {
00483                 union _dbswap hdrNum;
00484 
00485                 hdrNum.ui = set->recs[i].hdrNum;
00486                 if (_dbbyteswapped) {
00487                     _DBSWAP(hdrNum);
00488                 }
00489                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00490                 tdbir += sizeof(hdrNum.ui);
00491             }
00492             break;
00493         }
00494 
00495         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
00496 
00497         if (rc) {
00498             rpmError(RPMERR_DBPUTINDEX,
00499                 _("error(%d) storing record %s into %s\n"),
00500                 rc, keyp, tagName(dbi->dbi_rpmtag));
00501         }
00502 
00503     } else {
00504 
00505         rc = dbiDel(dbi, dbcursor, keyp, keylen, 0);
00506 
00507         if (rc) {
00508             rpmError(RPMERR_DBPUTINDEX,
00509                 _("error(%d) removing record %s from %s\n"),
00510                 rc, keyp, tagName(dbi->dbi_rpmtag));
00511         }
00512 
00513     }
00514 
00515     return rc;
00516 }
00517 /*@=compmempass@*/
00518 
00519 /* XXX assumes hdrNum is first int in dbiIndexItem */
00520 static int hdrNumCmp(const void * one, const void * two) {
00521     const int * a = one, * b = two;
00522     return (*a - *b);
00523 }
00524 
00534 static INLINE int dbiAppendSet(dbiIndexSet set, const void * recs,
00535         int nrecs, size_t recsize, int sortset)
00536 {
00537     const char * rptr = recs;
00538     size_t rlen = (recsize < sizeof(*(set->recs)))
00539                 ? recsize : sizeof(*(set->recs));
00540 
00541     if (set == NULL || recs == NULL || nrecs <= 0 || recsize <= 0)
00542         return 1;
00543 
00544     set->recs = (set->count == 0)
00545         ? xmalloc(nrecs * sizeof(*(set->recs)))
00546         : xrealloc(set->recs, (set->count + nrecs) * sizeof(*(set->recs)));
00547 
00548     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
00549 
00550     while (nrecs-- > 0) {
00551         memcpy(set->recs + set->count, rptr, rlen);
00552         rptr += recsize;
00553         set->count++;
00554     }
00555 
00556     if (set->count > 1 && sortset)
00557         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
00558 
00559     return 0;
00560 }
00561 
00571 static INLINE int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
00572         size_t recsize, int sorted)
00573 {
00574     int from;
00575     int to = 0;
00576     int num = set->count;
00577     int numCopied = 0;
00578 
00579     if (nrecs > 1 && !sorted)
00580         qsort(recs, nrecs, recsize, hdrNumCmp);
00581 
00582     for (from = 0; from < num; from++) {
00583         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
00584             set->count--;
00585             continue;
00586         }
00587         if (from != to)
00588             set->recs[to] = set->recs[from]; /* structure assignment */
00589         to++;
00590         numCopied++;
00591     }
00592 
00593     return (numCopied == num);
00594 }
00595 
00596 /* XXX transaction.c */
00597 unsigned int dbiIndexSetCount(dbiIndexSet set) {
00598     return set->count;
00599 }
00600 
00601 /* XXX transaction.c */
00602 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
00603     return set->recs[recno].hdrNum;
00604 }
00605 
00606 /* XXX transaction.c */
00607 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
00608     return set->recs[recno].tagNum;
00609 }
00610 
00611 /* XXX transaction.c */
00612 void dbiFreeIndexSet(dbiIndexSet set) {
00613     if (set) {
00614         if (set->recs) free(set->recs);
00615         free(set);
00616     }
00617 }
00618 
00622 static void blockSignals(rpmdb rpmdb, /*@out@*/ sigset_t * oldMask)
00623 {
00624     sigset_t newMask;
00625 
00626     /* XXX HACK (disabled) permit ^C aborts for now ... */
00627     if (!(rpmdb && rpmdb->db_api == 4)) {
00628         sigfillset(&newMask);           /* block all signals */
00629         sigprocmask(SIG_BLOCK, &newMask, oldMask);
00630     }
00631 }
00632 
00636 static void unblockSignals(rpmdb rpmdb, sigset_t * oldMask)
00637 {
00638     /* XXX HACK (disabled) permit ^C aborts for now ... */
00639     if (!(rpmdb && rpmdb->db_api == 4)) {
00640         sigprocmask(SIG_SETMASK, oldMask, NULL);
00641     }
00642 }
00643 
00644 #define _DB_ROOT        "/"
00645 #define _DB_HOME        "%{_dbpath}"
00646 #define _DB_FLAGS       0
00647 #define _DB_MODE        0
00648 #define _DB_PERMS       0644
00649 
00650 #define _DB_MAJOR       -1
00651 #define _DB_REMOVE_ENV  0
00652 #define _DB_FILTER_DUPS 0
00653 #define _DB_ERRPFX      "rpmdb"
00654 
00655 /*@-fullinitblock@*/
00656 /*@observer@*/ static struct rpmdb_s dbTemplate = {
00657     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
00658     _DB_MAJOR,  _DB_REMOVE_ENV, _DB_FILTER_DUPS, _DB_ERRPFX
00659 };
00660 /*@=fullinitblock@*/
00661 
00662 int rpmdbOpenAll (rpmdb rpmdb)
00663 {
00664     int dbix;
00665 
00666     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00667         if (rpmdb->_dbi[dbix] != NULL)
00668             continue;
00669         (void) dbiOpen(rpmdb, dbiTags[dbix], rpmdb->db_flags);
00670     }
00671     return 0;
00672 }
00673 
00674 /* XXX query.c, rpminstall.c, verify.c */
00675 int rpmdbClose (rpmdb rpmdb)
00676 {
00677     int dbix;
00678 
00679     for (dbix = rpmdb->db_ndbi; --dbix >= 0; ) {
00680         if (rpmdb->_dbi[dbix] == NULL)
00681             continue;
00682         dbiClose(rpmdb->_dbi[dbix], 0);
00683         rpmdb->_dbi[dbix] = NULL;
00684     }
00685     if (rpmdb->db_errpfx) {
00686         free((void *)rpmdb->db_errpfx);
00687         rpmdb->db_errpfx = NULL;
00688     }
00689     if (rpmdb->db_root) {
00690         free((void *)rpmdb->db_root);
00691         rpmdb->db_root = NULL;
00692     }
00693     if (rpmdb->db_home) {
00694         free((void *)rpmdb->db_home);
00695         rpmdb->db_home = NULL;
00696     }
00697     if (rpmdb->_dbi) {
00698         free((void *)rpmdb->_dbi);
00699         rpmdb->_dbi = NULL;
00700     }
00701     free(rpmdb);
00702     return 0;
00703 }
00704 
00705 int rpmdbSync(rpmdb rpmdb)
00706 {
00707     int dbix;
00708 
00709     for (dbix = 0; dbix < rpmdb->db_ndbi; dbix++) {
00710         int xx;
00711         if (rpmdb->_dbi[dbix] == NULL)
00712             continue;
00713         xx = dbiSync(rpmdb->_dbi[dbix], 0);
00714     }
00715     return 0;
00716 }
00717 
00718 static /*@only@*/ rpmdb newRpmdb(/*@kept@*/ const char * root,
00719                 /*@kept@*/ const char * home,
00720                 int mode, int perms, int flags)
00721 {
00722     rpmdb rpmdb = xcalloc(sizeof(*rpmdb), 1);
00723     const char * epfx = _DB_ERRPFX;
00724     static int _initialized = 0;
00725 
00726     if (!_initialized) {
00727         _filterDbDups = rpmExpandNumeric("%{_filterdbdups}");
00728         _initialized = 1;
00729     }
00730 
00731     *rpmdb = dbTemplate;        /* structure assignment */
00732 
00733     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
00734 
00735     if (mode >= 0)      rpmdb->db_mode = mode;
00736     if (perms >= 0)     rpmdb->db_perms = perms;
00737     if (flags >= 0)     rpmdb->db_flags = flags;
00738 
00739     rpmdb->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL);
00740     rpmdb->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
00741     if (!(rpmdb->db_home && rpmdb->db_home[0] != '%')) {
00742         rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
00743         rpmdbClose(rpmdb);
00744         return NULL;
00745     }
00746     rpmdb->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
00747     rpmdb->db_remove_env = 0;
00748     rpmdb->db_filter_dups = _filterDbDups;
00749     rpmdb->db_ndbi = dbiTagsMax;
00750     rpmdb->_dbi = xcalloc(rpmdb->db_ndbi, sizeof(*rpmdb->_dbi));
00751     return rpmdb;
00752 }
00753 
00754 static int openDatabase(const char * prefix, const char * dbpath, int _dbapi,
00755         /*@out@*/ rpmdb *dbp, int mode, int perms, int flags)
00756 {
00757     rpmdb rpmdb;
00758     int rc;
00759     static int _initialized = 0;
00760     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
00761     int minimal = flags & RPMDB_FLAG_MINIMAL;
00762 
00763     if (!_initialized || dbiTagsMax == 0) {
00764         dbiTagsInit();
00765         _initialized++;
00766     }
00767 
00768     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
00769     if (_dbapi < -1 || _dbapi > 3)
00770         _dbapi = -1;
00771     if (_dbapi == 0)
00772         _dbapi = 1;
00773 
00774     if (dbp)
00775         *dbp = NULL;
00776     if (mode & O_WRONLY) 
00777         return 1;
00778 
00779     rpmdb = newRpmdb(prefix, dbpath, mode, perms, flags);
00780     rpmdb->db_api = _dbapi;
00781 
00782     {   int dbix;
00783 
00784         rc = 0;
00785         for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
00786             dbiIndex dbi;
00787             int rpmtag;
00788 
00789             /* Filter out temporary databases */
00790             switch ((rpmtag = dbiTags[dbix])) {
00791             case RPMDBI_AVAILABLE:
00792             case RPMDBI_ADDED:
00793             case RPMDBI_REMOVED:
00794             case RPMDBI_DEPENDS:
00795                 continue;
00796                 /*@notreached@*/ break;
00797             default:
00798                 break;
00799             }
00800 
00801             dbi = dbiOpen(rpmdb, rpmtag, 0);
00802 
00803             switch (rpmtag) {
00804             case RPMDBI_PACKAGES:
00805                 if (dbi == NULL) rc |= 1;
00806                 /* XXX open only Packages, indices created on the fly. */
00807 #if 0
00808                 if (rpmdb->db_api == 3)
00809 #endif
00810                     goto exit;
00811                 /*@notreached@*/ break;
00812             case RPMTAG_NAME:
00813                 if (dbi == NULL) rc |= 1;
00814                 if (minimal)
00815                     goto exit;
00816                 break;
00817             case RPMTAG_BASENAMES:
00818             {   void * keyp = NULL;
00819                 DBC * dbcursor;
00820                 int xx;
00821 
00822     /* We used to store the fileindexes as complete paths, rather then
00823        plain basenames. Let's see which version we are... */
00824     /*
00825      * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
00826      * XXX db1/db2 linkage) conditions.
00827      */
00828                 if (justCheck)
00829                     break;
00830                 dbcursor = NULL;
00831                 xx = dbiCopen(dbi, &dbcursor, 0);
00832                 xx = dbiGet(dbi, dbcursor, &keyp, NULL, NULL, NULL, 0);
00833                 if (xx == 0) {
00834                     const char * akey = keyp;
00835                     if (strchr(akey, '/')) {
00836                         rpmError(RPMERR_OLDDB, _("old format database is present; "
00837                                 "use --rebuilddb to generate a new format database\n"));
00838                         rc |= 1;
00839                     }
00840                 }
00841                 xx = dbiCclose(dbi, dbcursor, 0);
00842                 dbcursor = NULL;
00843             }   break;
00844             default:
00845                 break;
00846             }
00847         }
00848     }
00849 
00850 exit:
00851     if (rc || justCheck || dbp == NULL)
00852         rpmdbClose(rpmdb);
00853     else
00854         *dbp = rpmdb;
00855 
00856     return rc;
00857 }
00858 
00859 /* XXX python/rpmmodule.c */
00860 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
00861 {
00862     int _dbapi = rpmExpandNumeric("%{_dbapi}");
00863     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
00864 }
00865 
00866 int rpmdbInit (const char * prefix, int perms)
00867 {
00868     rpmdb rpmdb = NULL;
00869     int _dbapi = rpmExpandNumeric("%{_dbapi}");
00870     int rc;
00871 
00872     rc = openDatabase(prefix, NULL, _dbapi, &rpmdb, (O_CREAT | O_RDWR),
00873                 perms, RPMDB_FLAG_JUSTCHECK);
00874     if (rpmdb) {
00875         rpmdbOpenAll(rpmdb);
00876         rpmdbClose(rpmdb);
00877         rpmdb = NULL;
00878     }
00879     return rc;
00880 }
00881 
00882 static int rpmdbFindByFile(rpmdb rpmdb, const char * filespec,
00883                         /*@out@*/ dbiIndexSet * matches)
00884 {
00885     const char * dirName;
00886     const char * baseName;
00887     fingerPrintCache fpc;
00888     fingerPrint fp1;
00889     dbiIndex dbi = NULL;
00890     DBC * dbcursor;
00891     dbiIndexSet allMatches = NULL;
00892     dbiIndexItem rec = NULL;
00893     int i;
00894     int rc;
00895     int xx;
00896 
00897     *matches = NULL;
00898     if ((baseName = strrchr(filespec, '/')) != NULL) {
00899         char * t;
00900         size_t len;
00901 
00902         len = baseName - filespec + 1;
00903         t = strncpy(alloca(len + 1), filespec, len);
00904         t[len] = '\0';
00905         dirName = t;
00906         baseName++;
00907     } else {
00908         dirName = "";
00909         baseName = filespec;
00910     }
00911 
00912     fpc = fpCacheCreate(20);
00913     fp1 = fpLookup(fpc, dirName, baseName, 1);
00914 
00915     dbi = dbiOpen(rpmdb, RPMTAG_BASENAMES, 0);
00916     dbcursor = NULL;
00917     xx = dbiCopen(dbi, &dbcursor, 0);
00918     rc = dbiSearch(dbi, dbcursor, baseName, strlen(baseName), &allMatches);
00919     xx = dbiCclose(dbi, dbcursor, 0);
00920     dbcursor = NULL;
00921     if (rc) {
00922         dbiFreeIndexSet(allMatches);
00923         allMatches = NULL;
00924         fpCacheFree(fpc);
00925         return rc;
00926     }
00927 
00928     *matches = xcalloc(1, sizeof(**matches));
00929     rec = dbiIndexNewItem(0, 0);
00930     i = 0;
00931     while (i < allMatches->count) {
00932         const char ** baseNames, ** dirNames;
00933         int_32 * dirIndexes;
00934         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
00935         unsigned int prevoff;
00936         Header h;
00937 
00938         {   rpmdbMatchIterator mi;
00939             mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &offset, sizeof(offset));
00940             h = rpmdbNextIterator(mi);
00941             if (h)
00942                 h = headerLink(h);
00943             rpmdbFreeIterator(mi);
00944         }
00945 
00946         if (h == NULL) {
00947             i++;
00948             continue;
00949         }
00950 
00951         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
00952                                 (const void **) &baseNames, NULL);
00953         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
00954                                 (const void **) &dirNames, NULL);
00955         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
00956                                 (const void **) &dirIndexes, NULL);
00957 
00958         do {
00959             fingerPrint fp2;
00960             int num = dbiIndexRecordFileNumber(allMatches, i);
00961 
00962             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
00963             if (FP_EQUAL(fp1, fp2)) {
00964                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
00965                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
00966                 dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
00967             }
00968 
00969             prevoff = offset;
00970             i++;
00971             offset = dbiIndexRecordOffset(allMatches, i);
00972         } while (i < allMatches->count && 
00973                 (i == 0 || offset == prevoff));
00974 
00975         free(baseNames);
00976         free(dirNames);
00977         headerFree(h);
00978     }
00979 
00980     if (rec) {
00981         free(rec);
00982         rec = NULL;
00983     }
00984     if (allMatches) {
00985         dbiFreeIndexSet(allMatches);
00986         allMatches = NULL;
00987     }
00988 
00989     fpCacheFree(fpc);
00990 
00991     if ((*matches)->count == 0) {
00992         dbiFreeIndexSet(*matches);
00993         *matches = NULL; 
00994         return 1;
00995     }
00996 
00997     return 0;
00998 }
00999 
01000 /* XXX python/upgrade.c, install.c, uninstall.c */
01001 int rpmdbCountPackages(rpmdb rpmdb, const char * name)
01002 {
01003     dbiIndex dbi;
01004     dbiIndexSet matches = NULL;
01005     int rc = -1;
01006     int xx;
01007 
01008     dbi = dbiOpen(rpmdb, RPMTAG_NAME, 0);
01009     if (dbi) {
01010         DBC * dbcursor = NULL;
01011         xx = dbiCopen(dbi, &dbcursor, 0);
01012         rc = dbiSearch(dbi, dbcursor, name, strlen(name), &matches);
01013         xx = dbiCclose(dbi, dbcursor, 0);
01014         dbcursor = NULL;
01015     }
01016 
01017     if (rc == 0)        /* success */
01018         rc = dbiIndexSetCount(matches);
01019     else if (rc > 0)    /* error */
01020         rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages\n"), rc);
01021     else                /* not found */
01022         rc = 0;
01023 
01024     if (matches)
01025         dbiFreeIndexSet(matches);
01026 
01027     return rc;
01028 }
01029 
01030 /* XXX transaction.c */
01031 /* 0 found matches */
01032 /* 1 no matches */
01033 /* 2 error */
01034 static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
01035         const char * name, const char * version, const char * release,
01036         /*@out@*/ dbiIndexSet * matches)
01037 {
01038     int gotMatches;
01039     int rc;
01040     int i;
01041 
01042     rc = dbiSearch(dbi, dbcursor, name, strlen(name), matches);
01043 
01044     if (rc != 0) {
01045         rc = ((rc == -1) ? 2 : 1);
01046         goto exit;
01047     }
01048 
01049     if (!version && !release) {
01050         rc = 0;
01051         goto exit;
01052     }
01053 
01054     gotMatches = 0;
01055 
01056     /* make sure the version and releases match */
01057     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
01058         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
01059         int goodRelease, goodVersion;
01060         const char * pkgVersion;
01061         const char * pkgRelease;
01062         Header h;
01063 
01064         if (recoff == 0)
01065             continue;
01066 
01067     {   rpmdbMatchIterator mi;
01068         mi = rpmdbInitIterator(dbi->dbi_rpmdb, RPMDBI_PACKAGES, &recoff, sizeof(recoff));
01069         h = rpmdbNextIterator(mi);
01070         if (h)
01071             h = headerLink(h);
01072         rpmdbFreeIterator(mi);
01073     }
01074 
01075         if (h == NULL) {
01076             rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
01077                 "findMatches", recoff);
01078             rc = 2;
01079             goto exit;
01080         }
01081 
01082         headerNVR(h, NULL, &pkgVersion, &pkgRelease);
01083             
01084         goodRelease = goodVersion = 1;
01085 
01086         if (release && strcmp(release, pkgRelease)) goodRelease = 0;
01087         if (version && strcmp(version, pkgVersion)) goodVersion = 0;
01088 
01089         if (goodRelease && goodVersion) {
01090             /* structure assignment */
01091             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
01092         } else 
01093             (*matches)->recs[i].hdrNum = 0;
01094 
01095         headerFree(h);
01096     }
01097 
01098     if (gotMatches) {
01099         (*matches)->count = gotMatches;
01100         rc = 0;
01101     } else {
01102         rc = 1;
01103     }
01104 
01105 exit:
01106     if (rc && matches && *matches) {
01107         dbiFreeIndexSet(*matches);
01108         *matches = NULL;
01109     }
01110     return rc;
01111 }
01112 
01121 static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, const char * arg, dbiIndexSet * matches)
01122 {
01123     char * localarg, * chptr;
01124     char * release;
01125     int rc;
01126  
01127     if (!strlen(arg)) return 1;
01128 
01129     /* did they give us just a name? */
01130     rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches);
01131     if (rc != 1) return rc;
01132     if (*matches) {
01133         dbiFreeIndexSet(*matches);
01134         *matches = NULL;
01135     }
01136 
01137     /* maybe a name and a release */
01138     localarg = alloca(strlen(arg) + 1);
01139     strcpy(localarg, arg);
01140 
01141     chptr = (localarg + strlen(localarg)) - 1;
01142     while (chptr > localarg && *chptr != '-') chptr--;
01143     if (chptr == localarg) return 1;
01144 
01145     *chptr = '\0';
01146     rc = dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, NULL, matches);
01147     if (rc != 1) return rc;
01148     if (*matches) dbiFreeIndexSet(*matches);
01149     
01150     /* how about name-version-release? */
01151 
01152     release = chptr + 1;
01153     while (chptr > localarg && *chptr != '-') chptr--;
01154     if (chptr == localarg) return 1;
01155 
01156     *chptr = '\0';
01157     return dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, release, matches);
01158 }
01159 
01170 static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h)
01171 {
01172     sigset_t signalMask;
01173     void * uh;
01174     size_t uhlen;
01175     int rc;
01176     int xx;
01177 
01178     if (_noDirTokens)
01179         expandFilelist(h);
01180 
01181     uhlen = headerSizeof(h, HEADER_MAGIC_NO);
01182     uh = headerUnload(h);
01183     blockSignals(dbi->dbi_rpmdb, &signalMask);
01184     rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, 0);
01185     xx = dbiSync(dbi, 0);
01186     unblockSignals(dbi->dbi_rpmdb, &signalMask);
01187     free(uh);
01188 
01189     return rc;
01190 }
01191 
01192 struct _rpmdbMatchIterator {
01193 /*@only@*/ const void * mi_keyp;
01194     size_t              mi_keylen;
01195 /*@kept@*/ rpmdb        mi_rpmdb;
01196     int                 mi_rpmtag;
01197     dbiIndexSet         mi_set;
01198     DBC *               mi_dbc;
01199     int                 mi_setx;
01200     Header              mi_h;
01201     int                 mi_sorted;
01202     int                 mi_modified;
01203     unsigned int        mi_prevoffset;
01204     unsigned int        mi_offset;
01205     unsigned int        mi_filenum;
01206     unsigned int        mi_fpnum;
01207     unsigned int        mi_dbnum;
01208 /*@only@*/ const char *mi_version;
01209 /*@only@*/ const char *mi_release;
01210 };
01211 
01212 void rpmdbFreeIterator(rpmdbMatchIterator mi)
01213 {
01214     dbiIndex dbi = NULL;
01215     int xx;
01216 
01217     if (mi == NULL)
01218         return;
01219 
01220     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01221     if (mi->mi_h) {
01222         if (mi->mi_modified && mi->mi_prevoffset) {
01223             DBC * dbcursor = NULL;
01224             xx = dbiCopen(dbi, &dbcursor, 0);
01225             dbiUpdateRecord(dbi, dbcursor, mi->mi_prevoffset, mi->mi_h);
01226             xx = dbiCclose(dbi, dbcursor, 0);
01227             dbcursor = NULL;
01228         }
01229         headerFree(mi->mi_h);
01230         mi->mi_h = NULL;
01231     }
01232     if (dbi->dbi_rmw) {
01233         xx = dbiCclose(dbi, dbi->dbi_rmw, 0);
01234         dbi->dbi_rmw = NULL;
01235     }
01236 
01237     if (mi->mi_release) {
01238         free((void *)mi->mi_release);
01239         mi->mi_release = NULL;
01240     }
01241     if (mi->mi_version) {
01242         free((void *)mi->mi_version);
01243         mi->mi_version = NULL;
01244     }
01245     if (mi->mi_dbc) {
01246         xx = dbiCclose(dbi, mi->mi_dbc, 1);
01247         mi->mi_dbc = NULL;
01248     }
01249     if (mi->mi_set) {
01250         dbiFreeIndexSet(mi->mi_set);
01251         mi->mi_set = NULL;
01252     }
01253     if (mi->mi_keyp) {
01254         free((void *)mi->mi_keyp);
01255         mi->mi_keyp = NULL;
01256     }
01257     free(mi);
01258 }
01259 
01260 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
01261     if (mi == NULL)
01262         return 0;
01263     return mi->mi_rpmdb;
01264 }
01265 
01266 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
01267     if (mi == NULL)
01268         return 0;
01269     return mi->mi_offset;
01270 }
01271 
01272 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
01273     if (mi == NULL)
01274         return 0;
01275     return mi->mi_filenum;
01276 }
01277 
01278 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
01279     if (!(mi && mi->mi_set))
01280         return 0;       /* XXX W2DO? */
01281     return mi->mi_set->count;
01282 }
01283 
01284 void rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
01285     if (mi == NULL)
01286         return;
01287     if (mi->mi_release) {
01288         free((void *)mi->mi_release);
01289         mi->mi_release = NULL;
01290     }
01291     mi->mi_release = (release ? xstrdup(release) : NULL);
01292 }
01293 
01294 void rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
01295     if (mi == NULL)
01296         return;
01297     if (mi->mi_version) {
01298         free((void *)mi->mi_version);
01299         mi->mi_version = NULL;
01300     }
01301     mi->mi_version = (version ? xstrdup(version) : NULL);
01302 }
01303 
01304 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
01305     int rc;
01306     if (mi == NULL)
01307         return 0;
01308     rc = mi->mi_modified;
01309     mi->mi_modified = modified;
01310     return rc;
01311 }
01312 
01313 Header XrpmdbNextIterator(rpmdbMatchIterator mi, const char * f, unsigned l)
01314 {
01315     dbiIndex dbi;
01316     void * uh = NULL;
01317     size_t uhlen = 0;
01318     void * keyp;
01319     size_t keylen;
01320     int rc;
01321     int xx;
01322 
01323     if (mi == NULL)
01324         return NULL;
01325 
01326     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01327     if (dbi == NULL)
01328         return NULL;
01329     /* XXX cursors need to be per-iterator, not per-dbi. Get a cursor now. */
01330     if (mi->mi_dbc == NULL) {
01331         xx = XdbiCopen(dbi, &mi->mi_dbc, 1, f, l);
01332     }
01333     dbi->dbi_lastoffset = mi->mi_prevoffset;
01334 
01335 top:
01336     /* XXX skip over instances with 0 join key */
01337     do {
01338         if (mi->mi_set) {
01339             if (!(mi->mi_setx < mi->mi_set->count))
01340                 return NULL;
01341             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
01342             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
01343             keyp = &mi->mi_offset;
01344             keylen = sizeof(mi->mi_offset);
01345         } else {
01346             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
01347             keylen = mi->mi_keylen;
01348 
01349             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
01350 
01351             /*
01352              * If we got the next key, save the header instance number.
01353              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
01354              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
01355              * largest header instance in the database, and should be
01356              * skipped.
01357              */
01358             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
01359                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01360 
01361             /* Terminate on error or end of keys */
01362             if (rc || (mi->mi_setx && mi->mi_offset == 0))
01363                 return NULL;
01364         }
01365         mi->mi_setx++;
01366     } while (mi->mi_offset == 0);
01367 
01368     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
01369         goto exit;
01370 
01371     /* Retrieve next header */
01372     if (uh == NULL) {
01373         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
01374         if (rc)
01375             return NULL;
01376     }
01377 
01378     /* Free current header */
01379     if (mi->mi_h) {
01380         if (mi->mi_modified && mi->mi_prevoffset)
01381             dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
01382         headerFree(mi->mi_h);
01383         mi->mi_h = NULL;
01384     }
01385 
01386     mi->mi_h = headerCopyLoad(uh);
01387 
01388     if (mi->mi_release) {
01389         const char *release;
01390         headerNVR(mi->mi_h, NULL, NULL, &release);
01391         if (strcmp(mi->mi_release, release))
01392             goto top;
01393     }
01394 
01395     if (mi->mi_version) {
01396         const char *version;
01397         headerNVR(mi->mi_h, NULL, &version, NULL);
01398         if (strcmp(mi->mi_version, version))
01399             goto top;
01400     }
01401 
01402     mi->mi_prevoffset = mi->mi_offset;
01403     mi->mi_modified = 0;
01404 
01405 exit:
01406 #ifdef  NOTNOW
01407     if (mi->mi_h) {
01408         const char *n, *v, *r;
01409         headerNVR(mi->mi_h, &n, &v, &r);
01410         rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r,
01411                 mi->mi_offset, mi->mi_h);
01412     }
01413 #endif
01414     return mi->mi_h;
01415 }
01416 
01417 static void rpmdbSortIterator(rpmdbMatchIterator mi) {
01418     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
01419         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
01420                 hdrNumCmp);
01421         mi->mi_sorted = 1;
01422     }
01423 }
01424 
01425 static int rpmdbGrowIterator(rpmdbMatchIterator mi,
01426         const void * keyp, size_t keylen, int fpNum)
01427 {
01428     dbiIndex dbi = NULL;
01429     DBC * dbcursor = NULL;
01430     dbiIndexSet set = NULL;
01431     int rc;
01432     int xx;
01433 
01434     if (!(mi && keyp))
01435         return 1;
01436 
01437     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
01438     if (dbi == NULL)
01439         return 1;
01440 
01441     if (keylen == 0)
01442         keylen = strlen(keyp);
01443 
01444     xx = dbiCopen(dbi, &dbcursor, 0);
01445     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01446     xx = dbiCclose(dbi, dbcursor, 0);
01447     dbcursor = NULL;
01448 
01449     if (rc == 0) {      /* success */
01450         int i;
01451         for (i = 0; i < set->count; i++)
01452             set->recs[i].fpNum = fpNum;
01453 
01454         if (mi->mi_set == NULL) {
01455             mi->mi_set = set;
01456             set = NULL;
01457         } else {
01458             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
01459                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
01460             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
01461                 set->count * sizeof(*(mi->mi_set->recs)));
01462             mi->mi_set->count += set->count;
01463         }
01464     }
01465 
01466     if (set)
01467         dbiFreeIndexSet(set);
01468     return rc;
01469 }
01470 
01471 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
01472         int nHdrNums, int sorted)
01473 {
01474     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
01475         return 1;
01476 
01477     if (mi->mi_set)
01478         dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
01479     return 0;
01480 }
01481 
01482 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
01483 {
01484     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
01485         return 1;
01486 
01487     if (mi->mi_set == NULL)
01488         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
01489     dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
01490     return 0;
01491 }
01492 
01493 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
01494         const void * keyp, size_t keylen)
01495 {
01496     rpmdbMatchIterator mi = NULL;
01497     dbiIndexSet set = NULL;
01498     dbiIndex dbi;
01499     const void * mi_keyp = NULL;
01500     int isLabel = 0;
01501 
01502     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
01503     switch (rpmtag) {
01504     case RPMDBI_LABEL:
01505         rpmtag = RPMTAG_NAME;
01506         isLabel = 1;
01507         break;
01508     }
01509 
01510     dbi = dbiOpen(rpmdb, rpmtag, 0);
01511     if (dbi == NULL)
01512         return NULL;
01513 
01514 #if 0
01515     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
01516     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
01517 #else
01518 if (dbi->dbi_rmw)
01519 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
01520 #endif
01521 
01522     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
01523 
01524     if (rpmtag != RPMDBI_PACKAGES && keyp) {
01525         DBC * dbcursor = NULL;
01526         int rc;
01527         int xx;
01528 
01529         if (isLabel) {
01530             /* XXX HACK to get rpmdbFindByLabel out of the API */
01531             xx = dbiCopen(dbi, &dbcursor, 0);
01532             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
01533             xx = dbiCclose(dbi, dbcursor, 0);
01534             dbcursor = NULL;
01535         } else if (rpmtag == RPMTAG_BASENAMES) {
01536             rc = rpmdbFindByFile(rpmdb, keyp, &set);
01537         } else {
01538             xx = dbiCopen(dbi, &dbcursor, 0);
01539             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01540             xx = dbiCclose(dbi, dbcursor, 0);
01541             dbcursor = NULL;
01542         }
01543         if (rc) {       /* error/not found */
01544             if (set)
01545                 dbiFreeIndexSet(set);
01546             return NULL;
01547         }
01548     }
01549 
01550     if (keyp) {
01551         char * k;
01552 
01553         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
01554             keylen = strlen(keyp);
01555         k = xmalloc(keylen + 1);
01556         memcpy(k, keyp, keylen);
01557         k[keylen] = '\0';       /* XXX for strings */
01558         mi_keyp = k;
01559     }
01560 
01561     mi = xcalloc(1, sizeof(*mi));
01562     mi->mi_keyp = mi_keyp;
01563     mi->mi_keylen = keylen;
01564 
01565     mi->mi_rpmdb = rpmdb;
01566     mi->mi_rpmtag = rpmtag;
01567 
01568     mi->mi_dbc = NULL;
01569     mi->mi_set = set;
01570     mi->mi_setx = 0;
01571     mi->mi_h = NULL;
01572     mi->mi_sorted = 0;
01573     mi->mi_modified = 0;
01574     mi->mi_prevoffset = 0;
01575     mi->mi_offset = 0;
01576     mi->mi_filenum = 0;
01577     mi->mi_fpnum = 0;
01578     mi->mi_dbnum = 0;
01579     mi->mi_version = NULL;
01580     mi->mi_release = NULL;
01581     return mi;
01582 }
01583 
01593 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor,
01594         const void * keyp, size_t keylen, dbiIndexItem rec)
01595 {
01596     dbiIndexSet set = NULL;
01597     int rc;
01598     
01599     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01600 
01601     if (rc < 0)                 /* not found */
01602         rc = 0;
01603     else if (rc > 0)            /* error */
01604         rc = 1;         /* error message already generated from dbindex.c */
01605     else {                      /* success */
01606         if (!dbiPruneSet(set, rec, 1, sizeof(*rec), 1) &&
01607             dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
01608             rc = 1;
01609     }
01610 
01611     if (set) {
01612         dbiFreeIndexSet(set);
01613         set = NULL;
01614     }
01615 
01616     return rc;
01617 }
01618 
01619 /* XXX install.c uninstall.c */
01620 int rpmdbRemove(rpmdb rpmdb, int rid, unsigned int hdrNum)
01621 {
01622     Header h;
01623     sigset_t signalMask;
01624 
01625     {   rpmdbMatchIterator mi;
01626         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
01627         h = rpmdbNextIterator(mi);
01628         if (h)
01629             h = headerLink(h);
01630         rpmdbFreeIterator(mi);
01631     }
01632 
01633     if (h == NULL) {
01634         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
01635               "rpmdbRemove", hdrNum);
01636         return 1;
01637     }
01638 
01639     /* Add remove transaction id to header. */
01640     if (rid > 0) {
01641         int_32 tid = rid;
01642         headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
01643     }
01644 
01645     {   const char *n, *v, *r;
01646         headerNVR(h, &n, &v, &r);
01647         rpmMessage(RPMMESS_DEBUG, "  --- %10d %s-%s-%s\n", hdrNum, n, v, r);
01648     }
01649 
01650     blockSignals(rpmdb, &signalMask);
01651 
01652     {   int dbix;
01653         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
01654 
01655         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
01656             dbiIndex dbi;
01657             DBC * dbcursor = NULL;
01658             const char *av[1];
01659             const char ** rpmvals = NULL;
01660             int rpmtype = 0;
01661             int rpmcnt = 0;
01662             int rpmtag;
01663             int xx;
01664             int i;
01665 
01666             dbi = NULL;
01667             rpmtag = dbiTags[dbix];
01668 
01669             switch (rpmtag) {
01670             /* Filter out temporary databases */
01671             case RPMDBI_AVAILABLE:
01672             case RPMDBI_ADDED:
01673             case RPMDBI_REMOVED:
01674             case RPMDBI_DEPENDS:
01675                 continue;
01676                 /*@notreached@*/ break;
01677             case RPMDBI_PACKAGES:
01678                 dbi = dbiOpen(rpmdb, rpmtag, 0);
01679                 xx = dbiCopen(dbi, &dbcursor, 0);
01680                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
01681                 xx = dbiCclose(dbi, dbcursor, 0);
01682                 dbcursor = NULL;
01683                 if (!dbi->dbi_no_dbsync)
01684                     xx = dbiSync(dbi, 0);
01685                 continue;
01686                 /*@notreached@*/ break;
01687             }
01688         
01689             if (!headerGetEntry(h, rpmtag, &rpmtype,
01690                         (void **) &rpmvals, &rpmcnt))
01691                 continue;
01692 
01693             dbi = dbiOpen(rpmdb, rpmtag, 0);
01694             xx = dbiCopen(dbi, &dbcursor, 0);
01695 
01696             if (rpmtype == RPM_STRING_TYPE) {
01697 
01698                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
01699                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
01700 
01701                 /* XXX force uniform headerGetEntry return */
01702                 av[0] = (const char *) rpmvals;
01703                 rpmvals = av;
01704                 rpmcnt = 1;
01705             } else {
01706 
01707                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
01708                         rpmcnt, tagName(dbi->dbi_rpmtag));
01709 
01710             }
01711 
01712             for (i = 0; i < rpmcnt; i++) {
01713                 const void * valp;
01714                 size_t vallen;
01715 
01716                 /* Identify value pointer and length. */
01717                 switch (rpmtype) {
01718                 case RPM_CHAR_TYPE:
01719                 case RPM_INT8_TYPE:
01720                     vallen = sizeof(RPM_CHAR_TYPE);
01721                     valp = rpmvals + i;
01722                     break;
01723                 case RPM_INT16_TYPE:
01724                     vallen = sizeof(int_16);
01725                     valp = rpmvals + i;
01726                     break;
01727                 case RPM_INT32_TYPE:
01728                     vallen = sizeof(int_32);
01729                     valp = rpmvals + i;
01730                     break;
01731                 case RPM_BIN_TYPE:
01732                     vallen = rpmcnt;
01733                     valp = rpmvals;
01734                     rpmcnt = 1;         /* XXX break out of loop. */
01735                     break;
01736                 case RPM_STRING_TYPE:
01737                 case RPM_I18NSTRING_TYPE:
01738                     rpmcnt = 1;         /* XXX break out of loop. */
01739                     /*@fallthrough@*/
01740                 case RPM_STRING_ARRAY_TYPE:
01741                 default:
01742                     vallen = strlen(rpmvals[i]);
01743                     valp = rpmvals[i];
01744                     break;
01745                 }
01746 
01747                 /*
01748                  * This is almost right, but, if there are duplicate tag
01749                  * values, there will be duplicate attempts to remove
01750                  * the header instance. It's easier to just ignore errors
01751                  * than to do things correctly.
01752                  */
01753                 xx = removeIndexEntry(dbi, dbcursor, valp, vallen, rec);
01754             }
01755 
01756             xx = dbiCclose(dbi, dbcursor, 0);
01757             dbcursor = NULL;
01758 
01759             if (!dbi->dbi_no_dbsync)
01760                 xx = dbiSync(dbi, 0);
01761 
01762             rpmvals = headerFreeData(rpmvals, rpmtype);
01763             rpmtype = 0;
01764             rpmcnt = 0;
01765         }
01766 
01767         if (rec) {
01768             free(rec);
01769             rec = NULL;
01770         }
01771     }
01772 
01773     unblockSignals(rpmdb, &signalMask);
01774 
01775     headerFree(h);
01776 
01777     return 0;
01778 }
01779 
01789 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor,
01790         const char * keyp, size_t keylen, dbiIndexItem rec)
01791 {
01792     dbiIndexSet set = NULL;
01793     int rc;
01794 
01795     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01796 
01797     if (rc > 0) {
01798         rc = 1;                 /* error */
01799     } else {
01800         if (rc < 0) {           /* not found */
01801             rc = 0;
01802             set = xcalloc(1, sizeof(*set));
01803         }
01804         dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
01805         if (dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
01806             rc = 1;
01807     }
01808 
01809     if (set) {
01810         dbiFreeIndexSet(set);
01811         set = NULL;
01812     }
01813 
01814     return 0;
01815 }
01816 
01817 /* XXX install.c */
01818 int rpmdbAdd(rpmdb rpmdb, int iid, Header h)
01819 {
01820     sigset_t signalMask;
01821     const char ** baseNames;
01822     int count = 0;
01823     int type;
01824     dbiIndex dbi;
01825     int dbix;
01826     unsigned int hdrNum;
01827     int rc = 0;
01828     int xx;
01829 
01830     if (iid > 0) {
01831         int_32 tid = iid;
01832         headerRemoveEntry(h, RPMTAG_REMO