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