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