- don't abort on unknown elements
[platform/upstream/libsolv.git] / tools / repo_rpmdb.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_rpmdb
10  * 
11  * convert rpm db to repo
12  * 
13  */
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <assert.h>
24
25 #include <rpm/db.h>
26
27 #include "pool.h"
28 #include "repo.h"
29 #include "hash.h"
30 #include "util.h"
31 #include "repo_rpmdb.h"
32
33 #define RPMDB_COOKIE_VERSION 2
34
35 #define TAG_NAME                1000
36 #define TAG_VERSION             1001
37 #define TAG_RELEASE             1002
38 #define TAG_EPOCH               1003
39 #define TAG_SUMMARY             1004
40 #define TAG_DESCRIPTION         1005
41 #define TAG_BUILDTIME           1006
42 #define TAG_BUILDHOST           1007
43 #define TAG_INSTALLTIME         1008
44 #define TAG_SIZE                1009
45 #define TAG_DISTRIBUTION        1010
46 #define TAG_VENDOR              1011
47 #define TAG_LICENSE             1014
48 #define TAG_PACKAGER            1015
49 #define TAG_GROUP               1016
50 #define TAG_URL                 1020
51 #define TAG_ARCH                1022
52 #define TAG_FILESIZES           1028
53 #define TAG_FILEMODES           1030
54 #define TAG_SOURCERPM           1044
55 #define TAG_PROVIDENAME         1047
56 #define TAG_REQUIREFLAGS        1048
57 #define TAG_REQUIRENAME         1049
58 #define TAG_REQUIREVERSION      1050
59 #define TAG_NOSOURCE            1051
60 #define TAG_NOPATCH             1052
61 #define TAG_CONFLICTFLAGS       1053
62 #define TAG_CONFLICTNAME        1054
63 #define TAG_CONFLICTVERSION     1055
64 #define TAG_OBSOLETENAME        1090
65 #define TAG_FILEDEVICES         1095
66 #define TAG_FILEINODES          1096
67 #define TAG_PROVIDEFLAGS        1112
68 #define TAG_PROVIDEVERSION      1113
69 #define TAG_OBSOLETEFLAGS       1114
70 #define TAG_OBSOLETEVERSION     1115
71 #define TAG_DIRINDEXES          1116
72 #define TAG_BASENAMES           1117
73 #define TAG_DIRNAMES            1118
74 #define TAG_PAYLOADFORMAT       1124
75 #define TAG_PATCHESNAME         1133
76 #define TAG_FILECOLORS          1140
77 #define TAG_SUGGESTSNAME        1156
78 #define TAG_SUGGESTSVERSION     1157
79 #define TAG_SUGGESTSFLAGS       1158
80 #define TAG_ENHANCESNAME        1159
81 #define TAG_ENHANCESVERSION     1160
82 #define TAG_ENHANCESFLAGS       1161
83
84 #define DEP_LESS                (1 << 1)
85 #define DEP_GREATER             (1 << 2)
86 #define DEP_EQUAL               (1 << 3)
87 #define DEP_STRONG              (1 << 27)
88 #define DEP_PRE                 ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
89
90
91 struct rpmid {
92   unsigned int dbid;
93   char *name;
94 };
95
96 typedef struct rpmhead {
97   int cnt;
98   int dcnt;
99   unsigned char *dp;
100   unsigned char data[1];
101 } RpmHead;
102
103 static int
104 headexists(RpmHead *h, int tag)
105 {
106   unsigned int i;
107   unsigned char *d, taga[4];
108
109   d = h->dp - 16;
110   taga[0] = tag >> 24;
111   taga[1] = tag >> 16;
112   taga[2] = tag >> 8;
113   taga[3] = tag;
114   for (i = 0; i < h->cnt; i++, d -= 16)
115     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
116       return 1;
117   return 0;
118 }
119
120 static unsigned int *
121 headint32array(RpmHead *h, int tag, int *cnt)
122 {
123   unsigned int i, o, *r;
124   unsigned char *d, taga[4];
125
126   d = h->dp - 16;
127   taga[0] = tag >> 24;
128   taga[1] = tag >> 16;
129   taga[2] = tag >> 8;
130   taga[3] = tag;
131   for (i = 0; i < h->cnt; i++, d -= 16)
132     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
133       break;
134   if (i >= h->cnt)
135     return 0;
136   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
137     return 0;
138   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
139   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
140   if (o + 4 * i > h->dcnt)
141     return 0;
142   d = h->dp + o;
143   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
144   if (cnt)
145     *cnt = i;
146   for (o = 0; o < i; o++, d += 4)
147     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
148   return r;
149 }
150
151 static unsigned int
152 headint32(RpmHead *h, int tag)
153 {
154   unsigned int i, o;
155   unsigned char *d, taga[4];
156
157   d = h->dp - 16; 
158   taga[0] = tag >> 24; 
159   taga[1] = tag >> 16; 
160   taga[2] = tag >> 8;
161   taga[3] = tag;
162   for (i = 0; i < h->cnt; i++, d -= 16) 
163     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
164       break;
165   if (i >= h->cnt)
166     return 0;
167   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
168     return 0;
169   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
170   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
171   if (i == 0 || o + 4 * i > h->dcnt)
172     return 0;
173   d = h->dp + o;
174   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
175 }
176
177 static unsigned int *
178 headint16array(RpmHead *h, int tag, int *cnt)
179 {
180   unsigned int i, o, *r;
181   unsigned char *d, taga[4];
182
183   d = h->dp - 16;
184   taga[0] = tag >> 24;
185   taga[1] = tag >> 16;
186   taga[2] = tag >> 8;
187   taga[3] = tag;
188   for (i = 0; i < h->cnt; i++, d -= 16)
189     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
190       break;
191   if (i >= h->cnt)
192     return 0;
193   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
194     return 0;
195   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
196   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
197   if (o + 4 * i > h->dcnt)
198     return 0;
199   d = h->dp + o;
200   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
201   if (cnt)
202     *cnt = i;
203   for (o = 0; o < i; o++, d += 2)
204     r[o] = d[0] << 8 | d[1];
205   return r;
206 }
207
208 static char *
209 headstring(RpmHead *h, int tag)
210 {
211   unsigned int i, o;
212   unsigned char *d, taga[4];
213   d = h->dp - 16;
214   taga[0] = tag >> 24;
215   taga[1] = tag >> 16;
216   taga[2] = tag >> 8;
217   taga[3] = tag;
218   for (i = 0; i < h->cnt; i++, d -= 16)
219     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
220       break;
221   if (i >= h->cnt)
222     return 0;
223   /* 6: STRING, 9: I18NSTRING */
224   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
225     return 0;
226   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
227   return (char *)h->dp + o;
228 }
229
230 static char **
231 headstringarray(RpmHead *h, int tag, int *cnt)
232 {
233   unsigned int i, o;
234   unsigned char *d, taga[4];
235   char **r;
236
237   d = h->dp - 16;
238   taga[0] = tag >> 24;
239   taga[1] = tag >> 16;
240   taga[2] = tag >> 8;
241   taga[3] = tag;
242   for (i = 0; i < h->cnt; i++, d -= 16)
243     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
244       break;
245   if (i >= h->cnt)
246     return 0;
247   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
248     return 0;
249   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
250   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
251   r = sat_calloc(i ? i : 1, sizeof(char *));
252   if (cnt)
253     *cnt = i;
254   d = h->dp + o;
255   for (o = 0; o < i; o++)
256     {
257       r[o] = (char *)d;
258       if (o + 1 < i)
259         d += strlen((char *)d) + 1;
260       if (d >= h->dp + h->dcnt)
261         {
262           sat_free(r);
263           return 0;
264         }
265     }
266   return r;
267 }
268
269 static char *headtoevr(RpmHead *h)
270 {
271   unsigned int epoch;
272   char *version, *v;
273   char *release;
274   char *evr;
275
276   version  = headstring(h, TAG_VERSION);
277   release  = headstring(h, TAG_RELEASE);
278   epoch = headint32(h, TAG_EPOCH);
279   if (!version || !release)
280     {
281       fprintf(stderr, "headtoevr: bad rpm header\n");
282       exit(1);
283     }
284   for (v = version; *v >= 0 && *v <= '9'; v++)
285     ;
286   if (epoch || (v != version && *v == ':'))
287     {
288       char epochbuf[11];        /* 32bit decimal will fit in */
289       sprintf(epochbuf, "%u", epoch);
290       evr = sat_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
291       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
292     }
293   else
294     {
295       evr = sat_malloc(strlen(version) + 1 + strlen(release) + 1);
296       sprintf(evr, "%s-%s", version, release);
297     }
298   return evr;
299 }
300
301
302 static void
303 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
304 {
305   const unsigned char *cp;
306   int state = 0;
307   int c;
308   char *buf = 0, *bp;
309
310   /* check if it's already utf8, code taken from screen ;-) */
311   cp = (const unsigned char *)str;
312   while ((c = *cp++) != 0)
313     {
314       if (state)
315         {
316           if ((c & 0xc0) != 0x80)
317             break; /* encoding error */
318           c = (c & 0x3f) | (state << 6);
319           if (!(state & 0x40000000))
320             {
321               /* check for overlong sequences */
322               if ((c & 0x820823e0) == 0x80000000)
323                 c = 0xfdffffff;
324               else if ((c & 0x020821f0) == 0x02000000)
325                 c = 0xfff7ffff;
326               else if ((c & 0x000820f8) == 0x00080000)
327                 c = 0xffffd000;
328               else if ((c & 0x0000207c) == 0x00002000)
329                 c = 0xffffff70;
330             }
331         }
332       else
333         {
334           /* new sequence */
335           if (c >= 0xfe)
336             c = 0xfffd;
337           else if (c >= 0xfc)
338             c = (c & 0x01) | 0xbffffffc;    /* 5 bytes to follow */
339           else if (c >= 0xf8)
340             c = (c & 0x03) | 0xbfffff00;    /* 4 */
341           else if (c >= 0xf0)
342             c = (c & 0x07) | 0xbfffc000;    /* 3 */
343           else if (c >= 0xe0)
344             c = (c & 0x0f) | 0xbff00000;    /* 2 */
345           else if (c >= 0xc2)
346             c = (c & 0x1f) | 0xfc000000;    /* 1 */
347           else if (c >= 0xc0)
348             c = 0xfdffffff;         /* overlong */
349           else if (c >= 0x80)
350             c = 0xfffd;
351         }
352       state = (c & 0x80000000) ? c : 0;
353     }
354   if (c)
355     {
356       /* not utf8, assume latin1 */
357       buf = sat_malloc(2 * strlen(str) + 1);
358       cp = (const unsigned char *)str;
359       str = buf;
360       bp = buf;
361       while ((c = *cp++) != 0)
362         {
363           if (c >= 0xc0)
364             {
365               *bp++ = 0xc3;
366               c ^= 0x80;
367             }
368           else if (c >= 0x80)
369             *bp++ = 0xc2;
370           *bp++ = c;
371         }
372       *bp++ = 0;
373     }
374   repodata_set_str(repodata, handle, tag, str);
375   if (buf)
376     sat_free(buf);
377 }
378
379 static unsigned int
380 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int strong)
381 {
382   char **n, **v;
383   unsigned int *f;
384   int i, cc, nc, vc, fc;
385   int haspre = 0;
386   unsigned int olddeps;
387   Id *ida;
388
389   n = headstringarray(rpmhead, tagn, &nc);
390   if (!n)
391     return 0;
392   v = headstringarray(rpmhead, tagv, &vc);
393   if (!v)
394     {
395       sat_free(n);
396       return 0;
397     }
398   f = headint32array(rpmhead, tagf, &fc);
399   if (!f)
400     {
401       sat_free(n);
402       free(v);
403       return 0;
404     }
405   if (nc != vc || nc != fc)
406     {
407       fprintf(stderr, "bad dependency entries\n");
408       exit(1);
409     }
410
411   cc = nc;
412   if (strong)
413     {
414       cc = 0;
415       for (i = 0; i < nc; i++)
416         if ((f[i] & DEP_STRONG) == (strong == 1 ? 0 : DEP_STRONG))
417           {
418             cc++;
419             if ((f[i] & DEP_PRE) != 0)
420               haspre = 1;
421           }
422     }
423   else
424     {
425       for (i = 0; i < nc; i++)
426         if ((f[i] & DEP_PRE) != 0)
427           {
428             haspre = 1;
429             break;
430           }
431     }
432   if (tagn != TAG_REQUIRENAME)
433      haspre = 0;
434   if (cc == 0)
435     {
436       sat_free(n);
437       sat_free(v);
438       sat_free(f);
439       return 0;
440     }
441   cc += haspre;
442   olddeps = repo_reserve_ids(repo, 0, cc);
443   ida = repo->idarraydata + olddeps;
444   for (i = 0; ; i++)
445     {
446       if (i == nc)
447         {
448           if (haspre != 1)
449             break;
450           haspre = 2;
451           i = 0;
452           *ida++ = SOLVABLE_PREREQMARKER;
453         }
454       if (strong && (f[i] & DEP_STRONG) != (strong == 1 ? 0 : DEP_STRONG))
455         continue;
456       if (haspre == 1 && (f[i] & DEP_PRE) != 0)
457         continue;
458       if (haspre == 2 && (f[i] & DEP_PRE) == 0)
459         continue;
460       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
461         {
462           Id name, evr;
463           int flags = 0;
464           if ((f[i] & DEP_LESS) != 0)
465             flags |= 4;
466           if ((f[i] & DEP_EQUAL) != 0)
467             flags |= 2;
468           if ((f[i] & DEP_GREATER) != 0)
469             flags |= 1;
470           name = str2id(pool, n[i], 1);
471           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
472             evr = str2id(pool, v[i] + 2, 1);
473           else
474             evr = str2id(pool, v[i], 1);
475           *ida++ = rel2id(pool, name, evr, flags, 1);
476         }
477       else
478         *ida++ = str2id(pool, n[i], 1);
479     }
480   *ida++ = 0;
481   repo->idarraysize += cc + 1;
482   sat_free(n);
483   sat_free(v);
484   sat_free(f);
485   return olddeps;
486 }
487
488
489 #ifdef USE_FILEFILTER
490
491 #define FILEFILTER_EXACT    0
492 #define FILEFILTER_STARTS   1
493 #define FILEFILTER_CONTAINS 2
494
495 struct filefilter {
496   int dirmatch;
497   char *dir;
498   char *base;
499 };
500
501 static struct filefilter filefilters[] = {
502   { FILEFILTER_CONTAINS, "/bin/", 0},
503   { FILEFILTER_CONTAINS, "/sbin/", 0},
504   { FILEFILTER_CONTAINS, "/lib/", 0},
505   { FILEFILTER_CONTAINS, "/lib64/", 0},
506   { FILEFILTER_CONTAINS, "/etc/", 0},
507   { FILEFILTER_STARTS, "/usr/games/", 0},
508   { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
509   { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
510   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
511 };
512
513 #endif
514
515 static void
516 adddudata(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dic)
517 {
518   Id handle, did;
519   int i, fszc;
520   unsigned int *fkb, *fn, *fsz, *fm, *fino;
521   unsigned int inotest[256], inotestok;
522
523   if (!fc)
524     return;
525   fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc);
526   if (!fsz || fc != fszc)
527     {
528       sat_free(fsz);
529       return;
530     }
531   /* stupid rpm recodrs sizes of directories, so we have to check the mode */
532   fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
533   if (!fm || fc != fszc)
534     {
535       sat_free(fsz);
536       sat_free(fm);
537       return;
538     }
539   fino = headint32array(rpmhead, TAG_FILEINODES, &fszc);
540   if (!fino || fc != fszc)
541     {
542       sat_free(fsz);
543       sat_free(fm);
544       sat_free(fino);
545       return;
546     }
547   inotestok = 0;
548   if (fc < sizeof(inotest))
549     {
550       memset(inotest, 0, sizeof(inotest));
551       for (i = 0; i < fc; i++)
552         {
553           int off, bit;
554           if (fsz[i] == 0 || !S_ISREG(fm[i]))
555             continue;
556           off = (fino[i] >> 5) & (sizeof(inotest)/sizeof(*inotest) - 1);
557           bit = 1 << (fino[i] & 31);
558           if ((inotest[off] & bit) != 0)
559             break;
560           inotest[off] |= bit;
561         }
562       if (i == fc)
563         inotestok = 1;
564     }
565   if (!inotestok)
566     {
567       unsigned int *fdev = headint32array(rpmhead, TAG_FILEDEVICES, &fszc);
568       unsigned int *fx, j;
569       unsigned int mask, hash, hh;
570       if (!fdev || fc != fszc)
571         {
572           sat_free(fsz);
573           sat_free(fm);
574           sat_free(fdev);
575           sat_free(fino);
576           return;
577         }
578       mask = fc;
579       while ((mask & (mask - 1)) != 0)
580         mask = mask & (mask - 1);
581       mask <<= 2;
582       if (mask > sizeof(inotest)/sizeof(*inotest))
583         fx = sat_calloc(mask, sizeof(unsigned int));
584       else
585         {
586           fx = inotest;
587           memset(fx, 0, mask * sizeof(unsigned int));
588         }
589       mask--;
590       for (i = 0; i < fc; i++)
591         {
592           if (fsz[i] == 0 || !S_ISREG(fm[i]))
593             continue;
594           hash = (fino[i] + fdev[i] * 31) & mask;
595           hh = 7;
596           while ((j = fx[hash]) != 0)
597             {
598               if (fino[j - 1] == fino[i] && fdev[j - 1] == fdev[i])
599                 {
600                   fsz[i] = 0;   /* kill entry */
601                   break;
602                 }
603               hash = (hash + hh++) & mask;
604             }
605           if (!j)
606             fx[hash] = i + 1;
607         }
608       if (fx != inotest)
609         sat_free(fx);
610       sat_free(fdev);
611     }
612   sat_free(fino);
613   fn = sat_calloc(dic, sizeof(unsigned int));
614   fkb = sat_calloc(dic, sizeof(unsigned int));
615   for (i = 0; i < fc; i++)
616     {
617       if (fsz[i] == 0 || !S_ISREG(fm[i]))
618         continue;
619       if (di[i] >= dic)
620         continue;
621       fn[di[i]]++;
622       fkb[di[i]] += fsz[i] / 1024 + 1;
623     }
624   sat_free(fsz);
625   sat_free(fm);
626   /* commit */
627   handle = s - pool->solvables;
628   for (i = 0; i < fc; i++)
629     {
630       if (!fn[i])
631         continue;
632       if (!*dn[i] && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
633         did = repodata_str2dir(data, "/usr/src", 1);
634       else
635         did = repodata_str2dir(data, dn[i], 1);
636       repodata_add_dirnumnum(data, handle, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
637     }
638   sat_free(fn);
639   sat_free(fkb);
640 }
641
642 /* assumes last processed array is provides! */
643 static unsigned int
644 addfileprovides(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, unsigned int olddeps)
645 {
646   char **bn;
647   char **dn;
648   unsigned int *di;
649   int bnc, dnc, dic;
650   int i;
651 #ifdef USE_FILEFILTER
652   int j;
653   struct filefilter *ff;
654 #endif
655 #if 0
656   char *fn = 0;
657   int fna = 0;
658 #endif
659
660   if (!data)
661     return olddeps;
662   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
663   if (!bn)
664     return olddeps;
665   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
666   if (!dn)
667     {
668       sat_free(bn);
669       return olddeps;
670     }
671   di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
672   if (!di)
673     {
674       sat_free(bn);
675       sat_free(dn);
676       return olddeps;
677     }
678   if (bnc != dic)
679     {
680       fprintf(stderr, "bad filelist\n");
681       exit(1);
682     }
683
684   if (data)
685     adddudata(pool, repo, data, s, rpmhead, dn, di, bnc, dic);
686
687   for (i = 0; i < bnc; i++)
688     {
689 #ifdef USE_FILEFILTER
690       ff = filefilters;
691       for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
692         {
693           if (ff->dir)
694             {
695               switch (ff->dirmatch)
696                 {
697                 case FILEFILTER_STARTS:
698                   if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
699                     continue;
700                   break;
701                 case FILEFILTER_CONTAINS:
702                   if (!strstr(dn[di[i]], ff->dir))
703                     continue;
704                   break;
705                 case FILEFILTER_EXACT:
706                 default:
707                   if (strcmp(dn[di[i]], ff->dir))
708                     continue;
709                   break;
710                 }
711             }
712           if (ff->base)
713             {
714               if (strcmp(bn[i], ff->base))
715                 continue;
716             }
717           break;
718         }
719       if (j == sizeof(filefilters)/sizeof(*filefilters))
720         continue;
721 #endif
722 #if 0
723       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
724       if (j > fna)
725         {
726           fna = j + 256;
727           fn = sat_realloc(fn, fna);
728         }
729       strcpy(fn, dn[di[i]]);
730       strcat(fn, bn[i]);
731       olddeps = repo_addid_dep(repo, olddeps, str2id(pool, fn, 1), SOLVABLE_FILEMARKER);
732 #endif
733       if (data)
734         {
735           Id handle, did;
736           handle = s - pool->solvables;
737           did = repodata_str2dir(data, dn[di[i]], 1);
738           if (!did)
739             did = repodata_str2dir(data, "/", 1);
740           repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, bn[i]);
741         }
742     }
743 #if 0
744   if (fn)
745     sat_free(fn);
746 #endif
747   sat_free(bn);
748   sat_free(dn);
749   sat_free(di);
750   return olddeps;
751 }
752
753 static void
754 addsourcerpm(Pool *pool, Repodata *data, Id handle, char *sourcerpm, char *name, char *evr)
755 {
756   const char *p, *sevr, *sarch;
757
758   p = strrchr(sourcerpm, '.');
759   if (!p || strcmp(p, ".rpm") != 0)
760     return;
761   p--;
762   while (p > sourcerpm && *p != '.')
763     p--;
764   if (*p != '.' || p == sourcerpm)
765     return;
766   sarch = p-- + 1;
767   while (p > sourcerpm && *p != '-')
768     p--;
769   if (*p != '-' || p == sourcerpm)
770     return;
771   p--;
772   while (p > sourcerpm && *p != '-')
773     p--;
774   if (*p != '-' || p == sourcerpm)
775     return;
776   sevr = p + 1;
777   if (!strcmp(sarch, "src.rpm"))
778     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_SRC);
779   else if (!strcmp(sarch, "nosrc.rpm"))
780     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
781   else
782     repodata_set_constantid(data, handle, SOLVABLE_SOURCEARCH, strn2id(pool, sarch, strlen(sarch) - 4, 1));
783   if (!strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
784     repodata_set_void(data, handle, SOLVABLE_SOURCEEVR);
785   else
786     repodata_set_id(data, handle, SOLVABLE_SOURCEEVR, strn2id(pool, sevr, sarch - sevr - 1, 1));
787   if (!strncmp(sourcerpm, name, sevr - sourcerpm - 1) && name[sevr - sourcerpm - 1] == 0)
788     repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
789   else
790     repodata_set_id(data, handle, SOLVABLE_SOURCENAME, strn2id(pool, sourcerpm, sevr - sourcerpm - 1, 1));
791 }
792
793 static int
794 rpm2solv(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead)
795 {
796   char *name;
797   char *evr;
798   char *sourcerpm;
799
800   name = headstring(rpmhead, TAG_NAME);
801   if (!strcmp(name, "gpg-pubkey"))
802     return 0;
803   s->name = str2id(pool, name, 1);
804   if (!s->name)
805     {
806       fprintf(stderr, "package has no name\n");
807       exit(1);
808     }
809   sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
810   if (sourcerpm)
811     s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
812   else
813     {
814       if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
815         s->arch = ARCH_NOSRC;
816       else
817         s->arch = ARCH_SRC;
818     }
819   if (!s->arch)
820     s->arch = ARCH_NOARCH;
821   evr = headtoevr(rpmhead);
822   s->evr = str2id(pool, evr, 1);
823   s->vendor = str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
824
825   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
826   s->provides = addfileprovides(pool, repo, data, s, rpmhead, s->provides);
827   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
828     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
829   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
830   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
831   s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
832
833   s->recommends = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 2);
834   s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 1);
835   s->supplements = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 2);
836   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
837   s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
838   s->conflicts = repo_fix_conflicts(repo, s->conflicts);
839
840   if (data)
841     {
842       Id handle;
843       char *str;
844       unsigned int u32;
845
846       handle = s - pool->solvables;
847       str = headstring(rpmhead, TAG_SUMMARY);
848       if (str)
849         setutf8string(data, handle, SOLVABLE_SUMMARY, str);
850       str = headstring(rpmhead, TAG_DESCRIPTION);
851       if (str)
852         {
853           char *aut, *p;
854           for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
855             if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
856               break;
857           if (aut)
858             {
859               /* oh my, found SUSE special author section */
860               int l = aut - str;
861               str = strdup(str);
862               aut = str + l;
863               str[l] = 0;
864               while (l > 0 && str[l - 1] == '\n')
865                 str[--l] = 0;
866               if (l)
867                 setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
868               p = aut + 19;
869               aut = str;        /* copy over */
870               while (*p == ' ' || *p == '\n')
871                 p++;
872               while (*p)
873                 {
874                   if (*p == '\n')
875                     {
876                       *aut++ = *p++;
877                       while (*p == ' ')
878                         p++;
879                       continue;
880                     }
881                   *aut++ = *p++;
882                 }
883               while (aut != str && aut[-1] == '\n')
884                 aut--;
885               *aut = 0;
886               if (*str)
887                 setutf8string(data, handle, SOLVABLE_AUTHORS, str);
888               free(str);
889             }
890           else if (*str)
891             setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
892         }
893       str = headstring(rpmhead, TAG_GROUP);
894       if (str)
895         repodata_set_poolstr(data, handle, SOLVABLE_GROUP, str);
896       str = headstring(rpmhead, TAG_LICENSE);
897       if (str)
898         repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, str);
899       str = headstring(rpmhead, TAG_URL);
900       if (str)
901         repodata_set_str(data, handle, SOLVABLE_URL, str);
902       str = headstring(rpmhead, TAG_DISTRIBUTION);
903       if (str)
904         repodata_set_poolstr(data, handle, SOLVABLE_DISTRIBUTION, str);
905       str = headstring(rpmhead, TAG_PACKAGER);
906       if (str)
907         repodata_set_poolstr(data, handle, SOLVABLE_PACKAGER, str);
908       u32 = headint32(rpmhead, TAG_BUILDTIME);
909       if (u32)
910         repodata_set_num(data, handle, SOLVABLE_BUILDTIME, u32);
911       u32 = headint32(rpmhead, TAG_INSTALLTIME);
912       if (u32)
913         repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, u32);
914       u32 = headint32(rpmhead, TAG_SIZE);
915       if (u32)
916         repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, (u32 + 1023) / 1024);
917       if (sourcerpm)
918         addsourcerpm(pool, data, handle, sourcerpm, name, evr);
919     }
920   sat_free(evr);
921   return 1;
922 }
923
924 static Id
925 copyreldep(Pool *pool, Pool *frompool, Id id)
926 {
927   Reldep *rd = GETRELDEP(frompool, id);
928   Id name = rd->name, evr = rd->evr;
929   if (ISRELDEP(name))
930     name = copyreldep(pool, frompool, name);
931   else
932     name = str2id(pool, id2str(frompool, name), 1);
933   if (ISRELDEP(evr))
934     evr = copyreldep(pool, frompool, evr);
935   else
936     evr = str2id(pool, id2str(frompool, evr), 1);
937   return rel2id(pool, name, evr, rd->flags, 1);
938 }
939
940 static Offset
941 copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
942 {
943   int cc;
944   Id id, *ida, *from;
945   Offset ido;
946   Pool *frompool = fromrepo->pool;
947
948   if (!fromoff)
949     return 0;
950   from = fromrepo->idarraydata + fromoff;
951   for (ida = from, cc = 0; *ida; ida++, cc++)
952     ;
953   if (cc == 0)
954     return 0;
955   ido = repo_reserve_ids(repo, 0, cc);
956   ida = repo->idarraydata + ido;
957   if (frompool && pool != frompool)
958     {
959       while (*from)
960         {
961           id = *from++;
962           if (ISRELDEP(id))
963             id = copyreldep(pool, frompool, id);
964           else
965             id = str2id(pool, id2str(frompool, id), 1);
966           *ida++ = id;
967         }
968       *ida = 0;
969     }
970   else
971     memcpy(ida, from, (cc + 1) * sizeof(Id));
972   repo->idarraysize += cc + 1;
973   return ido;
974 }
975
976 static Id copydir_complex(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache);
977
978 static inline Id
979 copydir(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache)
980 {
981   if (cache && cache[did & 255] == did)
982     return cache[(did & 255) + 256];
983   return copydir_complex(pool, data, fromspool, fromdata, did, cache);
984 }
985
986 static Id
987 copydir_complex(Pool *pool, Repodata *data, Stringpool *fromspool, Repodata *fromdata, Id did, Id *cache)
988 {
989   Id parent = dirpool_parent(&fromdata->dirpool, did);
990   Id compid = dirpool_compid(&fromdata->dirpool, did);
991   if (parent)
992     parent = copydir(pool, data, fromspool, fromdata, parent, cache);
993   if (fromspool != &pool->ss)
994     compid = str2id(pool, stringpool_id2str(fromspool, compid), 1);
995   compid = dirpool_add_dir(&data->dirpool, parent, compid, 1);
996   if (cache)
997     {
998       cache[did & 255] = did;
999       cache[(did & 255) + 256] = compid;
1000     }
1001   return compid;
1002 }
1003
1004 struct solvable_copy_cbdata {
1005   Repodata *data;
1006   Id handle;
1007   Id *dircache;
1008 };
1009
1010 static int
1011 solvable_copy_cb(void *vcbdata, Solvable *r, Repodata *fromdata, Repokey *key, KeyValue *kv)
1012 {
1013   struct solvable_copy_cbdata *cbdata = vcbdata;
1014   Id id, keyname;
1015   Repodata *data = cbdata->data;
1016   Id handle = cbdata->handle;
1017   Pool *pool = data->repo->pool, *frompool = fromdata->repo->pool;
1018   Stringpool *fromspool = fromdata->localpool ? &fromdata->spool : &frompool->ss;
1019
1020   keyname = key->name;
1021   if (keyname >= ID_NUM_INTERNAL)
1022     keyname = str2id(pool, id2str(frompool, keyname), 1);
1023   switch(key->type)
1024     {
1025     case REPOKEY_TYPE_ID:
1026     case REPOKEY_TYPE_CONSTANTID:
1027       id = kv->id;
1028       assert(!data->localpool); /* implement me! */
1029       if (pool != frompool || fromdata->localpool)
1030         {
1031           if (ISRELDEP(id))
1032             id = copyreldep(pool, frompool, id);
1033           else
1034             id = str2id(pool, stringpool_id2str(fromspool, id), 1);
1035         }
1036       if (key->type == REPOKEY_TYPE_ID)
1037         repodata_set_id(data, handle, keyname, id);
1038       else
1039         repodata_set_constantid(data, handle, keyname, id);
1040       break;
1041     case REPOKEY_TYPE_STR:
1042       repodata_set_str(data, handle, keyname, kv->str);
1043       break;
1044     case REPOKEY_TYPE_VOID:
1045       repodata_set_void(data, handle, keyname);
1046       break;
1047     case REPOKEY_TYPE_NUM:
1048       repodata_set_num(data, handle, keyname, kv->num);
1049       break;
1050     case REPOKEY_TYPE_CONSTANT:
1051       repodata_set_constant(data, handle, keyname, kv->num);
1052       break;
1053     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1054       id = kv->id;
1055       assert(!data->localpool); /* implement me! */
1056       id = copydir(pool, data, fromspool, fromdata, id, cbdata->dircache);
1057       repodata_add_dirnumnum(data, handle, keyname, id, kv->num, kv->num2);
1058       break;
1059     case REPOKEY_TYPE_DIRSTRARRAY:
1060       id = kv->id;
1061       assert(!data->localpool); /* implement me! */
1062       id = copydir(pool, data, fromspool, fromdata, id, cbdata->dircache);
1063       repodata_add_dirstr(data, handle, keyname, id, kv->str);
1064       break;
1065     default:
1066       break;
1067     }
1068   return 0;
1069 }
1070
1071 static void
1072 solvable_copy(Solvable *s, Solvable *r, Repodata *data, Id *dircache)
1073 {
1074   Repo *repo = s->repo;
1075   Repo *fromrepo = r->repo;
1076   Pool *pool = repo->pool;
1077   struct solvable_copy_cbdata cbdata;
1078
1079   /* copy solvable data */
1080   if (pool == fromrepo->pool)
1081     {
1082       s->name = r->name;
1083       s->evr = r->evr;
1084       s->arch = r->arch;
1085       s->vendor = r->vendor;
1086     }
1087   else
1088     {
1089       if (r->name)
1090         s->name = str2id(pool, id2str(fromrepo->pool, r->name), 1);
1091       if (r->evr)
1092         s->evr = str2id(pool, id2str(fromrepo->pool, r->evr), 1);
1093       if (r->arch)
1094         s->arch = str2id(pool, id2str(fromrepo->pool, r->arch), 1);
1095       if (r->vendor)
1096         s->vendor = str2id(pool, id2str(fromrepo->pool, r->vendor), 1);
1097     }
1098   s->provides = copydeps(pool, repo, r->provides, fromrepo);
1099   s->requires = copydeps(pool, repo, r->requires, fromrepo);
1100   s->conflicts = copydeps(pool, repo, r->conflicts, fromrepo);
1101   s->obsoletes = copydeps(pool, repo, r->obsoletes, fromrepo);
1102   s->recommends = copydeps(pool, repo, r->recommends, fromrepo);
1103   s->suggests = copydeps(pool, repo, r->suggests, fromrepo);
1104   s->supplements = copydeps(pool, repo, r->supplements, fromrepo);
1105   s->enhances  = copydeps(pool, repo, r->enhances, fromrepo);
1106
1107   /* copy all attributes */
1108   if (!data)
1109     return;
1110   cbdata.data = data;
1111   cbdata.handle = s - pool->solvables;
1112   cbdata.dircache = dircache;
1113   repo_search(fromrepo, (r - fromrepo->pool->solvables), 0, 0, SEARCH_NO_STORAGE_SOLVABLE, solvable_copy_cb, &cbdata);
1114 }
1115
1116 /* used to sort entries returned in some database order */
1117 static int
1118 rpmids_sort_cmp(const void *va, const void *vb)
1119 {
1120   struct rpmid const *a = va, *b = vb;
1121   int r;
1122   r = strcmp(a->name, b->name);
1123   if (r)
1124     return r;
1125   return a->dbid - b->dbid;
1126 }
1127
1128 static Repo *pkgids_sort_cmp_data;
1129
1130 static int
1131 pkgids_sort_cmp(const void *va, const void *vb)
1132 {
1133   Pool *pool = pkgids_sort_cmp_data->pool;
1134   Solvable *a = pool->solvables + *(Id *)va;
1135   Solvable *b = pool->solvables + *(Id *)vb;
1136   Id *rpmdbid;
1137
1138   if (a->name != b->name)
1139     return strcmp(id2str(pool, a->name), id2str(pool, b->name));
1140   rpmdbid = pkgids_sort_cmp_data->rpmdbid;
1141   return rpmdbid[(a - pool->solvables) - pkgids_sort_cmp_data->start] - rpmdbid[(b - pool->solvables) - pkgids_sort_cmp_data->start];
1142 }
1143
1144 static void
1145 swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
1146 {
1147   Pool *pool = repo->pool;
1148   Solvable tmp;
1149
1150   tmp = pool->solvables[pa];
1151   pool->solvables[pa] = pool->solvables[pb];
1152   pool->solvables[pb] = tmp;
1153   if (repo->rpmdbid)
1154     {
1155       Id tmpid = repo->rpmdbid[pa - repo->start];
1156       repo->rpmdbid[pa - repo->start] = repo->rpmdbid[pb - repo->start];
1157       repo->rpmdbid[pb - repo->start] = tmpid;
1158     }
1159   /* only works if nothing is already internalized! */
1160   if (data && data->attrs)
1161     {
1162       Id *tmpattrs = data->attrs[pa - data->start];
1163       data->attrs[pa - data->start] = data->attrs[pb - data->start];
1164       data->attrs[pb - data->start] = tmpattrs;
1165     }
1166 }
1167
1168 static void
1169 mkrpmdbcookie(struct stat *st, unsigned char *cookie)
1170 {
1171   memset(cookie, 0, 32);
1172   cookie[3] = RPMDB_COOKIE_VERSION;
1173   memcpy(cookie + 16, &st->st_ino, sizeof(st->st_ino));
1174   memcpy(cookie + 24, &st->st_dev, sizeof(st->st_dev));
1175 }
1176
1177 /*
1178  * read rpm db as repo
1179  * 
1180  */
1181
1182 void
1183 repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir, int flags)
1184 {
1185   Pool *pool = repo->pool;
1186   unsigned char buf[16];
1187   DB *db = 0;
1188   DBC *dbc = 0;
1189   int byteswapped;
1190   unsigned int dbid;
1191   unsigned char *dp, *dbidp;
1192   int dl, nrpmids;
1193   struct rpmid *rpmids, *rp;
1194   int i;
1195   int rpmheadsize;
1196   RpmHead *rpmhead;
1197   Solvable *s;
1198   Id id, *refhash;
1199   unsigned int refmask, h;
1200   char dbpath[PATH_MAX];
1201   DB_ENV *dbenv = 0;
1202   DBT dbkey;
1203   DBT dbdata;
1204   struct stat packagesstat;
1205   unsigned char newcookie[32];
1206   const unsigned char *oldcookie = 0;
1207   Id oldcookietype = 0;
1208   Repodata *data;
1209   
1210   memset(&dbkey, 0, sizeof(dbkey));
1211   memset(&dbdata, 0, sizeof(dbdata));
1212
1213   if (!rootdir)
1214     rootdir = "";
1215
1216   
1217   if (!(flags & REPO_REUSE_REPODATA))
1218     data = repo_add_repodata(repo, 0);
1219   else
1220     data = repo_last_repodata(repo);
1221   
1222   if (ref && !(ref->nsolvables && ref->rpmdbid))
1223     ref = 0;
1224
1225   if (db_env_create(&dbenv, 0))
1226     {
1227       perror("db_env_create");
1228       exit(1);
1229     }
1230   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir);
1231   if (dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0))
1232     {
1233       perror("dbenv open");
1234       exit(1);
1235     }
1236   if (db_create(&db, dbenv, 0))
1237     {
1238       perror("db_create");
1239       exit(1);
1240     }
1241
1242   /* XXX: should get ro lock of Packages database! */
1243   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
1244   if (stat(dbpath, &packagesstat))
1245     {
1246       perror(dbpath);
1247       exit(1);
1248     }
1249   mkrpmdbcookie(&packagesstat, newcookie);
1250   repodata_set_bin_checksum(data, SOLVID_META, REPOSITORY_RPMDBCOOKIE, REPOKEY_TYPE_SHA256, newcookie);
1251
1252   if (ref)
1253     oldcookie = repo_lookup_bin_checksum(ref, SOLVID_META, REPOSITORY_RPMDBCOOKIE, &oldcookietype);
1254   if (!ref || !oldcookie || oldcookietype != REPOKEY_TYPE_SHA256 || memcmp(oldcookie, newcookie, 32) != 0)
1255     {
1256       Id *pkgids;
1257       if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
1258         {
1259           perror("db->open var/lib/rpm/Packages");
1260           exit(1);
1261         }
1262       if (db->get_byteswapped(db, &byteswapped))
1263         {
1264           perror("db->get_byteswapped");
1265           exit(1);
1266         }
1267       if (db->cursor(db, NULL, &dbc, 0))
1268         {
1269           perror("db->cursor");
1270           exit(1);
1271         }
1272       dbidp = (unsigned char *)&dbid;
1273       rpmheadsize = 0;
1274       rpmhead = 0;
1275       i = 0;
1276       s = 0;
1277       while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1278         {
1279           if (!s)
1280             s = pool_id2solvable(pool, repo_add_solvable(repo));
1281           if (!repo->rpmdbid)
1282             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1283           if (dbkey.size != 4)
1284             {
1285               fprintf(stderr, "corrupt Packages database (key size)\n");
1286               exit(1);
1287             }
1288           dp = dbkey.data;
1289           if (byteswapped)
1290             {
1291               dbidp[0] = dp[3];
1292               dbidp[1] = dp[2];
1293               dbidp[2] = dp[1];
1294               dbidp[3] = dp[0];
1295             }
1296           else
1297             memcpy(dbidp, dp, 4);
1298           if (dbid == 0)                /* the join key */
1299             continue;
1300           if (dbdata.size < 8)
1301             {
1302               fprintf(stderr, "corrupt rpm database (size %u)\n", dbdata.size);
1303               exit(1);
1304             }
1305           if (dbdata.size > rpmheadsize)
1306             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + dbdata.size);
1307           memcpy(buf, dbdata.data, 8);
1308           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1309           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1310           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1311             {
1312               fprintf(stderr, "corrupt rpm database (data size)\n");
1313               exit(1);
1314             }
1315           memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1316           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1317           repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
1318           if (rpm2solv(pool, repo, data, s, rpmhead))
1319             {
1320               i++;
1321               s = 0;
1322             }
1323           else
1324             {
1325               /* We can reuse this solvable, but make sure it's still
1326                  associated with this repo.  */
1327               memset(s, 0, sizeof(*s));
1328               s->repo = repo;
1329             }
1330         }
1331       if (s)
1332         {
1333           /* oops, could not reuse. free it instead */
1334           repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
1335           s = 0;
1336         }
1337       dbc->c_close(dbc);
1338       db->close(db, 0);
1339       db = 0;
1340       /* now sort all solvables */
1341       if (repo->end - repo->start > 1)
1342         {
1343           pkgids = sat_malloc2(repo->end - repo->start, sizeof(Id));
1344           for (i = repo->start; i < repo->end; i++)
1345             pkgids[i - repo->start] = i;
1346           pkgids_sort_cmp_data = repo;
1347           qsort(pkgids, repo->end - repo->start, sizeof(Id), pkgids_sort_cmp);
1348           /* adapt order */
1349           for (i = repo->start; i < repo->end; i++)
1350             {
1351               int j = pkgids[i - repo->start];
1352               while (j < i)
1353                 j = pkgids[i - repo->start] = pkgids[j - repo->start];
1354               if (j != i)
1355                 swap_solvables(repo, data, i, j);
1356             }
1357           sat_free(pkgids);
1358         }
1359     }
1360   else
1361     {
1362       Id dircache[512];
1363
1364       memset(dircache, 0, sizeof(dircache));
1365       snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir);
1366       if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
1367         {
1368           perror("db->open var/lib/rpm/Name");
1369           exit(1);
1370         }
1371       if (db->get_byteswapped(db, &byteswapped))
1372         {
1373           perror("db->get_byteswapped");
1374           exit(1);
1375         }
1376       if (db->cursor(db, NULL, &dbc, 0))
1377         {
1378           perror("db->cursor");
1379           exit(1);
1380         }
1381       dbidp = (unsigned char *)&dbid;
1382       nrpmids = 0;
1383       rpmids = 0;
1384       while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1385         {
1386           if (dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
1387             continue;
1388           dl = dbdata.size;
1389           dp = dbdata.data;
1390           while(dl >= 8)
1391             {
1392               if (byteswapped)
1393                 {
1394                   dbidp[0] = dp[3];
1395                   dbidp[1] = dp[2];
1396                   dbidp[2] = dp[1];
1397                   dbidp[3] = dp[0];
1398                 }
1399               else
1400                 memcpy(dbidp, dp, 4);
1401               rpmids = sat_extend(rpmids, nrpmids, 1, sizeof(*rpmids), 255);
1402               rpmids[nrpmids].dbid = dbid;
1403               rpmids[nrpmids].name = sat_malloc((int)dbkey.size + 1);
1404               memcpy(rpmids[nrpmids].name, dbkey.data, (int)dbkey.size);
1405               rpmids[nrpmids].name[(int)dbkey.size] = 0;
1406               nrpmids++;
1407               dp += 8;
1408               dl -= 8;
1409             }
1410         }
1411       dbc->c_close(dbc);
1412       db->close(db, 0);
1413       db = 0;
1414
1415       /* sort rpmids */
1416       qsort(rpmids, nrpmids, sizeof(*rpmids), rpmids_sort_cmp);
1417
1418       rp = rpmids;
1419       dbidp = (unsigned char *)&dbid;
1420       rpmheadsize = 0;
1421       rpmhead = 0;
1422
1423       /* create hash from dbid to ref */
1424       refmask = mkmask(ref->nsolvables);
1425       refhash = sat_calloc(refmask + 1, sizeof(Id));
1426       for (i = 0; i < ref->end - ref->start; i++)
1427         {
1428           if (!ref->rpmdbid[i])
1429             continue;
1430           h = ref->rpmdbid[i] & refmask;
1431           while (refhash[h])
1432             h = (h + 317) & refmask;
1433           refhash[h] = i + 1;   /* make it non-zero */
1434         }
1435
1436       s = pool_id2solvable(pool, repo_add_solvable_block(repo, nrpmids));
1437       if (!repo->rpmdbid)
1438         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1439
1440       for (i = 0; i < nrpmids; i++, rp++, s++)
1441         {
1442           dbid = rp->dbid;
1443           repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
1444           if (refhash)
1445             {
1446               h = dbid & refmask;
1447               while ((id = refhash[h]))
1448                 {
1449                   if (ref->rpmdbid[id - 1] == dbid)
1450                     break;
1451                   h = (h + 317) & refmask;
1452                 }
1453               if (id)
1454                 {
1455                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
1456                   if (r->repo == ref)
1457                     {
1458                       solvable_copy(s, r, data, dircache);
1459                       continue;
1460                     }
1461                 }
1462             }
1463           if (!db)
1464             {
1465               if (db_create(&db, 0, 0))
1466                 {
1467                   perror("db_create");
1468                   exit(1);
1469                 }
1470               snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
1471               if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
1472                 {
1473                   perror("db->open var/lib/rpm/Packages");
1474                   exit(1);
1475                 }
1476               if (db->get_byteswapped(db, &byteswapped))
1477                 {
1478                   perror("db->get_byteswapped");
1479                   exit(1);
1480                 }
1481             }
1482           if (byteswapped)
1483             {
1484               buf[0] = dbidp[3];
1485               buf[1] = dbidp[2];
1486               buf[2] = dbidp[1];
1487               buf[3] = dbidp[0];
1488             }
1489           else
1490             memcpy(buf, dbidp, 4);
1491           dbkey.data = buf;
1492           dbkey.size = 4;
1493           dbdata.data = 0;
1494           dbdata.size = 0;
1495           if (db->get(db, NULL, &dbkey, &dbdata, 0))
1496             {
1497               perror("db->get");
1498               fprintf(stderr, "corrupt rpm database\n");
1499               exit(1);
1500             }
1501           if (dbdata.size < 8)
1502             {
1503               fprintf(stderr, "corrupt rpm database (size)\n");
1504               exit(1);
1505             }
1506           if (dbdata.size > rpmheadsize)
1507             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + dbdata.size);
1508           memcpy(buf, dbdata.data, 8);
1509           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1510           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1511           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1512             {
1513               fprintf(stderr, "corrupt rpm database (data size)\n");
1514               exit(1);
1515             }
1516           memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1517           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1518
1519           rpm2solv(pool, repo, data, s, rpmhead);
1520         }
1521
1522       if (refhash)
1523         sat_free(refhash);
1524       if (rpmids)
1525         {
1526           for (i = 0; i < nrpmids; i++)
1527             sat_free(rpmids[i].name);
1528           sat_free(rpmids);
1529         }
1530     }
1531   if (!(flags & REPO_NO_INTERNALIZE))
1532     repodata_internalize(data);
1533   if (rpmhead)
1534     sat_free(rpmhead);
1535   if (db)
1536     db->close(db, 0);
1537   dbenv->close(dbenv, 0);
1538 }
1539
1540
1541 static inline unsigned int
1542 getu32(unsigned char *dp)
1543 {
1544   return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
1545 }
1546
1547
1548 static void
1549 add_location(Repodata *data, Solvable *s, Id handle, const char *location)
1550 {
1551   Pool *pool = s->repo->pool;
1552   const char *name, *n1, *n2;
1553   int l;
1554
1555   /* skip ./ prefix */
1556   if (location[0] == '.' && location[1] == '/' && location[2] != '/')
1557     location += 2;
1558
1559   name = strrchr(location, '/');
1560   if (!name)
1561     name = location;
1562   else
1563     {
1564       name++;
1565       n2 = id2str(pool, s->arch);
1566       l = strlen(n2);
1567       if (strncmp(location, n2, l) != 0 || location + l + 1 != name)
1568         {
1569           /* too bad, need to store directory */
1570           char *dir = strdup(location);
1571           dir[name - location - 1] = 0;
1572           repodata_set_str(data, handle, SOLVABLE_MEDIADIR, dir);
1573           free(dir);
1574         }
1575       else
1576         repodata_set_void(data, handle, SOLVABLE_MEDIADIR);
1577     }
1578   n1 = name;
1579   for (n2 = id2str(pool, s->name); *n2; n1++, n2++)
1580     if (*n1 != *n2)
1581       break;
1582   if (*n2 || *n1 != '-')
1583     goto nontrivial;
1584   n1++;
1585   for (n2 = id2str (pool, s->evr); *n2; n1++, n2++)
1586     if (*n1 != *n2)
1587       break;
1588   if (*n2 || *n1 != '.')
1589     goto nontrivial;
1590   n1++;
1591   for (n2 = id2str (pool, s->arch); *n2; n1++, n2++)
1592     if (*n1 != *n2)
1593       break;
1594   if (*n2 || strcmp (n1, ".rpm"))
1595     goto nontrivial;
1596   repodata_set_void(data, handle, SOLVABLE_MEDIAFILE);
1597   return;
1598
1599 nontrivial:
1600   repodata_set_str(data, handle, SOLVABLE_MEDIAFILE, name);
1601   return;
1602 }
1603
1604 void
1605 repo_add_rpms(Repo *repo, const char **rpms, int nrpms, int flags)
1606 {
1607   int i, sigdsize, sigcnt, l;
1608   Pool *pool = repo->pool;
1609   Solvable *s;
1610   RpmHead *rpmhead = 0;
1611   int rpmheadsize = 0;
1612   char *payloadformat;
1613   FILE *fp;
1614   unsigned char lead[4096];
1615   int headerstart, headerend;
1616   struct stat stb;
1617   Repodata *data;
1618
1619   if (!(flags & REPO_REUSE_REPODATA))
1620     data = repo_add_repodata(repo, 0);
1621   else
1622     data = repo_last_repodata(repo);
1623
1624   if (nrpms <= 0)
1625     return;
1626
1627   for (i = 0; i < nrpms; i++)
1628     {
1629       if ((fp = fopen(rpms[i], "r")) == 0)
1630         {
1631           perror(rpms[i]);
1632           continue;
1633         }
1634       if (fstat(fileno(fp), &stb))
1635         {
1636           perror("stat");
1637           continue;
1638         }
1639       if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
1640         {
1641           fprintf(stderr, "%s: not a rpm\n", rpms[i]);
1642           fclose(fp);
1643           continue;
1644         }
1645       if (lead[78] != 0 || lead[79] != 5)
1646         {
1647           fprintf(stderr, "%s: not a V5 header\n", rpms[i]);
1648           fclose(fp);
1649           continue;
1650         }
1651       if (getu32(lead + 96) != 0x8eade801)
1652         {
1653           fprintf(stderr, "%s: bad signature header\n", rpms[i]);
1654           fclose(fp);
1655           continue;
1656         }
1657       sigcnt = getu32(lead + 96 + 8);
1658       sigdsize = getu32(lead + 96 + 12);
1659       if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
1660         {
1661           fprintf(stderr, "%s: bad signature header\n", rpms[i]);
1662           fclose(fp);
1663           continue;
1664         }
1665       sigdsize += sigcnt * 16;
1666       sigdsize = (sigdsize + 7) & ~7;
1667       headerstart = 96 + 16 + sigdsize;
1668       while (sigdsize)
1669         {
1670           l = sigdsize > 4096 ? 4096 : sigdsize;
1671           if (fread(lead, l, 1, fp) != 1)
1672             {
1673               fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1674               fclose(fp);
1675               continue;
1676             }
1677           sigdsize -= l;
1678         }
1679       if (fread(lead, 16, 1, fp) != 1)
1680         {
1681           fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1682           fclose(fp);
1683           continue;
1684         }
1685       if (getu32(lead) != 0x8eade801)
1686         {
1687           fprintf(stderr, "%s: bad header\n", rpms[i]);
1688           fclose(fp);
1689           continue;
1690         }
1691       sigcnt = getu32(lead + 8);
1692       sigdsize = getu32(lead + 12);
1693       if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
1694         {
1695           fprintf(stderr, "%s: bad header\n", rpms[i]);
1696           fclose(fp);
1697           continue;
1698         }
1699       l = sigdsize + sigcnt * 16;
1700       headerend = headerstart + 16 + l;
1701       if (l > rpmheadsize)
1702         rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + l);
1703       if (fread(rpmhead->data, l, 1, fp) != 1)
1704         {
1705           fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
1706           fclose(fp);
1707           continue;
1708         }
1709       rpmhead->cnt = sigcnt;
1710       rpmhead->dcnt = sigdsize;
1711       rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1712       if (headexists(rpmhead, TAG_PATCHESNAME))
1713         {
1714           /* this is a patch rpm, ignore */
1715           fclose(fp);
1716           continue;
1717         }
1718       payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
1719       if (payloadformat && !strcmp(payloadformat, "drpm"))
1720         {
1721           /* this is a delta rpm */
1722           fclose(fp);
1723           continue;
1724         }
1725       fclose(fp);
1726       s = pool_id2solvable(pool, repo_add_solvable(repo));
1727       rpm2solv(pool, repo, data, s, rpmhead);
1728       if (data)
1729         {
1730           Id handle = s - pool->solvables;
1731           add_location(data, s, handle, rpms[i]);
1732           repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, (unsigned int)((stb.st_size + 1023) / 1024));
1733           repodata_set_num(data, handle, SOLVABLE_HEADEREND, headerend);
1734         }
1735     }
1736   if (rpmhead)
1737     sat_free(rpmhead);
1738   if (!(flags & REPO_NO_INTERNALIZE))
1739     repodata_internalize(data);
1740 }