- rename str_lang to str_poollang
[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 const unsigned char *
454 repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyid, Id *typep)
455 {
456   Id schema;
457   Id *keyp;
458   Repokey *key;
459   unsigned char *dp;
460
461   if (!maybe_load_repodata(data, &keyid))
462     return 0;
463   dp = data->incoredata + data->incoreoffset[entry];
464   dp = data_read_id(dp, &schema);
465   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
466     if (!*keyp)
467       return 0;
468   dp = forward_to_key(data, keyid, schema, dp);
469   key = data->keys + keyid;
470   *typep = key->type;
471   return get_data(data, key, &dp);
472 }
473
474 void
475 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
476 {
477   Id schema;
478   Repokey *key;
479   Id k, keyid, *kp, *keyp;
480   unsigned char *dp, *ddp;
481   int onekey = 0;
482   int stop;
483   KeyValue kv;
484
485   if (!maybe_load_repodata(data, 0))
486     return;
487
488   dp = data->incoredata + data->incoreoffset[entry];
489   dp = data_read_id(dp, &schema);
490   keyp = data->schemadata + data->schemata[schema];
491   if (keyname)
492     {
493       /* search in a specific key */
494       for (kp = keyp; (k = *kp++) != 0; )
495         if (data->keys[k].name == keyname)
496           break;
497       if (k == 0)
498         return;
499       dp = forward_to_key(data, k, schema, dp);
500       if (!dp)
501         return;
502       keyp = kp - 1;
503       onekey = 1;
504     }
505   while ((keyid = *keyp++) != 0)
506     {
507       stop = 0;
508       key = data->keys + keyid;
509       ddp = get_data(data, key, &dp);
510       do
511         {
512           ddp = data_fetch(ddp, &kv, key);
513           if (!ddp)
514             break;
515           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
516         }
517       while (!kv.eof && !stop);
518       if (onekey || stop > SEARCH_NEXT_KEY)
519         return;
520     }
521 }
522
523 static void
524 dataiterator_newdata(Dataiterator *di)
525 {
526   Id keyname = di->keyname;
527   Repodata *data = di->data;
528   di->nextkeydp = 0;
529
530   if (data->state == REPODATA_STUB)
531     {
532       if (keyname)
533         {
534           int j;
535           for (j = 1; j < data->nkeys; j++)
536             if (keyname == data->keys[j].name)
537               break;
538           if (j == data->nkeys)
539             return;
540         }
541       /* load it */
542       if (data->loadcallback)
543         data->loadcallback(data);
544       else
545         data->state = REPODATA_ERROR;
546     }
547   if (data->state == REPODATA_ERROR)
548     return;
549
550   Id schema;
551   unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
552   dp = data_read_id(dp, &schema);
553   Id *keyp = data->schemadata + data->schemata[schema];
554   if (keyname)
555     {
556       Id k, *kp;
557       /* search in a specific key */
558       for (kp = keyp; (k = *kp++) != 0; )
559         if (data->keys[k].name == keyname)
560           break;
561       if (k == 0)
562         return;
563       dp = forward_to_key(data, k, schema, dp);
564       if (!dp)
565         return;
566       keyp = kp - 1;
567     }
568   Id keyid = *keyp++;
569   if (!keyid)
570     return;
571
572   di->data = data;
573   di->key = di->data->keys + keyid;
574   di->keyp = keyp;
575   di->dp = 0;
576
577   di->nextkeydp = dp;
578   di->dp = get_data(di->data, di->key, &di->nextkeydp);
579   di->kv.eof = 0;
580 }
581
582 void
583 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
584                   const char *match, int flags)
585 {
586   di->flags = flags;
587   if (p)
588     {
589       di->solvid = p;
590       di->flags |= __SEARCH_ONESOLVABLE;
591       di->data = repo->repodata - 1;
592       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
593         di->state = 0;
594       else
595         di->state = 1;
596     }
597   else
598     {
599       di->solvid = repo->start - 1;
600       di->data = repo->repodata + repo->nrepodata - 1;
601       di->state = 0;
602     }
603   di->match = match;
604   di->keyname = keyname;
605   static Id zeroid = 0;
606   di->keyp = &zeroid;
607   di->kv.eof = 1;
608   di->repo = repo;
609   di->idp = 0;
610 }
611
612 /* FIXME factor and merge with repo_matchvalue */
613 static int
614 dataiterator_match(Dataiterator *di, KeyValue *kv)
615 {
616   int flags = di->flags;
617
618   if ((flags & SEARCH_STRINGMASK) != 0)
619     {
620       switch (di->key->type)
621         {
622         case REPOKEY_TYPE_ID:
623         case REPOKEY_TYPE_IDARRAY:
624           if (di->data && di->data->localpool)
625             kv->str = stringpool_id2str(&di->data->spool, kv->id);
626           else
627             kv->str = id2str(di->repo->pool, kv->id);
628           break;
629         case REPOKEY_TYPE_STR:
630           break;
631         default:
632           return 0;
633         }
634       switch ((flags & SEARCH_STRINGMASK))
635         {
636           case SEARCH_SUBSTRING:
637             if (flags & SEARCH_NOCASE)
638               {
639                 if (!strcasestr(kv->str, di->match))
640                   return 0;
641               }
642             else
643               {
644                 if (!strstr(kv->str, di->match))
645                   return 0;
646               }
647             break;
648           case SEARCH_STRING:
649             if (flags & SEARCH_NOCASE)
650               {
651                 if (strcasecmp(di->match, kv->str))
652                   return 0;
653               }
654             else
655               {
656                 if (strcmp(di->match, kv->str))
657                   return 0;
658               }
659             break;
660           case SEARCH_GLOB:
661             if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
662               return 0;
663             break;
664 #if 0
665           case SEARCH_REGEX:
666             if (regexec(&di->regexp, kv->str, 0, NULL, 0))
667               return 0;
668 #endif
669           default:
670             return 0;
671         }
672     }
673   return 1;
674 }
675
676 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
677   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
678   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
679   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
680   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
681   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
682   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
683   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
684   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
685   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
686   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
687   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
688   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
689   { SOLVABLE_FRESHENS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
690   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
691 };
692
693 int
694 dataiterator_step(Dataiterator *di)
695 {
696 restart:
697   while (1)
698     {
699       if (di->state)
700         {
701           if (di->idp)
702             {
703               Id *idp = di->idp;
704               if (*idp)
705                 {
706                   di->kv.id = *idp;
707                   di->idp++;
708                   di->kv.eof = idp[1] ? 0 : 1;
709                   goto weg2;
710                 }
711               else
712                 di->idp = 0;
713             }
714           Solvable *s = di->repo->pool->solvables + di->solvid;
715           int state = di->state;
716           di->key = solvablekeys + state - 1;
717           if (di->keyname)
718             di->state = RPM_RPMDBID;
719           else
720             di->state++;
721           if (state == 1)
722             {
723               di->data = 0;
724               if (di->keyname)
725                 state = di->keyname - 1;
726             }
727           switch (state + 1)
728             {
729               case SOLVABLE_NAME:
730                 if (!s->name)
731                   continue;
732                 di->kv.id = s->name;
733                 break;
734               case SOLVABLE_ARCH:
735                 if (!s->arch)
736                   continue;
737                 di->kv.id = s->arch;
738                 break;
739               case SOLVABLE_EVR:
740                 if (!s->evr)
741                   continue;
742                 di->kv.id = s->evr;
743                 break;
744               case SOLVABLE_VENDOR:
745                 if (!s->vendor)
746                   continue;
747                 di->kv.id = s->vendor;
748                 break;
749               case SOLVABLE_PROVIDES:
750                 di->idp = s->provides
751                     ? di->repo->idarraydata + s->provides : 0;
752                 continue;
753               case SOLVABLE_OBSOLETES:
754                 di->idp = s->obsoletes
755                     ? di->repo->idarraydata + s->obsoletes : 0;
756                 continue;
757               case SOLVABLE_CONFLICTS:
758                 di->idp = s->conflicts
759                     ? di->repo->idarraydata + s->conflicts : 0;
760                 continue;
761               case SOLVABLE_REQUIRES:
762                 di->idp = s->requires
763                     ? di->repo->idarraydata + s->requires : 0;
764                 continue;
765               case SOLVABLE_RECOMMENDS:
766                 di->idp = s->recommends
767                     ? di->repo->idarraydata + s->recommends : 0;
768                 continue;
769               case SOLVABLE_SUPPLEMENTS:
770                 di->idp = s->supplements
771                     ? di->repo->idarraydata + s->supplements : 0;
772                 continue;
773               case SOLVABLE_SUGGESTS:
774                 di->idp = s->suggests
775                     ? di->repo->idarraydata + s->suggests : 0;
776                 continue;
777               case SOLVABLE_ENHANCES:
778                 di->idp = s->enhances
779                     ? di->repo->idarraydata + s->enhances : 0;
780                 continue;
781               case SOLVABLE_FRESHENS:
782                 di->idp = s->freshens
783                     ? di->repo->idarraydata + s->freshens : 0;
784                 continue;
785               case RPM_RPMDBID:
786                 if (!di->repo->rpmdbid)
787                   continue;
788                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
789                 break;
790               default:
791                 di->data = di->repo->repodata - 1;
792                 di->kv.eof = 1;
793                 di->state = 0;
794                 continue;
795             }
796         }
797       else
798         {
799           if (di->kv.eof)
800             di->dp = 0;
801           else
802             di->dp = data_fetch(di->dp, &di->kv, di->key);
803
804           while (!di->dp)
805             {
806               Id keyid;
807               if (di->keyname || !(keyid = *di->keyp++))
808                 {
809                   while (1)
810                     {
811                       Repo *repo = di->repo;
812                       Repodata *data = ++di->data;
813                       if (data >= repo->repodata + repo->nrepodata)
814                         {
815                           if (di->flags & __SEARCH_ONESOLVABLE)
816                             return 0;
817                           while (++di->solvid < repo->end)
818                             if (repo->pool->solvables[di->solvid].repo == repo)
819                               break;
820                           if (di->solvid >= repo->end)
821                             return 0;
822                           di->data = repo->repodata - 1;
823                           if (di->flags & SEARCH_NO_STORAGE_SOLVABLE)
824                             continue;
825                           static Id zeroid = 0;
826                           di->keyp = &zeroid;
827                           di->state = 1;
828                           goto restart;
829                         }
830                       if (di->solvid >= data->start && di->solvid < data->end)
831                         {
832                           dataiterator_newdata(di);
833                           if (di->nextkeydp)
834                             break;
835                         }
836                     }
837                 }
838               else
839                 {
840                   di->key = di->data->keys + keyid;
841                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
842                 }
843               di->dp = data_fetch(di->dp, &di->kv, di->key);
844             }
845         }
846 weg2:
847       if (!di->match
848           || dataiterator_match(di, &di->kv))
849         break;
850     }
851   return 1;
852 }
853
854 /* extend repodata so that it includes solvables p */
855 void
856 repodata_extend(Repodata *data, Id p)
857 {
858   if (data->start == data->end)
859     data->start = data->end = p;
860   if (p >= data->end)
861     {
862       int old = data->end - data->start;
863       int new = p - data->end + 1;
864       if (data->attrs)
865         {
866           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
867           memset(data->attrs + old, 0, new * sizeof(Id *));
868         }
869       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
870       memset(data->incoreoffset + old, 0, new * sizeof(Id));
871       data->end = p + 1;
872     }
873   if (p < data->start)
874     {
875       int old = data->end - data->start;
876       int new = data->start - p;
877       if (data->attrs)
878         {
879           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
880           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
881           memset(data->attrs, 0, new * sizeof(Id *));
882         }
883       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
884       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
885       memset(data->incoreoffset, 0, new * sizeof(Id));
886       data->start = p;
887     }
888 }
889
890 void
891 repodata_extend_block(Repodata *data, Id start, Id num)
892 {
893   if (!num)
894     return;
895   if (!data->incoreoffset)
896     {
897       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
898       data->start = start;
899       data->end = start + num;
900       return;
901     }
902   repodata_extend(data, start);
903   if (num > 1)
904     repodata_extend(data, start + num - 1);
905 }
906
907 /**********************************************************************/
908
909 #define REPODATA_ATTRS_BLOCK 63
910 #define REPODATA_ATTRDATA_BLOCK 1023
911 #define REPODATA_ATTRIDDATA_BLOCK 63
912
913 static void
914 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
915 {
916   Id *pp;
917   int i;
918   if (!data->attrs)
919     {
920       data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
921                                      REPODATA_BLOCK);
922     }
923   i = 0;
924   if (data->attrs[entry])
925     {
926       for (pp = data->attrs[entry]; *pp; pp += 2)
927         /* Determine equality based on the name only, allows us to change
928            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
929         if (data->keys[*pp].name == data->keys[keyid].name)
930           break;
931       if (*pp)
932         {
933           if (overwrite)
934             {
935               pp[0] = keyid;
936               pp[1] = val;
937             }
938           return;
939         }
940       i = pp - data->attrs[entry];
941     }
942   data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
943   pp = data->attrs[entry] + i;
944   *pp++ = keyid;
945   *pp++ = val;
946   *pp = 0;
947 }
948
949 void
950 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
951 {
952   Id keyid;
953
954   /* find key in keys */
955   for (keyid = 1; keyid < data->nkeys; keyid++)
956     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
957       {
958         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
959           continue;
960         break;
961       }
962   if (keyid == data->nkeys)
963     {
964       /* allocate new key */
965       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
966       data->keys[data->nkeys++] = *key;
967       if (data->verticaloffset)
968         {
969           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
970           data->verticaloffset[data->nkeys - 1] = 0;
971         }
972     }
973   repodata_insert_keyid(data, entry, keyid, val, 1);
974 }
975
976 void
977 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
978 {
979   Repokey key;
980   key.name = keyname;
981   key.type = REPOKEY_TYPE_ID;
982   key.size = 0;
983   key.storage = KEY_STORAGE_INCORE;
984   repodata_set(data, entry, &key, id);
985 }
986
987 void
988 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
989 {
990   Repokey key;
991   key.name = keyname;
992   key.type = REPOKEY_TYPE_NUM;
993   key.size = 0;
994   key.storage = KEY_STORAGE_INCORE;
995   repodata_set(data, entry, &key, num);
996 }
997
998 void
999 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
1000 {
1001   Repokey key;
1002   Id id;
1003   if (data->localpool)
1004     id = stringpool_str2id(&data->spool, str, 1);
1005   else
1006     id = str2id(data->repo->pool, str, 1);
1007   key.name = keyname;
1008   key.type = REPOKEY_TYPE_ID;
1009   key.size = 0;
1010   key.storage = KEY_STORAGE_INCORE;
1011   repodata_set(data, entry, &key, id);
1012 }
1013
1014 void
1015 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
1016 {
1017   Repokey key;
1018   key.name = keyname;
1019   key.type = REPOKEY_TYPE_CONSTANT;
1020   key.size = constant;
1021   key.storage = KEY_STORAGE_INCORE;
1022   repodata_set(data, entry, &key, 0);
1023 }
1024
1025 void
1026 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1027 {
1028   Repokey key;
1029   key.name = keyname;
1030   key.type = REPOKEY_TYPE_CONSTANTID;
1031   key.size = id;
1032   key.storage = KEY_STORAGE_INCORE;
1033   repodata_set(data, entry, &key, 0);
1034 }
1035
1036 void
1037 repodata_set_void(Repodata *data, Id entry, Id keyname)
1038 {
1039   Repokey key;
1040   key.name = keyname;
1041   key.type = REPOKEY_TYPE_VOID;
1042   key.size = 0;
1043   key.storage = KEY_STORAGE_INCORE;
1044   repodata_set(data, entry, &key, 0);
1045 }
1046
1047 void
1048 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1049 {
1050   Repokey key;
1051   int l;
1052
1053   l = strlen(str) + 1;
1054   key.name = keyname;
1055   key.type = REPOKEY_TYPE_STR;
1056   key.size = 0;
1057   key.storage = KEY_STORAGE_INCORE;
1058   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1059   memcpy(data->attrdata + data->attrdatalen, str, l);
1060   repodata_set(data, entry, &key, data->attrdatalen);
1061   data->attrdatalen += l;
1062 }
1063
1064 static void
1065 repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
1066 {
1067   int oldsize;
1068   Id *ida, *pp;
1069
1070   pp = 0;
1071   if (data->attrs && data->attrs[entry])
1072     for (pp = data->attrs[entry]; *pp; pp += 2)
1073       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1074         break;
1075   if (!pp || !*pp)
1076     {
1077       /* not found. allocate new key */
1078       Repokey key;
1079       key.name = keyname;
1080       key.type = keytype;
1081       key.size = 0;
1082       key.storage = KEY_STORAGE_INCORE;
1083       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1084       repodata_set(data, entry, &key, data->attriddatalen);
1085       return;
1086     }
1087   oldsize = 0;
1088   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1089     oldsize += entrysize;
1090   if (ida + 1 == data->attriddata + data->attriddatalen)
1091     {
1092       /* this was the last entry, just append it */
1093       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1094       data->attriddatalen--;    /* overwrite terminating 0  */
1095     }
1096   else
1097     {
1098       /* too bad. move to back. */
1099       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1100       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1101       pp[1] = data->attriddatalen;
1102       data->attriddatalen += oldsize;
1103     }
1104 }
1105
1106 static inline int
1107 checksumtype2len(Id type)
1108 {
1109   switch (type)
1110     {
1111     case REPOKEY_TYPE_MD5:
1112       return SIZEOF_MD5;
1113     case REPOKEY_TYPE_SHA1:
1114       return SIZEOF_SHA1;
1115     case REPOKEY_TYPE_SHA256:
1116       return SIZEOF_SHA256;
1117     default:
1118       return 0;
1119     }
1120 }
1121
1122 void
1123 repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1124                       const unsigned char *str)
1125 {
1126   Repokey key;
1127   int l = checksumtype2len(type);
1128
1129   if (!l)
1130     return;
1131   key.name = keyname;
1132   key.type = type;
1133   key.size = 0;
1134   key.storage = KEY_STORAGE_INCORE;
1135   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1136   memcpy(data->attrdata + data->attrdatalen, str, l);
1137   repodata_set(data, entry, &key, data->attrdatalen);
1138   data->attrdatalen += l;
1139 }
1140
1141 static int
1142 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1143 {
1144   int i;
1145   for (i = 0; i < buflen; i++)
1146     {
1147 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1148                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1149                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1150                 : -1)
1151       int v = c2h(*str);
1152       str++;
1153       if (v < 0)
1154         return 0;
1155       buf[i] = v;
1156       v = c2h(*str);
1157       str++;
1158       if (v < 0)
1159         return 0;
1160       buf[i] = (buf[i] << 4) | v;
1161 #undef c2h
1162     }
1163   return buflen;
1164 }
1165
1166 void
1167 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1168                       const char *str)
1169 {
1170   unsigned char buf[64];
1171   int l = checksumtype2len(type);
1172
1173   if (!l)
1174     return;
1175   if (hexstr2bytes(buf, str, l) != l)
1176     {
1177       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1178       return;
1179     }
1180   repodata_set_bin_checksum(data, entry, keyname, type, buf);
1181 }
1182
1183 const char *
1184 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1185 {
1186   int i, l;
1187   char *str, *s;
1188
1189   l = checksumtype2len(type);
1190   if (!l)
1191     return "";
1192   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1193   for (i = 0; i < l; i++)
1194     {
1195       unsigned char v = buf[i];
1196       unsigned char w = v >> 4;
1197       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1198       w = v & 15;
1199       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1200     }
1201   *s = 0;
1202   return str;
1203 }
1204
1205 Id repodata_globalize_id(Repodata *data, Id id)
1206
1207   if (!data || !data->localpool)
1208     return id;
1209   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1210 }
1211
1212 void
1213 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1214 {
1215
1216 #if 0
1217 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1218 #endif
1219   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1220   data->attriddata[data->attriddatalen++] = dir;
1221   data->attriddata[data->attriddatalen++] = num;
1222   data->attriddata[data->attriddatalen++] = num2;
1223   data->attriddata[data->attriddatalen++] = 0;
1224 }
1225
1226 void
1227 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1228 {
1229   Id stroff;
1230   int l;
1231
1232   l = strlen(str) + 1;
1233   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1234   memcpy(data->attrdata + data->attrdatalen, str, l);
1235   stroff = data->attrdatalen;
1236   data->attrdatalen += l;
1237
1238 #if 0
1239 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1240 #endif
1241   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1242   data->attriddata[data->attriddatalen++] = dir;
1243   data->attriddata[data->attriddatalen++] = stroff;
1244   data->attriddata[data->attriddatalen++] = 0;
1245 }
1246
1247 void
1248 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1249 {
1250 #if 0
1251 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1252 #endif
1253   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1254   data->attriddata[data->attriddatalen++] = id;
1255   data->attriddata[data->attriddatalen++] = 0;
1256 }
1257
1258 void
1259 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1260                            const char *str)
1261 {
1262   Id id;
1263   if (data->localpool)
1264     id = stringpool_str2id(&data->spool, str, 1);
1265   else
1266     id = str2id(data->repo->pool, str, 1);
1267   repodata_add_idarray(data, entry, keyname, id);
1268 }
1269
1270 void
1271 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1272 {
1273   Id *keyp;
1274   if (dest == src || !(keyp = data->attrs[src]))
1275     return;
1276   for (; *keyp; keyp += 2)
1277     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1278 }
1279
1280 /*********************************/
1281
1282 /* unify with repo_write! */
1283
1284 #define EXTDATA_BLOCK 1023
1285 #define SCHEMATA_BLOCK 31
1286 #define SCHEMATADATA_BLOCK 255
1287
1288 struct extdata {
1289   unsigned char *buf;
1290   int len;
1291 };
1292
1293 static void
1294 data_addid(struct extdata *xd, Id x)
1295 {
1296   unsigned char *dp;
1297   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1298   dp = xd->buf + xd->len;
1299
1300   if (x >= (1 << 14))
1301     {
1302       if (x >= (1 << 28))
1303         *dp++ = (x >> 28) | 128;
1304       if (x >= (1 << 21))
1305         *dp++ = (x >> 21) | 128;
1306       *dp++ = (x >> 14) | 128;
1307     }
1308   if (x >= (1 << 7))
1309     *dp++ = (x >> 7) | 128;
1310   *dp++ = x & 127;
1311   xd->len = dp - xd->buf;
1312 }
1313
1314 static void
1315 data_addideof(struct extdata *xd, Id x, int eof)
1316 {
1317   if (x >= 64)
1318     x = (x & 63) | ((x & ~63) << 1);
1319   data_addid(xd, (eof ? x: x | 64));
1320 }
1321
1322 static void
1323 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1324 {
1325   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1326   memcpy(xd->buf + xd->len, blob, len);
1327   xd->len += len;
1328 }
1329
1330 /*********************************/
1331
1332 static void
1333 addschema_prepare(Repodata *data, Id *schematacache)
1334 {
1335   int h, len, i;
1336   Id *sp;
1337
1338   memset(schematacache, 0, 256 * sizeof(Id));
1339   for (i = 0; i < data->nschemata; i++)
1340     {
1341       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1342         h = h * 7 + *sp++;
1343       h &= 255;
1344       schematacache[h] = i + 1;
1345     }
1346   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1347   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1348 }
1349
1350 static Id
1351 addschema(Repodata *data, Id *schema, Id *schematacache)
1352 {
1353   int h, len; 
1354   Id *sp, cid; 
1355
1356   for (sp = schema, len = 0, h = 0; *sp; len++)
1357     h = h * 7 + *sp++;
1358   h &= 255; 
1359   len++;
1360
1361   cid = schematacache[h];
1362   if (cid)
1363     {    
1364       cid--;
1365       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1366         return cid;
1367       /* cache conflict */
1368       for (cid = 0; cid < data->nschemata; cid++)
1369         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1370           return cid;
1371     }
1372   /* a new one. make room. */
1373   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1374   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1375   /* add schema */
1376   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1377   data->schemata[data->nschemata] = data->schemadatalen;
1378   data->schemadatalen += len;
1379   schematacache[h] = data->nschemata + 1;
1380 #if 0
1381 fprintf(stderr, "addschema: new schema\n");
1382 #endif
1383   return data->nschemata++; 
1384 }
1385
1386
1387 void
1388 repodata_internalize(Repodata *data)
1389 {
1390   Repokey *key;
1391   Id id, entry, nentry, *ida;
1392   Id schematacache[256];
1393   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1394   unsigned char *dp, *ndp;
1395   int newschema, oldcount;
1396   struct extdata newincore;
1397   struct extdata newvincore;
1398
1399   if (!data->attrs)
1400     return;
1401
1402   newvincore.buf = data->vincore;
1403   newvincore.len = data->vincorelen;
1404
1405   schema = sat_malloc2(data->nkeys, sizeof(Id));
1406   seen = sat_malloc2(data->nkeys, sizeof(Id));
1407
1408   /* Merge the data already existing (in data->schemata, ->incoredata and
1409      friends) with the new attributes in data->attrs[].  */
1410   nentry = data->end - data->start;
1411   addschema_prepare(data, schematacache);
1412   memset(&newincore, 0, sizeof(newincore));
1413   data_addid(&newincore, 0);
1414   for (entry = 0; entry < nentry; entry++)
1415     {
1416       memset(seen, 0, data->nkeys * sizeof(Id));
1417       sp = schema;
1418       dp = data->incoredata + data->incoreoffset[entry];
1419       if (data->incoredata)
1420         dp = data_read_id(dp, &oldschema);
1421       else
1422         oldschema = 0;
1423 #if 0
1424 fprintf(stderr, "oldschema %d\n", oldschema);
1425 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1426 fprintf(stderr, "schemadata %p\n", data->schemadata);
1427 #endif
1428       /* seen: -1: old data  0: skipped  >0: id + 1 */
1429       newschema = 0;
1430       oldcount = 0;
1431       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1432         {
1433           if (seen[*keyp])
1434             {
1435               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1436               exit(1);
1437             }
1438           seen[*keyp] = -1;
1439           *sp++ = *keyp;
1440           oldcount++;
1441         }
1442       if (data->attrs[entry])
1443         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1444           {
1445             if (!seen[*keyp])
1446               {
1447                 newschema = 1;
1448                 *sp++ = *keyp;
1449               }
1450             seen[*keyp] = keyp[1] + 1;
1451           }
1452       *sp++ = 0;
1453       if (newschema)
1454         /* Ideally we'd like to sort the new schema here, to ensure
1455            schema equality independend of the ordering.  We can't do that
1456            yet.  For once see below (old ids need to come before new ids).
1457            An additional difficulty is that we also need to move
1458            the values with the keys.  */
1459         schemaid = addschema(data, schema, schematacache);
1460       else
1461         schemaid = oldschema;
1462
1463
1464       /* Now create data blob.  We walk through the (possibly new) schema
1465          and either copy over old data, or insert the new.  */
1466       /* XXX Here we rely on the fact that the (new) schema has the form
1467          o1 o2 o3 o4 ... | n1 n2 n3 ...
1468          (oX being the old keyids (possibly overwritten), and nX being
1469           the new keyids).  This rules out sorting the keyids in order
1470          to ensure a small schema count.  */
1471       data->incoreoffset[entry] = newincore.len;
1472       data_addid(&newincore, schemaid);
1473       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1474         {
1475           key = data->keys + *keyp;
1476           ndp = dp;
1477           if (oldcount)
1478             {
1479               /* Skip the data associated with this old key.  */
1480               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1481                 {
1482                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1483                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1484                 }
1485               else if (key->storage == KEY_STORAGE_INCORE)
1486                 ndp = data_skip(dp, key->type);
1487               oldcount--;
1488             }
1489           if (seen[*keyp] == -1)
1490             {
1491               /* If this key was an old one _and_ was not overwritten with
1492                  a different value copy over the old value (we skipped it
1493                  above).  */
1494               if (dp != ndp)
1495                 data_addblob(&newincore, dp, ndp - dp);
1496               seen[*keyp] = 0;
1497             }
1498           else if (seen[*keyp])
1499             {
1500               /* Otherwise we have a new value.  Parse it into the internal
1501                  form.  */
1502               struct extdata *xd;
1503               unsigned int oldvincorelen = 0;
1504
1505               xd = &newincore;
1506               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1507                 {
1508                   xd = &newvincore;
1509                   oldvincorelen = xd->len;
1510                 }
1511               id = seen[*keyp] - 1;
1512               switch (key->type)
1513                 {
1514                 case REPOKEY_TYPE_VOID:
1515                 case REPOKEY_TYPE_CONSTANT:
1516                 case REPOKEY_TYPE_CONSTANTID:
1517                   break;
1518                 case REPOKEY_TYPE_STR:
1519                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1520                   break;
1521                 case REPOKEY_TYPE_MD5:
1522                   data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1523                   break;
1524                 case REPOKEY_TYPE_SHA1:
1525                   data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1526                   break;
1527                 case REPOKEY_TYPE_ID:
1528                 case REPOKEY_TYPE_NUM:
1529                 case REPOKEY_TYPE_DIR:
1530                   data_addid(xd, id);
1531                   break;
1532                 case REPOKEY_TYPE_IDARRAY:
1533                   for (ida = data->attriddata + id; *ida; ida++)
1534                     data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1535                   break;
1536                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1537                   for (ida = data->attriddata + id; *ida; ida += 3)
1538                     {
1539                       data_addid(xd, ida[0]);
1540                       data_addid(xd, ida[1]);
1541                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1542                     }
1543                   break;
1544                 case REPOKEY_TYPE_DIRSTRARRAY:
1545                   for (ida = data->attriddata + id; *ida; ida += 2)
1546                     {
1547                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1548                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1549                     }
1550                   break;
1551                 default:
1552                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1553                   exit(1);
1554                 }
1555               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1556                 {
1557                   /* put offset/len in incore */
1558                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1559                   oldvincorelen = xd->len - oldvincorelen;
1560                   data_addid(&newincore, oldvincorelen);
1561                 }
1562             }
1563           dp = ndp;
1564         }
1565       if (data->attrs[entry])
1566         sat_free(data->attrs[entry]);
1567     }
1568   sat_free(schema);
1569   sat_free(seen);
1570
1571   sat_free(data->incoredata);
1572   data->incoredata = newincore.buf;
1573   data->incoredatalen = newincore.len;
1574   data->incoredatafree = 0;
1575   
1576   sat_free(data->vincore);
1577   data->vincore = newvincore.buf;
1578   data->vincorelen = newvincore.len;
1579
1580   data->attrs = sat_free(data->attrs);
1581   data->attrdata = sat_free(data->attrdata);
1582   data->attriddata = sat_free(data->attriddata);
1583   data->attrdatalen = 0;
1584   data->attriddatalen = 0;
1585 }
1586
1587 Id
1588 repodata_str2dir(Repodata *data, const char *dir, int create)
1589 {
1590   Id id, parent;
1591   const char *dire;
1592
1593   parent = 0;
1594   while (*dir == '/' && dir[1] == '/')
1595     dir++;
1596   if (*dir == '/' && !dir[1])
1597     return 1;
1598   while (*dir)
1599     {
1600       dire = strchrnul(dir, '/');
1601       if (data->localpool)
1602         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1603       else
1604         id = strn2id(data->repo->pool, dir, dire - dir, create);
1605       if (!id)
1606         return 0;
1607       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1608       if (!parent)
1609         return 0;
1610       if (!*dire)
1611         break;
1612       dir = dire + 1;
1613       while (*dir == '/')
1614         dir++;
1615     }
1616   return parent;
1617 }
1618
1619 const char *
1620 repodata_dir2str(Repodata *data, Id did, const char *suf)
1621 {
1622   Pool *pool = data->repo->pool;
1623   int l = 0;
1624   Id parent, comp;
1625   const char *comps;
1626   char *p;
1627
1628   if (!did)
1629     return suf ? suf : "";
1630   parent = did;
1631   while (parent)
1632     {
1633       comp = dirpool_compid(&data->dirpool, parent);
1634       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1635       l += strlen(comps);
1636       parent = dirpool_parent(&data->dirpool, parent);
1637       if (parent)
1638         l++;
1639     }
1640   if (suf)
1641     l += strlen(suf) + 1;
1642   p = pool_alloctmpspace(pool, l + 1) + l;
1643   *p = 0;
1644   if (suf)
1645     {
1646       p -= strlen(suf);
1647       strcpy(p, suf);
1648       *--p = '/';
1649     }
1650   parent = did;
1651   while (parent)
1652     {
1653       comp = dirpool_compid(&data->dirpool, parent);
1654       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1655       l = strlen(comps);
1656       p -= l;
1657       strncpy(p, comps, l);
1658       parent = dirpool_parent(&data->dirpool, parent);
1659       if (parent)
1660         *--p = '/';
1661     }
1662   return p;
1663 }
1664
1665 unsigned int
1666 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1667 {
1668   return compress_buf(page, len, cpage, max);
1669 }
1670
1671 #define SOLV_ERROR_EOF              3
1672
1673 static inline unsigned int
1674 read_u32(FILE *fp)
1675 {
1676   int c, i;
1677   unsigned int x = 0; 
1678
1679   for (i = 0; i < 4; i++) 
1680     {    
1681       c = getc(fp);
1682       if (c == EOF) 
1683         return 0;
1684       x = (x << 8) | c; 
1685     }    
1686   return x;
1687 }
1688
1689 #define SOLV_ERROR_EOF          3
1690 #define SOLV_ERROR_CORRUPT      6
1691
1692 /* Try to either setup on-demand paging (using FP as backing
1693    file), or in case that doesn't work (FP not seekable) slurps in
1694    all pages and deactivates paging.  */
1695 void
1696 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1697 {
1698   FILE *fp = data->fp;
1699   unsigned int npages;
1700   unsigned int i;
1701   unsigned int can_seek;
1702   long cur_file_ofs;
1703   unsigned char buf[BLOB_PAGESIZE];
1704
1705   if (pagesz != BLOB_PAGESIZE)
1706     {
1707       /* We could handle this by slurping in everything.  */
1708       data->error = SOLV_ERROR_CORRUPT;
1709       return;
1710     }
1711   can_seek = 1;
1712   if ((cur_file_ofs = ftell(fp)) < 0)
1713     can_seek = 0;
1714   clearerr(fp);
1715   if (can_seek)
1716     data->pagefd = dup(fileno(fp));
1717   if (data->pagefd == -1)
1718     can_seek = 0;
1719
1720 #ifdef DEBUG_PAGING
1721   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1722 #endif
1723   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1724
1725   data->num_pages = npages;
1726   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1727
1728   /* If we can't seek on our input we have to slurp in everything.  */
1729   if (!can_seek)
1730     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1731   for (i = 0; i < npages; i++)
1732     {
1733       unsigned int in_len = read_u32(fp);
1734       unsigned int compressed = in_len & 1;
1735       Attrblobpage *p = data->pages + i;
1736       in_len >>= 1;
1737 #ifdef DEBUG_PAGING
1738       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1739                i, in_len, compressed ? "" : "not ");
1740 #endif
1741       if (can_seek)
1742         {
1743           cur_file_ofs += 4;
1744           p->mapped_at = -1;
1745           p->file_offset = cur_file_ofs;
1746           p->file_size = in_len * 2 + compressed;
1747           if (fseek(fp, in_len, SEEK_CUR) < 0)
1748             {
1749               perror ("fseek");
1750               fprintf (stderr, "can't seek after we thought we can\n");
1751               /* We can't fall back to non-seeking behaviour as we already
1752                  read over some data pages without storing them away.  */
1753               data->error = SOLV_ERROR_EOF;
1754               close(data->pagefd);
1755               data->pagefd = -1;
1756               return;
1757             }
1758           cur_file_ofs += in_len;
1759         }
1760       else
1761         {
1762           unsigned int out_len;
1763           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1764           p->mapped_at = i * BLOB_PAGESIZE;
1765           p->file_offset = 0;
1766           p->file_size = 0;
1767           /* We can't seek, so suck everything in.  */
1768           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1769             {
1770               perror("fread");
1771               data->error = SOLV_ERROR_EOF;
1772               return;
1773             }
1774           if (compressed)
1775             {
1776               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1777               if (out_len != BLOB_PAGESIZE && i < npages - 1)
1778                 {
1779                   data->error = SOLV_ERROR_CORRUPT;
1780                   return;
1781                 }
1782             }
1783         }
1784     }
1785 }
1786
1787 void
1788 repodata_disable_paging(Repodata *data)
1789 {
1790   if (maybe_load_repodata(data, 0)
1791       && data->num_pages)
1792     load_page_range (data, 0, data->num_pages - 1);
1793 }
1794 /*
1795 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1796 */