Minimally convert rpmdbFindbyFile() to new headerGet()
[platform/upstream/rpm.git] / lib / rpmdb.c
1 /** \ingroup rpmdb dbi
2  * \file lib/rpmdb.c
3  */
4
5 #include "system.h"
6
7 #define _USE_COPY_LOAD  /* XXX don't use DB_DBT_MALLOC (yet) */
8
9 #include <sys/file.h>
10
11 #ifndef DYING   /* XXX already in "system.h" */
12 #include <fnmatch.h>
13 #endif
14
15 #include <regex.h>
16
17 #include <rpm/rpmtypes.h>
18 #include <rpm/rpmurl.h>
19 #include <rpm/rpmpgp.h>
20 #include <rpm/rpmpgp.h>
21 #include <rpm/rpmmacro.h>
22 #include <rpm/rpmsq.h>
23 #include <rpm/rpmstring.h>
24 #include <rpm/rpmfileutil.h>
25 #include <rpm/rpmds.h>                  /* XXX isInstallPreReq macro only */
26 #include <rpm/rpmlog.h>
27 #include <rpm/rpmdb.h>
28 #include <rpm/argv.h>
29
30 #include "lib/rpmdb_internal.h"
31 #include "lib/fprint.h"
32 #include "lib/header_internal.h"        /* XXX for HEADERFLAG_ALLOCATED */
33 #include "debug.h"
34
35 int _rpmdb_debug = 0;
36
37 static int _rebuildinprogress = 0;
38 static int _db_filter_dups = 0;
39
40 #define _DBI_FLAGS      0
41 #define _DBI_PERMS      0644
42 #define _DBI_MAJOR      -1
43
44 struct dbiTags_s {
45     rpmTag * tags;
46     rpmTag max;
47     int nlink;
48 };
49
50 /* XXX should dbitags be per-db instead? */
51 static struct dbiTags_s dbiTags = { NULL, 0, 0 };
52
53 /* Bit mask macros. */
54 typedef unsigned int __pbm_bits;
55 #define __PBM_NBITS             (8 * sizeof (__pbm_bits))
56 #define __PBM_IX(d)             ((d) / __PBM_NBITS)
57 #define __PBM_MASK(d)           ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
58 typedef struct {
59     __pbm_bits bits[1];
60 } pbm_set;
61 #define __PBM_BITS(set) ((set)->bits)
62
63 #define PBM_FREE(s)     _free(s);
64 #define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
65 #define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
66 #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
67
68 #define PBM_ALLOC(d)    xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
69
70 /**
71  * Reallocate a bit map.
72  * @retval sp           address of bit map pointer
73  * @retval odp          no. of bits in map
74  * @param nd            desired no. of bits
75  */
76 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
77 {
78     int i, nb;
79
80     if (nd > (*odp)) {
81         nd *= 2;
82         nb = __PBM_IX(nd) + 1;
83         *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
84         for (i = __PBM_IX(*odp) + 1; i < nb; i++)
85             __PBM_BITS(*sp)[i] = 0;
86         *odp = nd;
87     }
88     return *sp;
89 }
90
91 /**
92  * Return dbi index used for rpm tag.
93  * @param rpmtag        rpm header tag
94  * @return              dbi index, -1 on error
95  */
96 static int dbiTagToDbix(rpmTag rpmtag)
97 {
98     int dbix;
99
100     if (dbiTags.tags != NULL)
101     for (dbix = 0; dbix < dbiTags.max; dbix++) {
102         if (rpmtag == dbiTags.tags[dbix])
103             return dbix;
104     }
105     return -1;
106 }
107
108 /**
109  * Initialize database (index, tag) tuple from configuration.
110  */
111 static void dbiTagsInit(void)
112 {
113     static const char * const _dbiTagStr_default =
114         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filemd5s:Depends:Pubkeys";
115     char * dbiTagStr = NULL;
116     char * o, * oe;
117     rpmTag rpmtag;
118
119     dbiTags.nlink++;
120     if (dbiTags.tags != NULL && dbiTags.max > 0) {
121         return;
122     }
123
124     dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
125     if (!(dbiTagStr && *dbiTagStr)) {
126         dbiTagStr = _free(dbiTagStr);
127         dbiTagStr = xstrdup(_dbiTagStr_default);
128     }
129
130     /* Discard previous values. */
131     dbiTags.tags = _free(dbiTags.tags);
132     dbiTags.max = 0;
133
134     /* Always allocate package index */
135     dbiTags.tags = xcalloc(1, sizeof(*dbiTags.tags));
136     dbiTags.tags[dbiTags.max++] = RPMDBI_PACKAGES;
137
138     for (o = dbiTagStr; o && *o; o = oe) {
139         while (*o && risspace(*o))
140             o++;
141         if (*o == '\0')
142             break;
143         for (oe = o; oe && *oe; oe++) {
144             if (risspace(*oe))
145                 break;
146             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
147                 break;
148         }
149         if (oe && *oe)
150             *oe++ = '\0';
151         rpmtag = rpmTagGetValue(o);
152         if (rpmtag == RPMTAG_NOT_FOUND) {
153             rpmlog(RPMLOG_WARNING,
154                 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
155             continue;
156         }
157         if (dbiTagToDbix(rpmtag) >= 0)
158             continue;
159
160         dbiTags.tags = xrealloc(dbiTags.tags, (dbiTags.max + 1) * sizeof(*dbiTags.tags)); /* XXX memory leak */
161         dbiTags.tags[dbiTags.max++] = rpmtag;
162     }
163
164     dbiTagStr = _free(dbiTagStr);
165 }
166
167 static void dbiTagsFree(void)
168 {
169     if (--dbiTags.nlink > 0) {
170         return;
171     }
172     dbiTags.tags = _free(dbiTags.tags);
173     dbiTags.max = 0;
174 }
175
176 #define DB1vec          NULL
177 #define DB2vec          NULL
178
179 #ifdef HAVE_DB_H
180 extern struct _dbiVec db3vec;
181 #define DB3vec          &db3vec
182 #else
183 #define DB3vec          NULL
184 #endif
185
186 #ifdef HAVE_SQLITE3_H
187 extern struct _dbiVec sqlitevec;
188 #define SQLITEvec       &sqlitevec
189 #else
190 #define SQLITEvec       NULL
191 #endif
192
193 static struct _dbiVec * const mydbvecs[] = {
194     DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
195 };
196
197 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, unsigned int flags)
198 {
199     int dbix;
200     dbiIndex dbi = NULL;
201     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
202     int rc = 0;
203
204     if (db == NULL)
205         return NULL;
206
207     dbix = dbiTagToDbix(rpmtag);
208     if (dbix < 0 || dbix >= dbiTags.max)
209         return NULL;
210
211     /* Is this index already open ? */
212     /* FIX: db->_dbi may be NULL */
213     if ((dbi = db->_dbi[dbix]) != NULL)
214         return dbi;
215
216     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
217     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 4)
218         _dbapi_rebuild = 4;
219 /*    _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); */
220     _dbapi_wanted = (_rebuildinprogress ? _dbapi_rebuild : db->db_api);
221
222     switch (_dbapi_wanted) {
223     default:
224         _dbapi = _dbapi_wanted;
225         if (_dbapi < 0 || _dbapi >= 5 || mydbvecs[_dbapi] == NULL) {
226             rpmlog(RPMLOG_ERR, _("dbiOpen: dbapi %d not available\n"), _dbapi);
227             return NULL;
228         }
229         errno = 0;
230         dbi = NULL;
231         rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
232         if (rc) {
233             static int _printed[32];
234             if (!_printed[dbix & 0x1f]++)
235                 rpmlog(RPMLOG_ERR,
236                         _("cannot open %s index using db%d - %s (%d)\n"),
237                         rpmTagGetName(rpmtag), _dbapi,
238                         (rc > 0 ? strerror(rc) : ""), rc);
239             _dbapi = -1;
240         }
241         break;
242     case -1:
243         _dbapi = 5;
244         while (_dbapi-- > 1) {
245             if (mydbvecs[_dbapi] == NULL)
246                 continue;
247             errno = 0;
248             dbi = NULL;
249             rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
250             if (rc == 0 && dbi)
251                 break;
252         }
253         if (_dbapi <= 0) {
254             static int _printed[32];
255             if (!_printed[dbix & 0x1f]++)
256                 rpmlog(RPMLOG_ERR, _("cannot open %s index\n"),
257                         rpmTagGetName(rpmtag));
258             rc = 1;
259             goto exit;
260         }
261         if (db->db_api == -1 && _dbapi > 0)
262             db->db_api = _dbapi;
263         break;
264     }
265
266 /* We don't ever _REQUIRE_ conversion... */
267 #define SQLITE_HACK
268 #ifdef  SQLITE_HACK_XXX
269     /* Require conversion. */
270     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
271         rc = (_rebuildinprogress ? 0 : 1);
272         goto exit;
273     }
274
275     /* Suggest possible configuration */
276     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
277         rc = 1;
278         goto exit;
279     }
280
281     /* Suggest possible configuration */
282     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
283         rc = (_rebuildinprogress ? 0 : 1);
284         goto exit;
285     }
286 #endif
287
288 exit:
289     if (dbi != NULL && rc == 0) {
290         db->_dbi[dbix] = dbi;
291         if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
292             db->db_nbits = 1024;
293             if (!dbiStat(dbi, DB_FAST_STAT)) {
294                 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
295                 if (hash)
296                     db->db_nbits += hash->hash_nkeys;
297             }
298             db->db_bits = PBM_ALLOC(db->db_nbits);
299         }
300     }
301 #ifdef HAVE_DB_H
302       else
303         dbi = db3Free(dbi);
304 #endif
305
306 /* FIX: db->_dbi may be NULL */
307     return dbi;
308 }
309
310 /**
311  * Create and initialize item for index database set.
312  * @param hdrNum        header instance in db
313  * @param tagNum        tag index in header
314  * @return              new item
315  */
316 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
317 {
318     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
319     rec->hdrNum = hdrNum;
320     rec->tagNum = tagNum;
321     return rec;
322 }
323
324 union _dbswap {
325     unsigned int ui;
326     unsigned char uc[4];
327 };
328
329 #define _DBSWAP(_a) \
330 \
331   { unsigned char _b, *_c = (_a).uc; \
332     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
333     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
334 \
335   }
336
337 /**
338  * Convert retrieved data to index set.
339  * @param dbi           index database handle
340  * @param data          retrieved data
341  * @retval setp         (malloc'ed) index set
342  * @return              0 on success
343  */
344 static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp)
345 {
346     int _dbbyteswapped = dbiByteSwapped(dbi);
347     const char * sdbir;
348     dbiIndexSet set;
349     int i;
350
351     if (dbi == NULL || data == NULL || setp == NULL)
352         return -1;
353
354     if ((sdbir = data->data) == NULL) {
355         *setp = NULL;
356         return 0;
357     }
358
359     set = xmalloc(sizeof(*set));
360     set->count = data->size / dbi->dbi_jlen;
361     set->recs = xmalloc(set->count * sizeof(*(set->recs)));
362
363     switch (dbi->dbi_jlen) {
364     default:
365     case 2*sizeof(int32_t):
366         for (i = 0; i < set->count; i++) {
367             union _dbswap hdrNum, tagNum;
368
369             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
370             sdbir += sizeof(hdrNum.ui);
371             memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
372             sdbir += sizeof(tagNum.ui);
373             if (_dbbyteswapped) {
374                 _DBSWAP(hdrNum);
375                 _DBSWAP(tagNum);
376             }
377             set->recs[i].hdrNum = hdrNum.ui;
378             set->recs[i].tagNum = tagNum.ui;
379             set->recs[i].fpNum = 0;
380         }
381         break;
382     case 1*sizeof(int32_t):
383         for (i = 0; i < set->count; i++) {
384             union _dbswap hdrNum;
385
386             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
387             sdbir += sizeof(hdrNum.ui);
388             if (_dbbyteswapped) {
389                 _DBSWAP(hdrNum);
390             }
391             set->recs[i].hdrNum = hdrNum.ui;
392             set->recs[i].tagNum = 0;
393             set->recs[i].fpNum = 0;
394         }
395         break;
396     }
397     *setp = set;
398     return 0;
399 }
400
401 /**
402  * Convert index set to database representation.
403  * @param dbi           index database handle
404  * @param data          retrieved data
405  * @param set           index set
406  * @return              0 on success
407  */
408 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
409 {
410     int _dbbyteswapped = dbiByteSwapped(dbi);
411     char * tdbir;
412     int i;
413
414     if (dbi == NULL || data == NULL || set == NULL)
415         return -1;
416
417     data->size = set->count * (dbi->dbi_jlen);
418     if (data->size == 0) {
419         data->data = NULL;
420         return 0;
421     }
422     tdbir = data->data = xmalloc(data->size);
423
424     switch (dbi->dbi_jlen) {
425     default:
426     case 2*sizeof(int32_t):
427         for (i = 0; i < set->count; i++) {
428             union _dbswap hdrNum, tagNum;
429
430             memset(&hdrNum, 0, sizeof(hdrNum));
431             memset(&tagNum, 0, sizeof(tagNum));
432             hdrNum.ui = set->recs[i].hdrNum;
433             tagNum.ui = set->recs[i].tagNum;
434             if (_dbbyteswapped) {
435                 _DBSWAP(hdrNum);
436                 _DBSWAP(tagNum);
437             }
438             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
439             tdbir += sizeof(hdrNum.ui);
440             memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
441             tdbir += sizeof(tagNum.ui);
442         }
443         break;
444     case 1*sizeof(int32_t):
445         for (i = 0; i < set->count; i++) {
446             union _dbswap hdrNum;
447
448             memset(&hdrNum, 0, sizeof(hdrNum));
449             hdrNum.ui = set->recs[i].hdrNum;
450             if (_dbbyteswapped) {
451                 _DBSWAP(hdrNum);
452             }
453             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
454             tdbir += sizeof(hdrNum.ui);
455         }
456         break;
457     }
458
459     return 0;
460 }
461
462 /* XXX assumes hdrNum is first int in dbiIndexItem */
463 static int hdrNumCmp(const void * one, const void * two)
464 {
465     const int * a = one, * b = two;
466     return (*a - *b);
467 }
468
469 /**
470  * Append element(s) to set of index database items.
471  * @param set           set of index database items
472  * @param recs          array of items to append to set
473  * @param nrecs         number of items
474  * @param recsize       size of an array item
475  * @param sortset       should resulting set be sorted?
476  * @return              0 success, 1 failure (bad args)
477  */
478 static int dbiAppendSet(dbiIndexSet set, const void * recs,
479         int nrecs, size_t recsize, int sortset)
480 {
481     const char * rptr = recs;
482     size_t rlen = (recsize < sizeof(*(set->recs)))
483                 ? recsize : sizeof(*(set->recs));
484
485     if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
486         return 1;
487
488     set->recs = xrealloc(set->recs,
489                         (set->count + nrecs) * sizeof(*(set->recs)));
490
491     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
492
493     while (nrecs-- > 0) {
494         memcpy(set->recs + set->count, rptr, rlen);
495         rptr += recsize;
496         set->count++;
497     }
498
499     if (sortset && set->count > 1)
500         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
501
502     return 0;
503 }
504
505 /**
506  * Remove element(s) from set of index database items.
507  * @param set           set of index database items
508  * @param recs          array of items to remove from set
509  * @param nrecs         number of items
510  * @param recsize       size of an array item
511  * @param sorted        array is already sorted?
512  * @return              0 success, 1 failure (no items found)
513  */
514 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
515                 size_t recsize, int sorted)
516 {
517     int from;
518     int to = 0;
519     int num = set->count;
520     int numCopied = 0;
521
522     assert(set->count > 0);
523     if (nrecs > 1 && !sorted)
524         qsort(recs, nrecs, recsize, hdrNumCmp);
525
526     for (from = 0; from < num; from++) {
527         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
528             set->count--;
529             continue;
530         }
531         if (from != to)
532             set->recs[to] = set->recs[from]; /* structure assignment */
533         to++;
534         numCopied++;
535     }
536     return (numCopied == num);
537 }
538
539 /* XXX transaction.c */
540 unsigned int dbiIndexSetCount(dbiIndexSet set) {
541     return set->count;
542 }
543
544 /* XXX transaction.c */
545 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
546     return set->recs[recno].hdrNum;
547 }
548
549 /* XXX transaction.c */
550 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
551     return set->recs[recno].tagNum;
552 }
553
554 /* XXX transaction.c */
555 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
556     if (set) {
557         set->recs = _free(set->recs);
558         set = _free(set);
559     }
560     return set;
561 }
562
563 typedef struct miRE_s {
564     rpmTag              tag;            /*!< header tag */
565     rpmMireMode         mode;           /*!< pattern match mode */
566     char *              pattern;        /*!< pattern string */
567     int                 notmatch;       /*!< like "grep -v" */
568     regex_t *           preg;           /*!< regex compiled pattern buffer */
569     int                 cflags;         /*!< regcomp(3) flags */
570     int                 eflags;         /*!< regexec(3) flags */
571     int                 fnflags;        /*!< fnmatch(3) flags */
572 } * miRE;
573
574 struct rpmdbMatchIterator_s {
575     rpmdbMatchIterator  mi_next;
576     void *              mi_keyp;
577     size_t              mi_keylen;
578     rpmdb               mi_db;
579     rpmTag              mi_rpmtag;
580     dbiIndexSet         mi_set;
581     DBC *               mi_dbc;
582     DBT                 mi_key;
583     DBT                 mi_data;
584     int                 mi_setx;
585     Header              mi_h;
586     int                 mi_sorted;
587     int                 mi_cflags;
588     int                 mi_modified;
589     unsigned int        mi_prevoffset;  /* header instance (native endian) */
590     unsigned int        mi_offset;      /* header instance (native endian) */
591     unsigned int        mi_filenum;     /* tag element (native endian) */
592     int                 mi_nre;
593     miRE                mi_re;
594     rpmts               mi_ts;
595     rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, char ** msg);
596
597 };
598
599 static rpmdb rpmdbRock;
600
601 static rpmdbMatchIterator rpmmiRock;
602
603 int rpmdbCheckTerminate(int terminate)
604 {
605     sigset_t newMask, oldMask;
606     static int terminating = 0;
607
608     if (terminating) return 0;
609
610     (void) sigfillset(&newMask);                /* block all signals */
611     (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
612
613     if (sigismember(&rpmsqCaught, SIGINT)
614      || sigismember(&rpmsqCaught, SIGQUIT)
615      || sigismember(&rpmsqCaught, SIGHUP)
616      || sigismember(&rpmsqCaught, SIGTERM)
617      || sigismember(&rpmsqCaught, SIGPIPE)
618      || terminate)
619         terminating = 1;
620
621     if (terminating) {
622         rpmdb db;
623         rpmdbMatchIterator mi;
624
625         while ((mi = rpmmiRock) != NULL) {
626             rpmmiRock = mi->mi_next;
627             mi->mi_next = NULL;
628             mi = rpmdbFreeIterator(mi);
629         }
630
631         while ((db = rpmdbRock) != NULL) {
632             rpmdbRock = db->db_next;
633             db->db_next = NULL;
634             (void) rpmdbClose(db);
635         }
636     }
637     sigprocmask(SIG_SETMASK, &oldMask, NULL);
638     return terminating;
639 }
640
641 int rpmdbCheckSignals(void)
642 {
643     if (rpmdbCheckTerminate(0)) {
644         /* sigset_t is abstract type */
645         rpmlog(RPMLOG_DEBUG, "Exiting on signal(0x%lx) ...\n", 
646                 *((unsigned long *)&rpmsqCaught));
647         exit(EXIT_FAILURE);
648     }
649     return 0;
650 }
651
652 /**
653  * Block all signals, returning previous signal mask.
654  */
655 static int blockSignals(sigset_t * oldMask)
656 {
657     sigset_t newMask;
658
659     (void) sigfillset(&newMask);                /* block all signals */
660     (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
661     (void) sigdelset(&newMask, SIGINT);
662     (void) sigdelset(&newMask, SIGQUIT);
663     (void) sigdelset(&newMask, SIGHUP);
664     (void) sigdelset(&newMask, SIGTERM);
665     (void) sigdelset(&newMask, SIGPIPE);
666     return sigprocmask(SIG_BLOCK, &newMask, NULL);
667 }
668
669 /**
670  * Restore signal mask.
671  */
672 static int unblockSignals(sigset_t * oldMask)
673 {
674     (void) rpmdbCheckSignals();
675     return sigprocmask(SIG_SETMASK, oldMask, NULL);
676 }
677
678 #define _DB_ROOT        "/"
679 #define _DB_HOME        "%{_dbpath}"
680 #define _DB_FLAGS       0
681 #define _DB_MODE        0
682 #define _DB_PERMS       0644
683
684 #define _DB_MAJOR       -1
685 #define _DB_ERRPFX      "rpmdb"
686
687 static struct rpmdb_s const dbTemplate = {
688     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
689     _DB_MAJOR,  _DB_ERRPFX
690 };
691
692 static int isTemporaryDB(rpmTag rpmtag) 
693 {
694     int rc = 0;
695     switch (rpmtag) {
696     case RPMDBI_AVAILABLE:
697     case RPMDBI_ADDED:
698     case RPMDBI_REMOVED:
699     case RPMDBI_DEPENDS:
700         rc = 1;
701         break;
702     default:
703         break;
704     }
705     return rc;
706 }
707
708 rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
709 {
710     rpmop op = NULL;
711     switch (opx) {
712     case RPMDB_OP_DBGET:
713         op = &rpmdb->db_getops;
714         break;
715     case RPMDB_OP_DBPUT:
716         op = &rpmdb->db_putops;
717         break;
718     case RPMDB_OP_DBDEL:
719         op = &rpmdb->db_delops;
720         break;
721     default:
722         break;
723     }
724     return op;
725 }
726
727 int rpmdbSetChrootDone(rpmdb db, int chrootDone)
728 {
729     int ochrootDone = 0;
730     if (db != NULL) {
731         ochrootDone = db->db_chrootDone;
732         db->db_chrootDone = chrootDone;
733     }
734     return ochrootDone;
735 }
736
737 int rpmdbOpenAll(rpmdb db)
738 {
739     int dbix;
740     int rc = 0;
741
742     if (db == NULL) return -2;
743
744     if (dbiTags.tags != NULL)
745     for (dbix = 0; dbix < dbiTags.max; dbix++) {
746         if (db->_dbi[dbix] != NULL)
747             continue;
748         /* Filter out temporary databases */
749         if (isTemporaryDB(dbiTags.tags[dbix])) 
750             continue;
751         (void) dbiOpen(db, dbiTags.tags[dbix], db->db_flags);
752     }
753     return rc;
754 }
755
756 int rpmdbCloseDBI(rpmdb db, rpmTag rpmtag)
757 {
758     int dbix;
759     int rc = 0;
760
761     if (db == NULL || db->_dbi == NULL || dbiTags.tags == NULL)
762         return 0;
763
764     for (dbix = 0; dbix < dbiTags.max; dbix++) {
765         if (dbiTags.tags[dbix] != rpmtag)
766             continue;
767         if (db->_dbi[dbix] != NULL) {
768             int xx;
769                         /* FIX: double indirection. */
770             xx = dbiClose(db->_dbi[dbix], 0);
771             if (xx && rc == 0) rc = xx;
772             db->_dbi[dbix] = NULL;
773         }
774         break;
775     }
776     return rc;
777 }
778
779 /* XXX query.c, rpminstall.c, verify.c */
780 int rpmdbClose(rpmdb db)
781 {
782     rpmdb * prev, next;
783     int dbix;
784     int rc = 0;
785
786     if (db == NULL)
787         goto exit;
788
789     (void) rpmdbUnlink(db, RPMDBG_M("rpmdbClose"));
790
791     if (db->nrefs > 0)
792         goto exit;
793
794     if (db->_dbi)
795     for (dbix = db->db_ndbi; --dbix >= 0; ) {
796         int xx;
797         if (db->_dbi[dbix] == NULL)
798             continue;
799         xx = dbiClose(db->_dbi[dbix], 0);
800         if (xx && rc == 0) rc = xx;
801         db->_dbi[dbix] = NULL;
802     }
803     db->db_errpfx = _free(db->db_errpfx);
804     db->db_root = _free(db->db_root);
805     db->db_home = _free(db->db_home);
806     db->db_bits = PBM_FREE(db->db_bits);
807     db->_dbi = _free(db->_dbi);
808
809     prev = &rpmdbRock;
810     while ((next = *prev) != NULL && next != db)
811         prev = &next->db_next;
812     if (next) {
813         *prev = next->db_next;
814         next->db_next = NULL;
815     }
816
817     db = _free(db);
818
819     dbiTagsFree();
820
821 exit:
822     (void) rpmsqEnable(-SIGHUP, NULL);
823     (void) rpmsqEnable(-SIGINT, NULL);
824     (void) rpmsqEnable(-SIGTERM,NULL);
825     (void) rpmsqEnable(-SIGQUIT,NULL);
826     (void) rpmsqEnable(-SIGPIPE,NULL);
827     return rc;
828 }
829
830 int rpmdbSync(rpmdb db)
831 {
832     int dbix;
833     int rc = 0;
834
835     if (db == NULL) return 0;
836     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
837         int xx;
838         if (db->_dbi[dbix] == NULL)
839             continue;
840         if (db->_dbi[dbix]->dbi_no_dbsync)
841             continue;
842         xx = dbiSync(db->_dbi[dbix], 0);
843         if (xx && rc == 0) rc = xx;
844     }
845     return rc;
846 }
847
848 /* FIX: dbTemplate structure assignment */
849 static
850 rpmdb newRpmdb(const char * root,
851                 const char * home,
852                 int mode, int perms, int flags)
853 {
854     rpmdb db = xcalloc(sizeof(*db), 1);
855     const char * epfx = _DB_ERRPFX;
856     static int _initialized = 0;
857
858     if (!_initialized) {
859         _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
860         _initialized = 1;
861     }
862
863     *db = dbTemplate;   /* structure assignment */
864
865     db->_dbi = NULL;
866
867     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
868
869     if (mode >= 0)      db->db_mode = mode;
870     if (perms >= 0)     db->db_perms = perms;
871     if (flags >= 0)     db->db_flags = flags;
872
873     if (root && *root) {
874         db->db_root = rpmGetPath(root, NULL);
875     } else
876         db->db_root = rpmGetPath(_DB_ROOT, NULL);
877
878     db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
879     if (!(db->db_home && db->db_home[0] != '%')) {
880         rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
881         db->db_root = _free(db->db_root);
882         db->db_home = _free(db->db_home);
883         db = _free(db);
884         return NULL;
885     }
886     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
887     db->db_remove_env = 0;
888     db->db_filter_dups = _db_filter_dups;
889     db->db_ndbi = dbiTags.max;
890     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
891     db->nrefs = 0;
892     return rpmdbLink(db, RPMDBG_M("rpmdbCreate"));
893 }
894
895 static int openDatabase(const char * prefix,
896                 const char * dbpath,
897                 int _dbapi, rpmdb *dbp,
898                 int mode, int perms, int flags)
899 {
900     rpmdb db;
901     int rc, xx;
902     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
903     int minimal = flags & RPMDB_FLAG_MINIMAL;
904
905     dbiTagsInit();
906
907     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
908     if (_dbapi < -1 || _dbapi > 4)
909         _dbapi = -1;
910     if (_dbapi == 0)
911         _dbapi = 1;
912
913     if (dbp)
914         *dbp = NULL;
915     if (mode & O_WRONLY) 
916         return 1;
917
918     db = newRpmdb(prefix, dbpath, mode, perms, flags);
919     if (db == NULL)
920         return 1;
921
922     (void) rpmsqEnable(SIGHUP,  NULL);
923     (void) rpmsqEnable(SIGINT,  NULL);
924     (void) rpmsqEnable(SIGTERM,NULL);
925     (void) rpmsqEnable(SIGQUIT,NULL);
926     (void) rpmsqEnable(SIGPIPE,NULL);
927
928     db->db_api = _dbapi;
929
930     {   int dbix;
931
932         rc = 0;
933         if (dbiTags.tags != NULL)
934         for (dbix = 0; rc == 0 && dbix < dbiTags.max; dbix++) {
935             dbiIndex dbi;
936             rpmTag rpmtag;
937
938             /* Filter out temporary databases */
939             if (isTemporaryDB((rpmtag = dbiTags.tags[dbix])))
940                 continue;
941
942             dbi = dbiOpen(db, rpmtag, 0);
943             if (dbi == NULL) {
944                 rc = -2;
945                 break;
946             }
947
948             switch (rpmtag) {
949             case RPMDBI_PACKAGES:
950                 if (dbi == NULL) rc |= 1;
951 #if 0
952                 /* XXX open only Packages, indices created on the fly. */
953                 if (db->db_api == 3)
954 #endif
955                     goto exit;
956                 break;
957             case RPMTAG_NAME:
958                 if (dbi == NULL) rc |= 1;
959                 if (minimal)
960                     goto exit;
961                 break;
962             default:
963                 break;
964             }
965         }
966     }
967
968 exit:
969     if (rc || justCheck || dbp == NULL)
970         xx = rpmdbClose(db);
971     else {
972         db->db_next = rpmdbRock;
973         rpmdbRock = db;
974         *dbp = db;
975     }
976
977     return rc;
978 }
979
980 rpmdb rpmdbUnlink(rpmdb db, const char * msg)
981 {
982 if (_rpmdb_debug)
983 fprintf(stderr, "--> db %p -- %d %s\n", db, db->nrefs, msg);
984     db->nrefs--;
985     return NULL;
986 }
987
988 rpmdb rpmdbLink(rpmdb db, const char * msg)
989 {
990     db->nrefs++;
991 if (_rpmdb_debug)
992 fprintf(stderr, "--> db %p ++ %d %s\n", db, db->nrefs, msg);
993     return db;
994 }
995
996 /* XXX python/rpmmodule.c */
997 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
998 {
999     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1000     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
1001 }
1002
1003 int rpmdbInit (const char * prefix, int perms)
1004 {
1005     rpmdb db = NULL;
1006     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1007     int rc;
1008
1009     rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1010                 perms, RPMDB_FLAG_JUSTCHECK);
1011     if (db != NULL) {
1012         int xx;
1013         xx = rpmdbOpenAll(db);
1014         if (xx && rc == 0) rc = xx;
1015         xx = rpmdbClose(db);
1016         if (xx && rc == 0) rc = xx;
1017         db = NULL;
1018     }
1019     return rc;
1020 }
1021
1022 int rpmdbVerify(const char * prefix)
1023 {
1024     rpmdb db = NULL;
1025     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1026     int rc = 0;
1027
1028     rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1029
1030     if (db != NULL) {
1031         int dbix;
1032         int xx;
1033         rc = rpmdbOpenAll(db);
1034
1035         for (dbix = db->db_ndbi; --dbix >= 0; ) {
1036             if (db->_dbi[dbix] == NULL)
1037                 continue;
1038                         /* FIX: double indirection. */
1039             xx = dbiVerify(db->_dbi[dbix], 0);
1040             if (xx && rc == 0) rc = xx;
1041             db->_dbi[dbix] = NULL;
1042         }
1043
1044         /* FIX: db->_dbi[] may be NULL. */
1045         xx = rpmdbClose(db);
1046         if (xx && rc == 0) rc = xx;
1047         db = NULL;
1048     }
1049     return rc;
1050 }
1051
1052 /**
1053  * Find file matches in database.
1054  * @param db            rpm database
1055  * @param filespec
1056  * @param key
1057  * @param data
1058  * @param matches
1059  * @return              0 on success, 1 on not found, -2 on error
1060  */
1061 static int rpmdbFindByFile(rpmdb db, const char * filespec,
1062                 DBT * key, DBT * data, dbiIndexSet * matches)
1063 {
1064     char * dirName;
1065     const char * baseName;
1066     fingerPrintCache fpc;
1067     fingerPrint fp1;
1068     dbiIndex dbi = NULL;
1069     DBC * dbcursor;
1070     dbiIndexSet allMatches = NULL;
1071     dbiIndexItem rec = NULL;
1072     int i;
1073     int rc;
1074     int xx;
1075
1076     *matches = NULL;
1077     if (filespec == NULL) return -2;
1078
1079     if ((baseName = strrchr(filespec, '/')) != NULL) {
1080         size_t len = baseName - filespec + 1;
1081         dirName = strncpy(xmalloc(len + 1), filespec, len);
1082         dirName[len] = '\0';
1083         baseName++;
1084     } else {
1085         dirName = xstrdup("");
1086         baseName = filespec;
1087     }
1088     if (baseName == NULL)
1089         return -2;
1090
1091     fpc = fpCacheCreate(20);
1092     fp1 = fpLookup(fpc, dirName, baseName, 1);
1093     free(dirName);
1094
1095     dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1096     if (dbi != NULL) {
1097         dbcursor = NULL;
1098         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1099
1100         key->data = (void *) baseName;
1101         key->size = strlen(baseName);
1102         if (key->size == 0) 
1103             key->size++;        /* XXX "/" fixup. */
1104
1105         rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1106         if (rc > 0) {
1107             rpmlog(RPMLOG_ERR,
1108                 _("error(%d) getting \"%s\" records from %s index\n"),
1109                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1110         }
1111
1112         if (rc == 0)
1113             (void) dbt2set(dbi, data, &allMatches);
1114
1115         xx = dbiCclose(dbi, dbcursor, 0);
1116         dbcursor = NULL;
1117     } else
1118         rc = -2;
1119
1120     if (rc) {
1121         allMatches = dbiFreeIndexSet(allMatches);
1122         fpc = fpCacheFree(fpc);
1123         return rc;
1124     }
1125
1126     *matches = xcalloc(1, sizeof(**matches));
1127     rec = dbiIndexNewItem(0, 0);
1128     i = 0;
1129     if (allMatches != NULL)
1130     while (i < allMatches->count) {
1131         struct rpmtd_s bn, dn, di;
1132         const char ** baseNames, ** dirNames;
1133         uint32_t * dirIndexes;
1134         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
1135         unsigned int prevoff;
1136         Header h;
1137
1138         {   rpmdbMatchIterator mi;
1139             mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1140             h = rpmdbNextIterator(mi);
1141             if (h)
1142                 h = headerLink(h);
1143             mi = rpmdbFreeIterator(mi);
1144         }
1145
1146         if (h == NULL) {
1147             i++;
1148             continue;
1149         }
1150
1151         headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM);
1152         headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
1153         headerGet(h, RPMTAG_DIRINDEXES, &di, HEADERGET_MINMEM);
1154         baseNames = bn.data;
1155         dirNames = dn.data;
1156         dirIndexes = di.data;
1157
1158         do {
1159             fingerPrint fp2;
1160             int num = dbiIndexRecordFileNumber(allMatches, i);
1161
1162             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1163             if (FP_EQUAL(fp1, fp2)) {
1164                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1165                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1166                 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1167             }
1168
1169             prevoff = offset;
1170             i++;
1171             if (i < allMatches->count)
1172                 offset = dbiIndexRecordOffset(allMatches, i);
1173         } while (i < allMatches->count && offset == prevoff);
1174
1175         rpmtdFreeData(&bn);
1176         rpmtdFreeData(&dn);
1177         rpmtdFreeData(&di);
1178         h = headerFree(h);
1179     }
1180
1181     rec = _free(rec);
1182     allMatches = dbiFreeIndexSet(allMatches);
1183
1184     fpc = fpCacheFree(fpc);
1185
1186     if ((*matches)->count == 0) {
1187         *matches = dbiFreeIndexSet(*matches);
1188         return 1;
1189     }
1190
1191     return 0;
1192 }
1193
1194 /* XXX python/upgrade.c, install.c, uninstall.c */
1195 int rpmdbCountPackages(rpmdb db, const char * name)
1196 {
1197     DBC * dbcursor = NULL;
1198     DBT key; 
1199     DBT data; 
1200     dbiIndex dbi;
1201     int rc;
1202     int xx;
1203
1204     if (db == NULL)
1205         return 0;
1206
1207     memset(&key, 0, sizeof(key));
1208     memset(&data, 0, sizeof(data));
1209
1210     dbi = dbiOpen(db, RPMTAG_NAME, 0);
1211     if (dbi == NULL)
1212         return 0;
1213
1214     key.data = (void *) name;
1215     key.size = strlen(name);
1216
1217     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1218     rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
1219 #ifndef SQLITE_HACK
1220     xx = dbiCclose(dbi, dbcursor, 0);
1221     dbcursor = NULL;
1222 #endif
1223
1224     if (rc == 0) {              /* success */
1225         dbiIndexSet matches;
1226         /* FIX: matches might be NULL */
1227         matches = NULL;
1228         (void) dbt2set(dbi, &data, &matches);
1229         if (matches) {
1230             rc = dbiIndexSetCount(matches);
1231             matches = dbiFreeIndexSet(matches);
1232         }
1233     } else
1234     if (rc == DB_NOTFOUND) {    /* not found */
1235         rc = 0;
1236     } else {                    /* error */
1237         rpmlog(RPMLOG_ERR,
1238                 _("error(%d) getting \"%s\" records from %s index\n"),
1239                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
1240         rc = -1;
1241     }
1242
1243 #ifdef  SQLITE_HACK
1244     xx = dbiCclose(dbi, dbcursor, 0);
1245     dbcursor = NULL;
1246 #endif
1247
1248     return rc;
1249 }
1250
1251 /**
1252  * Attempt partial matches on name[-version[-release]] strings.
1253  * @param dbi           index database handle (always RPMTAG_NAME)
1254  * @param dbcursor      index database cursor
1255  * @param key           search key/length/flags
1256  * @param data          search data/length/flags
1257  * @param name          package name
1258  * @param version       package version (can be a pattern)
1259  * @param release       package release (can be a pattern)
1260  * @retval matches      set of header instances that match
1261  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1262  */
1263 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1264                 DBT * key, DBT * data,
1265                 const char * name,
1266                 const char * version,
1267                 const char * release,
1268                 dbiIndexSet * matches)
1269 {
1270     int gotMatches = 0;
1271     int rc;
1272     int i;
1273
1274     key->data = (void *) name;
1275     key->size = strlen(name);
1276
1277     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1278
1279     if (rc == 0) {              /* success */
1280         (void) dbt2set(dbi, data, matches);
1281         if (version == NULL && release == NULL)
1282             return RPMRC_OK;
1283     } else
1284     if (rc == DB_NOTFOUND) {    /* not found */
1285         return RPMRC_NOTFOUND;
1286     } else {                    /* error */
1287         rpmlog(RPMLOG_ERR,
1288                 _("error(%d) getting \"%s\" records from %s index\n"),
1289                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1290         return RPMRC_FAIL;
1291     }
1292
1293     /* Make sure the version and release match. */
1294     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1295         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1296         rpmdbMatchIterator mi;
1297         Header h;
1298
1299         if (recoff == 0)
1300             continue;
1301
1302         mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1303                         RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1304
1305         /* Set iterator selectors for version/release if available. */
1306         if (version &&
1307             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1308         {
1309             rc = RPMRC_FAIL;
1310             goto exit;
1311         }
1312         if (release &&
1313             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1314         {
1315             rc = RPMRC_FAIL;
1316             goto exit;
1317         }
1318
1319         h = rpmdbNextIterator(mi);
1320         if (h)
1321             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1322         else
1323             (*matches)->recs[i].hdrNum = 0;
1324         mi = rpmdbFreeIterator(mi);
1325     }
1326
1327     if (gotMatches) {
1328         (*matches)->count = gotMatches;
1329         rc = RPMRC_OK;
1330     } else
1331         rc = RPMRC_NOTFOUND;
1332
1333 exit:
1334 /* FIX: double indirection */
1335     if (rc && matches && *matches)
1336         *matches = dbiFreeIndexSet(*matches);
1337     return rc;
1338 }
1339
1340 /**
1341  * Lookup by name, name-version, and finally by name-version-release.
1342  * Both version and release can be patterns.
1343  * @todo Name must be an exact match, as name is a db key.
1344  * @param dbi           index database handle (always RPMTAG_NAME)
1345  * @param dbcursor      index database cursor
1346  * @param key           search key/length/flags
1347  * @param data          search data/length/flags
1348  * @param arg           name[-version[-release]] string
1349  * @retval matches      set of header instances that match
1350  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1351  */
1352 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1353                 const char * arg, dbiIndexSet * matches)
1354 {
1355     const char * release;
1356     char * localarg;
1357     char * s;
1358     char c;
1359     int brackets;
1360     rpmRC rc;
1361  
1362     if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
1363
1364     /* did they give us just a name? */
1365     rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
1366     if (rc != RPMRC_NOTFOUND) return rc;
1367
1368     /* FIX: double indirection */
1369     *matches = dbiFreeIndexSet(*matches);
1370
1371     /* maybe a name and a release */
1372     localarg = xmalloc(strlen(arg) + 1);
1373     s = stpcpy(localarg, arg);
1374
1375     c = '\0';
1376     brackets = 0;
1377     for (s -= 1; s > localarg; s--) {
1378         switch (*s) {
1379         case '[':
1380             brackets = 1;
1381             break;
1382         case ']':
1383             if (c != '[') brackets = 0;
1384             break;
1385         }
1386         c = *s;
1387         if (!brackets && *s == '-')
1388             break;
1389     }
1390
1391         /* FIX: *matches may be NULL. */
1392     if (s == localarg) {
1393         rc = RPMRC_NOTFOUND;
1394         goto exit;
1395     }
1396
1397     *s = '\0';
1398     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1399     if (rc != RPMRC_NOTFOUND) goto exit;
1400
1401     /* FIX: double indirection */
1402     *matches = dbiFreeIndexSet(*matches);
1403     
1404     /* how about name-version-release? */
1405
1406     release = s + 1;
1407
1408     c = '\0';
1409     brackets = 0;
1410     for (; s > localarg; s--) {
1411         switch (*s) {
1412         case '[':
1413             brackets = 1;
1414             break;
1415         case ']':
1416             if (c != '[') brackets = 0;
1417             break;
1418         }
1419         c = *s;
1420         if (!brackets && *s == '-')
1421             break;
1422     }
1423
1424     if (s == localarg) {
1425         rc = RPMRC_NOTFOUND;
1426         goto exit;
1427     }
1428
1429     *s = '\0';
1430         /* FIX: *matches may be NULL. */
1431     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
1432 exit:
1433     free(localarg);
1434     return rc;
1435 }
1436
1437 /**
1438  * Rewrite a header into packages (if necessary) and free the header.
1439  *   Note: this is called from a markReplacedFiles iteration, and *must*
1440  *   preserve the "join key" (i.e. offset) for the header.
1441  * @param mi            database iterator
1442  * @param dbi           index database handle
1443  * @return              0 on success
1444  */
1445 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1446 {
1447     int rc = 0;
1448
1449     if (mi == NULL || mi->mi_h == NULL)
1450         return 0;
1451
1452     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
1453         DBT * key = &mi->mi_key;
1454         DBT * data = &mi->mi_data;
1455         sigset_t signalMask;
1456         rpmRC rpmrc = RPMRC_NOTFOUND;
1457         int xx;
1458
1459         key->data = (void *) &mi->mi_prevoffset;
1460         key->size = sizeof(mi->mi_prevoffset);
1461         data->data = headerUnload(mi->mi_h);
1462         data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
1463
1464         /* Check header digest/signature on blob export (if requested). */
1465         if (mi->mi_hdrchk && mi->mi_ts) {
1466             char * msg = NULL;
1467             int lvl;
1468
1469             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
1470             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
1471             rpmlog(lvl, "%s h#%8u %s",
1472                 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
1473                         mi->mi_prevoffset, (msg ? msg : "\n"));
1474             msg = _free(msg);
1475         }
1476
1477         if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1478             (void) blockSignals(&signalMask);
1479             rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1480             if (rc) {
1481                 rpmlog(RPMLOG_ERR,
1482                         _("error(%d) storing record #%d into %s\n"),
1483                         rc, mi->mi_prevoffset, rpmTagGetName(dbi->dbi_rpmtag));
1484             }
1485             xx = dbiSync(dbi, 0);
1486             (void) unblockSignals(&signalMask);
1487         }
1488         data->data = _free(data->data);
1489         data->size = 0;
1490     }
1491
1492     mi->mi_h = headerFree(mi->mi_h);
1493
1494     return rc;
1495 }
1496
1497 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1498 {
1499     rpmdbMatchIterator * prev, next;
1500     dbiIndex dbi;
1501     int xx;
1502     int i;
1503
1504     if (mi == NULL)
1505         return NULL;
1506
1507     prev = &rpmmiRock;
1508     while ((next = *prev) != NULL && next != mi)
1509         prev = &next->mi_next;
1510     if (next) {
1511         *prev = next->mi_next;
1512         next->mi_next = NULL;
1513     }
1514
1515     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1516     if (dbi == NULL)    /* XXX can't happen */
1517         return NULL;
1518
1519     xx = miFreeHeader(mi, dbi);
1520
1521     if (mi->mi_dbc)
1522         xx = dbiCclose(dbi, mi->mi_dbc, 0);
1523     mi->mi_dbc = NULL;
1524
1525     if (mi->mi_re != NULL)
1526     for (i = 0; i < mi->mi_nre; i++) {
1527         miRE mire = mi->mi_re + i;
1528         mire->pattern = _free(mire->pattern);
1529         if (mire->preg != NULL) {
1530             regfree(mire->preg);
1531             /* LCL: regfree has bogus only */
1532             mire->preg = _free(mire->preg);
1533         }
1534     }
1535     mi->mi_re = _free(mi->mi_re);
1536
1537     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
1538     mi->mi_keyp = _free(mi->mi_keyp);
1539     mi->mi_db = rpmdbUnlink(mi->mi_db, RPMDBG_M("matchIterator"));
1540
1541     mi = _free(mi);
1542
1543     (void) rpmdbCheckSignals();
1544
1545     return mi;
1546 }
1547
1548 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1549     return (mi ? mi->mi_offset : 0);
1550 }
1551
1552 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1553     return (mi ? mi->mi_filenum : 0);
1554 }
1555
1556 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1557     return (mi && mi->mi_set ?  mi->mi_set->count : 0);
1558 }
1559
1560 /**
1561  * Return pattern match.
1562  * @param mire          match iterator regex
1563  * @param val           value to match
1564  * @return              0 if pattern matches, >0 on nomatch, <0 on error
1565  */
1566 static int miregexec(miRE mire, const char * val)
1567 {
1568     int rc = 0;
1569
1570     switch (mire->mode) {
1571     case RPMMIRE_STRCMP:
1572         rc = strcmp(mire->pattern, val);
1573         if (rc) rc = 1;
1574         break;
1575     case RPMMIRE_DEFAULT:
1576     case RPMMIRE_REGEX:
1577         rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1578         if (rc && rc != REG_NOMATCH) {
1579             char msg[256];
1580             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1581             msg[sizeof(msg)-1] = '\0';
1582             rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s\n"),
1583                         mire->pattern, msg);
1584             rc = -1;
1585         }
1586         break;
1587     case RPMMIRE_GLOB:
1588         rc = fnmatch(mire->pattern, val, mire->fnflags);
1589         if (rc && rc != FNM_NOMATCH)
1590             rc = -1;
1591         break;
1592     default:
1593         rc = -1;
1594         break;
1595     }
1596
1597     return rc;
1598 }
1599
1600 /**
1601  * Compare iterator selectors by rpm tag (qsort/bsearch).
1602  * @param a             1st iterator selector
1603  * @param b             2nd iterator selector
1604  * @return              result of comparison
1605  */
1606 static int mireCmp(const void * a, const void * b)
1607 {
1608     const miRE mireA = (const miRE) a;
1609     const miRE mireB = (const miRE) b;
1610     return (mireA->tag - mireB->tag);
1611 }
1612
1613 /**
1614  * Copy pattern, escaping for appropriate mode.
1615  * @param tag           rpm tag
1616  * @retval modep        type of pattern match
1617  * @param pattern       pattern to duplicate
1618  * @return              duplicated pattern
1619  */
1620 static char * mireDup(rpmTag tag, rpmMireMode *modep,
1621                         const char * pattern)
1622 {
1623     const char * s;
1624     char * pat;
1625     char * t;
1626     int brackets;
1627     size_t nb;
1628     int c;
1629
1630     switch (*modep) {
1631     default:
1632     case RPMMIRE_DEFAULT:
1633         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1634             *modep = RPMMIRE_GLOB;
1635             pat = xstrdup(pattern);
1636             break;
1637         }
1638
1639         nb = strlen(pattern) + sizeof("^$");
1640
1641         /* Find no. of bytes needed for pattern. */
1642         /* periods and plusses are escaped, splats become '.*' */
1643         c = '\0';
1644         brackets = 0;
1645         for (s = pattern; *s != '\0'; s++) {
1646             switch (*s) {
1647             case '.':
1648             case '+':
1649             case '*':
1650                 if (!brackets) nb++;
1651                 break;
1652             case '\\':
1653                 s++;
1654                 break;
1655             case '[':
1656                 brackets = 1;
1657                 break;
1658             case ']':
1659                 if (c != '[') brackets = 0;
1660                 break;
1661             }
1662             c = *s;
1663         }
1664
1665         pat = t = xmalloc(nb);
1666
1667         if (pattern[0] != '^') *t++ = '^';
1668
1669         /* Copy pattern, escaping periods, prefixing splats with period. */
1670         c = '\0';
1671         brackets = 0;
1672         for (s = pattern; *s != '\0'; s++, t++) {
1673             switch (*s) {
1674             case '.':
1675             case '+':
1676                 if (!brackets) *t++ = '\\';
1677                 break;
1678             case '*':
1679                 if (!brackets) *t++ = '.';
1680                 break;
1681             case '\\':
1682                 *t++ = *s++;
1683                 break;
1684             case '[':
1685                 brackets = 1;
1686                 break;
1687             case ']':
1688                 if (c != '[') brackets = 0;
1689                 break;
1690             }
1691             c = *t = *s;
1692         }
1693
1694         if (s > pattern && s[-1] != '$') *t++ = '$';
1695         *t = '\0';
1696         *modep = RPMMIRE_REGEX;
1697         break;
1698     case RPMMIRE_STRCMP:
1699     case RPMMIRE_REGEX:
1700     case RPMMIRE_GLOB:
1701         pat = xstrdup(pattern);
1702         break;
1703     }
1704
1705     return pat;
1706 }
1707
1708 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1709                 rpmMireMode mode, const char * pattern)
1710 {
1711     static rpmMireMode defmode = (rpmMireMode)-1;
1712     miRE mire = NULL;
1713     char * allpat = NULL;
1714     int notmatch = 0;
1715     regex_t * preg = NULL;
1716     int cflags = 0;
1717     int eflags = 0;
1718     int fnflags = 0;
1719     int rc = 0;
1720
1721     if (defmode == (rpmMireMode)-1) {
1722         char *t = rpmExpand("%{?_query_selector_match}", NULL);
1723
1724         if (*t == '\0' || !strcmp(t, "default"))
1725             defmode = RPMMIRE_DEFAULT;
1726         else if (!strcmp(t, "strcmp"))
1727             defmode = RPMMIRE_STRCMP;
1728         else if (!strcmp(t, "regex"))
1729             defmode = RPMMIRE_REGEX;
1730         else if (!strcmp(t, "glob"))
1731             defmode = RPMMIRE_GLOB;
1732         else
1733             defmode = RPMMIRE_DEFAULT;
1734         t = _free(t);
1735      }
1736
1737     if (mi == NULL || pattern == NULL)
1738         return rc;
1739
1740     /* Leading '!' inverts pattern match sense, like "grep -v". */
1741     if (*pattern == '!') {
1742         notmatch = 1;
1743         pattern++;
1744     }
1745
1746     allpat = mireDup(tag, &mode, pattern);
1747
1748     if (mode == RPMMIRE_DEFAULT)
1749         mode = defmode;
1750
1751     switch (mode) {
1752     case RPMMIRE_DEFAULT:
1753     case RPMMIRE_STRCMP:
1754         break;
1755     case RPMMIRE_REGEX:
1756         preg = xcalloc(1, sizeof(*preg));
1757         cflags = (REG_EXTENDED | REG_NOSUB);
1758         rc = regcomp(preg, allpat, cflags);
1759         if (rc) {
1760             char msg[256];
1761             (void) regerror(rc, preg, msg, sizeof(msg)-1);
1762             msg[sizeof(msg)-1] = '\0';
1763             rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"), allpat, msg);
1764         }
1765         break;
1766     case RPMMIRE_GLOB:
1767         fnflags = FNM_PATHNAME | FNM_PERIOD;
1768         break;
1769     default:
1770         rc = -1;
1771         break;
1772     }
1773
1774     if (rc) {
1775         /* FIX: mire has kept values */
1776         allpat = _free(allpat);
1777         if (preg) {
1778             regfree(preg);
1779             /* LCL: regfree has bogus only */
1780             preg = _free(preg);
1781         }
1782         return rc;
1783     }
1784
1785     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1786     mire = mi->mi_re + mi->mi_nre;
1787     mi->mi_nre++;
1788     
1789     mire->tag = tag;
1790     mire->mode = mode;
1791     mire->pattern = allpat;
1792     mire->notmatch = notmatch;
1793     mire->preg = preg;
1794     mire->cflags = cflags;
1795     mire->eflags = eflags;
1796     mire->fnflags = fnflags;
1797
1798     if (mi->mi_nre > 1)
1799         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1800
1801     return rc;
1802 }
1803
1804 /**
1805  * Return iterator selector match.
1806  * @param mi            rpm database iterator
1807  * @return              1 if header should be skipped
1808  */
1809 static int mireSkip (const rpmdbMatchIterator mi)
1810 {
1811     miRE mire;
1812     static int32_t zero = 0;
1813     int ntags = 0;
1814     int nmatches = 0;
1815     int rc;
1816
1817     if (mi->mi_h == NULL)       /* XXX can't happen */
1818         return 0;
1819
1820     /*
1821      * Apply tag tests, implicitly "||" for multiple patterns/values of a
1822      * single tag, implicitly "&&" between multiple tag patterns.
1823      */
1824     if ((mire = mi->mi_re) != NULL)
1825     for (int i = 0; i < mi->mi_nre; i++, mire++) {
1826         int anymatch;
1827         struct rpmtd_s td;
1828
1829         if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
1830             if (mire->tag != RPMTAG_EPOCH) {
1831                 ntags++;
1832                 continue;
1833             }
1834             /* "is package already installed" checks rely on this behavior */
1835             td.count = 1;
1836             td.type = RPM_INT32_TYPE;
1837             td.data = &zero;
1838         }
1839
1840         anymatch = 0;           /* no matches yet */
1841         while (1) {
1842             rpmtdInit(&td);
1843             while (rpmtdNext(&td) >= 0) {
1844                 char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
1845                 if (str) {
1846                     rc = miregexec(mire, str);
1847                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1848                         anymatch++;
1849                     free(str);
1850                 }
1851             }
1852             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
1853                 i++;
1854                 mire++;
1855                 continue;
1856             }
1857             break;
1858         }
1859         rpmtdFreeData(&td);
1860
1861         ntags++;
1862         if (anymatch)
1863             nmatches++;
1864     }
1865
1866     return (ntags == nmatches ? 0 : 1);
1867 }
1868
1869 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
1870 {
1871     int rc;
1872     if (mi == NULL)
1873         return 0;
1874     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
1875     if (rewrite)
1876         mi->mi_cflags |= DB_WRITECURSOR;
1877     else
1878         mi->mi_cflags &= ~DB_WRITECURSOR;
1879     return rc;
1880 }
1881
1882 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
1883 {
1884     int rc;
1885     if (mi == NULL)
1886         return 0;
1887     rc = mi->mi_modified;
1888     mi->mi_modified = modified;
1889     return rc;
1890 }
1891
1892 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
1893         rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
1894 {
1895     int rc = 0;
1896     if (mi == NULL)
1897         return 0;
1898     /* XXX forward linkage prevents rpmtsLink */
1899     mi->mi_ts = ts;
1900     mi->mi_hdrchk = hdrchk;
1901     return rc;
1902 }
1903
1904
1905 /* FIX: mi->mi_key.data may be NULL */
1906 Header rpmdbNextIterator(rpmdbMatchIterator mi)
1907 {
1908     dbiIndex dbi;
1909     void * uh;
1910     size_t uhlen;
1911     DBT * key;
1912     DBT * data;
1913     void * keyp;
1914     size_t keylen;
1915     int rc;
1916     int xx;
1917
1918     if (mi == NULL)
1919         return NULL;
1920
1921     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1922     if (dbi == NULL)
1923         return NULL;
1924
1925     /*
1926      * Cursors are per-iterator, not per-dbi, so get a cursor for the
1927      * iterator on 1st call. If the iteration is to rewrite headers, and the
1928      * CDB model is used for the database, then the cursor needs to
1929      * marked with DB_WRITECURSOR as well.
1930      */
1931     if (mi->mi_dbc == NULL)
1932         xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
1933
1934     key = &mi->mi_key;
1935     memset(key, 0, sizeof(*key));
1936     data = &mi->mi_data;
1937     memset(data, 0, sizeof(*data));
1938
1939 top:
1940     uh = NULL;
1941     uhlen = 0;
1942
1943     do {
1944         union _dbswap mi_offset;
1945
1946         if (mi->mi_set) {
1947             if (!(mi->mi_setx < mi->mi_set->count))
1948                 return NULL;
1949             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1950             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1951             mi_offset.ui = mi->mi_offset;
1952             if (dbiByteSwapped(dbi) == 1)
1953                 _DBSWAP(mi_offset);
1954             keyp = &mi_offset;
1955             keylen = sizeof(mi_offset.ui);
1956         } else {
1957
1958             key->data = keyp = (void *)mi->mi_keyp;
1959             key->size = keylen = mi->mi_keylen;
1960             data->data = uh;
1961             data->size = uhlen;
1962 #if !defined(_USE_COPY_LOAD)
1963             data->flags |= DB_DBT_MALLOC;
1964 #endif
1965             rc = dbiGet(dbi, mi->mi_dbc, key, data,
1966                         (key->data == NULL ? DB_NEXT : DB_SET));
1967             data->flags = 0;
1968             keyp = key->data;
1969             keylen = key->size;
1970             uh = data->data;
1971             uhlen = data->size;
1972
1973             /*
1974              * If we got the next key, save the header instance number.
1975              *
1976              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
1977              * largest header instance in the database, and should be
1978              * skipped.
1979              */
1980             if (keyp && mi->mi_setx && rc == 0) {
1981                 memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
1982             if (dbiByteSwapped(dbi) == 1)
1983                 _DBSWAP(mi_offset);
1984                 mi->mi_offset = mi_offset.ui;
1985             }
1986
1987             /* Terminate on error or end of keys */
1988             if (rc || (mi->mi_setx && mi->mi_offset == 0))
1989                 return NULL;
1990         }
1991         mi->mi_setx++;
1992     } while (mi->mi_offset == 0);
1993
1994     /* If next header is identical, return it now. */
1995     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
1996         return mi->mi_h;
1997
1998     /* Retrieve next header blob for index iterator. */
1999     if (uh == NULL) {
2000         key->data = keyp;
2001         key->size = keylen;
2002 #if !defined(_USE_COPY_LOAD)
2003         data->flags |= DB_DBT_MALLOC;
2004 #endif
2005         rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2006         data->flags = 0;
2007         keyp = key->data;
2008         keylen = key->size;
2009         uh = data->data;
2010         uhlen = data->size;
2011         if (rc)
2012             return NULL;
2013     }
2014
2015     /* Rewrite current header (if necessary) and unlink. */
2016     xx = miFreeHeader(mi, dbi);
2017
2018     /* Is this the end of the iteration? */
2019     if (uh == NULL)
2020         return NULL;
2021
2022     /* Check header digest/signature once (if requested). */
2023     if (mi->mi_hdrchk && mi->mi_ts) {
2024         rpmRC rpmrc = RPMRC_NOTFOUND;
2025
2026         /* Don't bother re-checking a previously read header. */
2027         if (mi->mi_db->db_bits) {
2028             pbm_set * set;
2029
2030             set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2031                         &mi->mi_db->db_nbits, mi->mi_offset);
2032             if (PBM_ISSET(mi->mi_offset, set))
2033                 rpmrc = RPMRC_OK;
2034         }
2035
2036         /* If blob is unchecked, check blob import consistency now. */
2037         if (rpmrc != RPMRC_OK) {
2038             char * msg = NULL;
2039             int lvl;
2040
2041             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
2042             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2043             rpmlog(lvl, "%s h#%8u %s",
2044                 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
2045                         mi->mi_offset, (msg ? msg : "\n"));
2046             msg = _free(msg);
2047
2048             /* Mark header checked. */
2049             if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
2050                 pbm_set * set;
2051
2052                 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2053                         &mi->mi_db->db_nbits, mi->mi_offset);
2054                 PBM_SET(mi->mi_offset, set);
2055             }
2056
2057             /* Skip damaged and inconsistent headers. */
2058             if (rpmrc == RPMRC_FAIL)
2059                 goto top;
2060         }
2061     }
2062
2063     /* Did the header blob load correctly? */
2064 #if !defined(_USE_COPY_LOAD)
2065     mi->mi_h = headerLoad(uh);
2066     if (mi->mi_h)
2067         mi->mi_h->flags |= HEADERFLAG_ALLOCATED;
2068 #else
2069     mi->mi_h = headerCopyLoad(uh);
2070 #endif
2071     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2072         rpmlog(RPMLOG_ERR,
2073                 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2074                 mi->mi_offset);
2075         goto top;
2076     }
2077
2078     /*
2079      * Skip this header if iterator selector (if any) doesn't match.
2080      */
2081     if (mireSkip(mi)) {
2082         /* XXX hack, can't restart with Packages locked on single instance. */
2083         if (mi->mi_set || mi->mi_keyp == NULL)
2084             goto top;
2085         return NULL;
2086     }
2087
2088     mi->mi_prevoffset = mi->mi_offset;
2089     mi->mi_modified = 0;
2090
2091     return mi->mi_h;
2092 }
2093
2094 static void rpmdbSortIterator(rpmdbMatchIterator mi)
2095 {
2096     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2097     /*
2098      * mergesort is much (~10x with lots of identical basenames) faster
2099      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2100      */
2101 #if defined(__GLIBC__)
2102         qsort(mi->mi_set->recs, mi->mi_set->count,
2103                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2104 #else
2105         mergesort(mi->mi_set->recs, mi->mi_set->count,
2106                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2107 #endif
2108         mi->mi_sorted = 1;
2109     }
2110 }
2111
2112 /* LCL: segfault */
2113 static int rpmdbGrowIterator(rpmdbMatchIterator mi, int fpNum)
2114 {
2115     DBC * dbcursor;
2116     DBT * key;
2117     DBT * data;
2118     dbiIndex dbi = NULL;
2119     dbiIndexSet set;
2120     int rc;
2121     int xx;
2122     int i;
2123
2124     if (mi == NULL)
2125         return 1;
2126
2127     dbcursor = mi->mi_dbc;
2128     key = &mi->mi_key;
2129     data = &mi->mi_data;
2130     if (key->data == NULL)
2131         return 1;
2132
2133     dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2134     if (dbi == NULL)
2135         return 1;
2136
2137     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2138     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2139 #ifndef SQLITE_HACK
2140     xx = dbiCclose(dbi, dbcursor, 0);
2141     dbcursor = NULL;
2142 #endif
2143
2144     if (rc) {                   /* error/not found */
2145         if (rc != DB_NOTFOUND)
2146             rpmlog(RPMLOG_ERR,
2147                 _("error(%d) getting \"%s\" records from %s index\n"),
2148                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
2149 #ifdef  SQLITE_HACK
2150         xx = dbiCclose(dbi, dbcursor, 0);
2151         dbcursor = NULL;
2152 #endif
2153         return rc;
2154     }
2155
2156     set = NULL;
2157     (void) dbt2set(dbi, data, &set);
2158     for (i = 0; i < set->count; i++)
2159         set->recs[i].fpNum = fpNum;
2160
2161 #ifdef  SQLITE_HACK
2162     xx = dbiCclose(dbi, dbcursor, 0);
2163     dbcursor = NULL;
2164 #endif
2165
2166     if (mi->mi_set == NULL) {
2167         mi->mi_set = set;
2168     } else {
2169 #if 0
2170 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data));
2171 #endif
2172         mi->mi_set->recs = xrealloc(mi->mi_set->recs,
2173                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
2174         memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
2175                 set->count * sizeof(*(mi->mi_set->recs)));
2176         mi->mi_set->count += set->count;
2177         set = dbiFreeIndexSet(set);
2178     }
2179
2180     return rc;
2181 }
2182
2183 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2184         int nHdrNums, int sorted)
2185 {
2186     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2187         return 1;
2188
2189     if (mi->mi_set)
2190         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2191     return 0;
2192 }
2193
2194 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2195 {
2196     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2197         return 1;
2198
2199     if (mi->mi_set == NULL)
2200         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
2201     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
2202     return 0;
2203 }
2204
2205 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2206                 const void * keyp, size_t keylen)
2207 {
2208     rpmdbMatchIterator mi;
2209     DBT * key;
2210     DBT * data;
2211     dbiIndexSet set = NULL;
2212     dbiIndex dbi;
2213     void * mi_keyp = NULL;
2214     int isLabel = 0;
2215
2216     if (db == NULL)
2217         return NULL;
2218
2219     (void) rpmdbCheckSignals();
2220
2221     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2222     if (rpmtag == RPMDBI_LABEL) {
2223         rpmtag = RPMTAG_NAME;
2224         isLabel = 1;
2225     }
2226
2227     dbi = dbiOpen(db, rpmtag, 0);
2228     if (dbi == NULL)
2229         return NULL;
2230
2231     /* Chain cursors for teardown on abnormal exit. */
2232     mi = xcalloc(1, sizeof(*mi));
2233     mi->mi_next = rpmmiRock;
2234     rpmmiRock = mi;
2235
2236     key = &mi->mi_key;
2237     data = &mi->mi_data;
2238
2239     /*
2240      * Handle label and file name special cases.
2241      * Otherwise, retrieve join keys for secondary lookup.
2242      */
2243     if (rpmtag != RPMDBI_PACKAGES && keyp) {
2244         DBC * dbcursor = NULL;
2245         int rc;
2246         int xx;
2247
2248         if (isLabel) {
2249             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2250             rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2251             xx = dbiCclose(dbi, dbcursor, 0);
2252             dbcursor = NULL;
2253         } else if (rpmtag == RPMTAG_BASENAMES) {
2254             rc = rpmdbFindByFile(db, keyp, key, data, &set);
2255         } else {
2256             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2257
2258         key->data = (void *) keyp;
2259         key->size = keylen;
2260         if (key->data && key->size == 0) 
2261             key->size = strlen((char *)key->data);
2262         if (key->data && key->size == 0) 
2263             key->size++;        /* XXX "/" fixup. */
2264
2265             rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2266             if (rc > 0) {
2267                 rpmlog(RPMLOG_ERR,
2268                         _("error(%d) getting \"%s\" records from %s index\n"),
2269                         rc, (key->data ? (char *)key->data : "???"), 
2270                         rpmTagGetName(dbi->dbi_rpmtag));
2271             }
2272
2273             /* Join keys need to be native endian internally. */
2274             if (rc == 0)
2275                 (void) dbt2set(dbi, data, &set);
2276
2277             xx = dbiCclose(dbi, dbcursor, 0);
2278             dbcursor = NULL;
2279         }
2280         if (rc) {       /* error/not found */
2281             set = dbiFreeIndexSet(set);
2282             rpmmiRock = mi->mi_next;
2283             mi->mi_next = NULL;
2284             mi = _free(mi);
2285             return NULL;
2286         }
2287     }
2288
2289     /* Copy the retrieval key, byte swapping header instance if necessary. */
2290     if (keyp) {
2291         switch (rpmtag) {
2292         case RPMDBI_PACKAGES:
2293           { union _dbswap *k;
2294
2295             assert(keylen == sizeof(k->ui));    /* xxx programmer error */
2296             k = xmalloc(sizeof(*k));
2297             memcpy(k, keyp, keylen);
2298             if (dbiByteSwapped(dbi) == 1)
2299                 _DBSWAP(*k);
2300             mi_keyp = k;
2301           } break;
2302         default:
2303           { char * k;
2304             if (keylen == 0)
2305                 keylen = strlen(keyp);
2306             k = xmalloc(keylen + 1);
2307             memcpy(k, keyp, keylen);
2308             k[keylen] = '\0';   /* XXX assumes strings */
2309             mi_keyp = k;
2310           } break;
2311         }
2312     }
2313
2314     mi->mi_keyp = mi_keyp;
2315     mi->mi_keylen = keylen;
2316
2317     mi->mi_db = rpmdbLink(db, RPMDBG_M("matchIterator"));
2318     mi->mi_rpmtag = rpmtag;
2319
2320     mi->mi_dbc = NULL;
2321     mi->mi_set = set;
2322     mi->mi_setx = 0;
2323     mi->mi_h = NULL;
2324     mi->mi_sorted = 0;
2325     mi->mi_cflags = 0;
2326     mi->mi_modified = 0;
2327     mi->mi_prevoffset = 0;
2328     mi->mi_offset = 0;
2329     mi->mi_filenum = 0;
2330     mi->mi_nre = 0;
2331     mi->mi_re = NULL;
2332
2333     mi->mi_ts = NULL;
2334     mi->mi_hdrchk = NULL;
2335
2336     return mi;
2337 }
2338
2339 /* XXX psm.c */
2340 int rpmdbRemove(rpmdb db, int rid, unsigned int hdrNum,
2341                 rpmts ts,
2342                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2343 {
2344     DBC * dbcursor = NULL;
2345     DBT key;
2346     DBT data;
2347     union _dbswap mi_offset;
2348     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2349     HFD_t hfd = headerFreeData;
2350     Header h;
2351     sigset_t signalMask;
2352     int ret = 0;
2353     int rc = 0;
2354
2355     if (db == NULL)
2356         return 0;
2357
2358     memset(&key, 0, sizeof(key));
2359     memset(&data, 0, sizeof(data));
2360
2361     {   rpmdbMatchIterator mi;
2362         mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2363         h = rpmdbNextIterator(mi);
2364         if (h)
2365             h = headerLink(h);
2366         mi = rpmdbFreeIterator(mi);
2367     }
2368
2369     if (h == NULL) {
2370         rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
2371               "rpmdbRemove", hdrNum);
2372         return 1;
2373     }
2374
2375     {   
2376         char *nevra = headerGetNEVRA(h, NULL);
2377         rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", hdrNum, nevra);
2378         free(nevra);
2379     }
2380
2381     (void) blockSignals(&signalMask);
2382
2383         /* FIX: rpmvals heartburn */
2384     {   int dbix;
2385         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2386
2387         if (dbiTags.tags != NULL)
2388         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2389             dbiIndex dbi;
2390             const char *av[1];
2391             const char ** rpmvals = NULL;
2392             rpmTagType rpmtype = 0;
2393             rpm_count_t rpmcnt = 0;
2394             rpmTag rpmtag;
2395             int xx;
2396             rpm_count_t i, j;
2397
2398             dbi = NULL;
2399             rpmtag = dbiTags.tags[dbix];
2400
2401             /* Filter out temporary databases */
2402             if (isTemporaryDB(rpmtag)) 
2403                 continue;
2404
2405             switch ((rpm_tag_t) rpmtag) {
2406             case RPMDBI_PACKAGES:
2407                 dbi = dbiOpen(db, rpmtag, 0);
2408                 if (dbi == NULL)        /* XXX shouldn't happen */
2409                     continue;
2410               
2411                 mi_offset.ui = hdrNum;
2412                 if (dbiByteSwapped(dbi) == 1)
2413                     _DBSWAP(mi_offset);
2414                 key.data = &mi_offset;
2415                 key.size = sizeof(mi_offset.ui);
2416
2417                 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2418                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2419                 if (rc) {
2420                     rpmlog(RPMLOG_ERR,
2421                         _("error(%d) setting header #%d record for %s removal\n"),
2422                         rc, hdrNum, rpmTagGetName(dbi->dbi_rpmtag));
2423                 } else
2424                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2425                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2426                 dbcursor = NULL;
2427                 if (!dbi->dbi_no_dbsync)
2428                     xx = dbiSync(dbi, 0);
2429                 continue;
2430                 break;
2431             }
2432         
2433             if (!hge(h, rpmtag, &rpmtype, (rpm_data_t *) &rpmvals, &rpmcnt))
2434                 continue;
2435
2436           dbi = dbiOpen(db, rpmtag, 0);
2437           if (dbi != NULL) {
2438             int printed;
2439
2440             if (rpmtype == RPM_STRING_TYPE) {
2441                 /* XXX force uniform headerGetEntry return */
2442                 av[0] = (const char *) rpmvals;
2443                 rpmvals = av;
2444                 rpmcnt = 1;
2445             }
2446
2447             printed = 0;
2448             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2449             for (i = 0; i < rpmcnt; i++) {
2450                 dbiIndexSet set;
2451                 int stringvalued;
2452                 uint8_t bin[32];
2453
2454                 switch (dbi->dbi_rpmtag) {
2455                 case RPMTAG_FILEMD5S:
2456                     /* Filter out empty MD5 strings. */
2457                     if (!(rpmvals[i] && *rpmvals[i] != '\0'))
2458                         continue;
2459                     break;
2460                 default:
2461                     break;
2462                 }
2463
2464                 /* Identify value pointer and length. */
2465                 stringvalued = 0;
2466                 switch (rpmtype) {
2467                 case RPM_CHAR_TYPE:
2468                 case RPM_INT8_TYPE:
2469                     key.size = sizeof(RPM_CHAR_TYPE);
2470                     key.data = rpmvals + i;
2471                     break;
2472                 case RPM_INT16_TYPE:
2473                     key.size = sizeof(int16_t);
2474                     key.data = rpmvals + i;
2475                     break;
2476                 case RPM_INT32_TYPE:
2477                     key.size = sizeof(int32_t);
2478                     key.data = rpmvals + i;
2479                     break;
2480                 case RPM_BIN_TYPE:
2481                     key.size = rpmcnt;
2482                     key.data = rpmvals;
2483                     rpmcnt = 1;         /* XXX break out of loop. */
2484                     break;
2485                 case RPM_STRING_TYPE:
2486                 case RPM_I18NSTRING_TYPE:
2487                     rpmcnt = 1;         /* XXX break out of loop. */
2488                 case RPM_STRING_ARRAY_TYPE:
2489                     /* Convert from hex to binary. */
2490                     if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
2491                         const char * s;
2492                         uint8_t * t;
2493
2494                         s = rpmvals[i];
2495                         t = bin;
2496                         for (j = 0; j < 16; j++, t++, s += 2)
2497                             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
2498                         key.data = bin;
2499                         key.size = 16;
2500                         break;
2501                     }
2502                     /* Extract the pubkey id from the base64 blob. */
2503                     if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
2504                         int nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
2505                         if (nbin <= 0)
2506                             continue;
2507                         key.data = bin;
2508                         key.size = nbin;
2509                         break;
2510                     }
2511                 default:
2512                     key.data = (void *) rpmvals[i];
2513                     key.size = strlen(rpmvals[i]);
2514                     stringvalued = 1;
2515                     break;
2516                 }
2517
2518                 if (!printed) {
2519                     if (rpmcnt == 1 && stringvalued) {
2520                         rpmlog(RPMLOG_DEBUG,
2521                                 "removing \"%s\" from %s index.\n",
2522                                 (char *)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2523                     } else {
2524                         rpmlog(RPMLOG_DEBUG,
2525                                 "removing %d entries from %s index.\n",
2526                                 rpmcnt, rpmTagGetName(dbi->dbi_rpmtag));
2527                     }
2528                     printed++;
2529                 }
2530
2531                 /* XXX
2532                  * This is almost right, but, if there are duplicate tag
2533                  * values, there will be duplicate attempts to remove
2534                  * the header instance. It's faster to just ignore errors
2535                  * than to do things correctly.
2536                  */
2537
2538                 /* 
2539                  * XXX with duplicates, an accurate data value and 
2540                  * DB_GET_BOTH is needed. 
2541                  * */
2542                 set = NULL;
2543
2544                 if (key.size == 0) 
2545                     key.size = strlen((char *)key.data);
2546                 if (key.size == 0) 
2547                     key.size++; /* XXX "/" fixup. */
2548  
2549                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2550                 if (rc == 0) {                  /* success */
2551                     (void) dbt2set(dbi, &data, &set);
2552                 } else if (rc == DB_NOTFOUND) { /* not found */
2553                     continue;
2554                 } else {                        /* error */
2555                     rpmlog(RPMLOG_ERR,
2556                         _("error(%d) setting \"%s\" records from %s index\n"),
2557                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2558                     ret += 1;
2559                     continue;
2560                 }
2561
2562                 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2563
2564                 /* If nothing was pruned, then don't bother updating. */
2565                 if (rc) {
2566                     set = dbiFreeIndexSet(set);
2567                     continue;
2568                 }
2569
2570                 if (set->count > 0) {
2571                     (void) set2dbt(dbi, &data, set);
2572                     rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2573                     if (rc) {
2574                         rpmlog(RPMLOG_ERR,
2575                                 _("error(%d) storing record \"%s\" into %s\n"),
2576                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2577                         ret += 1;
2578                     }
2579                     data.data = _free(data.data);
2580                     data.size = 0;
2581                 } else {
2582                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2583                     if (rc) {
2584                         rpmlog(RPMLOG_ERR,
2585                                 _("error(%d) removing record \"%s\" from %s\n"),
2586                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2587                         ret += 1;
2588                     }
2589                 }
2590                 set = dbiFreeIndexSet(set);
2591             }
2592
2593             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2594             dbcursor = NULL;
2595
2596             if (!dbi->dbi_no_dbsync)
2597                 xx = dbiSync(dbi, 0);
2598           }
2599
2600             if (rpmtype != RPM_BIN_TYPE)        /* XXX WTFO? HACK ALERT */
2601                 rpmvals = hfd(rpmvals, rpmtype);
2602             rpmtype = 0;
2603             rpmcnt = 0;
2604         }
2605
2606         rec = _free(rec);
2607     }
2608
2609     (void) unblockSignals(&signalMask);
2610
2611     h = headerFree(h);
2612
2613     /* XXX return ret; */
2614     return 0;
2615 }
2616
2617 /* XXX install.c */
2618 int rpmdbAdd(rpmdb db, int iid, Header h,
2619              rpmts ts,
2620              rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2621 {
2622     DBC * dbcursor = NULL;
2623     DBT key;
2624     DBT data;
2625     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2626     HFD_t hfd = headerFreeData;
2627     sigset_t signalMask;
2628     const char ** baseNames;
2629     rpmTagType bnt;
2630     rpm_count_t count = 0;
2631     dbiIndex dbi;
2632     int dbix;
2633     union _dbswap mi_offset;
2634     unsigned int hdrNum = 0;
2635     int ret = 0;
2636     int rc;
2637     int xx;
2638
2639     if (db == NULL)
2640         return 0;
2641
2642     memset(&key, 0, sizeof(key));
2643     memset(&data, 0, sizeof(data));
2644
2645 #ifdef  NOTYET  /* XXX headerRemoveEntry() broken on dribbles. */
2646     xx = headerRemoveEntry(h, RPMTAG_REMOVETID);
2647 #endif
2648     if (iid != 0 && iid != -1) {
2649         rpm_tid_t tid = iid;
2650         if (!headerIsEntry(h, RPMTAG_INSTALLTID))
2651            xx = headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
2652     }
2653
2654     /*
2655      * If old style filename tags is requested, the basenames need to be
2656      * retrieved early, and the header needs to be converted before
2657      * being written to the package header database.
2658      */
2659
2660     xx = hge(h, RPMTAG_BASENAMES, &bnt, (rpm_data_t *) &baseNames, &count);
2661
2662     (void) blockSignals(&signalMask);
2663
2664     {
2665         unsigned int firstkey = 0;
2666         void * keyp = &firstkey;
2667         size_t keylen = sizeof(firstkey);
2668         void * datap = NULL;
2669         size_t datalen = 0;
2670
2671       dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2672       if (dbi != NULL) {
2673
2674         /* XXX db0: hack to pass sizeof header to fadAlloc */
2675         datap = h;
2676         datalen = headerSizeof(h, HEADER_MAGIC_NO);
2677
2678         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2679
2680         /* Retrieve join key for next header instance. */
2681
2682         key.data = keyp;
2683         key.size = keylen;
2684         data.data = datap;
2685         data.size = datalen;
2686         ret = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2687         keyp = key.data;
2688         keylen = key.size;
2689         datap = data.data;
2690         datalen = data.size;
2691
2692         hdrNum = 0;
2693         if (ret == 0 && datap) {
2694             memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
2695             if (dbiByteSwapped(dbi) == 1)
2696                 _DBSWAP(mi_offset);
2697             hdrNum = mi_offset.ui;
2698         }
2699         ++hdrNum;
2700         mi_offset.ui = hdrNum;
2701         if (dbiByteSwapped(dbi) == 1)
2702             _DBSWAP(mi_offset);
2703         if (ret == 0 && datap) {
2704             memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
2705         } else {
2706             datap = &mi_offset;
2707             datalen = sizeof(mi_offset.ui);
2708         }
2709
2710         key.data = keyp;
2711         key.size = keylen;
2712         data.data = datap;
2713         data.size = datalen;
2714
2715         ret = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2716         xx = dbiSync(dbi, 0);
2717
2718         xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2719         dbcursor = NULL;
2720       }
2721
2722     }
2723
2724     if (ret) {
2725         rpmlog(RPMLOG_ERR,
2726                 _("error(%d) allocating new package instance\n"), ret);
2727         goto exit;
2728     }
2729
2730     /* Now update the indexes */
2731
2732     if (hdrNum)
2733     {   
2734         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2735
2736         if (dbiTags.tags != NULL)
2737         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2738             const char *av[1];
2739             const char **rpmvals = NULL;
2740             rpmTagType rpmtype = 0;
2741             rpm_count_t rpmcnt = 0;
2742             rpmTag rpmtag;
2743             rpm_flag_t * requireFlags;
2744             rpmRC rpmrc;
2745             int i, j;
2746
2747             rpmrc = RPMRC_NOTFOUND;
2748             dbi = NULL;
2749             requireFlags = NULL;
2750             rpmtag = dbiTags.tags[dbix];
2751
2752             /* Filter out temporary databases */
2753             if (isTemporaryDB(rpmtag)) 
2754                 continue;
2755
2756             switch (rpmtag) {
2757             case RPMDBI_PACKAGES:
2758                 dbi = dbiOpen(db, rpmtag, 0);
2759                 if (dbi == NULL)        /* XXX shouldn't happen */
2760                     continue;
2761                 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2762
2763                 mi_offset.ui = hdrNum;
2764                 if (dbiByteSwapped(dbi) == 1)
2765                     _DBSWAP(mi_offset);
2766                 key.data = (void *) &mi_offset;
2767                 key.size = sizeof(mi_offset.ui);
2768                 data.data = headerUnload(h);
2769                 data.size = headerSizeof(h, HEADER_MAGIC_NO);
2770
2771                 /* Check header digest/signature on blob export. */
2772                 if (hdrchk && ts) {
2773                     char * msg = NULL;
2774                     int lvl;
2775
2776                     rpmrc = (*hdrchk) (ts, data.data, data.size, &msg);
2777                     lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2778                     rpmlog(lvl, "%s h#%8u %s",
2779                         (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : "  +++"),
2780                                 hdrNum, (msg ? msg : "\n"));
2781                     msg = _free(msg);
2782                 }
2783
2784                 if (data.data != NULL && rpmrc != RPMRC_FAIL) {
2785                     xx = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2786                     xx = dbiSync(dbi, 0);
2787                 }
2788                 data.data = _free(data.data);
2789                 data.size = 0;
2790                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2791                 dbcursor = NULL;
2792                 if (!dbi->dbi_no_dbsync)
2793                     xx = dbiSync(dbi, 0);
2794                 continue;
2795                 break;
2796             case RPMTAG_BASENAMES:      /* XXX preserve legacy behavior */
2797                 rpmtype = bnt;
2798                 rpmvals = baseNames;
2799                 rpmcnt = count;
2800                 break;
2801             case RPMTAG_REQUIRENAME:
2802                 xx = hge(h, rpmtag, &rpmtype, (rpm_data_t *)&rpmvals, &rpmcnt);
2803                 xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (rpm_data_t *)&requireFlags, NULL);
2804                 break;
2805             default:
2806                 xx = hge(h, rpmtag, &rpmtype, (rpm_data_t *)&rpmvals, &rpmcnt);
2807                 break;
2808             }
2809
2810             if (rpmcnt == 0) {
2811                 if (rpmtag != RPMTAG_GROUP)
2812                     continue;
2813
2814                 /* XXX preserve legacy behavior */
2815                 rpmtype = RPM_STRING_TYPE;
2816                 rpmvals = (const char **) "Unknown";
2817                 rpmcnt = 1;
2818             }
2819
2820           dbi = dbiOpen(db, rpmtag, 0);
2821           if (dbi != NULL) {
2822             int printed;
2823
2824             if (rpmtype == RPM_STRING_TYPE) {
2825                 /* XXX force uniform headerGetEntry return */
2826                 av[0] = (const char *) rpmvals;
2827                 rpmvals = av;
2828                 rpmcnt = 1;
2829             }
2830
2831             printed = 0;
2832             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2833
2834             for (i = 0; i < rpmcnt; i++) {
2835                 dbiIndexSet set;
2836                 int stringvalued;
2837                 uint8_t bin[32];
2838                 uint8_t * t;
2839
2840                 /*
2841                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
2842                  * included the tagNum only for files.
2843                  */
2844                 rec->tagNum = i;
2845                 switch (dbi->dbi_rpmtag) {
2846                 case RPMTAG_PUBKEYS:
2847                     break;
2848                 case RPMTAG_FILEMD5S:
2849                     /* Filter out empty MD5 strings. */
2850                     if (!(rpmvals[i] && *rpmvals[i] != '\0'))
2851                         continue;
2852                     break;
2853                 case RPMTAG_REQUIRENAME:
2854                     /* Filter out install prerequisites. */
2855                     if (requireFlags && isInstallPreReq(requireFlags[i]))
2856                         continue;
2857                     break;
2858                 case RPMTAG_TRIGGERNAME:
2859                     if (i) {    /* don't add duplicates */
2860                         for (j = 0; j < i; j++) {
2861                             if (!strcmp(rpmvals[i], rpmvals[j]))
2862                                 break;
2863                         }
2864                         if (j < i)
2865                             continue;
2866                     }
2867                     break;
2868                 default:
2869                     break;
2870                 }
2871
2872                 /* Identify value pointer and length. */
2873                 stringvalued = 0;
2874                 switch (rpmtype) {
2875                 case RPM_CHAR_TYPE:
2876                 case RPM_INT8_TYPE:
2877                     key.size = sizeof(int8_t);
2878                     key.data = rpmvals + i;
2879                     break;
2880                 case RPM_INT16_TYPE:
2881                     key.size = sizeof(int16_t);
2882                     key.data = rpmvals + i;
2883                     break;
2884                 case RPM_INT32_TYPE:
2885                     key.size = sizeof(int32_t);
2886                     key.data = rpmvals + i;
2887                     break;
2888                 case RPM_BIN_TYPE:
2889                     key.size = rpmcnt;
2890                     key.data = rpmvals;
2891                     rpmcnt = 1;         /* XXX break out of loop. */
2892                     break;
2893                 case RPM_STRING_TYPE:
2894                 case RPM_I18NSTRING_TYPE:
2895                     rpmcnt = 1;         /* XXX break out of loop. */
2896                 case RPM_STRING_ARRAY_TYPE:
2897                     /* Convert from hex to binary. */
2898                     if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
2899                         const char * s;
2900
2901                         s = rpmvals[i];
2902                         t = bin;
2903                         for (j = 0; j < 16; j++, t++, s += 2)
2904                             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
2905                         key.data = bin;
2906                         key.size = 16;
2907                         break;
2908                     }
2909                     /* Extract the pubkey id from the base64 blob. */
2910                     if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
2911                         int nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
2912                         if (nbin <= 0)
2913                             continue;
2914                         key.data = bin;
2915                         key.size = nbin;
2916                         break;
2917                     }
2918                 default:
2919                     key.data = (void *) rpmvals[i];
2920                     key.size = strlen(rpmvals[i]);
2921                     stringvalued = 1;
2922                     break;
2923                 }
2924
2925                 if (!printed) {
2926                     if (rpmcnt == 1 && stringvalued) {
2927                         rpmlog(RPMLOG_DEBUG,
2928                                 "adding \"%s\" to %s index.\n",
2929                                 (char *)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2930                     } else {
2931                         rpmlog(RPMLOG_DEBUG,
2932                                 "adding %d entries to %s index.\n",
2933                                 rpmcnt, rpmTagGetName(dbi->dbi_rpmtag));
2934                     }
2935                     printed++;
2936                 }
2937
2938                 /* 
2939                  * XXX with duplicates, an accurate data value and 
2940                  * DB_GET_BOTH is needed. 
2941                  */
2942
2943                 set = NULL;
2944
2945                 if (key.size == 0) 
2946                     key.size = strlen((char *)key.data);
2947                 if (key.size == 0) 
2948                     key.size++; /* XXX "/" fixup. */
2949
2950                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2951                 if (rc == 0) {                  /* success */
2952                 /* With duplicates, cursor is positioned, discard the record. */
2953                     if (!dbi->dbi_permit_dups)
2954                         (void) dbt2set(dbi, &data, &set);
2955                 } else if (rc != DB_NOTFOUND) { /* error */
2956                     rpmlog(RPMLOG_ERR,
2957                         _("error(%d) getting \"%s\" records from %s index\n"),
2958                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2959                     ret += 1;
2960                     continue;
2961                 }
2962
2963                 if (set == NULL)                /* not found or duplicate */
2964                     set = xcalloc(1, sizeof(*set));
2965
2966                 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
2967
2968                 (void) set2dbt(dbi, &data, set);
2969                 rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2970
2971                 if (rc) {
2972                     rpmlog(RPMLOG_ERR,
2973                                 _("error(%d) storing record %s into %s\n"),
2974                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2975                     ret += 1;
2976                 }
2977                 data.data = _free(data.data);
2978                 data.size = 0;
2979                 set = dbiFreeIndexSet(set);
2980             }
2981
2982             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2983             dbcursor = NULL;
2984
2985             if (!dbi->dbi_no_dbsync)
2986                 xx = dbiSync(dbi, 0);
2987           }
2988
2989             if (rpmtype != RPM_BIN_TYPE)        /* XXX WTFO? HACK ALERT */
2990                 rpmvals = hfd(rpmvals, rpmtype);
2991             rpmtype = 0;
2992             rpmcnt = 0;
2993         }
2994
2995         rec = _free(rec);
2996     }
2997
2998 exit:
2999     (void) unblockSignals(&signalMask);
3000
3001     return ret;
3002 }
3003
3004 #define _skip(_dn)      { sizeof(_dn)-1, (_dn) }
3005
3006 static struct skipDir_s {
3007     int dnlen;
3008     const char * dn;
3009 } skipDirs[] = {
3010     { 0, NULL }
3011 };
3012
3013 static int skipDir(const char * dn)
3014 {
3015     struct skipDir_s * sd = skipDirs;
3016     int dnlen;
3017
3018     dnlen = strlen(dn);
3019     for (sd = skipDirs; sd->dn != NULL; sd++) {
3020         if (dnlen < sd->dnlen)
3021             continue;
3022         if (strncmp(dn, sd->dn, sd->dnlen))
3023             continue;
3024         return 1;
3025     }
3026     return 0;
3027 }
3028
3029 /* XXX transaction.c */
3030 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList, 
3031                     int numItems)
3032 {
3033     DBT * key;
3034     DBT * data;
3035     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
3036     HFD_t hfd = headerFreeData;
3037     rpmdbMatchIterator mi;
3038     fingerPrintCache fpc;
3039     Header h;
3040     int i, xx;
3041
3042     if (db == NULL) return 1;
3043
3044     mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
3045     if (mi == NULL)     /* XXX should  never happen */
3046         return 1;
3047
3048     key = &mi->mi_key;
3049     data = &mi->mi_data;
3050
3051     /* Gather all installed headers with matching basename's. */
3052     for (i = 0; i < numItems; i++) {
3053
3054         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
3055
3056         key->data = (void *) fpList[i].baseName;
3057         key->size = strlen((char *)key->data);
3058         if (key->size == 0) 
3059             key->size++;        /* XXX "/" fixup. */
3060
3061         if (skipDir(fpList[i].entry->dirName))
3062             continue;
3063
3064         xx = rpmdbGrowIterator(mi, i);
3065
3066     }
3067
3068     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
3069         mi = rpmdbFreeIterator(mi);
3070         return 0;
3071     }
3072     fpc = fpCacheCreate(i);
3073
3074     rpmdbSortIterator(mi);
3075     /* iterator is now sorted by (recnum, filenum) */
3076
3077     /* For all installed headers with matching basename's ... */
3078     if (mi != NULL)
3079     while ((h = rpmdbNextIterator(mi)) != NULL) {
3080         const char ** dirNames;
3081         const char ** baseNames;
3082         const char ** fullBaseNames;
3083         rpmTagType bnt, dnt;
3084         uint32_t * dirIndexes;
3085         uint32_t * fullDirIndexes;
3086         fingerPrint * fps;
3087         dbiIndexItem im;
3088         int start;
3089         int num;
3090         int end;
3091
3092         start = mi->mi_setx - 1;
3093         im = mi->mi_set->recs + start;
3094
3095         /* Find the end of the set of matched basename's in this package. */
3096         for (end = start + 1; end < mi->mi_set->count; end++) {
3097             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
3098                 break;
3099         }
3100         num = end - start;
3101
3102         /* Compute fingerprints for this installed header's matches */
3103         xx = hge(h, RPMTAG_BASENAMES, &bnt, (rpm_data_t *) &fullBaseNames, NULL);
3104         xx = hge(h, RPMTAG_DIRNAMES, &dnt, (rpm_data_t *) &dirNames, NULL);
3105         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (rpm_data_t *) &fullDirIndexes, NULL);
3106
3107         baseNames = xcalloc(num, sizeof(*baseNames));
3108         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
3109         for (i = 0; i < num; i++) {
3110             baseNames[i] = fullBaseNames[im[i].tagNum];
3111             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
3112         }
3113
3114         fps = xcalloc(num, sizeof(*fps));
3115         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
3116
3117         /* Add db (recnum,filenum) to list for fingerprint matches. */
3118         for (i = 0; i < num; i++, im++) {
3119             /* FIX: fpList[].subDir may be NULL */
3120             if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
3121                 continue;
3122             xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
3123         }
3124
3125         fps = _free(fps);
3126         dirNames = hfd(dirNames, dnt);
3127         fullBaseNames = hfd(fullBaseNames, bnt);
3128         baseNames = _free(baseNames);
3129         dirIndexes = _free(dirIndexes);
3130
3131         mi->mi_setx = end;
3132     }
3133
3134     mi = rpmdbFreeIterator(mi);
3135
3136     fpc = fpCacheFree(fpc);
3137
3138     return 0;
3139
3140 }
3141
3142 /*
3143  * Remove DB4 environment (and lock), ie the equivalent of 
3144  * rm -f <prefix>/<dbpath>/__db.???
3145  * Environment files not existing is not an error, failure to unlink is,
3146  * return zero on success.
3147  * Only useful for BDB, dbapi 3 and 4.  
3148  * TODO/FIX: push this down to db3.c where it belongs
3149  */
3150 static int cleanDbenv(const char *prefix, const char *dbpath)
3151 {
3152     ARGV_t paths = NULL, p;
3153     int rc = 0; 
3154     char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL);
3155
3156     if (rpmGlob(pattern, NULL, &paths) == 0) {
3157         for (p = paths; *p; p++) {
3158             rc += unlink(*p);
3159         }
3160         argvFree(paths);
3161     }
3162     free(pattern);
3163     return rc;
3164 }
3165
3166 static int rpmdbRemoveDatabase(const char * prefix,
3167                 const char * dbpath, int _dbapi)
3168
3169     int i;
3170     char *path;
3171     int xx;
3172
3173     switch (_dbapi) {
3174     case 4:
3175     case 3:
3176         if (dbiTags.tags != NULL)
3177         for (i = 0; i < dbiTags.max; i++) {
3178             const char * base = rpmTagGetName(dbiTags.tags[i]);
3179             path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL);
3180             if (access(path, F_OK) == 0)
3181                 xx = unlink(path);
3182             free(path);
3183         }
3184         cleanDbenv(prefix, dbpath);
3185         break;
3186     case 2:
3187     case 1:
3188     case 0:
3189         break;
3190     }
3191
3192     path = rpmGetPath(prefix, "/", dbpath, NULL);
3193     xx = rmdir(path);
3194     free(path);
3195
3196     return 0;
3197 }
3198
3199 static int rpmdbMoveDatabase(const char * prefix,
3200                 const char * olddbpath, int _olddbapi,
3201                 const char * newdbpath, int _newdbapi)
3202 {
3203     int i;
3204     struct stat st;
3205     int rc = 0;
3206     int xx;
3207     int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1);
3208     sigset_t sigMask;
3209
3210     blockSignals(&sigMask);
3211     switch (_olddbapi) {
3212     case 4:
3213         /* Fall through */
3214     case 3:
3215         if (dbiTags.tags != NULL)
3216         for (i = 0; i < dbiTags.max; i++) {
3217             const char * base;
3218             char *src, *dest;
3219             rpmTag rpmtag;
3220
3221             /* Filter out temporary databases */
3222             if (isTemporaryDB((rpmtag = dbiTags.tags[i])))
3223                 continue;
3224
3225             base = rpmTagGetName(rpmtag);
3226             src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
3227             dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
3228
3229             if (access(src, F_OK) != 0)
3230                 goto cont;
3231
3232             /*
3233              * Restore uid/gid/mode/mtime/security context if possible.
3234              */
3235             if (stat(dest, &st) < 0)
3236                 if (stat(src, &st) < 0)
3237                     goto cont;
3238
3239             if ((xx = rename(src, dest)) != 0) {
3240                 rc = 1;
3241                 goto cont;
3242             }
3243             xx = chown(dest, st.st_uid, st.st_gid);
3244             xx = chmod(dest, (st.st_mode & 07777));
3245             {   struct utimbuf stamp;
3246                 stamp.actime = st.st_atime;
3247                 stamp.modtime = st.st_mtime;
3248                 xx = utime(dest, &stamp);
3249             }
3250
3251             if (selinux) {
3252                 security_context_t scon = NULL;
3253                 if (matchpathcon(dest, st.st_mode, &scon) != -1) {
3254                     (void) setfilecon(dest, scon);
3255                     freecon(scon);
3256                 }
3257             }
3258                 
3259 cont:
3260             free(src);
3261             free(dest);
3262         }
3263
3264         cleanDbenv(prefix, olddbpath);
3265         cleanDbenv(prefix, newdbpath);
3266         break;
3267     case 2:
3268     case 1:
3269     case 0:
3270         break;
3271     }
3272     unblockSignals(&sigMask);
3273
3274 #ifdef  SQLITE_HACK_XXX
3275     if (rc || _olddbapi == _newdbapi)
3276         return rc;
3277
3278     rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3279
3280 #endif
3281     if (selinux) {
3282         (void) matchpathcon_fini();
3283     }
3284     return rc;
3285 }
3286
3287 int rpmdbRebuild(const char * prefix, rpmts ts,
3288                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
3289 {
3290     rpmdb olddb;
3291     char * dbpath = NULL;
3292     char * rootdbpath = NULL;
3293     rpmdb newdb;
3294     char * newdbpath = NULL;
3295     char * newrootdbpath = NULL;
3296     char * tfn;
3297     int nocleanup = 1;
3298     int failed = 0;
3299     int removedir = 0;
3300     int rc = 0, xx;
3301     int _dbapi;
3302     int _dbapi_rebuild;
3303
3304     if (prefix == NULL) prefix = "/";
3305
3306     _dbapi = rpmExpandNumeric("%{_dbapi}");
3307     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3308
3309     tfn = rpmGetPath("%{?_dbpath}", NULL);
3310     if (!(tfn && tfn[0] != '\0'))
3311     {
3312         rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
3313         rc = 1;
3314         goto exit;
3315     }
3316     dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3317     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3318         dbpath += strlen(prefix) - 1;
3319     tfn = _free(tfn);
3320
3321     tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3322     if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
3323     {
3324         tfn = _free(tfn);
3325         rasprintf(&tfn, "%srebuilddb.%d", dbpath, (int) getpid());
3326         nocleanup = 0;
3327     }
3328     newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3329     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3330         newdbpath += strlen(prefix) - 1;
3331     tfn = _free(tfn);
3332
3333     rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
3334         rootdbpath, newrootdbpath);
3335
3336     if (!access(newrootdbpath, F_OK)) {
3337         rpmlog(RPMLOG_ERR, _("temporary database %s already exists\n"),
3338               newrootdbpath);
3339         rc = 1;
3340         goto exit;
3341     }
3342
3343     rpmlog(RPMLOG_DEBUG, "creating directory %s\n", newrootdbpath);
3344     if (mkdir(newrootdbpath, 0755)) {
3345         rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
3346               newrootdbpath, strerror(errno));
3347         rc = 1;
3348         goto exit;
3349     }
3350     removedir = 1;
3351
3352     _rebuildinprogress = 0;
3353
3354     rpmlog(RPMLOG_DEBUG, "opening old database with dbapi %d\n",
3355                 _dbapi);
3356     if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
3357                      RPMDB_FLAG_MINIMAL)) {
3358         rc = 1;
3359         goto exit;
3360     }
3361     _dbapi = olddb->db_api;
3362     _rebuildinprogress = 1;
3363     rpmlog(RPMLOG_DEBUG, "opening new database with dbapi %d\n",
3364                 _dbapi_rebuild);
3365     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3366     if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3367         rc = 1;
3368         goto exit;
3369     }
3370
3371     _rebuildinprogress = 0;
3372
3373     _dbapi_rebuild = newdb->db_api;
3374     
3375     {   Header h = NULL;
3376         rpmdbMatchIterator mi;
3377 #define _RECNUM rpmdbGetIteratorOffset(mi)
3378
3379         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3380         if (ts && hdrchk)
3381             (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3382
3383         while ((h = rpmdbNextIterator(mi)) != NULL) {
3384
3385             /* let's sanity check this record a bit, otherwise just skip it */
3386             if (!(headerIsEntry(h, RPMTAG_NAME) &&
3387                 headerIsEntry(h, RPMTAG_VERSION) &&
3388                 headerIsEntry(h, RPMTAG_RELEASE) &&
3389                 headerIsEntry(h, RPMTAG_BUILDTIME)))
3390             {
3391                 rpmlog(RPMLOG_ERR,
3392                         _("header #%u in the database is bad -- skipping.\n"),
3393                         _RECNUM);
3394                 continue;
3395             }
3396
3397             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
3398             if (_db_filter_dups || newdb->db_filter_dups) {
3399                 const char * name, * version, * release;
3400                 int skip = 0;
3401
3402                 (void) headerNVR(h, &name, &version, &release);
3403
3404                 {   rpmdbMatchIterator mi;
3405                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
3406                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
3407                                 RPMMIRE_DEFAULT, version);
3408                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
3409                                 RPMMIRE_DEFAULT, release);
3410                     while (rpmdbNextIterator(mi)) {
3411                         skip = 1;
3412                         break;
3413                     }
3414                     mi = rpmdbFreeIterator(mi);
3415                 }
3416
3417                 if (skip)
3418                     continue;
3419             }
3420
3421             /* Deleted entries are eliminated in legacy headers by copy. */
3422             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
3423                                 ? headerCopy(h) : NULL);
3424                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
3425                 nh = headerFree(nh);
3426             }
3427
3428             if (rc) {
3429                 rpmlog(RPMLOG_ERR,
3430                         _("cannot add record originally at %u\n"), _RECNUM);
3431                 failed = 1;
3432                 break;
3433             }
3434         }
3435
3436         mi = rpmdbFreeIterator(mi);
3437
3438     }
3439
3440     xx = rpmdbClose(olddb);
3441     xx = rpmdbClose(newdb);
3442
3443     if (failed) {
3444         rpmlog(RPMLOG_NOTICE, _("failed to rebuild database: original database "
3445                 "remains in place\n"));
3446
3447         xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3448         rc = 1;
3449         goto exit;
3450     } else if (!nocleanup) {
3451         if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3452             rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
3453                         "database!\n"));
3454             rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
3455                         "to recover"), dbpath, newdbpath);
3456             rc = 1;
3457             goto exit;
3458         }
3459     }
3460     rc = 0;
3461
3462 exit:
3463     if (removedir && !(rc == 0 && nocleanup)) {
3464         rpmlog(RPMLOG_DEBUG, "removing directory %s\n", newrootdbpath);
3465         if (rmdir(newrootdbpath))
3466             rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"),
3467                         newrootdbpath, strerror(errno));
3468     }
3469     newrootdbpath = _free(newrootdbpath);
3470     rootdbpath = _free(rootdbpath);
3471
3472     return rc;
3473 }