456b0551fbda7026ea74f5547f103ae67b977181
[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_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1087                       const unsigned char *str)
1088 {
1089   Repokey key;
1090   int l;
1091   switch (type)
1092     {
1093       case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1094       case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1095       default: return;
1096     }
1097   key.name = keyname;
1098   key.type = type;
1099   key.size = 0;
1100   key.storage = KEY_STORAGE_INCORE;
1101   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1102   memcpy(data->attrdata + data->attrdatalen, str, l);
1103   repodata_set(data, entry, &key, data->attrdatalen);
1104   data->attrdatalen += l;
1105 }
1106
1107 static int
1108 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1109 {
1110   int i;
1111   for (i = 0; i < buflen; i++)
1112     {
1113 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1114                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1115                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1116                 : -1)
1117       int v = c2h(*str);
1118       str++;
1119       if (v < 0)
1120         return 0;
1121       buf[i] = v;
1122       v = c2h(*str);
1123       str++;
1124       if (v < 0)
1125         return 0;
1126       buf[i] = (buf[i] << 4) | v;
1127 #undef c2h
1128     }
1129   return buflen;
1130 }
1131
1132 void
1133 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1134                       const char *str)
1135 {
1136   int l;
1137   switch (type)
1138     {
1139       case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1140       case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1141       default: return;
1142     }
1143   unsigned char buf[l];
1144   if (hexstr2bytes(buf, str, l) != l)
1145     {
1146       fprintf(stderr, "Invalid hex character in %s\n", str);
1147       return;
1148     }
1149   repodata_set_bin_checksum(data, entry, keyname, type, buf);
1150 }
1151
1152 const char *
1153 repodata_chk2str(Repodata *data, Id type, const char *buf)
1154 {
1155   int i, l;
1156   char *str, *s;
1157   switch (type)
1158     {
1159       case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
1160       case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
1161       default: return id2str(data->repo->pool, ID_EMPTY);
1162     }
1163   s = str = pool_alloctmpspace(data->repo->pool, 2*l + 1);
1164   for (i = 0; i < l; i++, s+=2)
1165     {
1166       unsigned char v = buf[i];
1167       unsigned char w = v >> 4;
1168       s[0] = w >= 10 ? (w-10)+'a' : w + '0';
1169       w = v & 15;
1170       s[1] = w >= 10 ? (w-10)+'a' : w + '0';
1171     }
1172   *s = 0;
1173   return str;
1174 }
1175
1176 void
1177 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1178 {
1179
1180 #if 0
1181 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1182 #endif
1183   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1184   data->attriddata[data->attriddatalen++] = dir;
1185   data->attriddata[data->attriddatalen++] = num;
1186   data->attriddata[data->attriddatalen++] = num2;
1187   data->attriddata[data->attriddatalen++] = 0;
1188 }
1189
1190 void
1191 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1192 {
1193   Id stroff;
1194   int l;
1195
1196   l = strlen(str) + 1;
1197   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1198   memcpy(data->attrdata + data->attrdatalen, str, l);
1199   stroff = data->attrdatalen;
1200   data->attrdatalen += l;
1201
1202 #if 0
1203 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1204 #endif
1205   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1206   data->attriddata[data->attriddatalen++] = dir;
1207   data->attriddata[data->attriddatalen++] = stroff;
1208   data->attriddata[data->attriddatalen++] = 0;
1209 }
1210
1211 void
1212 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1213 {
1214 #if 0
1215 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1216 #endif
1217   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1218   data->attriddata[data->attriddatalen++] = id;
1219   data->attriddata[data->attriddatalen++] = 0;
1220 }
1221
1222 void
1223 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1224                            const char *str)
1225 {
1226   Id id;
1227   if (data->localpool)
1228     id = stringpool_str2id(&data->spool, str, 1);
1229   else
1230     id = str2id(data->repo->pool, str, 1);
1231   repodata_add_idarray(data, entry, keyname, id);
1232 }
1233
1234 void
1235 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1236 {
1237   Id *keyp;
1238   if (dest == src || !(keyp = data->attrs[src]))
1239     return;
1240   for (; *keyp; keyp += 2)
1241     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1242 }
1243
1244 /*********************************/
1245
1246 /* unify with repo_write! */
1247
1248 #define EXTDATA_BLOCK 1023
1249 #define SCHEMATA_BLOCK 31
1250 #define SCHEMATADATA_BLOCK 255
1251
1252 struct extdata {
1253   unsigned char *buf;
1254   int len;
1255 };
1256
1257 static void
1258 data_addid(struct extdata *xd, Id x)
1259 {
1260   unsigned char *dp;
1261   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1262   dp = xd->buf + xd->len;
1263
1264   if (x >= (1 << 14))
1265     {
1266       if (x >= (1 << 28))
1267         *dp++ = (x >> 28) | 128;
1268       if (x >= (1 << 21))
1269         *dp++ = (x >> 21) | 128;
1270       *dp++ = (x >> 14) | 128;
1271     }
1272   if (x >= (1 << 7))
1273     *dp++ = (x >> 7) | 128;
1274   *dp++ = x & 127;
1275   xd->len = dp - xd->buf;
1276 }
1277
1278 static void
1279 data_addideof(struct extdata *xd, Id x, int eof)
1280 {
1281   if (x >= 64)
1282     x = (x & 63) | ((x & ~63) << 1);
1283   data_addid(xd, (eof ? x: x | 64));
1284 }
1285
1286 static void
1287 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1288 {
1289   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1290   memcpy(xd->buf + xd->len, blob, len);
1291   xd->len += len;
1292 }
1293
1294 /*********************************/
1295
1296 static void
1297 addschema_prepare(Repodata *data, Id *schematacache)
1298 {
1299   int h, len, i;
1300   Id *sp;
1301
1302   memset(schematacache, 0, 256 * sizeof(Id));
1303   for (i = 0; i < data->nschemata; i++)
1304     {
1305       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1306         h = h * 7 + *sp++;
1307       h &= 255;
1308       schematacache[h] = i + 1;
1309     }
1310   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1311   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1312 }
1313
1314 static Id
1315 addschema(Repodata *data, Id *schema, Id *schematacache)
1316 {
1317   int h, len; 
1318   Id *sp, cid; 
1319
1320   for (sp = schema, len = 0, h = 0; *sp; len++)
1321     h = h * 7 + *sp++;
1322   h &= 255; 
1323   len++;
1324
1325   cid = schematacache[h];
1326   if (cid)
1327     {    
1328       cid--;
1329       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1330         return cid;
1331       /* cache conflict */
1332       for (cid = 0; cid < data->nschemata; cid++)
1333         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1334           return cid;
1335     }
1336   /* a new one. make room. */
1337   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1338   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1339   /* add schema */
1340   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1341   data->schemata[data->nschemata] = data->schemadatalen;
1342   data->schemadatalen += len;
1343   schematacache[h] = data->nschemata + 1;
1344 #if 0
1345 fprintf(stderr, "addschema: new schema\n");
1346 #endif
1347   return data->nschemata++; 
1348 }
1349
1350
1351 void
1352 repodata_internalize(Repodata *data)
1353 {
1354   Repokey *key;
1355   Id id, entry, nentry, *ida;
1356   Id schematacache[256];
1357   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1358   unsigned char *dp, *ndp;
1359   int newschema, oldcount;
1360   struct extdata newincore;
1361   struct extdata newvincore;
1362
1363   if (!data->attrs)
1364     return;
1365
1366   newvincore.buf = data->vincore;
1367   newvincore.len = data->vincorelen;
1368
1369   schema = sat_malloc2(data->nkeys, sizeof(Id));
1370   seen = sat_malloc2(data->nkeys, sizeof(Id));
1371
1372   /* Merge the data already existing (in data->schemata, ->incoredata and
1373      friends) with the new attributes in data->attrs[].  */
1374   nentry = data->end - data->start;
1375   addschema_prepare(data, schematacache);
1376   memset(&newincore, 0, sizeof(newincore));
1377   data_addid(&newincore, 0);
1378   for (entry = 0; entry < nentry; entry++)
1379     {
1380       memset(seen, 0, data->nkeys * sizeof(Id));
1381       sp = schema;
1382       dp = data->incoredata + data->incoreoffset[entry];
1383       if (data->incoredata)
1384         dp = data_read_id(dp, &oldschema);
1385       else
1386         oldschema = 0;
1387 #if 0
1388 fprintf(stderr, "oldschema %d\n", oldschema);
1389 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1390 fprintf(stderr, "schemadata %p\n", data->schemadata);
1391 #endif
1392       /* seen: -1: old data  0: skipped  >0: id + 1 */
1393       newschema = 0;
1394       oldcount = 0;
1395       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1396         {
1397           if (seen[*keyp])
1398             {
1399               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1400               exit(1);
1401             }
1402           seen[*keyp] = -1;
1403           *sp++ = *keyp;
1404           oldcount++;
1405         }
1406       if (data->attrs[entry])
1407         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1408           {
1409             if (!seen[*keyp])
1410               {
1411                 newschema = 1;
1412                 *sp++ = *keyp;
1413               }
1414             seen[*keyp] = keyp[1] + 1;
1415           }
1416       *sp++ = 0;
1417       if (newschema)
1418         /* Ideally we'd like to sort the new schema here, to ensure
1419            schema equality independend of the ordering.  We can't do that
1420            yet.  For once see below (old ids need to come before new ids).
1421            An additional difficulty is that we also need to move
1422            the values with the keys.  */
1423         schemaid = addschema(data, schema, schematacache);
1424       else
1425         schemaid = oldschema;
1426
1427
1428       /* Now create data blob.  We walk through the (possibly new) schema
1429          and either copy over old data, or insert the new.  */
1430       /* XXX Here we rely on the fact that the (new) schema has the form
1431          o1 o2 o3 o4 ... | n1 n2 n3 ...
1432          (oX being the old keyids (possibly overwritten), and nX being
1433           the new keyids).  This rules out sorting the keyids in order
1434          to ensure a small schema count.  */
1435       data->incoreoffset[entry] = newincore.len;
1436       data_addid(&newincore, schemaid);
1437       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1438         {
1439           key = data->keys + *keyp;
1440           ndp = dp;
1441           if (oldcount)
1442             {
1443               /* Skip the data associated with this old key.  */
1444               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1445                 {
1446                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1447                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1448                 }
1449               else if (key->storage == KEY_STORAGE_INCORE)
1450                 ndp = data_skip(dp, key->type);
1451               oldcount--;
1452             }
1453           if (seen[*keyp] == -1)
1454             {
1455               /* If this key was an old one _and_ was not overwritten with
1456                  a different value copy over the old value (we skipped it
1457                  above).  */
1458               if (dp != ndp)
1459                 data_addblob(&newincore, dp, ndp - dp);
1460               seen[*keyp] = 0;
1461             }
1462           else if (seen[*keyp])
1463             {
1464               /* Otherwise we have a new value.  Parse it into the internal
1465                  form.  */
1466               struct extdata *xd;
1467               unsigned int oldvincorelen = 0;
1468
1469               xd = &newincore;
1470               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1471                 {
1472                   xd = &newvincore;
1473                   oldvincorelen = xd->len;
1474                 }
1475               id = seen[*keyp] - 1;
1476               switch (key->type)
1477                 {
1478                 case REPOKEY_TYPE_VOID:
1479                 case REPOKEY_TYPE_CONSTANT:
1480                 case REPOKEY_TYPE_CONSTANTID:
1481                   break;
1482                 case REPOKEY_TYPE_STR:
1483                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1484                   break;
1485                 case REPOKEY_TYPE_MD5:
1486                   data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1487                   break;
1488                 case REPOKEY_TYPE_SHA1:
1489                   data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1490                   break;
1491                 case REPOKEY_TYPE_ID:
1492                 case REPOKEY_TYPE_NUM:
1493                 case REPOKEY_TYPE_DIR:
1494                   data_addid(xd, id);
1495                   break;
1496                 case REPOKEY_TYPE_IDARRAY:
1497                   for (ida = data->attriddata + id; *ida; ida++)
1498                     data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1499                   break;
1500                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1501                   for (ida = data->attriddata + id; *ida; ida += 3)
1502                     {
1503                       data_addid(xd, ida[0]);
1504                       data_addid(xd, ida[1]);
1505                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1506                     }
1507                   break;
1508                 case REPOKEY_TYPE_DIRSTRARRAY:
1509                   for (ida = data->attriddata + id; *ida; ida += 2)
1510                     {
1511                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1512                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1513                     }
1514                   break;
1515                 default:
1516                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1517                   exit(1);
1518                 }
1519               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1520                 {
1521                   /* put offset/len in incore */
1522                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1523                   oldvincorelen = xd->len - oldvincorelen;
1524                   data_addid(&newincore, oldvincorelen);
1525                 }
1526             }
1527           dp = ndp;
1528         }
1529       if (data->attrs[entry])
1530         sat_free(data->attrs[entry]);
1531     }
1532   sat_free(schema);
1533   sat_free(seen);
1534
1535   sat_free(data->incoredata);
1536   data->incoredata = newincore.buf;
1537   data->incoredatalen = newincore.len;
1538   data->incoredatafree = 0;
1539   
1540   sat_free(data->vincore);
1541   data->vincore = newvincore.buf;
1542   data->vincorelen = newvincore.len;
1543
1544   data->attrs = sat_free(data->attrs);
1545   data->attrdata = sat_free(data->attrdata);
1546   data->attriddata = sat_free(data->attriddata);
1547   data->attrdatalen = 0;
1548   data->attriddatalen = 0;
1549 }
1550
1551 Id
1552 repodata_str2dir(Repodata *data, const char *dir, int create)
1553 {
1554   Id id, parent;
1555   const char *dire;
1556
1557   parent = 0;
1558   while (*dir == '/' && dir[1] == '/')
1559     dir++;
1560   if (*dir == '/' && !dir[1])
1561     return 1;
1562   while (*dir)
1563     {
1564       dire = strchrnul(dir, '/');
1565       if (data->localpool)
1566         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1567       else
1568         id = strn2id(data->repo->pool, dir, dire - dir, create);
1569       if (!id)
1570         return 0;
1571       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1572       if (!parent)
1573         return 0;
1574       if (!*dire)
1575         break;
1576       dir = dire + 1;
1577       while (*dir == '/')
1578         dir++;
1579     }
1580   return parent;
1581 }
1582
1583 const char *
1584 repodata_dir2str(Repodata *data, Id did, const char *suf)
1585 {
1586   Pool *pool = data->repo->pool;
1587   int l = 0;
1588   Id parent, comp;
1589   const char *comps;
1590   char *p;
1591
1592   if (!did)
1593     return suf ? suf : "";
1594   parent = did;
1595   while (parent)
1596     {
1597       comp = dirpool_compid(&data->dirpool, parent);
1598       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1599       l += strlen(comps);
1600       parent = dirpool_parent(&data->dirpool, parent);
1601       if (parent)
1602         l++;
1603     }
1604   if (suf)
1605     l += strlen(suf) + 1;
1606   p = pool_alloctmpspace(pool, l + 1) + l;
1607   *p = 0;
1608   if (suf)
1609     {
1610       p -= strlen(suf);
1611       strcpy(p, suf);
1612       *--p = '/';
1613     }
1614   parent = did;
1615   while (parent)
1616     {
1617       comp = dirpool_compid(&data->dirpool, parent);
1618       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1619       l = strlen(comps);
1620       p -= l;
1621       strncpy(p, comps, l);
1622       parent = dirpool_parent(&data->dirpool, parent);
1623       if (parent)
1624         *--p = '/';
1625     }
1626   return p;
1627 }
1628
1629 unsigned int
1630 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1631 {
1632   return compress_buf(page, len, cpage, max);
1633 }
1634
1635 #define SOLV_ERROR_EOF              3
1636
1637 static inline unsigned int
1638 read_u32(FILE *fp)
1639 {
1640   int c, i;
1641   unsigned int x = 0; 
1642
1643   for (i = 0; i < 4; i++) 
1644     {    
1645       c = getc(fp);
1646       if (c == EOF) 
1647         return 0;
1648       x = (x << 8) | c; 
1649     }    
1650   return x;
1651 }
1652
1653 #define SOLV_ERROR_EOF          3
1654 #define SOLV_ERROR_CORRUPT      6
1655
1656 /* Try to either setup on-demand paging (using FP as backing
1657    file), or in case that doesn't work (FP not seekable) slurps in
1658    all pages and deactivates paging.  */
1659 void
1660 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1661 {
1662   FILE *fp = data->fp;
1663   unsigned int npages;
1664   unsigned int i;
1665   unsigned int can_seek;
1666   long cur_file_ofs;
1667   unsigned char buf[BLOB_PAGESIZE];
1668
1669   if (pagesz != BLOB_PAGESIZE)
1670     {
1671       /* We could handle this by slurping in everything.  */
1672       data->error = SOLV_ERROR_CORRUPT;
1673       return;
1674     }
1675   can_seek = 1;
1676   if ((cur_file_ofs = ftell(fp)) < 0)
1677     can_seek = 0;
1678   clearerr(fp);
1679   if (can_seek)
1680     data->pagefd = dup(fileno(fp));
1681   if (data->pagefd == -1)
1682     can_seek = 0;
1683
1684 #ifdef DEBUG_PAGING
1685   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1686 #endif
1687   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1688
1689   data->num_pages = npages;
1690   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1691
1692   /* If we can't seek on our input we have to slurp in everything.  */
1693   if (!can_seek)
1694     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1695   for (i = 0; i < npages; i++)
1696     {
1697       unsigned int in_len = read_u32(fp);
1698       unsigned int compressed = in_len & 1;
1699       Attrblobpage *p = data->pages + i;
1700       in_len >>= 1;
1701 #ifdef DEBUG_PAGING
1702       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1703                i, in_len, compressed ? "" : "not ");
1704 #endif
1705       if (can_seek)
1706         {
1707           cur_file_ofs += 4;
1708           p->mapped_at = -1;
1709           p->file_offset = cur_file_ofs;
1710           p->file_size = in_len * 2 + compressed;
1711           if (fseek(fp, in_len, SEEK_CUR) < 0)
1712             {
1713               perror ("fseek");
1714               fprintf (stderr, "can't seek after we thought we can\n");
1715               /* We can't fall back to non-seeking behaviour as we already
1716                  read over some data pages without storing them away.  */
1717               data->error = SOLV_ERROR_EOF;
1718               close(data->pagefd);
1719               data->pagefd = -1;
1720               return;
1721             }
1722           cur_file_ofs += in_len;
1723         }
1724       else
1725         {
1726           unsigned int out_len;
1727           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1728           p->mapped_at = i * BLOB_PAGESIZE;
1729           p->file_offset = 0;
1730           p->file_size = 0;
1731           /* We can't seek, so suck everything in.  */
1732           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1733             {
1734               perror("fread");
1735               data->error = SOLV_ERROR_EOF;
1736               return;
1737             }
1738           if (compressed)
1739             {
1740               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1741               if (out_len != BLOB_PAGESIZE && i < npages - 1)
1742                 {
1743                   data->error = SOLV_ERROR_CORRUPT;
1744                   return;
1745                 }
1746             }
1747         }
1748     }
1749 }
1750
1751 void
1752 repodata_disable_paging(Repodata *data)
1753 {
1754   if (maybe_load_repodata(data, 0)
1755       && data->num_pages)
1756     load_page_range (data, 0, data->num_pages - 1);
1757 }
1758 /*
1759 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1760 */