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