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