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