6ffd339c9d1d8a5fbea9f76025c7ae770c0276d7
[platform/upstream/libsolv.git] / tools / source_rpmdb.c
1 /*
2  * source_rpmdb
3  * 
4  * convert rpm db to source
5  * 
6  */
7
8 #include <sys/types.h>
9 #include <limits.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include <db43/db.h>
16
17 #include "pool.h"
18 #include "hash.h"
19 #include "source_rpmdb.h"
20
21 #define TAG_NAME                1000
22 #define TAG_VERSION             1001
23 #define TAG_RELEASE             1002
24 #define TAG_EPOCH               1003
25 #define TAG_SUMMARY             1004
26 #define TAG_DESCRIPTION         1005
27 #define TAG_BUILDTIME           1006
28 #define TAG_VENDOR              1011
29 #define TAG_ARCH                1022
30 #define TAG_PROVIDENAME         1047
31 #define TAG_REQUIREFLAGS        1048
32 #define TAG_REQUIRENAME         1049
33 #define TAG_REQUIREVERSION      1050
34 #define TAG_CONFLICTFLAGS       1053
35 #define TAG_CONFLICTNAME        1054
36 #define TAG_CONFLICTVERSION     1055
37 #define TAG_OBSOLETENAME        1090
38 #define TAG_PROVIDEFLAGS        1112
39 #define TAG_PROVIDEVERSION      1113
40 #define TAG_OBSOLETEFLAGS       1114
41 #define TAG_OBSOLETEVERSION     1115
42 #define TAG_DIRINDEXES          1116
43 #define TAG_BASENAMES           1117
44 #define TAG_DIRNAMES            1118
45 #define TAG_SUGGESTSNAME        1156
46 #define TAG_SUGGESTSVERSION     1157
47 #define TAG_SUGGESTSFLAGS       1158
48 #define TAG_ENHANCESNAME        1159
49 #define TAG_ENHANCESVERSION     1160
50 #define TAG_ENHANCESFLAGS       1161
51
52 #define DEP_LESS                (1 << 1)
53 #define DEP_GREATER             (1 << 2)
54 #define DEP_EQUAL               (1 << 3)
55 #define DEP_STRONG              (1 << 27)
56 #define DEP_PRE                 ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
57
58
59 static DBT key;
60 static DBT data;
61
62 struct rpmid {
63   unsigned int dbid;
64   char *name;
65 };
66
67 typedef struct rpmhead {
68   int cnt;
69   int dcnt;
70   unsigned char *dp;
71   unsigned char data[1];
72 } RpmHead;
73
74 static unsigned int *
75 headint32(RpmHead *h, int tag, int *cnt)
76 {
77   unsigned int i, o, *r;
78   unsigned char *d, taga[4];
79
80   d = h->dp - 16;
81   taga[0] = tag >> 24;
82   taga[1] = tag >> 16;
83   taga[2] = tag >> 8;
84   taga[3] = tag;
85   for (i = 0; i < h->cnt; i++, d -= 16)
86     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
87       break;
88   if (i >= h->cnt)
89     return 0;
90   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
91     return 0;
92   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
93   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
94   if (o + 4 * i > h->dcnt)
95     return 0;
96   d = h->dp + o;
97   r = calloc(i ? i : 1, sizeof(unsigned int));
98   if (cnt)
99     *cnt = i;
100   for (o = 0; o < i; o++, d += 4)
101     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
102   return r;
103 }
104
105 static char *
106 headstring(RpmHead *h, int tag)
107 {
108   unsigned int i, o;
109   unsigned char *d, taga[4];
110   d = h->dp - 16;
111   taga[0] = tag >> 24;
112   taga[1] = tag >> 16;
113   taga[2] = tag >> 8;
114   taga[3] = tag;
115   for (i = 0; i < h->cnt; i++, d -= 16)
116     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
117       break;
118   if (i >= h->cnt)
119     return 0;
120   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 6)
121     return 0;
122   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
123   return (char *)h->dp + o;
124 }
125
126 static char **
127 headstringarray(RpmHead *h, int tag, int *cnt)
128 {
129   unsigned int i, o;
130   unsigned char *d, taga[4];
131   char **r;
132
133   d = h->dp - 16;
134   taga[0] = tag >> 24;
135   taga[1] = tag >> 16;
136   taga[2] = tag >> 8;
137   taga[3] = tag;
138   for (i = 0; i < h->cnt; i++, d -= 16)
139     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
140       break;
141   if (i >= h->cnt)
142     return 0;
143   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
144     return 0;
145   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
146   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
147   r = calloc(i ? i : 1, sizeof(char *));
148   if (cnt)
149     *cnt = i;
150   d = h->dp + o;
151   for (o = 0; o < i; o++)
152     {
153       r[o] = (char *)d;
154       if (o + 1 < i)
155         d += strlen((char *)d) + 1;
156       if (d >= h->dp + h->dcnt)
157         {
158           free(r);
159           return 0;
160         }
161     }
162   return r;
163 }
164
165 static char *headtoevr(RpmHead *h)
166 {
167   unsigned int epoch, *epochp; 
168   char *version, *v;
169   char *release;
170   char *evr;
171   int epochcnt = 0;
172
173   version  = headstring(h, TAG_VERSION);
174   release  = headstring(h, TAG_RELEASE);
175   epochp = headint32(h, TAG_EPOCH, &epochcnt);
176   if (!version || !release)
177     {
178       fprintf(stderr, "headtoevr: bad rpm header\n");
179       exit(1);
180     }
181   for (v = version; *v >= 0 && *v <= '9'; v++)
182     ;
183   epoch = epochp && epochcnt ? *epochp : 0;
184   if (epoch || (v != version && *v == ':'))
185     {
186       char epochbuf[11];        /* 32bit decimal will fit in */
187       sprintf(epochbuf, "%u", epoch);
188       evr = malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
189       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
190     }
191   else
192     {
193       evr = malloc(strlen(version) + 1 + strlen(release) + 1);
194       sprintf(evr, "%s-%s", version, release);
195     }
196   if (epochp)
197     free(epochp);
198   return evr;
199 }
200
201 static unsigned int
202 makedeps(Pool *pool, Source *source, RpmHead *rpmhead, int tagn, int tagv, int tagf, int strong)
203 {
204   char **n, **v;
205   unsigned int *f;
206   int i, cc, nc, vc, fc;
207   int haspre = 0;
208   unsigned int olddeps;
209   Id *ida;
210
211   n = headstringarray(rpmhead, tagn, &nc);
212   if (!n)
213     return 0;
214   v = headstringarray(rpmhead, tagv, &vc);
215   if (!v)
216     {
217       free(n);
218       return 0;
219     }
220   f = headint32(rpmhead, tagf, &fc);
221   if (!f)
222     {
223       free(n);
224       free(v);
225       return 0;
226     }
227   if (nc != vc || nc != fc)
228     {
229       fprintf(stderr, "bad dependency entries\n");
230       exit(1);
231     }
232
233   cc = nc;
234   if (strong)
235     {
236       cc = 0;
237       for (i = 0; i < nc; i++)
238         if ((f[i] & DEP_STRONG) == (strong == 1 ? 0 : DEP_STRONG))
239           {
240             cc++;
241             if ((f[i] & DEP_PRE) != 0)
242               haspre = 1;
243           }
244     }
245   else
246     {
247       for (i = 0; i < nc; i++)
248         if ((f[i] & DEP_PRE) != 0)
249           {
250             haspre = 1;
251             break;
252           }
253     }
254   if (tagn != TAG_REQUIRENAME)
255      haspre = 0;
256   if (cc == 0)
257     {
258       free(n);
259       free(v);
260       free(f);
261       return 0;
262     }
263   cc += haspre;
264   olddeps = source_reserve_ids(source, 0, cc);
265   ida = source->idarraydata + olddeps;
266   for (i = 0; ; i++)
267     {
268       if (i == nc)
269         {
270           if (haspre != 1)
271             break;
272           haspre = 2;
273           i = 0;
274           *ida++ = SOLVABLE_PREREQMARKER;
275         }
276       if (strong && (f[i] & DEP_STRONG) != (strong == 1 ? 0 : DEP_STRONG))
277         continue;
278       if (haspre == 1 && (f[i] & DEP_PRE) != 0)
279         continue;
280       if (haspre == 2 && (f[i] & DEP_PRE) == 0)
281         continue;
282       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
283         {
284           Id name, evr;
285           int flags = 0;
286           if ((f[i] & DEP_LESS) != 0)
287             flags |= 4;
288           if ((f[i] & DEP_EQUAL) != 0)
289             flags |= 2;
290           if ((f[i] & DEP_GREATER) != 0)
291             flags |= 1;
292           name = str2id(pool, n[i], 1);
293           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
294             evr = str2id(pool, v[i] + 2, 1);
295           else
296             evr = str2id(pool, v[i], 1);
297           *ida++ = rel2id(pool, name, evr, flags, 1);
298         }
299       else
300         *ida++ = str2id(pool, n[i], 1);
301     }
302   *ida++ = 0;
303   source->idarraysize += cc + 1;
304   free(n);
305   free(v);
306   free(f);
307   return olddeps;
308 }
309
310 static unsigned int
311 copydeps(Pool *pool, Source *source, Id *from, Pool *frompool)
312 {
313   int cc;
314   Id id, *ida;
315   unsigned int olddeps;
316
317   if (!from)
318     return 0;
319   for (ida = from, cc = 0; *ida; ida++, cc++)
320     ;
321   if (cc == 0)
322     return 0;
323   olddeps = source_reserve_ids(source, 0, cc);
324   ida = source->idarraydata + olddeps;
325   if (frompool && pool != frompool)
326     {
327       while (*from)
328         {
329           id = *from++;
330           if (ISRELDEP(id))
331             {
332               Reldep *rd = GETRELDEP(frompool, id);
333               Id name = str2id(pool, id2str(frompool, rd->name), 1);
334               Id evr = str2id(pool, id2str(frompool, rd->evr), 1);
335               id = rel2id(pool, name, evr, rd->flags, 1);
336             }
337           else
338             id = str2id(pool, id2str(frompool, id), 1);
339           *ida++ = id;
340         }
341       *ida = 0;
342     }
343   else
344     memcpy(ida, from, (cc + 1) * sizeof(Id));
345   source->idarraysize += cc + 1;
346   return olddeps;
347 }
348
349
350 #define FILEFILTER_EXACT    0
351 #define FILEFILTER_STARTS   1
352 #define FILEFILTER_CONTAINS 2
353
354 struct filefilter {
355   int dirmatch;
356   char *dir;
357   char *base;
358 };
359
360 static struct filefilter filefilters[] = {
361   { FILEFILTER_CONTAINS, "/bin/", 0},
362   { FILEFILTER_CONTAINS, "/sbin/", 0},
363   { FILEFILTER_CONTAINS, "/lib/", 0},
364   { FILEFILTER_CONTAINS, "/lib64/", 0},
365   { FILEFILTER_CONTAINS, "/etc/", 0},
366   { FILEFILTER_STARTS, "/usr/games/", 0},
367   { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
368   { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
369   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
370 };
371
372 /* assumes last processed array is provides! */
373 static unsigned int
374 addfileprovides(Pool *pool, Source *source, RpmHead *rpmhead, unsigned int olddeps)
375 {
376   char **bn;
377   char **dn;
378   unsigned int *di;
379   int bnc, dnc, dic;
380   int i, j;
381   struct filefilter *ff;
382   char *fn = 0;
383   int fna = 0;
384
385   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
386   if (!bn)
387     return olddeps;
388   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
389   if (!dn)
390     {
391       free(bn);
392       return olddeps;
393     }
394   di = headint32(rpmhead, TAG_DIRINDEXES, &dic);
395   if (!di)
396     {
397       free(bn);
398       free(dn);
399       return olddeps;
400     }
401   if (bnc != dic)
402     {
403       fprintf(stderr, "bad filelist\n");
404       exit(1);
405     }
406   for (i = 0; i < bnc; i++)
407     {
408       ff = filefilters;
409       for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
410         {
411           if (ff->dir)
412             {
413               switch (ff->dirmatch)
414                 {
415                 case FILEFILTER_STARTS:
416                   if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
417                     continue;
418                   break;
419                 case FILEFILTER_CONTAINS:
420                   if (!strstr(dn[di[i]], ff->dir))
421                     continue;
422                   break;
423                 case FILEFILTER_EXACT:
424                 default:
425                   if (strcmp(dn[di[i]], ff->dir))
426                     continue;
427                   break;
428                 }
429             }
430           if (ff->base)
431             {
432               if (strcmp(bn[i], ff->base))
433                 continue;
434             }
435           break;
436         }
437       if (j == sizeof(filefilters)/sizeof(*filefilters))
438         continue;
439       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
440       if (j > fna)
441         {
442           if (fn)
443             fn = realloc(fn, j + 256);
444           else
445             fn = malloc(j + 256);
446           fna = j + 256;
447         }
448       strcpy(fn, dn[di[i]]);
449       strcat(fn, bn[i]);
450       olddeps = source_addid(source, olddeps, str2id(pool, fn, 1));
451     }
452   if (fn)
453     free(fn);
454   free(bn);
455   free(dn);
456   free(di);
457   return olddeps;
458 }
459
460 struct deps {
461   unsigned int provides;
462   unsigned int requires;
463   unsigned int obsoletes;
464   unsigned int conflicts;
465   unsigned int recommends;
466   unsigned int supplements;
467   unsigned int enhances;
468   unsigned int suggests;
469   unsigned int freshens;
470 };
471
472 static int
473 rpm2solv(Pool *pool, Source *source, Solvable *s, struct deps *deps, RpmHead *rpmhead)
474 {
475   char *name;
476   char *evr;
477
478   name = headstring(rpmhead, TAG_NAME);
479   if (!strcmp(name, "gpg-pubkey"))
480     return 0;
481   s->name = str2id(pool, name, 1);
482   if (!s->name)
483     {
484       fprintf(stderr, "package has no name\n");
485       exit(1);
486     }
487   s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
488   if (!s->arch)
489     {
490       fprintf(stderr, "package %s has no arch\n", id2str(pool, s->name));
491       exit(1);
492     }
493   evr = headtoevr(rpmhead);
494   s->evr = str2id(pool, evr, 1);
495   free(evr);
496
497   deps->provides = makedeps(pool, source, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
498   deps->provides = addfileprovides(pool, source, rpmhead, deps->provides);
499   deps->provides = source_addid_dep(source, deps->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
500   deps->requires = makedeps(pool, source, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
501   deps->conflicts = makedeps(pool, source, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
502   deps->obsoletes = makedeps(pool, source, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
503
504   deps->recommends = makedeps(pool, source, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 2);
505   deps->suggests = makedeps(pool, source, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 1);
506   deps->supplements = makedeps(pool, source, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 2);
507   deps->enhances  = makedeps(pool, source, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
508   deps->freshens = 0;
509   deps->supplements = source_fix_legacy(source, deps->provides, deps->supplements);
510   return 1;
511 }
512
513
514 /*
515  * read rpm db as source
516  * 
517  */
518
519 Source *
520 pool_addsource_rpmdb(Pool *pool, Source *ref)
521 {
522   unsigned char buf[16];
523   DB *db = 0;
524   DBC *dbc = 0;
525   int byteswapped;
526   unsigned int dbid;
527   unsigned char *dp, *dbidp;
528   int dl, nrpmids;
529   struct rpmid *rpmids, *rp;
530   int i;
531   int rpmheadsize;
532   RpmHead *rpmhead;
533   Source *source;
534   Solvable *s;
535   struct deps *deps;
536   Id id, *refhash;
537   unsigned int refmask, h;
538   int asolv;
539
540   source = pool_addsource_empty(pool);
541
542   if (ref && !(ref->nsolvables && ref->rpmdbid))
543     ref = 0;
544
545   if (db_create(&db, 0, 0))
546     {
547       perror("db_create");
548       exit(1);
549     }
550
551   if (!ref)
552     {
553       if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
554         {
555           perror("db->open /var/lib/rpm/Packages");
556           exit(1);
557         }
558       if (db->get_byteswapped(db, &byteswapped))
559         {
560           perror("db->get_byteswapped");
561           exit(1);
562         }
563       if (db->cursor(db, NULL, &dbc, 0))
564         {
565           perror("db->cursor");
566           exit(1);
567         }
568       dbidp = (unsigned char *)&dbid;
569       pool->solvables = realloc(pool->solvables, (pool->nsolvables + 256) * sizeof(Solvable));
570       memset(pool->solvables + source->start, 0, 256 * sizeof(Solvable));
571       source->rpmdbid = calloc(256, sizeof(unsigned int));
572       deps = calloc(256, sizeof(*deps));
573       asolv = 256;
574       rpmheadsize = 0;
575       rpmhead = 0;
576       i = 0;
577       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
578         {
579           if (i >= asolv)
580             {
581               pool->solvables = realloc(pool->solvables, (pool->nsolvables + asolv + 256) * sizeof(Solvable));
582               memset(pool->solvables + source->start + asolv, 0, 256 * sizeof(Solvable));
583               source->rpmdbid = realloc(source->rpmdbid, (asolv + 256) * sizeof(unsigned int));
584               memset(source->rpmdbid + asolv, 0, 256 * sizeof(unsigned int));
585               deps = realloc(deps, (asolv + 256) * sizeof(*deps));
586               memset(deps + asolv, 0, 256 * sizeof(*deps));
587               asolv += 256;
588             }
589           if (key.size != 4)
590             {
591               fprintf(stderr, "corrupt Packages database (key size)\n");
592               exit(1);
593             }
594           dp = key.data;
595           if (byteswapped)
596             {
597               dbidp[0] = dp[3];
598               dbidp[1] = dp[2];
599               dbidp[2] = dp[1];
600               dbidp[3] = dp[0];
601             }
602           else
603             memcpy(dbidp, dp, 4);
604           if (dbid == 0)                /* the join key */
605             continue;
606           if (data.size < 8)
607             {
608               fprintf(stderr, "corrupt rpm database (size %u)\n", data.size);
609               exit(1);
610             }
611           if (!rpmhead)
612             rpmhead = malloc(sizeof(*rpmhead) + data.size);
613           else if (data.size > rpmheadsize)
614             rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
615           memcpy(buf, data.data, 8);
616           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
617           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
618           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
619             {
620               fprintf(stderr, "corrupt rpm database (data size)\n");
621               exit(1);
622             }
623           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
624           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
625           source->rpmdbid[i] = dbid;
626           if (rpm2solv(pool, source, pool->solvables + source->start + i, deps + i, rpmhead))
627             i++;
628         }
629       nrpmids = i;
630       dbc->c_close(dbc);
631       db->close(db, 0);
632       db = 0;
633     }
634   else
635     {
636       if (db->open(db, 0, "/var/lib/rpm/Name", 0, DB_HASH, DB_RDONLY, 0664))
637         {
638           perror("db->open /var/lib/rpm/Name");
639           exit(1);
640         }
641       if (db->get_byteswapped(db, &byteswapped))
642         {
643           perror("db->get_byteswapped");
644           exit(1);
645         }
646       if (db->cursor(db, NULL, &dbc, 0))
647         {
648           perror("db->cursor");
649           exit(1);
650         }
651       dbidp = (unsigned char *)&dbid;
652       nrpmids = 0;
653       rpmids = 0;
654       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
655         {
656           if (key.size == 10 && !memcmp(key.data, "gpg-pubkey", 10))
657             continue;
658           dl = data.size;
659           dp = data.data;
660           while(dl >= 8)
661             {
662               if (byteswapped)
663                 {
664                   dbidp[0] = dp[3];
665                   dbidp[1] = dp[2];
666                   dbidp[2] = dp[1];
667                   dbidp[3] = dp[0];
668                 }
669               else
670                 memcpy(dbidp, dp, 4);
671               if ((nrpmids & 255) == 0)
672                 {
673                   if (rpmids)
674                     rpmids = realloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
675                   else
676                     rpmids = malloc(sizeof(*rpmids) * 256);
677                 }
678               rpmids[nrpmids].dbid = dbid;
679               rpmids[nrpmids].name = malloc((int)key.size + 1);
680               memcpy(rpmids[nrpmids].name, key.data, (int)key.size);
681               rpmids[nrpmids].name[(int)key.size] = 0;
682               nrpmids++;
683               dp += 8;
684               dl -= 8;
685             }
686           
687         }
688       dbc->c_close(dbc);
689       db->close(db, 0);
690       db = 0;
691
692       rp = rpmids;
693       dbidp = (unsigned char *)&dbid;
694       rpmheadsize = 0;
695       rpmhead = 0;
696
697       pool->solvables = realloc(pool->solvables, (pool->nsolvables + nrpmids) * sizeof(Solvable));
698       memset(pool->solvables + source->start, 0, nrpmids * sizeof(Solvable));
699       source->rpmdbid = calloc(nrpmids, sizeof(unsigned int));
700       deps = calloc(nrpmids, sizeof(*deps));
701
702       refhash = 0;
703       refmask = 0;
704       if (ref)
705         {
706           refmask = mkmask(ref->nsolvables);
707           refhash = calloc(refmask + 1, sizeof(Id));
708           for (i = 0; i < ref->nsolvables; i++)
709             {
710               h = ref->rpmdbid[i] & refmask;
711               while (refhash[h])
712                 h = (h + 317) & refmask;
713               refhash[h] = i + 1;       /* make it non-zero */
714             }
715         }
716       s = pool->solvables + source->start;
717       for (i = 0; i < nrpmids; i++, rp++, s++)
718         {
719           dbid = rp->dbid;
720           source->rpmdbid[i] = dbid;
721           if (refhash)
722             {
723               h = dbid & refmask;
724               while ((id = refhash[h]))
725                 {
726                   if (ref->rpmdbid[id - 1] == dbid)
727                     break;
728                   h = (h + 317) & refmask;
729                 }
730               if (id)
731                 {
732                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
733                   if (pool == ref->pool)
734                     {
735                       s->name = r->name;
736                       s->evr = r->evr;
737                       s->arch = r->arch;
738                     }
739                   else
740                     {
741                       if (r->name)
742                         s->name = str2id(pool, id2str(ref->pool, r->name), 1);
743                       if (r->evr)
744                         s->evr = str2id(pool, id2str(ref->pool, r->evr), 1);
745                       if (r->arch)
746                         s->arch = str2id(pool, id2str(ref->pool, r->arch), 1);
747                     }
748                   deps[i].provides = copydeps(pool, source, r->provides, ref->pool);
749                   deps[i].requires = copydeps(pool, source, r->requires, ref->pool);
750                   deps[i].conflicts = copydeps(pool, source, r->conflicts, ref->pool);
751                   deps[i].obsoletes = copydeps(pool, source, r->obsoletes, ref->pool);
752                   deps[i].recommends = copydeps(pool, source, r->recommends, ref->pool);
753                   deps[i].suggests = copydeps(pool, source, r->suggests, ref->pool);
754                   deps[i].supplements = copydeps(pool, source, r->supplements, ref->pool);
755                   deps[i].enhances  = copydeps(pool, source, r->enhances, ref->pool);
756                   deps[i].freshens = copydeps(pool, source, r->freshens, ref->pool);
757                   continue;
758                 }
759             }
760           if (!db)
761             {
762               if (db_create(&db, 0, 0))
763                 {
764                   perror("db_create");
765                   exit(1);
766                 }
767               if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
768                 {
769                   perror("db->open /var/lib/rpm/Packages");
770                   exit(1);
771                 }
772               if (db->get_byteswapped(db, &byteswapped))
773                 {
774                   perror("db->get_byteswapped");
775                   exit(1);
776                 }
777             }
778           if (byteswapped)
779             {
780               buf[0] = dbidp[3];
781               buf[1] = dbidp[2];
782               buf[2] = dbidp[1];
783               buf[3] = dbidp[0];
784             }
785           else
786             memcpy(buf, dbidp, 4);
787           key.data = buf;
788           key.size = 4;
789           data.data = 0;
790           data.size = 0;
791           if (db->get(db, NULL, &key, &data, 0))
792             {
793               perror("db->get");
794               fprintf(stderr, "corrupt rpm database\n");
795               exit(1);
796             }
797           if (data.size < 8)
798             {
799               fprintf(stderr, "corrupt rpm database (size)\n");
800               exit(1);
801             }
802           if (!rpmhead)
803             rpmhead = malloc(sizeof(*rpmhead) + data.size);
804           else if (data.size > rpmheadsize)
805             rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
806           memcpy(buf, data.data, 8);
807           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
808           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
809           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
810             {
811               fprintf(stderr, "corrupt rpm database (data size)\n");
812               exit(1);
813             }
814           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
815           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
816
817           rpm2solv(pool, source, s, deps + i, rpmhead);
818         }
819
820       if (refhash)
821         free(refhash);
822       if (rpmids)
823         {
824           for (i = 0; i < nrpmids; i++)
825             free(rpmids[i].name);
826           free(rpmids);
827         }
828     }
829   if (rpmhead)
830     free(rpmhead);
831   pool->nsolvables += nrpmids;
832   source->nsolvables = nrpmids;
833
834   // copy solvable data to pool
835   s = pool->solvables + source->start;
836   for (i = 0; i < nrpmids; i++, s++)
837     {
838       if (deps[i].provides)
839         s->provides = source->idarraydata + deps[i].provides;
840       if (deps[i].requires)
841         s->requires = source->idarraydata + deps[i].requires;
842       if (deps[i].conflicts)
843         s->conflicts = source->idarraydata + deps[i].conflicts;
844       if (deps[i].obsoletes)
845         s->obsoletes = source->idarraydata + deps[i].obsoletes;
846       if (deps[i].recommends)
847         s->recommends = source->idarraydata + deps[i].recommends;
848       if (deps[i].supplements)
849         s->supplements = source->idarraydata + deps[i].supplements;
850       if (deps[i].suggests)
851         s->suggests = source->idarraydata + deps[i].suggests;
852       if (deps[i].enhances)
853         s->enhances = source->idarraydata + deps[i].enhances;
854       if (deps[i].freshens)
855         s->freshens = source->idarraydata + deps[i].freshens;
856     }
857   free(deps);
858   if (db)
859     db->close(db, 0);
860   return source;
861 }