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