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