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

rpmdb/rpmdb.c

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

Generated on Fri Apr 4 14:39:41 2003 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002