- add cscope/ctags (Rodrigo Barbosa<rodrigob@conectiva.com.br>).
[platform/upstream/rpm.git] / lib / rpmdb.c
1 /** \ingroup rpmdb dbi
2  * \file lib/rpmdb.c
3  */
4
5 #include "system.h"
6
7 static int _debug = 0;
8 #define INLINE
9
10 #include <sys/file.h>
11 #include <signal.h>
12 #include <sys/signal.h>
13
14 #include <rpmlib.h>
15 #include <rpmurl.h>     /* XXX for assert.h */
16 #include <rpmmacro.h>   /* XXX for rpmGetPath/rpmGenPath */
17
18 #include "rpmdb.h"
19
20 /*@access dbiIndexSet@*/
21 /*@access dbiIndexItem@*/
22 /*@access Header@*/             /* XXX compared with NULL */
23 /*@access rpmdbMatchIterator@*/
24
25 #include "fprint.h"
26 #include "misc.h"
27
28 extern int _noDirTokens;
29 static int _rebuildinprogress = 0;
30
31 int _filterDbDups = 0;  /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
32
33 #define _DBI_FLAGS      0
34 #define _DBI_PERMS      0644
35 #define _DBI_MAJOR      -1
36
37 static int dbiTagsMax = 0;
38 /*@only@*/ static int *dbiTags = NULL;
39
40 /**
41  * Return dbi index used for rpm tag.
42  * @param rpmtag        rpm header tag
43  * @return dbi index, -1 on error
44  */
45 static int dbiTagToDbix(int rpmtag)
46 {
47     int dbix;
48
49     if (!(dbiTagsMax > 0 && dbiTags))
50         return -1;
51     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
52         if (rpmtag == dbiTags[dbix])
53             return dbix;
54     }
55     return -1;
56 }
57
58 /**
59  * Initialize database (index, tag) tuple from configuration.
60  */
61 static void dbiTagsInit(void)
62 {
63     static const char * _dbiTagStr_default =
64         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername";
65     char * dbiTagStr;
66     char * o, * oe;
67     int rpmtag;
68
69     dbiTagStr = rpmExpand("%{_dbi_tags}", NULL);
70     if (!(dbiTagStr && *dbiTagStr && *dbiTagStr != '%')) {
71         xfree(dbiTagStr);
72         dbiTagStr = xstrdup(_dbiTagStr_default);
73     }
74
75     if (dbiTagsMax || dbiTags) {
76         free(dbiTags);
77         dbiTags = NULL;
78         dbiTagsMax = 0;
79     }
80
81     /* Always allocate package index */
82     dbiTagsMax = 1;
83     dbiTags = xcalloc(1, dbiTagsMax * sizeof(*dbiTags));
84
85     for (o = dbiTagStr; o && *o; o = oe) {
86         while (*o && isspace(*o))
87             o++;
88         if (*o == '\0')
89             break;
90         for (oe = o; oe && *oe; oe++) {
91             if (isspace(*oe))
92                 break;
93             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
94                 break;
95         }
96         if (oe && *oe)
97             *oe++ = '\0';
98         rpmtag = tagValue(o);
99         if (rpmtag < 0) {
100
101             fprintf(stderr, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
102             continue;
103         }
104         if (dbiTagToDbix(rpmtag) >= 0)
105             continue;
106
107         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags));
108         dbiTags[dbiTagsMax++] = rpmtag;
109     }
110
111     free(dbiTagStr);
112 }
113
114 #if USE_DB1
115 extern struct _dbiVec db1vec;
116 #define DB1vec          &db1vec
117 #else
118 #define DB1vec          NULL
119 #endif
120
121 #if USE_DB2
122 extern struct _dbiVec db2vec;
123 #define DB2vec          &db2vec
124 #else
125 #define DB2vec          NULL
126 #endif
127
128 #if USE_DB3
129 extern struct _dbiVec db3vec;
130 #define DB3vec          &db3vec
131 #else
132 #define DB3vec          NULL
133 #endif
134
135 static struct _dbiVec *mydbvecs[] = {
136     DB1vec, DB1vec, DB2vec, DB3vec, NULL
137 };
138
139 INLINE int dbiSync(dbiIndex dbi, unsigned int flags) {
140 if (_debug < 0 || dbi->dbi_debug)
141 fprintf(stderr, "    Sync %s\n", tagName(dbi->dbi_rpmtag));
142     return (*dbi->dbi_vec->sync) (dbi, flags);
143 }
144
145 INLINE int dbiByteSwapped(dbiIndex dbi) {
146     return (*dbi->dbi_vec->byteswapped) (dbi);
147 }
148
149 INLINE int XdbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags,
150         const char * f, unsigned int l)
151 {
152 if (_debug < 0 || dbi->dbi_debug)
153 fprintf(stderr, "+++ RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
154     return (*dbi->dbi_vec->copen) (dbi, dbcp, flags);
155 }
156
157 INLINE int XdbiCclose(dbiIndex dbi, /*@only@*/ DBC * dbcursor, unsigned int flags,
158         const char * f, unsigned int l)
159 {
160 if (_debug < 0 || dbi->dbi_debug)
161 fprintf(stderr, "--- RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
162     return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags);
163 }
164
165 INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, unsigned int flags)
166 {
167     int NULkey;
168     int rc;
169
170     /* XXX make sure that keylen is correct for "" lookup */
171     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
172     if (NULkey) keylen++;
173     rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags);
174     if (NULkey) keylen--;
175
176 if (_debug < 0 || dbi->dbi_debug)
177 fprintf(stderr, "    Del %s key (%p,%ld) %s rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), rc);
178
179     return rc;
180 }
181
182 INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp,
183         void ** datapp, size_t * datalenp, unsigned int flags)
184 {
185     int NULkey;
186     int rc;
187
188     /* XXX make sure that keylen is correct for "" lookup */
189     NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0' && keylenp && *keylenp == 0);
190     if (NULkey) (*keylenp)++;
191     rc = (*dbi->dbi_vec->cget) (dbi, dbcursor, keypp, keylenp, datapp, datalenp, flags);
192     if (NULkey) (*keylenp)--;
193
194 if (_debug < 0 || dbi->dbi_debug) {
195 char keyval[32];
196 int dataval = 0xdeadbeef;
197 if (dbi->dbi_rpmtag == RPMDBI_PACKAGES && keypp && *keypp && keylenp && *keylenp >= sizeof(keyval)) {
198     int keyint;
199     memcpy(&keyint, *keypp, sizeof(keyint));
200     sprintf(keyval, "%d", keyint);
201 } else keyval[0] = '\0';
202 if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval))
203     memcpy(&dataval, *datapp, sizeof(dataval));
204 fprintf(stderr, "    Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n",
205     tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp,
206     (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)*keypp : keyval), dataval, rc);
207 }
208     return rc;
209 }
210
211 INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen,
212         const void * datap, size_t datalen, unsigned int flags)
213 {
214     int NULkey;
215     int rc;
216
217     /* XXX make sure that keylen is correct for "" lookup */
218     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
219     if (NULkey) keylen++;
220     rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags);
221     if (NULkey) keylen--;
222
223 if (_debug < 0 || dbi->dbi_debug) {
224 int dataval = 0xdeadbeef;
225 if (datap) memcpy(&dataval, datap, sizeof(dataval));
226 fprintf(stderr, "    Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, datap, (long)datalen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), dataval, rc);
227 }
228
229     return rc;
230 }
231
232 INLINE int dbiClose(dbiIndex dbi, unsigned int flags) {
233 if (_debug < 0 || dbi->dbi_debug)
234 fprintf(stderr, "    %s Close\n", tagName(dbi->dbi_rpmtag));
235     return (*dbi->dbi_vec->close) (dbi, flags);
236 }
237
238 dbiIndex dbiOpen(rpmdb rpmdb, int rpmtag, /*@unused@*/ unsigned int flags)
239 {
240     int dbix;
241     dbiIndex dbi = NULL;
242     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
243     int rc = 0;
244
245     dbix = dbiTagToDbix(rpmtag);
246     if (dbix < 0 || dbix >= dbiTagsMax)
247         return NULL;
248
249     /* Is this index already open ? */
250     if ((dbi = rpmdb->_dbi[dbix]) != NULL)
251         return dbi;
252
253     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
254     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
255         _dbapi_rebuild = 3;
256     _dbapi_wanted = (_rebuildinprogress ? -1 : rpmdb->db_api);
257
258     switch (_dbapi_wanted) {
259     default:
260         _dbapi = _dbapi_wanted;
261         if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
262             static int _printed = 0;
263             if (!_printed++)
264                 fprintf(stderr, _("\n\
265 --> This version of rpm was not compiled with support for \"%%_dbapi %d\".\n\
266     Please verify the setting of the macro %%_dbapi using \"rpm --showrc\"\n\
267     and configure \"%%_dbapi 3\" (e.g. create and/or edit /etc/rpm/macros).\n\
268 \n\
269 "),         _dbapi_wanted);
270             return NULL;
271         }
272         errno = 0;
273         dbi = NULL;
274         rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
275         if (rc) {
276             static int _printed[32];
277             if (!_printed[dbix & 0x1f]++)
278                 rpmError(RPMERR_DBOPEN,
279                         _("cannot open %s index using db%d - %s (%d)"),
280                         tagName(rpmtag), _dbapi,
281                         (rc > 0 ? strerror(rc) : ""), rc);
282             _dbapi = -1;
283         }
284         break;
285     case -1:
286         _dbapi = 4;
287         while (_dbapi-- > 1) {
288             if (mydbvecs[_dbapi] == NULL)
289                 continue;
290             errno = 0;
291             dbi = NULL;
292             rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
293             if (rc == 0 && dbi)
294                 break;
295         }
296         if (_dbapi <= 0) {
297             static int _printed[32];
298             if (!_printed[dbix & 0x1f]++)
299                 rpmError(RPMERR_DBOPEN, _("cannot open %s index"),
300                         tagName(rpmtag));
301             rc = 1;
302             goto exit;
303         }
304         if (rpmdb->db_api == -1 && _dbapi > 0)
305             rpmdb->db_api = _dbapi;
306         break;
307     }
308
309     /* Require conversion. */
310     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
311         static int _printed = 0;
312         rc = (_rebuildinprogress ? 0 : 1);
313         if (rc && !_printed++)
314             fprintf(stderr, _("\n\
315 --> The rpm database cannot be opened in db%d format.\n\
316     If you have just upgraded the rpm package you need to convert\n\
317     your database to db%d format by running \"rpm --rebuilddb\" as root.\n\
318 \n\
319 "),     _dbapi_wanted, (_dbapi_rebuild > 0 ? _dbapi_rebuild : 3));
320         goto exit;
321     }
322
323     /* Suggest possible configuration */
324     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
325         static int _printed = 0;
326         if (!_printed++)
327             fprintf(stderr, _("\n\
328 --> The configured %%_dbapi was db%d, but the rpm database is db%d format.\n\
329     Please verify the setting of the macro %%_dbapi using \"rpm --showrc\"\n\
330     and configure \"%%_dbapi %d\" (e.g. create and/or edit /etc/rpm/macros).\n\
331 \n\
332 "),     _dbapi_wanted, _dbapi, _dbapi);
333         rc = 1;
334         goto exit;
335     }
336
337     /* Suggest possible configuration */
338     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
339         static int _printed = 0;
340         rc = (_rebuildinprogress ? 0 : 1);
341         if (rc && !_printed++)
342             fprintf(stderr, _("\n\
343 --> The rpm database is in db%d format, not the suggested db%d format.\n\
344     Please verify the setting of the macros %%_dbapi and %%_dbapi_rebuild\n\
345     using \"rpm --showrc\", and either run \"rpm --rebuilddb\" as root\n\
346     to convert your database from db%d to db%d format, or configure\n\
347     \"%%_dbapi_rebuild %d\" (e.g. create and/or edit /etc/rpm/macros).\n\
348 \n\
349 "),     _dbapi, (_dbapi_rebuild > 0 ? _dbapi_rebuild : 3),
350         _dbapi, (_dbapi_rebuild > 0 ? _dbapi_rebuild : 3), _dbapi);
351         goto exit;
352     }
353
354 exit:
355     if (rc == 0 && dbi) {
356         rpmdb->_dbi[dbix] = dbi;
357     } else if (dbi) {
358         db3Free(dbi);
359         dbi = NULL;
360     }
361
362     return dbi;
363 }
364
365 /**
366  * Create and initialize item for index database set.
367  * @param hdrNum        header instance in db
368  * @param tagNum        tag index in header
369  * @return              new item
370  */
371 static INLINE dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum) {
372     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
373     rec->hdrNum = hdrNum;
374     rec->tagNum = tagNum;
375     return rec;
376 }
377
378 union _dbswap {
379     unsigned int ui;
380     unsigned char uc[4];
381 };
382
383 #define _DBSWAP(_a) \
384   { unsigned char _b, *_c = (_a).uc; \
385     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
386     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
387   }
388
389 /**
390  * Return items that match criteria.
391  * @param dbi           index database handle
392  * @param keyp          search key
393  * @param keylen        search key length (0 will use strlen(key))
394  * @param setp          address of items retrieved from index database
395  * @return              -1 error, 0 success, 1 not found
396  */
397 static int dbiSearch(dbiIndex dbi, DBC * dbcursor, const char * keyp, size_t keylen,
398                 dbiIndexSet * setp)
399 {
400     void * datap;
401     size_t datalen;
402     int rc;
403
404     if (setp) *setp = NULL;
405     if (keylen == 0) keylen = strlen(keyp);
406
407     rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen, 0);
408
409     if (rc > 0) {
410         rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index"),
411                 rc, keyp, tagName(dbi->dbi_rpmtag));
412     } else
413     if (rc == 0 && setp) {
414         int _dbbyteswapped = dbiByteSwapped(dbi);
415         const char * sdbir = datap;
416         dbiIndexSet set;
417         int i;
418
419         set = xmalloc(sizeof(*set));
420
421         /* Convert to database internal format */
422         switch (dbi->dbi_jlen) {
423         default:
424         case 2*sizeof(int_32):
425             set->count = datalen / (2*sizeof(int_32));
426             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
427             for (i = 0; i < set->count; i++) {
428                 union _dbswap hdrNum, tagNum;
429
430                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
431                 sdbir += sizeof(hdrNum.ui);
432                 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
433                 sdbir += sizeof(tagNum.ui);
434                 if (_dbbyteswapped) {
435                     _DBSWAP(hdrNum);
436                     _DBSWAP(tagNum);
437                 }
438                 set->recs[i].hdrNum = hdrNum.ui;
439                 set->recs[i].tagNum = tagNum.ui;
440                 set->recs[i].fpNum = 0;
441                 set->recs[i].dbNum = 0;
442             }
443             break;
444         case 1*sizeof(int_32):
445             set->count = datalen / (1*sizeof(int_32));
446             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
447             for (i = 0; i < set->count; i++) {
448                 union _dbswap hdrNum;
449
450                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
451                 sdbir += sizeof(hdrNum.ui);
452                 if (_dbbyteswapped) {
453                     _DBSWAP(hdrNum);
454                 }
455                 set->recs[i].hdrNum = hdrNum.ui;
456                 set->recs[i].tagNum = 0;
457                 set->recs[i].fpNum = 0;
458                 set->recs[i].dbNum = 0;
459             }
460             break;
461         }
462         *setp = set;
463     }
464     return rc;
465 }
466
467 /**
468  * Change/delete items that match criteria.
469  * @param dbi   index database handle
470  * @param keyp  update key
471  * @param set   items to update in index database
472  * @return      0 success, 1 not found
473  */
474 /*@-compmempass@*/
475 static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor, const char * keyp, dbiIndexSet set)
476 {
477     size_t keylen = strlen(keyp);
478     void * datap;
479     size_t datalen;
480     int rc;
481
482     if (set->count) {
483         char * tdbir;
484         int i;
485         int _dbbyteswapped = dbiByteSwapped(dbi);
486
487         /* Convert to database internal format */
488
489         switch (dbi->dbi_jlen) {
490         default:
491         case 2*sizeof(int_32):
492             datalen = set->count * (2 * sizeof(int_32));
493             datap = tdbir = alloca(datalen);
494             for (i = 0; i < set->count; i++) {
495                 union _dbswap hdrNum, tagNum;
496
497                 hdrNum.ui = set->recs[i].hdrNum;
498                 tagNum.ui = set->recs[i].tagNum;
499                 if (_dbbyteswapped) {
500                     _DBSWAP(hdrNum);
501                     _DBSWAP(tagNum);
502                 }
503                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
504                 tdbir += sizeof(hdrNum.ui);
505                 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
506                 tdbir += sizeof(tagNum.ui);
507             }
508             break;
509         case 1*sizeof(int_32):
510             datalen = set->count * (1 * sizeof(int_32));
511             datap = tdbir = alloca(datalen);
512             for (i = 0; i < set->count; i++) {
513                 union _dbswap hdrNum;
514
515                 hdrNum.ui = set->recs[i].hdrNum;
516                 if (_dbbyteswapped) {
517                     _DBSWAP(hdrNum);
518                 }
519                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
520                 tdbir += sizeof(hdrNum.ui);
521             }
522             break;
523         }
524
525         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
526
527         if (rc) {
528             rpmError(RPMERR_DBPUTINDEX, _("error(%d) storing record %s into %s"),
529                 rc, keyp, tagName(dbi->dbi_rpmtag));
530         }
531
532     } else {
533
534         rc = dbiDel(dbi, dbcursor, keyp, keylen, 0);
535
536         if (rc) {
537             rpmError(RPMERR_DBPUTINDEX, _("error(%d) removing record %s from %s"),
538                 rc, keyp, tagName(dbi->dbi_rpmtag));
539         }
540
541     }
542
543     return rc;
544 }
545 /*@=compmempass@*/
546
547 /* XXX assumes hdrNum is first int in dbiIndexItem */
548 static int hdrNumCmp(const void * one, const void * two) {
549     const int * a = one, * b = two;
550     return (*a - *b);
551 }
552
553 /**
554  * Append element(s) to set of index database items.
555  * @param set           set of index database items
556  * @param recs          array of items to append to set
557  * @param nrecs         number of items
558  * @param recsize       size of an array item
559  * @param sortset       should resulting set be sorted?
560  * @return              0 success, 1 failure (bad args)
561  */
562 static INLINE int dbiAppendSet(dbiIndexSet set, const void * recs,
563         int nrecs, size_t recsize, int sortset)
564 {
565     const char * rptr = recs;
566     size_t rlen = (recsize < sizeof(*(set->recs)))
567                 ? recsize : sizeof(*(set->recs));
568
569     if (set == NULL || recs == NULL || nrecs <= 0 || recsize <= 0)
570         return 1;
571
572     set->recs = (set->count == 0)
573         ? xmalloc(nrecs * sizeof(*(set->recs)))
574         : xrealloc(set->recs, (set->count + nrecs) * sizeof(*(set->recs)));
575
576     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
577
578     while (nrecs-- > 0) {
579         memcpy(set->recs + set->count, rptr, rlen);
580         rptr += recsize;
581         set->count++;
582     }
583
584     if (set->count > 1 && sortset)
585         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
586
587     return 0;
588 }
589
590 /**
591  * Remove element(s) from set of index database items.
592  * @param set           set of index database items
593  * @param recs          array of items to remove from set
594  * @param nrecs         number of items
595  * @param recsize       size of an array item
596  * @param sorted        array is already sorted?
597  * @return              0 success, 1 failure (no items found)
598  */
599 static INLINE int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
600         size_t recsize, int sorted)
601 {
602     int from;
603     int to = 0;
604     int num = set->count;
605     int numCopied = 0;
606
607     if (nrecs > 1 && !sorted)
608         qsort(recs, nrecs, recsize, hdrNumCmp);
609
610     for (from = 0; from < num; from++) {
611         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
612             set->count--;
613             continue;
614         }
615         if (from != to)
616             set->recs[to] = set->recs[from]; /* structure assignment */
617         to++;
618         numCopied++;
619     }
620
621     return (numCopied == num);
622 }
623
624 /* XXX transaction.c */
625 unsigned int dbiIndexSetCount(dbiIndexSet set) {
626     return set->count;
627 }
628
629 /* XXX transaction.c */
630 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
631     return set->recs[recno].hdrNum;
632 }
633
634 /* XXX transaction.c */
635 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
636     return set->recs[recno].tagNum;
637 }
638
639 /* XXX transaction.c */
640 void dbiFreeIndexSet(dbiIndexSet set) {
641     if (set) {
642         if (set->recs) free(set->recs);
643         free(set);
644     }
645 }
646
647 /**
648  * Disable all signals, returning previous signal mask.
649  */
650 static void blockSignals(rpmdb rpmdb, /*@out@*/ sigset_t * oldMask)
651 {
652     sigset_t newMask;
653
654     /* XXX HACK (disabled) permit ^C aborts for now ... */
655     if (!(rpmdb && rpmdb->db_api == 4)) {
656         sigfillset(&newMask);           /* block all signals */
657         sigprocmask(SIG_BLOCK, &newMask, oldMask);
658     }
659 }
660
661 /**
662  * Restore signal mask.
663  */
664 static void unblockSignals(rpmdb rpmdb, sigset_t * oldMask)
665 {
666     /* XXX HACK (disabled) permit ^C aborts for now ... */
667     if (!(rpmdb && rpmdb->db_api == 4)) {
668         sigprocmask(SIG_SETMASK, oldMask, NULL);
669     }
670 }
671
672 #define _DB_ROOT        "/"
673 #define _DB_HOME        "%{_dbpath}"
674 #define _DB_FLAGS       0
675 #define _DB_MODE        0
676 #define _DB_PERMS       0644
677
678 #define _DB_MAJOR       -1
679 #define _DB_REMOVE_ENV  0
680 #define _DB_FILTER_DUPS 0
681 #define _DB_ERRPFX      "rpmdb"
682
683 static struct rpmdb_s dbTemplate = {
684     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
685     _DB_MAJOR,  _DB_REMOVE_ENV, _DB_FILTER_DUPS, _DB_ERRPFX
686 };
687
688 int rpmdbOpenAll (rpmdb rpmdb)
689 {
690     int dbix;
691
692     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
693         if (rpmdb->_dbi[dbix] != NULL)
694             continue;
695         (void) dbiOpen(rpmdb, dbiTags[dbix], rpmdb->db_flags);
696     }
697     return 0;
698 }
699
700 /* XXX query.c, rpminstall.c, verify.c */
701 int rpmdbClose (rpmdb rpmdb)
702 {
703     int dbix;
704
705     for (dbix = rpmdb->db_ndbi; --dbix >= 0; ) {
706         if (rpmdb->_dbi[dbix] == NULL)
707             continue;
708         dbiClose(rpmdb->_dbi[dbix], 0);
709         rpmdb->_dbi[dbix] = NULL;
710     }
711     if (rpmdb->db_errpfx) {
712         xfree(rpmdb->db_errpfx);
713         rpmdb->db_errpfx = NULL;
714     }
715     if (rpmdb->db_root) {
716         xfree(rpmdb->db_root);
717         rpmdb->db_root = NULL;
718     }
719     if (rpmdb->db_home) {
720         xfree(rpmdb->db_home);
721         rpmdb->db_home = NULL;
722     }
723     if (rpmdb->_dbi) {
724         xfree(rpmdb->_dbi);
725         rpmdb->_dbi = NULL;
726     }
727     free(rpmdb);
728     return 0;
729 }
730
731 int rpmdbSync(rpmdb rpmdb)
732 {
733     int dbix;
734
735     for (dbix = 0; dbix < rpmdb->db_ndbi; dbix++) {
736         int xx;
737         if (rpmdb->_dbi[dbix] == NULL)
738             continue;
739         xx = dbiSync(rpmdb->_dbi[dbix], 0);
740     }
741     return 0;
742 }
743
744 static /*@only@*/ rpmdb newRpmdb(const char * root, const char * home,
745                 int mode, int perms, int flags)
746 {
747     rpmdb rpmdb = xcalloc(sizeof(*rpmdb), 1);
748     static int _initialized = 0;
749
750     if (!_initialized) {
751         _filterDbDups = rpmExpandNumeric("%{_filterdbdups}");
752         _initialized = 1;
753     }
754
755     *rpmdb = dbTemplate;        /* structure assignment */
756
757     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
758
759     if (root)
760         rpmdb->db_root = (*root ? root : _DB_ROOT);
761     if (home)
762         rpmdb->db_home = (*home ? home : _DB_HOME);
763     if (mode >= 0)      rpmdb->db_mode = mode;
764     if (perms >= 0)     rpmdb->db_perms = perms;
765     if (flags >= 0)     rpmdb->db_flags = flags;
766
767     if (rpmdb->db_root)
768         rpmdb->db_root = rpmGetPath(rpmdb->db_root, NULL);
769     if (rpmdb->db_home) {
770         rpmdb->db_home = rpmGetPath(rpmdb->db_home, NULL);
771         if (!(rpmdb->db_home && rpmdb->db_home[0] != '%')) {
772             rpmError(RPMERR_DBOPEN, _("no dbpath has been set"));
773            goto errxit;
774         }
775     }
776     if (rpmdb->db_errpfx)
777         rpmdb->db_errpfx = xstrdup(rpmdb->db_errpfx);
778     rpmdb->db_remove_env = 0;
779     rpmdb->db_filter_dups = _filterDbDups;
780     rpmdb->db_ndbi = dbiTagsMax;
781     rpmdb->_dbi = xcalloc(rpmdb->db_ndbi, sizeof(*rpmdb->_dbi));
782     return rpmdb;
783
784 errxit:
785     if (rpmdb)
786         rpmdbClose(rpmdb);
787     return NULL;
788 }
789
790 static int openDatabase(const char * prefix, const char * dbpath, int _dbapi,
791         /*@out@*/ rpmdb *dbp, int mode, int perms, int flags)
792 {
793     rpmdb rpmdb;
794     int rc;
795     static int _initialized = 0;
796     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
797     int minimal = flags & RPMDB_FLAG_MINIMAL;
798
799     if (!_initialized || dbiTagsMax == 0) {
800         dbiTagsInit();
801         _initialized++;
802     }
803
804     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
805     if (_dbapi < -1 || _dbapi > 3)
806         _dbapi = -1;
807     if (_dbapi == 0)
808         _dbapi = 1;
809
810     if (dbp)
811         *dbp = NULL;
812     if (mode & O_WRONLY) 
813         return 1;
814
815     rpmdb = newRpmdb(prefix, dbpath, mode, perms, flags);
816     rpmdb->db_api = _dbapi;
817
818     {   int dbix;
819
820         rc = 0;
821         for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
822             dbiIndex dbi;
823             int rpmtag;
824
825             /* Filter out temporary databases */
826             switch ((rpmtag = dbiTags[dbix])) {
827             case RPMDBI_AVAILABLE:
828             case RPMDBI_ADDED:
829             case RPMDBI_REMOVED:
830             case RPMDBI_DEPENDS:
831                 continue;
832                 /*@notreached@*/ break;
833             default:
834                 break;
835             }
836
837             dbi = dbiOpen(rpmdb, rpmtag, 0);
838
839             switch (rpmtag) {
840             case RPMDBI_PACKAGES:
841                 if (dbi == NULL) rc |= 1;
842 #if 0
843                 if (rpmdb->db_api == 3)
844 #endif
845                     goto exit;
846                 break;
847             case RPMTAG_NAME:
848                 if (dbi == NULL) rc |= 1;
849                 if (minimal)
850                     goto exit;
851                 break;
852             case RPMTAG_BASENAMES:
853             {   void * keyp = NULL;
854                 DBC * dbcursor;
855                 int xx;
856
857     /* We used to store the fileindexes as complete paths, rather then
858        plain basenames. Let's see which version we are... */
859     /*
860      * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
861      * XXX db1/db2 linkage) conditions.
862      */
863                 if (justCheck)
864                     break;
865                 dbcursor = NULL;
866                 xx = dbiCopen(dbi, &dbcursor, 0);
867                 xx = dbiGet(dbi, dbcursor, &keyp, NULL, NULL, NULL, 0);
868                 if (xx == 0) {
869                     const char * akey = keyp;
870                     if (strchr(akey, '/')) {
871                         rpmError(RPMERR_OLDDB, _("old format database is present; "
872                                 "use --rebuilddb to generate a new format database"));
873                         rc |= 1;
874                     }
875                 }
876                 xx = dbiCclose(dbi, dbcursor, 0);
877                 dbcursor = NULL;
878             }   break;
879             default:
880                 break;
881             }
882         }
883     }
884
885 exit:
886     if (rc || justCheck || dbp == NULL)
887         rpmdbClose(rpmdb);
888     else
889         *dbp = rpmdb;
890
891     return rc;
892 }
893
894 /* XXX python/rpmmodule.c */
895 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
896 {
897     int _dbapi = rpmExpandNumeric("%{_dbapi}");
898     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
899 }
900
901 int rpmdbInit (const char * prefix, int perms)
902 {
903     rpmdb rpmdb = NULL;
904     int _dbapi = rpmExpandNumeric("%{_dbapi}");
905     int rc;
906
907     rc = openDatabase(prefix, NULL, _dbapi, &rpmdb, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK);
908     if (rpmdb) {
909         rpmdbClose(rpmdb);
910         rpmdb = NULL;
911     }
912     return rc;
913 }
914
915 #ifdef  DYING
916 static Header rpmdbGetRecord(rpmdb rpmdb, unsigned int offset)
917 {
918     dbiIndex dbi;
919     DBC * dbcursor = NULL;
920     void * uh;
921     size_t uhlen;
922     void * keyp = &offset;
923     size_t keylen = sizeof(offset);
924     int rc;
925     int xx;
926
927     dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
928     if (dbi == NULL)
929         return NULL;
930     xx = dbiCopen(dbi, &dbcursor, 0);
931     rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &uh, &uhlen, 0);
932     xx = dbiCclose(dbi, dbcursor, 0);
933     dbcursor = NULL;
934     if (rc)
935         return NULL;
936     return headerLoad(uh);
937 }
938 #endif
939
940 static int rpmdbFindByFile(rpmdb rpmdb, const char * filespec,
941                         /*@out@*/ dbiIndexSet * matches)
942 {
943     const char * dirName;
944     const char * baseName;
945     fingerPrintCache fpc;
946     fingerPrint fp1;
947     dbiIndex dbi = NULL;
948     DBC * dbcursor;
949     dbiIndexSet allMatches = NULL;
950     dbiIndexItem rec = NULL;
951     int i;
952     int rc;
953     int xx;
954
955     *matches = NULL;
956     if ((baseName = strrchr(filespec, '/')) != NULL) {
957         char * t;
958         size_t len;
959
960         len = baseName - filespec + 1;
961         t = strncpy(alloca(len + 1), filespec, len);
962         t[len] = '\0';
963         dirName = t;
964         baseName++;
965     } else {
966         dirName = "";
967         baseName = filespec;
968     }
969
970     fpc = fpCacheCreate(20);
971     fp1 = fpLookup(fpc, dirName, baseName, 1);
972
973     dbi = dbiOpen(rpmdb, RPMTAG_BASENAMES, 0);
974     dbcursor = NULL;
975     xx = dbiCopen(dbi, &dbcursor, 0);
976     rc = dbiSearch(dbi, dbcursor, baseName, 0, &allMatches);
977     xx = dbiCclose(dbi, dbcursor, 0);
978     dbcursor = NULL;
979     if (rc) {
980         dbiFreeIndexSet(allMatches);
981         allMatches = NULL;
982         fpCacheFree(fpc);
983         return rc;
984     }
985
986     *matches = xcalloc(1, sizeof(**matches));
987     rec = dbiIndexNewItem(0, 0);
988     i = 0;
989     while (i < allMatches->count) {
990         const char ** baseNames, ** dirNames;
991         int_32 * dirIndexes;
992         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
993         unsigned int prevoff;
994         Header h;
995
996 #ifdef  DYING
997         h = rpmdbGetRecord(rpmdb, offset);
998 #else
999         {   rpmdbMatchIterator mi;
1000             mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &offset, sizeof(offset));
1001             h = rpmdbNextIterator(mi);
1002             if (h)
1003                 h = headerLink(h);
1004             rpmdbFreeIterator(mi);
1005         }
1006 #endif
1007
1008         if (h == NULL) {
1009             i++;
1010             continue;
1011         }
1012
1013         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
1014                                 (void **) &baseNames, NULL);
1015         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
1016                                 (void **) &dirIndexes, NULL);
1017         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
1018                                 (void **) &dirNames, NULL);
1019
1020         do {
1021             fingerPrint fp2;
1022             int num = dbiIndexRecordFileNumber(allMatches, i);
1023
1024             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1025             if (FP_EQUAL(fp1, fp2)) {
1026                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1027                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1028                 dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1029             }
1030
1031             prevoff = offset;
1032             i++;
1033             offset = dbiIndexRecordOffset(allMatches, i);
1034         } while (i < allMatches->count && 
1035                 (i == 0 || offset == prevoff));
1036
1037         free(baseNames);
1038         free(dirNames);
1039         headerFree(h);
1040     }
1041
1042     if (rec) {
1043         free(rec);
1044         rec = NULL;
1045     }
1046     if (allMatches) {
1047         dbiFreeIndexSet(allMatches);
1048         allMatches = NULL;
1049     }
1050
1051     fpCacheFree(fpc);
1052
1053     if ((*matches)->count == 0) {
1054         dbiFreeIndexSet(*matches);
1055         *matches = NULL; 
1056         return 1;
1057     }
1058
1059     return 0;
1060 }
1061
1062 /* XXX python/upgrade.c, install.c, uninstall.c */
1063 int rpmdbCountPackages(rpmdb rpmdb, const char * name)
1064 {
1065     dbiIndex dbi;
1066     dbiIndexSet matches = NULL;
1067     int rc = -1;
1068     int xx;
1069
1070     dbi = dbiOpen(rpmdb, RPMTAG_NAME, 0);
1071     if (dbi) {
1072         DBC * dbcursor = NULL;
1073         xx = dbiCopen(dbi, &dbcursor, 0);
1074         rc = dbiSearch(dbi, dbcursor, name, 0, &matches);
1075         xx = dbiCclose(dbi, dbcursor, 0);
1076         dbcursor = NULL;
1077     }
1078
1079     if (rc == 0)        /* success */
1080         rc = dbiIndexSetCount(matches);
1081     else if (rc > 0)    /* error */
1082         rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages"), rc);
1083     else                /* not found */
1084         rc = 0;
1085
1086     if (matches)
1087         dbiFreeIndexSet(matches);
1088
1089     return rc;
1090 }
1091
1092 /* XXX transaction.c */
1093 /* 0 found matches */
1094 /* 1 no matches */
1095 /* 2 error */
1096 static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1097         const char * name, const char * version, const char * release,
1098         /*@out@*/ dbiIndexSet * matches)
1099 {
1100     int gotMatches;
1101     int rc;
1102     int i;
1103
1104     rc = dbiSearch(dbi, dbcursor, name, 0, matches);
1105
1106     if (rc != 0) {
1107         rc = ((rc == -1) ? 2 : 1);
1108         goto exit;
1109     }
1110
1111     if (!version && !release) {
1112         rc = 0;
1113         goto exit;
1114     }
1115
1116     gotMatches = 0;
1117
1118     /* make sure the version and releases match */
1119     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1120         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1121         int goodRelease, goodVersion;
1122         const char * pkgVersion;
1123         const char * pkgRelease;
1124         Header h;
1125
1126         if (recoff == 0)
1127             continue;
1128
1129 #ifdef  DYING
1130         h = rpmdbGetRecord(dbi->dbi_rpmdb, recoff);
1131 #else
1132     {   rpmdbMatchIterator mi;
1133         mi = rpmdbInitIterator(dbi->dbi_rpmdb, RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1134         h = rpmdbNextIterator(mi);
1135         if (h)
1136             h = headerLink(h);
1137         rpmdbFreeIterator(mi);
1138     }
1139 #endif
1140         if (h == NULL) {
1141             rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x"),
1142                 "findMatches", recoff);
1143             rc = 2;
1144             goto exit;
1145         }
1146
1147         headerNVR(h, NULL, &pkgVersion, &pkgRelease);
1148             
1149         goodRelease = goodVersion = 1;
1150
1151         if (release && strcmp(release, pkgRelease)) goodRelease = 0;
1152         if (version && strcmp(version, pkgVersion)) goodVersion = 0;
1153
1154         if (goodRelease && goodVersion) {
1155             /* structure assignment */
1156             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1157         } else 
1158             (*matches)->recs[i].hdrNum = 0;
1159
1160         headerFree(h);
1161     }
1162
1163     if (gotMatches) {
1164         (*matches)->count = gotMatches;
1165         rc = 0;
1166     } else {
1167         rc = 1;
1168     }
1169
1170 exit:
1171     if (rc && matches && *matches) {
1172         dbiFreeIndexSet(*matches);
1173         *matches = NULL;
1174     }
1175     return rc;
1176 }
1177
1178 /* 0 found matches */
1179 /* 1 no matches */
1180 /* 2 error */
1181 static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, const char * arg, dbiIndexSet * matches)
1182 {
1183     char * localarg, * chptr;
1184     char * release;
1185     int rc;
1186  
1187     if (!strlen(arg)) return 1;
1188
1189     /* did they give us just a name? */
1190     rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches);
1191     if (rc != 1) return rc;
1192     if (*matches) {
1193         dbiFreeIndexSet(*matches);
1194         *matches = NULL;
1195     }
1196
1197     /* maybe a name and a release */
1198     localarg = alloca(strlen(arg) + 1);
1199     strcpy(localarg, arg);
1200
1201     chptr = (localarg + strlen(localarg)) - 1;
1202     while (chptr > localarg && *chptr != '-') chptr--;
1203     if (chptr == localarg) return 1;
1204
1205     *chptr = '\0';
1206     rc = dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, NULL, matches);
1207     if (rc != 1) return rc;
1208     if (*matches) dbiFreeIndexSet(*matches);
1209     
1210     /* how about name-version-release? */
1211
1212     release = chptr + 1;
1213     while (chptr > localarg && *chptr != '-') chptr--;
1214     if (chptr == localarg) return 1;
1215
1216     *chptr = '\0';
1217     return dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, release, matches);
1218 }
1219
1220 /**
1221  * Rewrite a header in the database.
1222  *   Note: this is called from a markReplacedFiles iteration, and *must*
1223  *   preserve the "join key" (i.e. offset) for the header.
1224  * @param dbi           index database handle (always RPMDBI_PACKAGES)
1225  * @param offset        join key
1226  * @param h             rpm header
1227  * @return              0 on success
1228  */
1229 static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h)
1230 {
1231     sigset_t signalMask;
1232     void * uh;
1233     size_t uhlen;
1234     int rc;
1235     int xx;
1236
1237     if (_noDirTokens)
1238         expandFilelist(h);
1239
1240     uhlen = headerSizeof(h, HEADER_MAGIC_NO);
1241     uh = headerUnload(h);
1242     blockSignals(dbi->dbi_rpmdb, &signalMask);
1243     rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, 0);
1244     xx = dbiSync(dbi, 0);
1245     unblockSignals(dbi->dbi_rpmdb, &signalMask);
1246     free(uh);
1247
1248     return rc;
1249 }
1250
1251 struct _rpmdbMatchIterator {
1252     const void *        mi_keyp;
1253     size_t              mi_keylen;
1254     rpmdb               mi_rpmdb;
1255     int                 mi_rpmtag;
1256     dbiIndexSet         mi_set;
1257     DBC *               mi_dbc;
1258     int                 mi_setx;
1259     Header              mi_h;
1260     int                 mi_sorted;
1261     int                 mi_modified;
1262     unsigned int        mi_prevoffset;
1263     unsigned int        mi_offset;
1264     unsigned int        mi_filenum;
1265     unsigned int        mi_fpnum;
1266     unsigned int        mi_dbnum;
1267     const char *        mi_version;
1268     const char *        mi_release;
1269 };
1270
1271 void rpmdbFreeIterator(rpmdbMatchIterator mi)
1272 {
1273     dbiIndex dbi = NULL;
1274     int xx;
1275
1276     if (mi == NULL)
1277         return;
1278
1279     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
1280     if (mi->mi_h) {
1281         if (mi->mi_modified && mi->mi_prevoffset) {
1282             DBC * dbcursor = NULL;
1283             xx = dbiCopen(dbi, &dbcursor, 0);
1284             dbiUpdateRecord(dbi, dbcursor, mi->mi_prevoffset, mi->mi_h);
1285             xx = dbiCclose(dbi, dbcursor, 0);
1286             dbcursor = NULL;
1287         }
1288         headerFree(mi->mi_h);
1289         mi->mi_h = NULL;
1290     }
1291     if (dbi->dbi_rmw) {
1292         xx = dbiCclose(dbi, dbi->dbi_rmw, 0);
1293         dbi->dbi_rmw = NULL;
1294     }
1295
1296     if (mi->mi_release) {
1297         xfree(mi->mi_release);
1298         mi->mi_release = NULL;
1299     }
1300     if (mi->mi_version) {
1301         xfree(mi->mi_version);
1302         mi->mi_version = NULL;
1303     }
1304     if (mi->mi_dbc) {
1305         int xx = dbiCclose(dbi, mi->mi_dbc, 1);
1306         mi->mi_dbc = NULL;
1307     }
1308     if (mi->mi_set) {
1309         dbiFreeIndexSet(mi->mi_set);
1310         mi->mi_set = NULL;
1311     }
1312     if (mi->mi_keyp) {
1313         xfree(mi->mi_keyp);
1314         mi->mi_keyp = NULL;
1315     }
1316     free(mi);
1317 }
1318
1319 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
1320     if (mi == NULL)
1321         return 0;
1322     return mi->mi_rpmdb;
1323 }
1324
1325 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1326     if (mi == NULL)
1327         return 0;
1328     return mi->mi_offset;
1329 }
1330
1331 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1332     if (mi == NULL)
1333         return 0;
1334     return mi->mi_filenum;
1335 }
1336
1337 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1338     if (!(mi && mi->mi_set))
1339         return 0;       /* XXX W2DO? */
1340     return mi->mi_set->count;
1341 }
1342
1343 void rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
1344     if (mi == NULL)
1345         return;
1346     if (mi->mi_release) {
1347         xfree(mi->mi_release);
1348         mi->mi_release = NULL;
1349     }
1350     mi->mi_release = (release ? xstrdup(release) : NULL);
1351 }
1352
1353 void rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
1354     if (mi == NULL)
1355         return;
1356     if (mi->mi_version) {
1357         xfree(mi->mi_version);
1358         mi->mi_version = NULL;
1359     }
1360     mi->mi_version = (version ? xstrdup(version) : NULL);
1361 }
1362
1363 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
1364     int rc;
1365     if (mi == NULL)
1366         return 0;
1367     rc = mi->mi_modified;
1368     mi->mi_modified = modified;
1369     return rc;
1370 }
1371
1372 Header XrpmdbNextIterator(rpmdbMatchIterator mi, const char * f, unsigned l)
1373 {
1374     dbiIndex dbi;
1375     void * uh = NULL;
1376     size_t uhlen = 0;
1377     void * keyp;
1378     size_t keylen;
1379     int rc;
1380     int xx;
1381
1382     if (mi == NULL)
1383         return NULL;
1384
1385     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
1386     if (dbi == NULL)
1387         return NULL;
1388     /* XXX cursors need to be per-iterator, not per-dbi. Get a cursor now. */
1389     if (mi->mi_dbc == NULL) {
1390         xx = XdbiCopen(dbi, &mi->mi_dbc, 1, f, l);
1391      }
1392
1393 top:
1394     /* XXX skip over instances with 0 join key */
1395     do {
1396         if (mi->mi_set) {
1397             if (!(mi->mi_setx < mi->mi_set->count))
1398                 return NULL;
1399             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1400             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1401             keyp = &mi->mi_offset;
1402             keylen = sizeof(mi->mi_offset);
1403         } else {
1404             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
1405             keylen = mi->mi_keylen;
1406
1407             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
1408
1409             /*
1410              * If we got the next key, save the header instance number.
1411              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
1412              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
1413              * largest header instance in the database, and should be
1414              * skipped.
1415              */
1416             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
1417                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
1418
1419             /* Terminate on error or end of keys */
1420             if (rc || (mi->mi_setx && mi->mi_offset == 0))
1421                 return NULL;
1422         }
1423         mi->mi_setx++;
1424     } while (mi->mi_offset == 0);
1425
1426     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
1427         return mi->mi_h;
1428
1429     /* Retrieve next header */
1430     if (uh == NULL) {
1431         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
1432         if (rc)
1433             return NULL;
1434     }
1435
1436     /* Free current header */
1437     if (mi->mi_h) {
1438         if (mi->mi_modified && mi->mi_prevoffset)
1439             dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
1440         headerFree(mi->mi_h);
1441         mi->mi_h = NULL;
1442     }
1443
1444     mi->mi_h = headerLoad(uh);
1445
1446     if (mi->mi_release) {
1447         const char *release;
1448         headerNVR(mi->mi_h, NULL, NULL, &release);
1449         if (strcmp(mi->mi_release, release))
1450             goto top;
1451     }
1452
1453     if (mi->mi_version) {
1454         const char *version;
1455         headerNVR(mi->mi_h, NULL, &version, NULL);
1456         if (strcmp(mi->mi_version, version))
1457             goto top;
1458     }
1459
1460     mi->mi_prevoffset = mi->mi_offset;
1461     mi->mi_modified = 0;
1462     return mi->mi_h;
1463 }
1464
1465 static void rpmdbSortIterator(rpmdbMatchIterator mi) {
1466     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
1467         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
1468                 hdrNumCmp);
1469         mi->mi_sorted = 1;
1470     }
1471 }
1472
1473 static int rpmdbGrowIterator(rpmdbMatchIterator mi,
1474         const void * keyp, size_t keylen, int fpNum)
1475 {
1476     dbiIndex dbi = NULL;
1477     DBC * dbcursor = NULL;
1478     dbiIndexSet set = NULL;
1479     int rc;
1480     int xx;
1481
1482     if (!(mi && keyp))
1483         return 1;
1484
1485     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
1486     if (dbi == NULL)
1487         return 1;
1488
1489     if (keylen == 0)
1490         keylen = strlen(keyp);
1491
1492     xx = dbiCopen(dbi, &dbcursor, 0);
1493     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1494     xx = dbiCclose(dbi, dbcursor, 0);
1495     dbcursor = NULL;
1496
1497     if (rc == 0) {      /* success */
1498         int i;
1499         for (i = 0; i < set->count; i++)
1500             set->recs[i].fpNum = fpNum;
1501
1502         if (mi->mi_set == NULL) {
1503             mi->mi_set = set;
1504             set = NULL;
1505         } else {
1506             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
1507                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
1508             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
1509                 set->count * sizeof(*(mi->mi_set->recs)));
1510             mi->mi_set->count += set->count;
1511         }
1512     }
1513
1514     if (set)
1515         dbiFreeIndexSet(set);
1516     return rc;
1517 }
1518
1519 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
1520         int nHdrNums, int sorted)
1521 {
1522     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
1523         return 1;
1524
1525     if (mi->mi_set)
1526         dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
1527     return 0;
1528 }
1529
1530 int rpmdbAppendIterator(rpmdbMatchIterator mi, int * hdrNums, int nHdrNums)
1531 {
1532     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
1533         return 1;
1534
1535     if (mi->mi_set == NULL)
1536         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
1537     dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
1538     return 0;
1539 }
1540
1541 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
1542         const void * keyp, size_t keylen)
1543 {
1544     rpmdbMatchIterator mi = NULL;
1545     dbiIndexSet set = NULL;
1546     dbiIndex dbi;
1547     int isLabel = 0;
1548
1549     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
1550     switch (rpmtag) {
1551     case RPMDBI_LABEL:
1552         rpmtag = RPMTAG_NAME;
1553         isLabel = 1;
1554         break;
1555     }
1556
1557     dbi = dbiOpen(rpmdb, rpmtag, 0);
1558     if (dbi == NULL)
1559         return NULL;
1560
1561 #if 0
1562     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
1563 #else
1564 if (dbi->dbi_rmw)
1565 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
1566 #endif
1567     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
1568
1569     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
1570
1571     if (rpmtag != RPMDBI_PACKAGES && keyp) {
1572         DBC * dbcursor = NULL;
1573         int rc;
1574         int xx;
1575
1576         if (isLabel) {
1577             /* XXX HACK to get rpmdbFindByLabel out of the API */
1578             xx = dbiCopen(dbi, &dbcursor, 0);
1579             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
1580             xx = dbiCclose(dbi, dbcursor, 0);
1581             dbcursor = NULL;
1582         } else if (rpmtag == RPMTAG_BASENAMES) {
1583             rc = rpmdbFindByFile(rpmdb, keyp, &set);
1584         } else {
1585             xx = dbiCopen(dbi, &dbcursor, 0);
1586             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1587             xx = dbiCclose(dbi, dbcursor, 0);
1588             dbcursor = NULL;
1589         }
1590         if (rc) {       /* error/not found */
1591             if (set)
1592                 dbiFreeIndexSet(set);
1593             return NULL;
1594         }
1595     }
1596
1597     if (keyp) {
1598         char * k;
1599
1600         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
1601             keylen = strlen(keyp);
1602         k = xmalloc(keylen + 1);
1603         memcpy(k, keyp, keylen);
1604         k[keylen] = '\0';       /* XXX for strings */
1605         keyp = k;
1606     }
1607
1608     mi = xcalloc(sizeof(*mi), 1);
1609     mi->mi_keyp = keyp;
1610     mi->mi_keylen = keylen;
1611
1612     mi->mi_rpmdb = rpmdb;
1613     mi->mi_rpmtag = rpmtag;
1614
1615     mi->mi_dbc = NULL;
1616     mi->mi_set = set;
1617     mi->mi_setx = 0;
1618     mi->mi_h = NULL;
1619     mi->mi_sorted = 0;
1620     mi->mi_modified = 0;
1621     mi->mi_prevoffset = 0;
1622     mi->mi_offset = 0;
1623     mi->mi_filenum = 0;
1624     mi->mi_fpnum = 0;
1625     mi->mi_dbnum = 0;
1626     mi->mi_version = NULL;
1627     mi->mi_release = NULL;
1628     return mi;
1629 }
1630
1631 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor, const char * keyp,
1632                 dbiIndexItem rec)
1633 {
1634     dbiIndexSet set = NULL;
1635     int rc;
1636     
1637     rc = dbiSearch(dbi, dbcursor, keyp, 0, &set);
1638
1639     if (rc < 0)                 /* not found */
1640         rc = 0;
1641     else if (rc > 0)            /* error */
1642         rc = 1;         /* error message already generated from dbindex.c */
1643     else {                      /* success */
1644         if (!dbiPruneSet(set, rec, 1, sizeof(*rec), 1) &&
1645             dbiUpdateIndex(dbi, dbcursor, keyp, set))
1646             rc = 1;
1647     }
1648
1649     if (set) {
1650         dbiFreeIndexSet(set);
1651         set = NULL;
1652     }
1653
1654     return rc;
1655 }
1656
1657 /* XXX install.c uninstall.c */
1658 int rpmdbRemove(rpmdb rpmdb, unsigned int hdrNum)
1659 {
1660     Header h;
1661     sigset_t signalMask;
1662
1663 #ifdef  DYING
1664     h = rpmdbGetRecord(rpmdb, hdrNum);
1665 #else
1666   { rpmdbMatchIterator mi;
1667     mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
1668     h = rpmdbNextIterator(mi);
1669     if (h)
1670         h = headerLink(h);
1671     rpmdbFreeIterator(mi);
1672   }
1673 #endif
1674     if (h == NULL) {
1675         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x"),
1676               "rpmdbRemove", hdrNum);
1677         return 1;
1678     }
1679
1680     {   const char *n, *v, *r;
1681         headerNVR(h, &n, &v, &r);
1682         rpmMessage(RPMMESS_DEBUG, "  --- %10d %s-%s-%s\n", hdrNum, n, v, r);
1683     }
1684
1685     blockSignals(rpmdb, &signalMask);
1686
1687     {   int dbix;
1688         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
1689
1690         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
1691             dbiIndex dbi;
1692             DBC * dbcursor = NULL;
1693             const char *av[1];
1694             const char ** rpmvals = NULL;
1695             int rpmtype = 0;
1696             int rpmcnt = 0;
1697             int rpmtag;
1698             int xx;
1699             int i;
1700
1701             dbi = NULL;
1702             rpmtag = dbiTags[dbix];
1703
1704             switch (rpmtag) {
1705             /* Filter out temporary databases */
1706             case RPMDBI_AVAILABLE:
1707             case RPMDBI_ADDED:
1708             case RPMDBI_REMOVED:
1709             case RPMDBI_DEPENDS:
1710                 continue;
1711                 /*@notreached@*/ break;
1712             case RPMDBI_PACKAGES:
1713                 dbi = dbiOpen(rpmdb, rpmtag, 0);
1714                 xx = dbiCopen(dbi, &dbcursor, 0);
1715                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
1716                 xx = dbiCclose(dbi, dbcursor, 0);
1717                 dbcursor = NULL;
1718                 /* XXX HACK sync is on the bt with multiple db access */
1719                 if (!dbi->dbi_no_dbsync)
1720                     xx = dbiSync(dbi, 0);
1721                 continue;
1722                 /*@notreached@*/ break;
1723             }
1724         
1725             if (!headerGetEntry(h, rpmtag, &rpmtype,
1726                 (void **) &rpmvals, &rpmcnt)) {
1727 #if 0
1728                 rpmMessage(RPMMESS_DEBUG, _("removing 0 %s entries.\n"),
1729                         tagName(rpmtag));
1730 #endif
1731                 continue;
1732             }
1733
1734             dbi = dbiOpen(rpmdb, rpmtag, 0);
1735             xx = dbiCopen(dbi, &dbcursor, 0);
1736
1737             if (rpmtype == RPM_STRING_TYPE) {
1738
1739                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
1740                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
1741
1742                 /* XXX force uniform headerGetEntry return */
1743                 av[0] = (const char *) rpmvals;
1744                 rpmvals = av;
1745                 rpmcnt = 1;
1746             } else {
1747
1748                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
1749                         rpmcnt, tagName(dbi->dbi_rpmtag));
1750
1751             }
1752
1753             for (i = 0; i < rpmcnt; i++) {
1754 #if 0
1755                 rpmMessage(RPMMESS_DEBUG, ("%6d %s\n"), i, rpmvals[i]);
1756 #endif
1757                 /*
1758                  * This is almost right, but, if there are duplicate tag
1759                  * values, there will be duplicate attempts to remove
1760                  * the header instance. It's easier to just ignore errors
1761                  * than to do things correctly.
1762                  */
1763                 xx = removeIndexEntry(dbi, dbcursor, rpmvals[i], rec);
1764             }
1765
1766             xx = dbiCclose(dbi, dbcursor, 0);
1767             dbcursor = NULL;
1768
1769             /* XXX HACK sync is on the bt with multiple db access */
1770             if (!dbi->dbi_no_dbsync)
1771                 xx = dbiSync(dbi, 0);
1772
1773             switch (rpmtype) {
1774             case RPM_STRING_ARRAY_TYPE:
1775             case RPM_I18NSTRING_TYPE:
1776                 xfree(rpmvals);
1777                 rpmvals = NULL;
1778                 break;
1779             }
1780             rpmtype = 0;
1781             rpmcnt = 0;
1782         }
1783
1784         if (rec) {
1785             free(rec);
1786             rec = NULL;
1787         }
1788     }
1789
1790     unblockSignals(rpmdb, &signalMask);
1791
1792     headerFree(h);
1793
1794     return 0;
1795 }
1796
1797 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor, const char *index, dbiIndexItem rec)
1798 {
1799     dbiIndexSet set = NULL;
1800     int rc;
1801
1802     rc = dbiSearch(dbi, dbcursor, index, 0, &set);
1803
1804     if (rc > 0) {
1805         rc = 1;                 /* error */
1806     } else {
1807         if (rc < 0) {           /* not found */
1808             rc = 0;
1809             set = xcalloc(1, sizeof(*set));
1810         }
1811         dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
1812         if (dbiUpdateIndex(dbi, dbcursor, index, set))
1813             rc = 1;
1814     }
1815
1816     if (set) {
1817         dbiFreeIndexSet(set);
1818         set = NULL;
1819     }
1820
1821     return 0;
1822 }
1823
1824 /* XXX install.c */
1825 int rpmdbAdd(rpmdb rpmdb, Header h)
1826 {
1827     sigset_t signalMask;
1828     const char ** baseNames;
1829     int count = 0;
1830     int type;
1831     dbiIndex dbi;
1832     int dbix;
1833     unsigned int hdrNum;
1834     int rc = 0;
1835     int xx;
1836
1837     /*
1838      * If old style filename tags is requested, the basenames need to be
1839      * retrieved early, and the header needs to be converted before
1840      * being written to the package header database.
1841      */
1842
1843     headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames, &count);
1844
1845     if (_noDirTokens)
1846         expandFilelist(h);
1847
1848     blockSignals(rpmdb, &signalMask);
1849
1850     {
1851         unsigned int firstkey = 0;
1852         DBC * dbcursor = NULL;
1853         void * keyp = &firstkey;
1854         size_t keylen = sizeof(firstkey);
1855         void * datap = NULL;
1856         size_t datalen = 0;
1857
1858         dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
1859
1860         /* XXX db0: hack to pass sizeof header to fadAlloc */
1861         datap = h;
1862         datalen = headerSizeof(h, HEADER_MAGIC_NO);
1863
1864         xx = dbiCopen(dbi, &dbcursor, 0);
1865
1866         /* Retrieve join key for next header instance. */
1867
1868         rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, 0);
1869
1870         hdrNum = 0;
1871         if (rc == 0 && datap)
1872             memcpy(&hdrNum, datap, sizeof(hdrNum));
1873         ++hdrNum;
1874         if (rc == 0 && datap) {
1875             memcpy(datap, &hdrNum, sizeof(hdrNum));
1876         } else {
1877             datap = &hdrNum;
1878             datalen = sizeof(hdrNum);
1879         }
1880
1881         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
1882         xx = dbiSync(dbi, 0);
1883
1884         xx = dbiCclose(dbi, dbcursor, 0);
1885         dbcursor = NULL;
1886
1887     }
1888
1889     if (rc) {
1890         rpmError(RPMERR_DBCORRUPT, _("error(%d) allocating new package instance"), rc);
1891         goto exit;
1892     }
1893
1894     /* Now update the indexes */
1895
1896     {   dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
1897
1898         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
1899             DBC * dbcursor = NULL;
1900             const char *av[1];
1901             const char **rpmvals = NULL;
1902             int rpmtype = 0;
1903             int rpmcnt = 0;
1904             int rpmtag;
1905             int i, j;
1906
1907             dbi = NULL;
1908             rpmtag = dbiTags[dbix];
1909
1910             switch (rpmtag) {
1911             /* Filter out temporary databases */
1912             case RPMDBI_AVAILABLE:
1913             case RPMDBI_ADDED:
1914             case RPMDBI_REMOVED:
1915             case RPMDBI_DEPENDS:
1916                 continue;
1917                 /*@notreached@*/ break;
1918             case RPMDBI_PACKAGES:
1919                 dbi = dbiOpen(rpmdb, rpmtag, 0);
1920                 xx = dbiCopen(dbi, &dbcursor, 0);
1921                 xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h);
1922                 xx = dbiCclose(dbi, dbcursor, 0);
1923                 dbcursor = NULL;
1924                 if (!dbi->dbi_no_dbsync)
1925                     xx = dbiSync(dbi, 0);
1926                 {   const char *n, *v, *r;
1927                     headerNVR(h, &n, &v, &r);
1928                     rpmMessage(RPMMESS_DEBUG, "  +++ %10d %s-%s-%s\n", hdrNum, n, v, r);
1929                 }
1930                 continue;
1931                 /*@notreached@*/ break;
1932             /* XXX preserve legacy behavior */
1933             case RPMTAG_BASENAMES:
1934                 rpmtype = type;
1935                 rpmvals = baseNames;
1936                 rpmcnt = count;
1937                 break;
1938             default:
1939                 headerGetEntry(h, rpmtag, &rpmtype,
1940                         (void **) &rpmvals, &rpmcnt);
1941                 break;
1942             }
1943
1944             if (rpmcnt <= 0) {
1945                 if (rpmtag != RPMTAG_GROUP) {
1946 #if 0
1947                     rpmMessage(RPMMESS_DEBUG, _("adding 0 %s entries.\n"),
1948                         tagName(rpmtag));
1949 #endif
1950                     continue;
1951                 }
1952
1953                 /* XXX preserve legacy behavior */
1954                 rpmtype = RPM_STRING_TYPE;
1955                 rpmvals = (const char **) "Unknown";
1956                 rpmcnt = 1;
1957             }
1958
1959             dbi = dbiOpen(rpmdb, rpmtag, 0);
1960
1961             xx = dbiCopen(dbi, &dbcursor, 0);
1962             if (rpmtype == RPM_STRING_TYPE) {
1963                 rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), 
1964                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
1965
1966                 /* XXX force uniform headerGetEntry return */
1967                 av[0] = (const char *) rpmvals;
1968                 rpmvals = av;
1969                 rpmcnt = 1;
1970             } else {
1971
1972                 rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), 
1973                         rpmcnt, tagName(dbi->dbi_rpmtag));
1974
1975             }
1976
1977             for (i = 0; i < rpmcnt; i++) {
1978 #if 0
1979                 rpmMessage(RPMMESS_DEBUG, ("%6d %s\n"), i, rpmvals[i]);
1980 #endif
1981
1982                 /*
1983                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
1984                  * included the tagNum only for files.
1985                  */
1986                 switch (dbi->dbi_rpmtag) {
1987                 case RPMTAG_TRIGGERNAME:
1988                     if (i) {    /* don't add duplicates */
1989                         for (j = 0; j < i; j++) {
1990                             if (!strcmp(rpmvals[i], rpmvals[j]))
1991                                 break;
1992                         }
1993                         if (j < i)
1994                             continue;
1995                     }
1996                     /*@fallthrough@*/
1997                 default:
1998                     rec->tagNum = i;
1999                     break;
2000                 }
2001
2002                 rc += addIndexEntry(dbi, dbcursor, rpmvals[i], rec);
2003             }
2004             xx = dbiCclose(dbi, dbcursor, 0);
2005             dbcursor = NULL;
2006
2007             /* XXX HACK sync is on the bt with multiple db access */
2008             if (!dbi->dbi_no_dbsync)
2009                 xx = dbiSync(dbi, 0);
2010
2011             switch (rpmtype) {
2012             case RPM_STRING_ARRAY_TYPE:
2013             case RPM_I18NSTRING_TYPE:
2014                 xfree(rpmvals);
2015                 rpmvals = NULL;
2016                 break;
2017             }
2018             rpmtype = 0;
2019             rpmcnt = 0;
2020         }
2021
2022         if (rec) {
2023             free(rec);
2024             rec = NULL;
2025         }
2026     }
2027
2028 exit:
2029     unblockSignals(rpmdb, &signalMask);
2030
2031     return rc;
2032 }
2033
2034 /* XXX transaction.c */
2035 int rpmdbFindFpList(rpmdb rpmdb, fingerPrint * fpList, dbiIndexSet * matchList, 
2036                     int numItems)
2037 {
2038     rpmdbMatchIterator mi;
2039     fingerPrintCache fpc;
2040     Header h;
2041     int i;
2042
2043     mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, NULL, 0);
2044
2045     /* Gather all matches from the database */
2046     for (i = 0; i < numItems; i++) {
2047         rpmdbGrowIterator(mi, fpList[i].baseName, 0, i);
2048         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
2049     }
2050
2051     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
2052         rpmdbFreeIterator(mi);
2053         return 0;
2054     }
2055     fpc = fpCacheCreate(i);
2056
2057     rpmdbSortIterator(mi);
2058     /* iterator is now sorted by (recnum, filenum) */
2059
2060     /* For each set of files matched in a package ... */
2061     while ((h = rpmdbNextIterator(mi)) != NULL) {
2062         const char ** dirNames;
2063         const char ** baseNames;
2064         const char ** fullBaseNames;
2065         int_32 * dirIndexes;
2066         int_32 * fullDirIndexes;
2067         fingerPrint * fps;
2068         dbiIndexItem im;
2069         int start;
2070         int num;
2071         int end;
2072
2073         start = mi->mi_setx - 1;
2074         im = mi->mi_set->recs + start;
2075
2076         /* Find the end of the set of matched files in this package. */
2077         for (end = start + 1; end < mi->mi_set->count; end++) {
2078             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
2079                 break;
2080         }
2081         num = end - start;
2082
2083         /* Compute fingerprints for this header's matches */
2084         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
2085                             (void **) &dirNames, NULL);
2086         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
2087                             (void **) &fullBaseNames, NULL);
2088         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
2089                             (void **) &fullDirIndexes, NULL);
2090
2091         baseNames = xcalloc(num, sizeof(*baseNames));
2092         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
2093         for (i = 0; i < num; i++) {
2094             baseNames[i] = fullBaseNames[im[i].tagNum];
2095             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
2096         }
2097
2098         fps = xcalloc(num, sizeof(*fps));
2099         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
2100
2101         /* Add db (recnum,filenum) to list for fingerprint matches. */
2102         for (i = 0; i < num; i++, im++) {
2103             if (FP_EQUAL(fps[i], fpList[im->fpNum]))
2104                 dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
2105         }
2106
2107         free(fps);
2108         free(dirNames);
2109         free(fullBaseNames);
2110         free(baseNames);
2111         free(dirIndexes);
2112
2113         mi->mi_setx = end;
2114     }
2115
2116     rpmdbFreeIterator(mi);
2117
2118     fpCacheFree(fpc);
2119
2120     return 0;
2121
2122 }
2123
2124 static int rpmdbRemoveDatabase(const char * rootdir,
2125         const char * dbpath, int _dbapi)
2126
2127     int i;
2128     char * filename;
2129     int xx;
2130
2131     i = strlen(dbpath);
2132     if (dbpath[i - 1] != '/') {
2133         filename = alloca(i);
2134         strcpy(filename, dbpath);
2135         filename[i] = '/';
2136         filename[i + 1] = '\0';
2137         dbpath = filename;
2138     }
2139     
2140     filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
2141
2142     switch (_dbapi) {
2143     case 3:
2144         for (i = 0; i < dbiTagsMax; i++) {
2145             const char * base = tagName(dbiTags[i]);
2146             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
2147             (void)rpmCleanPath(filename);
2148             xx = unlink(filename);
2149         }
2150         for (i = 0; i < 16; i++) {
2151             sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
2152             (void)rpmCleanPath(filename);
2153             xx = unlink(filename);
2154         }
2155         break;
2156     case 2:
2157     case 1:
2158     case 0:
2159         for (i = 0; i < dbiTagsMax; i++) {
2160             const char * base = db1basename(dbiTags[i]);
2161             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
2162             (void)rpmCleanPath(filename);
2163             xx = unlink(filename);
2164             xfree(base);
2165         }
2166         break;
2167     }
2168
2169     sprintf(filename, "%s/%s", rootdir, dbpath);
2170     (void)rpmCleanPath(filename);
2171     xx = rmdir(filename);
2172
2173     return 0;
2174 }
2175
2176 static int rpmdbMoveDatabase(const char * rootdir,
2177         const char * olddbpath, int _olddbapi,
2178         const char * newdbpath, int _newdbapi)
2179 {
2180     int i;
2181     char * ofilename, * nfilename;
2182     int rc = 0;
2183     int xx;
2184  
2185     i = strlen(olddbpath);
2186     if (olddbpath[i - 1] != '/') {
2187         ofilename = alloca(i + 2);
2188         strcpy(ofilename, olddbpath);
2189         ofilename[i] = '/';
2190         ofilename[i + 1] = '\0';
2191         olddbpath = ofilename;
2192     }
2193     
2194     i = strlen(newdbpath);
2195     if (newdbpath[i - 1] != '/') {
2196         nfilename = alloca(i + 2);
2197         strcpy(nfilename, newdbpath);
2198         nfilename[i] = '/';
2199         nfilename[i + 1] = '\0';
2200         newdbpath = nfilename;
2201     }
2202     
2203     ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
2204     nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
2205
2206     switch (_olddbapi) {
2207     case 3:
2208         for (i = 0; i < dbiTagsMax; i++) {
2209             const char * base;
2210             int rpmtag;
2211
2212             /* Filter out temporary databases */
2213             switch ((rpmtag = dbiTags[i])) {
2214             case RPMDBI_AVAILABLE:
2215             case RPMDBI_ADDED:
2216             case RPMDBI_REMOVED:
2217             case RPMDBI_DEPENDS:
2218                 continue;
2219                 /*@notreached@*/ break;
2220             default:
2221                 break;
2222             }
2223
2224             base = tagName(rpmtag);
2225             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
2226             (void)rpmCleanPath(ofilename);
2227             if (!rpmfileexists(ofilename))
2228                 continue;
2229             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
2230             (void)rpmCleanPath(nfilename);
2231             if ((xx = Rename(ofilename, nfilename)) != 0)
2232                 rc = 1;
2233         }
2234         for (i = 0; i < 16; i++) {
2235             sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
2236             (void)rpmCleanPath(ofilename);
2237             if (!rpmfileexists(ofilename))
2238                 continue;
2239             sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
2240             (void)rpmCleanPath(nfilename);
2241             if ((xx = Rename(ofilename, nfilename)) != 0)
2242                 rc = 1;
2243         }
2244         break;
2245     case 2:
2246     case 1:
2247     case 0:
2248         for (i = 0; i < dbiTagsMax; i++) {
2249             const char * base;
2250             int rpmtag;
2251
2252             /* Filter out temporary databases */
2253             switch ((rpmtag = dbiTags[i])) {
2254             case RPMDBI_AVAILABLE:
2255             case RPMDBI_ADDED:
2256             case RPMDBI_REMOVED:
2257             case RPMDBI_DEPENDS:
2258                 continue;
2259                 /*@notreached@*/ break;
2260             default:
2261                 break;
2262             }
2263
2264             base = db1basename(rpmtag);
2265             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
2266             (void)rpmCleanPath(ofilename);
2267             if (!rpmfileexists(ofilename))
2268                 continue;
2269             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
2270             (void)rpmCleanPath(nfilename);
2271             if ((xx = Rename(ofilename, nfilename)) != 0)
2272                 rc = 1;
2273             xfree(base);
2274         }
2275         break;
2276     }
2277     if (rc || _olddbapi == _newdbapi)
2278         return rc;
2279
2280     rc = rpmdbRemoveDatabase(rootdir, newdbpath, _newdbapi);
2281     return rc;
2282 }
2283
2284 int rpmdbRebuild(const char * rootdir)
2285 {
2286     rpmdb olddb;
2287     const char * dbpath = NULL;
2288     const char * rootdbpath = NULL;
2289     rpmdb newdb;
2290     const char * newdbpath = NULL;
2291     const char * newrootdbpath = NULL;
2292     const char * tfn;
2293     int nocleanup = 1;
2294     int failed = 0;
2295     int removedir = 0;
2296     int rc = 0;
2297     int _dbapi;
2298     int _dbapi_rebuild;
2299
2300     _dbapi = rpmExpandNumeric("%{_dbapi}");
2301     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
2302
2303     tfn = rpmGetPath("%{_dbpath}", NULL);
2304     if (!(tfn && tfn[0] != '%')) {
2305         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
2306         rc = 1;
2307         goto exit;
2308     }
2309     dbpath = rootdbpath = rpmGetPath(rootdir, tfn, NULL);
2310     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
2311         dbpath += strlen(rootdir);
2312     xfree(tfn);
2313
2314     tfn = rpmGetPath("%{_dbpath_rebuild}", NULL);
2315     if (!(tfn && tfn[0] != '%' && strcmp(tfn, dbpath))) {
2316         char pidbuf[20];
2317         char *t;
2318         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
2319         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
2320         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
2321         if (tfn) xfree(tfn);
2322         tfn = t;
2323         nocleanup = 0;
2324     }
2325     newdbpath = newrootdbpath = rpmGetPath(rootdir, tfn, NULL);
2326     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
2327         newdbpath += strlen(rootdir);
2328     xfree(tfn);
2329
2330     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
2331         rootdbpath, newrootdbpath);
2332
2333     if (!access(newrootdbpath, F_OK)) {
2334         rpmError(RPMERR_MKDIR, _("temporary database %s already exists"),
2335               newrootdbpath);
2336         rc = 1;
2337         goto exit;
2338     }
2339
2340     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
2341     if (Mkdir(newrootdbpath, 0755)) {
2342         rpmError(RPMERR_MKDIR, _("error creating directory %s: %s"),
2343               newrootdbpath, strerror(errno));
2344         rc = 1;
2345         goto exit;
2346     }
2347     removedir = 1;
2348
2349     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
2350                 _dbapi);
2351     _rebuildinprogress = 1;
2352     if (openDatabase(rootdir, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
2353                      RPMDB_FLAG_MINIMAL)) {
2354         rc = 1;
2355         goto exit;
2356     }
2357     _dbapi = olddb->db_api;
2358     _rebuildinprogress = 0;
2359
2360     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
2361                 _dbapi_rebuild);
2362     if (openDatabase(rootdir, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
2363         rc = 1;
2364         goto exit;
2365     }
2366     _dbapi_rebuild = newdb->db_api;
2367     
2368     {   Header h = NULL;
2369         rpmdbMatchIterator mi;
2370 #define _RECNUM rpmdbGetIteratorOffset(mi)
2371
2372         /* RPMDBI_PACKAGES */
2373         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
2374         while ((h = rpmdbNextIterator(mi)) != NULL) {
2375
2376             /* let's sanity check this record a bit, otherwise just skip it */
2377             if (!(headerIsEntry(h, RPMTAG_NAME) &&
2378                 headerIsEntry(h, RPMTAG_VERSION) &&
2379                 headerIsEntry(h, RPMTAG_RELEASE) &&
2380                 headerIsEntry(h, RPMTAG_BUILDTIME)))
2381             {
2382                 rpmError(RPMERR_INTERNAL,
2383                         _("record number %d in database is bad -- skipping."), 
2384                         _RECNUM);
2385                 continue;
2386             }
2387
2388             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
2389             if (newdb->db_filter_dups) {
2390                 const char * name, * version, * release;
2391                 int skip = 0;
2392
2393                 headerNVR(h, &name, &version, &release);
2394
2395                 {   rpmdbMatchIterator mi;
2396                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
2397                     rpmdbSetIteratorVersion(mi, version);
2398                     rpmdbSetIteratorRelease(mi, release);
2399                     while (rpmdbNextIterator(mi)) {
2400                         skip = 1;
2401                         break;
2402                     }
2403                     rpmdbFreeIterator(mi);
2404                 }
2405
2406                 if (skip)
2407                     continue;
2408             }
2409
2410             /* Retrofit "Provide: name = EVR" for binary packages. */
2411             providePackageNVR(h);
2412
2413             if (rpmdbAdd(newdb, h)) {
2414                 rpmError(RPMERR_INTERNAL,
2415                         _("cannot add record originally at %d"), _RECNUM);
2416                 failed = 1;
2417                 break;
2418             }
2419         }
2420
2421         rpmdbFreeIterator(mi);
2422
2423     }
2424
2425     if (!nocleanup) {
2426         olddb->db_remove_env = 1;
2427         newdb->db_remove_env = 1;
2428     }
2429     rpmdbClose(olddb);
2430     rpmdbClose(newdb);
2431
2432     if (failed) {
2433         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
2434                 "remains in place\n"));
2435
2436         rpmdbRemoveDatabase(rootdir, newdbpath, _dbapi_rebuild);
2437         rc = 1;
2438         goto exit;
2439     } else if (!nocleanup) {
2440         if (rpmdbMoveDatabase(rootdir, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
2441             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
2442                         "database!\n"));
2443             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
2444                         "to recover"), dbpath, newdbpath);
2445             rc = 1;
2446             goto exit;
2447         }
2448     }
2449     rc = 0;
2450
2451 exit:
2452     if (removedir && !(rc == 0 && nocleanup)) {
2453         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
2454         if (Rmdir(newrootdbpath))
2455             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
2456                         newrootdbpath, strerror(errno));
2457     }
2458     if (newrootdbpath)  xfree(newrootdbpath);
2459     if (rootdbpath)     xfree(rootdbpath);
2460
2461     return rc;
2462 }