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

rpmdb/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 <fnmatch.h>
00015 #include <regex.h>
00016 
00017 #include <rpmcli.h>
00018 
00019 #include "rpmdb.h"
00020 #include "fprint.h"
00021 #include "misc.h"
00022 #include "debug.h"
00023 
00024 /*@access dbiIndexSet@*/
00025 /*@access dbiIndexItem@*/
00026 /*@access Header@*/             /* XXX compared with NULL */
00027 /*@access rpmdbMatchIterator@*/
00028 
00029 /*@-redecl@*/
00030 extern int _noDirTokens;
00031 /*@=redecl@*/
00032 static int _rebuildinprogress = 0;
00033 static int _db_filter_dups = 0;
00034 
00035 #define _DBI_FLAGS      0
00036 #define _DBI_PERMS      0644
00037 #define _DBI_MAJOR      -1
00038 
00039 /*@globstate@*/ /*@null@*/ int * dbiTags = NULL;
00040 int dbiTagsMax = 0;
00041 
00047 static int dbiTagToDbix(int rpmtag)
00048         /*@*/
00049 {
00050     int dbix;
00051 
00052     if (dbiTags != NULL)
00053     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00054         if (rpmtag == dbiTags[dbix])
00055             return dbix;
00056     }
00057     return -1;
00058 }
00059 
00063 static void dbiTagsInit(void)
00064         /*@modifies dbiTags, dbiTagsMax @*/
00065 {
00066 /*@observer@*/ static const char * const _dbiTagStr_default =
00067         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Removetid";
00068     char * dbiTagStr = NULL;
00069     char * o, * oe;
00070     int rpmtag;
00071 
00072     /*@-nullpass@*/
00073     dbiTagStr = rpmExpand("%{_dbi_tags}", NULL);
00074     /*@=nullpass@*/
00075     if (!(dbiTagStr && *dbiTagStr && *dbiTagStr != '%')) {
00076         dbiTagStr = _free(dbiTagStr);
00077         dbiTagStr = xstrdup(_dbiTagStr_default);
00078     }
00079 
00080     /* Discard previous values. */
00081     dbiTags = _free(dbiTags);
00082     dbiTagsMax = 0;
00083 
00084     /* Always allocate package index */
00085     dbiTags = xcalloc(1, sizeof(*dbiTags));
00086     dbiTags[dbiTagsMax++] = RPMDBI_PACKAGES;
00087 
00088     for (o = dbiTagStr; o && *o; o = oe) {
00089         while (*o && xisspace(*o))
00090             o++;
00091         if (*o == '\0')
00092             break;
00093         for (oe = o; oe && *oe; oe++) {
00094             if (xisspace(*oe))
00095                 /*@innerbreak@*/ break;
00096             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
00097                 /*@innerbreak@*/ break;
00098         }
00099         if (oe && *oe)
00100             *oe++ = '\0';
00101         rpmtag = tagValue(o);
00102         if (rpmtag < 0) {
00103 
00104             fprintf(stderr, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
00105             continue;
00106         }
00107         if (dbiTagToDbix(rpmtag) >= 0)
00108             continue;
00109 
00110         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
00111         dbiTags[dbiTagsMax++] = rpmtag;
00112     }
00113 
00114     dbiTagStr = _free(dbiTagStr);
00115 }
00116 
00117 /*@-redecl@*/
00118 #if USE_DB1
00119 extern struct _dbiVec db1vec;
00120 #define DB1vec          &db1vec
00121 #else
00122 #define DB1vec          NULL
00123 #endif
00124 
00125 #if USE_DB2
00126 extern struct _dbiVec db2vec;
00127 #define DB2vec          &db2vec
00128 #else
00129 #define DB2vec          NULL
00130 #endif
00131 
00132 #if USE_DB3
00133 extern struct _dbiVec db3vec;
00134 #define DB3vec          &db3vec
00135 #else
00136 #define DB3vec          NULL
00137 #endif
00138 /*@=redecl@*/
00139 
00140 /*@-nullassign@*/
00141 static struct _dbiVec *mydbvecs[] = {
00142     DB1vec, DB1vec, DB2vec, DB3vec, NULL
00143 };
00144 /*@=nullassign@*/
00145 
00146 INLINE int dbiSync(dbiIndex dbi, unsigned int flags)
00147 {
00148 if (_debug < 0 || dbi->dbi_debug)
00149 fprintf(stderr, "    Sync %s\n", tagName(dbi->dbi_rpmtag));
00150     return (*dbi->dbi_vec->sync) (dbi, flags);
00151 }
00152 
00153 INLINE int dbiByteSwapped(dbiIndex dbi)
00154 {
00155     return (*dbi->dbi_vec->byteswapped) (dbi);
00156 }
00157 
00158 INLINE int dbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags)
00159 {
00160 if (_debug < 0 || dbi->dbi_debug)
00161 fprintf(stderr, "+++ RMW %s %s\n", tagName(dbi->dbi_rpmtag), ((flags & DBI_WRITECURSOR) ? "WRITECURSOR" : ""));
00162     return (*dbi->dbi_vec->copen) (dbi, dbcp, flags);
00163 }
00164 
00165 INLINE int dbiCclose(dbiIndex dbi, /*@only@*/DBC * dbcursor, unsigned int flags)
00166 {
00167 if (_debug < 0 || dbi->dbi_debug)
00168 fprintf(stderr, "--- RMW %s\n", tagName(dbi->dbi_rpmtag));
00169     return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags);
00170 }
00171 
00172 static int printable(const void * ptr, size_t len)      /*@*/
00173 {
00174     const char * s = ptr;
00175     int i;
00176     for (i = 0; i < len; i++, s++)
00177         if (!(*s >= ' ' && *s <= '~')) return 0;
00178     return 1;
00179 }
00180 
00181 INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor,
00182         const void * keyp, size_t keylen, unsigned int flags)
00183 {
00184     int NULkey;
00185     int rc;
00186 
00187     /* Make sure that keylen is correct for "" lookup. */
00188     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00189     if (NULkey) keylen++;
00190     rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags);
00191     if (NULkey) keylen--;
00192 
00193 if (_debug < 0 || dbi->dbi_debug)
00194 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);
00195 
00196     return rc;
00197 }
00198 
00199 INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp,
00200         void ** datapp, size_t * datalenp, unsigned int flags)
00201 {
00202     int NULkey;
00203     int rc;
00204 
00205     /* Make sure that keylen is correct for "" lookup. */
00206     NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0');
00207     NULkey = (keylenp && *keylenp == 0 && NULkey);
00208     if (keylenp && NULkey) (*keylenp)++;
00209     rc = (*dbi->dbi_vec->cget) (dbi, dbcursor,
00210                 keypp, keylenp, datapp, datalenp, flags);
00211     if (keylenp && NULkey) (*keylenp)--;
00212 
00213 /*@-nullderef -nullpass@*/
00214 if (_debug < 0 || dbi->dbi_debug) {
00215  int dataval = 0xdeadbeef;
00216  const char * kvp;
00217  char keyval[64];
00218  keyval[0] = '\0';
00219  if (keypp && *keypp && keylenp) {
00220   if (*keylenp <= sizeof(int) && !printable(*keypp, *keylenp)) {
00221     int keyint = 0;
00222     memcpy(&keyint, *keypp, sizeof(keyint));
00223     sprintf(keyval, "#%d", keyint);
00224     kvp = keyval;
00225   } else {
00226     kvp = *keypp;
00227   }
00228  } else
00229    kvp = keyval;
00230  if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval)) {
00231     memcpy(&dataval, *datapp, sizeof(dataval));
00232  }
00233  fprintf(stderr, "    Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n",
00234     tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp,
00235     kvp, (unsigned)dataval, rc);
00236 }
00237 /*@=nullderef =nullpass@*/
00238     return rc;
00239 }
00240 
00241 INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor,
00242         const void * keyp, size_t keylen,
00243         const void * datap, size_t datalen, unsigned int flags)
00244 {
00245     int NULkey;
00246     int rc;
00247 
00248     /* XXX make sure that keylen is correct for "" lookup */
00249     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00250     if (NULkey) keylen++;
00251     rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags);
00252     if (NULkey) keylen--;
00253 
00254 /*@-nullderef -nullpass@*/
00255 if (_debug < 0 || dbi->dbi_debug) {
00256  int dataval = 0xdeadbeef;
00257  const char * kvp;
00258  char keyval[64];
00259  keyval[0] = '\0';
00260  if (keyp) {
00261   if (keylen == sizeof(int) && !printable(keyp, keylen)) {
00262     int keyint = 0;
00263     memcpy(&keyint, keyp, sizeof(keyint));
00264     sprintf(keyval, "#%d", keyint);
00265     kvp = keyval;
00266   } else {
00267     kvp = keyp;
00268   }
00269  } else
00270    kvp = keyval;
00271  if (rc == 0 && datap && datalen >= sizeof(dataval)) {
00272     memcpy(&dataval, datap, sizeof(dataval));
00273  }
00274  fprintf(stderr, "    Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (datap ? datap : NULL), (long)datalen, kvp, (unsigned)dataval, rc);
00275 }
00276 /*@=nullderef =nullpass@*/
00277 
00278     return rc;
00279 }
00280 
00281 INLINE int dbiCount(dbiIndex dbi, DBC * dbcursor,
00282         unsigned int * countp, unsigned int flags)
00283 {
00284     int rc = (*dbi->dbi_vec->ccount) (dbi, dbcursor, countp, flags);
00285 
00286 if (rc == 0 && countp && *countp > 1)
00287 fprintf(stderr, "    Count %s: %u rc %d\n", tagName(dbi->dbi_rpmtag), *countp, rc);
00288 
00289     return rc;
00290 }
00291 
00292 INLINE int dbiVerify(dbiIndex dbi, unsigned int flags)
00293 {
00294     int dbi_debug = dbi->dbi_debug;
00295     int dbi_rpmtag = dbi->dbi_rpmtag;
00296     int rc;
00297 
00298     dbi->dbi_verify_on_close = 1;
00299     rc = (*dbi->dbi_vec->close) (dbi, flags);
00300 
00301 if (_debug < 0 || dbi_debug)
00302 fprintf(stderr, "    Verify %s rc %d\n", tagName(dbi_rpmtag), rc);
00303 
00304     return rc;
00305 }
00306 
00307 INLINE int dbiClose(dbiIndex dbi, unsigned int flags) {
00308 if (_debug < 0 || dbi->dbi_debug)
00309 fprintf(stderr, "    Close %s\n", tagName(dbi->dbi_rpmtag));
00310     return (*dbi->dbi_vec->close) (dbi, flags);
00311 }
00312 
00313 dbiIndex dbiOpen(rpmdb db, int rpmtag, /*@unused@*/ unsigned int flags)
00314 {
00315     int dbix;
00316     dbiIndex dbi = NULL;
00317     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
00318     int rc = 0;
00319 
00320     if (db == NULL)
00321         return NULL;
00322 
00323     dbix = dbiTagToDbix(rpmtag);
00324     if (dbix < 0 || dbix >= dbiTagsMax)
00325         return NULL;
00326 
00327     /* Is this index already open ? */
00328     if ((dbi = db->_dbi[dbix]) != NULL)
00329         return dbi;
00330 
00331     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
00332     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
00333         _dbapi_rebuild = 3;
00334     _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api);
00335 
00336     switch (_dbapi_wanted) {
00337     default:
00338         _dbapi = _dbapi_wanted;
00339         if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
00340             return NULL;
00341         }
00342         errno = 0;
00343         dbi = NULL;
00344         rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
00345         if (rc) {
00346             static int _printed[32];
00347             if (!_printed[dbix & 0x1f]++)
00348                 rpmError(RPMERR_DBOPEN,
00349                         _("cannot open %s index using db%d - %s (%d)\n"),
00350                         tagName(rpmtag), _dbapi,
00351                         (rc > 0 ? strerror(rc) : ""), rc);
00352             _dbapi = -1;
00353         }
00354         break;
00355     case -1:
00356         _dbapi = 4;
00357         while (_dbapi-- > 1) {
00358             if (mydbvecs[_dbapi] == NULL)
00359                 continue;
00360             errno = 0;
00361             dbi = NULL;
00362             rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
00363             if (rc == 0 && dbi)
00364                 /*@loopbreak@*/ break;
00365         }
00366         if (_dbapi <= 0) {
00367             static int _printed[32];
00368             if (!_printed[dbix & 0x1f]++)
00369                 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
00370                         tagName(rpmtag));
00371             rc = 1;
00372             goto exit;
00373         }
00374         if (db->db_api == -1 && _dbapi > 0)
00375             db->db_api = _dbapi;
00376         break;
00377     }
00378 
00379     /* Require conversion. */
00380     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
00381         rc = (_rebuildinprogress ? 0 : 1);
00382         goto exit;
00383     }
00384 
00385     /* Suggest possible configuration */
00386     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
00387         rc = 1;
00388         goto exit;
00389     }
00390 
00391     /* Suggest possible configuration */
00392     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
00393         rc = (_rebuildinprogress ? 0 : 1);
00394         goto exit;
00395     }
00396 
00397 exit:
00398     if (rc == 0 && dbi)
00399         db->_dbi[dbix] = dbi;
00400     else
00401         dbi = db3Free(dbi);
00402 
00403     return dbi;
00404 }
00405 
00412 static INLINE dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
00413         /*@*/
00414 {
00415     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
00416     rec->hdrNum = hdrNum;
00417     rec->tagNum = tagNum;
00418     return rec;
00419 }
00420 
00421 union _dbswap {
00422     unsigned int ui;
00423     unsigned char uc[4];
00424 };
00425 
00426 #define _DBSWAP(_a) \
00427   { unsigned char _b, *_c = (_a).uc; \
00428     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
00429     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
00430   }
00431 
00441 static int dbiSearch(dbiIndex dbi, DBC * dbcursor,
00442                 const char * keyp, size_t keylen, /*@out@*/ dbiIndexSet * setp)
00443         /*@modifies *dbcursor, *setp, fileSystem @*/
00444 {
00445     unsigned int gflags = 0;    /* dbiGet() flags */
00446     void * datap = NULL;
00447     size_t datalen = 0;
00448     int rc;
00449 
00450     if (setp) *setp = NULL;
00451     if (keylen == 0) keylen = strlen(keyp);
00452 
00453     /*@-mods@*/         /* FIX: indirection @*/
00454     rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen,
00455                 gflags);
00456     /*@=mods@*/
00457 
00458     if (rc > 0) {
00459         rpmError(RPMERR_DBGETINDEX,
00460                 _("error(%d) getting \"%s\" records from %s index\n"),
00461                 rc, keyp, tagName(dbi->dbi_rpmtag));
00462     } else
00463     if (rc == 0 && setp) {
00464         int _dbbyteswapped = dbiByteSwapped(dbi);
00465         const char * sdbir = datap;
00466         dbiIndexSet set;
00467         int i;
00468 
00469         set = xmalloc(sizeof(*set));
00470 
00471         /* Convert to database internal format */
00472         if (sdbir)
00473         switch (dbi->dbi_jlen) {
00474         default:
00475         case 2*sizeof(int_32):
00476             set->count = datalen / (2*sizeof(int_32));
00477             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00478             for (i = 0; i < set->count; i++) {
00479                 union _dbswap hdrNum, tagNum;
00480 
00481                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00482                 sdbir += sizeof(hdrNum.ui);
00483                 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
00484                 sdbir += sizeof(tagNum.ui);
00485                 if (_dbbyteswapped) {
00486                     _DBSWAP(hdrNum);
00487                     _DBSWAP(tagNum);
00488                 }
00489                 set->recs[i].hdrNum = hdrNum.ui;
00490                 set->recs[i].tagNum = tagNum.ui;
00491                 set->recs[i].fpNum = 0;
00492                 set->recs[i].dbNum = 0;
00493             }
00494             break;
00495         case 1*sizeof(int_32):
00496             set->count = datalen / (1*sizeof(int_32));
00497             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00498             for (i = 0; i < set->count; i++) {
00499                 union _dbswap hdrNum;
00500 
00501                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00502                 sdbir += sizeof(hdrNum.ui);
00503                 if (_dbbyteswapped) {
00504                     _DBSWAP(hdrNum);
00505                 }
00506                 set->recs[i].hdrNum = hdrNum.ui;
00507                 set->recs[i].tagNum = 0;
00508                 set->recs[i].fpNum = 0;
00509                 set->recs[i].dbNum = 0;
00510             }
00511             break;
00512         }
00513         if (setp) *setp = set;
00514     }
00515     return rc;
00516 }
00517 
00527 /*@-compmempass -mustmod@*/
00528 static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor,
00529                 const void * keyp, size_t keylen, dbiIndexSet set)
00530         /*@modifies *dbcursor, set, fileSystem @*/
00531 {
00532     unsigned int pflags = 0;    /* dbiPut() flags */
00533     unsigned int dflags = 0;    /* dbiDel() flags */
00534     void * datap;
00535     size_t datalen;
00536     int rc;
00537 
00538     if (set->count) {
00539         char * tdbir;
00540         int i;
00541         int _dbbyteswapped = dbiByteSwapped(dbi);
00542 
00543         /* Convert to database internal format */
00544 
00545         switch (dbi->dbi_jlen) {
00546         default:
00547         case 2*sizeof(int_32):
00548             datalen = set->count * (2 * sizeof(int_32));
00549             datap = tdbir = alloca(datalen);
00550             for (i = 0; i < set->count; i++) {
00551                 union _dbswap hdrNum, tagNum;
00552 
00553                 memset(&hdrNum, 0, sizeof(hdrNum));
00554                 memset(&tagNum, 0, sizeof(tagNum));
00555                 hdrNum.ui = set->recs[i].hdrNum;
00556                 tagNum.ui = set->recs[i].tagNum;
00557                 if (_dbbyteswapped) {
00558                     _DBSWAP(hdrNum);
00559                     _DBSWAP(tagNum);
00560                 }
00561                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00562                 tdbir += sizeof(hdrNum.ui);
00563                 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
00564                 tdbir += sizeof(tagNum.ui);
00565             }
00566             break;
00567         case 1*sizeof(int_32):
00568             datalen = set->count * (1 * sizeof(int_32));
00569             datap = tdbir = alloca(datalen);
00570             for (i = 0; i < set->count; i++) {
00571                 union _dbswap hdrNum;
00572 
00573                 memset(&hdrNum, 0, sizeof(hdrNum));
00574                 hdrNum.ui = set->recs[i].hdrNum;
00575                 if (_dbbyteswapped) {
00576                     _DBSWAP(hdrNum);
00577                 }
00578                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00579                 tdbir += sizeof(hdrNum.ui);
00580             }
00581             break;
00582         }
00583 
00584         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, pflags);
00585 
00586         if (rc) {
00587             rpmError(RPMERR_DBPUTINDEX,
00588                 _("error(%d) storing record %s into %s\n"),
00589                 rc, keyp, tagName(dbi->dbi_rpmtag));
00590         }
00591 
00592     } else {
00593 
00594         rc = dbiDel(dbi, dbcursor, keyp, keylen, dflags);
00595 
00596         if (rc) {
00597             rpmError(RPMERR_DBPUTINDEX,
00598                 _("error(%d) removing record %s from %s\n"),
00599                 rc, keyp, tagName(dbi->dbi_rpmtag));
00600         }
00601 
00602     }
00603 
00604     return rc;
00605 }
00606 /*@=compmempass =mustmod@*/
00607 
00608 /* XXX assumes hdrNum is first int in dbiIndexItem */
00609 static int hdrNumCmp(const void * one, const void * two)
00610         /*@*/
00611 {
00612     const int * a = one, * b = two;
00613     return (*a - *b);
00614 }
00615 
00625 static INLINE int dbiAppendSet(dbiIndexSet set, const void * recs,
00626         int nrecs, size_t recsize, int sortset)
00627         /*@modifies set @*/
00628 {
00629     const char * rptr = recs;
00630     size_t rlen = (recsize < sizeof(*(set->recs)))
00631                 ? recsize : sizeof(*(set->recs));
00632 
00633     if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
00634         return 1;
00635 
00636     if (set->count == 0)
00637         set->recs = xmalloc(nrecs * sizeof(*(set->recs)));
00638     else
00639         set->recs = xrealloc(set->recs,
00640                                 (set->count + nrecs) * sizeof(*(set->recs)));
00641 
00642     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
00643 
00644     while (nrecs-- > 0) {
00645         /*@-mayaliasunique@*/
00646         memcpy(set->recs + set->count, rptr, rlen);
00647         /*@=mayaliasunique@*/
00648         rptr += recsize;
00649         set->count++;
00650     }
00651 
00652     if (set->count > 1 && sortset)
00653         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
00654 
00655     return 0;
00656 }
00657 
00667 static INLINE int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
00668                 size_t recsize, int sorted)
00669         /*@modifies set, recs @*/
00670 {
00671     int from;
00672     int to = 0;
00673     int num = set->count;
00674     int numCopied = 0;
00675 
00676     if (nrecs > 1 && !sorted)
00677         qsort(recs, nrecs, recsize, hdrNumCmp);
00678 
00679     for (from = 0; from < num; from++) {
00680         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
00681             set->count--;
00682             continue;
00683         }
00684         if (from != to)
00685             set->recs[to] = set->recs[from]; /* structure assignment */
00686         to++;
00687         numCopied++;
00688     }
00689 
00690     return (numCopied == num);
00691 }
00692 
00693 /* XXX transaction.c */
00694 unsigned int dbiIndexSetCount(dbiIndexSet set) {
00695     return set->count;
00696 }
00697 
00698 /* XXX transaction.c */
00699 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
00700     return set->recs[recno].hdrNum;
00701 }
00702 
00703 /* XXX transaction.c */
00704 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
00705     return set->recs[recno].tagNum;
00706 }
00707 
00708 /* XXX transaction.c */
00709 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
00710     if (set) {
00711         set->recs = _free(set->recs);
00712         set = _free(set);
00713     }
00714     return set;
00715 }
00716 
00720 static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask)
00721         /*@modifies *oldMask, internalState @*/
00722 {
00723     sigset_t newMask;
00724 
00725     (void) sigfillset(&newMask);                /* block all signals */
00726     return sigprocmask(SIG_BLOCK, &newMask, oldMask);
00727 }
00728 
00732 static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask)
00733         /*@modifies internalState @*/
00734 {
00735     return sigprocmask(SIG_SETMASK, oldMask, NULL);
00736 }
00737 
00738 #define _DB_ROOT        "/"
00739 #define _DB_HOME        "%{_dbpath}"
00740 #define _DB_FLAGS       0
00741 #define _DB_MODE        0
00742 #define _DB_PERMS       0644
00743 
00744 #define _DB_MAJOR       -1
00745 #define _DB_ERRPFX      "rpmdb"
00746 
00747 /*@-fullinitblock@*/
00748 /*@observer@*/ static struct rpmdb_s dbTemplate = {
00749     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
00750     _DB_MAJOR,  _DB_ERRPFX
00751 };
00752 /*@=fullinitblock@*/
00753 
00754 int rpmdbOpenAll(rpmdb db)
00755 {
00756     int dbix;
00757     int rc = 0;
00758 
00759     if (db == NULL) return -2;
00760 
00761     if (dbiTags != NULL)
00762     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00763         if (db->_dbi[dbix] != NULL)
00764             continue;
00765         (void) dbiOpen(db, dbiTags[dbix], db->db_flags);
00766     }
00767     return rc;
00768 }
00769 
00770 /* XXX query.c, rpminstall.c, verify.c */
00771 int rpmdbClose(rpmdb db)
00772 {
00773     int dbix;
00774     int rc = 0;
00775 
00776     if (db == NULL) return 0;
00777     if (db->_dbi)
00778     for (dbix = db->db_ndbi; --dbix >= 0; ) {
00779         int xx;
00780         if (db->_dbi[dbix] == NULL)
00781             continue;
00782         /*@-unqualifiedtrans@*/         /* FIX: double indirection. */
00783         xx = dbiClose(db->_dbi[dbix], 0);
00784         if (xx && rc == 0) rc = xx;
00785         db->_dbi[dbix] = NULL;
00786         /*@=unqualifiedtrans@*/
00787     }
00788     db->db_errpfx = _free(db->db_errpfx);
00789     db->db_root = _free(db->db_root);
00790     db->db_home = _free(db->db_home);
00791     db->_dbi = _free(db->_dbi);
00792     db = _free(db);
00793     return rc;
00794 }
00795 
00796 int rpmdbSync(rpmdb db)
00797 {
00798     int dbix;
00799     int rc = 0;
00800 
00801     if (db == NULL) return 0;
00802     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00803         int xx;
00804         if (db->_dbi[dbix] == NULL)
00805             continue;
00806         xx = dbiSync(db->_dbi[dbix], 0);
00807         if (xx && rc == 0) rc = xx;
00808     }
00809     return rc;
00810 }
00811 
00812 static /*@only@*/ /*@null@*/
00813 rpmdb newRpmdb(/*@kept@*/ /*@null@*/ const char * root,
00814                 /*@kept@*/ /*@null@*/ const char * home,
00815                 int mode, int perms, int flags)
00816         /*@modifies _db_filter_dups @*/
00817 {
00818     rpmdb db = xcalloc(sizeof(*db), 1);
00819     const char * epfx = _DB_ERRPFX;
00820     static int _initialized = 0;
00821 
00822     if (!_initialized) {
00823         _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
00824         _initialized = 1;
00825     }
00826 
00827     /*@-assignexpose@*/
00828     *db = dbTemplate;   /* structure assignment */
00829     /*@=assignexpose@*/
00830 
00831     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
00832 
00833     if (mode >= 0)      db->db_mode = mode;
00834     if (perms >= 0)     db->db_perms = perms;
00835     if (flags >= 0)     db->db_flags = flags;
00836 
00837     /*@-nullpass@*/
00838     db->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL);
00839     db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
00840     /*@=nullpass@*/
00841     if (!(db->db_home && db->db_home[0] != '%')) {
00842         rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
00843         (void) rpmdbClose(db);
00844         /*@-globstate@*/ return NULL; /*@=globstate@*/
00845     }
00846     /*@-nullpass@*/
00847     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
00848     /*@=nullpass@*/
00849     db->db_remove_env = 0;
00850     db->db_filter_dups = _db_filter_dups;
00851     db->db_ndbi = dbiTagsMax;
00852     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
00853     /*@-globstate@*/ return db; /*@=globstate@*/
00854 }
00855 
00856 static int openDatabase(/*@null@*/ const char * prefix,
00857                 /*@null@*/ const char * dbpath,
00858                 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
00859                 int mode, int perms, int flags)
00860         /*@modifies *dbp, fileSystem @*/
00861 {
00862     rpmdb db;
00863     int rc;
00864     unsigned int gflags = 0;    /* dbiGet() flags */
00865     static int _initialized = 0;
00866     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
00867     int minimal = flags & RPMDB_FLAG_MINIMAL;
00868 
00869     if (!_initialized || dbiTagsMax == 0) {
00870 
00871 #if 1
00872         static int _enable_cdb = -1;
00873 
00874         /* XXX hack in suoport for CDB, otherwise nuke the state. */
00875         if (_enable_cdb < 0)
00876             _enable_cdb = rpmExpandNumeric("%{?__dbi_cdb:1}");
00877 
00878         if (!_enable_cdb) {
00879             char * filename;
00880             int i;
00881 
00882             i = sizeof("//__db.000");
00883             if (prefix) i += strlen(prefix);
00884             if (dbpath) i += strlen(dbpath);
00885             filename = alloca(i);
00886             for (i = 0; i < 16; i++) {
00887                 sprintf(filename, "%s/%s/__db.%03d",
00888                         (prefix ? prefix : ""), (dbpath ? dbpath : ""),  i);
00889                 (void) rpmCleanPath(filename);
00890                 (void) unlink(filename);
00891             }
00892         }
00893 #endif
00894         dbiTagsInit();
00895         _initialized++;
00896     }
00897 
00898     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
00899     if (_dbapi < -1 || _dbapi > 3)
00900         _dbapi = -1;
00901     if (_dbapi == 0)
00902         _dbapi = 1;
00903 
00904     if (dbp)
00905         *dbp = NULL;
00906     if (mode & O_WRONLY) 
00907         return 1;
00908 
00909     db = newRpmdb(prefix, dbpath, mode, perms, flags);
00910     if (db == NULL)
00911         return 1;
00912     db->db_api = _dbapi;
00913 
00914     {   int dbix;
00915 
00916         rc = 0;
00917         if (dbiTags != NULL)
00918         for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
00919             dbiIndex dbi;
00920             int rpmtag;
00921 
00922             /* Filter out temporary databases */
00923             switch ((rpmtag = dbiTags[dbix])) {
00924             case RPMDBI_AVAILABLE:
00925             case RPMDBI_ADDED:
00926             case RPMDBI_REMOVED:
00927             case RPMDBI_DEPENDS:
00928                 continue;
00929                 /*@notreached@*/ break;
00930             default:
00931                 break;
00932             }
00933 
00934             dbi = dbiOpen(db, rpmtag, 0);
00935             if (dbi == NULL) {
00936                 rc = -2;
00937                 break;
00938             }
00939 
00940             switch (rpmtag) {
00941             case RPMDBI_PACKAGES:
00942                 if (dbi == NULL) rc |= 1;
00943                 /* XXX open only Packages, indices created on the fly. */
00944 #if 0
00945                 if (db->db_api == 3)
00946 #endif
00947                     goto exit;
00948                 /*@notreached@*/ break;
00949             case RPMTAG_NAME:
00950                 if (dbi == NULL) rc |= 1;
00951                 if (minimal)
00952                     goto exit;
00953                 break;
00954             case RPMTAG_BASENAMES:
00955             {   void * keyp = NULL;
00956                 DBC * dbcursor;
00957                 int xx;
00958 
00959     /* We used to store the fileindexes as complete paths, rather then
00960        plain basenames. Let's see which version we are... */
00961     /*
00962      * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
00963      * XXX db1/db2 linkage) conditions.
00964      */
00965                 if (justCheck)
00966                     break;
00967                 dbcursor = NULL;
00968                 xx = dbiCopen(dbi, &dbcursor, 0);
00969                 xx = dbiGet(dbi, dbcursor, &keyp, NULL, NULL, NULL, gflags);
00970                 if (xx == 0) {
00971                     const char * akey = keyp;
00972                     if (akey && strchr(akey, '/')) {
00973                         rpmError(RPMERR_OLDDB, _("old format database is present; "
00974                                 "use --rebuilddb to generate a new format database\n"));
00975                         rc |= 1;
00976                     }
00977                 }
00978                 xx = dbiCclose(dbi, dbcursor, 0);
00979                 dbcursor = NULL;
00980             }   break;
00981             default:
00982                 break;
00983             }
00984         }
00985     }
00986 
00987 exit:
00988     if (rc || justCheck || dbp == NULL)
00989         (void) rpmdbClose(db);
00990     else
00991         *dbp = db;
00992 
00993     return rc;
00994 }
00995 
00996 /* XXX python/rpmmodule.c */
00997 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
00998 {
00999     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01000     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
01001 }
01002 
01003 int rpmdbInit (const char * prefix, int perms)
01004 {
01005     rpmdb db = NULL;
01006     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01007     int rc;
01008 
01009     rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
01010                 perms, RPMDB_FLAG_JUSTCHECK);
01011     if (db != NULL) {
01012         int xx;
01013         xx = rpmdbOpenAll(db);
01014         if (xx && rc == 0) rc = xx;
01015         xx = rpmdbClose(db);
01016         if (xx && rc == 0) rc = xx;
01017         db = NULL;
01018     }
01019     return rc;
01020 }
01021 
01022 int rpmdbVerify(const char * prefix)
01023 {
01024     rpmdb db = NULL;
01025     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01026     int rc = 0;
01027 
01028     rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
01029     if (rc) return rc;
01030 
01031     if (db != NULL) {
01032         int dbix;
01033         int xx;
01034         rc = rpmdbOpenAll(db);
01035 
01036         for (dbix = db->db_ndbi; --dbix >= 0; ) {
01037             if (db->_dbi[dbix] == NULL)
01038                 continue;
01039             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
01040             xx = dbiVerify(db->_dbi[dbix], 0);
01041             if (xx && rc == 0) rc = xx;
01042             db->_dbi[dbix] = NULL;
01043             /*@=unqualifiedtrans@*/
01044         }
01045 
01046         /*@-nullstate@*/        /* FIX: db->_dbi[] may be NULL. */
01047         xx = rpmdbClose(db);
01048         /*@=nullstate@*/
01049         if (xx && rc == 0) rc = xx;
01050         db = NULL;
01051     }
01052     return rc;
01053 }
01054 
01055 static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec,
01056                         /*@out@*/ dbiIndexSet * matches)
01057         /*@modifies db, *matches, fileSystem @*/
01058 {
01059     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
01060     HFD_t hfd = headerFreeData;
01061     const char * dirName;
01062     const char * baseName;
01063     rpmTagType bnt, dnt;
01064     fingerPrintCache fpc;
01065     fingerPrint fp1;
01066     dbiIndex dbi = NULL;
01067     DBC * dbcursor;
01068     dbiIndexSet allMatches = NULL;
01069     dbiIndexItem rec = NULL;
01070     int i;
01071     int rc;
01072     int xx;
01073 
01074     *matches = NULL;
01075     if (filespec == NULL) return -2;
01076     if ((baseName = strrchr(filespec, '/')) != NULL) {
01077         char * t;
01078         size_t len;
01079 
01080         len = baseName - filespec + 1;
01081         t = strncpy(alloca(len + 1), filespec, len);
01082         t[len] = '\0';
01083         dirName = t;
01084         baseName++;
01085     } else {
01086         dirName = "";
01087         baseName = filespec;
01088     }
01089     if (baseName == NULL)
01090         return -2;
01091 
01092     fpc = fpCacheCreate(20);
01093     fp1 = fpLookup(fpc, dirName, baseName, 1);
01094 
01095     dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
01096     if (dbi != NULL) {
01097         dbcursor = NULL;
01098         xx = dbiCopen(dbi, &dbcursor, 0);
01099         rc = dbiSearch(dbi, dbcursor, baseName, strlen(baseName), &allMatches);
01100         xx = dbiCclose(dbi, dbcursor, 0);
01101         dbcursor = NULL;
01102     } else
01103         rc = -2;
01104 
01105     if (rc) {
01106         allMatches = dbiFreeIndexSet(allMatches);
01107         fpCacheFree(fpc);
01108         return rc;
01109     }
01110 
01111     *matches = xcalloc(1, sizeof(**matches));
01112     rec = dbiIndexNewItem(0, 0);
01113     i = 0;
01114     if (allMatches != NULL)
01115     while (i < allMatches->count) {
01116         const char ** baseNames, ** dirNames;
01117         int_32 * dirIndexes;
01118         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
01119         unsigned int prevoff;
01120         Header h;
01121 
01122         {   rpmdbMatchIterator mi;
01123             mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
01124             h = rpmdbNextIterator(mi);
01125             if (h)
01126                 h = headerLink(h);
01127             mi = rpmdbFreeIterator(mi);
01128         }
01129 
01130         if (h == NULL) {
01131             i++;
01132             continue;
01133         }
01134 
01135         (void) hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL);
01136         (void) hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
01137         (void) hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
01138 
01139         do {
01140             fingerPrint fp2;
01141             int num = dbiIndexRecordFileNumber(allMatches, i);
01142 
01143             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
01144             /*@-nullpass@*/
01145             if (FP_EQUAL(fp1, fp2)) {
01146             /*@=nullpass@*/
01147                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
01148                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
01149                 (void) dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
01150             }
01151 
01152             prevoff = offset;
01153             i++;
01154             offset = dbiIndexRecordOffset(allMatches, i);
01155         } while (i < allMatches->count && 
01156                 (i == 0 || offset == prevoff));
01157 
01158         baseNames = hfd(baseNames, bnt);
01159         dirNames = hfd(dirNames, dnt);
01160         h = headerFree(h);
01161     }
01162 
01163     rec = _free(rec);
01164     allMatches = dbiFreeIndexSet(allMatches);
01165 
01166     fpCacheFree(fpc);
01167 
01168     if ((*matches)->count == 0) {
01169         *matches = dbiFreeIndexSet(*matches);
01170         return 1;
01171     }
01172 
01173     return 0;
01174 }
01175 
01176 /* XXX python/upgrade.c, install.c, uninstall.c */
01177 int rpmdbCountPackages(rpmdb db, const char * name)
01178 {
01179     dbiIndex dbi;
01180     dbiIndexSet matches = NULL;
01181     int rc = -1;
01182     int xx;
01183 
01184     if (db == NULL)
01185         return 0;
01186 
01187     /* XXX
01188      * There's a segfault here with CDB access, let's treat the symptom
01189      * while diagnosing the disease.
01190      */
01191     if (name == NULL || *name == '\0')
01192         return 0;
01193 
01194     dbi = dbiOpen(db, RPMTAG_NAME, 0);
01195     if (dbi) {
01196         DBC * dbcursor = NULL;
01197         xx = dbiCopen(dbi, &dbcursor, 0);
01198         rc = dbiSearch(dbi, dbcursor, name, strlen(name), &matches);
01199         xx = dbiCclose(dbi, dbcursor, 0);
01200         dbcursor = NULL;
01201     }
01202 
01203     if (rc == 0)        /* success */
01204         rc = dbiIndexSetCount(matches);
01205     else if (rc > 0)    /* error */
01206         rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages\n"), rc);
01207     else                /* not found */
01208         rc = 0;
01209 
01210     matches = dbiFreeIndexSet(matches);
01211 
01212     return rc;
01213 }
01214 
01215 /* XXX transaction.c */
01216 /* 0 found matches */
01217 /* 1 no matches */
01218 /* 2 error */
01229 static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
01230                 const char * name,
01231                 /*@null@*/ const char * version,
01232                 /*@null@*/ const char * release,
01233                 /*@out@*/ dbiIndexSet * matches)
01234         /*@modifies dbi, *dbcursor, *matches, fileSystem @*/
01235 {
01236     int gotMatches;
01237     int rc;
01238     int i;
01239 
01240     rc = dbiSearch(dbi, dbcursor, name, strlen(name), matches);
01241 
01242     if (rc != 0) {
01243         rc = ((rc == -1) ? 2 : 1);
01244         goto exit;
01245     }
01246 
01247     if (version == NULL && release == NULL) {
01248         rc = 0;
01249         goto exit;
01250     }
01251 
01252     gotMatches = 0;
01253 
01254     /* Make sure the version and release match. */
01255     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
01256         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
01257         Header h;
01258 
01259         if (recoff == 0)
01260             continue;
01261 
01262         {   rpmdbMatchIterator mi;
01263             mi = rpmdbInitIterator(dbi->dbi_rpmdb,
01264                         RPMDBI_PACKAGES, &recoff, sizeof(recoff));
01265 
01266             /* Set iterator selectors for version/release if available. */
01267             if (version &&
01268                 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
01269             {
01270                 rc = 2;
01271                 goto exit;
01272             }
01273             if (release &&
01274                 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
01275             {
01276                 rc = 2;
01277                 goto exit;
01278             }
01279 
01280             h = rpmdbNextIterator(mi);
01281             if (h)
01282                 h = headerLink(h);
01283             mi = rpmdbFreeIterator(mi);
01284         }
01285 
01286         if (h)  /* structure assignment */
01287             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
01288         else
01289             (*matches)->recs[i].hdrNum = 0;
01290 
01291         h = headerFree(h);
01292     }
01293 
01294     if (gotMatches) {
01295         (*matches)->count = gotMatches;
01296         rc = 0;
01297     } else
01298         rc = 1;
01299 
01300 exit:
01301     if (rc && matches && *matches) {
01302         /*@-unqualifiedtrans@*/         /* FIX: double indirection */
01303         *matches = dbiFreeIndexSet(*matches);
01304         /*@=unqualifiedtrans@*/
01305     }
01306     return rc;
01307 }
01308 
01319 static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor,
01320                 /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches)
01321         /*@modifies dbi, *dbcursor, *matches, fileSystem @*/
01322 {
01323     const char * release;
01324     char * localarg;
01325     char * s;
01326     char c;
01327     int brackets;
01328     int rc;
01329  
01330     if (arg == NULL || strlen(arg) == 0) return 1;
01331 
01332     /* did they give us just a name? */
01333     rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches);
01334     if (rc != 1) return rc;
01335 
01336     /*@-unqualifiedtrans@*/
01337     *matches = dbiFreeIndexSet(*matches);
01338     /*@=unqualifiedtrans@*/
01339 
01340     /* maybe a name and a release */
01341     localarg = alloca(strlen(arg) + 1);
01342     s = stpcpy(localarg, arg);
01343 
01344     c = '\0';
01345     brackets = 0;
01346     for (s -= 1; s > localarg; s--) {
01347         switch (*s) {
01348         case '[':       brackets = 1;                   break;
01349         case ']':       if (c != '[') brackets = 0;     break;
01350         }
01351         c = *s;
01352         if (!brackets && *s == '-')
01353             break;
01354     }
01355 
01356     /*@-nullstate@*/    /* FIX: *matches may be NULL. */
01357     if (s == localarg) return 1;
01358 
01359     *s = '\0';
01360     rc = dbiFindMatches(dbi, dbcursor, localarg, s + 1, NULL, matches);
01361     if (rc != 1) return rc;
01362 
01363     /*@-unqualifiedtrans@*/
01364     *matches = dbiFreeIndexSet(*matches);
01365     /*@=unqualifiedtrans@*/
01366     
01367     /* how about name-version-release? */
01368 
01369     release = s + 1;
01370 
01371     c = '\0';
01372     brackets = 0;
01373     for (; s > localarg; s--) {
01374         switch (*s) {
01375         case '[':       brackets = 1;                   break;
01376         case ']':       if (c != '[') brackets = 0;     break;
01377         }
01378         c = *s;
01379         if (!brackets && *s == '-')
01380             break;
01381     }
01382 
01383     if (s == localarg) return 1;
01384 
01385     *s = '\0';
01386     return dbiFindMatches(dbi, dbcursor, localarg, s + 1, release, matches);
01387     /*@=nullstate@*/
01388 }
01389 
01400 static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h)
01401         /*@modifies *dbcursor, h, fileSystem @*/
01402 {
01403     sigset_t signalMask;
01404     void * uh;
01405     size_t uhlen;
01406     int rc = EINVAL;    /* XXX W2DO? */
01407     unsigned int pflags = 0;    /* dbiPut() flags */
01408     int xx;
01409 
01410     if (_noDirTokens)
01411         expandFilelist(h);
01412 
01413     uhlen = headerSizeof(h, HEADER_MAGIC_NO);
01414     uh = headerUnload(h);
01415     if (uh) {
01416         (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
01417         rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, pflags);
01418         xx = dbiSync(dbi, 0);
01419         (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
01420         uh = _free(uh);
01421     }
01422     return rc;
01423 }
01424 
01425 typedef struct miRE_s {
01426     rpmTag              tag;            
01427     rpmMireMode         mode;           
01428 /*@only@*/ const char * pattern;        
01429     int                 notmatch;       
01430 /*@only@*/ regex_t *    preg;           
01431     int                 cflags;         
01432     int                 eflags;         
01433     int                 fnflags;        
01434 } * miRE;
01435 
01436 struct _rpmdbMatchIterator {
01437 /*@only@*/ const void * mi_keyp;
01438     size_t              mi_keylen;
01439 /*@kept@*/ rpmdb        mi_rpmdb;
01440     int                 mi_rpmtag;
01441     dbiIndexSet         mi_set;
01442     DBC *               mi_dbc;
01443     unsigned int        mi_ndups;
01444     int                 mi_setx;
01445 /*@null@*/ Header       mi_h;
01446     int                 mi_sorted;
01447     int                 mi_cflags;
01448     int                 mi_modified;
01449     unsigned int        mi_prevoffset;
01450     unsigned int        mi_offset;
01451     unsigned int        mi_filenum;
01452     unsigned int        mi_fpnum;
01453     unsigned int        mi_dbnum;
01454     int                 mi_nre;
01455 /*@only@*//*@null@*/ miRE mi_re;
01456 /*@only@*//*@null@*/ const char * mi_version;
01457 /*@only@*//*@null@*/ const char * mi_release;
01458 };
01459 
01460 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
01461 {
01462     dbiIndex dbi = NULL;
01463     int xx;
01464     int i;
01465 
01466     if (mi == NULL)
01467         return mi;
01468 
01469     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01470     if (mi->mi_h) {
01471         if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
01472             xx = dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
01473         }
01474         mi->mi_h = headerFree(mi->mi_h);
01475     }
01476     if (dbi) {
01477         if (dbi->dbi_rmw)
01478             xx = dbiCclose(dbi, dbi->dbi_rmw, 0);
01479         dbi->dbi_rmw = NULL;
01480     }
01481 
01482     if (mi->mi_re != NULL)
01483     for (i = 0; i < mi->mi_nre; i++) {
01484         miRE mire = mi->mi_re + i;
01485         mire->pattern = _free(mire->pattern);
01486         if (mire->preg != NULL) {
01487             regfree(mire->preg);
01488             /*@+voidabstract@*/
01489             mire->preg = _free(mire->preg);
01490             /*@=voidabstract@*/
01491         }
01492     }
01493     mi->mi_re = _free(mi->mi_re);
01494 
01495     mi->mi_release = _free(mi->mi_release);
01496     mi->mi_version = _free(mi->mi_version);
01497     if (dbi && mi->mi_dbc)
01498         xx = dbiCclose(dbi, mi->mi_dbc, DBI_ITERATOR);
01499     mi->mi_dbc = NULL;
01500     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
01501     mi->mi_keyp = _free(mi->mi_keyp);
01502     mi = _free(mi);
01503     return mi;
01504 }
01505 
01506 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
01507     if (mi == NULL)
01508         return NULL;
01509     /*@-retexpose -retalias@*/
01510     return mi->mi_rpmdb;
01511     /*@=retexpose =retalias@*/
01512 }
01513 
01514 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
01515     if (mi == NULL)
01516         return 0;
01517     return mi->mi_offset;
01518 }
01519 
01520 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
01521     if (mi == NULL)
01522         return 0;
01523     return mi->mi_filenum;
01524 }
01525 
01526 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
01527     if (!(mi && mi->mi_set))
01528         return 0;       /* XXX W2DO? */
01529     return mi->mi_set->count;
01530 }
01531 
01537 static int miregexec(miRE mire, const char * val)
01538         /*@*/
01539 {
01540     int rc = 0;
01541 
01542     switch (mire->mode) {
01543     case RPMMIRE_STRCMP:
01544         rc = strcmp(mire->pattern, val);
01545         break;
01546     case RPMMIRE_DEFAULT:
01547     case RPMMIRE_REGEX:
01548         rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
01549         if (rc && rc != REG_NOMATCH) {
01550             char msg[256];
01551             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
01552             msg[sizeof(msg)-1] = '\0';
01553             rpmError(RPMERR_REGEXEC, "%s: regexec failed: %s\n",
01554                         mire->pattern, msg);
01555             rc = -1;
01556         }
01557         break;
01558     case RPMMIRE_GLOB:
01559         rc = fnmatch(mire->pattern, val, mire->fnflags);
01560         if (rc && rc != FNM_NOMATCH)
01561             rc = -1;
01562         break;
01563     default:
01564         rc = -1;
01565         break;
01566     }
01567 
01568     return rc;
01569 }
01570 
01577 static int mireCmp(const void * a, const void * b)
01578 {
01579     const miRE mireA = (const miRE) a;
01580     const miRE mireB = (const miRE) b;
01581     return (mireA->tag - mireB->tag);
01582 }
01583 
01591 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
01592                         const char * pattern)
01593         /*@modifies *modep @*/
01594 {
01595     const char * s;
01596     char * pat;
01597     char * t;
01598     int brackets;
01599     size_t nb;
01600     int c;
01601 
01602     switch (*modep) {
01603     default:
01604     case RPMMIRE_DEFAULT:
01605         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
01606             *modep = RPMMIRE_GLOB;
01607             pat = xstrdup(pattern);
01608             break;
01609         }
01610 
01611         nb = strlen(pattern) + sizeof("^$");
01612 
01613         /* periods are escaped, splats become '.*' */
01614         c = '\0';
01615         brackets = 0;
01616         for (s = pattern; *s != '\0'; s++) {
01617             switch (*s) {
01618             case '.':
01619             case '*':   if (!brackets) nb++;            break;
01620             case '\\':  s++;                            break;
01621             case '[':   brackets = 1;                   break;
01622             case ']':   if (c != '[') brackets = 0;     break;
01623             }
01624             c = *s;
01625         }
01626 
01627         pat = t = xmalloc(nb);
01628 
01629         if (pattern[0] != '^') *t++ = '^';
01630 
01631         /* periods are escaped, splats become '.*' */
01632         c = '\0';
01633         brackets = 0;
01634         for (s = pattern; *s != '\0'; s++, t++) {
01635             switch (*s) {
01636             case '.':   if (!brackets) *t++ = '\\';     break;
01637             case '*':   if (!brackets) *t++ = '.';      break;
01638             case '\\':  *t++ = *s++;                    break;
01639             case '[':   brackets = 1;                   break;
01640             case ']':   if (c != '[') brackets = 0;     break;
01641             }
01642             c = *t = *s;
01643         }
01644 
01645         if (pattern[nb-1] != '$') *t++ = '$';
01646         *t = '\0';
01647         *modep = RPMMIRE_REGEX;
01648         break;
01649     case RPMMIRE_STRCMP:
01650     case RPMMIRE_REGEX:
01651     case RPMMIRE_GLOB:
01652         pat = xstrdup(pattern);
01653         break;
01654     }
01655 
01656     return pat;
01657 }
01658 
01659 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
01660                 rpmMireMode mode, const char * pattern)
01661 {
01662     static rpmMireMode defmode = (rpmMireMode)-1;
01663     miRE mire = NULL;
01664     const char * allpat = NULL;
01665     int notmatch = 0;
01666     regex_t * preg = NULL;
01667     int cflags = 0;
01668     int eflags = 0;
01669     int fnflags = 0;
01670     int rc = 0;
01671 
01672     if (defmode == (rpmMireMode)-1) {
01673         const char *t = rpmExpand("%{?_query_selector_match}", NULL);
01674         if (*t == '\0' || !strcmp(t, "default"))
01675             defmode = RPMMIRE_DEFAULT;
01676         else if (!strcmp(t, "strcmp"))
01677             defmode = RPMMIRE_STRCMP;
01678         else if (!strcmp(t, "regex"))
01679             defmode = RPMMIRE_REGEX;
01680         else if (!strcmp(t, "glob"))
01681             defmode = RPMMIRE_GLOB;
01682         else
01683             defmode = RPMMIRE_DEFAULT;
01684         t = _free(t);
01685      }
01686 
01687     if (mi == NULL || pattern == NULL)
01688         return rc;
01689 
01690     /* Leading '!' inverts pattern match sense, like "grep -v". */
01691     if (*pattern == '!') {
01692         notmatch = 1;
01693         pattern++;
01694     }
01695 
01696     /*@-mods@*/         /* FIX: WTFO? */
01697     allpat = mireDup(tag, &mode, pattern);
01698     /*@=mods@*/
01699 
01700     if (mode == RPMMIRE_DEFAULT)
01701         mode = defmode;
01702 
01703     switch (mode) {
01704     case RPMMIRE_DEFAULT:
01705     case RPMMIRE_STRCMP:
01706         break;
01707     case RPMMIRE_REGEX:
01708         preg = xcalloc(1, sizeof(*preg));
01709         cflags = (REG_EXTENDED | REG_NOSUB);
01710         rc = regcomp(preg, allpat, cflags);
01711         if (rc) {
01712             char msg[256];
01713             (void) regerror(rc, preg, msg, sizeof(msg)-1);
01714             msg[sizeof(msg)-1] = '\0';
01715             rpmError(RPMERR_REGCOMP, "%s: regcomp failed: %s\n", allpat, msg);
01716         }
01717         break;
01718     case RPMMIRE_GLOB:
01719         fnflags = FNM_PATHNAME | FNM_PERIOD;
01720         break;
01721     default:
01722         rc = -1;
01723         break;
01724     }
01725 
01726     if (rc) {
01727         /*@=kepttrans@*/        /* FIX: mire has kept values */
01728         allpat = _free(allpat);
01729         if (preg) {
01730             regfree(preg);
01731             /*@+voidabstract@*/
01732             preg = _free(preg);
01733             /*@=voidabstract@*/
01734         }
01735         /*@=kepttrans@*/
01736         return rc;
01737     }
01738 
01739     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
01740     mire = mi->mi_re + mi->mi_nre;
01741     mi->mi_nre++;
01742     
01743     mire->tag = tag;
01744     mire->mode = mode;
01745     mire->pattern = allpat;
01746     mire->notmatch = notmatch;
01747     mire->preg = preg;
01748     mire->cflags = cflags;
01749     mire->eflags = eflags;
01750     mire->fnflags = fnflags;
01751 
01752     (void) qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
01753 
01754     return rc;
01755 }
01756 
01762 static int mireSkip (const rpmdbMatchIterator mi)
01763         /*@*/
01764 {
01765 
01766     HGE_t hge = (HGE_t) headerGetEntryMinMemory;
01767     HFD_t hfd = (HFD_t) headerFreeData;
01768     union {
01769         void * ptr;
01770         const char ** argv;
01771         const char * str;
01772         int_32 * i32p;
01773         int_16 * i16p;
01774         int_8 * i8p;
01775     } u;
01776     char numbuf[32];
01777     rpmTagType t;
01778     int_32 c;
01779     miRE mire;
01780     int ntags = 0;
01781     int nmatches = 0;
01782     int i, j;
01783     int rc;
01784 
01785     if (mi->mi_h == NULL)       /* XXX can't happen */
01786         return 0;
01787 
01788     /*
01789      * Apply tag tests, implictly "||" for multiple patterns/values of a
01790      * single tag, implictly "&&" between multiple tag patterns.
01791      */
01792     if ((mire = mi->mi_re) != NULL)
01793     for (i = 0; i < mi->mi_nre; i++, mire++) {
01794         int anymatch;
01795 
01796         if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c))
01797             continue;
01798 
01799         anymatch = 0;           /* no matches yet */
01800         while (1) {
01801             switch (t) {
01802             case RPM_CHAR_TYPE:
01803             case RPM_INT8_TYPE:
01804                 sprintf(numbuf, "%d", (int) *u.i8p);
01805                 rc = miregexec(mire, numbuf);
01806                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01807                     anymatch++;
01808                 break;
01809             case RPM_INT16_TYPE:
01810                 sprintf(numbuf, "%d", (int) *u.i16p);
01811                 rc = miregexec(mire, numbuf);
01812                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01813                     anymatch++;
01814                 break;
01815             case RPM_INT32_TYPE:
01816                 sprintf(numbuf, "%d", (int) *u.i32p);
01817                 rc = miregexec(mire, numbuf);
01818                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01819                     anymatch++;
01820                 break;
01821             case RPM_STRING_TYPE:
01822                 rc = miregexec(mire, u.str);
01823                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01824                     anymatch++;
01825                 break;
01826             case RPM_I18NSTRING_TYPE:
01827             case RPM_STRING_ARRAY_TYPE:
01828                 for (j = 0; j < c; j++) {
01829                     rc = miregexec(mire, u.argv[j]);
01830                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) {
01831                         anymatch++;
01832                         /*@innerbreak@*/ break;
01833                     }
01834                 }
01835                 break;
01836             case RPM_NULL_TYPE:
01837             case RPM_BIN_TYPE:
01838             default:
01839                 break;
01840             }
01841             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
01842                 i++;
01843                 mire++;
01844                 continue;
01845             }
01846             /*@innerbreak@*/ break;
01847         }
01848 
01849         u.ptr = hfd(u.ptr, t);
01850 
01851         ntags++;
01852         if (anymatch)
01853             nmatches++;
01854     }
01855 
01856     return (ntags == nmatches ? 0 : 1);
01857 
01858 }
01859 
01860 int rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
01861     return rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
01862 }
01863 
01864 int rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
01865     return rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
01866 }
01867 
01868 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite) {
01869     int rc;
01870     if (mi == NULL)
01871         return 0;
01872     rc = (mi->mi_cflags & DBI_WRITECURSOR) ? 1 : 0;
01873     if (rewrite)
01874         mi->mi_cflags |= DBI_WRITECURSOR;
01875     else
01876         mi->mi_cflags &= ~DBI_WRITECURSOR;
01877     return rc;
01878 }
01879 
01880 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
01881     int rc;
01882     if (mi == NULL)
01883         return 0;
01884     rc = mi->mi_modified;
01885     mi->mi_modified = modified;
01886     return rc;
01887 }
01888 
01889 Header XrpmdbNextIterator(rpmdbMatchIterator mi,
01890                 /*@unused@*/ const char * f, /*@unused@*/ unsigned int l)
01891 {
01892     return rpmdbNextIterator(mi);
01893 }
01894 
01895 Header rpmdbNextIterator(rpmdbMatchIterator mi)
01896 {
01897     dbiIndex dbi;
01898     void * uh = NULL;
01899     size_t uhlen = 0;
01900     unsigned int gflags = 0;    /* dbiGet() flags */
01901     void * keyp;
01902     size_t keylen;
01903     int rc;
01904     int xx;
01905 
01906     if (mi == NULL)
01907         return NULL;
01908 
01909     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01910     if (dbi == NULL)
01911         return NULL;
01912 
01913     /*
01914      * Cursors are per-iterator, not per-dbi, so get a cursor for the
01915      * iterator on 1st call. If the iteration is to rewrite headers, and the
01916      * CDB model is used for the database, then the cursor needs to
01917      * marked with DB_WRITECURSOR as well.
01918      */
01919     if (mi->mi_dbc == NULL)
01920         xx = dbiCopen(dbi, &mi->mi_dbc, (mi->mi_cflags | DBI_ITERATOR));
01921     dbi->dbi_lastoffset = mi->mi_prevoffset;
01922 
01923 top:
01924     /* XXX skip over instances with 0 join key */
01925     do {
01926         if (mi->mi_set) {
01927             if (!(mi->mi_setx < mi->mi_set->count))
01928                 return NULL;
01929             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
01930             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
01931             keyp = &mi->mi_offset;
01932             keylen = sizeof(mi->mi_offset);
01933         } else {
01934             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
01935             keylen = mi->mi_keylen;
01936 
01937             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags);
01938 if (dbi->dbi_api == 1 && dbi->dbi_rpmtag == RPMDBI_PACKAGES && rc == EFAULT) {
01939     rpmError(RPMERR_INTERNAL,
01940         _("record number %u in database is bad -- skipping.\n"), dbi->dbi_lastoffset);
01941     if (keyp && dbi->dbi_lastoffset)
01942         memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01943     continue;
01944 }
01945 
01946             /*
01947              * If we got the next key, save the header instance number.
01948              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
01949              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
01950              * largest header instance in the database, and should be
01951              * skipped.
01952              */
01953             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
01954                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01955 
01956             /* Terminate on error or end of keys */
01957             if (rc || (mi->mi_setx && mi->mi_offset == 0))
01958                 return NULL;
01959         }
01960         mi->mi_setx++;
01961     } while (mi->mi_offset == 0);
01962 
01963     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
01964         goto exit;
01965 
01966     /* Retrieve next header */
01967     if (uh == NULL) {
01968         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags);
01969         if (rc)
01970             return NULL;
01971     }
01972 
01973     /* Free current header */
01974     if (mi->mi_h) {
01975         if (mi->mi_modified && mi->mi_prevoffset)
01976             (void)dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
01977         mi->mi_h = headerFree(mi->mi_h);
01978     }
01979 
01980     /* Is this the end of the iteration? */
01981     if (uh == NULL)
01982         goto exit;
01983 
01984     mi->mi_h = headerCopyLoad(uh);
01985     /* XXX db1 with hybrid, simulated db interface on falloc.c needs free. */
01986     if (dbi->dbi_api == 1) uh = _free(uh);
01987 
01988     /* Did the header load correctly? */
01989     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
01990         rpmError(RPMERR_BADHEADER,
01991                 _("rpmdb: damaged header instance #%u retrieved, skipping.\n"),
01992                 mi->mi_offset);
01993         goto top;
01994     }
01995 
01996     /*
01997      * Skip this header if iterator selector (if any) doesn't match.
01998      */
01999     if (mireSkip(mi)) {
02000         /* XXX hack, can't restart with Packages locked on single instance. */
02001         if (mi->mi_set || mi->mi_keyp == NULL)
02002             goto top;
02003         return NULL;
02004     }
02005 
02006     mi->mi_prevoffset = mi->mi_offset;
02007     mi->mi_modified = 0;
02008 
02009 exit:
02010 #ifdef  NOTNOW
02011     if (mi->mi_h) {
02012         const char *n, *v, *r;
02013         (void) headerNVR(mi->mi_h, &n, &v, &r);
02014         rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r,
02015                 mi->mi_offset, mi->mi_h);
02016     }
02017 #endif
02018     /*@-retexpose -retalias@*/
02019     /*@-compdef -usereleased@*/ return mi->mi_h; /*@=compdef =usereleased@*/
02020     /*@=retexpose =retalias@*/
02021 }
02022 
02023 static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi)
02024         /*@modifies mi @*/
02025 {
02026     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
02027         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
02028                 hdrNumCmp);
02029         mi->mi_sorted = 1;
02030     }
02031 }
02032 
02033 static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi,
02034                 const void * keyp, size_t keylen, int fpNum)
02035         /*@modifies mi, fileSystem @*/
02036 {
02037     dbiIndex dbi = NULL;
02038     DBC * dbcursor = NULL;
02039     dbiIndexSet set = NULL;
02040     int rc;
02041     int xx;
02042 
02043     if (!(mi && keyp))
02044         return 1;
02045 
02046     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
02047     if (dbi == NULL)
02048         return 1;
02049 
02050     if (keylen == 0)
02051         keylen = strlen(keyp);
02052 
02053     xx = dbiCopen(dbi, &dbcursor, 0);
02054     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02055     xx = dbiCclose(dbi, dbcursor, 0);
02056     dbcursor = NULL;
02057 
02058     if (rc == 0) {      /* success */
02059         int i;
02060         for (i = 0; i < set->count; i++)
02061             set->recs[i].fpNum = fpNum;
02062 
02063         if (mi->mi_set == NULL) {
02064             mi->mi_set = set;
02065             set = NULL;
02066         } else {
02067             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
02068                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
02069             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
02070                 set->count * sizeof(*(mi->mi_set->recs)));
02071             mi->mi_set->count += set->count;
02072         }
02073     }
02074 
02075     set = dbiFreeIndexSet(set);
02076     return rc;
02077 }
02078 
02079 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
02080         int nHdrNums, int sorted)
02081 {
02082     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02083         return 1;
02084 
02085     if (mi->mi_set)
02086         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
02087     return 0;
02088 }
02089 
02090 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
02091 {
02092     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02093         return 1;
02094 
02095     if (mi->mi_set == NULL)
02096         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
02097     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
02098     return 0;
02099 }
02100 
02101 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
02102         const void * keyp, size_t keylen)
02103 {
02104     rpmdbMatchIterator mi = NULL;
02105     dbiIndexSet set = NULL;
02106     dbiIndex dbi;
02107     const void * mi_keyp = NULL;
02108     int isLabel = 0;
02109 
02110     if (rpmdb == NULL)
02111         return NULL;
02112     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
02113     switch (rpmtag) {
02114     case RPMDBI_LABEL:
02115         rpmtag = RPMTAG_NAME;
02116         isLabel = 1;
02117         break;
02118     }
02119 
02120     dbi = dbiOpen(rpmdb, rpmtag, 0);
02121     if (dbi == NULL)
02122         return NULL;
02123 
02124 #if 0
02125     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
02126     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
02127 #else
02128 if (dbi->dbi_rmw)
02129 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
02130 #endif
02131 
02132     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
02133 
02134     if (rpmtag != RPMDBI_PACKAGES && keyp) {
02135         DBC * dbcursor = NULL;
02136         int rc;
02137         int xx;
02138 
02139         if (isLabel) {
02140             /* XXX HACK to get rpmdbFindByLabel out of the API */
02141             xx = dbiCopen(dbi, &dbcursor, 0);
02142             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
02143             xx = dbiCclose(dbi, dbcursor, 0);
02144             dbcursor = NULL;
02145         } else if (rpmtag == RPMTAG_BASENAMES) {
02146             rc = rpmdbFindByFile(rpmdb, keyp, &set);
02147         } else {
02148             xx = dbiCopen(dbi, &dbcursor, 0);
02149             /*@-nullpass@*/     /* LCL: keyp != NULL here. */
02150             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02151             /*@=nullpass@*/
02152             xx = dbiCclose(dbi, dbcursor, 0);
02153             dbcursor = NULL;
02154         }
02155         if (rc) {       /* error/not found */
02156             set = dbiFreeIndexSet(set);
02157             return NULL;
02158         }
02159     }
02160 
02161     if (keyp) {
02162         char * k;
02163 
02164         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
02165             keylen = strlen(keyp);
02166         k = xmalloc(keylen + 1);
02167         memcpy(k, keyp, keylen);
02168         k[keylen] = '\0';       /* XXX for strings */
02169         mi_keyp = k;
02170     }
02171 
02172     mi = xcalloc(1, sizeof(*mi));
02173     mi->mi_keyp = mi_keyp;
02174     mi->mi_keylen = keylen;
02175 
02176     /*@-assignexpose@*/
02177     mi->mi_rpmdb = rpmdb;
02178     /*@=assignexpose@*/
02179     mi->mi_rpmtag = rpmtag;
02180 
02181     mi->mi_dbc = NULL;
02182     mi->mi_set = set;
02183     mi->mi_setx = 0;
02184     mi->mi_ndups = 0;
02185     mi->mi_h = NULL;
02186     mi->mi_sorted = 0;
02187     mi->mi_cflags = 0;
02188     mi->mi_modified = 0;
02189     mi->mi_prevoffset = 0;
02190     mi->mi_offset = 0;
02191     mi->mi_filenum = 0;
02192     mi->mi_fpnum = 0;
02193     mi->mi_dbnum = 0;
02194     mi->mi_nre = 0;
02195     mi->mi_re = NULL;
02196     mi->mi_version = NULL;
02197     mi->mi_release = NULL;
02198     return mi;
02199 }
02200 
02210 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor,
02211                 const void * keyp, size_t keylen, dbiIndexItem rec)
02212         /*@modifies *dbcursor, fileSystem @*/
02213 {
02214     dbiIndexSet set = NULL;
02215     int rc;
02216     
02217     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02218 
02219     if (rc < 0)                 /* not found */
02220         rc = 0;
02221     else if (rc > 0)            /* error */
02222         rc = 1;         /* error message already generated from dbindex.c */
02223     else {                      /* success */
02224         /*@-mods@*/     /* a single rec is not modified */
02225         rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
02226         /*@=mods@*/
02227         if (rc == 0 && dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
02228             rc = 1;
02229     }
02230 
02231     set = dbiFreeIndexSet(set);
02232 
02233     return rc;
02234 }
02235 
02236 /* XXX install.c uninstall.c */
02237 int rpmdbRemove(rpmdb rpmdb, /*@unused@*/ int rid, unsigned int hdrNum)
02238 {
02239     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02240     HFD_t hfd = headerFreeData;
02241     Header h;
02242     sigset_t signalMask;
02243 
02244     if (rpmdb == NULL)
02245         return 0;
02246 
02247     {   rpmdbMatchIterator mi;
02248         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
02249         h = rpmdbNextIterator(mi);
02250         if (h)
02251             h = headerLink(h);
02252         mi = rpmdbFreeIterator(mi);
02253     }
02254 
02255     if (h == NULL) {
02256         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
02257               "rpmdbRemove", hdrNum);
02258         return 1;
02259     }
02260 
02261 #ifdef  DYING
02262     /* Add remove transaction id to header. */
02263     if (rid != 0 && rid != -1) {
02264         int_32 tid = rid;
02265         (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
02266     }
02267 #endif
02268 
02269     {   const char *n, *v, *r;
02270         (void) headerNVR(h, &n, &v, &r);
02271         rpmMessage(RPMMESS_DEBUG, "  --- %10u %s-%s-%s\n", hdrNum, n, v, r);
02272     }
02273 
02274     (void) blockSignals(rpmdb, &signalMask);
02275 
02276     {   int dbix;
02277         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
02278 
02279         if (dbiTags != NULL)
02280         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
02281             dbiIndex dbi;
02282             DBC * dbcursor = NULL;
02283             const char *av[1];
02284             const char ** rpmvals = NULL;
02285             rpmTagType rpmtype = 0;
02286             int rpmcnt = 0;
02287             int rpmtag;
02288             int xx;
02289             int i;
02290 
02291             dbi = NULL;
02292             rpmtag = dbiTags[dbix];
02293 
02294             switch (rpmtag) {
02295             /* Filter out temporary databases */
02296             case RPMDBI_AVAILABLE:
02297             case RPMDBI_ADDED:
02298             case RPMDBI_REMOVED:
02299             case RPMDBI_DEPENDS:
02300                 continue;
02301                 /*@notreached@*/ break;
02302             case RPMDBI_PACKAGES:
02303               dbi = dbiOpen(rpmdb, rpmtag, 0);
02304               if (dbi != NULL) {
02305                 xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02306                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
02307                 xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02308                 dbcursor = NULL;
02309                 if (!dbi->dbi_no_dbsync)
02310                     xx = dbiSync(dbi, 0);
02311               }
02312                 continue;
02313                 /*@notreached@*/ break;
02314             }
02315         
02316             if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt))
02317                 continue;
02318 
02319           dbi = dbiOpen(rpmdb, rpmtag, 0);
02320           if (dbi != NULL) {
02321             xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02322 
02323             if (rpmtype == RPM_STRING_TYPE) {
02324 
02325                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
02326                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
02327 
02328                 /* XXX force uniform headerGetEntry return */
02329                 av[0] = (const char *) rpmvals;
02330                 rpmvals = av;
02331                 rpmcnt = 1;
02332             } else {
02333 
02334                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
02335                         rpmcnt, tagName(dbi->dbi_rpmtag));
02336 
02337             }
02338 
02339             for (i = 0; i < rpmcnt; i++) {
02340                 const void * valp;
02341                 size_t vallen;
02342 
02343                 /* Identify value pointer and length. */
02344                 switch (rpmtype) {
02345                 case RPM_CHAR_TYPE:
02346                 case RPM_INT8_TYPE:
02347                     vallen = sizeof(RPM_CHAR_TYPE);
02348                     valp = rpmvals + i;
02349                     break;
02350                 case RPM_INT16_TYPE:
02351                     vallen = sizeof(int_16);
02352                     valp = rpmvals + i;
02353                     break;
02354                 case RPM_INT32_TYPE:
02355                     vallen = sizeof(int_32);
02356                     valp = rpmvals + i;
02357                     break;
02358                 case RPM_BIN_TYPE:
02359                     vallen = rpmcnt;
02360                     valp = rpmvals;
02361                     rpmcnt = 1;         /* XXX break out of loop. */
02362                     break;
02363                 case RPM_STRING_TYPE:
02364                 case RPM_I18NSTRING_TYPE:
02365                     rpmcnt = 1;         /* XXX break out of loop. */
02366                     /*@fallthrough@*/
02367                 case RPM_STRING_ARRAY_TYPE:
02368                 default:
02369                     vallen = strlen(rpmvals[i]);
02370                     valp = rpmvals[i];
02371                     break;
02372                 }
02373 
02374                 /*
02375                  * This is almost right, but, if there are duplicate tag
02376                  * values, there will be duplicate attempts to remove
02377                  * the header instance. It's easier to just ignore errors
02378                  * than to do things correctly.
02379                  */
02380                 xx = removeIndexEntry(dbi, dbcursor, valp, vallen, rec);
02381             }
02382 
02383             xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02384             dbcursor = NULL;
02385 
02386             if (!dbi->dbi_no_dbsync)
02387                 xx = dbiSync(dbi, 0);
02388           }
02389 
02390             rpmvals = hfd(rpmvals, rpmtype);
02391             rpmtype = 0;
02392             rpmcnt = 0;
02393         }
02394 
02395         rec = _free(rec);
02396     }
02397 
02398     (void) unblockSignals(rpmdb, &signalMask);
02399 
02400     h = headerFree(h);
02401 
02402     return 0;
02403 }
02404 
02414 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor,
02415                 const char * keyp, size_t keylen, dbiIndexItem rec)
02416         /*@modifies *dbcursor, fileSystem @*/
02417 {
02418     dbiIndexSet set = NULL;
02419     int rc;
02420 
02421     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02422 
02423     if (rc > 0) {               /* error */
02424         rc = 1;
02425     } else {
02426 
02427         /* With duplicates, cursor is positioned, discard the record. */
02428         if (rc == 0 && dbi->dbi_permit_dups)
02429             set = dbiFreeIndexSet(set);
02430 
02431         if (set == NULL || rc < 0) {            /* not found */
02432             rc = 0;
02433             set = xcalloc(1, sizeof(*set));
02434         }
02435         (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
02436         if (dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
02437             rc = 1;
02438     }
02439     set = dbiFreeIndexSet(set);
02440 
02441     return 0;
02442 }
02443 
02444 /* XXX install.c */
02445 int rpmdbAdd(rpmdb rpmdb, int iid, Header h)
02446 {
02447     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02448     HFD_t hfd = headerFreeData;
02449     sigset_t signalMask;
02450     const char ** baseNames;
02451     rpmTagType bnt;
02452     int count = 0;
02453     dbiIndex dbi;
02454     int dbix;
02455     unsigned int gflags = 0;    /* dbiGet() flags */
02456     unsigned int pflags = 0;    /* dbiPut() flags */
02457     unsigned int hdrNum = 0;
02458     int rc = 0;
02459     int xx;
02460 
02461     if (rpmdb == NULL)
02462         return 0;
02463 
02464     if (iid != 0 && iid != -1) {
02465         int_32 tid = iid;
02466         (void) headerRemoveEntry(h, RPMTAG_REMOVETID);
02467         if (!headerIsEntry(h, RPMTAG_INSTALLTID))
02468            (void) headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
02469     }
02470 
02471     /*
02472      * If old style filename tags is requested, the basenames need to be
02473      * retrieved early, and the header needs to be converted before
02474      * being written to the package header database.
02475      */
02476 
02477     (void) hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count);
02478 
02479     if (_noDirTokens)
02480         expandFilelist(h);
02481 
02482     (void) blockSignals(rpmdb, &signalMask);
02483 
02484     {
02485         unsigned int firstkey = 0;
02486         DBC * dbcursor = NULL;
02487         void * keyp = &firstkey;
02488         size_t keylen = sizeof(firstkey);
02489         void * datap = NULL;
02490         size_t datalen = 0;
02491 
02492       dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
02493       if (dbi != NULL) {
02494 
02495         /* XXX db0: hack to pass sizeof header to fadAlloc */
02496         datap = h;
02497         datalen = headerSizeof(h, HEADER_MAGIC_NO);
02498 
02499         xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02500 
02501         /* Retrieve join key for next header instance. */
02502 
02503         rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, gflags);
02504 
02505         hdrNum = 0;
02506         if (rc == 0 && datap)
02507             memcpy(&hdrNum, datap, sizeof(hdrNum));
02508         ++hdrNum;
02509         if (rc == 0 && datap) {
02510             /*@-refcounttrans@*/        /* FIX: datap aliases h */
02511             memcpy(datap, &hdrNum, sizeof(hdrNum));
02512             /*@=refcounttrans@*/
02513         } else {
02514             datap = &hdrNum;
02515             datalen = sizeof(hdrNum);
02516         }
02517 
02518         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, pflags);
02519         xx = dbiSync(dbi, 0);
02520 
02521         xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02522         dbcursor = NULL;
02523       }
02524 
02525     }
02526 
02527     if (rc) {
02528         rpmError(RPMERR_DBCORRUPT,
02529                 _("error(%d) allocating new package instance\n"), rc);
02530         goto exit;
02531     }
02532 
02533     /* Now update the indexes */
02534 
02535     if (hdrNum)
02536     {   dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
02537 
02538         if (dbiTags != NULL)
02539         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
02540             DBC * dbcursor = NULL;
02541             const char *av[1];
02542             const char **rpmvals = NULL;
02543             rpmTagType rpmtype = 0;
02544             int rpmcnt = 0;
02545             int rpmtag;
02546             int_32 * requireFlags;
02547             int i, j;
02548 
02549             dbi = NULL;
02550             requireFlags = NULL;
02551             rpmtag = dbiTags[dbix];
02552 
02553             switch (rpmtag) {
02554             /* Filter out temporary databases */
02555             case RPMDBI_AVAILABLE:
02556             case RPMDBI_ADDED:
02557             case RPMDBI_REMOVED:
02558             case RPMDBI_DEPENDS:
02559                 continue;
02560                 /*@notreached@*/ break;
02561             case RPMDBI_PACKAGES:
02562               dbi = dbiOpen(rpmdb, rpmtag, 0);
02563               if (dbi != NULL) {
02564                 xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02565                 xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h);
02566                 xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02567                 dbcursor = NULL;
02568                 if (!dbi->dbi_no_dbsync)
02569                     xx = dbiSync(dbi, 0);
02570                 {   const char *n, *v, *r;
02571                     (void) headerNVR(h, &n, &v, &r);
02572                     rpmMessage(RPMMESS_DEBUG, "  +++ %10u %s-%s-%s\n", hdrNum, n, v, r);
02573                 }
02574               }
02575                 continue;
02576                 /*@notreached@*/ break;
02577             /* XXX preserve legacy behavior */
02578             case RPMTAG_BASENAMES:
02579                 rpmtype = bnt;
02580                 rpmvals = baseNames;
02581                 rpmcnt = count;
02582                 break;
02583             case RPMTAG_REQUIRENAME:
02584                 (void) hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
02585                 (void) hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL);
02586                 break;
02587             default:
02588                 (void) hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
02589                 break;
02590             }
02591 
02592             if (rpmcnt <= 0) {
02593                 if (rpmtag != RPMTAG_GROUP)
02594                     continue;
02595 
02596                 /* XXX preserve legacy behavior */
02597                 rpmtype = RPM_STRING_TYPE;
02598                 rpmvals = (const char **) "Unknown";
02599                 rpmcnt = 1;
02600             }
02601 
02602           dbi = dbiOpen(rpmdb, rpmtag, 0);
02603           if (dbi != NULL) {
02604 
02605             xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02606             if (rpmtype == RPM_STRING_TYPE) {
02607                 rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), 
02608                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
02609 
02610                 /* XXX force uniform headerGetEntry return */
02611                 /*@-observertrans@*/
02612                 av[0] = (const char *) rpmvals;
02613                 /*@=observertrans@*/
02614                 rpmvals = av;
02615                 rpmcnt = 1;
02616             } else {
02617 
02618                 rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), 
02619                         rpmcnt, tagName(dbi->dbi_rpmtag));
02620 
02621             }
02622 
02623             for (i = 0; i < rpmcnt; i++) {
02624                 const void * valp;
02625                 size_t vallen;
02626 
02627                 /*
02628                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
02629                  * included the tagNum only for files.
02630                  */
02631                 switch (dbi->dbi_rpmtag) {
02632                 case RPMTAG_REQUIRENAME:
02633                     /* Filter out install prerequisites. */
02634                     if (requireFlags && isInstallPreReq(requireFlags[i]))
02635                         continue;
02636                     rec->tagNum = i;
02637                     break;
02638                 case RPMTAG_TRIGGERNAME:
02639                     if (i) {    /* don't add duplicates */
02640                         for (j = 0; j < i; j++) {
02641                             if (!strcmp(rpmvals[i], rpmvals[j]))
02642                                 /*@innerbreak@*/ break;
02643                         }
02644                         if (j < i)
02645                             continue;
02646                     }
02647                     rec->tagNum = i;
02648                     break;
02649                 default:
02650                     rec->tagNum = i;
02651                     break;
02652                 }
02653 
02654                 /* Identify value pointer and length. */
02655                 switch (rpmtype) {
02656                 case RPM_CHAR_TYPE:
02657                 case RPM_INT8_TYPE:
02658                     vallen = sizeof(int_8);
02659                     valp = rpmvals + i;
02660                     break;
02661                 case RPM_INT16_TYPE:
02662                     vallen = sizeof(int_16);
02663                     valp = rpmvals + i;
02664                     break;
02665                 case RPM_INT32_TYPE:
02666                     vallen = sizeof(int_32);
02667                     valp = rpmvals + i;
02668                     break;
02669                 case RPM_BIN_TYPE:
02670                     vallen = rpmcnt;
02671                     valp = rpmvals;
02672                     rpmcnt = 1;         /* XXX break out of loop. */
02673                     break;
02674                 case RPM_STRING_TYPE:
02675                 case RPM_I18NSTRING_TYPE:
02676                     rpmcnt = 1;         /* XXX break out of loop. */
02677                     /*@fallthrough@*/
02678                 case RPM_STRING_ARRAY_TYPE:
02679                 default:
02680                     valp = rpmvals[i];
02681                     vallen = strlen(rpmvals[i]);
02682                     break;
02683                 }
02684 
02685                 rc += addIndexEntry(dbi, dbcursor, valp, vallen, rec);
02686             }
02687             xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02688             dbcursor = NULL;
02689 
02690             if (!dbi->dbi_no_dbsync)
02691                 xx = dbiSync(dbi, 0);
02692           }
02693 
02694         /*@-observertrans@*/
02695             rpmvals = hfd(rpmvals, rpmtype);
02696         /*@=observertrans@*/
02697             rpmtype = 0;
02698             rpmcnt = 0;
02699         }
02700 
02701         rec = _free(rec);
02702     }
02703 
02704 exit:
02705     (void) unblockSignals(rpmdb, &signalMask);
02706 
02707     return rc;
02708 }
02709 
02710 /* XXX transaction.c */
02711 int rpmdbFindFpList(rpmdb rpmdb, fingerPrint * fpList, dbiIndexSet * matchList, 
02712                     int numItems)
02713 {
02714     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02715     HFD_t hfd = headerFreeData;
02716     rpmdbMatchIterator mi;
02717     fingerPrintCache fpc;
02718     Header h;
02719     int i;
02720 
02721     if (rpmdb == NULL) return 0;
02722 
02723     mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, NULL, 0);
02724 
02725     /* Gather all matches from the database */
02726     for (i = 0; i < numItems; i++) {
02727         (void) rpmdbGrowIterator(mi, fpList[i].baseName, 0, i);
02728         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
02729     }
02730 
02731     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
02732         mi = rpmdbFreeIterator(mi);
02733         return 0;
02734     }
02735     fpc = fpCacheCreate(i);
02736 
02737     rpmdbSortIterator(mi);
02738     /* iterator is now sorted by (recnum, filenum) */
02739 
02740     /* For each set of files matched in a package ... */
02741     if (mi != NULL)
02742     while ((h = rpmdbNextIterator(mi)) != NULL) {
02743         const char ** dirNames;
02744         const char ** baseNames;
02745         const char ** fullBaseNames;
02746         rpmTagType bnt, dnt;
02747         int_32 * dirIndexes;
02748         int_32 * fullDirIndexes;
02749         fingerPrint * fps;
02750         dbiIndexItem im;
02751         int start;
02752         int num;
02753         int end;
02754 
02755         start = mi->mi_setx - 1;
02756         im = mi->mi_set->recs + start;
02757 
02758         /* Find the end of the set of matched files in this package. */
02759         for (end = start + 1; end < mi->mi_set->count; end++) {
02760             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
02761                 /*@innerbreak@*/ break;
02762         }
02763         num = end - start;
02764 
02765         /* Compute fingerprints for this header's matches */
02766         (void) hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL);
02767         (void) hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
02768         (void) hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL);
02769 
02770         baseNames = xcalloc(num, sizeof(*baseNames));
02771         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
02772         for (i = 0; i < num; i++) {
02773             baseNames[i] = fullBaseNames[im[i].tagNum];
02774             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
02775         }
02776 
02777         fps = xcalloc(num, sizeof(*fps));
02778         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
02779 
02780         /* Add db (recnum,filenum) to list for fingerprint matches. */
02781         for (i = 0; i < num; i++, im++) {
02782             /*@-nullpass@*/
02783             if (FP_EQUAL(fps[i], fpList[im->fpNum])) {
02784             /*@=nullpass@*/
02785                 /*@-usedef@*/
02786                 (void) dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
02787                 /*@=usedef@*/
02788             }
02789         }
02790 
02791         fps = _free(fps);
02792         dirNames = hfd(dirNames, dnt);
02793         fullBaseNames = hfd(fullBaseNames, bnt);
02794         baseNames = _free(baseNames);
02795         dirIndexes = _free(dirIndexes);
02796 
02797         mi->mi_setx = end;
02798     }
02799 
02800     mi = rpmdbFreeIterator(mi);
02801 
02802     fpCacheFree(fpc);
02803 
02804     return 0;
02805 
02806 }
02807 
02808 char * db1basename (int rpmtag)
02809 {
02810     char * base = NULL;
02811     switch (rpmtag) {
02812     case RPMDBI_PACKAGES:       base = "packages.rpm";          break;
02813     case RPMTAG_NAME:           base = "nameindex.rpm";         break;
02814     case RPMTAG_BASENAMES:      base = "fileindex.rpm";         break;
02815     case RPMTAG_GROUP:          base = "groupindex.rpm";        break;
02816     case RPMTAG_REQUIRENAME:    base = "requiredby.rpm";        break;
02817     case RPMTAG_PROVIDENAME:    base = "providesindex.rpm";     break;
02818     case RPMTAG_CONFLICTNAME:   base = "conflictsindex.rpm";    break;
02819     case RPMTAG_TRIGGERNAME:    base = "triggerindex.rpm";      break;
02820     default:
02821       { const char * tn = tagName(rpmtag);
02822         base = alloca( strlen(tn) + sizeof(".idx") + 1 );
02823         (void) stpcpy( stpcpy(base, tn), ".idx");
02824       } break;
02825     }
02826     return xstrdup(base);
02827 }
02828 
02829 static int rpmdbRemoveDatabase(const char * rootdir,
02830                 const char * dbpath, int _dbapi)
02831         /*@modifies fileSystem @*/
02832 { 
02833     int i;
02834     char * filename;
02835     int xx;
02836 
02837     i = strlen(dbpath);
02838     if (dbpath[i - 1] != '/') {
02839         filename = alloca(i);
02840         strcpy(filename, dbpath);
02841         filename[i] = '/';
02842         filename[i + 1] = '\0';
02843         dbpath = filename;
02844     }
02845     
02846     filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
02847 
02848     switch (_dbapi) {
02849     case 3:
02850         if (dbiTags != NULL)
02851         for (i = 0; i < dbiTagsMax; i++) {
02852             const char * base = tagName(dbiTags[i]);
02853             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02854             (void)rpmCleanPath(filename);
02855             if (!rpmfileexists(filename))
02856                 continue;
02857             xx = unlink(filename);
02858         }
02859         for (i = 0; i < 16; i++) {
02860             sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
02861             (void)rpmCleanPath(filename);
02862             if (!rpmfileexists(filename))
02863                 continue;
02864             xx = unlink(filename);
02865         }
02866         break;
02867     case 2:
02868     case 1:
02869     case 0:
02870         if (dbiTags != NULL)
02871         for (i = 0; i < dbiTagsMax; i++) {
02872             const char * base = db1basename(dbiTags[i]);
02873             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02874             (void)rpmCleanPath(filename);
02875             if (!rpmfileexists(filename))
02876                 continue;
02877             xx = unlink(filename);
02878             base = _free(base);
02879         }
02880         break;
02881     }
02882 
02883     sprintf(filename, "%s/%s", rootdir, dbpath);
02884     (void)rpmCleanPath(filename);
02885     xx = rmdir(filename);
02886 
02887     return 0;
02888 }
02889 
02890 static int rpmdbMoveDatabase(const char * rootdir,
02891                 const char * olddbpath, int _olddbapi,
02892                 const char * newdbpath, int _newdbapi)
02893         /*@modifies fileSystem @*/
02894 {
02895     int i;
02896     char * ofilename, * nfilename;
02897     int rc = 0;
02898     int xx;
02899  
02900     i = strlen(olddbpath);
02901     if (olddbpath[i - 1] != '/') {
02902         ofilename = alloca(i + 2);
02903         strcpy(ofilename, olddbpath);
02904         ofilename[i] = '/';
02905         ofilename[i + 1] = '\0';
02906         olddbpath = ofilename;
02907     }
02908     
02909     i = strlen(newdbpath);
02910     if (newdbpath[i - 1] != '/') {
02911         nfilename = alloca(i + 2);
02912         strcpy(nfilename, newdbpath);
02913         nfilename[i] = '/';
02914         nfilename[i + 1] = '\0';
02915         newdbpath = nfilename;
02916     }
02917     
02918     ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
02919     nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
02920 
02921     switch (_olddbapi) {
02922     case 3:
02923         if (dbiTags != NULL)
02924         for (i = 0; i < dbiTagsMax; i++) {
02925             const char * base;
02926             int rpmtag;
02927 
02928             /* Filter out temporary databases */
02929             switch ((rpmtag = dbiTags[i])) {
02930             case RPMDBI_AVAILABLE:
02931             case RPMDBI_ADDED:
02932             case RPMDBI_REMOVED:
02933             case RPMDBI_DEPENDS:
02934                 continue;
02935                 /*@notreached@*/ break;
02936             default:
02937                 break;
02938             }
02939 
02940             base = tagName(rpmtag);
02941             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02942             (void)rpmCleanPath(ofilename);
02943             if (!rpmfileexists(ofilename))
02944                 continue;
02945             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02946             (void)rpmCleanPath(nfilename);
02947             if ((xx = Rename(ofilename, nfilename)) != 0)
02948                 rc = 1;
02949         }
02950         for (i = 0; i < 16; i++) {
02951             sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
02952             (void)rpmCleanPath(ofilename);
02953             if (!rpmfileexists(ofilename))
02954                 continue;
02955             xx = unlink(ofilename);
02956             sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
02957             (void)rpmCleanPath(nfilename);
02958 #ifdef  DYING
02959             if ((xx = Rename(ofilename, nfilename)) != 0)
02960                 rc = 1;
02961 #else
02962             xx = unlink(nfilename);
02963 #endif
02964         }
02965         break;
02966     case 2:
02967     case 1:
02968     case 0:
02969         if (dbiTags != NULL)
02970         for (i = 0; i < dbiTagsMax; i++) {
02971             const char * base;
02972             int rpmtag;
02973 
02974             /* Filter out temporary databases */
02975             switch ((rpmtag = dbiTags[i])) {
02976             case RPMDBI_AVAILABLE:
02977             case RPMDBI_ADDED:
02978             case RPMDBI_REMOVED:
02979             case RPMDBI_DEPENDS:
02980                 continue;
02981                 /*@notreached@*/ break;
02982             default:
02983                 break;
02984             }
02985 
02986             base = db1basename(rpmtag);
02987             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02988             (void)rpmCleanPath(ofilename);
02989             if (!rpmfileexists(ofilename))
02990                 continue;
02991             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02992             (void)rpmCleanPath(nfilename);
02993             if ((xx = Rename(ofilename, nfilename)) != 0)
02994                 rc = 1;
02995             base = _free(base);
02996         }
02997         break;
02998     }
02999     if (rc || _olddbapi == _newdbapi)
03000         return rc;
03001 
03002     rc = rpmdbRemoveDatabase(rootdir, newdbpath, _newdbapi);
03003 
03004 
03005     /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
03006     if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
03007         const char * mdb1 = "/etc/rpm/macros.db1";
03008         struct stat st;
03009         if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
03010             rpmMessage(RPMMESS_DEBUG,
03011                 _("removing %s after successful db3 rebuild.\n"), mdb1);
03012     }
03013     return rc;
03014 }
03015 
03016 int rpmdbRebuild(const char * rootdir)
03017 {
03018     rpmdb olddb;
03019     const char * dbpath = NULL;
03020     const char * rootdbpath = NULL;
03021     rpmdb newdb;
03022     const char * newdbpath = NULL;
03023     const char * newrootdbpath = NULL;
03024     const char * tfn;
03025     int nocleanup = 1;
03026     int failed = 0;
03027     int removedir = 0;
03028     int rc = 0, xx;
03029     int _dbapi;
03030     int _dbapi_rebuild;
03031 
03032     if (rootdir == NULL) rootdir = "/";
03033 
03034     _dbapi = rpmExpandNumeric("%{_dbapi}");
03035     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
03036 
03037     /*@-nullpass@*/
03038     tfn = rpmGetPath("%{_dbpath}", NULL);
03039     /*@=nullpass@*/
03040     if (!(tfn && tfn[0] != '%')) {
03041         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
03042         rc = 1;
03043         goto exit;
03044     }
03045     dbpath = rootdbpath = rpmGetPath(rootdir, tfn, NULL);
03046     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
03047         dbpath += strlen(rootdir);
03048     tfn = _free(tfn);
03049 
03050     /*@-nullpass@*/
03051     tfn = rpmGetPath("%{_dbpath_rebuild}", NULL);
03052     /*@=nullpass@*/
03053     if (!(tfn && tfn[0] != '%' && strcmp(tfn, dbpath))) {
03054         char pidbuf[20];
03055         char *t;
03056         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
03057         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
03058         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
03059         tfn = _free(tfn);
03060         tfn = t;
03061         nocleanup = 0;
03062     }
03063     newdbpath = newrootdbpath = rpmGetPath(rootdir, tfn, NULL);
03064     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
03065         newdbpath += strlen(rootdir);
03066     tfn = _free(tfn);
03067 
03068     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
03069         rootdbpath, newrootdbpath);
03070 
03071     if (!access(newrootdbpath, F_OK)) {
03072         rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
03073               newrootdbpath);
03074         rc = 1;
03075         goto exit;
03076     }
03077 
03078     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
03079     if (Mkdir(newrootdbpath, 0755)) {
03080         rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
03081               newrootdbpath, strerror(errno));
03082         rc = 1;
03083         goto exit;
03084     }
03085     removedir = 1;
03086 
03087     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
03088                 _dbapi);
03089     _rebuildinprogress = 1;
03090     if (openDatabase(rootdir, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
03091                      RPMDB_FLAG_MINIMAL)) {
03092         rc = 1;
03093         goto exit;
03094     }
03095     _dbapi = olddb->db_api;
03096     _rebuildinprogress = 0;
03097 
03098     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
03099                 _dbapi_rebuild);
03100     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
03101     if (openDatabase(rootdir, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
03102         rc = 1;
03103         goto exit;
03104     }
03105     _dbapi_rebuild = newdb->db_api;
03106     
03107     {   Header h = NULL;
03108         rpmdbMatchIterator mi;
03109 #define _RECNUM rpmdbGetIteratorOffset(mi)
03110 
03111         /* RPMDBI_PACKAGES */
03112         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
03113         while ((h = rpmdbNextIterator(mi)) != NULL) {
03114 
03115             /* let's sanity check this record a bit, otherwise just skip it */
03116             if (!(headerIsEntry(h, RPMTAG_NAME) &&
03117                 headerIsEntry(h, RPMTAG_VERSION) &&
03118                 headerIsEntry(h, RPMTAG_RELEASE) &&
03119                 headerIsEntry(h, RPMTAG_BUILDTIME)))
03120             {
03121                 rpmError(RPMERR_INTERNAL,
03122                         _("record number %u in database is bad -- skipping.\n"),
03123                         _RECNUM);
03124                 continue;
03125             }
03126 
03127             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
03128             if (_db_filter_dups || newdb->db_filter_dups) {
03129                 const char * name, * version, * release;
03130                 int skip = 0;
03131 
03132                 (void) headerNVR(h, &name, &version, &release);
03133 
03134                 /*@-shadow@*/
03135                 {   rpmdbMatchIterator mi;
03136                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
03137                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
03138                                 RPMMIRE_DEFAULT, version);
03139                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
03140                                 RPMMIRE_DEFAULT, release);
03141                     while (rpmdbNextIterator(mi)) {
03142                         skip = 1;
03143                         /*@innerbreak@*/ break;
03144                     }
03145                     mi = rpmdbFreeIterator(mi);
03146                 }
03147                 /*@=shadow@*/
03148 
03149                 if (skip)
03150                     continue;
03151             }
03152 
03153             /* Deleted entries are eliminated in legacy headers by copy. */
03154             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
03155                                 ? headerCopy(h) : NULL);
03156                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h));
03157                 nh = headerFree(nh);
03158             }
03159 
03160             if (rc) {
03161                 rpmError(RPMERR_INTERNAL,
03162                         _("cannot add record originally at %u\n"), _RECNUM);
03163                 failed = 1;
03164                 break;
03165             }
03166         }
03167 
03168         mi = rpmdbFreeIterator(mi);
03169 
03170     }
03171 
03172     if (!nocleanup) {
03173         olddb->db_remove_env = 1;
03174         newdb->db_remove_env = 1;
03175     }
03176     xx = rpmdbClose(olddb);
03177     xx = rpmdbClose(newdb);
03178 
03179     if (failed) {
03180         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
03181                 "remains in place\n"));
03182 
03183         xx = rpmdbRemoveDatabase(rootdir, newdbpath, _dbapi_rebuild);
03184         rc = 1;
03185         goto exit;
03186     } else if (!nocleanup) {
03187         if (rpmdbMoveDatabase(rootdir, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
03188             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
03189                         "database!\n"));
03190             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
03191                         "to recover"), dbpath, newdbpath);
03192             rc = 1;
03193             goto exit;
03194         }
03195     }
03196     rc = 0;
03197 
03198 exit:
03199     if (removedir && !(rc == 0 && nocleanup)) {
03200         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
03201         if (Rmdir(newrootdbpath))
03202             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
03203                         newrootdbpath, strerror(errno));
03204     }
03205     newrootdbpath = _free(newrootdbpath);
03206     rootdbpath = _free(rootdbpath);
03207 
03208     return rc;
03209 }

Generated on Wed Mar 13 15:34:50 2002 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002