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             mire->preg = _free(mire->preg);
01489         }
01490     }
01491     mi->mi_re = _free(mi->mi_re);
01492 
01493     mi->mi_release = _free(mi->mi_release);
01494     mi->mi_version = _free(mi->mi_version);
01495     if (dbi && mi->mi_dbc)
01496         xx = dbiCclose(dbi, mi->mi_dbc, DBI_ITERATOR);
01497     mi->mi_dbc = NULL;
01498     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
01499     mi->mi_keyp = _free(mi->mi_keyp);
01500     mi = _free(mi);
01501     return mi;
01502 }
01503 
01504 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
01505     if (mi == NULL)
01506         return NULL;
01507     /*@-retexpose -retalias@*/
01508     return mi->mi_rpmdb;
01509     /*@=retexpose =retalias@*/
01510 }
01511 
01512 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
01513     if (mi == NULL)
01514         return 0;
01515     return mi->mi_offset;
01516 }
01517 
01518 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
01519     if (mi == NULL)
01520         return 0;
01521     return mi->mi_filenum;
01522 }
01523 
01524 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
01525     if (!(mi && mi->mi_set))
01526         return 0;       /* XXX W2DO? */
01527     return mi->mi_set->count;
01528 }
01529 
01535 static int miregexec(miRE mire, const char * val)
01536         /*@*/
01537 {
01538     int rc = 0;
01539 
01540     switch (mire->mode) {
01541     case RPMMIRE_STRCMP:
01542         rc = strcmp(mire->pattern, val);
01543         break;
01544     case RPMMIRE_DEFAULT:
01545     case RPMMIRE_REGEX:
01546         rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
01547         if (rc && rc != REG_NOMATCH) {
01548             char msg[256];
01549             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
01550             msg[sizeof(msg)-1] = '\0';
01551             rpmError(RPMERR_REGEXEC, "%s: regexec failed: %s\n",
01552                         mire->pattern, msg);
01553             rc = -1;
01554         }
01555         break;
01556     case RPMMIRE_GLOB:
01557         rc = fnmatch(mire->pattern, val, mire->fnflags);
01558         if (rc && rc != FNM_NOMATCH)
01559             rc = -1;
01560         break;
01561     default:
01562         rc = -1;
01563         break;
01564     }
01565 
01566     return rc;
01567 }
01568 
01575 static int mireCmp(const void * a, const void * b)
01576 {
01577     const miRE mireA = (const miRE) a;
01578     const miRE mireB = (const miRE) b;
01579     return (mireA->tag - mireB->tag);
01580 }
01581 
01589 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
01590                         const char * pattern)
01591         /*@modifies *modep @*/
01592 {
01593     const char * s;
01594     char * pat;
01595     char * t;
01596     int brackets;
01597     size_t nb;
01598     int c;
01599 
01600     switch (*modep) {
01601     default:
01602     case RPMMIRE_DEFAULT:
01603         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
01604             *modep = RPMMIRE_GLOB;
01605             pat = xstrdup(pattern);
01606             break;
01607         }
01608 
01609         nb = strlen(pattern) + sizeof("^$");
01610 
01611         /* periods are escaped, splats become '.*' */
01612         c = '\0';
01613         brackets = 0;
01614         for (s = pattern; *s != '\0'; s++) {
01615             switch (*s) {
01616             case '.':
01617             case '*':   if (!brackets) nb++;            break;
01618             case '\\':  s++;                            break;
01619             case '[':   brackets = 1;                   break;
01620             case ']':   if (c != '[') brackets = 0;     break;
01621             }
01622             c = *s;
01623         }
01624 
01625         pat = t = xmalloc(nb);
01626 
01627         if (pattern[0] != '^') *t++ = '^';
01628 
01629         /* periods are escaped, splats become '.*' */
01630         c = '\0';
01631         brackets = 0;
01632         for (s = pattern; *s != '\0'; s++, t++) {
01633             switch (*s) {
01634             case '.':   if (!brackets) *t++ = '\\';     break;
01635             case '*':   if (!brackets) *t++ = '.';      break;
01636             case '\\':  *t++ = *s++;                    break;
01637             case '[':   brackets = 1;                   break;
01638             case ']':   if (c != '[') brackets = 0;     break;
01639             }
01640             c = *t = *s;
01641         }
01642 
01643         if (pattern[nb-1] != '$') *t++ = '$';
01644         *t = '\0';
01645         *modep = RPMMIRE_REGEX;
01646         break;
01647     case RPMMIRE_STRCMP:
01648     case RPMMIRE_REGEX:
01649     case RPMMIRE_GLOB:
01650         pat = xstrdup(pattern);
01651         break;
01652     }
01653 
01654     return pat;
01655 }
01656 
01657 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
01658                 rpmMireMode mode, const char * pattern)
01659 {
01660     static rpmMireMode defmode = (rpmMireMode)-1;
01661     miRE mire = NULL;
01662     const char * allpat = NULL;
01663     int notmatch = 0;
01664     regex_t * preg = NULL;
01665     int cflags = 0;
01666     int eflags = 0;
01667     int fnflags = 0;
01668     int rc = 0;
01669 
01670     if (defmode == (rpmMireMode)-1) {
01671         const char *t = rpmExpand("%{?_query_selector_match}", NULL);
01672         if (*t == '\0' || !strcmp(t, "default"))
01673             defmode = RPMMIRE_DEFAULT;
01674         else if (!strcmp(t, "strcmp"))
01675             defmode = RPMMIRE_STRCMP;
01676         else if (!strcmp(t, "regex"))
01677             defmode = RPMMIRE_REGEX;
01678         else if (!strcmp(t, "glob"))
01679             defmode = RPMMIRE_GLOB;
01680         else
01681             defmode = RPMMIRE_DEFAULT;
01682         t = _free(t);
01683      }
01684 
01685     if (mi == NULL || pattern == NULL)
01686         return rc;
01687 
01688     /* Leading '!' inverts pattern match sense, like "grep -v". */
01689     if (*pattern == '!') {
01690         notmatch = 1;
01691         pattern++;
01692     }
01693 
01694     /*@-mods@*/         /* FIX: WTFO? */
01695     allpat = mireDup(tag, &mode, pattern);
01696     /*@=mods@*/
01697 
01698     if (mode == RPMMIRE_DEFAULT)
01699         mode = defmode;
01700 
01701     switch (mode) {
01702     case RPMMIRE_DEFAULT:
01703     case RPMMIRE_STRCMP:
01704         break;
01705     case RPMMIRE_REGEX:
01706         preg = xcalloc(1, sizeof(*preg));
01707         cflags = (REG_EXTENDED | REG_NOSUB);
01708         rc = regcomp(preg, allpat, cflags);
01709         if (rc) {
01710             char msg[256];
01711             (void) regerror(rc, preg, msg, sizeof(msg)-1);
01712             msg[sizeof(msg)-1] = '\0';
01713             rpmError(RPMERR_REGCOMP, "%s: regcomp failed: %s\n", allpat, msg);
01714         }
01715         break;
01716     case RPMMIRE_GLOB:
01717         fnflags = FNM_PATHNAME | FNM_PERIOD;
01718         break;
01719     default:
01720         rc = -1;
01721         break;
01722     }
01723 
01724     if (rc) {
01725         /*@=kepttrans@*/        /* FIX: mire has kept values */
01726         allpat = _free(allpat);
01727         if (preg) {
01728             regfree(preg);
01729             preg = _free(preg);
01730         }
01731         /*@=kepttrans@*/
01732         return rc;
01733     }
01734 
01735     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
01736     mire = mi->mi_re + mi->mi_nre;
01737     mi->mi_nre++;
01738     
01739     mire->tag = tag;
01740     mire->mode = mode;
01741     mire->pattern = allpat;
01742     mire->notmatch = notmatch;
01743     mire->preg = preg;
01744     mire->cflags = cflags;
01745     mire->eflags = eflags;
01746     mire->fnflags = fnflags;
01747 
01748     (void) qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
01749 
01750     return rc;
01751 }
01752 
01758 static int mireSkip (const rpmdbMatchIterator mi)
01759         /*@*/
01760 {
01761 
01762     HGE_t hge = (HGE_t) headerGetEntryMinMemory;
01763     HFD_t hfd = (HFD_t) headerFreeData;
01764     union {
01765         void * ptr;
01766         const char ** argv;
01767         const char * str;
01768         int_32 * i32p;
01769         int_16 * i16p;
01770         int_8 * i8p;
01771     } u;
01772     char numbuf[32];
01773     rpmTagType t;
01774     int_32 c;
01775     miRE mire;
01776     int ntags = 0;
01777     int nmatches = 0;
01778     int i, j;
01779     int rc;
01780 
01781     if (mi->mi_h == NULL)       /* XXX can't happen */
01782         return 0;
01783 
01784     /*
01785      * Apply tag tests, implictly "||" for multiple patterns/values of a
01786      * single tag, implictly "&&" between multiple tag patterns.
01787      */
01788     if ((mire = mi->mi_re) != NULL)
01789     for (i = 0; i < mi->mi_nre; i++, mire++) {
01790         int anymatch;
01791 
01792         if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c))
01793             continue;
01794 
01795         anymatch = 0;           /* no matches yet */
01796         while (1) {
01797             switch (t) {
01798             case RPM_CHAR_TYPE:
01799             case RPM_INT8_TYPE:
01800                 sprintf(numbuf, "%d", (int) *u.i8p);
01801                 rc = miregexec(mire, numbuf);
01802                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01803                     anymatch++;
01804                 break;
01805             case RPM_INT16_TYPE:
01806                 sprintf(numbuf, "%d", (int) *u.i16p);
01807                 rc = miregexec(mire, numbuf);
01808                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01809                     anymatch++;
01810                 break;
01811             case RPM_INT32_TYPE:
01812                 sprintf(numbuf, "%d", (int) *u.i32p);
01813                 rc = miregexec(mire, numbuf);
01814                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01815                     anymatch++;
01816                 break;
01817             case RPM_STRING_TYPE:
01818                 rc = miregexec(mire, u.str);
01819                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
01820                     anymatch++;
01821                 break;
01822             case RPM_I18NSTRING_TYPE:
01823             case RPM_STRING_ARRAY_TYPE:
01824                 for (j = 0; j < c; j++) {
01825                     rc = miregexec(mire, u.argv[j]);
01826                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) {
01827                         anymatch++;
01828                         /*@innerbreak@*/ break;
01829                     }
01830                 }
01831                 break;
01832             case RPM_NULL_TYPE:
01833             case RPM_BIN_TYPE:
01834             default:
01835                 break;
01836             }
01837             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
01838                 i++;
01839                 mire++;
01840                 continue;
01841             }
01842             /*@innerbreak@*/ break;
01843         }
01844 
01845         u.ptr = hfd(u.ptr, t);
01846 
01847         ntags++;
01848         if (anymatch)
01849             nmatches++;
01850     }
01851 
01852     return (ntags == nmatches ? 0 : 1);
01853 
01854 }
01855 
01856 int rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
01857     return rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
01858 }
01859 
01860 int rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
01861     return rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
01862 }
01863 
01864 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite) {
01865     int rc;
01866     if (mi == NULL)
01867         return 0;
01868     rc = (mi->mi_cflags & DBI_WRITECURSOR) ? 1 : 0;
01869     if (rewrite)
01870         mi->mi_cflags |= DBI_WRITECURSOR;
01871     else
01872         mi->mi_cflags &= ~DBI_WRITECURSOR;
01873     return rc;
01874 }
01875 
01876 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
01877     int rc;
01878     if (mi == NULL)
01879         return 0;
01880     rc = mi->mi_modified;
01881     mi->mi_modified = modified;
01882     return rc;
01883 }
01884 
01885 Header XrpmdbNextIterator(rpmdbMatchIterator mi,
01886                 /*@unused@*/ const char * f, /*@unused@*/ unsigned int l)
01887 {
01888     return rpmdbNextIterator(mi);
01889 }
01890 
01891 Header rpmdbNextIterator(rpmdbMatchIterator mi)
01892 {
01893     dbiIndex dbi;
01894     void * uh = NULL;
01895     size_t uhlen = 0;
01896     unsigned int gflags = 0;    /* dbiGet() flags */
01897     void * keyp;
01898     size_t keylen;
01899     int rc;
01900     int xx;
01901 
01902     if (mi == NULL)
01903         return NULL;
01904 
01905     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01906     if (dbi == NULL)
01907         return NULL;
01908 
01909     /*
01910      * Cursors are per-iterator, not per-dbi, so get a cursor for the
01911      * iterator on 1st call. If the iteration is to rewrite headers, and the
01912      * CDB model is used for the database, then the cursor needs to
01913      * marked with DB_WRITECURSOR as well.
01914      */
01915     if (mi->mi_dbc == NULL)
01916         xx = dbiCopen(dbi, &mi->mi_dbc, (mi->mi_cflags | DBI_ITERATOR));
01917     dbi->dbi_lastoffset = mi->mi_prevoffset;
01918 
01919 top:
01920     /* XXX skip over instances with 0 join key */
01921     do {
01922         if (mi->mi_set) {
01923             if (!(mi->mi_setx < mi->mi_set->count))
01924                 return NULL;
01925             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
01926             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
01927             keyp = &mi->mi_offset;
01928             keylen = sizeof(mi->mi_offset);
01929         } else {
01930             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
01931             keylen = mi->mi_keylen;
01932 
01933             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags);
01934 if (dbi->dbi_api == 1 && dbi->dbi_rpmtag == RPMDBI_PACKAGES && rc == EFAULT) {
01935     rpmError(RPMERR_INTERNAL,
01936         _("record number %u in database is bad -- skipping.\n"), dbi->dbi_lastoffset);
01937     if (keyp && dbi->dbi_lastoffset)
01938         memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01939     continue;
01940 }
01941 
01942             /*
01943              * If we got the next key, save the header instance number.
01944              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
01945              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
01946              * largest header instance in the database, and should be
01947              * skipped.
01948              */
01949             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
01950                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01951 
01952             /* Terminate on error or end of keys */
01953             if (rc || (mi->mi_setx && mi->mi_offset == 0))
01954                 return NULL;
01955         }
01956         mi->mi_setx++;
01957     } while (mi->mi_offset == 0);
01958 
01959     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
01960         goto exit;
01961 
01962     /* Retrieve next header */
01963     if (uh == NULL) {
01964         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags);
01965         if (rc)
01966             return NULL;
01967     }
01968 
01969     /* Free current header */
01970     if (mi->mi_h) {
01971         if (mi->mi_modified && mi->mi_prevoffset)
01972             (void)dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
01973         mi->mi_h = headerFree(mi->mi_h);
01974     }
01975 
01976     /* Is this the end of the iteration? */
01977     if (uh == NULL)
01978         goto exit;
01979 
01980     mi->mi_h = headerCopyLoad(uh);
01981     /* XXX db1 with hybrid, simulated db interface on falloc.c needs free. */
01982     if (dbi->dbi_api == 1) uh = _free(uh);
01983 
01984     /* Did the header load correctly? */
01985     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
01986         rpmError(RPMERR_BADHEADER,
01987                 _("rpmdb: damaged header instance #%u retrieved, skipping.\n"),
01988                 mi->mi_offset);
01989         goto top;
01990     }
01991 
01992     /*
01993      * Skip this header if iterator selector (if any) doesn't match.
01994      */
01995     if (mireSkip(mi)) {
01996         /* XXX hack, can't restart with Packages locked on single instance. */
01997         if (mi->mi_set || mi->mi_keyp == NULL)
01998             goto top;
01999         return NULL;
02000     }
02001 
02002     mi->mi_prevoffset = mi->mi_offset;
02003     mi->mi_modified = 0;
02004 
02005 exit:
02006 #ifdef  NOTNOW
02007     if (mi->mi_h) {
02008         const char *n, *v, *r;
02009         (void) headerNVR(mi->mi_h, &n, &v, &r);
02010         rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r,
02011                 mi->mi_offset, mi->mi_h);
02012     }
02013 #endif
02014     /*@-retexpose -retalias@*/
02015     /*@-compdef -usereleased@*/ return mi->mi_h; /*@=compdef =usereleased@*/
02016     /*@=retexpose =retalias@*/
02017 }
02018 
02019 static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi)
02020         /*@modifies mi @*/
02021 {
02022     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
02023         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
02024                 hdrNumCmp);
02025         mi->mi_sorted = 1;
02026     }
02027 }
02028 
02029 static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi,
02030                 const void * keyp, size_t keylen, int fpNum)
02031         /*@modifies mi, fileSystem @*/
02032 {
02033     dbiIndex dbi = NULL;
02034     DBC * dbcursor = NULL;
02035     dbiIndexSet set = NULL;
02036     int rc;
02037     int xx;
02038 
02039     if (!(mi && keyp))
02040         return 1;
02041 
02042     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
02043     if (dbi == NULL)
02044         return 1;
02045 
02046     if (keylen == 0)
02047         keylen = strlen(keyp);
02048 
02049     xx = dbiCopen(dbi, &dbcursor, 0);
02050     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02051     xx = dbiCclose(dbi, dbcursor, 0);
02052     dbcursor = NULL;
02053 
02054     if (rc == 0) {      /* success */
02055         int i;
02056         for (i = 0; i < set->count; i++)
02057             set->recs[i].fpNum = fpNum;
02058 
02059         if (mi->mi_set == NULL) {
02060             mi->mi_set = set;
02061             set = NULL;
02062         } else {
02063             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
02064                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
02065             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
02066                 set->count * sizeof(*(mi->mi_set->recs)));
02067             mi->mi_set->count += set->count;
02068         }
02069     }
02070 
02071     set = dbiFreeIndexSet(set);
02072     return rc;
02073 }
02074 
02075 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
02076         int nHdrNums, int sorted)
02077 {
02078     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02079         return 1;
02080 
02081     if (mi->mi_set)
02082         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
02083     return 0;
02084 }
02085 
02086 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
02087 {
02088     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02089         return 1;
02090 
02091     if (mi->mi_set == NULL)
02092         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
02093     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
02094     return 0;
02095 }
02096 
02097 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
02098         const void * keyp, size_t keylen)
02099 {
02100     rpmdbMatchIterator mi = NULL;
02101     dbiIndexSet set = NULL;
02102     dbiIndex dbi;
02103     const void * mi_keyp = NULL;
02104     int isLabel = 0;
02105 
02106     if (rpmdb == NULL)
02107         return NULL;
02108     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
02109     switch (rpmtag) {
02110     case RPMDBI_LABEL:
02111         rpmtag = RPMTAG_NAME;
02112         isLabel = 1;
02113         break;
02114     }
02115 
02116     dbi = dbiOpen(rpmdb, rpmtag, 0);
02117     if (dbi == NULL)
02118         return NULL;
02119 
02120 #if 0
02121     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
02122     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
02123 #else
02124 if (dbi->dbi_rmw)
02125 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
02126 #endif
02127 
02128     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
02129 
02130     if (rpmtag != RPMDBI_PACKAGES && keyp) {
02131         DBC * dbcursor = NULL;
02132         int rc;
02133         int xx;
02134 
02135         if (isLabel) {
02136             /* XXX HACK to get rpmdbFindByLabel out of the API */
02137             xx = dbiCopen(dbi, &dbcursor, 0);
02138             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
02139             xx = dbiCclose(dbi, dbcursor, 0);
02140             dbcursor = NULL;
02141         } else if (rpmtag == RPMTAG_BASENAMES) {
02142             rc = rpmdbFindByFile(rpmdb, keyp, &set);
02143         } else {
02144             xx = dbiCopen(dbi, &dbcursor, 0);
02145             /*@-nullpass@*/     /* LCL: keyp != NULL here. */
02146             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02147             /*@=nullpass@*/
02148             xx = dbiCclose(dbi, dbcursor, 0);
02149             dbcursor = NULL;
02150         }
02151         if (rc) {       /* error/not found */
02152             set = dbiFreeIndexSet(set);
02153             return NULL;
02154         }
02155     }
02156 
02157     if (keyp) {
02158         char * k;
02159 
02160         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
02161             keylen = strlen(keyp);
02162         k = xmalloc(keylen + 1);
02163         memcpy(k, keyp, keylen);
02164         k[keylen] = '\0';       /* XXX for strings */
02165         mi_keyp = k;
02166     }
02167 
02168     mi = xcalloc(1, sizeof(*mi));
02169     mi->mi_keyp = mi_keyp;
02170     mi->mi_keylen = keylen;
02171 
02172     /*@-assignexpose@*/
02173     mi->mi_rpmdb = rpmdb;
02174     /*@=assignexpose@*/
02175     mi->mi_rpmtag = rpmtag;
02176 
02177     mi->mi_dbc = NULL;
02178     mi->mi_set = set;
02179     mi->mi_setx = 0;
02180     mi->mi_ndups = 0;
02181     mi->mi_h = NULL;
02182     mi->mi_sorted = 0;
02183     mi->mi_cflags = 0;
02184     mi->mi_modified = 0;
02185     mi->mi_prevoffset = 0;
02186     mi->mi_offset = 0;
02187     mi->mi_filenum = 0;
02188     mi->mi_fpnum = 0;
02189     mi->mi_dbnum = 0;
02190     mi->mi_nre = 0;
02191     mi->mi_re = NULL;
02192     mi->mi_version = NULL;
02193     mi->mi_release = NULL;
02194     return mi;
02195 }
02196 
02206 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor,
02207                 const void * keyp, size_t keylen, dbiIndexItem rec)
02208         /*@modifies *dbcursor, fileSystem @*/
02209 {
02210     dbiIndexSet set = NULL;
02211     int rc;
02212     
02213     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02214 
02215     if (rc < 0)                 /* not found */
02216         rc = 0;
02217     else if (rc > 0)            /* error */
02218         rc = 1;         /* error message already generated from dbindex.c */
02219     else {                      /* success */
02220         /*@-mods@*/     /* a single rec is not modified */
02221         rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
02222         /*@=mods@*/
02223         if (rc == 0 && dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
02224             rc = 1;
02225     }
02226 
02227     set = dbiFreeIndexSet(set);
02228 
02229     return rc;
02230 }
02231 
02232 /* XXX install.c uninstall.c */
02233 int rpmdbRemove(rpmdb rpmdb, /*@unused@*/ int rid, unsigned int hdrNum)
02234 {
02235     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02236     HFD_t hfd = headerFreeData;
02237     Header h;
02238     sigset_t signalMask;
02239 
02240     if (rpmdb == NULL)
02241         return 0;
02242 
02243     {   rpmdbMatchIterator mi;
02244         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
02245         h = rpmdbNextIterator(mi);
02246         if (h)
02247             h = headerLink(h);
02248         mi = rpmdbFreeIterator(mi);
02249     }
02250 
02251     if (h == NULL) {
02252         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
02253               "rpmdbRemove", hdrNum);
02254         return 1;
02255     }
02256 
02257 #ifdef  DYING
02258     /* Add remove transaction id to header. */
02259     if (rid != 0 && rid != -1) {
02260         int_32 tid = rid;
02261         (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
02262     }
02263 #endif
02264 
02265     {   const char *n, *v, *r;
02266         (void) headerNVR(h, &n, &v, &r);
02267         rpmMessage(RPMMESS_DEBUG, "  --- %10u %s-%s-%s\n", hdrNum, n, v, r);
02268     }
02269 
02270     (void) blockSignals(rpmdb, &signalMask);
02271 
02272     {   int dbix;
02273         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
02274 
02275         if (dbiTags != NULL)
02276         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
02277             dbiIndex dbi;
02278             DBC * dbcursor = NULL;
02279             const char *av[1];
02280             const char ** rpmvals = NULL;
02281             rpmTagType rpmtype = 0;
02282             int rpmcnt = 0;
02283             int rpmtag;
02284             int xx;
02285             int i;
02286 
02287             dbi = NULL;
02288             rpmtag = dbiTags[dbix];
02289 
02290             switch (rpmtag) {
02291             /* Filter out temporary databases */
02292             case RPMDBI_AVAILABLE:
02293             case RPMDBI_ADDED:
02294             case RPMDBI_REMOVED:
02295             case RPMDBI_DEPENDS:
02296                 continue;
02297                 /*@notreached@*/ break;
02298             case RPMDBI_PACKAGES:
02299               dbi = dbiOpen(rpmdb, rpmtag, 0);
02300               if (dbi != NULL) {
02301                 xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02302                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
02303                 xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02304                 dbcursor = NULL;
02305                 if (!dbi->dbi_no_dbsync)
02306                     xx = dbiSync(dbi, 0);
02307               }
02308                 continue;
02309                 /*@notreached@*/ break;
02310             }
02311         
02312             if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt))
02313                 continue;
02314 
02315           dbi = dbiOpen(rpmdb, rpmtag, 0);
02316           if (dbi != NULL) {
02317             xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02318 
02319             if (rpmtype == RPM_STRING_TYPE) {
02320 
02321                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
02322                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
02323 
02324                 /* XXX force uniform headerGetEntry return */
02325                 av[0] = (const char *) rpmvals;
02326                 rpmvals = av;
02327                 rpmcnt = 1;
02328             } else {
02329 
02330                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
02331                         rpmcnt, tagName(dbi->dbi_rpmtag));
02332 
02333             }
02334 
02335             for (i = 0; i < rpmcnt; i++) {
02336                 const void * valp;
02337                 size_t vallen;
02338 
02339                 /* Identify value pointer and length. */
02340                 switch (rpmtype) {
02341                 case RPM_CHAR_TYPE:
02342                 case RPM_INT8_TYPE:
02343                     vallen = sizeof(RPM_CHAR_TYPE);
02344                     valp = rpmvals + i;
02345                     break;
02346                 case RPM_INT16_TYPE:
02347                     vallen = sizeof(int_16);
02348                     valp = rpmvals + i;
02349                     break;
02350                 case RPM_INT32_TYPE:
02351                     vallen = sizeof(int_32);
02352                     valp = rpmvals + i;
02353                     break;
02354                 case RPM_BIN_TYPE:
02355                     vallen = rpmcnt;
02356                     valp = rpmvals;
02357                     rpmcnt = 1;         /* XXX break out of loop. */
02358                     break;
02359                 case RPM_STRING_TYPE:
02360                 case RPM_I18NSTRING_TYPE:
02361                     rpmcnt = 1;         /* XXX break out of loop. */
02362                     /*@fallthrough@*/
02363                 case RPM_STRING_ARRAY_TYPE:
02364                 default:
02365                     vallen = strlen(rpmvals[i]);
02366                     valp = rpmvals[i];
02367                     break;
02368                 }
02369 
02370                 /*
02371                  * This is almost right, but, if there are duplicate tag
02372                  * values, there will be duplicate attempts to remove
02373                  * the header instance. It's easier to just ignore errors
02374                  * than to do things correctly.
02375                  */
02376                 xx = removeIndexEntry(dbi, dbcursor, valp, vallen, rec);
02377             }
02378 
02379             xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02380             dbcursor = NULL;
02381 
02382             if (!dbi->dbi_no_dbsync)
02383                 xx = dbiSync(dbi, 0);
02384           }
02385 
02386             rpmvals = hfd(rpmvals, rpmtype);
02387             rpmtype = 0;
02388             rpmcnt = 0;
02389         }
02390 
02391         rec = _free(rec);
02392     }
02393 
02394     (void) unblockSignals(rpmdb, &signalMask);
02395 
02396     h = headerFree(h);
02397 
02398     return 0;
02399 }
02400 
02410 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor,
02411                 const char * keyp, size_t keylen, dbiIndexItem rec)
02412         /*@modifies *dbcursor, fileSystem @*/
02413 {
02414     dbiIndexSet set = NULL;
02415     int rc;
02416 
02417     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
02418 
02419     if (rc > 0) {               /* error */
02420         rc = 1;
02421     } else {
02422 
02423         /* With duplicates, cursor is positioned, discard the record. */
02424         if (rc == 0 && dbi->dbi_permit_dups)
02425             set = dbiFreeIndexSet(set);
02426 
02427         if (set == NULL || rc < 0) {            /* not found */
02428             rc = 0;
02429             set = xcalloc(1, sizeof(*set));
02430         }
02431         (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
02432         if (dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
02433             rc = 1;
02434     }
02435     set = dbiFreeIndexSet(set);
02436 
02437     return 0;
02438 }
02439 
02440 /* XXX install.c */
02441 int rpmdbAdd(rpmdb rpmdb, int iid, Header h)
02442 {
02443     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02444     HFD_t hfd = headerFreeData;
02445     sigset_t signalMask;
02446     const char ** baseNames;
02447     rpmTagType bnt;
02448     int count = 0;
02449     dbiIndex dbi;
02450     int dbix;
02451     unsigned int gflags = 0;    /* dbiGet() flags */
02452     unsigned int pflags = 0;    /* dbiPut() flags */
02453     unsigned int hdrNum = 0;
02454     int rc = 0;
02455     int xx;
02456 
02457     if (rpmdb == NULL)
02458         return 0;
02459 
02460     if (iid != 0 && iid != -1) {
02461         int_32 tid = iid;
02462         (void) headerRemoveEntry(h, RPMTAG_REMOVETID);
02463         if (!headerIsEntry(h, RPMTAG_INSTALLTID))
02464            (void) headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
02465     }
02466 
02467     /*
02468      * If old style filename tags is requested, the basenames need to be
02469      * retrieved early, and the header needs to be converted before
02470      * being written to the package header database.
02471      */
02472 
02473     (void) hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count);
02474 
02475     if (_noDirTokens)
02476         expandFilelist(h);
02477 
02478     (void) blockSignals(rpmdb, &signalMask);
02479 
02480     {
02481         unsigned int firstkey = 0;
02482         DBC * dbcursor = NULL;
02483         void * keyp = &firstkey;
02484         size_t keylen = sizeof(firstkey);
02485         void * datap = NULL;
02486         size_t datalen = 0;
02487 
02488       dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
02489       if (dbi != NULL) {
02490 
02491         /* XXX db0: hack to pass sizeof header to fadAlloc */
02492         datap = h;
02493         datalen = headerSizeof(h, HEADER_MAGIC_NO);
02494 
02495         xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02496 
02497         /* Retrieve join key for next header instance. */
02498 
02499         rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, gflags);
02500 
02501         hdrNum = 0;
02502         if (rc == 0 && datap)
02503             memcpy(&hdrNum, datap, sizeof(hdrNum));
02504         ++hdrNum;
02505         if (rc == 0 && datap) {
02506             /*@-refcounttrans@*/        /* FIX: datap aliases h */
02507             memcpy(datap, &hdrNum, sizeof(hdrNum));
02508             /*@=refcounttrans@*/
02509         } else {
02510             datap = &hdrNum;
02511             datalen = sizeof(hdrNum);
02512         }
02513 
02514         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, pflags);
02515         xx = dbiSync(dbi, 0);
02516 
02517         xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02518         dbcursor = NULL;
02519       }
02520 
02521     }
02522 
02523     if (rc) {
02524         rpmError(RPMERR_DBCORRUPT,
02525                 _("error(%d) allocating new package instance\n"), rc);
02526         goto exit;
02527     }
02528 
02529     /* Now update the indexes */
02530 
02531     if (hdrNum)
02532     {   dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
02533 
02534         if (dbiTags != NULL)
02535         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
02536             DBC * dbcursor = NULL;
02537             const char *av[1];
02538             const char **rpmvals = NULL;
02539             rpmTagType rpmtype = 0;
02540             int rpmcnt = 0;
02541             int rpmtag;
02542             int_32 * requireFlags;
02543             int i, j;
02544 
02545             dbi = NULL;
02546             requireFlags = NULL;
02547             rpmtag = dbiTags[dbix];
02548 
02549             switch (rpmtag) {
02550             /* Filter out temporary databases */
02551             case RPMDBI_AVAILABLE:
02552             case RPMDBI_ADDED:
02553             case RPMDBI_REMOVED:
02554             case RPMDBI_DEPENDS:
02555                 continue;
02556                 /*@notreached@*/ break;
02557             case RPMDBI_PACKAGES:
02558               dbi = dbiOpen(rpmdb, rpmtag, 0);
02559               if (dbi != NULL) {
02560                 xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02561                 xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h);
02562                 xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02563                 dbcursor = NULL;
02564                 if (!dbi->dbi_no_dbsync)
02565                     xx = dbiSync(dbi, 0);
02566                 {   const char *n, *v, *r;
02567                     (void) headerNVR(h, &n, &v, &r);
02568                     rpmMessage(RPMMESS_DEBUG, "  +++ %10u %s-%s-%s\n", hdrNum, n, v, r);
02569                 }
02570               }
02571                 continue;
02572                 /*@notreached@*/ break;
02573             /* XXX preserve legacy behavior */
02574             case RPMTAG_BASENAMES:
02575                 rpmtype = bnt;
02576                 rpmvals = baseNames;
02577                 rpmcnt = count;
02578                 break;
02579             case RPMTAG_REQUIRENAME:
02580                 (void) hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
02581                 (void) hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL);
02582                 break;
02583             default:
02584                 (void) hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
02585                 break;
02586             }
02587 
02588             if (rpmcnt <= 0) {
02589                 if (rpmtag != RPMTAG_GROUP)
02590                     continue;
02591 
02592                 /* XXX preserve legacy behavior */
02593                 rpmtype = RPM_STRING_TYPE;
02594                 rpmvals = (const char **) "Unknown";
02595                 rpmcnt = 1;
02596             }
02597 
02598           dbi = dbiOpen(rpmdb, rpmtag, 0);
02599           if (dbi != NULL) {
02600 
02601             xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR);
02602             if (rpmtype == RPM_STRING_TYPE) {
02603                 rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), 
02604                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
02605 
02606                 /* XXX force uniform headerGetEntry return */
02607                 /*@-observertrans@*/
02608                 av[0] = (const char *) rpmvals;
02609                 /*@=observertrans@*/
02610                 rpmvals = av;
02611                 rpmcnt = 1;
02612             } else {
02613 
02614                 rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), 
02615                         rpmcnt, tagName(dbi->dbi_rpmtag));
02616 
02617             }
02618 
02619             for (i = 0; i < rpmcnt; i++) {
02620                 const void * valp;
02621                 size_t vallen;
02622 
02623                 /*
02624                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
02625                  * included the tagNum only for files.
02626                  */
02627                 switch (dbi->dbi_rpmtag) {
02628                 case RPMTAG_REQUIRENAME:
02629                     /* Filter out install prerequisites. */
02630                     if (requireFlags && isInstallPreReq(requireFlags[i]))
02631                         continue;
02632                     rec->tagNum = i;
02633                     break;
02634                 case RPMTAG_TRIGGERNAME:
02635                     if (i) {    /* don't add duplicates */
02636                         for (j = 0; j < i; j++) {
02637                             if (!strcmp(rpmvals[i], rpmvals[j]))
02638                                 /*@innerbreak@*/ break;
02639                         }
02640                         if (j < i)
02641                             continue;
02642                     }
02643                     rec->tagNum = i;
02644                     break;
02645                 default:
02646                     rec->tagNum = i;
02647                     break;
02648                 }
02649 
02650                 /* Identify value pointer and length. */
02651                 switch (rpmtype) {
02652                 case RPM_CHAR_TYPE:
02653                 case RPM_INT8_TYPE:
02654                     vallen = sizeof(int_8);
02655                     valp = rpmvals + i;
02656                     break;
02657                 case RPM_INT16_TYPE:
02658                     vallen = sizeof(int_16);
02659                     valp = rpmvals + i;
02660                     break;
02661                 case RPM_INT32_TYPE:
02662                     vallen = sizeof(int_32);
02663                     valp = rpmvals + i;
02664                     break;
02665                 case RPM_BIN_TYPE:
02666                     vallen = rpmcnt;
02667                     valp = rpmvals;
02668                     rpmcnt = 1;         /* XXX break out of loop. */
02669                     break;
02670                 case RPM_STRING_TYPE:
02671                 case RPM_I18NSTRING_TYPE:
02672                     rpmcnt = 1;         /* XXX break out of loop. */
02673                     /*@fallthrough@*/
02674                 case RPM_STRING_ARRAY_TYPE:
02675                 default:
02676                     valp = rpmvals[i];
02677                     vallen = strlen(rpmvals[i]);
02678                     break;
02679                 }
02680 
02681                 rc += addIndexEntry(dbi, dbcursor, valp, vallen, rec);
02682             }
02683             xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR);
02684             dbcursor = NULL;
02685 
02686             if (!dbi->dbi_no_dbsync)
02687                 xx = dbiSync(dbi, 0);
02688           }
02689 
02690         /*@-observertrans@*/
02691             rpmvals = hfd(rpmvals, rpmtype);
02692         /*@=observertrans@*/
02693             rpmtype = 0;
02694             rpmcnt = 0;
02695         }
02696 
02697         rec = _free(rec);
02698     }
02699 
02700 exit:
02701     (void) unblockSignals(rpmdb, &signalMask);
02702 
02703     return rc;
02704 }
02705 
02706 /* XXX transaction.c */
02707 int rpmdbFindFpList(rpmdb rpmdb, fingerPrint * fpList, dbiIndexSet * matchList, 
02708                     int numItems)
02709 {
02710     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02711     HFD_t hfd = headerFreeData;
02712     rpmdbMatchIterator mi;
02713     fingerPrintCache fpc;
02714     Header h;
02715     int i;
02716 
02717     if (rpmdb == NULL) return 0;
02718 
02719     mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, NULL, 0);
02720 
02721     /* Gather all matches from the database */
02722     for (i = 0; i < numItems; i++) {
02723         (void) rpmdbGrowIterator(mi, fpList[i].baseName, 0, i);
02724         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
02725     }
02726 
02727     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
02728         mi = rpmdbFreeIterator(mi);
02729         return 0;
02730     }
02731     fpc = fpCacheCreate(i);
02732 
02733     rpmdbSortIterator(mi);
02734     /* iterator is now sorted by (recnum, filenum) */
02735 
02736     /* For each set of files matched in a package ... */
02737     if (mi != NULL)
02738     while ((h = rpmdbNextIterator(mi)) != NULL) {
02739         const char ** dirNames;
02740         const char ** baseNames;
02741         const char ** fullBaseNames;
02742         rpmTagType bnt, dnt;
02743         int_32 * dirIndexes;
02744         int_32 * fullDirIndexes;
02745         fingerPrint * fps;
02746         dbiIndexItem im;
02747         int start;
02748         int num;
02749         int end;
02750 
02751         start = mi->mi_setx - 1;
02752         im = mi->mi_set->recs + start;
02753 
02754         /* Find the end of the set of matched files in this package. */
02755         for (end = start + 1; end < mi->mi_set->count; end++) {
02756             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
02757                 /*@innerbreak@*/ break;
02758         }
02759         num = end - start;
02760 
02761         /* Compute fingerprints for this header's matches */
02762         (void) hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL);
02763         (void) hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
02764         (void) hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL);
02765 
02766         baseNames = xcalloc(num, sizeof(*baseNames));
02767         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
02768         for (i = 0; i < num; i++) {
02769             baseNames[i] = fullBaseNames[im[i].tagNum];
02770             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
02771         }
02772 
02773         fps = xcalloc(num, sizeof(*fps));
02774         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
02775 
02776         /* Add db (recnum,filenum) to list for fingerprint matches. */
02777         for (i = 0; i < num; i++, im++) {
02778             /*@-nullpass@*/
02779             if (FP_EQUAL(fps[i], fpList[im->fpNum])) {
02780             /*@=nullpass@*/
02781                 /*@-usedef@*/
02782                 (void) dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
02783                 /*@=usedef@*/
02784             }
02785         }
02786 
02787         fps = _free(fps);
02788         dirNames = hfd(dirNames, dnt);
02789         fullBaseNames = hfd(fullBaseNames, bnt);
02790         baseNames = _free(baseNames);
02791         dirIndexes = _free(dirIndexes);
02792 
02793         mi->mi_setx = end;
02794     }
02795 
02796     mi = rpmdbFreeIterator(mi);
02797 
02798     fpCacheFree(fpc);
02799 
02800     return 0;
02801 
02802 }
02803 
02804 char * db1basename (int rpmtag)
02805 {
02806     char * base = NULL;
02807     switch (rpmtag) {
02808     case RPMDBI_PACKAGES:       base = "packages.rpm";          break;
02809     case RPMTAG_NAME:           base = "nameindex.rpm";         break;
02810     case RPMTAG_BASENAMES:      base = "fileindex.rpm";         break;
02811     case RPMTAG_GROUP:          base = "groupindex.rpm";        break;
02812     case RPMTAG_REQUIRENAME:    base = "requiredby.rpm";        break;
02813     case RPMTAG_PROVIDENAME:    base = "providesindex.rpm";     break;
02814     case RPMTAG_CONFLICTNAME:   base = "conflictsindex.rpm";    break;
02815     case RPMTAG_TRIGGERNAME:    base = "triggerindex.rpm";      break;
02816     default:
02817       { const char * tn = tagName(rpmtag);
02818         base = alloca( strlen(tn) + sizeof(".idx") + 1 );
02819         (void) stpcpy( stpcpy(base, tn), ".idx");
02820       } break;
02821     }
02822     return xstrdup(base);
02823 }
02824 
02825 static int rpmdbRemoveDatabase(const char * rootdir,
02826                 const char * dbpath, int _dbapi)
02827         /*@modifies fileSystem @*/
02828 { 
02829     int i;
02830     char * filename;
02831     int xx;
02832 
02833     i = strlen(dbpath);
02834     if (dbpath[i - 1] != '/') {
02835         filename = alloca(i);
02836         strcpy(filename, dbpath);
02837         filename[i] = '/';
02838         filename[i + 1] = '\0';
02839         dbpath = filename;
02840     }
02841     
02842     filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
02843 
02844     switch (_dbapi) {
02845     case 3:
02846         if (dbiTags != NULL)
02847         for (i = 0; i < dbiTagsMax; i++) {
02848             const char * base = tagName(dbiTags[i]);
02849             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02850             (void)rpmCleanPath(filename);
02851             if (!rpmfileexists(filename))
02852                 continue;
02853             xx = unlink(filename);
02854         }
02855         for (i = 0; i < 16; i++) {
02856             sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
02857             (void)rpmCleanPath(filename);
02858             if (!rpmfileexists(filename))
02859                 continue;
02860             xx = unlink(filename);
02861         }
02862         break;
02863     case 2:
02864     case 1:
02865     case 0:
02866         if (dbiTags != NULL)
02867         for (i = 0; i < dbiTagsMax; i++) {
02868             const char * base = db1basename(dbiTags[i]);
02869             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02870             (void)rpmCleanPath(filename);
02871             if (!rpmfileexists(filename))
02872                 continue;
02873             xx = unlink(filename);
02874             base = _free(base);
02875         }
02876         break;
02877     }
02878 
02879     sprintf(filename, "%s/%s", rootdir, dbpath);
02880     (void)rpmCleanPath(filename);
02881     xx = rmdir(filename);
02882 
02883     return 0;
02884 }
02885 
02886 static int rpmdbMoveDatabase(const char * rootdir,
02887                 const char * olddbpath, int _olddbapi,
02888                 const char * newdbpath, int _newdbapi)
02889         /*@modifies fileSystem @*/
02890 {
02891     int i;
02892     char * ofilename, * nfilename;
02893     int rc = 0;
02894     int xx;
02895  
02896     i = strlen(olddbpath);
02897     if (olddbpath[i - 1] != '/') {
02898         ofilename = alloca(i + 2);
02899         strcpy(ofilename, olddbpath);
02900         ofilename[i] = '/';
02901         ofilename[i + 1] = '\0';
02902         olddbpath = ofilename;
02903     }
02904     
02905     i = strlen(newdbpath);
02906     if (newdbpath[i - 1] != '/') {
02907         nfilename = alloca(i + 2);
02908         strcpy(nfilename, newdbpath);
02909         nfilename[i] = '/';
02910         nfilename[i + 1] = '\0';
02911         newdbpath = nfilename;
02912     }
02913     
02914     ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
02915     nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
02916 
02917     switch (_olddbapi) {
02918     case 3:
02919         if (dbiTags != NULL)
02920         for (i = 0; i < dbiTagsMax; i++) {
02921             const char * base;
02922             int rpmtag;
02923 
02924             /* Filter out temporary databases */
02925             switch ((rpmtag = dbiTags[i])) {
02926             case RPMDBI_AVAILABLE:
02927             case RPMDBI_ADDED:
02928             case RPMDBI_REMOVED:
02929             case RPMDBI_DEPENDS:
02930                 continue;
02931                 /*@notreached@*/ break;
02932             default:
02933                 break;
02934             }
02935 
02936             base = tagName(rpmtag);
02937             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02938             (void)rpmCleanPath(ofilename);
02939             if (!rpmfileexists(ofilename))
02940                 continue;
02941             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02942             (void)rpmCleanPath(nfilename);
02943             if ((xx = Rename(ofilename, nfilename)) != 0)
02944                 rc = 1;
02945         }
02946         for (i = 0; i < 16; i++) {
02947             sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
02948             (void)rpmCleanPath(ofilename);
02949             if (!rpmfileexists(ofilename))
02950                 continue;
02951             xx = unlink(ofilename);
02952             sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
02953             (void)rpmCleanPath(nfilename);
02954 #ifdef  DYING
02955             if ((xx = Rename(ofilename, nfilename)) != 0)
02956                 rc = 1;
02957 #else
02958             xx = unlink(nfilename);
02959 #endif
02960         }
02961         break;
02962     case 2:
02963     case 1:
02964     case 0:
02965         if (dbiTags != NULL)
02966         for (i = 0; i < dbiTagsMax; i++) {
02967             const char * base;
02968             int rpmtag;
02969 
02970             /* Filter out temporary databases */
02971             switch ((rpmtag = dbiTags[i])) {
02972             case RPMDBI_AVAILABLE:
02973             case RPMDBI_ADDED:
02974             case RPMDBI_REMOVED:
02975             case RPMDBI_DEPENDS:
02976                 continue;
02977                 /*@notreached@*/ break;
02978             default:
02979                 break;
02980             }
02981 
02982             base = db1basename(rpmtag);
02983             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02984             (void)rpmCleanPath(ofilename);
02985             if (!rpmfileexists(ofilename))
02986                 continue;
02987             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02988             (void)rpmCleanPath(nfilename);
02989             if ((xx = Rename(ofilename, nfilename)) != 0)
02990                 rc = 1;
02991             base = _free(base);
02992         }
02993         break;
02994     }
02995     if (rc || _olddbapi == _newdbapi)
02996         return rc;
02997 
02998     rc = rpmdbRemoveDatabase(rootdir, newdbpath, _newdbapi);
02999 
03000 
03001     /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
03002     if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
03003         const char * mdb1 = "/etc/rpm/macros.db1";
03004         struct stat st;
03005         if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
03006             rpmMessage(RPMMESS_DEBUG,
03007                 _("removing %s after successful db3 rebuild.\n"), mdb1);
03008     }
03009     return rc;
03010 }
03011 
03012 int rpmdbRebuild(const char * rootdir)
03013 {
03014     rpmdb olddb;
03015     const char * dbpath = NULL;
03016     const char * rootdbpath = NULL;
03017     rpmdb newdb;
03018     const char * newdbpath = NULL;
03019     const char * newrootdbpath = NULL;
03020     const char * tfn;
03021     int nocleanup = 1;
03022     int failed = 0;
03023     int removedir = 0;
03024     int rc = 0, xx;
03025     int _dbapi;
03026     int _dbapi_rebuild;
03027 
03028     if (rootdir == NULL) rootdir = "/";
03029 
03030     _dbapi = rpmExpandNumeric("%{_dbapi}");
03031     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
03032 
03033     /*@-nullpass@*/
03034     tfn = rpmGetPath("%{_dbpath}", NULL);
03035     /*@=nullpass@*/
03036     if (!(tfn && tfn[0] != '%')) {
03037         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
03038         rc = 1;
03039         goto exit;
03040     }
03041     dbpath = rootdbpath = rpmGetPath(rootdir, tfn, NULL);
03042     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
03043         dbpath += strlen(rootdir);
03044     tfn = _free(tfn);
03045 
03046     /*@-nullpass@*/
03047     tfn = rpmGetPath("%{_dbpath_rebuild}", NULL);
03048     /*@=nullpass@*/
03049     if (!(tfn && tfn[0] != '%' && strcmp(tfn, dbpath))) {
03050         char pidbuf[20];
03051         char *t;
03052         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
03053         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
03054         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
03055         tfn = _free(tfn);
03056         tfn = t;
03057         nocleanup = 0;
03058     }
03059     newdbpath = newrootdbpath = rpmGetPath(rootdir, tfn, NULL);
03060     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
03061         newdbpath += strlen(rootdir);
03062     tfn = _free(tfn);
03063 
03064     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
03065         rootdbpath, newrootdbpath);
03066 
03067     if (!access(newrootdbpath, F_OK)) {
03068         rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
03069               newrootdbpath);
03070         rc = 1;
03071         goto exit;
03072     }
03073 
03074     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
03075     if (Mkdir(newrootdbpath, 0755)) {
03076         rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
03077               newrootdbpath, strerror(errno));
03078         rc = 1;
03079         goto exit;
03080     }
03081     removedir = 1;
03082 
03083     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
03084                 _dbapi);
03085     _rebuildinprogress = 1;
03086     if (openDatabase(rootdir, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
03087                      RPMDB_FLAG_MINIMAL)) {
03088         rc = 1;
03089         goto exit;
03090     }
03091     _dbapi = olddb->db_api;
03092     _rebuildinprogress = 0;
03093 
03094     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
03095                 _dbapi_rebuild);
03096     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
03097     if (openDatabase(rootdir, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
03098         rc = 1;
03099         goto exit;
03100     }
03101     _dbapi_rebuild = newdb->db_api;
03102     
03103     {   Header h = NULL;
03104         rpmdbMatchIterator mi;
03105 #define _RECNUM rpmdbGetIteratorOffset(mi)
03106 
03107         /* RPMDBI_PACKAGES */
03108         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
03109         while ((h = rpmdbNextIterator(mi)) != NULL) {
03110 
03111             /* let's sanity check this record a bit, otherwise just skip it */
03112             if (!(headerIsEntry(h, RPMTAG_NAME) &&
03113                 headerIsEntry(h, RPMTAG_VERSION) &&
03114                 headerIsEntry(h, RPMTAG_RELEASE) &&
03115                 headerIsEntry(h, RPMTAG_BUILDTIME)))
03116             {
03117                 rpmError(RPMERR_INTERNAL,
03118                         _("record number %u in database is bad -- skipping.\n"),
03119                         _RECNUM);
03120                 continue;
03121             }
03122 
03123             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
03124             if (_db_filter_dups || newdb->db_filter_dups) {
03125                 const char * name, * version, * release;
03126                 int skip = 0;
03127 
03128                 (void) headerNVR(h, &name, &version, &release);
03129 
03130                 /*@-shadow@*/
03131                 {   rpmdbMatchIterator mi;
03132                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
03133                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
03134                                 RPMMIRE_DEFAULT, version);
03135                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
03136                                 RPMMIRE_DEFAULT, release);
03137                     while (rpmdbNextIterator(mi)) {
03138                         skip = 1;
03139                         /*@innerbreak@*/ break;
03140                     }
03141                     mi = rpmdbFreeIterator(mi);
03142                 }
03143                 /*@=shadow@*/
03144 
03145                 if (skip)
03146                     continue;
03147             }
03148 
03149             /* Deleted entries are eliminated in legacy headers by copy. */
03150             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
03151                                 ? headerCopy(h) : NULL);
03152                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h));
03153                 nh = headerFree(nh);
03154             }
03155 
03156             if (rc) {
03157                 rpmError(RPMERR_INTERNAL,
03158                         _("cannot add record originally at %u\n"), _RECNUM);
03159                 failed = 1;
03160                 break;
03161             }
03162         }
03163 
03164         mi = rpmdbFreeIterator(mi);
03165 
03166     }
03167 
03168     if (!nocleanup) {
03169         olddb->db_remove_env = 1;
03170         newdb->db_remove_env = 1;
03171     }
03172     xx = rpmdbClose(olddb);
03173     xx = rpmdbClose(newdb);
03174 
03175     if (failed) {
03176         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
03177                 "remains in place\n"));
03178 
03179         xx = rpmdbRemoveDatabase(rootdir, newdbpath, _dbapi_rebuild);
03180         rc = 1;
03181         goto exit;
03182     } else if (!nocleanup) {
03183         if (rpmdbMoveDatabase(rootdir, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
03184             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
03185                         "database!\n"));
03186             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
03187                         "to recover"), dbpath, newdbpath);
03188             rc = 1;
03189             goto exit;
03190         }
03191     }
03192     rc = 0;
03193 
03194 exit:
03195     if (removedir && !(rc == 0 && nocleanup)) {
03196         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
03197         if (Rmdir(newrootdbpath))
03198             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
03199                         newrootdbpath, strerror(errno));
03200     }
03201     newrootdbpath = _free(newrootdbpath);
03202     rootdbpath = _free(rootdbpath);
03203 
03204     return rc;
03205 }

Generated at Mon Sep 24 10:37:29 2001 for rpm by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001