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