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