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