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