to follow SUSE tools conventions
[platform/upstream/libsolv.git] / src / repo_solv.c
1 /*
2  * repo_solv.c
3  * 
4  * Read the binary dump of a Repo and create a Repo * from it
5  * 
6  *  See
7  *   Repo *pool_addrepo_solv(Pool *pool, FILE *fp)
8  * below
9  * 
10  */
11
12
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18
19 #include "repo_solv.h"
20 #include "util.h"
21
22 #define INTERESTED_START        2
23 #define INTERESTED_END          13
24
25 /*-----------------------------------------------------------------*/
26 /* .solv read functions */
27
28 /*
29  * read u32
30  */
31
32 static unsigned int
33 read_u32(FILE *fp)
34 {
35   int c, i;
36   unsigned int x = 0;
37
38   for (i = 0; i < 4; i++)
39     {
40       c = getc(fp);
41       if (c == EOF)
42         {
43           fprintf(stderr, "unexpected EOF\n");
44           exit(1);
45         }
46       x = (x << 8) | c;
47     }
48   return x;
49 }
50
51
52 /*
53  * read u8
54  */
55
56 static unsigned int
57 read_u8(FILE *fp)
58 {
59   int c;
60   c = getc(fp);
61   if (c == EOF)
62     {
63       fprintf(stderr, "unexpected EOF\n");
64       exit(1);
65     }
66   return c;
67 }
68
69
70 /*
71  * read Id
72  */
73
74 static Id
75 read_id(FILE *fp, Id max)
76 {
77   unsigned int x = 0;
78   int c, i;
79
80   for (i = 0; i < 5; i++)
81     {
82       c = getc(fp);
83       if (c == EOF)
84         {
85           fprintf(stderr, "unexpected EOF\n");
86           exit(1);
87         }
88       if (!(c & 128))
89         {
90           x = (x << 7) | c;
91           if (x >= max)
92             {
93               fprintf(stderr, "read_id: id too large (%u/%u)\n", x, max);
94               exit(1);
95             }
96           return x;
97         }
98       x = (x << 7) ^ c ^ 128;
99     }
100   fprintf(stderr, "read_id: id too long\n");
101   exit(1);
102 }
103
104
105 /*
106  * read array of Ids
107  */
108
109 static Id *
110 read_idarray(FILE *fp, Id max, Id *map, Id *store, Id *end)
111 {
112   unsigned int x = 0;
113   int c;
114   for (;;)
115     {
116       c = getc(fp);
117       if (c == EOF)
118         {
119           fprintf(stderr, "unexpected EOF\n");
120           exit(1);
121         }
122       if ((c & 128) == 0)
123         {
124           x = (x << 6) | (c & 63);
125           if (x >= max)
126             {
127               fprintf(stderr, "read_idarray: id too large (%u/%u)\n", x, max);
128               exit(1);
129             }
130           if (store == end)
131             {
132               fprintf(stderr, "read_idarray: array overflow\n");
133               exit(1);
134             }
135           *store++ = map[x];
136           if ((c & 64) == 0)
137             {
138               if (store == end)
139                 {
140                   fprintf(stderr, "read_idarray: array overflow\n");
141                   exit(1);
142                 }
143               *store++ = 0;
144               return store;
145             }
146           x = 0;
147           continue;
148         }
149       x = (x << 7) ^ c ^ 128;
150     }
151 }
152
153
154 /*-----------------------------------------------------------------*/
155
156 typedef struct solvdata {
157   int type;
158   Id id;
159   unsigned int size;
160 } SolvData;
161
162
163 // ----------------------------------------------
164
165 /*
166  * read repo from .solv file
167  *  and add it to pool
168  */
169
170 Repo *
171 pool_addrepo_solv(Pool *pool, FILE *fp, const char *reponame)
172 {
173   int i, j, l;
174   unsigned int numid, numrel, numsolv, numsrcdata, numsolvdata;
175   int numsolvdatabits, type;
176   Offset sizeid;
177   Offset *str;                         /* map Id -> Offset into string space */
178   char *strsp;                         /* repo string space */
179   char *sp;                            /* pointer into string space */
180   Id *idmap;                           /* map of repo Ids to pool Ids */
181   Id id;
182   unsigned int hashmask, h;
183   int hh;
184   Id *hashtbl;
185   Id name, evr, did;
186   int flags;
187   Reldep *ran;
188   SolvData *solvdata;
189   unsigned int size, size_str, size_idarray;
190   Repo *repo;
191   Id *idarraydatap, *idarraydataend;
192   Offset ido;
193   unsigned int databits;
194   Solvable *s;
195
196   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
197     {
198       fprintf(stderr, "not a SOLV file\n");
199       exit(1);
200     }
201   if (read_u32(fp) != SOLV_VERSION)
202     {
203       fprintf(stderr, "unsupported SOLV version\n");
204       exit(1);
205     }
206
207                                        /* create empty Repo */
208   repo = pool_addrepo_empty(pool);
209   pool_freeidhashes(pool);
210
211   repo->name = reponame;
212   
213   numid = read_u32(fp);
214   numrel = read_u32(fp);
215   numsolv= read_u32(fp);
216
217   sizeid = read_u32(fp);               /* size of string+Id space */
218
219   /*
220    * read strings and Ids
221    * 
222    */
223
224   
225   /*
226    * alloc buffers
227    */
228                                        /* alloc string buffer */
229   strsp = (char *)xrealloc(pool->stringspace, pool->sstrings + sizeid + 1);
230                                        /* alloc string offsets (Id -> Offset into string space) */
231   str = (Offset *)xrealloc(pool->strings, (pool->nstrings + numid) * sizeof(Offset));
232
233   pool->stringspace = strsp;
234   pool->strings = str;                 /* array of offsets into strsp, indexed by Id */
235
236   /* point to _BEHIND_ already allocated string/Id space */
237   strsp += pool->sstrings;
238
239                                        /* alloc id map for name and rel Ids */
240   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
241
242   /*
243    * read new repo at end of pool
244    */
245   
246   if (fread(strsp, sizeid, 1, fp) != 1)
247     {
248       fprintf(stderr, "read error while reading strings\n");
249       exit(1);
250     }
251   strsp[sizeid] = 0;                   /* make string space \0 terminated */
252   sp = strsp;
253
254   /*
255    * build hashes for all read strings
256    * 
257    */
258   
259   hashmask = mkmask(pool->nstrings + numid);
260
261 #if 0
262   printf("read %d strings\n", numid);
263   printf("string hash buckets: %d\n", hashmask + 1);
264 #endif
265
266   /*
267    * ensure sufficient hash size
268    */
269   
270   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
271
272   /*
273    * fill hashtable with strings already in pool
274    */
275   
276   for (i = 1; i < pool->nstrings; i++)  /* leave out our dummy zero id */
277     {
278       h = strhash(pool->stringspace + pool->strings[i]) & hashmask;
279       hh = HASHCHAIN_START;
280       while (hashtbl[h])
281         h = HASHCHAIN_NEXT(h, hh, hashmask);
282       hashtbl[h] = i;
283     }
284
285   /*
286    * run over string space, calculate offsets
287    * 
288    * build id map (maps solv Id -> pool Id)
289    */
290   
291   for (i = 1; i < numid; i++)
292     {
293       if (sp >= strsp + sizeid)
294         {
295           fprintf(stderr, "not enough strings\n");
296           exit(1);
297         }
298       if (!*sp)                        /* empty string */
299         {
300           idmap[i] = ID_EMPTY;
301           sp++;
302           continue;
303         }
304
305       /* find hash slot */
306       h = strhash(sp) & hashmask;
307       hh = HASHCHAIN_START;
308       for (;;)
309         {
310           id = hashtbl[h];
311           if (id == 0)
312             break;
313           if (!strcmp(pool->stringspace + pool->strings[id], sp))
314             break;                     /* existing string */
315           h = HASHCHAIN_NEXT(h, hh, hashmask);
316         }
317
318       /* length == offset to next string */
319       l = strlen(sp) + 1;
320       if (id == ID_NULL)               /* end of hash chain -> new string */
321         {
322           id = pool->nstrings++;
323           hashtbl[h] = id;
324           str[id] = pool->sstrings;    /* save Offset */
325           if (sp != pool->stringspace + pool->sstrings)   /* not at end-of-buffer */
326             memmove(pool->stringspace + pool->sstrings, sp, l);   /* append to pool buffer */
327           pool->sstrings += l;
328         }
329       idmap[i] = id;                   /* repo relative -> pool relative */
330       sp += l;                         /* next string */
331     }
332   xfree(hashtbl);
333   pool_shrink_strings(pool);           /* vacuum */
334
335   
336   /*
337    * read RelDeps
338    * 
339    */
340   
341   if (numrel)
342     {
343       /* extend rels */
344       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
345       if (!ran)
346         {
347           fprintf(stderr, "no mem for rel space\n");
348           exit(1);
349         }
350       pool->rels = ran;        /* extended rel space */
351
352       hashmask = mkmask(pool->nrels + numrel);
353 #if 0
354       printf("read %d rels\n", numrel);
355       printf("rel hash buckets: %d\n", hashmask + 1);
356 #endif
357       /*
358        * prep hash table with already existing RelDeps
359        */
360       
361       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
362       for (i = 1; i < pool->nrels; i++)
363         {
364           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
365           hh = HASHCHAIN_START;
366           while (hashtbl[h])
367             h = HASHCHAIN_NEXT(h, hh, hashmask);
368           hashtbl[h] = i;
369         }
370
371       /*
372        * read RelDeps from repo
373        */
374       
375       for (i = 0; i < numrel; i++)
376         {
377           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
378           evr = read_id(fp, i + numid);
379           flags = read_u8(fp);
380           name = idmap[name];           /* map to (pool relative) Ids */
381           evr = idmap[evr];
382           h = relhash(name, evr, flags) & hashmask;
383           hh = HASHCHAIN_START;
384           for (;;)
385             {
386               id = hashtbl[h];
387               if (id == ID_NULL)        /* end of hash chain */
388                 break;
389               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
390                 break;
391               h = HASHCHAIN_NEXT(h, hh, hashmask);
392             }
393           if (id == ID_NULL)            /* new RelDep */
394             {
395               id = pool->nrels++;
396               hashtbl[h] = id;
397               ran[id].name = name;
398               ran[id].evr = evr;
399               ran[id].flags = flags;
400             }
401           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
402         }
403       xfree(hashtbl);
404       pool_shrink_rels(pool);           /* vacuum */
405     }
406
407   /*
408    * read (but dont store) repo data
409    */
410
411 #if 0
412   printf("read repo data\n");
413 #endif
414   numsrcdata = read_u32(fp);
415   for (i = 0; i < numsrcdata; i++)
416     {
417       type = read_u8(fp);
418       id = idmap[read_id(fp, numid)];
419       switch(type)
420         {
421         case TYPE_ID:
422           read_id(fp, numid + numrel);   /* just check Id */
423           break;
424         case TYPE_U32:
425           read_u32(fp);
426           break;
427         case TYPE_STR:
428           while(read_u8(fp) != 0)
429             ;
430           break;
431         default:
432           fprintf(stderr, "unknown type\n");
433           exit(0);
434         }
435     }
436
437
438   /*
439    * read solvables
440    */
441   
442 #if 0
443   printf("read solvable data info\n");
444 #endif
445   numsolvdata = read_u32(fp);
446   numsolvdatabits = 0;
447   solvdata = (SolvData *)xmalloc(numsolvdata * sizeof(SolvData));
448   size_idarray = 0;
449   size_str = 0;
450
451   for (i = 0; i < numsolvdata; i++)
452     {
453       type = read_u8(fp);
454       solvdata[i].type = type;
455       if ((type & TYPE_BITMAP) != 0)
456         {
457           type ^= TYPE_BITMAP;
458           numsolvdatabits++;
459         }
460       id = idmap[read_id(fp, numid)];
461 #if 0
462       printf("#%d: %s\n", i, id2str(pool, id));
463 #endif
464       solvdata[i].id = id;
465       size = read_u32(fp);
466       solvdata[i].size = size;
467       if (id >= INTERESTED_START && id <= INTERESTED_END)
468         {
469           if (type == TYPE_STR)
470             size_str += size;
471           if (type == TYPE_IDARRAY)
472             size_idarray += size;
473         }
474     }
475
476   if (numsolvdatabits >= 32)
477     {
478       fprintf(stderr, "too many data map bits\n");
479       exit(1);
480     }
481   if (size_idarray)
482     {
483       size_idarray++;   /* first entry is always zero */
484       repo->idarraydata = (Id *)xmalloc(sizeof(Id) * size_idarray);
485       repo->idarraysize = size_idarray;
486       idarraydatap = repo->idarraydata;
487       *idarraydatap++ = 0;
488       idarraydataend = repo->idarraydata + size_idarray;
489     }
490   else
491     {
492       repo->idarraydata = 0;
493       repo->idarraysize = 0;
494       idarraydatap = 0;
495       idarraydataend = 0;
496     }
497
498   /* alloc solvables */
499   pool->solvables = (Solvable *)xrealloc(pool->solvables, (pool->nsolvables + numsolv) * sizeof(Solvable));
500
501   if (numsolv)                         /* clear newly allocated area */
502     memset(pool->solvables + pool->nsolvables, 0, numsolv * sizeof(Solvable));
503   repo->start = pool->nsolvables;
504   repo->nsolvables = numsolv;
505
506   /*
507    * read solvables
508    */
509   
510 #if 0
511   printf("read solvables\n");
512 #endif
513   for (i = 0, s = pool->solvables + repo->start; i < numsolv; i++, s++)
514     {
515       s->repo = repo;
516       databits = 0;
517       if (numsolvdatabits)
518         {
519           for (j = 0; j < (numsolvdatabits + 7) >> 3; j++)
520             databits = (databits << 8) | read_u8(fp);
521         }
522       for (j = 0; j < numsolvdata; j++)
523         {
524           type = solvdata[j].type;
525           if ((type & TYPE_BITMAP) != 0)
526             {
527               if (!(databits & 1))
528                 {
529                   databits >>= 1;
530                   continue;
531                 }
532               databits >>= 1;
533               type ^= TYPE_BITMAP;
534             }
535           id = solvdata[j].id;
536           switch (type)
537             {
538             case TYPE_ID:
539               did = idmap[read_id(fp, numid + numrel)];
540               if (id == SOLVABLE_NAME)
541                 s->name = did;
542               else if (id == SOLVABLE_ARCH)
543                 s->arch = did;
544               else if (id == SOLVABLE_EVR)
545                 s->evr= did;
546 #if 0
547               printf("%s -> %s\n", id2str(pool, id), id2str(pool, did));
548 #endif
549               break;
550             case TYPE_U32:
551               h = read_u32(fp);
552 #if 0
553               printf("%s -> %u\n", id2str(pool, id), h);
554 #endif
555               if (id == RPM_RPMDBID)
556                 {
557                   if (!repo->rpmdbid)
558                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
559                   repo->rpmdbid[i] = h;
560                 }
561               break;
562             case TYPE_STR:
563               while(read_u8(fp) != 0)
564                 ;
565               break;
566             case TYPE_IDARRAY:
567               if (id < INTERESTED_START || id > INTERESTED_END)
568                 {
569                   /* not interested in array */
570                   while ((read_u8(fp) & 0xc0) != 0)
571                     ;
572                   break;
573                 }
574               ido = idarraydatap - repo->idarraydata;
575               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend);
576               if (id == SOLVABLE_PROVIDES)
577                 s->provides = ido;
578               else if (id == SOLVABLE_OBSOLETES)
579                 s->obsoletes = ido;
580               else if (id == SOLVABLE_CONFLICTS)
581                 s->conflicts = ido;
582               else if (id == SOLVABLE_REQUIRES)
583                 s->requires = ido;
584               else if (id == SOLVABLE_RECOMMENDS)
585                 s->recommends= ido;
586               else if (id == SOLVABLE_SUPPLEMENTS)
587                 s->supplements = ido;
588               else if (id == SOLVABLE_SUGGESTS)
589                 s->suggests = ido;
590               else if (id == SOLVABLE_ENHANCES)
591                 s->enhances = ido;
592               else if (id == SOLVABLE_FRESHENS)
593                 s->freshens = ido;
594 #if 0
595               printf("%s ->\n", id2str(pool, id));
596               for (; repo->idarraydata[ido]; ido++)
597                 printf("  %s\n", dep2str(pool, repo->idarraydata[ido]));
598 #endif
599               break;
600             }
601         }
602     }
603
604   xfree(idmap);
605   xfree(solvdata);
606
607   pool->nsolvables += numsolv;
608
609   return repo;
610 }
611
612 // EOF