- fix bug in solv loading callback
[platform/upstream/libsolv.git] / src / repodata.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  * repodata.c
10  *
11  * Manage data coming from one repository
12  * 
13  */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17 #include <fnmatch.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <assert.h>
23
24 #include "repo.h"
25 #include "pool.h"
26 #include "poolid_private.h"
27 #include "util.h"
28
29 #include "repopack.h"
30
31 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
32                                   unsigned char *out, unsigned int out_len);
33 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
34                                               unsigned int in_len,
35                                               unsigned char *out,
36                                               unsigned int out_len);
37
38 #define REPODATA_BLOCK 255
39
40 void
41 repodata_free(Repodata *data)
42 {
43   sat_free(data->keys);
44   sat_free(data->schemata);
45   sat_free(data->schemadata);
46
47   sat_free(data->spool.strings);
48   sat_free(data->spool.stringspace);
49   sat_free(data->spool.stringhashtbl);
50
51   sat_free(data->dirpool.dirs);
52   sat_free(data->dirpool.dirtraverse);
53
54   sat_free(data->incoredata);
55   sat_free(data->incoreoffset);
56   sat_free(data->verticaloffset);
57
58   sat_free(data->blob_store);
59   sat_free(data->pages);
60   sat_free(data->mapped);
61
62   sat_free(data->vincore);
63
64   sat_free(data->attrs);
65   sat_free(data->attrdata);
66   sat_free(data->attriddata);
67   
68   if (data->pagefd != -1)
69     close(data->pagefd);
70 }
71
72 static unsigned char *
73 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
74 {
75   Id k, *keyp;
76
77   keyp = data->schemadata + data->schemata[schemaid];
78   while ((k = *keyp++) != 0)
79     {
80       if (k == key)
81         return dp;
82       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
83         {
84           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that offset */
85           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that length */
86           continue;
87         }
88       if (data->keys[k].storage != KEY_STORAGE_INCORE)
89         continue;
90       dp = data_skip(dp, data->keys[k].type);
91     }
92   return 0;
93 }
94
95 #define BLOB_PAGEBITS 15
96 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
97
98 static unsigned char *
99 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
100 {
101 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
102    and are consecutive.  Return a pointer to the mapping of PSTART.  */
103   unsigned char buf[BLOB_PAGESIZE];
104   unsigned int i;
105
106   /* Quick check in case all pages are there already and consecutive.  */
107   for (i = pstart; i <= pend; i++)
108     if (data->pages[i].mapped_at == -1
109         || (i > pstart
110             && data->pages[i].mapped_at
111                != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
112       break;
113   if (i > pend)
114     return data->blob_store + data->pages[pstart].mapped_at;
115
116   /* Ensure that we can map the numbers of pages we need at all.  */
117   if (pend - pstart + 1 > data->ncanmap)
118     {
119       unsigned int oldcan = data->ncanmap;
120       data->ncanmap = pend - pstart + 1;
121       if (data->ncanmap < 4)
122         data->ncanmap = 4;
123       data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
124       memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
125       data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
126 #ifdef DEBUG_PAGING
127       fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
128 #endif
129     }
130
131   /* Now search for "cheap" space in our store.  Space is cheap if it's either
132      free (very cheap) or contains pages we search for anyway.  */
133
134   /* Setup cost array.  */
135   unsigned int cost[data->ncanmap];
136   for (i = 0; i < data->ncanmap; i++)
137     {
138       unsigned int pnum = data->mapped[i];
139       if (pnum == 0)
140         cost[i] = 0;
141       else
142         {
143           pnum--;
144           Attrblobpage *p = data->pages + pnum;
145           assert (p->mapped_at != -1);
146           if (pnum >= pstart && pnum <= pend)
147             cost[i] = 1;
148           else
149             cost[i] = 3;
150         }
151     }
152
153   /* And search for cheapest space.  */
154   unsigned int best_cost = -1;
155   unsigned int best = 0;
156   unsigned int same_cost = 0;
157   for (i = 0; i + pend - pstart < data->ncanmap; i++)
158     {
159       unsigned int c = cost[i];
160       unsigned int j;
161       for (j = 0; j < pend - pstart + 1; j++)
162         c += cost[i+j];
163       if (c < best_cost)
164         best_cost = c, best = i;
165       else if (c == best_cost)
166         same_cost++;
167       /* A null cost won't become better.  */
168       if (c == 0)
169         break;
170     }
171   /* If all places have the same cost we would thrash on slot 0.  Avoid
172      this by doing a round-robin strategy in this case.  */
173   if (same_cost == data->ncanmap - pend + pstart - 1)
174     best = data->rr_counter++ % (data->ncanmap - pend + pstart);
175
176   /* So we want to map our pages from [best] to [best+pend-pstart].
177      Use a very simple strategy, which doesn't make the best use of
178      our resources, but works.  Throw away all pages in that range
179      (even ours) then copy around ours (in case they were outside the 
180      range) or read them in.  */
181   for (i = best; i < best + pend - pstart + 1; i++)
182     {
183       unsigned int pnum = data->mapped[i];
184       if (pnum--
185           /* If this page is exactly at the right place already,
186              no need to evict it.  */
187           && pnum != pstart + i - best)
188         {
189           /* Evict this page.  */
190 #ifdef DEBUG_PAGING
191           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
192 #endif
193           cost[i] = 0;
194           data->mapped[i] = 0;
195           data->pages[pnum].mapped_at = -1;
196         }
197     }
198
199   /* Everything is free now.  Read in the pages we want.  */
200   for (i = pstart; i <= pend; i++)
201     {
202       Attrblobpage *p = data->pages + i;
203       unsigned int pnum = i - pstart + best;
204       void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
205       if (p->mapped_at != -1)
206         {
207           if (p->mapped_at != pnum * BLOB_PAGESIZE)
208             {
209 #ifdef DEBUG_PAGING
210               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
211 #endif
212               /* Still mapped somewhere else, so just copy it from there.  */
213               memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
214               data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
215             }
216         }
217       else
218         {
219           unsigned int in_len = p->file_size;
220           unsigned int compressed = in_len & 1;
221           in_len >>= 1;
222 #ifdef DEBUG_PAGING
223           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
224 #endif
225           if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
226             {
227               perror ("mapping pread");
228               return 0;
229             }
230           if (compressed)
231             {
232               unsigned int out_len;
233               out_len = unchecked_decompress_buf(buf, in_len,
234                                                   dest, BLOB_PAGESIZE);
235               if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
236                 {
237                   fprintf(stderr, "can't decompress\n");
238                   return 0;
239                 }
240 #ifdef DEBUG_PAGING
241               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
242 #endif
243             }
244 #ifdef DEBUG_PAGING
245           fprintf (stderr, "\n");
246 #endif
247         }
248       p->mapped_at = pnum * BLOB_PAGESIZE;
249       data->mapped[pnum] = i + 1;
250     }
251   return data->blob_store + best * BLOB_PAGESIZE;
252 }
253
254 static unsigned char *
255 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
256 {
257   unsigned char *dp;
258   if (key->type == REPOKEY_TYPE_VOID)
259     return 0;
260   if (off >= data->lastverticaloffset)
261     {
262       off -= data->lastverticaloffset;
263       if (off + len > data->vincorelen)
264         return 0;
265       return data->vincore + off;
266     }
267   if (data->pagefd == -1)
268     return 0;
269   if (off + len > key->size)
270     return 0;
271   /* we now have the offset, go into vertical */
272   off += data->verticaloffset[key - data->keys];
273   dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
274   if (dp)
275     dp += off % BLOB_PAGESIZE;
276   return dp;
277 }
278
279 static inline unsigned char *
280 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
281 {
282   unsigned char *dp = *dpp;
283
284   if (!dp)
285     return 0;
286   if (key->storage == KEY_STORAGE_INCORE)
287     {
288       /* hmm, this is a bit expensive */
289       *dpp = data_skip(dp, key->type);
290       return dp;
291     }
292   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
293     {
294       Id off, len;
295       dp = data_read_id(dp, &off);
296       dp = data_read_id(dp, &len);
297       *dpp = dp;
298       return make_vertical_available(data, key, off, len);
299     }
300   return 0;
301 }
302
303 static inline int
304 maybe_load_repodata(Repodata *data, Id *keyid)
305 {
306   if (data->state == REPODATA_STUB)
307     {
308       if (data->loadcallback)
309         {
310           if (keyid)
311             {
312               /* key order may change when loading */
313               int i;
314               Id name = data->keys[*keyid].name;
315               Id type = data->keys[*keyid].type;
316               data->loadcallback(data);
317               if (data->state == REPODATA_AVAILABLE)
318                 {
319                   for (i = 1; i < data->nkeys; i++)
320                     if (data->keys[i].name == name && data->keys[i].type == type)
321                       break;
322                   if (i < data->nkeys)
323                     *keyid = i;
324                   else
325                     return 0;
326                 }
327             }
328           else
329             data->loadcallback(data);
330         }
331       else
332         data->state = REPODATA_ERROR;
333     }
334   if (data->state == REPODATA_AVAILABLE)
335     return 1;
336   data->state = REPODATA_ERROR;
337   return 0;
338 }
339
340 const char *
341 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
342 {
343   Id schema;
344   Repokey *key;
345   Id id, *keyp;
346   unsigned char *dp;
347
348   if (!maybe_load_repodata(data, &keyid))
349     return 0;
350
351   dp = data->incoredata + data->incoreoffset[entry];
352   dp = data_read_id(dp, &schema);
353   /* make sure the schema of this solvable contains the key */
354   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
355     if (!*keyp)
356       return 0;
357   dp = forward_to_key(data, keyid, schema, dp);
358   key = data->keys + keyid;
359   dp = get_data(data, key, &dp);
360   if (!dp)
361     return 0;
362   if (key->type == REPOKEY_TYPE_STR)
363     return (const char *)dp;
364   if (key->type == REPOKEY_TYPE_CONSTANTID)
365     return id2str(data->repo->pool, key->size);
366   if (key->type == REPOKEY_TYPE_ID)
367     dp = data_read_id(dp, &id);
368   else
369     return 0;
370   if (data->localpool)
371     return data->spool.stringspace + data->spool.strings[id];
372   return id2str(data->repo->pool, id);
373 }
374
375 int
376 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
377 {
378   Id schema;
379   Repokey *key;
380   Id *keyp;
381   KeyValue kv;
382   unsigned char *dp;
383
384   *value = 0;
385
386   if (!maybe_load_repodata(data, &keyid))
387     return 0;
388
389   dp = data->incoredata + data->incoreoffset[entry];
390   dp = data_read_id(dp, &schema);
391   /* make sure the schema of this solvable contains the key */
392   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
393     if (!*keyp)
394       return 0;
395   dp = forward_to_key(data, keyid, schema, dp);
396   key = data->keys + keyid;
397   dp = get_data(data, key, &dp);
398   if (!dp)
399     return 0;
400   if (key->type == REPOKEY_TYPE_NUM
401       || key->type == REPOKEY_TYPE_U32
402       || key->type == REPOKEY_TYPE_CONSTANT)
403     {
404       dp = data_fetch(dp, &kv, key);
405       *value = kv.num;
406       return 1;
407     }
408   return 0;
409 }
410
411 void
412 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
413 {
414   Id schema;
415   Repokey *key;
416   Id k, keyid, *kp, *keyp;
417   unsigned char *dp, *ddp;
418   int onekey = 0;
419   int stop;
420   KeyValue kv;
421
422   if (!maybe_load_repodata(data, 0))
423     return;
424
425   dp = data->incoredata + data->incoreoffset[entry];
426   dp = data_read_id(dp, &schema);
427   keyp = data->schemadata + data->schemata[schema];
428   if (keyname)
429     {
430       /* search in a specific key */
431       for (kp = keyp; (k = *kp++) != 0; )
432         if (data->keys[k].name == keyname)
433           break;
434       if (k == 0)
435         return;
436       dp = forward_to_key(data, k, schema, dp);
437       if (!dp)
438         return;
439       keyp = kp - 1;
440       onekey = 1;
441     }
442   while ((keyid = *keyp++) != 0)
443     {
444       stop = 0;
445       key = data->keys + keyid;
446       ddp = get_data(data, key, &dp);
447       do
448         {
449           ddp = data_fetch(ddp, &kv, key);
450           if (!ddp)
451             break;
452           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
453         }
454       while (!kv.eof && !stop);
455       if (onekey || stop > SEARCH_NEXT_KEY)
456         return;
457     }
458 }
459
460 static void
461 dataiterator_newdata(Dataiterator *di)
462 {
463   Id keyname = di->keyname;
464   Repodata *data = di->data;
465   di->nextkeydp = 0;
466
467   if (data->state == REPODATA_STUB)
468     {
469       if (keyname)
470         {
471           int j;
472           for (j = 1; j < data->nkeys; j++)
473             if (keyname == data->keys[j].name)
474               break;
475           if (j == data->nkeys)
476             return;
477         }
478       /* load it */
479       if (data->loadcallback)
480         data->loadcallback(data);
481       else
482         data->state = REPODATA_ERROR;
483     }
484   if (data->state == REPODATA_ERROR)
485     return;
486
487   Id schema;
488   unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
489   dp = data_read_id(dp, &schema);
490   Id *keyp = data->schemadata + data->schemata[schema];
491   if (keyname)
492     {
493       Id k, *kp;
494       /* search in a specific key */
495       for (kp = keyp; (k = *kp++) != 0; )
496         if (data->keys[k].name == keyname)
497           break;
498       if (k == 0)
499         return;
500       dp = forward_to_key(data, k, schema, dp);
501       if (!dp)
502         return;
503       keyp = kp - 1;
504     }
505   Id keyid = *keyp++;
506   if (!keyid)
507     return;
508
509   di->data = data;
510   di->key = di->data->keys + keyid;
511   di->keyp = keyp;
512   di->dp = 0;
513
514   di->nextkeydp = dp;
515   di->dp = get_data(di->data, di->key, &di->nextkeydp);
516   di->kv.eof = 0;
517 }
518
519 void
520 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
521                   const char *match, int flags)
522 {
523   di->flags = flags;
524   if (p)
525     {
526       di->solvid = p;
527       di->flags |= __SEARCH_ONESOLVABLE;
528       di->data = repo->repodata - 1;
529       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
530         di->state = 0;
531       else
532         di->state = 1;
533     }
534   else
535     {
536       di->solvid = repo->start - 1;
537       di->data = repo->repodata + repo->nrepodata - 1;
538       di->state = 0;
539     }
540   di->match = match;
541   di->keyname = keyname;
542   static Id zeroid = 0;
543   di->keyp = &zeroid;
544   di->kv.eof = 1;
545   di->repo = repo;
546   di->idp = 0;
547 }
548
549 /* FIXME factor and merge with repo_matchvalue */
550 static int
551 dataiterator_match(Dataiterator *di, KeyValue *kv)
552 {
553   int flags = di->flags;
554
555   if ((flags & SEARCH_STRINGMASK) != 0)
556     {
557       switch (di->key->type)
558         {
559         case REPOKEY_TYPE_ID:
560         case REPOKEY_TYPE_IDARRAY:
561           if (di->data && di->data->localpool)
562             kv->str = stringpool_id2str(&di->data->spool, kv->id);
563           else
564             kv->str = id2str(di->repo->pool, kv->id);
565           break;
566         case REPOKEY_TYPE_STR:
567           break;
568         default:
569           return 0;
570         }
571       switch ((flags & SEARCH_STRINGMASK))
572         {
573           case SEARCH_SUBSTRING:
574             if (flags & SEARCH_NOCASE)
575               {
576                 if (!strcasestr(kv->str, di->match))
577                   return 0;
578               }
579             else
580               {
581                 if (!strstr(kv->str, di->match))
582                   return 0;
583               }
584             break;
585           case SEARCH_STRING:
586             if (flags & SEARCH_NOCASE)
587               {
588                 if (strcasecmp(di->match, kv->str))
589                   return 0;
590               }
591             else
592               {
593                 if (strcmp(di->match, kv->str))
594                   return 0;
595               }
596             break;
597           case SEARCH_GLOB:
598             if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
599               return 0;
600             break;
601 #if 0
602           case SEARCH_REGEX:
603             if (regexec(&di->regexp, kv->str, 0, NULL, 0))
604               return 0;
605 #endif
606           default:
607             return 0;
608         }
609     }
610   return 1;
611 }
612
613 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
614   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
615   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
616   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
617   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
618   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
619   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
620   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
621   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
622   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
623   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
624   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
625   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
626   { SOLVABLE_FRESHENS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
627   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
628 };
629
630 int
631 dataiterator_step(Dataiterator *di)
632 {
633 restart:
634   while (1)
635     {
636       if (di->state)
637         {
638           if (di->idp)
639             {
640               Id *idp = di->idp;
641               if (*idp)
642                 {
643                   di->kv.id = *idp;
644                   di->idp++;
645                   di->kv.eof = idp[1] ? 0 : 1;
646                   goto weg2;
647                 }
648               else
649                 di->idp = 0;
650             }
651           Solvable *s = di->repo->pool->solvables + di->solvid;
652           int state = di->state;
653           di->key = solvablekeys + state - 1;
654           if (di->keyname)
655             di->state = RPM_RPMDBID;
656           else
657             di->state++;
658           if (state == 1)
659             {
660               di->data = 0;
661               if (di->keyname)
662                 state = di->keyname - 1;
663             }
664           switch (state + 1)
665             {
666               case SOLVABLE_NAME:
667                 if (!s->name)
668                   continue;
669                 di->kv.id = s->name;
670                 break;
671               case SOLVABLE_ARCH:
672                 if (!s->arch)
673                   continue;
674                 di->kv.id = s->arch;
675                 break;
676               case SOLVABLE_EVR:
677                 if (!s->evr)
678                   continue;
679                 di->kv.id = s->evr;
680                 break;
681               case SOLVABLE_VENDOR:
682                 if (!s->vendor)
683                   continue;
684                 di->kv.id = s->vendor;
685                 break;
686               case SOLVABLE_PROVIDES:
687                 di->idp = s->provides
688                     ? di->repo->idarraydata + s->provides : 0;
689                 continue;
690               case SOLVABLE_OBSOLETES:
691                 di->idp = s->obsoletes
692                     ? di->repo->idarraydata + s->obsoletes : 0;
693                 continue;
694               case SOLVABLE_CONFLICTS:
695                 di->idp = s->conflicts
696                     ? di->repo->idarraydata + s->conflicts : 0;
697                 continue;
698               case SOLVABLE_REQUIRES:
699                 di->idp = s->requires
700                     ? di->repo->idarraydata + s->requires : 0;
701                 continue;
702               case SOLVABLE_RECOMMENDS:
703                 di->idp = s->recommends
704                     ? di->repo->idarraydata + s->recommends : 0;
705                 continue;
706               case SOLVABLE_SUPPLEMENTS:
707                 di->idp = s->supplements
708                     ? di->repo->idarraydata + s->supplements : 0;
709                 continue;
710               case SOLVABLE_SUGGESTS:
711                 di->idp = s->suggests
712                     ? di->repo->idarraydata + s->suggests : 0;
713                 continue;
714               case SOLVABLE_ENHANCES:
715                 di->idp = s->enhances
716                     ? di->repo->idarraydata + s->enhances : 0;
717                 continue;
718               case SOLVABLE_FRESHENS:
719                 di->idp = s->freshens
720                     ? di->repo->idarraydata + s->freshens : 0;
721                 continue;
722               case RPM_RPMDBID:
723                 if (!di->repo->rpmdbid)
724                   continue;
725                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
726                 break;
727               default:
728                 di->data = di->repo->repodata - 1;
729                 di->kv.eof = 1;
730                 di->state = 0;
731                 continue;
732             }
733         }
734       else
735         {
736           if (di->kv.eof)
737             di->dp = 0;
738           else
739             di->dp = data_fetch(di->dp, &di->kv, di->key);
740
741           while (!di->dp)
742             {
743               Id keyid;
744               if (di->keyname || !(keyid = *di->keyp++))
745                 {
746                   while (1)
747                     {
748                       Repo *repo = di->repo;
749                       Repodata *data = ++di->data;
750                       if (data >= repo->repodata + repo->nrepodata)
751                         {
752                           if (di->flags & __SEARCH_ONESOLVABLE)
753                             return 0;
754                           while (++di->solvid < repo->end)
755                             if (repo->pool->solvables[di->solvid].repo == repo)
756                               break;
757                           if (di->solvid >= repo->end)
758                             return 0;
759                           di->data = repo->repodata - 1;
760                           if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
761                             continue;
762                           static Id zeroid = 0;
763                           di->keyp = &zeroid;
764                           di->state = 1;
765                           goto restart;
766                         }
767                       if (di->solvid >= data->start && di->solvid < data->end)
768                         {
769                           dataiterator_newdata(di);
770                           if (di->nextkeydp)
771                             break;
772                         }
773                     }
774                 }
775               else
776                 {
777                   di->key = di->data->keys + keyid;
778                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
779                 }
780               di->dp = data_fetch(di->dp, &di->kv, di->key);
781             }
782         }
783 weg2:
784       if (!di->match
785           || dataiterator_match(di, &di->kv))
786         break;
787     }
788   return 1;
789 }
790
791 void
792 repodata_init(Repodata *data, Repo *repo, int localpool)
793 {
794   memset(data, 0, sizeof (*data));
795   data->repo = repo;
796   data->localpool = localpool;
797   if (localpool)
798     stringpool_init_empty(&data->spool);
799   data->keys = sat_calloc(1, sizeof(Repokey));
800   data->nkeys = 1;
801   data->schemata = sat_calloc(1, sizeof(Id));
802   data->schemadata = sat_calloc(1, sizeof(Id));
803   data->nschemata = 1;
804   data->schemadatalen = 1;
805   data->start = repo->start;
806   data->end = repo->end;
807   data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
808   data->pagefd = -1;
809 }
810
811 /* extend repodata so that it includes solvables p */
812 void
813 repodata_extend(Repodata *data, Id p)
814 {
815   if (data->start == data->end)
816     data->start = data->end = p;
817   if (p >= data->end)
818     {
819       int old = data->end - data->start;
820       int new = p - data->end + 1;
821       if (data->attrs)
822         {
823           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
824           memset(data->attrs + old, 0, new * sizeof(Id *));
825         }
826       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
827       memset(data->incoreoffset + old, 0, new * sizeof(Id));
828       data->end = p + 1;
829     }
830   if (p < data->start)
831     {
832       int old = data->end - data->start;
833       int new = data->start - p;
834       if (data->attrs)
835         {
836           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
837           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
838           memset(data->attrs, 0, new * sizeof(Id *));
839         }
840       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
841       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
842       memset(data->incoreoffset, 0, new * sizeof(Id));
843       data->start = p;
844     }
845 }
846
847 void
848 repodata_extend_block(Repodata *data, Id start, Id num)
849 {
850   if (!num)
851     return;
852   if (!data->incoreoffset)
853     {
854       data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
855       memset(data->incoreoffset, 0, num * sizeof(Id));
856       data->start = start;
857       data->end = start + num;
858       return;
859     }
860   repodata_extend(data, start);
861   if (num > 1)
862     repodata_extend(data, start + num - 1);
863 }
864
865 #define REPODATA_ATTRS_BLOCK 63
866 #define REPODATA_ATTRDATA_BLOCK 1023
867 #define REPODATA_ATTRIDDATA_BLOCK 63
868
869 static void
870 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
871 {
872   Id *pp;
873   int i;
874   if (!data->attrs)
875     {
876       data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
877       memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
878     }
879   i = 0;
880   if (data->attrs[entry])
881     {
882       for (pp = data->attrs[entry]; *pp; pp += 2)
883         /* Determine equality based on the name only, allows us to change
884            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
885         if (data->keys[*pp].name == data->keys[keyid].name)
886           break;
887       if (*pp)
888         {
889           if (overwrite)
890             {
891               pp[0] = keyid;
892               pp[1] = val;
893             }
894           return;
895         }
896       i = pp - data->attrs[entry];
897     }
898   data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
899   pp = data->attrs[entry] + i;
900   *pp++ = keyid;
901   *pp++ = val;
902   *pp = 0;
903 }
904
905 void
906 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
907 {
908   Id keyid;
909
910   /* find key in keys */
911   for (keyid = 1; keyid < data->nkeys; keyid++)
912     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
913       {
914         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
915           continue;
916         break;
917       }
918   if (keyid == data->nkeys)
919     {
920       /* allocate new key */
921       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
922       data->keys[data->nkeys++] = *key;
923       if (data->verticaloffset)
924         {
925           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
926           data->verticaloffset[data->nkeys - 1] = 0;
927         }
928     }
929   repodata_insert_keyid(data, entry, keyid, val, 1);
930 }
931
932 void
933 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
934 {
935   Repokey key;
936   key.name = keyname;
937   key.type = REPOKEY_TYPE_ID;
938   key.size = 0;
939   key.storage = KEY_STORAGE_INCORE;
940   repodata_set(data, entry, &key, id);
941 }
942
943 void
944 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
945 {
946   Repokey key;
947   key.name = keyname;
948   key.type = REPOKEY_TYPE_NUM;
949   key.size = 0;
950   key.storage = KEY_STORAGE_INCORE;
951   repodata_set(data, entry, &key, num);
952 }
953
954 void
955 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
956 {
957   Repokey key;
958   Id id;
959   if (data->localpool)
960     id = stringpool_str2id(&data->spool, str, 1);
961   else
962     id = str2id(data->repo->pool, str, 1);
963   key.name = keyname;
964   key.type = REPOKEY_TYPE_ID;
965   key.size = 0;
966   key.storage = KEY_STORAGE_INCORE;
967   repodata_set(data, entry, &key, id);
968 }
969
970 void
971 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
972 {
973   Repokey key;
974   key.name = keyname;
975   key.type = REPOKEY_TYPE_CONSTANT;
976   key.size = constant;
977   key.storage = KEY_STORAGE_INCORE;
978   repodata_set(data, entry, &key, 0);
979 }
980
981 void
982 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
983 {
984   Repokey key;
985   key.name = keyname;
986   key.type = REPOKEY_TYPE_CONSTANTID;
987   key.size = id;
988   key.storage = KEY_STORAGE_INCORE;
989   repodata_set(data, entry, &key, 0);
990 }
991
992 void
993 repodata_set_void(Repodata *data, Id entry, Id keyname)
994 {
995   Repokey key;
996   key.name = keyname;
997   key.type = REPOKEY_TYPE_VOID;
998   key.size = 0;
999   key.storage = KEY_STORAGE_INCORE;
1000   repodata_set(data, entry, &key, 0);
1001 }
1002
1003 void
1004 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1005 {
1006   Repokey key;
1007   int l;
1008
1009   l = strlen(str) + 1;
1010   key.name = keyname;
1011   key.type = REPOKEY_TYPE_STR;
1012   key.size = 0;
1013   key.storage = KEY_STORAGE_INCORE;
1014   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1015   memcpy(data->attrdata + data->attrdatalen, str, l);
1016   repodata_set(data, entry, &key, data->attrdatalen);
1017   data->attrdatalen += l;
1018 }
1019
1020 void
1021 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1022 {
1023   Id *ida, *pp;
1024   Repokey key;
1025
1026 #if 0
1027 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1028 #endif
1029   if (data->attrs && data->attrs[entry])
1030     {
1031       for (pp = data->attrs[entry]; *pp; pp += 2)
1032         if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRNUMNUMARRAY)
1033           break;
1034       if (*pp)
1035         {
1036           int oldsize = 0;
1037           for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1038             oldsize += 3;
1039           if (ida + 1 == data->attriddata + data->attriddatalen)
1040             {
1041               /* this was the last entry, just append it */
1042               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1043               data->attriddatalen--;    /* overwrite terminating 0  */
1044             }
1045           else
1046             {
1047               /* too bad. move to back. */
1048               data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1049               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1050               pp[1] = data->attriddatalen;
1051               data->attriddatalen += oldsize;
1052             }
1053           data->attriddata[data->attriddatalen++] = dir;
1054           data->attriddata[data->attriddatalen++] = num;
1055           data->attriddata[data->attriddatalen++] = num2;
1056           data->attriddata[data->attriddatalen++] = 0;
1057           return;
1058         }
1059     }
1060   key.name = keyname;
1061   key.type = REPOKEY_TYPE_DIRNUMNUMARRAY;
1062   key.size = 0;
1063   key.storage = KEY_STORAGE_INCORE;
1064   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1065   repodata_set(data, entry, &key, data->attriddatalen);
1066   data->attriddata[data->attriddatalen++] = dir;
1067   data->attriddata[data->attriddatalen++] = num;
1068   data->attriddata[data->attriddatalen++] = num2;
1069   data->attriddata[data->attriddatalen++] = 0;
1070 }
1071
1072 void
1073 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1074 {
1075   Id *ida, *pp, stroff;
1076   Repokey key;
1077   int l;
1078
1079   l = strlen(str) + 1;
1080   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1081   memcpy(data->attrdata + data->attrdatalen, str, l);
1082   stroff = data->attrdatalen;
1083   data->attrdatalen += l;
1084
1085 #if 0
1086 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1087 #endif
1088   if (data->attrs && data->attrs[entry])
1089     {
1090       for (pp = data->attrs[entry]; *pp; pp += 2)
1091         if (data->keys[*pp].name == keyname && data->keys[*pp].type == REPOKEY_TYPE_DIRSTRARRAY)
1092           break;
1093       if (*pp)
1094         {
1095           int oldsize = 0;
1096           for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1097             oldsize += 2;
1098           if (ida + 1 == data->attriddata + data->attriddatalen)
1099             {
1100               /* this was the last entry, just append it */
1101               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1102               data->attriddatalen--;    /* overwrite terminating 0  */
1103             }
1104           else
1105             {
1106               /* too bad. move to back. */
1107               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1108               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1109               pp[1] = data->attriddatalen;
1110               data->attriddatalen += oldsize;
1111             }
1112           data->attriddata[data->attriddatalen++] = dir;
1113           data->attriddata[data->attriddatalen++] = stroff;
1114           data->attriddata[data->attriddatalen++] = 0;
1115           return;
1116         }
1117     }
1118   key.name = keyname;
1119   key.type = REPOKEY_TYPE_DIRSTRARRAY;
1120   key.size = 0;
1121   key.storage = KEY_STORAGE_INCORE;
1122   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1123   repodata_set(data, entry, &key, data->attriddatalen);
1124   data->attriddata[data->attriddatalen++] = dir;
1125   data->attriddata[data->attriddatalen++] = stroff;
1126   data->attriddata[data->attriddatalen++] = 0;
1127 }
1128
1129 void
1130 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1131 {
1132   Id *keyp;
1133   for (keyp = data->attrs[src]; *keyp; keyp += 2)
1134     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1135 }
1136
1137 /*********************************/
1138
1139 /* unify with repo_write! */
1140
1141 #define EXTDATA_BLOCK 1023
1142 #define SCHEMATA_BLOCK 31
1143 #define SCHEMATADATA_BLOCK 255
1144
1145 struct extdata {
1146   unsigned char *buf;
1147   int len;
1148 };
1149
1150 static void
1151 data_addid(struct extdata *xd, Id x)
1152 {
1153   unsigned char *dp;
1154   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1155   dp = xd->buf + xd->len;
1156
1157   if (x >= (1 << 14))
1158     {
1159       if (x >= (1 << 28))
1160         *dp++ = (x >> 28) | 128;
1161       if (x >= (1 << 21))
1162         *dp++ = (x >> 21) | 128;
1163       *dp++ = (x >> 14) | 128;
1164     }
1165   if (x >= (1 << 7))
1166     *dp++ = (x >> 7) | 128;
1167   *dp++ = x & 127;
1168   xd->len = dp - xd->buf;
1169 }
1170
1171 static void
1172 data_addideof(struct extdata *xd, Id x, int eof)
1173 {
1174   if (x >= 64)
1175     x = (x & 63) | ((x & ~63) << 1);
1176   data_addid(xd, (eof ? x: x | 64));
1177 }
1178
1179 static void
1180 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1181 {
1182   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1183   memcpy(xd->buf + xd->len, blob, len);
1184   xd->len += len;
1185 }
1186
1187 /*********************************/
1188
1189 static void
1190 addschema_prepare(Repodata *data, Id *schematacache)
1191 {
1192   int h, len, i;
1193   Id *sp;
1194
1195   memset(schematacache, 0, 256 * sizeof(Id));
1196   for (i = 0; i < data->nschemata; i++)
1197     {
1198       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1199         h = h * 7 + *sp++;
1200       h &= 255;
1201       schematacache[h] = i + 1;
1202     }
1203   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1204   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1205 }
1206
1207 static Id
1208 addschema(Repodata *data, Id *schema, Id *schematacache)
1209 {
1210   int h, len; 
1211   Id *sp, cid; 
1212
1213   for (sp = schema, len = 0, h = 0; *sp; len++)
1214     h = h * 7 + *sp++;
1215   h &= 255; 
1216   len++;
1217
1218   cid = schematacache[h];
1219   if (cid)
1220     {    
1221       cid--;
1222       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1223         return cid;
1224       /* cache conflict */
1225       for (cid = 0; cid < data->nschemata; cid++)
1226         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1227           return cid;
1228     }
1229   /* a new one. make room. */
1230   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1231   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1232   /* add schema */
1233   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1234   data->schemata[data->nschemata] = data->schemadatalen;
1235   data->schemadatalen += len;
1236   schematacache[h] = data->nschemata + 1;
1237 #if 0
1238 fprintf(stderr, "addschema: new schema\n");
1239 #endif
1240   return data->nschemata++; 
1241 }
1242
1243
1244 void
1245 repodata_internalize(Repodata *data)
1246 {
1247   Repokey *key;
1248   Id id, entry, nentry, *ida;
1249   Id schematacache[256];
1250   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1251   unsigned char *dp, *ndp;
1252   int newschema, oldcount;
1253   struct extdata newincore;
1254   struct extdata newvincore;
1255
1256   if (!data->attrs)
1257     return;
1258
1259   newvincore.buf = data->vincore;
1260   newvincore.len = data->vincorelen;
1261
1262   schema = sat_malloc2(data->nkeys, sizeof(Id));
1263   seen = sat_malloc2(data->nkeys, sizeof(Id));
1264
1265   /* Merge the data already existing (in data->schemata, ->incoredata and
1266      friends) with the new attributes in data->attrs[].  */
1267   nentry = data->end - data->start;
1268   addschema_prepare(data, schematacache);
1269   memset(&newincore, 0, sizeof(newincore));
1270   for (entry = 0; entry < nentry; entry++)
1271     {
1272       memset(seen, 0, data->nkeys * sizeof(Id));
1273       sp = schema;
1274       dp = data->incoredata + data->incoreoffset[entry];
1275       if (data->incoredata)
1276         dp = data_read_id(dp, &oldschema);
1277       else
1278         oldschema = 0;
1279 #if 0
1280 fprintf(stderr, "oldschema %d\n", oldschema);
1281 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1282 fprintf(stderr, "schemadata %p\n", data->schemadata);
1283 #endif
1284       /* seen: -1: old data  0: skipped  >0: id + 1 */
1285       newschema = 0;
1286       oldcount = 0;
1287       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1288         {
1289           if (seen[*keyp])
1290             {
1291               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1292               exit(1);
1293             }
1294           seen[*keyp] = -1;
1295           *sp++ = *keyp;
1296           oldcount++;
1297         }
1298       if (data->attrs[entry])
1299         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1300           {
1301             if (!seen[*keyp])
1302               {
1303                 newschema = 1;
1304                 *sp++ = *keyp;
1305               }
1306             seen[*keyp] = keyp[1] + 1;
1307           }
1308       *sp++ = 0;
1309       if (newschema)
1310         /* Ideally we'd like to sort the new schema here, to ensure
1311            schema equality independend of the ordering.  We can't do that
1312            yet.  For once see below (old ids need to come before new ids).
1313            An additional difficulty is that we also need to move
1314            the values with the keys.  */
1315         schemaid = addschema(data, schema, schematacache);
1316       else
1317         schemaid = oldschema;
1318
1319
1320       /* Now create data blob.  We walk through the (possibly new) schema
1321          and either copy over old data, or insert the new.  */
1322       /* XXX Here we rely on the fact that the (new) schema has the form
1323          o1 o2 o3 o4 ... | n1 n2 n3 ...
1324          (oX being the old keyids (possibly overwritten), and nX being
1325           the new keyids).  This rules out sorting the keyids in order
1326          to ensure a small schema count.  */
1327       data->incoreoffset[entry] = newincore.len;
1328       data_addid(&newincore, schemaid);
1329       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1330         {
1331           key = data->keys + *keyp;
1332           ndp = dp;
1333           if (oldcount)
1334             {
1335               /* Skip the data associated with this old key.  */
1336               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1337                 {
1338                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1339                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1340                 }
1341               else if (key->storage == KEY_STORAGE_INCORE)
1342                 ndp = data_skip(dp, key->type);
1343               oldcount--;
1344             }
1345           if (seen[*keyp] == -1)
1346             {
1347               /* If this key was an old one _and_ was not overwritten with
1348                  a different value copy over the old value (we skipped it
1349                  above).  */
1350               if (dp != ndp)
1351                 data_addblob(&newincore, dp, ndp - dp);
1352               seen[*keyp] = 0;
1353             }
1354           else if (seen[*keyp])
1355             {
1356               /* Otherwise we have a new value.  Parse it into the internal
1357                  form.  */
1358               struct extdata *xd;
1359               unsigned int oldvincorelen = 0;
1360
1361               xd = &newincore;
1362               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1363                 {
1364                   xd = &newvincore;
1365                   oldvincorelen = xd->len;
1366                 }
1367               id = seen[*keyp] - 1;
1368               switch (key->type)
1369                 {
1370                 case REPOKEY_TYPE_VOID:
1371                 case REPOKEY_TYPE_CONSTANT:
1372                 case REPOKEY_TYPE_CONSTANTID:
1373                   break;
1374                 case REPOKEY_TYPE_STR:
1375                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1376                   break;
1377                 case REPOKEY_TYPE_ID:
1378                 case REPOKEY_TYPE_NUM:
1379                 case REPOKEY_TYPE_DIR:
1380                   data_addid(xd, id);
1381                   break;
1382                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1383                   for (ida = data->attriddata + id; *ida; ida += 3)
1384                     {
1385                       data_addid(xd, ida[0]);
1386                       data_addid(xd, ida[1]);
1387                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1388                     }
1389                   break;
1390                 case REPOKEY_TYPE_DIRSTRARRAY:
1391                   for (ida = data->attriddata + id; *ida; ida += 2)
1392                     {
1393                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1394                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1395                     }
1396                   break;
1397                 default:
1398                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1399                   exit(1);
1400                 }
1401               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1402                 {
1403                   /* put offset/len in incore */
1404                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1405                   oldvincorelen = xd->len - oldvincorelen;
1406                   data_addid(&newincore, oldvincorelen);
1407                 }
1408             }
1409           dp = ndp;
1410         }
1411       if (data->attrs[entry])
1412         sat_free(data->attrs[entry]);
1413     }
1414   sat_free(schema);
1415   sat_free(seen);
1416
1417   sat_free(data->incoredata);
1418   data->incoredata = newincore.buf;
1419   data->incoredatalen = newincore.len;
1420   data->incoredatafree = 0;
1421   
1422   sat_free(data->vincore);
1423   data->vincore = newvincore.buf;
1424   data->vincorelen = newvincore.len;
1425
1426   data->attrs = sat_free(data->attrs);
1427   data->attrdata = sat_free(data->attrdata);
1428   data->attriddata = sat_free(data->attriddata);
1429   data->attrdatalen = 0;
1430   data->attriddatalen = 0;
1431 }
1432
1433 Id
1434 repodata_str2dir(Repodata *data, const char *dir, int create)
1435 {
1436   Id id, parent;
1437   const char *dire;
1438
1439   parent = 0;
1440   while (*dir == '/' && dir[1] == '/')
1441     dir++;
1442   while (*dir)
1443     {
1444       dire = strchrnul(dir, '/');
1445       if (data->localpool)
1446         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1447       else
1448         id = strn2id(data->repo->pool, dir, dire - dir, create);
1449       if (!id)
1450         return 0;
1451       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1452       if (!parent)
1453         return 0;
1454       if (!*dire)
1455         break;
1456       dir = dire + 1;
1457       while (*dir == '/')
1458         dir++;
1459     }
1460   return parent;
1461 }
1462
1463 unsigned int
1464 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1465 {
1466   return compress_buf(page, len, cpage, max);
1467 }
1468
1469 #define SOLV_ERROR_EOF              3
1470
1471 static inline unsigned int
1472 read_u32(FILE *fp)
1473 {
1474   int c, i;
1475   unsigned int x = 0; 
1476
1477   for (i = 0; i < 4; i++) 
1478     {    
1479       c = getc(fp);
1480       if (c == EOF) 
1481         return 0;
1482       x = (x << 8) | c; 
1483     }    
1484   return x;
1485 }
1486
1487 #define SOLV_ERROR_EOF          3
1488 #define SOLV_ERROR_CORRUPT      6
1489
1490 /* Try to either setup on-demand paging (using FP as backing
1491    file), or in case that doesn't work (FP not seekable) slurps in
1492    all pages and deactivates paging.  */
1493 void
1494 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1495 {
1496   FILE *fp = data->fp;
1497   unsigned int npages;
1498   unsigned int i;
1499   unsigned int can_seek;
1500   long cur_file_ofs;
1501   unsigned char buf[BLOB_PAGESIZE];
1502
1503   if (pagesz != BLOB_PAGESIZE)
1504     {
1505       /* We could handle this by slurping in everything.  */
1506       data->error = SOLV_ERROR_CORRUPT;
1507       return;
1508     }
1509   can_seek = 1;
1510   if ((cur_file_ofs = ftell(fp)) < 0)
1511     can_seek = 0;
1512   clearerr(fp);
1513   if (can_seek)
1514     data->pagefd = dup(fileno(fp));
1515   if (data->pagefd == -1)
1516     can_seek = 0;
1517
1518 #ifdef DEBUG_PAGING
1519   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1520 #endif
1521   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1522
1523   data->num_pages = npages;
1524   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1525
1526   /* If we can't seek on our input we have to slurp in everything.  */
1527   if (!can_seek)
1528     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1529   for (i = 0; i < npages; i++)
1530     {
1531       unsigned int in_len = read_u32(fp);
1532       unsigned int compressed = in_len & 1;
1533       Attrblobpage *p = data->pages + i;
1534       in_len >>= 1;
1535 #ifdef DEBUG_PAGING
1536       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1537                i, in_len, compressed ? "" : "not ");
1538 #endif
1539       if (can_seek)
1540         {
1541           cur_file_ofs += 4;
1542           p->mapped_at = -1;
1543           p->file_offset = cur_file_ofs;
1544           p->file_size = in_len * 2 + compressed;
1545           if (fseek(fp, in_len, SEEK_CUR) < 0)
1546             {
1547               perror ("fseek");
1548               fprintf (stderr, "can't seek after we thought we can\n");
1549               /* We can't fall back to non-seeking behaviour as we already
1550                  read over some data pages without storing them away.  */
1551               data->error = SOLV_ERROR_EOF;
1552               close(data->pagefd);
1553               data->pagefd = -1;
1554               return;
1555             }
1556           cur_file_ofs += in_len;
1557         }
1558       else
1559         {
1560           unsigned int out_len;
1561           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1562           p->mapped_at = i * BLOB_PAGESIZE;
1563           p->file_offset = 0;
1564           p->file_size = 0;
1565           /* We can't seek, so suck everything in.  */
1566           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1567             {
1568               perror("fread");
1569               data->error = SOLV_ERROR_EOF;
1570               return;
1571             }
1572           if (compressed)
1573             {
1574               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1575               if (out_len != BLOB_PAGESIZE && i < npages - 1)
1576                 {
1577                   data->error = SOLV_ERROR_CORRUPT;
1578                   return;
1579                 }
1580             }
1581         }
1582     }
1583 }
1584
1585 /*
1586 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1587 */