don't free 3.2 T on update
[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
23 #include <rpm/db.h>
24
25 #include "pool.h"
26 #include "repo.h"
27 #include "hash.h"
28 #include "util.h"
29 #include "repo_rpmdb.h"
30
31 #include "tools_util.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_SIZE                1009
42 #define TAG_VENDOR              1011
43 #define TAG_GROUP               1016
44 #define TAG_ARCH                1022
45 #define TAG_FILESIZES           1028
46 #define TAG_FILEMODES           1030
47 #define TAG_PROVIDENAME         1047
48 #define TAG_REQUIREFLAGS        1048
49 #define TAG_REQUIRENAME         1049
50 #define TAG_REQUIREVERSION      1050
51 #define TAG_CONFLICTFLAGS       1053
52 #define TAG_CONFLICTNAME        1054
53 #define TAG_CONFLICTVERSION     1055
54 #define TAG_OBSOLETENAME        1090
55 #define TAG_PROVIDEFLAGS        1112
56 #define TAG_PROVIDEVERSION      1113
57 #define TAG_OBSOLETEFLAGS       1114
58 #define TAG_OBSOLETEVERSION     1115
59 #define TAG_DIRINDEXES          1116
60 #define TAG_BASENAMES           1117
61 #define TAG_DIRNAMES            1118
62 #define TAG_SUGGESTSNAME        1156
63 #define TAG_SUGGESTSVERSION     1157
64 #define TAG_SUGGESTSFLAGS       1158
65 #define TAG_ENHANCESNAME        1159
66 #define TAG_ENHANCESVERSION     1160
67 #define TAG_ENHANCESFLAGS       1161
68
69 #define DEP_LESS                (1 << 1)
70 #define DEP_GREATER             (1 << 2)
71 #define DEP_EQUAL               (1 << 3)
72 #define DEP_STRONG              (1 << 27)
73 #define DEP_PRE                 ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
74
75
76 static DBT key;
77 static DBT data;
78
79 struct rpmid {
80   unsigned int dbid;
81   char *name;
82 };
83
84 typedef struct rpmhead {
85   int cnt;
86   int dcnt;
87   unsigned char *dp;
88   unsigned char data[1];
89 } RpmHead;
90
91 static unsigned int *
92 headint32array(RpmHead *h, int tag, int *cnt)
93 {
94   unsigned int i, o, *r;
95   unsigned char *d, taga[4];
96
97   d = h->dp - 16;
98   taga[0] = tag >> 24;
99   taga[1] = tag >> 16;
100   taga[2] = tag >> 8;
101   taga[3] = tag;
102   for (i = 0; i < h->cnt; i++, d -= 16)
103     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
104       break;
105   if (i >= h->cnt)
106     return 0;
107   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
108     return 0;
109   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
110   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
111   if (o + 4 * i > h->dcnt)
112     return 0;
113   d = h->dp + o;
114   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
115   if (cnt)
116     *cnt = i;
117   for (o = 0; o < i; o++, d += 4)
118     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
119   return r;
120 }
121
122 static unsigned int
123 headint32(RpmHead *h, int tag)
124 {
125   unsigned int i, o;
126   unsigned char *d, taga[4];
127
128   d = h->dp - 16; 
129   taga[0] = tag >> 24; 
130   taga[1] = tag >> 16; 
131   taga[2] = tag >> 8;
132   taga[3] = tag;
133   for (i = 0; i < h->cnt; i++, d -= 16) 
134     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
135       break;
136   if (i >= h->cnt)
137     return 0;
138   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
139     return 0;
140   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
141   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
142   if (i == 0 || o + 4 * i > h->dcnt)
143     return 0;
144   d = h->dp + o;
145   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
146 }
147
148 static unsigned int *
149 headint16array(RpmHead *h, int tag, int *cnt)
150 {
151   unsigned int i, o, *r;
152   unsigned char *d, taga[4];
153
154   d = h->dp - 16;
155   taga[0] = tag >> 24;
156   taga[1] = tag >> 16;
157   taga[2] = tag >> 8;
158   taga[3] = tag;
159   for (i = 0; i < h->cnt; i++, d -= 16)
160     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
161       break;
162   if (i >= h->cnt)
163     return 0;
164   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
165     return 0;
166   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
167   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
168   if (o + 4 * i > h->dcnt)
169     return 0;
170   d = h->dp + o;
171   r = sat_calloc(i ? i : 1, sizeof(unsigned int));
172   if (cnt)
173     *cnt = i;
174   for (o = 0; o < i; o++, d += 2)
175     r[o] = d[0] << 8 | d[1];
176   return r;
177 }
178
179 static char *
180 headstring(RpmHead *h, int tag)
181 {
182   unsigned int i, o;
183   unsigned char *d, taga[4];
184   d = h->dp - 16;
185   taga[0] = tag >> 24;
186   taga[1] = tag >> 16;
187   taga[2] = tag >> 8;
188   taga[3] = tag;
189   for (i = 0; i < h->cnt; i++, d -= 16)
190     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
191       break;
192   if (i >= h->cnt)
193     return 0;
194   /* 6: STRING, 9: I18NSTRING */
195   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
196     return 0;
197   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
198   return (char *)h->dp + o;
199 }
200
201 static char **
202 headstringarray(RpmHead *h, int tag, int *cnt)
203 {
204   unsigned int i, o;
205   unsigned char *d, taga[4];
206   char **r;
207
208   d = h->dp - 16;
209   taga[0] = tag >> 24;
210   taga[1] = tag >> 16;
211   taga[2] = tag >> 8;
212   taga[3] = tag;
213   for (i = 0; i < h->cnt; i++, d -= 16)
214     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
215       break;
216   if (i >= h->cnt)
217     return 0;
218   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
219     return 0;
220   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
221   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
222   r = sat_calloc(i ? i : 1, sizeof(char *));
223   if (cnt)
224     *cnt = i;
225   d = h->dp + o;
226   for (o = 0; o < i; o++)
227     {
228       r[o] = (char *)d;
229       if (o + 1 < i)
230         d += strlen((char *)d) + 1;
231       if (d >= h->dp + h->dcnt)
232         {
233           sat_free(r);
234           return 0;
235         }
236     }
237   return r;
238 }
239
240 static char *headtoevr(RpmHead *h)
241 {
242   unsigned int epoch;
243   char *version, *v;
244   char *release;
245   char *evr;
246
247   version  = headstring(h, TAG_VERSION);
248   release  = headstring(h, TAG_RELEASE);
249   epoch = headint32(h, TAG_EPOCH);
250   if (!version || !release)
251     {
252       fprintf(stderr, "headtoevr: bad rpm header\n");
253       exit(1);
254     }
255   for (v = version; *v >= 0 && *v <= '9'; v++)
256     ;
257   if (epoch || (v != version && *v == ':'))
258     {
259       char epochbuf[11];        /* 32bit decimal will fit in */
260       sprintf(epochbuf, "%u", epoch);
261       evr = sat_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
262       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
263     }
264   else
265     {
266       evr = sat_malloc(strlen(version) + 1 + strlen(release) + 1);
267       sprintf(evr, "%s-%s", version, release);
268     }
269   return evr;
270 }
271
272 static unsigned int
273 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int strong)
274 {
275   char **n, **v;
276   unsigned int *f;
277   int i, cc, nc, vc, fc;
278   int haspre = 0;
279   unsigned int olddeps;
280   Id *ida;
281
282   n = headstringarray(rpmhead, tagn, &nc);
283   if (!n)
284     return 0;
285   v = headstringarray(rpmhead, tagv, &vc);
286   if (!v)
287     {
288       sat_free(n);
289       return 0;
290     }
291   f = headint32array(rpmhead, tagf, &fc);
292   if (!f)
293     {
294       sat_free(n);
295       free(v);
296       return 0;
297     }
298   if (nc != vc || nc != fc)
299     {
300       fprintf(stderr, "bad dependency entries\n");
301       exit(1);
302     }
303
304   cc = nc;
305   if (strong)
306     {
307       cc = 0;
308       for (i = 0; i < nc; i++)
309         if ((f[i] & DEP_STRONG) == (strong == 1 ? 0 : DEP_STRONG))
310           {
311             cc++;
312             if ((f[i] & DEP_PRE) != 0)
313               haspre = 1;
314           }
315     }
316   else
317     {
318       for (i = 0; i < nc; i++)
319         if ((f[i] & DEP_PRE) != 0)
320           {
321             haspre = 1;
322             break;
323           }
324     }
325   if (tagn != TAG_REQUIRENAME)
326      haspre = 0;
327   if (cc == 0)
328     {
329       sat_free(n);
330       sat_free(v);
331       sat_free(f);
332       return 0;
333     }
334   cc += haspre;
335   olddeps = repo_reserve_ids(repo, 0, cc);
336   ida = repo->idarraydata + olddeps;
337   for (i = 0; ; i++)
338     {
339       if (i == nc)
340         {
341           if (haspre != 1)
342             break;
343           haspre = 2;
344           i = 0;
345           *ida++ = SOLVABLE_PREREQMARKER;
346         }
347       if (strong && (f[i] & DEP_STRONG) != (strong == 1 ? 0 : DEP_STRONG))
348         continue;
349       if (haspre == 1 && (f[i] & DEP_PRE) != 0)
350         continue;
351       if (haspre == 2 && (f[i] & DEP_PRE) == 0)
352         continue;
353       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
354         {
355           Id name, evr;
356           int flags = 0;
357           if ((f[i] & DEP_LESS) != 0)
358             flags |= 4;
359           if ((f[i] & DEP_EQUAL) != 0)
360             flags |= 2;
361           if ((f[i] & DEP_GREATER) != 0)
362             flags |= 1;
363           name = str2id(pool, n[i], 1);
364           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
365             evr = str2id(pool, v[i] + 2, 1);
366           else
367             evr = str2id(pool, v[i], 1);
368           *ida++ = rel2id(pool, name, evr, flags, 1);
369         }
370       else
371         *ida++ = str2id(pool, n[i], 1);
372     }
373   *ida++ = 0;
374   repo->idarraysize += cc + 1;
375   sat_free(n);
376   sat_free(v);
377   sat_free(f);
378   return olddeps;
379 }
380
381 static Offset
382 copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
383 {
384   int cc;
385   Id id, *ida, *from;
386   Offset ido;
387   Pool *frompool = fromrepo->pool;
388
389   if (!fromoff)
390     return 0;
391   from = fromrepo->idarraydata + fromoff;
392   for (ida = from, cc = 0; *ida; ida++, cc++)
393     ;
394   if (cc == 0)
395     return 0;
396   ido = repo_reserve_ids(repo, 0, cc);
397   ida = repo->idarraydata + ido;
398   if (frompool && pool != frompool)
399     {
400       while (*from)
401         {
402           id = *from++;
403           if (ISRELDEP(id))
404             {
405               Reldep *rd = GETRELDEP(frompool, id);
406               Id name = str2id(pool, id2str(frompool, rd->name), 1);
407               Id evr = str2id(pool, id2str(frompool, rd->evr), 1);
408               id = rel2id(pool, name, evr, rd->flags, 1);
409             }
410           else
411             id = str2id(pool, id2str(frompool, id), 1);
412           *ida++ = id;
413         }
414       *ida = 0;
415     }
416   else
417     memcpy(ida, from, (cc + 1) * sizeof(Id));
418   repo->idarraysize += cc + 1;
419   return ido;
420 }
421
422
423 #define FILEFILTER_EXACT    0
424 #define FILEFILTER_STARTS   1
425 #define FILEFILTER_CONTAINS 2
426
427 struct filefilter {
428   int dirmatch;
429   char *dir;
430   char *base;
431 };
432
433 static struct filefilter filefilters[] = {
434   { FILEFILTER_CONTAINS, "/bin/", 0},
435   { FILEFILTER_CONTAINS, "/sbin/", 0},
436   { FILEFILTER_CONTAINS, "/lib/", 0},
437   { FILEFILTER_CONTAINS, "/lib64/", 0},
438   { FILEFILTER_CONTAINS, "/etc/", 0},
439   { FILEFILTER_STARTS, "/usr/games/", 0},
440   { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
441   { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
442   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
443 };
444
445 static void
446 adddudata(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dic)
447 {
448   Id entry, did;
449   int i, fszc;
450   unsigned int *fkb, *fn, *fsz, *fm;
451
452   fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc);
453   if (!fsz || fc != fszc)
454     {
455       sat_free(fsz);
456       return;
457     }
458   /* stupid rpm recodrs sizes of directories, so we have to check the mode */
459   fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
460   if (!fm || fc != fszc)
461     {
462       sat_free(fsz);
463       sat_free(fm);
464       return;
465     }
466   fn = sat_calloc(dic, sizeof(unsigned int));
467   fkb = sat_calloc(dic, sizeof(unsigned int));
468   for (i = 0; i < fc; i++)
469     {
470       if (fsz[i] == 0 || !S_ISREG(fm[i]))
471         continue;
472       if (di[i] >= dic)
473         continue;
474       fn[di[i]]++;
475       /* does not consider hard links. tough luck. */
476       fkb[di[i]] += fsz[i] / 1024 + 1;
477     }
478   sat_free(fsz);
479   sat_free(fm);
480   /* commit */
481   repodata_extend(repodata, s - pool->solvables);
482   entry = (s - pool->solvables) - repodata->start;
483   for (i = 0; i < fc; i++)
484     {
485       if (!fn[i])
486         continue;
487       did = repodata_str2dir(repodata, dn[i], 1);
488       repodata_add_dirnumnum(repodata, entry, id_diskusage, did, fkb[i], fn[i]);
489     }
490   sat_free(fn);
491   sat_free(fkb);
492 }
493
494 /* assumes last processed array is provides! */
495 static unsigned int
496 addfileprovides(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead, unsigned int olddeps)
497 {
498   char **bn;
499   char **dn;
500   unsigned int *di;
501   int bnc, dnc, dic;
502   int i, j;
503   struct filefilter *ff;
504   char *fn = 0;
505   int fna = 0;
506
507   if (!repodata)
508     return olddeps;
509   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
510   if (!bn)
511     return olddeps;
512   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
513   if (!dn)
514     {
515       sat_free(bn);
516       return olddeps;
517     }
518   di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
519   if (!di)
520     {
521       sat_free(bn);
522       sat_free(dn);
523       return olddeps;
524     }
525   if (bnc != dic)
526     {
527       fprintf(stderr, "bad filelist\n");
528       exit(1);
529     }
530
531   if (repodata)
532     adddudata(pool, repo, repodata, s, rpmhead, dn, di, bnc, dic);
533
534   for (i = 0; i < bnc; i++)
535     {
536       ff = filefilters;
537       for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
538         {
539           if (ff->dir)
540             {
541               switch (ff->dirmatch)
542                 {
543                 case FILEFILTER_STARTS:
544                   if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
545                     continue;
546                   break;
547                 case FILEFILTER_CONTAINS:
548                   if (!strstr(dn[di[i]], ff->dir))
549                     continue;
550                   break;
551                 case FILEFILTER_EXACT:
552                 default:
553                   if (strcmp(dn[di[i]], ff->dir))
554                     continue;
555                   break;
556                 }
557             }
558           if (ff->base)
559             {
560               if (strcmp(bn[i], ff->base))
561                 continue;
562             }
563           break;
564         }
565       if (j == sizeof(filefilters)/sizeof(*filefilters))
566         continue;
567       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
568       if (j > fna)
569         {
570           fna = j + 256;
571           fn = sat_realloc(fn, fna);
572         }
573       strcpy(fn, dn[di[i]]);
574       strcat(fn, bn[i]);
575 #if 0
576       olddeps = repo_addid_dep(repo, olddeps, str2id(pool, fn, 1), SOLVABLE_FILEMARKER);
577 #endif
578       if (repodata)
579         {
580           Id entry, did;
581           repodata_extend(repodata, s - pool->solvables);
582           entry = (s - pool->solvables) - repodata->start;
583           did = repodata_str2dir(repodata, dn[di[i]], 1);
584           repodata_add_dirstr(repodata, entry, id_filelist, did, bn[i]);
585         }
586     }
587   if (fn)
588     sat_free(fn);
589   sat_free(bn);
590   sat_free(dn);
591   sat_free(di);
592   return olddeps;
593 }
594
595 static int
596 rpm2solv(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead)
597 {
598   char *name;
599   char *evr;
600
601   name = headstring(rpmhead, TAG_NAME);
602   if (!strcmp(name, "gpg-pubkey"))
603     return 0;
604   s->name = str2id(pool, name, 1);
605   if (!s->name)
606     {
607       fprintf(stderr, "package has no name\n");
608       exit(1);
609     }
610   s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
611   if (!s->arch)
612     s->arch = ARCH_NOARCH;
613   evr = headtoevr(rpmhead);
614   s->evr = str2id(pool, evr, 1);
615   sat_free(evr);
616   s->vendor = str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
617
618   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
619   s->provides = addfileprovides(pool, repo, repodata, s, rpmhead, s->provides);
620   s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
621   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
622   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
623   s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
624
625   s->recommends = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 2);
626   s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 1);
627   s->supplements = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 2);
628   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
629   s->freshens = 0;
630   s->supplements = repo_fix_legacy(repo, s->provides, s->supplements);
631
632   if (repodata)
633     {
634       Id entry;
635       char *str;
636       unsigned int u32;
637
638       repodata_extend(repodata, s - pool->solvables);
639       entry = (s - pool->solvables) - repodata->start;
640       str = headstring(rpmhead, TAG_SUMMARY);
641       if (str)
642         repodata_set_str(repodata, entry, id_summary, str);
643       str = headstring(rpmhead, TAG_DESCRIPTION);
644       if (str)
645         {
646           char *aut, *p;
647           for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
648             if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
649               break;
650           if (aut)
651             {
652               /* oh my, found SUSE special author section */
653               int l = aut - str;
654               str = strdup(str);
655               aut = str + l;
656               str[l] = 0;
657               while (l > 0 && str[l - 1] == '\n')
658                 str[--l] = 0;
659               if (l)
660                 repodata_set_str(repodata, entry, id_description, str);
661               p = aut + 19;
662               aut = str;        /* copy over */
663               while (*p == ' ' || *p == '\n')
664                 p++;
665               while (*p)
666                 {
667                   if (*p == '\n')
668                     {
669                       *aut++ = *p++;
670                       while (*p == ' ')
671                         p++;
672                       continue;
673                     }
674                   *aut++ = *p++;
675                 }
676               while (aut != str && aut[-1] == '\n')
677                 aut--;
678               *aut = 0;
679               if (*str)
680                 repodata_set_str(repodata, entry, id_authors, str);
681               free(str);
682             }
683           else if (*str)
684             repodata_set_str(repodata, entry, id_description, str);
685         }
686       str = headstring(rpmhead, TAG_GROUP);
687       if (str)
688         repodata_set_poolstr(repodata, entry, id_group, str);
689       u32 = headint32(rpmhead, TAG_BUILDTIME);
690       if (u32)
691         repodata_set_num(repodata, entry, id_time, u32);
692       u32 = headint32(rpmhead, TAG_SIZE);
693       if (u32)
694         repodata_set_num(repodata, entry, id_installsize, (u32 + 1023) / 1024);
695
696     }
697   return 1;
698 }
699
700
701 /*
702  * read rpm db as repo
703  * 
704  */
705
706 void
707 repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir)
708 {
709   Pool *pool = repo->pool;
710   unsigned char buf[16];
711   DB *db = 0;
712   DBC *dbc = 0;
713   int byteswapped;
714   unsigned int dbid;
715   unsigned char *dp, *dbidp;
716   int dl, nrpmids;
717   struct rpmid *rpmids, *rp;
718   int i;
719   int rpmheadsize;
720   RpmHead *rpmhead;
721   Solvable *s;
722   Id id, *refhash;
723   unsigned int refmask, h;
724   int asolv;
725   Repodata *repodata;
726   char dbpath[PATH_MAX];
727
728   if (repo->start != repo->end)
729     abort();            /* FIXME: rpmdbid */
730
731   repodata = repo_add_repodata(repo);
732   init_attr_ids(repo->pool);
733
734   if (ref && !(ref->nsolvables && ref->rpmdbid))
735     ref = 0;
736
737   if (db_create(&db, 0, 0))
738     {
739       perror("db_create");
740       exit(1);
741     }
742
743   if (!ref)
744     {
745       snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
746       if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
747         {
748           perror("db->open var/lib/rpm/Packages");
749           exit(1);
750         }
751       if (db->get_byteswapped(db, &byteswapped))
752         {
753           perror("db->get_byteswapped");
754           exit(1);
755         }
756       if (db->cursor(db, NULL, &dbc, 0))
757         {
758           perror("db->cursor");
759           exit(1);
760         }
761       dbidp = (unsigned char *)&dbid;
762       repo->rpmdbid = sat_calloc(256, sizeof(unsigned int));
763       asolv = 256;
764       rpmheadsize = 0;
765       rpmhead = 0;
766       i = 0;
767       s = 0;
768       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
769         {
770           if (!s)
771             s = pool_id2solvable(pool, repo_add_solvable(repo));
772           if (i >= asolv)
773             {
774               repo->rpmdbid = sat_realloc(repo->rpmdbid, (asolv + 256) * sizeof(unsigned int));
775               memset(repo->rpmdbid + asolv, 0, 256 * sizeof(unsigned int));
776               asolv += 256;
777             }
778           if (key.size != 4)
779             {
780               fprintf(stderr, "corrupt Packages database (key size)\n");
781               exit(1);
782             }
783           dp = key.data;
784           if (byteswapped)
785             {
786               dbidp[0] = dp[3];
787               dbidp[1] = dp[2];
788               dbidp[2] = dp[1];
789               dbidp[3] = dp[0];
790             }
791           else
792             memcpy(dbidp, dp, 4);
793           if (dbid == 0)                /* the join key */
794             continue;
795           if (data.size < 8)
796             {
797               fprintf(stderr, "corrupt rpm database (size %u)\n", data.size);
798               exit(1);
799             }
800           if (data.size > rpmheadsize)
801             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + data.size);
802           memcpy(buf, data.data, 8);
803           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
804           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
805           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
806             {
807               fprintf(stderr, "corrupt rpm database (data size)\n");
808               exit(1);
809             }
810           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
811           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
812           repo->rpmdbid[i] = dbid;
813           if (rpm2solv(pool, repo, repodata, s, rpmhead))
814             {
815               i++;
816               s = 0;
817             }
818           else
819             {
820               /* We can reuse this solvable, but make sure it's still
821                  associated with this repo.  */
822               memset(s, 0, sizeof(*s));
823               s->repo = repo;
824             }
825         }
826       if (s)
827         {
828           /* oops, could not reuse. free it instead */
829           repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
830           s = 0;
831         }
832       dbc->c_close(dbc);
833       db->close(db, 0);
834       db = 0;
835     }
836   else
837     {
838       snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir);
839       if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
840         {
841           perror("db->open var/lib/rpm/Name");
842           exit(1);
843         }
844       if (db->get_byteswapped(db, &byteswapped))
845         {
846           perror("db->get_byteswapped");
847           exit(1);
848         }
849       if (db->cursor(db, NULL, &dbc, 0))
850         {
851           perror("db->cursor");
852           exit(1);
853         }
854       dbidp = (unsigned char *)&dbid;
855       nrpmids = 0;
856       rpmids = 0;
857       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
858         {
859           if (key.size == 10 && !memcmp(key.data, "gpg-pubkey", 10))
860             continue;
861           dl = data.size;
862           dp = data.data;
863           while(dl >= 8)
864             {
865               if (byteswapped)
866                 {
867                   dbidp[0] = dp[3];
868                   dbidp[1] = dp[2];
869                   dbidp[2] = dp[1];
870                   dbidp[3] = dp[0];
871                 }
872               else
873                 memcpy(dbidp, dp, 4);
874               if ((nrpmids & 255) == 0)
875                 rpmids = sat_realloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
876               rpmids[nrpmids].dbid = dbid;
877               rpmids[nrpmids].name = sat_malloc((int)key.size + 1);
878               memcpy(rpmids[nrpmids].name, key.data, (int)key.size);
879               rpmids[nrpmids].name[(int)key.size] = 0;
880               nrpmids++;
881               dp += 8;
882               dl -= 8;
883             }
884         }
885       dbc->c_close(dbc);
886       db->close(db, 0);
887       db = 0;
888
889       rp = rpmids;
890       dbidp = (unsigned char *)&dbid;
891       rpmheadsize = 0;
892       rpmhead = 0;
893
894       refhash = 0;
895       refmask = 0;
896       if (ref)
897         {
898           refmask = mkmask(ref->nsolvables);
899           refhash = sat_calloc(refmask + 1, sizeof(Id));
900           for (i = 0; i < ref->nsolvables; i++)
901             {
902               h = ref->rpmdbid[i] & refmask;
903               while (refhash[h])
904                 h = (h + 317) & refmask;
905               refhash[h] = i + 1;       /* make it non-zero */
906             }
907         }
908
909       repo->rpmdbid = sat_calloc(nrpmids, sizeof(unsigned int));
910
911       s = pool_id2solvable(pool, repo_add_solvable_block(repo, nrpmids));
912
913       for (i = 0; i < nrpmids; i++, rp++, s++)
914         {
915           dbid = rp->dbid;
916           repo->rpmdbid[i] = dbid;
917           if (refhash)
918             {
919               h = dbid & refmask;
920               while ((id = refhash[h]))
921                 {
922                   if (ref->rpmdbid[id - 1] == dbid)
923                     break;
924                   h = (h + 317) & refmask;
925                 }
926               if (id)
927                 {
928                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
929                   if (pool == ref->pool)
930                     {
931                       s->name = r->name;
932                       s->evr = r->evr;
933                       s->arch = r->arch;
934                       s->vendor = r->vendor;
935                     }
936                   else
937                     {
938                       if (r->name)
939                         s->name = str2id(pool, id2str(ref->pool, r->name), 1);
940                       if (r->evr)
941                         s->evr = str2id(pool, id2str(ref->pool, r->evr), 1);
942                       if (r->arch)
943                         s->arch = str2id(pool, id2str(ref->pool, r->arch), 1);
944                       if (r->vendor)
945                         s->vendor = str2id(pool, id2str(ref->pool, r->vendor), 1);
946                     }
947                   s->provides = copydeps(pool, repo, r->provides, ref);
948                   s->requires = copydeps(pool, repo, r->requires, ref);
949                   s->conflicts = copydeps(pool, repo, r->conflicts, ref);
950                   s->obsoletes = copydeps(pool, repo, r->obsoletes, ref);
951                   s->recommends = copydeps(pool, repo, r->recommends, ref);
952                   s->suggests = copydeps(pool, repo, r->suggests, ref);
953                   s->supplements = copydeps(pool, repo, r->supplements, ref);
954                   s->enhances  = copydeps(pool, repo, r->enhances, ref);
955                   s->freshens = copydeps(pool, repo, r->freshens, ref);
956                   continue;
957                 }
958             }
959           if (!db)
960             {
961               if (db_create(&db, 0, 0))
962                 {
963                   perror("db_create");
964                   exit(1);
965                 }
966               snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
967               if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
968                 {
969                   perror("db->open var/lib/rpm/Packages");
970                   exit(1);
971                 }
972               if (db->get_byteswapped(db, &byteswapped))
973                 {
974                   perror("db->get_byteswapped");
975                   exit(1);
976                 }
977             }
978           if (byteswapped)
979             {
980               buf[0] = dbidp[3];
981               buf[1] = dbidp[2];
982               buf[2] = dbidp[1];
983               buf[3] = dbidp[0];
984             }
985           else
986             memcpy(buf, dbidp, 4);
987           key.data = buf;
988           key.size = 4;
989           data.data = 0;
990           data.size = 0;
991           if (db->get(db, NULL, &key, &data, 0))
992             {
993               perror("db->get");
994               fprintf(stderr, "corrupt rpm database\n");
995               exit(1);
996             }
997           if (data.size < 8)
998             {
999               fprintf(stderr, "corrupt rpm database (size)\n");
1000               exit(1);
1001             }
1002           if (data.size > rpmheadsize)
1003             rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + data.size);
1004           memcpy(buf, data.data, 8);
1005           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1006           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1007           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
1008             {
1009               fprintf(stderr, "corrupt rpm database (data size)\n");
1010               exit(1);
1011             }
1012           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1013           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1014
1015           rpm2solv(pool, repo, repodata, s, rpmhead);
1016         }
1017
1018       if (refhash)
1019         sat_free(refhash);
1020       if (rpmids)
1021         {
1022           for (i = 0; i < nrpmids; i++)
1023             sat_free(rpmids[i].name);
1024           sat_free(rpmids);
1025         }
1026     }
1027   if (rpmhead)
1028     sat_free(rpmhead);
1029   if (db)
1030     db->close(db, 0);
1031   if (repodata)
1032     repodata_internalize(repodata);
1033 }
1034
1035 /* XXX: delete me! */
1036 void rpmdb_dummy()
1037 {
1038   makeevr(0, 0);
1039   split(0, 0, 0);
1040 }