Fix indentiation
[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 (rpmsqIsCaught(SIGINT) > 0
614      || rpmsqIsCaught(SIGQUIT) > 0
615      || rpmsqIsCaught(SIGHUP) > 0
616      || rpmsqIsCaught(SIGTERM) > 0
617      || rpmsqIsCaught(SIGPIPE) > 0
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         rpmlog(RPMLOG_DEBUG, "Exiting on signal...\n");
645         exit(EXIT_FAILURE);
646     }
647     return 0;
648 }
649
650 /**
651  * Block all signals, returning previous signal mask.
652  */
653 static int blockSignals(sigset_t * oldMask)
654 {
655     sigset_t newMask;
656
657     (void) sigfillset(&newMask);                /* block all signals */
658     (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
659     (void) sigdelset(&newMask, SIGINT);
660     (void) sigdelset(&newMask, SIGQUIT);
661     (void) sigdelset(&newMask, SIGHUP);
662     (void) sigdelset(&newMask, SIGTERM);
663     (void) sigdelset(&newMask, SIGPIPE);
664     return sigprocmask(SIG_BLOCK, &newMask, NULL);
665 }
666
667 /**
668  * Restore signal mask.
669  */
670 static int unblockSignals(sigset_t * oldMask)
671 {
672     (void) rpmdbCheckSignals();
673     return sigprocmask(SIG_SETMASK, oldMask, NULL);
674 }
675
676 #define _DB_ROOT        "/"
677 #define _DB_HOME        "%{_dbpath}"
678 #define _DB_FLAGS       0
679 #define _DB_MODE        0
680 #define _DB_PERMS       0644
681
682 #define _DB_MAJOR       -1
683 #define _DB_ERRPFX      "rpmdb"
684
685 static struct rpmdb_s const dbTemplate = {
686     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
687     _DB_MAJOR,  _DB_ERRPFX
688 };
689
690 static int isTemporaryDB(rpmTag rpmtag) 
691 {
692     int rc = 0;
693     switch (rpmtag) {
694     case RPMDBI_AVAILABLE:
695     case RPMDBI_ADDED:
696     case RPMDBI_REMOVED:
697     case RPMDBI_DEPENDS:
698         rc = 1;
699         break;
700     default:
701         break;
702     }
703     return rc;
704 }
705
706 rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
707 {
708     rpmop op = NULL;
709     switch (opx) {
710     case RPMDB_OP_DBGET:
711         op = &rpmdb->db_getops;
712         break;
713     case RPMDB_OP_DBPUT:
714         op = &rpmdb->db_putops;
715         break;
716     case RPMDB_OP_DBDEL:
717         op = &rpmdb->db_delops;
718         break;
719     default:
720         break;
721     }
722     return op;
723 }
724
725 int rpmdbSetChrootDone(rpmdb db, int chrootDone)
726 {
727     int ochrootDone = 0;
728     if (db != NULL) {
729         ochrootDone = db->db_chrootDone;
730         db->db_chrootDone = chrootDone;
731     }
732     return ochrootDone;
733 }
734
735 int rpmdbOpenAll(rpmdb db)
736 {
737     int dbix;
738     int rc = 0;
739
740     if (db == NULL) return -2;
741
742     if (dbiTags.tags != NULL)
743     for (dbix = 0; dbix < dbiTags.max; dbix++) {
744         if (db->_dbi[dbix] != NULL)
745             continue;
746         /* Filter out temporary databases */
747         if (isTemporaryDB(dbiTags.tags[dbix])) 
748             continue;
749         (void) dbiOpen(db, dbiTags.tags[dbix], db->db_flags);
750     }
751     return rc;
752 }
753
754 int rpmdbCloseDBI(rpmdb db, rpmTag rpmtag)
755 {
756     int dbix;
757     int rc = 0;
758
759     if (db == NULL || db->_dbi == NULL || dbiTags.tags == NULL)
760         return 0;
761
762     for (dbix = 0; dbix < dbiTags.max; dbix++) {
763         if (dbiTags.tags[dbix] != rpmtag)
764             continue;
765         if (db->_dbi[dbix] != NULL) {
766             int xx;
767                         /* FIX: double indirection. */
768             xx = dbiClose(db->_dbi[dbix], 0);
769             if (xx && rc == 0) rc = xx;
770             db->_dbi[dbix] = NULL;
771         }
772         break;
773     }
774     return rc;
775 }
776
777 /* XXX query.c, rpminstall.c, verify.c */
778 int rpmdbClose(rpmdb db)
779 {
780     rpmdb * prev, next;
781     int dbix;
782     int rc = 0;
783
784     if (db == NULL)
785         goto exit;
786
787     (void) rpmdbUnlink(db, RPMDBG_M("rpmdbClose"));
788
789     if (db->nrefs > 0)
790         goto exit;
791
792     if (db->_dbi)
793     for (dbix = db->db_ndbi; --dbix >= 0; ) {
794         int xx;
795         if (db->_dbi[dbix] == NULL)
796             continue;
797         xx = dbiClose(db->_dbi[dbix], 0);
798         if (xx && rc == 0) rc = xx;
799         db->_dbi[dbix] = NULL;
800     }
801     db->db_errpfx = _free(db->db_errpfx);
802     db->db_root = _free(db->db_root);
803     db->db_home = _free(db->db_home);
804     db->db_bits = PBM_FREE(db->db_bits);
805     db->_dbi = _free(db->_dbi);
806
807     prev = &rpmdbRock;
808     while ((next = *prev) != NULL && next != db)
809         prev = &next->db_next;
810     if (next) {
811         *prev = next->db_next;
812         next->db_next = NULL;
813     }
814
815     db = _free(db);
816
817     dbiTagsFree();
818
819 exit:
820     (void) rpmsqEnable(-SIGHUP, NULL);
821     (void) rpmsqEnable(-SIGINT, NULL);
822     (void) rpmsqEnable(-SIGTERM,NULL);
823     (void) rpmsqEnable(-SIGQUIT,NULL);
824     (void) rpmsqEnable(-SIGPIPE,NULL);
825     return rc;
826 }
827
828 int rpmdbSync(rpmdb db)
829 {
830     int dbix;
831     int rc = 0;
832
833     if (db == NULL) return 0;
834     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
835         int xx;
836         if (db->_dbi[dbix] == NULL)
837             continue;
838         if (db->_dbi[dbix]->dbi_no_dbsync)
839             continue;
840         xx = dbiSync(db->_dbi[dbix], 0);
841         if (xx && rc == 0) rc = xx;
842     }
843     return rc;
844 }
845
846 /* FIX: dbTemplate structure assignment */
847 static
848 rpmdb newRpmdb(const char * root,
849                 const char * home,
850                 int mode, int perms, int flags)
851 {
852     rpmdb db = xcalloc(sizeof(*db), 1);
853     const char * epfx = _DB_ERRPFX;
854     static int _initialized = 0;
855
856     if (!_initialized) {
857         _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
858         _initialized = 1;
859     }
860
861     *db = dbTemplate;   /* structure assignment */
862
863     db->_dbi = NULL;
864
865     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
866
867     if (mode >= 0)      db->db_mode = mode;
868     if (perms >= 0)     db->db_perms = perms;
869     if (flags >= 0)     db->db_flags = flags;
870
871     if (root && *root) {
872         db->db_root = rpmGetPath(root, NULL);
873     } else
874         db->db_root = rpmGetPath(_DB_ROOT, NULL);
875
876     db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
877     if (!(db->db_home && db->db_home[0] != '%')) {
878         rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
879         db->db_root = _free(db->db_root);
880         db->db_home = _free(db->db_home);
881         db = _free(db);
882         return NULL;
883     }
884     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
885     db->db_remove_env = 0;
886     db->db_filter_dups = _db_filter_dups;
887     db->db_ndbi = dbiTags.max;
888     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
889     db->nrefs = 0;
890     return rpmdbLink(db, RPMDBG_M("rpmdbCreate"));
891 }
892
893 static int openDatabase(const char * prefix,
894                 const char * dbpath,
895                 int _dbapi, rpmdb *dbp,
896                 int mode, int perms, int flags)
897 {
898     rpmdb db;
899     int rc, xx;
900     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
901     int minimal = flags & RPMDB_FLAG_MINIMAL;
902
903     dbiTagsInit();
904
905     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
906     if (_dbapi < -1 || _dbapi > 4)
907         _dbapi = -1;
908     if (_dbapi == 0)
909         _dbapi = 1;
910
911     if (dbp)
912         *dbp = NULL;
913     if ((mode & O_ACCMODE) == O_WRONLY) 
914         return 1;
915
916     db = newRpmdb(prefix, dbpath, mode, perms, flags);
917     if (db == NULL)
918         return 1;
919
920     (void) rpmsqEnable(SIGHUP,  NULL);
921     (void) rpmsqEnable(SIGINT,  NULL);
922     (void) rpmsqEnable(SIGTERM,NULL);
923     (void) rpmsqEnable(SIGQUIT,NULL);
924     (void) rpmsqEnable(SIGPIPE,NULL);
925
926     db->db_api = _dbapi;
927
928     {   int dbix;
929
930         rc = 0;
931         if (dbiTags.tags != NULL)
932         for (dbix = 0; rc == 0 && dbix < dbiTags.max; dbix++) {
933             dbiIndex dbi;
934             rpmTag rpmtag;
935
936             /* Filter out temporary databases */
937             if (isTemporaryDB((rpmtag = dbiTags.tags[dbix])))
938                 continue;
939
940             dbi = dbiOpen(db, rpmtag, 0);
941             if (dbi == NULL) {
942                 rc = -2;
943                 break;
944             }
945
946             switch (rpmtag) {
947             case RPMDBI_PACKAGES:
948                 if (dbi == NULL) rc |= 1;
949 #if 0
950                 /* XXX open only Packages, indices created on the fly. */
951                 if (db->db_api == 3)
952 #endif
953                     goto exit;
954                 break;
955             case RPMTAG_NAME:
956                 if (dbi == NULL) rc |= 1;
957                 if (minimal)
958                     goto exit;
959                 break;
960             default:
961                 break;
962             }
963         }
964     }
965
966 exit:
967     if (rc || justCheck || dbp == NULL)
968         xx = rpmdbClose(db);
969     else {
970         db->db_next = rpmdbRock;
971         rpmdbRock = db;
972         *dbp = db;
973     }
974
975     return rc;
976 }
977
978 rpmdb rpmdbUnlink(rpmdb db, const char * msg)
979 {
980 if (_rpmdb_debug)
981 fprintf(stderr, "--> db %p -- %d %s\n", db, db->nrefs, msg);
982     db->nrefs--;
983     return NULL;
984 }
985
986 rpmdb rpmdbLink(rpmdb db, const char * msg)
987 {
988     db->nrefs++;
989 if (_rpmdb_debug)
990 fprintf(stderr, "--> db %p ++ %d %s\n", db, db->nrefs, msg);
991     return db;
992 }
993
994 /* XXX python/rpmmodule.c */
995 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
996 {
997     int _dbapi = rpmExpandNumeric("%{_dbapi}");
998     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
999 }
1000
1001 int rpmdbInit (const char * prefix, int perms)
1002 {
1003     rpmdb db = NULL;
1004     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1005     int rc;
1006
1007     rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1008                 perms, RPMDB_FLAG_JUSTCHECK);
1009     if (db != NULL) {
1010         int xx;
1011         xx = rpmdbOpenAll(db);
1012         if (xx && rc == 0) rc = xx;
1013         xx = rpmdbClose(db);
1014         if (xx && rc == 0) rc = xx;
1015         db = NULL;
1016     }
1017     return rc;
1018 }
1019
1020 int rpmdbVerify(const char * prefix)
1021 {
1022     rpmdb db = NULL;
1023     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1024     int rc = 0;
1025
1026     rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1027
1028     if (db != NULL) {
1029         int dbix;
1030         int xx;
1031         rc = rpmdbOpenAll(db);
1032
1033         for (dbix = db->db_ndbi; --dbix >= 0; ) {
1034             if (db->_dbi[dbix] == NULL)
1035                 continue;
1036                         /* FIX: double indirection. */
1037             xx = dbiVerify(db->_dbi[dbix], 0);
1038             if (xx && rc == 0) rc = xx;
1039             db->_dbi[dbix] = NULL;
1040         }
1041
1042         /* FIX: db->_dbi[] may be NULL. */
1043         xx = rpmdbClose(db);
1044         if (xx && rc == 0) rc = xx;
1045         db = NULL;
1046     }
1047     return rc;
1048 }
1049
1050 /**
1051  * Find file matches in database.
1052  * @param db            rpm database
1053  * @param filespec
1054  * @param key
1055  * @param data
1056  * @param matches
1057  * @return              0 on success, 1 on not found, -2 on error
1058  */
1059 static int rpmdbFindByFile(rpmdb db, const char * filespec,
1060                 DBT * key, DBT * data, dbiIndexSet * matches)
1061 {
1062     char * dirName;
1063     const char * baseName;
1064     fingerPrintCache fpc;
1065     fingerPrint fp1;
1066     dbiIndex dbi = NULL;
1067     DBC * dbcursor;
1068     dbiIndexSet allMatches = NULL;
1069     dbiIndexItem rec = NULL;
1070     int i;
1071     int rc;
1072     int xx;
1073
1074     *matches = NULL;
1075     if (filespec == NULL) return -2;
1076
1077     if ((baseName = strrchr(filespec, '/')) != NULL) {
1078         size_t len = baseName - filespec + 1;
1079         dirName = strncpy(xmalloc(len + 1), filespec, len);
1080         dirName[len] = '\0';
1081         baseName++;
1082     } else {
1083         dirName = xstrdup("");
1084         baseName = filespec;
1085     }
1086     if (baseName == NULL)
1087         return -2;
1088
1089     fpc = fpCacheCreate(20);
1090     fp1 = fpLookup(fpc, dirName, baseName, 1);
1091     free(dirName);
1092
1093     dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1094     if (dbi != NULL) {
1095         dbcursor = NULL;
1096         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1097
1098         key->data = (void *) baseName;
1099         key->size = strlen(baseName);
1100         if (key->size == 0) 
1101             key->size++;        /* XXX "/" fixup. */
1102
1103         rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1104         if (rc > 0) {
1105             rpmlog(RPMLOG_ERR,
1106                 _("error(%d) getting \"%s\" records from %s index\n"),
1107                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1108         }
1109
1110         if (rc == 0)
1111             (void) dbt2set(dbi, data, &allMatches);
1112
1113         xx = dbiCclose(dbi, dbcursor, 0);
1114         dbcursor = NULL;
1115     } else
1116         rc = -2;
1117
1118     if (rc) {
1119         allMatches = dbiFreeIndexSet(allMatches);
1120         fpc = fpCacheFree(fpc);
1121         return rc;
1122     }
1123
1124     *matches = xcalloc(1, sizeof(**matches));
1125     rec = dbiIndexNewItem(0, 0);
1126     i = 0;
1127     if (allMatches != NULL)
1128     while (i < allMatches->count) {
1129         struct rpmtd_s bn, dn, di;
1130         const char ** baseNames, ** dirNames;
1131         uint32_t * dirIndexes;
1132         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
1133         unsigned int prevoff;
1134         Header h;
1135
1136         {   rpmdbMatchIterator mi;
1137             mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1138             h = rpmdbNextIterator(mi);
1139             if (h)
1140                 h = headerLink(h);
1141             mi = rpmdbFreeIterator(mi);
1142         }
1143
1144         if (h == NULL) {
1145             i++;
1146             continue;
1147         }
1148
1149         headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM);
1150         headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
1151         headerGet(h, RPMTAG_DIRINDEXES, &di, HEADERGET_MINMEM);
1152         baseNames = bn.data;
1153         dirNames = dn.data;
1154         dirIndexes = di.data;
1155
1156         do {
1157             fingerPrint fp2;
1158             int num = dbiIndexRecordFileNumber(allMatches, i);
1159
1160             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1161             if (FP_EQUAL(fp1, fp2)) {
1162                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1163                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1164                 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1165             }
1166
1167             prevoff = offset;
1168             i++;
1169             if (i < allMatches->count)
1170                 offset = dbiIndexRecordOffset(allMatches, i);
1171         } while (i < allMatches->count && offset == prevoff);
1172
1173         rpmtdFreeData(&bn);
1174         rpmtdFreeData(&dn);
1175         rpmtdFreeData(&di);
1176         h = headerFree(h);
1177     }
1178
1179     rec = _free(rec);
1180     allMatches = dbiFreeIndexSet(allMatches);
1181
1182     fpc = fpCacheFree(fpc);
1183
1184     if ((*matches)->count == 0) {
1185         *matches = dbiFreeIndexSet(*matches);
1186         return 1;
1187     }
1188
1189     return 0;
1190 }
1191
1192 /* XXX python/upgrade.c, install.c, uninstall.c */
1193 int rpmdbCountPackages(rpmdb db, const char * name)
1194 {
1195     DBC * dbcursor = NULL;
1196     DBT key; 
1197     DBT data; 
1198     dbiIndex dbi;
1199     int rc;
1200     int xx;
1201
1202     if (db == NULL)
1203         return 0;
1204
1205     memset(&key, 0, sizeof(key));
1206     memset(&data, 0, sizeof(data));
1207
1208     dbi = dbiOpen(db, RPMTAG_NAME, 0);
1209     if (dbi == NULL)
1210         return 0;
1211
1212     key.data = (void *) name;
1213     key.size = strlen(name);
1214
1215     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1216     rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
1217 #ifndef SQLITE_HACK
1218     xx = dbiCclose(dbi, dbcursor, 0);
1219     dbcursor = NULL;
1220 #endif
1221
1222     if (rc == 0) {              /* success */
1223         dbiIndexSet matches;
1224         /* FIX: matches might be NULL */
1225         matches = NULL;
1226         (void) dbt2set(dbi, &data, &matches);
1227         if (matches) {
1228             rc = dbiIndexSetCount(matches);
1229             matches = dbiFreeIndexSet(matches);
1230         }
1231     } else
1232     if (rc == DB_NOTFOUND) {    /* not found */
1233         rc = 0;
1234     } else {                    /* error */
1235         rpmlog(RPMLOG_ERR,
1236                 _("error(%d) getting \"%s\" records from %s index\n"),
1237                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
1238         rc = -1;
1239     }
1240
1241 #ifdef  SQLITE_HACK
1242     xx = dbiCclose(dbi, dbcursor, 0);
1243     dbcursor = NULL;
1244 #endif
1245
1246     return rc;
1247 }
1248
1249 /**
1250  * Attempt partial matches on name[-version[-release]] strings.
1251  * @param dbi           index database handle (always RPMTAG_NAME)
1252  * @param dbcursor      index database cursor
1253  * @param key           search key/length/flags
1254  * @param data          search data/length/flags
1255  * @param name          package name
1256  * @param version       package version (can be a pattern)
1257  * @param release       package release (can be a pattern)
1258  * @retval matches      set of header instances that match
1259  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1260  */
1261 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1262                 DBT * key, DBT * data,
1263                 const char * name,
1264                 const char * version,
1265                 const char * release,
1266                 dbiIndexSet * matches)
1267 {
1268     int gotMatches = 0;
1269     int rc;
1270     int i;
1271
1272     key->data = (void *) name;
1273     key->size = strlen(name);
1274
1275     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1276
1277     if (rc == 0) {              /* success */
1278         (void) dbt2set(dbi, data, matches);
1279         if (version == NULL && release == NULL)
1280             return RPMRC_OK;
1281     } else
1282     if (rc == DB_NOTFOUND) {    /* not found */
1283         return RPMRC_NOTFOUND;
1284     } else {                    /* error */
1285         rpmlog(RPMLOG_ERR,
1286                 _("error(%d) getting \"%s\" records from %s index\n"),
1287                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1288         return RPMRC_FAIL;
1289     }
1290
1291     /* Make sure the version and release match. */
1292     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1293         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1294         rpmdbMatchIterator mi;
1295         Header h;
1296
1297         if (recoff == 0)
1298             continue;
1299
1300         mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1301                         RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1302
1303         /* Set iterator selectors for version/release if available. */
1304         if (version &&
1305             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1306         {
1307             rc = RPMRC_FAIL;
1308             goto exit;
1309         }
1310         if (release &&
1311             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1312         {
1313             rc = RPMRC_FAIL;
1314             goto exit;
1315         }
1316
1317         h = rpmdbNextIterator(mi);
1318         if (h)
1319             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1320         else
1321             (*matches)->recs[i].hdrNum = 0;
1322         mi = rpmdbFreeIterator(mi);
1323     }
1324
1325     if (gotMatches) {
1326         (*matches)->count = gotMatches;
1327         rc = RPMRC_OK;
1328     } else
1329         rc = RPMRC_NOTFOUND;
1330
1331 exit:
1332 /* FIX: double indirection */
1333     if (rc && matches && *matches)
1334         *matches = dbiFreeIndexSet(*matches);
1335     return rc;
1336 }
1337
1338 /**
1339  * Lookup by name, name-version, and finally by name-version-release.
1340  * Both version and release can be patterns.
1341  * @todo Name must be an exact match, as name is a db key.
1342  * @param dbi           index database handle (always RPMTAG_NAME)
1343  * @param dbcursor      index database cursor
1344  * @param key           search key/length/flags
1345  * @param data          search data/length/flags
1346  * @param arg           name[-version[-release]] string
1347  * @retval matches      set of header instances that match
1348  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1349  */
1350 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1351                 const char * arg, dbiIndexSet * matches)
1352 {
1353     const char * release;
1354     char * localarg;
1355     char * s;
1356     char c;
1357     int brackets;
1358     rpmRC rc;
1359  
1360     if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
1361
1362     /* did they give us just a name? */
1363     rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
1364     if (rc != RPMRC_NOTFOUND) return rc;
1365
1366     /* FIX: double indirection */
1367     *matches = dbiFreeIndexSet(*matches);
1368
1369     /* maybe a name and a release */
1370     localarg = xmalloc(strlen(arg) + 1);
1371     s = stpcpy(localarg, arg);
1372
1373     c = '\0';
1374     brackets = 0;
1375     for (s -= 1; s > localarg; s--) {
1376         switch (*s) {
1377         case '[':
1378             brackets = 1;
1379             break;
1380         case ']':
1381             if (c != '[') brackets = 0;
1382             break;
1383         }
1384         c = *s;
1385         if (!brackets && *s == '-')
1386             break;
1387     }
1388
1389         /* FIX: *matches may be NULL. */
1390     if (s == localarg) {
1391         rc = RPMRC_NOTFOUND;
1392         goto exit;
1393     }
1394
1395     *s = '\0';
1396     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1397     if (rc != RPMRC_NOTFOUND) goto exit;
1398
1399     /* FIX: double indirection */
1400     *matches = dbiFreeIndexSet(*matches);
1401     
1402     /* how about name-version-release? */
1403
1404     release = s + 1;
1405
1406     c = '\0';
1407     brackets = 0;
1408     for (; s > localarg; s--) {
1409         switch (*s) {
1410         case '[':
1411             brackets = 1;
1412             break;
1413         case ']':
1414             if (c != '[') brackets = 0;
1415             break;
1416         }
1417         c = *s;
1418         if (!brackets && *s == '-')
1419             break;
1420     }
1421
1422     if (s == localarg) {
1423         rc = RPMRC_NOTFOUND;
1424         goto exit;
1425     }
1426
1427     *s = '\0';
1428         /* FIX: *matches may be NULL. */
1429     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
1430 exit:
1431     free(localarg);
1432     return rc;
1433 }
1434
1435 /**
1436  * Rewrite a header into packages (if necessary) and free the header.
1437  *   Note: this is called from a markReplacedFiles iteration, and *must*
1438  *   preserve the "join key" (i.e. offset) for the header.
1439  * @param mi            database iterator
1440  * @param dbi           index database handle
1441  * @return              0 on success
1442  */
1443 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1444 {
1445     int rc = 0;
1446
1447     if (mi == NULL || mi->mi_h == NULL)
1448         return 0;
1449
1450     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
1451         DBT * key = &mi->mi_key;
1452         DBT * data = &mi->mi_data;
1453         sigset_t signalMask;
1454         rpmRC rpmrc = RPMRC_NOTFOUND;
1455         int xx;
1456
1457         key->data = (void *) &mi->mi_prevoffset;
1458         key->size = sizeof(mi->mi_prevoffset);
1459         data->data = headerUnload(mi->mi_h);
1460         data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
1461
1462         /* Check header digest/signature on blob export (if requested). */
1463         if (mi->mi_hdrchk && mi->mi_ts) {
1464             char * msg = NULL;
1465             int lvl;
1466
1467             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
1468             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
1469             rpmlog(lvl, "%s h#%8u %s",
1470                 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
1471                         mi->mi_prevoffset, (msg ? msg : "\n"));
1472             msg = _free(msg);
1473         }
1474
1475         if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1476             (void) blockSignals(&signalMask);
1477             rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1478             if (rc) {
1479                 rpmlog(RPMLOG_ERR,
1480                         _("error(%d) storing record #%d into %s\n"),
1481                         rc, mi->mi_prevoffset, rpmTagGetName(dbi->dbi_rpmtag));
1482             }
1483             xx = dbiSync(dbi, 0);
1484             (void) unblockSignals(&signalMask);
1485         }
1486         data->data = _free(data->data);
1487         data->size = 0;
1488     }
1489
1490     mi->mi_h = headerFree(mi->mi_h);
1491
1492     return rc;
1493 }
1494
1495 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1496 {
1497     rpmdbMatchIterator * prev, next;
1498     dbiIndex dbi;
1499     int xx;
1500     int i;
1501
1502     if (mi == NULL)
1503         return NULL;
1504
1505     prev = &rpmmiRock;
1506     while ((next = *prev) != NULL && next != mi)
1507         prev = &next->mi_next;
1508     if (next) {
1509         *prev = next->mi_next;
1510         next->mi_next = NULL;
1511     }
1512
1513     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1514     if (dbi == NULL)    /* XXX can't happen */
1515         return NULL;
1516
1517     xx = miFreeHeader(mi, dbi);
1518
1519     if (mi->mi_dbc)
1520         xx = dbiCclose(dbi, mi->mi_dbc, 0);
1521     mi->mi_dbc = NULL;
1522
1523     if (mi->mi_re != NULL)
1524     for (i = 0; i < mi->mi_nre; i++) {
1525         miRE mire = mi->mi_re + i;
1526         mire->pattern = _free(mire->pattern);
1527         if (mire->preg != NULL) {
1528             regfree(mire->preg);
1529             /* LCL: regfree has bogus only */
1530             mire->preg = _free(mire->preg);
1531         }
1532     }
1533     mi->mi_re = _free(mi->mi_re);
1534
1535     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
1536     mi->mi_keyp = _free(mi->mi_keyp);
1537     mi->mi_db = rpmdbUnlink(mi->mi_db, RPMDBG_M("matchIterator"));
1538
1539     mi = _free(mi);
1540
1541     (void) rpmdbCheckSignals();
1542
1543     return mi;
1544 }
1545
1546 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1547     return (mi ? mi->mi_offset : 0);
1548 }
1549
1550 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1551     return (mi ? mi->mi_filenum : 0);
1552 }
1553
1554 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1555     return (mi && mi->mi_set ?  mi->mi_set->count : 0);
1556 }
1557
1558 /**
1559  * Return pattern match.
1560  * @param mire          match iterator regex
1561  * @param val           value to match
1562  * @return              0 if pattern matches, >0 on nomatch, <0 on error
1563  */
1564 static int miregexec(miRE mire, const char * val)
1565 {
1566     int rc = 0;
1567
1568     switch (mire->mode) {
1569     case RPMMIRE_STRCMP:
1570         rc = strcmp(mire->pattern, val);
1571         if (rc) rc = 1;
1572         break;
1573     case RPMMIRE_DEFAULT:
1574     case RPMMIRE_REGEX:
1575         rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1576         if (rc && rc != REG_NOMATCH) {
1577             char msg[256];
1578             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1579             msg[sizeof(msg)-1] = '\0';
1580             rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s\n"),
1581                         mire->pattern, msg);
1582             rc = -1;
1583         }
1584         break;
1585     case RPMMIRE_GLOB:
1586         rc = fnmatch(mire->pattern, val, mire->fnflags);
1587         if (rc && rc != FNM_NOMATCH)
1588             rc = -1;
1589         break;
1590     default:
1591         rc = -1;
1592         break;
1593     }
1594
1595     return rc;
1596 }
1597
1598 /**
1599  * Compare iterator selectors by rpm tag (qsort/bsearch).
1600  * @param a             1st iterator selector
1601  * @param b             2nd iterator selector
1602  * @return              result of comparison
1603  */
1604 static int mireCmp(const void * a, const void * b)
1605 {
1606     const miRE mireA = (const miRE) a;
1607     const miRE mireB = (const miRE) b;
1608     return (mireA->tag - mireB->tag);
1609 }
1610
1611 /**
1612  * Copy pattern, escaping for appropriate mode.
1613  * @param tag           rpm tag
1614  * @retval modep        type of pattern match
1615  * @param pattern       pattern to duplicate
1616  * @return              duplicated pattern
1617  */
1618 static char * mireDup(rpmTag tag, rpmMireMode *modep,
1619                         const char * pattern)
1620 {
1621     const char * s;
1622     char * pat;
1623     char * t;
1624     int brackets;
1625     size_t nb;
1626     int c;
1627
1628     switch (*modep) {
1629     default:
1630     case RPMMIRE_DEFAULT:
1631         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1632             *modep = RPMMIRE_GLOB;
1633             pat = xstrdup(pattern);
1634             break;
1635         }
1636
1637         nb = strlen(pattern) + sizeof("^$");
1638
1639         /* Find no. of bytes needed for pattern. */
1640         /* periods and plusses are escaped, splats become '.*' */
1641         c = '\0';
1642         brackets = 0;
1643         for (s = pattern; *s != '\0'; s++) {
1644             switch (*s) {
1645             case '.':
1646             case '+':
1647             case '*':
1648                 if (!brackets) nb++;
1649                 break;
1650             case '\\':
1651                 s++;
1652                 break;
1653             case '[':
1654                 brackets = 1;
1655                 break;
1656             case ']':
1657                 if (c != '[') brackets = 0;
1658                 break;
1659             }
1660             c = *s;
1661         }
1662
1663         pat = t = xmalloc(nb);
1664
1665         if (pattern[0] != '^') *t++ = '^';
1666
1667         /* Copy pattern, escaping periods, prefixing splats with period. */
1668         c = '\0';
1669         brackets = 0;
1670         for (s = pattern; *s != '\0'; s++, t++) {
1671             switch (*s) {
1672             case '.':
1673             case '+':
1674                 if (!brackets) *t++ = '\\';
1675                 break;
1676             case '*':
1677                 if (!brackets) *t++ = '.';
1678                 break;
1679             case '\\':
1680                 *t++ = *s++;
1681                 break;
1682             case '[':
1683                 brackets = 1;
1684                 break;
1685             case ']':
1686                 if (c != '[') brackets = 0;
1687                 break;
1688             }
1689             c = *t = *s;
1690         }
1691
1692         if (s > pattern && s[-1] != '$') *t++ = '$';
1693         *t = '\0';
1694         *modep = RPMMIRE_REGEX;
1695         break;
1696     case RPMMIRE_STRCMP:
1697     case RPMMIRE_REGEX:
1698     case RPMMIRE_GLOB:
1699         pat = xstrdup(pattern);
1700         break;
1701     }
1702
1703     return pat;
1704 }
1705
1706 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1707                 rpmMireMode mode, const char * pattern)
1708 {
1709     static rpmMireMode defmode = (rpmMireMode)-1;
1710     miRE mire = NULL;
1711     char * allpat = NULL;
1712     int notmatch = 0;
1713     regex_t * preg = NULL;
1714     int cflags = 0;
1715     int eflags = 0;
1716     int fnflags = 0;
1717     int rc = 0;
1718
1719     if (defmode == (rpmMireMode)-1) {
1720         char *t = rpmExpand("%{?_query_selector_match}", NULL);
1721
1722         if (*t == '\0' || !strcmp(t, "default"))
1723             defmode = RPMMIRE_DEFAULT;
1724         else if (!strcmp(t, "strcmp"))
1725             defmode = RPMMIRE_STRCMP;
1726         else if (!strcmp(t, "regex"))
1727             defmode = RPMMIRE_REGEX;
1728         else if (!strcmp(t, "glob"))
1729             defmode = RPMMIRE_GLOB;
1730         else
1731             defmode = RPMMIRE_DEFAULT;
1732         t = _free(t);
1733      }
1734
1735     if (mi == NULL || pattern == NULL)
1736         return rc;
1737
1738     /* Leading '!' inverts pattern match sense, like "grep -v". */
1739     if (*pattern == '!') {
1740         notmatch = 1;
1741         pattern++;
1742     }
1743
1744     allpat = mireDup(tag, &mode, pattern);
1745
1746     if (mode == RPMMIRE_DEFAULT)
1747         mode = defmode;
1748
1749     switch (mode) {
1750     case RPMMIRE_DEFAULT:
1751     case RPMMIRE_STRCMP:
1752         break;
1753     case RPMMIRE_REGEX:
1754         preg = xcalloc(1, sizeof(*preg));
1755         cflags = (REG_EXTENDED | REG_NOSUB);
1756         rc = regcomp(preg, allpat, cflags);
1757         if (rc) {
1758             char msg[256];
1759             (void) regerror(rc, preg, msg, sizeof(msg)-1);
1760             msg[sizeof(msg)-1] = '\0';
1761             rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"), allpat, msg);
1762         }
1763         break;
1764     case RPMMIRE_GLOB:
1765         fnflags = FNM_PATHNAME | FNM_PERIOD;
1766         break;
1767     default:
1768         rc = -1;
1769         break;
1770     }
1771
1772     if (rc) {
1773         /* FIX: mire has kept values */
1774         allpat = _free(allpat);
1775         if (preg) {
1776             regfree(preg);
1777             /* LCL: regfree has bogus only */
1778             preg = _free(preg);
1779         }
1780         return rc;
1781     }
1782
1783     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1784     mire = mi->mi_re + mi->mi_nre;
1785     mi->mi_nre++;
1786     
1787     mire->tag = tag;
1788     mire->mode = mode;
1789     mire->pattern = allpat;
1790     mire->notmatch = notmatch;
1791     mire->preg = preg;
1792     mire->cflags = cflags;
1793     mire->eflags = eflags;
1794     mire->fnflags = fnflags;
1795
1796     if (mi->mi_nre > 1)
1797         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1798
1799     return rc;
1800 }
1801
1802 /**
1803  * Return iterator selector match.
1804  * @param mi            rpm database iterator
1805  * @return              1 if header should be skipped
1806  */
1807 static int mireSkip (const rpmdbMatchIterator mi)
1808 {
1809     miRE mire;
1810     uint32_t zero = 0;
1811     int ntags = 0;
1812     int nmatches = 0;
1813     int rc;
1814
1815     if (mi->mi_h == NULL)       /* XXX can't happen */
1816         return 0;
1817
1818     /*
1819      * Apply tag tests, implicitly "||" for multiple patterns/values of a
1820      * single tag, implicitly "&&" between multiple tag patterns.
1821      */
1822     if ((mire = mi->mi_re) != NULL)
1823     for (int i = 0; i < mi->mi_nre; i++, mire++) {
1824         int anymatch;
1825         struct rpmtd_s td;
1826
1827         if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
1828             if (mire->tag != RPMTAG_EPOCH) {
1829                 ntags++;
1830                 continue;
1831             }
1832             /* "is package already installed" checks rely on this behavior */
1833             td.count = 1;
1834             td.type = RPM_INT32_TYPE;
1835             td.data = &zero;
1836         }
1837
1838         anymatch = 0;           /* no matches yet */
1839         while (1) {
1840             rpmtdInit(&td);
1841             while (rpmtdNext(&td) >= 0) {
1842                 char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
1843                 if (str) {
1844                     rc = miregexec(mire, str);
1845                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1846                         anymatch++;
1847                     free(str);
1848                 }
1849             }
1850             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
1851                 i++;
1852                 mire++;
1853                 continue;
1854             }
1855             break;
1856         }
1857         rpmtdFreeData(&td);
1858
1859         ntags++;
1860         if (anymatch)
1861             nmatches++;
1862     }
1863
1864     return (ntags == nmatches ? 0 : 1);
1865 }
1866
1867 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
1868 {
1869     int rc;
1870     if (mi == NULL)
1871         return 0;
1872     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
1873     if (rewrite)
1874         mi->mi_cflags |= DB_WRITECURSOR;
1875     else
1876         mi->mi_cflags &= ~DB_WRITECURSOR;
1877     return rc;
1878 }
1879
1880 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
1881 {
1882     int rc;
1883     if (mi == NULL)
1884         return 0;
1885     rc = mi->mi_modified;
1886     mi->mi_modified = modified;
1887     return rc;
1888 }
1889
1890 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
1891         rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
1892 {
1893     int rc = 0;
1894     if (mi == NULL)
1895         return 0;
1896     /* XXX forward linkage prevents rpmtsLink */
1897     mi->mi_ts = ts;
1898     mi->mi_hdrchk = hdrchk;
1899     return rc;
1900 }
1901
1902
1903 /* FIX: mi->mi_key.data may be NULL */
1904 Header rpmdbNextIterator(rpmdbMatchIterator mi)
1905 {
1906     dbiIndex dbi;
1907     void * uh;
1908     size_t uhlen;
1909     DBT * key;
1910     DBT * data;
1911     void * keyp;
1912     size_t keylen;
1913     int rc;
1914     int xx;
1915
1916     if (mi == NULL)
1917         return NULL;
1918
1919     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1920     if (dbi == NULL)
1921         return NULL;
1922
1923     /*
1924      * Cursors are per-iterator, not per-dbi, so get a cursor for the
1925      * iterator on 1st call. If the iteration is to rewrite headers, and the
1926      * CDB model is used for the database, then the cursor needs to
1927      * marked with DB_WRITECURSOR as well.
1928      */
1929     if (mi->mi_dbc == NULL)
1930         xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
1931
1932     key = &mi->mi_key;
1933     memset(key, 0, sizeof(*key));
1934     data = &mi->mi_data;
1935     memset(data, 0, sizeof(*data));
1936
1937 top:
1938     uh = NULL;
1939     uhlen = 0;
1940
1941     do {
1942         union _dbswap mi_offset;
1943
1944         if (mi->mi_set) {
1945             if (!(mi->mi_setx < mi->mi_set->count))
1946                 return NULL;
1947             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1948             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1949             mi_offset.ui = mi->mi_offset;
1950             if (dbiByteSwapped(dbi) == 1)
1951                 _DBSWAP(mi_offset);
1952             keyp = &mi_offset;
1953             keylen = sizeof(mi_offset.ui);
1954         } else {
1955
1956             key->data = keyp = (void *)mi->mi_keyp;
1957             key->size = keylen = mi->mi_keylen;
1958             data->data = uh;
1959             data->size = uhlen;
1960 #if !defined(_USE_COPY_LOAD)
1961             data->flags |= DB_DBT_MALLOC;
1962 #endif
1963             rc = dbiGet(dbi, mi->mi_dbc, key, data,
1964                         (key->data == NULL ? DB_NEXT : DB_SET));
1965             data->flags = 0;
1966             keyp = key->data;
1967             keylen = key->size;
1968             uh = data->data;
1969             uhlen = data->size;
1970
1971             /*
1972              * If we got the next key, save the header instance number.
1973              *
1974              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
1975              * largest header instance in the database, and should be
1976              * skipped.
1977              */
1978             if (keyp && mi->mi_setx && rc == 0) {
1979                 memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
1980             if (dbiByteSwapped(dbi) == 1)
1981                 _DBSWAP(mi_offset);
1982                 mi->mi_offset = mi_offset.ui;
1983             }
1984
1985             /* Terminate on error or end of keys */
1986             if (rc || (mi->mi_setx && mi->mi_offset == 0))
1987                 return NULL;
1988         }
1989         mi->mi_setx++;
1990     } while (mi->mi_offset == 0);
1991
1992     /* If next header is identical, return it now. */
1993     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
1994         return mi->mi_h;
1995
1996     /* Retrieve next header blob for index iterator. */
1997     if (uh == NULL) {
1998         key->data = keyp;
1999         key->size = keylen;
2000 #if !defined(_USE_COPY_LOAD)
2001         data->flags |= DB_DBT_MALLOC;
2002 #endif
2003         rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2004         data->flags = 0;
2005         keyp = key->data;
2006         keylen = key->size;
2007         uh = data->data;
2008         uhlen = data->size;
2009         if (rc)
2010             return NULL;
2011     }
2012
2013     /* Rewrite current header (if necessary) and unlink. */
2014     xx = miFreeHeader(mi, dbi);
2015
2016     /* Is this the end of the iteration? */
2017     if (uh == NULL)
2018         return NULL;
2019
2020     /* Check header digest/signature once (if requested). */
2021     if (mi->mi_hdrchk && mi->mi_ts) {
2022         rpmRC rpmrc = RPMRC_NOTFOUND;
2023
2024         /* Don't bother re-checking a previously read header. */
2025         if (mi->mi_db->db_bits) {
2026             pbm_set * set;
2027
2028             set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2029                         &mi->mi_db->db_nbits, mi->mi_offset);
2030             if (PBM_ISSET(mi->mi_offset, set))
2031                 rpmrc = RPMRC_OK;
2032         }
2033
2034         /* If blob is unchecked, check blob import consistency now. */
2035         if (rpmrc != RPMRC_OK) {
2036             char * msg = NULL;
2037             int lvl;
2038
2039             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
2040             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2041             rpmlog(lvl, "%s h#%8u %s",
2042                 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
2043                         mi->mi_offset, (msg ? msg : "\n"));
2044             msg = _free(msg);
2045
2046             /* Mark header checked. */
2047             if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
2048                 pbm_set * set;
2049
2050                 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2051                         &mi->mi_db->db_nbits, mi->mi_offset);
2052                 PBM_SET(mi->mi_offset, set);
2053             }
2054
2055             /* Skip damaged and inconsistent headers. */
2056             if (rpmrc == RPMRC_FAIL)
2057                 goto top;
2058         }
2059     }
2060
2061     /* Did the header blob load correctly? */
2062 #if !defined(_USE_COPY_LOAD)
2063     mi->mi_h = headerLoad(uh);
2064     if (mi->mi_h)
2065         mi->mi_h->flags |= HEADERFLAG_ALLOCATED;
2066 #else
2067     mi->mi_h = headerCopyLoad(uh);
2068 #endif
2069     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2070         rpmlog(RPMLOG_ERR,
2071                 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2072                 mi->mi_offset);
2073         goto top;
2074     }
2075
2076     /*
2077      * Skip this header if iterator selector (if any) doesn't match.
2078      */
2079     if (mireSkip(mi)) {
2080         /* XXX hack, can't restart with Packages locked on single instance. */
2081         if (mi->mi_set || mi->mi_keyp == NULL)
2082             goto top;
2083         return NULL;
2084     }
2085     mi->mi_h->instance = mi->mi_offset;
2086
2087     mi->mi_prevoffset = mi->mi_offset;
2088     mi->mi_modified = 0;
2089
2090     return mi->mi_h;
2091 }
2092
2093 static void rpmdbSortIterator(rpmdbMatchIterator mi)
2094 {
2095     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2096     /*
2097      * mergesort is much (~10x with lots of identical basenames) faster
2098      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2099      */
2100 #if defined(__GLIBC__)
2101         qsort(mi->mi_set->recs, mi->mi_set->count,
2102                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2103 #else
2104         mergesort(mi->mi_set->recs, mi->mi_set->count,
2105                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2106 #endif
2107         mi->mi_sorted = 1;
2108     }
2109 }
2110
2111 /* LCL: segfault */
2112 static int rpmdbGrowIterator(rpmdbMatchIterator mi, int fpNum)
2113 {
2114     DBC * dbcursor;
2115     DBT * key;
2116     DBT * data;
2117     dbiIndex dbi = NULL;
2118     dbiIndexSet set;
2119     int rc;
2120     int xx;
2121     int i;
2122
2123     if (mi == NULL)
2124         return 1;
2125
2126     dbcursor = mi->mi_dbc;
2127     key = &mi->mi_key;
2128     data = &mi->mi_data;
2129     if (key->data == NULL)
2130         return 1;
2131
2132     dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2133     if (dbi == NULL)
2134         return 1;
2135
2136     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2137     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2138 #ifndef SQLITE_HACK
2139     xx = dbiCclose(dbi, dbcursor, 0);
2140     dbcursor = NULL;
2141 #endif
2142
2143     if (rc) {                   /* error/not found */
2144         if (rc != DB_NOTFOUND)
2145             rpmlog(RPMLOG_ERR,
2146                 _("error(%d) getting \"%s\" records from %s index\n"),
2147                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
2148 #ifdef  SQLITE_HACK
2149         xx = dbiCclose(dbi, dbcursor, 0);
2150         dbcursor = NULL;
2151 #endif
2152         return rc;
2153     }
2154
2155     set = NULL;
2156     (void) dbt2set(dbi, data, &set);
2157     for (i = 0; i < set->count; i++)
2158         set->recs[i].fpNum = fpNum;
2159
2160 #ifdef  SQLITE_HACK
2161     xx = dbiCclose(dbi, dbcursor, 0);
2162     dbcursor = NULL;
2163 #endif
2164
2165     if (mi->mi_set == NULL) {
2166         mi->mi_set = set;
2167     } else {
2168 #if 0
2169 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data));
2170 #endif
2171         mi->mi_set->recs = xrealloc(mi->mi_set->recs,
2172                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
2173         memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
2174                 set->count * sizeof(*(mi->mi_set->recs)));
2175         mi->mi_set->count += set->count;
2176         set = dbiFreeIndexSet(set);
2177     }
2178
2179     return rc;
2180 }
2181
2182 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2183         int nHdrNums, int sorted)
2184 {
2185     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2186         return 1;
2187
2188     if (mi->mi_set)
2189         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2190     return 0;
2191 }
2192
2193 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2194 {
2195     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2196         return 1;
2197
2198     if (mi->mi_set == NULL)
2199         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
2200     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
2201     return 0;
2202 }
2203
2204 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2205                 const void * keyp, size_t keylen)
2206 {
2207     rpmdbMatchIterator mi;
2208     DBT * key;
2209     DBT * data;
2210     dbiIndexSet set = NULL;
2211     dbiIndex dbi;
2212     void * mi_keyp = NULL;
2213     int isLabel = 0;
2214
2215     if (db == NULL)
2216         return NULL;
2217
2218     (void) rpmdbCheckSignals();
2219
2220     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2221     if (rpmtag == RPMDBI_LABEL) {
2222         rpmtag = RPMTAG_NAME;
2223         isLabel = 1;
2224     }
2225
2226     dbi = dbiOpen(db, rpmtag, 0);
2227     if (dbi == NULL)
2228         return NULL;
2229
2230     /* Chain cursors for teardown on abnormal exit. */
2231     mi = xcalloc(1, sizeof(*mi));
2232     mi->mi_next = rpmmiRock;
2233     rpmmiRock = mi;
2234
2235     key = &mi->mi_key;
2236     data = &mi->mi_data;
2237
2238     /*
2239      * Handle label and file name special cases.
2240      * Otherwise, retrieve join keys for secondary lookup.
2241      */
2242     if (rpmtag != RPMDBI_PACKAGES && keyp) {
2243         DBC * dbcursor = NULL;
2244         int rc;
2245         int xx;
2246
2247         if (isLabel) {
2248             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2249             rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2250             xx = dbiCclose(dbi, dbcursor, 0);
2251             dbcursor = NULL;
2252         } else if (rpmtag == RPMTAG_BASENAMES) {
2253             rc = rpmdbFindByFile(db, keyp, key, data, &set);
2254         } else {
2255             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2256
2257             key->data = (void *) keyp;
2258             key->size = keylen;
2259             if (key->data && key->size == 0)
2260                 key->size = strlen((char *)key->data);
2261             if (key->data && key->size == 0)
2262                 key->size++;    /* XXX "/" fixup. */
2263
2264             rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2265             if (rc > 0) {
2266                 rpmlog(RPMLOG_ERR,
2267                         _("error(%d) getting \"%s\" records from %s index\n"),
2268                         rc, (key->data ? (char *)key->data : "???"), 
2269                         rpmTagGetName(dbi->dbi_rpmtag));
2270             }
2271
2272             /* Join keys need to be native endian internally. */
2273             if (rc == 0)
2274                 (void) dbt2set(dbi, data, &set);
2275
2276             xx = dbiCclose(dbi, dbcursor, 0);
2277             dbcursor = NULL;
2278         }
2279         if (rc) {       /* error/not found */
2280             set = dbiFreeIndexSet(set);
2281             rpmmiRock = mi->mi_next;
2282             mi->mi_next = NULL;
2283             mi = _free(mi);
2284             return NULL;
2285         }
2286     }
2287
2288     /* Copy the retrieval key, byte swapping header instance if necessary. */
2289     if (keyp) {
2290         switch (rpmtag) {
2291         case RPMDBI_PACKAGES:
2292           { union _dbswap *k;
2293
2294             assert(keylen == sizeof(k->ui));    /* xxx programmer error */
2295             k = xmalloc(sizeof(*k));
2296             memcpy(k, keyp, keylen);
2297             if (dbiByteSwapped(dbi) == 1)
2298                 _DBSWAP(*k);
2299             mi_keyp = k;
2300           } break;
2301         default:
2302           { char * k;
2303             if (keylen == 0)
2304                 keylen = strlen(keyp);
2305             k = xmalloc(keylen + 1);
2306             memcpy(k, keyp, keylen);
2307             k[keylen] = '\0';   /* XXX assumes strings */
2308             mi_keyp = k;
2309           } break;
2310         }
2311     }
2312
2313     mi->mi_keyp = mi_keyp;
2314     mi->mi_keylen = keylen;
2315
2316     mi->mi_db = rpmdbLink(db, RPMDBG_M("matchIterator"));
2317     mi->mi_rpmtag = rpmtag;
2318
2319     mi->mi_dbc = NULL;
2320     mi->mi_set = set;
2321     mi->mi_setx = 0;
2322     mi->mi_h = NULL;
2323     mi->mi_sorted = 0;
2324     mi->mi_cflags = 0;
2325     mi->mi_modified = 0;
2326     mi->mi_prevoffset = 0;
2327     mi->mi_offset = 0;
2328     mi->mi_filenum = 0;
2329     mi->mi_nre = 0;
2330     mi->mi_re = NULL;
2331
2332     mi->mi_ts = NULL;
2333     mi->mi_hdrchk = NULL;
2334
2335     return mi;
2336 }
2337
2338 /*
2339  * Convert current tag data to db key
2340  * @param tagdata       Tag data container
2341  * @retval key          DB key struct
2342  * @retval freedata     Should key.data be freed afterwards
2343  * Return 0 to signal this item should be discarded (ie continue)
2344  */
2345 static int td2key(rpmtd tagdata, DBT *key, int *freedata) 
2346 {
2347     const char *str = NULL;
2348     uint8_t *bin = NULL;
2349
2350     *freedata = 0; 
2351     switch (rpmtdType(tagdata)) {
2352     case RPM_CHAR_TYPE:
2353     case RPM_INT8_TYPE:
2354         key->size = sizeof(uint8_t);
2355         key->data = rpmtdGetChar(tagdata);
2356         break;
2357     case RPM_INT16_TYPE:
2358         key->size = sizeof(uint16_t);
2359         key->data = rpmtdGetUint16(tagdata);
2360         break;
2361     case RPM_INT32_TYPE:
2362         key->size = sizeof(uint32_t);
2363         key->data = rpmtdGetUint32(tagdata);
2364         break;
2365     case RPM_INT64_TYPE:
2366         key->size = sizeof(uint64_t);
2367         key->data = rpmtdGetUint64(tagdata);
2368         break;
2369     case RPM_BIN_TYPE:
2370         key->size = tagdata->count;
2371         key->data = tagdata->data;
2372         break;
2373     case RPM_STRING_TYPE:
2374     case RPM_I18NSTRING_TYPE:
2375     case RPM_STRING_ARRAY_TYPE:
2376         str = rpmtdGetString(tagdata);
2377         if (rpmtdTag(tagdata) == RPMTAG_FILEDIGESTS) {
2378             size_t binlen;
2379             uint8_t *t;
2380         
2381             /* Filter out empty MD5 strings. */
2382             if (!(str && *str != '\0'))
2383                 return 0;
2384
2385             binlen = strlen(str) / 2;
2386             bin = xmalloc(binlen);
2387             /* Convert from hex to binary. */
2388             t = bin;
2389             for (int j = 0; j < binlen; j++, t++, str += 2) 
2390                 *t = (rnibble(str[0]) << 4) | rnibble(str[1]);
2391             key->data = bin;
2392             key->size = binlen;
2393             *freedata = 1;
2394             break;
2395         } else if (rpmtdTag(tagdata) == RPMTAG_PUBKEYS) {
2396             /* Extract the pubkey id from the base64 blob. */
2397             bin = xmalloc(sizeof(pgpKeyID_t));
2398             int nbin = pgpExtractPubkeyFingerprint(str, bin);
2399             if (nbin <= 0) {
2400                 free(bin);
2401                 return 0;
2402             }
2403             key->data = bin;
2404             key->size = nbin;
2405             *freedata = 1;
2406             break;
2407         } else {
2408             /* fallthrough */;
2409         }
2410     default:
2411         str = rpmtdGetString(tagdata);
2412         key->data = (char *) str; /* XXX discards const */
2413         key->size = strlen(str);
2414         break;
2415     }
2416
2417     if (key->size == 0) 
2418         key->size = strlen((char *)key->data);
2419     if (key->size == 0) 
2420         key->size++;    /* XXX "/" fixup. */
2421
2422     return 1;
2423 }
2424
2425 static void logAddRemove(int removing, rpmtd tagdata)
2426 {
2427     rpm_count_t c = rpmtdCount(tagdata);
2428     if (c == 1 && rpmtdType(tagdata) == RPM_STRING_TYPE) {
2429         rpmlog(RPMLOG_DEBUG, "%s \"%s\" %s %s index.\n",
2430                 removing ? "removing" : "adding", rpmtdGetString(tagdata), 
2431                 removing ? "from" : "to", 
2432                 rpmTagGetName(rpmtdTag(tagdata)));
2433     } else if (c > 0) {
2434         rpmlog(RPMLOG_DEBUG, "%s %d entries %s %s index.\n",
2435                 removing ? "removing" : "adding", c, 
2436                 removing ? "from" : "to", 
2437                 rpmTagGetName(rpmtdTag(tagdata)));
2438     }
2439 }
2440
2441 /* XXX psm.c */
2442 int rpmdbRemove(rpmdb db, int rid, unsigned int hdrNum,
2443                 rpmts ts,
2444                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2445 {
2446     DBC * dbcursor = NULL;
2447     DBT key;
2448     DBT data;
2449     union _dbswap mi_offset;
2450     Header h;
2451     sigset_t signalMask;
2452     int ret = 0;
2453     int rc = 0;
2454
2455     if (db == NULL)
2456         return 0;
2457
2458     memset(&key, 0, sizeof(key));
2459     memset(&data, 0, sizeof(data));
2460
2461     {   rpmdbMatchIterator mi;
2462         mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2463         h = rpmdbNextIterator(mi);
2464         if (h)
2465             h = headerLink(h);
2466         mi = rpmdbFreeIterator(mi);
2467     }
2468
2469     if (h == NULL) {
2470         rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
2471               "rpmdbRemove", hdrNum);
2472         return 1;
2473     }
2474
2475     {   
2476         char *nevra = headerGetNEVRA(h, NULL);
2477         rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", hdrNum, nevra);
2478         free(nevra);
2479     }
2480
2481     (void) blockSignals(&signalMask);
2482
2483         /* FIX: rpmvals heartburn */
2484     {   int dbix;
2485         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2486
2487         if (dbiTags.tags != NULL)
2488         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2489             dbiIndex dbi;
2490             rpmTag rpmtag;
2491             int xx = 0;
2492             struct rpmtd_s tagdata;
2493
2494             dbi = NULL;
2495             rpmtag = dbiTags.tags[dbix];
2496
2497             /* Filter out temporary databases */
2498             if (isTemporaryDB(rpmtag)) 
2499                 continue;
2500
2501             if (rpmtag == RPMDBI_PACKAGES) {
2502                 dbi = dbiOpen(db, rpmtag, 0);
2503                 if (dbi == NULL)        /* XXX shouldn't happen */
2504                     continue;
2505               
2506                 mi_offset.ui = hdrNum;
2507                 if (dbiByteSwapped(dbi) == 1)
2508                     _DBSWAP(mi_offset);
2509                 key.data = &mi_offset;
2510                 key.size = sizeof(mi_offset.ui);
2511
2512                 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2513                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2514                 if (rc) {
2515                     rpmlog(RPMLOG_ERR,
2516                         _("error(%d) setting header #%d record for %s removal\n"),
2517                         rc, hdrNum, rpmTagGetName(dbi->dbi_rpmtag));
2518                 } else
2519                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2520                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2521                 dbcursor = NULL;
2522                 if (!dbi->dbi_no_dbsync)
2523                     xx = dbiSync(dbi, 0);
2524                 continue;
2525             }
2526         
2527             if (!headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM))
2528                 continue;
2529
2530             if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2531                 rpmtdFreeData(&tagdata);
2532                 continue;
2533             }
2534             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2535
2536             logAddRemove(1, &tagdata);
2537             while (rpmtdNext(&tagdata) >= 0) {
2538                 dbiIndexSet set;
2539                 int freedata = 0;
2540
2541                 if (!td2key(&tagdata, &key, &freedata)) {
2542                     continue;
2543                 }
2544
2545                 /* XXX
2546                  * This is almost right, but, if there are duplicate tag
2547                  * values, there will be duplicate attempts to remove
2548                  * the header instance. It's faster to just ignore errors
2549                  * than to do things correctly.
2550                  */
2551
2552                 /* 
2553                  * XXX with duplicates, an accurate data value and 
2554                  * DB_GET_BOTH is needed. 
2555                  * */
2556                 set = NULL;
2557
2558                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2559                 if (rc == 0) {                  /* success */
2560                     (void) dbt2set(dbi, &data, &set);
2561                 } else if (rc == DB_NOTFOUND) { /* not found */
2562                     goto cont;
2563                 } else {                        /* error */
2564                     rpmlog(RPMLOG_ERR,
2565                         _("error(%d) setting \"%s\" records from %s index\n"),
2566                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2567                     ret += 1;
2568                     goto cont;
2569                 }
2570
2571                 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2572
2573                 /* If nothing was pruned, then don't bother updating. */
2574                 if (rc) {
2575                     set = dbiFreeIndexSet(set);
2576                     goto cont;
2577                 }
2578
2579                 if (set->count > 0) {
2580                     (void) set2dbt(dbi, &data, set);
2581                     rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2582                     if (rc) {
2583                         rpmlog(RPMLOG_ERR,
2584                                 _("error(%d) storing record \"%s\" into %s\n"),
2585                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2586                         ret += 1;
2587                     }
2588                     data.data = _free(data.data);
2589                     data.size = 0;
2590                 } else {
2591                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2592                     if (rc) {
2593                         rpmlog(RPMLOG_ERR,
2594                                 _("error(%d) removing record \"%s\" from %s\n"),
2595                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2596                         ret += 1;
2597                     }
2598                 }
2599                 set = dbiFreeIndexSet(set);
2600 cont:
2601                 if (freedata) {
2602                    free(key.data); 
2603                 }
2604             }
2605
2606             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2607             dbcursor = NULL;
2608
2609             if (!dbi->dbi_no_dbsync)
2610                 xx = dbiSync(dbi, 0);
2611
2612             rpmtdFreeData(&tagdata);
2613         }
2614
2615         rec = _free(rec);
2616     }
2617
2618     (void) unblockSignals(&signalMask);
2619
2620     h = headerFree(h);
2621
2622     /* XXX return ret; */
2623     return 0;
2624 }
2625
2626 /* XXX install.c */
2627 int rpmdbAdd(rpmdb db, int iid, Header h,
2628              rpmts ts,
2629              rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2630 {
2631     DBC * dbcursor = NULL;
2632     DBT key;
2633     DBT data;
2634     sigset_t signalMask;
2635     dbiIndex dbi;
2636     int dbix;
2637     union _dbswap mi_offset;
2638     unsigned int hdrNum = 0;
2639     int ret = 0;
2640     int rc;
2641     int xx;
2642
2643     if (db == NULL)
2644         return 0;
2645
2646     memset(&key, 0, sizeof(key));
2647     memset(&data, 0, sizeof(data));
2648
2649 #ifdef  NOTYET  /* XXX headerDel() broken on dribbles. */
2650     xx = headerDel(h, RPMTAG_REMOVETID);
2651 #endif
2652     if (iid != 0 && iid != -1) {
2653         rpm_tid_t tid = iid;
2654         if (!headerIsEntry(h, RPMTAG_INSTALLTID)) 
2655             headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
2656     }
2657
2658     (void) blockSignals(&signalMask);
2659
2660     {
2661         unsigned int firstkey = 0;
2662         void * keyp = &firstkey;
2663         size_t keylen = sizeof(firstkey);
2664         void * datap = NULL;
2665         size_t datalen = 0;
2666
2667       dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2668       if (dbi != NULL) {
2669
2670         /* XXX db0: hack to pass sizeof header to fadAlloc */
2671         datap = h;
2672         datalen = headerSizeof(h, HEADER_MAGIC_NO);
2673
2674         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2675
2676         /* Retrieve join key for next header instance. */
2677
2678         key.data = keyp;
2679         key.size = keylen;
2680         data.data = datap;
2681         data.size = datalen;
2682         ret = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2683         keyp = key.data;
2684         keylen = key.size;
2685         datap = data.data;
2686         datalen = data.size;
2687
2688         hdrNum = 0;
2689         if (ret == 0 && datap) {
2690             memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
2691             if (dbiByteSwapped(dbi) == 1)
2692                 _DBSWAP(mi_offset);
2693             hdrNum = mi_offset.ui;
2694         }
2695         ++hdrNum;
2696         mi_offset.ui = hdrNum;
2697         if (dbiByteSwapped(dbi) == 1)
2698             _DBSWAP(mi_offset);
2699         if (ret == 0 && datap) {
2700             memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
2701         } else {
2702             datap = &mi_offset;
2703             datalen = sizeof(mi_offset.ui);
2704         }
2705
2706         key.data = keyp;
2707         key.size = keylen;
2708         data.data = datap;
2709         data.size = datalen;
2710
2711         ret = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2712         xx = dbiSync(dbi, 0);
2713
2714         xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2715         dbcursor = NULL;
2716       }
2717
2718     }
2719
2720     if (ret) {
2721         rpmlog(RPMLOG_ERR,
2722                 _("error(%d) allocating new package instance\n"), ret);
2723         goto exit;
2724     }
2725
2726     /* Now update the indexes */
2727
2728     if (hdrNum)
2729     {   
2730         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2731
2732         if (dbiTags.tags != NULL)
2733         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2734             rpmTag rpmtag;
2735             rpmRC rpmrc;
2736             int j;
2737             struct rpmtd_s tagdata, reqflags;
2738
2739             rpmrc = RPMRC_NOTFOUND;
2740             dbi = NULL;
2741             rpmtag = dbiTags.tags[dbix];
2742
2743             /* Filter out temporary databases */
2744             if (isTemporaryDB(rpmtag)) 
2745                 continue;
2746
2747             switch (rpmtag) {
2748             case RPMDBI_PACKAGES:
2749                 dbi = dbiOpen(db, rpmtag, 0);
2750                 if (dbi == NULL)        /* XXX shouldn't happen */
2751                     continue;
2752                 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2753
2754                 mi_offset.ui = hdrNum;
2755                 if (dbiByteSwapped(dbi) == 1)
2756                     _DBSWAP(mi_offset);
2757                 key.data = (void *) &mi_offset;
2758                 key.size = sizeof(mi_offset.ui);
2759                 data.data = headerUnload(h);
2760                 data.size = headerSizeof(h, HEADER_MAGIC_NO);
2761
2762                 /* Check header digest/signature on blob export. */
2763                 if (hdrchk && ts) {
2764                     char * msg = NULL;
2765                     int lvl;
2766
2767                     rpmrc = (*hdrchk) (ts, data.data, data.size, &msg);
2768                     lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2769                     rpmlog(lvl, "%s h#%8u %s",
2770                         (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : "  +++"),
2771                                 hdrNum, (msg ? msg : "\n"));
2772                     msg = _free(msg);
2773                 }
2774
2775                 if (data.data != NULL && rpmrc != RPMRC_FAIL) {
2776                     xx = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2777                     xx = dbiSync(dbi, 0);
2778                 }
2779                 data.data = _free(data.data);
2780                 data.size = 0;
2781                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2782                 dbcursor = NULL;
2783                 if (!dbi->dbi_no_dbsync)
2784                     xx = dbiSync(dbi, 0);
2785                 continue;
2786                 break;
2787             case RPMTAG_REQUIRENAME:
2788                 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2789                 headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM);
2790                 break;
2791             default:
2792                 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2793                 break;
2794             }
2795
2796             if (rpmtdCount(&tagdata) == 0) {
2797                 if (rpmtag != RPMTAG_GROUP)
2798                     continue;
2799
2800                 /* XXX preserve legacy behavior */
2801                 tagdata.type = RPM_STRING_TYPE;
2802                 tagdata.data = (const char **) "Unknown";
2803                 tagdata.count = 1;
2804             }
2805
2806             if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2807                 rpmtdFreeData(&tagdata);
2808                 continue;
2809             }
2810             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2811
2812             logAddRemove(0, &tagdata);
2813             while (rpmtdNext(&tagdata) >= 0) {
2814                 dbiIndexSet set;
2815                 int i, freedata = 0;
2816
2817                 /*
2818                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
2819                  * included the tagNum only for files.
2820                  */
2821                 i = rec->tagNum = rpmtdGetIndex(&tagdata);
2822                 switch (rpmtag) {
2823                 case RPMTAG_REQUIRENAME: {
2824                     /* Filter out install prerequisites. */
2825                     rpm_flag_t *rflag = rpmtdNextUint32(&reqflags);
2826                     if (rflag && isInstallPreReq(*rflag))
2827                         continue;
2828                     break;
2829                     }
2830                 case RPMTAG_TRIGGERNAME:
2831                     if (i > 0) {        /* don't add duplicates */
2832                         const char **tnames = tagdata.data;
2833                         const char *str = rpmtdGetString(&tagdata);
2834                         for (j = 0; j < i; j++) {
2835                             if (!strcmp(str, tnames[j]))
2836                                 break;
2837                         }
2838                         if (j < i)
2839                             continue;
2840                     }
2841                     break;
2842                 default:
2843                     break;
2844                 }
2845
2846                 if (!td2key(&tagdata, &key, &freedata)) {
2847                     continue;
2848                 }
2849
2850                 /* 
2851                  * XXX with duplicates, an accurate data value and 
2852                  * DB_GET_BOTH is needed. 
2853                  */
2854
2855                 set = NULL;
2856
2857                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2858                 if (rc == 0) {                  /* success */
2859                 /* With duplicates, cursor is positioned, discard the record. */
2860                     if (!dbi->dbi_permit_dups)
2861                         (void) dbt2set(dbi, &data, &set);
2862                 } else if (rc != DB_NOTFOUND) { /* error */
2863                     rpmlog(RPMLOG_ERR,
2864                         _("error(%d) getting \"%s\" records from %s index\n"),
2865                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2866                     ret += 1;
2867                     goto cont;
2868                 }
2869
2870                 if (set == NULL)                /* not found or duplicate */
2871                     set = xcalloc(1, sizeof(*set));
2872
2873                 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
2874
2875                 (void) set2dbt(dbi, &data, set);
2876                 rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2877
2878                 if (rc) {
2879                     rpmlog(RPMLOG_ERR,
2880                                 _("error(%d) storing record %s into %s\n"),
2881                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2882                     ret += 1;
2883                 }
2884                 data.data = _free(data.data);
2885                 data.size = 0;
2886                 set = dbiFreeIndexSet(set);
2887 cont:
2888                 if (freedata) {
2889                     free(key.data);
2890                 }
2891             }
2892
2893             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2894             dbcursor = NULL;
2895
2896             if (!dbi->dbi_no_dbsync)
2897                 xx = dbiSync(dbi, 0);
2898
2899             rpmtdFreeData(&tagdata);
2900         }
2901
2902         rec = _free(rec);
2903     }
2904
2905 exit:
2906     (void) unblockSignals(&signalMask);
2907
2908     return ret;
2909 }
2910
2911 #define _skip(_dn)      { sizeof(_dn)-1, (_dn) }
2912
2913 static struct skipDir_s {
2914     int dnlen;
2915     const char * dn;
2916 } skipDirs[] = {
2917     { 0, NULL }
2918 };
2919
2920 static int skipDir(const char * dn)
2921 {
2922     struct skipDir_s * sd = skipDirs;
2923     int dnlen;
2924
2925     dnlen = strlen(dn);
2926     for (sd = skipDirs; sd->dn != NULL; sd++) {
2927         if (dnlen < sd->dnlen)
2928             continue;
2929         if (strncmp(dn, sd->dn, sd->dnlen))
2930             continue;
2931         return 1;
2932     }
2933     return 0;
2934 }
2935
2936 /* XXX transaction.c */
2937 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList, 
2938                     int numItems, fingerPrintCache fpc)
2939 {
2940     DBT * key;
2941     DBT * data;
2942     rpmdbMatchIterator mi;
2943     Header h;
2944     int i, xx;
2945
2946     if (db == NULL) return 1;
2947
2948     mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
2949     if (mi == NULL)     /* XXX should  never happen */
2950         return 1;
2951
2952     key = &mi->mi_key;
2953     data = &mi->mi_data;
2954
2955     /* Gather all installed headers with matching basename's. */
2956     for (i = 0; i < numItems; i++) {
2957
2958         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
2959
2960         key->data = (void *) fpList[i].baseName;
2961         key->size = strlen((char *)key->data);
2962         if (key->size == 0) 
2963             key->size++;        /* XXX "/" fixup. */
2964
2965         if (skipDir(fpList[i].entry->dirName))
2966             continue;
2967
2968         xx = rpmdbGrowIterator(mi, i);
2969
2970     }
2971
2972     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
2973         mi = rpmdbFreeIterator(mi);
2974         return 0;
2975     }
2976
2977     rpmdbSortIterator(mi);
2978     /* iterator is now sorted by (recnum, filenum) */
2979
2980     /* For all installed headers with matching basename's ... */
2981     if (mi != NULL)
2982     while ((h = rpmdbNextIterator(mi)) != NULL) {
2983         headerGetFlags hgflags = HEADERGET_MINMEM;
2984         struct rpmtd_s bnames, dnames, dindexes;
2985         const char ** dirNames;
2986         const char ** baseNames;
2987         const char ** fullBaseNames;
2988         uint32_t * dirIndexes;
2989         uint32_t * fullDirIndexes;
2990         fingerPrint * fps;
2991         dbiIndexItem im;
2992         int start;
2993         int num;
2994         int end;
2995
2996         start = mi->mi_setx - 1;
2997         im = mi->mi_set->recs + start;
2998
2999         /* Find the end of the set of matched basename's in this package. */
3000         for (end = start + 1; end < mi->mi_set->count; end++) {
3001             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
3002                 break;
3003         }
3004         num = end - start;
3005
3006         /* Compute fingerprints for this installed header's matches */
3007         headerGet(h, RPMTAG_BASENAMES, &bnames, hgflags);
3008         headerGet(h, RPMTAG_DIRNAMES, &dnames, hgflags);
3009         headerGet(h, RPMTAG_DIRINDEXES, &dindexes, hgflags);
3010         fullBaseNames = bnames.data;
3011         dirNames = dnames.data;
3012         fullDirIndexes = dindexes.data;
3013
3014         baseNames = xcalloc(num, sizeof(*baseNames));
3015         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
3016         for (i = 0; i < num; i++) {
3017             baseNames[i] = fullBaseNames[im[i].tagNum];
3018             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
3019         }
3020
3021         fps = xcalloc(num, sizeof(*fps));
3022         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
3023
3024         /* Add db (recnum,filenum) to list for fingerprint matches. */
3025         for (i = 0; i < num; i++, im++) {
3026             /* FIX: fpList[].subDir may be NULL */
3027             if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
3028                 continue;
3029             xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
3030         }
3031
3032         fps = _free(fps);
3033         rpmtdFreeData(&bnames);
3034         rpmtdFreeData(&dnames);
3035         rpmtdFreeData(&dindexes);
3036         baseNames = _free(baseNames);
3037         dirIndexes = _free(dirIndexes);
3038
3039         mi->mi_setx = end;
3040     }
3041
3042     mi = rpmdbFreeIterator(mi);
3043
3044     return 0;
3045
3046 }
3047
3048 /*
3049  * Remove DB4 environment (and lock), ie the equivalent of 
3050  * rm -f <prefix>/<dbpath>/__db.???
3051  * Environment files not existing is not an error, failure to unlink is,
3052  * return zero on success.
3053  * Only useful for BDB, dbapi 3 and 4.  
3054  * TODO/FIX: push this down to db3.c where it belongs
3055  */
3056 static int cleanDbenv(const char *prefix, const char *dbpath)
3057 {
3058     ARGV_t paths = NULL, p;
3059     int rc = 0; 
3060     char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL);
3061
3062     if (rpmGlob(pattern, NULL, &paths) == 0) {
3063         for (p = paths; *p; p++) {
3064             rc += unlink(*p);
3065         }
3066         argvFree(paths);
3067     }
3068     free(pattern);
3069     return rc;
3070 }
3071
3072 static int rpmdbRemoveDatabase(const char * prefix,
3073                 const char * dbpath, int _dbapi)
3074
3075     int i;
3076     char *path;
3077     int xx;
3078
3079     dbiTagsInit();
3080     switch (_dbapi) {
3081     case 4:
3082     case 3:
3083         if (dbiTags.tags != NULL)
3084         for (i = 0; i < dbiTags.max; i++) {
3085             const char * base = rpmTagGetName(dbiTags.tags[i]);
3086             path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL);
3087             if (access(path, F_OK) == 0)
3088                 xx = unlink(path);
3089             free(path);
3090         }
3091         cleanDbenv(prefix, dbpath);
3092         break;
3093     case 2:
3094     case 1:
3095     case 0:
3096         break;
3097     }
3098     dbiTagsFree();
3099
3100     path = rpmGetPath(prefix, "/", dbpath, NULL);
3101     xx = rmdir(path);
3102     free(path);
3103
3104     return 0;
3105 }
3106
3107 static int rpmdbMoveDatabase(const char * prefix,
3108                 const char * olddbpath, int _olddbapi,
3109                 const char * newdbpath, int _newdbapi)
3110 {
3111     int i;
3112     struct stat st;
3113     int rc = 0;
3114     int xx;
3115     int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1);
3116     sigset_t sigMask;
3117
3118     dbiTagsInit();
3119     blockSignals(&sigMask);
3120     switch (_olddbapi) {
3121     case 4:
3122         /* Fall through */
3123     case 3:
3124         if (dbiTags.tags != NULL)
3125         for (i = 0; i < dbiTags.max; i++) {
3126             const char * base;
3127             char *src, *dest;
3128             rpmTag rpmtag;
3129
3130             /* Filter out temporary databases */
3131             if (isTemporaryDB((rpmtag = dbiTags.tags[i])))
3132                 continue;
3133
3134             base = rpmTagGetName(rpmtag);
3135             src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
3136             dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
3137
3138             if (access(src, F_OK) != 0)
3139                 goto cont;
3140
3141             /*
3142              * Restore uid/gid/mode/mtime/security context if possible.
3143              */
3144             if (stat(dest, &st) < 0)
3145                 if (stat(src, &st) < 0)
3146                     goto cont;
3147
3148             if ((xx = rename(src, dest)) != 0) {
3149                 rc = 1;
3150                 goto cont;
3151             }
3152             xx = chown(dest, st.st_uid, st.st_gid);
3153             xx = chmod(dest, (st.st_mode & 07777));
3154             {   struct utimbuf stamp;
3155                 stamp.actime = st.st_atime;
3156                 stamp.modtime = st.st_mtime;
3157                 xx = utime(dest, &stamp);
3158             }
3159
3160             if (selinux) {
3161                 security_context_t scon = NULL;
3162                 if (matchpathcon(dest, st.st_mode, &scon) != -1) {
3163                     (void) setfilecon(dest, scon);
3164                     freecon(scon);
3165                 }
3166             }
3167                 
3168 cont:
3169             free(src);
3170             free(dest);
3171         }
3172
3173         cleanDbenv(prefix, olddbpath);
3174         cleanDbenv(prefix, newdbpath);
3175         break;
3176     case 2:
3177     case 1:
3178     case 0:
3179         break;
3180     }
3181     unblockSignals(&sigMask);
3182
3183 #ifdef  SQLITE_HACK_XXX
3184     if (rc || _olddbapi == _newdbapi)
3185         return rc;
3186
3187     rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3188
3189 #endif
3190     if (selinux) {
3191         (void) matchpathcon_fini();
3192     }
3193     dbiTagsFree();
3194     return rc;
3195 }
3196
3197 int rpmdbRebuild(const char * prefix, rpmts ts,
3198                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
3199 {
3200     rpmdb olddb;
3201     char * dbpath = NULL;
3202     char * rootdbpath = NULL;
3203     rpmdb newdb;
3204     char * newdbpath = NULL;
3205     char * newrootdbpath = NULL;
3206     char * tfn;
3207     int nocleanup = 1;
3208     int failed = 0;
3209     int removedir = 0;
3210     int rc = 0, xx;
3211     int _dbapi;
3212     int _dbapi_rebuild;
3213
3214     if (prefix == NULL) prefix = "/";
3215
3216     _dbapi = rpmExpandNumeric("%{_dbapi}");
3217     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3218
3219     tfn = rpmGetPath("%{?_dbpath}", NULL);
3220     if (!(tfn && tfn[0] != '\0'))
3221     {
3222         rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
3223         rc = 1;
3224         goto exit;
3225     }
3226     dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3227     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3228         dbpath += strlen(prefix) - 1;
3229     tfn = _free(tfn);
3230
3231     tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3232     if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
3233     {
3234         tfn = _free(tfn);
3235         rasprintf(&tfn, "%srebuilddb.%d", dbpath, (int) getpid());
3236         nocleanup = 0;
3237     }
3238     newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3239     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3240         newdbpath += strlen(prefix) - 1;
3241     tfn = _free(tfn);
3242
3243     rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
3244         rootdbpath, newrootdbpath);
3245
3246     if (!access(newrootdbpath, F_OK)) {
3247         rpmlog(RPMLOG_ERR, _("temporary database %s already exists\n"),
3248               newrootdbpath);
3249         rc = 1;
3250         goto exit;
3251     }
3252
3253     rpmlog(RPMLOG_DEBUG, "creating directory %s\n", newrootdbpath);
3254     if (mkdir(newrootdbpath, 0755)) {
3255         rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
3256               newrootdbpath, strerror(errno));
3257         rc = 1;
3258         goto exit;
3259     }
3260     removedir = 1;
3261
3262     _rebuildinprogress = 0;
3263
3264     rpmlog(RPMLOG_DEBUG, "opening old database with dbapi %d\n",
3265                 _dbapi);
3266     if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
3267                      RPMDB_FLAG_MINIMAL)) {
3268         rc = 1;
3269         goto exit;
3270     }
3271     _dbapi = olddb->db_api;
3272     _rebuildinprogress = 1;
3273     rpmlog(RPMLOG_DEBUG, "opening new database with dbapi %d\n",
3274                 _dbapi_rebuild);
3275     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3276     if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3277         rc = 1;
3278         goto exit;
3279     }
3280
3281     _rebuildinprogress = 0;
3282
3283     _dbapi_rebuild = newdb->db_api;
3284     
3285     {   Header h = NULL;
3286         rpmdbMatchIterator mi;
3287 #define _RECNUM rpmdbGetIteratorOffset(mi)
3288
3289         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3290         if (ts && hdrchk)
3291             (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3292
3293         while ((h = rpmdbNextIterator(mi)) != NULL) {
3294
3295             /* let's sanity check this record a bit, otherwise just skip it */
3296             if (!(headerIsEntry(h, RPMTAG_NAME) &&
3297                 headerIsEntry(h, RPMTAG_VERSION) &&
3298                 headerIsEntry(h, RPMTAG_RELEASE) &&
3299                 headerIsEntry(h, RPMTAG_BUILDTIME)))
3300             {
3301                 rpmlog(RPMLOG_ERR,
3302                         _("header #%u in the database is bad -- skipping.\n"),
3303                         _RECNUM);
3304                 continue;
3305             }
3306
3307             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
3308             if (_db_filter_dups || newdb->db_filter_dups) {
3309                 const char * name, * version, * release;
3310                 int skip = 0;
3311
3312                 (void) headerNVR(h, &name, &version, &release);
3313
3314                 {   rpmdbMatchIterator mi;
3315                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
3316                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
3317                                 RPMMIRE_DEFAULT, version);
3318                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
3319                                 RPMMIRE_DEFAULT, release);
3320                     while (rpmdbNextIterator(mi)) {
3321                         skip = 1;
3322                         break;
3323                     }
3324                     mi = rpmdbFreeIterator(mi);
3325                 }
3326
3327                 if (skip)
3328                     continue;
3329             }
3330
3331             /* Deleted entries are eliminated in legacy headers by copy. */
3332             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
3333                                 ? headerCopy(h) : NULL);
3334                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
3335                 nh = headerFree(nh);
3336             }
3337
3338             if (rc) {
3339                 rpmlog(RPMLOG_ERR,
3340                         _("cannot add record originally at %u\n"), _RECNUM);
3341                 failed = 1;
3342                 break;
3343             }
3344         }
3345
3346         mi = rpmdbFreeIterator(mi);
3347
3348     }
3349
3350     xx = rpmdbClose(olddb);
3351     xx = rpmdbClose(newdb);
3352
3353     if (failed) {
3354         rpmlog(RPMLOG_WARNING, 
3355                 _("failed to rebuild database: original database "
3356                 "remains in place\n"));
3357
3358         xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3359         rc = 1;
3360         goto exit;
3361     } else if (!nocleanup) {
3362         if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3363             rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
3364                         "database!\n"));
3365             rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
3366                         "to recover"), dbpath, newdbpath);
3367             rc = 1;
3368             goto exit;
3369         }
3370     }
3371     rc = 0;
3372
3373 exit:
3374     if (removedir && !(rc == 0 && nocleanup)) {
3375         rpmlog(RPMLOG_DEBUG, "removing directory %s\n", newrootdbpath);
3376         if (rmdir(newrootdbpath))
3377             rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"),
3378                         newrootdbpath, strerror(errno));
3379     }
3380     newrootdbpath = _free(newrootdbpath);
3381     rootdbpath = _free(rootdbpath);
3382
3383     return rc;
3384 }