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