33993e094a3ff2ccc4a21459c2b2f6cdebb011b4
[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->nextra = repo->nextra;
58   data->extrastart = 0;
59   data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
60   data->extraoffset = sat_extend_resize(0, repo->nextra, sizeof(Id), REPODATA_BLOCK);
61   data->pagefd = -1;
62 }
63
64 void
65 repodata_free(Repodata *data)
66 {
67   sat_free(data->keys);
68   sat_free(data->schemata);
69   sat_free(data->schemadata);
70
71   sat_free(data->spool.strings);
72   sat_free(data->spool.stringspace);
73   sat_free(data->spool.stringhashtbl);
74
75   sat_free(data->dirpool.dirs);
76   sat_free(data->dirpool.dirtraverse);
77
78   sat_free(data->incoredata);
79   sat_free(data->incoreoffset);
80   sat_free(data->extraoffset);
81   sat_free(data->verticaloffset);
82
83   sat_free(data->blob_store);
84   sat_free(data->pages);
85   sat_free(data->mapped);
86
87   sat_free(data->vincore);
88
89   sat_free(data->attrs);
90   sat_free(data->extraattrs);
91   sat_free(data->attrdata);
92   sat_free(data->attriddata);
93   
94   sat_free(data->location);
95   sat_free(data->addedfileprovides);
96
97   if (data->pagefd != -1)
98     close(data->pagefd);
99 }
100
101 unsigned char *
102 data_skip_recursive(Repodata *data, unsigned char *dp, Repokey *key)
103 {
104   KeyValue kv;
105   if (key->type != REPOKEY_TYPE_COUNTED)
106     return data_skip(dp, key->type);
107   dp = data_fetch(dp, &kv, key);
108   int num = kv.num;
109   int schema = kv.id;
110   while (num--)
111     {
112       Id *keyp = data->schemadata + data->schemata[schema];
113       for (; *keyp; keyp++)
114         dp = data_skip_recursive(data, dp, data->keys + *keyp);
115     }
116   return dp;
117 }
118
119 static unsigned char *
120 forward_to_key(Repodata *data, Id keyid, Id schemaid, unsigned char *dp)
121 {
122   Id k, *keyp;
123
124   keyp = data->schemadata + data->schemata[schemaid];
125   while ((k = *keyp++) != 0)
126     {
127       if (k == keyid)
128         return dp;
129       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
130         {
131           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that offset */
132           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that length */
133           continue;
134         }
135       if (data->keys[k].storage != KEY_STORAGE_INCORE)
136         continue;
137       dp = data_skip_recursive(data, dp, data->keys + k);
138     }
139   return 0;
140 }
141
142 #define BLOB_PAGEBITS 15
143 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
144
145 static unsigned char *
146 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
147 {
148 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
149    and are consecutive.  Return a pointer to the mapping of PSTART.  */
150   unsigned char buf[BLOB_PAGESIZE];
151   unsigned int i;
152
153   /* Quick check in case all pages are there already and consecutive.  */
154   for (i = pstart; i <= pend; i++)
155     if (data->pages[i].mapped_at == -1
156         || (i > pstart
157             && data->pages[i].mapped_at
158                != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
159       break;
160   if (i > pend)
161     return data->blob_store + data->pages[pstart].mapped_at;
162
163   if (data->pagefd == -1)
164     return 0;
165
166   /* Ensure that we can map the numbers of pages we need at all.  */
167   if (pend - pstart + 1 > data->ncanmap)
168     {
169       unsigned int oldcan = data->ncanmap;
170       data->ncanmap = pend - pstart + 1;
171       if (data->ncanmap < 4)
172         data->ncanmap = 4;
173       data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
174       memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
175       data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
176 #ifdef DEBUG_PAGING
177       fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
178 #endif
179     }
180
181   /* Now search for "cheap" space in our store.  Space is cheap if it's either
182      free (very cheap) or contains pages we search for anyway.  */
183
184   /* Setup cost array.  */
185   unsigned int cost[data->ncanmap];
186   for (i = 0; i < data->ncanmap; i++)
187     {
188       unsigned int pnum = data->mapped[i];
189       if (pnum == 0)
190         cost[i] = 0;
191       else
192         {
193           pnum--;
194           Attrblobpage *p = data->pages + pnum;
195           assert (p->mapped_at != -1);
196           if (pnum >= pstart && pnum <= pend)
197             cost[i] = 1;
198           else
199             cost[i] = 3;
200         }
201     }
202
203   /* And search for cheapest space.  */
204   unsigned int best_cost = -1;
205   unsigned int best = 0;
206   unsigned int same_cost = 0;
207   for (i = 0; i + pend - pstart < data->ncanmap; i++)
208     {
209       unsigned int c = cost[i];
210       unsigned int j;
211       for (j = 0; j < pend - pstart + 1; j++)
212         c += cost[i+j];
213       if (c < best_cost)
214         best_cost = c, best = i;
215       else if (c == best_cost)
216         same_cost++;
217       /* A null cost won't become better.  */
218       if (c == 0)
219         break;
220     }
221   /* If all places have the same cost we would thrash on slot 0.  Avoid
222      this by doing a round-robin strategy in this case.  */
223   if (same_cost == data->ncanmap - pend + pstart - 1)
224     best = data->rr_counter++ % (data->ncanmap - pend + pstart);
225
226   /* So we want to map our pages from [best] to [best+pend-pstart].
227      Use a very simple strategy, which doesn't make the best use of
228      our resources, but works.  Throw away all pages in that range
229      (even ours) then copy around ours (in case they were outside the 
230      range) or read them in.  */
231   for (i = best; i < best + pend - pstart + 1; i++)
232     {
233       unsigned int pnum = data->mapped[i];
234       if (pnum--
235           /* If this page is exactly at the right place already,
236              no need to evict it.  */
237           && pnum != pstart + i - best)
238         {
239           /* Evict this page.  */
240 #ifdef DEBUG_PAGING
241           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
242 #endif
243           cost[i] = 0;
244           data->mapped[i] = 0;
245           data->pages[pnum].mapped_at = -1;
246         }
247     }
248
249   /* Everything is free now.  Read in the pages we want.  */
250   for (i = pstart; i <= pend; i++)
251     {
252       Attrblobpage *p = data->pages + i;
253       unsigned int pnum = i - pstart + best;
254       void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
255       if (p->mapped_at != -1)
256         {
257           if (p->mapped_at != pnum * BLOB_PAGESIZE)
258             {
259 #ifdef DEBUG_PAGING
260               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
261 #endif
262               /* Still mapped somewhere else, so just copy it from there.  */
263               memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
264               data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
265             }
266         }
267       else
268         {
269           unsigned int in_len = p->file_size;
270           unsigned int compressed = in_len & 1;
271           in_len >>= 1;
272 #ifdef DEBUG_PAGING
273           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
274 #endif
275           if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
276             {
277               perror ("mapping pread");
278               return 0;
279             }
280           if (compressed)
281             {
282               unsigned int out_len;
283               out_len = unchecked_decompress_buf(buf, in_len,
284                                                   dest, BLOB_PAGESIZE);
285               if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
286                 {
287                   fprintf(stderr, "can't decompress\n");
288                   return 0;
289                 }
290 #ifdef DEBUG_PAGING
291               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
292 #endif
293             }
294 #ifdef DEBUG_PAGING
295           fprintf (stderr, "\n");
296 #endif
297         }
298       p->mapped_at = pnum * BLOB_PAGESIZE;
299       data->mapped[pnum] = i + 1;
300     }
301   return data->blob_store + best * BLOB_PAGESIZE;
302 }
303
304 static unsigned char *
305 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
306 {
307   unsigned char *dp;
308   if (!len)
309     return 0;
310   if (off >= data->lastverticaloffset)
311     {
312       off -= data->lastverticaloffset;
313       if (off + len > data->vincorelen)
314         return 0;
315       return data->vincore + off;
316     }
317   if (off + len > key->size)
318     return 0;
319   /* we now have the offset, go into vertical */
320   off += data->verticaloffset[key - data->keys];
321   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
322   dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
323   if (dp)
324     dp += off % BLOB_PAGESIZE;
325   return dp;
326 }
327
328 static inline unsigned char *
329 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
330 {
331   unsigned char *dp = *dpp;
332
333   if (!dp)
334     return 0;
335   if (key->storage == KEY_STORAGE_INCORE)
336     {
337       /* hmm, this is a bit expensive */
338       *dpp = data_skip_recursive(data, dp, key);
339       return dp;
340     }
341   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
342     {
343       Id off, len;
344       dp = data_read_id(dp, &off);
345       dp = data_read_id(dp, &len);
346       *dpp = dp;
347       return make_vertical_available(data, key, off, len);
348     }
349   return 0;
350 }
351
352 static inline int
353 maybe_load_repodata(Repodata *data, Id *keyid)
354 {
355   if (data->state == REPODATA_STUB)
356     {
357       if (data->loadcallback)
358         {
359           if (keyid)
360             {
361               /* key order may change when loading */
362               int i;
363               Id name = data->keys[*keyid].name;
364               Id type = data->keys[*keyid].type;
365               data->loadcallback(data);
366               if (data->state == REPODATA_AVAILABLE)
367                 {
368                   for (i = 1; i < data->nkeys; i++)
369                     if (data->keys[i].name == name && data->keys[i].type == type)
370                       break;
371                   if (i < data->nkeys)
372                     *keyid = i;
373                   else
374                     return 0;
375                 }
376             }
377           else
378             data->loadcallback(data);
379         }
380       else
381         data->state = REPODATA_ERROR;
382     }
383   if (data->state == REPODATA_AVAILABLE)
384     return 1;
385   data->state = REPODATA_ERROR;
386   return 0;
387 }
388
389 static inline unsigned char*
390 entry2data(Repodata *data, Id entry)
391 {
392   if (entry < 0)
393     return data->incoredata + data->extraoffset[-1 - entry];
394   else
395     return data->incoredata + data->incoreoffset[entry];
396 }
397
398 Id
399 repodata_lookup_id(Repodata *data, Id entry, Id keyid)
400 {
401   Id schema;
402   Repokey *key;
403   Id id, *keyp;
404   unsigned char *dp;
405
406   if (!maybe_load_repodata(data, &keyid))
407     return 0;
408   dp = entry2data(data, entry);
409   if (!dp)
410     return 0;
411   dp = data_read_id(dp, &schema);
412   /* make sure the schema of this solvable contains the key */
413   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
414     if (!*keyp)
415       return 0;
416   dp = forward_to_key(data, keyid, schema, dp);
417   key = data->keys + keyid;
418   dp = get_data(data, key, &dp);
419   if (!dp)
420     return 0;
421   if (key->type == REPOKEY_TYPE_CONSTANTID)
422     return key->size;
423   if (key->type != REPOKEY_TYPE_ID)
424     return 0;
425   dp = data_read_id(dp, &id);
426   return id;
427 }
428
429 const char *
430 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
431 {
432   Id schema;
433   Repokey *key;
434   Id id, *keyp;
435   unsigned char *dp;
436
437   if (!maybe_load_repodata(data, &keyid))
438     return 0;
439
440   dp = entry2data(data, entry);
441   if (!dp)
442     return 0;
443   dp = data_read_id(dp, &schema);
444   /* make sure the schema of this solvable contains the key */
445   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
446     if (!*keyp)
447       return 0;
448   dp = forward_to_key(data, keyid, schema, dp);
449   key = data->keys + keyid;
450   dp = get_data(data, key, &dp);
451   if (!dp)
452     return 0;
453   if (key->type == REPOKEY_TYPE_STR)
454     return (const char *)dp;
455   if (key->type == REPOKEY_TYPE_CONSTANTID)
456     return id2str(data->repo->pool, key->size);
457   if (key->type == REPOKEY_TYPE_ID)
458     dp = data_read_id(dp, &id);
459   else
460     return 0;
461   if (data->localpool)
462     return data->spool.stringspace + data->spool.strings[id];
463   return id2str(data->repo->pool, id);
464 }
465
466 int
467 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
468 {
469   Id schema;
470   Repokey *key;
471   Id *keyp;
472   KeyValue kv;
473   unsigned char *dp;
474
475   *value = 0;
476
477   if (!maybe_load_repodata(data, &keyid))
478     return 0;
479
480   dp = entry2data(data, entry);
481   if (!dp)
482     return 0;
483   dp = data_read_id(dp, &schema);
484   /* make sure the schema of this solvable contains the key */
485   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
486     if (!*keyp)
487       return 0;
488   dp = forward_to_key(data, keyid, schema, dp);
489   key = data->keys + keyid;
490   dp = get_data(data, key, &dp);
491   if (!dp)
492     return 0;
493   if (key->type == REPOKEY_TYPE_NUM
494       || key->type == REPOKEY_TYPE_U32
495       || key->type == REPOKEY_TYPE_CONSTANT)
496     {
497       dp = data_fetch(dp, &kv, key);
498       *value = kv.num;
499       return 1;
500     }
501   return 0;
502 }
503
504 int
505 repodata_lookup_void(Repodata *data, Id entry, Id keyid)
506 {
507   Id schema;
508   Id *keyp;
509   unsigned char *dp;
510   if (!maybe_load_repodata(data, &keyid))
511     return 0;
512   dp = entry2data(data, entry);
513   if (!dp)
514     return 0;
515   dp = data_read_id(dp, &schema);
516   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
517     if (!*keyp)
518       return 0;
519   return 1;
520 }
521
522 const unsigned char *
523 repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyid, Id *typep)
524 {
525   Id schema;
526   Id *keyp;
527   Repokey *key;
528   unsigned char *dp;
529
530   if (!maybe_load_repodata(data, &keyid))
531     return 0;
532   dp = entry2data(data, entry);
533   if (!dp)
534     return 0;
535   dp = data_read_id(dp, &schema);
536   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
537     if (!*keyp)
538       return 0;
539   dp = forward_to_key(data, keyid, schema, dp);
540   key = data->keys + keyid;
541   *typep = key->type;
542   return get_data(data, key, &dp);
543 }
544
545 void
546 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
547 {
548   Id schema;
549   Repokey *key;
550   Id k, keyid, *kp, *keyp;
551   unsigned char *dp, *ddp;
552   int onekey = 0;
553   int stop;
554   KeyValue kv;
555
556   if (entry < 0
557       || !maybe_load_repodata(data, 0))
558     return;
559
560   dp = entry2data(data, entry);
561   if (!dp)
562     return;
563   dp = data_read_id(dp, &schema);
564   keyp = data->schemadata + data->schemata[schema];
565   if (keyname)
566     {
567       /* search in a specific key */
568       for (kp = keyp; (k = *kp++) != 0; )
569         if (data->keys[k].name == keyname)
570           break;
571       if (k == 0)
572         return;
573       dp = forward_to_key(data, k, schema, dp);
574       if (!dp)
575         return;
576       keyp = kp - 1;
577       onekey = 1;
578     }
579   while ((keyid = *keyp++) != 0)
580     {
581       stop = 0;
582       key = data->keys + keyid;
583       ddp = get_data(data, key, &dp);
584       do
585         {
586           ddp = data_fetch(ddp, &kv, key);
587           if (!ddp)
588             break;
589           if (key->type == REPOKEY_TYPE_COUNTED)
590             {
591               int num = kv.num;
592               int subschema = kv.id;
593               Repokey *countkey = key;
594               kv.eof = 0;
595               callback(cbdata, data->repo->pool->solvables + data->start + entry, data, countkey, &kv);
596               while (num--)
597                 {
598                   Id *kp = data->schemadata + data->schemata[subschema];
599                   for (; *kp; kp++)
600                     {
601                       key = data->keys + *kp;
602                       ddp = data_fetch(ddp, &kv, key);
603                       if (!ddp)
604                         exit(1);
605                       callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
606                     }
607                   kv.eof = 1;
608                   callback(cbdata, data->repo->pool->solvables + data->start + entry, data, countkey, &kv);
609                 }
610               kv.eof = 2;
611               stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, countkey, &kv);
612             }
613           else
614             stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
615         }
616       while (!kv.eof && !stop);
617       if (onekey || stop > SEARCH_NEXT_KEY)
618         return;
619     }
620 }
621
622 static void
623 dataiterator_newdata(Dataiterator *di)
624 {
625   Id keyname = di->keyname;
626   Repodata *data = di->data;
627   di->nextkeydp = 0;
628
629   if (data->state == REPODATA_STUB)
630     {
631       if (keyname)
632         {
633           int j;
634           for (j = 1; j < data->nkeys; j++)
635             if (keyname == data->keys[j].name)
636               break;
637           if (j == data->nkeys)
638             return;
639         }
640       /* load it */
641       if (data->loadcallback)
642         data->loadcallback(data);
643       else
644         data->state = REPODATA_ERROR;
645     }
646   if (data->state == REPODATA_ERROR)
647     return;
648
649   Id schema;
650   unsigned char *dp = data->incoredata;
651   if (!dp)
652     return;
653   if (di->solvid >= 0)
654     dp += data->incoreoffset[di->solvid - data->start];
655   else
656     dp += data->extraoffset[-1 - di->solvid - data->extrastart];
657   dp = data_read_id(dp, &schema);
658   Id *keyp = data->schemadata + data->schemata[schema];
659   if (keyname)
660     {
661       Id k, *kp;
662       /* search in a specific key */
663       for (kp = keyp; (k = *kp++) != 0; )
664         if (data->keys[k].name == keyname)
665           break;
666       if (k == 0)
667         return;
668       dp = forward_to_key(data, k, schema, dp);
669       if (!dp)
670         return;
671       keyp = kp - 1;
672     }
673   Id keyid = *keyp++;
674   if (!keyid)
675     return;
676
677   di->data = data;
678   di->key = di->data->keys + keyid;
679   di->keyp = keyp;
680   di->dp = 0;
681
682   di->nextkeydp = dp;
683   di->dp = get_data(di->data, di->key, &di->nextkeydp);
684   di->kv.eof = 0;
685 }
686
687 void
688 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
689                   const char *match, int flags)
690 {
691   di->flags = flags;
692   if (p)
693     {
694       di->solvid = p;
695       di->flags |= __SEARCH_ONESOLVABLE;
696       di->data = repo->repodata - 1;
697       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
698         di->state = 0;
699       else
700         di->state = 1;
701     }
702   else
703     {
704       di->solvid = repo->start - 1;
705       if (di->solvid < 0)
706         {
707           fprintf(stderr, "A repo contains the NULL solvable!\n");
708           exit(1);
709         }
710       di->data = repo->repodata + repo->nrepodata - 1;
711       di->state = 0;
712     }
713
714   di->match = match;
715   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
716     {
717       if (di->match)
718         {
719           /* We feed multiple lines eventually (e.g. authors or descriptions),
720              so set REG_NEWLINE. */
721           di->regex_err =
722             regcomp(&di->regex, di->match,
723               REG_EXTENDED | REG_NOSUB | REG_NEWLINE
724               | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
725 #if 0
726           if (di->regex_err != 0)
727             {
728               fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
729               fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
730               exit(1);
731             }
732 #else
733         }
734       else
735         {
736           di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
737           di->regex_err = 0;
738 #endif
739         }
740     }
741
742   di->keyname = keyname;
743   static Id zeroid = 0;
744   di->keyp = &zeroid;
745   di->kv.eof = 1;
746   di->repo = repo;
747   di->idp = 0;
748   di->subkeyp = 0;
749 }
750
751 /* FIXME factor and merge with repo_matchvalue */
752 static int
753 dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
754 {
755   KeyValue *kv = &di->kv;
756   const char *match = vmatch;
757   if ((flags & SEARCH_STRINGMASK) != 0)
758     {
759       switch (di->key->type)
760         {
761         case REPOKEY_TYPE_ID:
762         case REPOKEY_TYPE_IDARRAY:
763           if (di->data && di->data->localpool)
764             kv->str = stringpool_id2str(&di->data->spool, kv->id);
765           else
766             kv->str = id2str(di->repo->pool, kv->id);
767           break;
768         case REPOKEY_TYPE_STR:
769           break;
770         case REPOKEY_TYPE_DIRSTRARRAY:
771           if (!(flags & SEARCH_FILES))
772             return 0;
773           /* Put the full filename into kv->str.  */
774           kv->str = repodata_dir2str(di->data, kv->id, kv->str);
775           /* And to compensate for that put the "empty" directory into
776              kv->id, so that later calls to repodata_dir2str on this data
777              come up with the same filename again.  */
778           kv->id = 0;
779           break;
780         default:
781           return 0;
782         }
783       /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
784          for the others we can't know if a colon separates a kind or not.  */
785       if ((flags & SEARCH_SKIP_KIND)
786           && di->key->storage == KEY_STORAGE_SOLVABLE)
787         {
788           const char *s = strchr(kv->str, ':');
789           if (s)
790             kv->str = s + 1;
791         }
792       switch ((flags & SEARCH_STRINGMASK))
793         {
794           case SEARCH_SUBSTRING:
795             if (flags & SEARCH_NOCASE)
796               {
797                 if (!strcasestr(kv->str, match))
798                   return 0;
799               }
800             else
801               {
802                 if (!strstr(kv->str, match))
803                   return 0;
804               }
805             break;
806           case SEARCH_STRING:
807             if (flags & SEARCH_NOCASE)
808               {
809                 if (strcasecmp(match, kv->str))
810                   return 0;
811               }
812             else
813               {
814                 if (strcmp(match, kv->str))
815                   return 0;
816               }
817             break;
818           case SEARCH_GLOB:
819             if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
820               return 0;
821             break;
822           case SEARCH_REGEX:
823             if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
824               return 0;
825             break;
826           default:
827             return 0;
828         }
829     }
830   return 1;
831 }
832
833 static int
834 dataiterator_match_int(Dataiterator *di)
835 {
836   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
837     return dataiterator_match_int_real(di, di->flags, &di->regex);
838   else
839     return dataiterator_match_int_real(di, di->flags, di->match);
840 }
841
842 int
843 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
844 {
845   return dataiterator_match_int_real(di, flags, vmatch);
846 }
847
848 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
849   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
850   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
851   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
852   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
853   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
854   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
855   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
856   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
857   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
858   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
859   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
860   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
861   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
862 };
863
864 int
865 dataiterator_step(Dataiterator *di)
866 {
867 restart:
868   while (1)
869     {
870       if (di->state)
871         {
872           if (di->idp)
873             {
874               Id *idp = di->idp;
875               if (*idp)
876                 {
877                   di->kv.id = *idp;
878                   di->idp++;
879                   di->kv.eof = idp[1] ? 0 : 1;
880                   goto weg2;
881                 }
882               else
883                 di->idp = 0;
884             }
885           Solvable *s = di->repo->pool->solvables + di->solvid;
886           int state = di->state;
887           di->key = solvablekeys + state - 1;
888           if (di->keyname)
889             di->state = RPM_RPMDBID;
890           else
891             di->state++;
892           if (state == 1)
893             {
894               di->data = 0;
895               if (di->keyname)
896                 state = di->keyname - 1;
897             }
898           switch (state + 1)
899             {
900               case SOLVABLE_NAME:
901                 if (!s->name)
902                   continue;
903                 di->kv.id = s->name;
904                 di->kv.eof = 1;
905                 break;
906               case SOLVABLE_ARCH:
907                 if (!s->arch)
908                   continue;
909                 di->kv.id = s->arch;
910                 di->kv.eof = 1;
911                 break;
912               case SOLVABLE_EVR:
913                 if (!s->evr)
914                   continue;
915                 di->kv.id = s->evr;
916                 di->kv.eof = 1;
917                 break;
918               case SOLVABLE_VENDOR:
919                 if (!s->vendor)
920                   continue;
921                 di->kv.id = s->vendor;
922                 di->kv.eof = 1;
923                 break;
924               case SOLVABLE_PROVIDES:
925                 di->idp = s->provides
926                     ? di->repo->idarraydata + s->provides : 0;
927                 continue;
928               case SOLVABLE_OBSOLETES:
929                 di->idp = s->obsoletes
930                     ? di->repo->idarraydata + s->obsoletes : 0;
931                 continue;
932               case SOLVABLE_CONFLICTS:
933                 di->idp = s->conflicts
934                     ? di->repo->idarraydata + s->conflicts : 0;
935                 continue;
936               case SOLVABLE_REQUIRES:
937                 di->idp = s->requires
938                     ? di->repo->idarraydata + s->requires : 0;
939                 continue;
940               case SOLVABLE_RECOMMENDS:
941                 di->idp = s->recommends
942                     ? di->repo->idarraydata + s->recommends : 0;
943                 continue;
944               case SOLVABLE_SUPPLEMENTS:
945                 di->idp = s->supplements
946                     ? di->repo->idarraydata + s->supplements : 0;
947                 continue;
948               case SOLVABLE_SUGGESTS:
949                 di->idp = s->suggests
950                     ? di->repo->idarraydata + s->suggests : 0;
951                 continue;
952               case SOLVABLE_ENHANCES:
953                 di->idp = s->enhances
954                     ? di->repo->idarraydata + s->enhances : 0;
955                 continue;
956               case RPM_RPMDBID:
957                 if (!di->repo->rpmdbid)
958                   continue;
959                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
960                 di->kv.eof = 1;
961                 break;
962               default:
963                 di->data = di->repo->repodata - 1;
964                 di->kv.eof = 1;
965                 di->state = 0;
966                 continue;
967             }
968         }
969       else if (di->subkeyp)
970         {
971           Id keyid;
972           if (!di->subnum)
973             {
974               /* Send end-of-substruct.  We are here only when we saw a
975                  _COUNTED key one level up.  Since then we didn't increment
976                  ->keyp, so it still can be found at keyp[-1].  */
977               di->kv.eof = 2;
978               di->key = di->data->keys + di->keyp[-1];
979               di->subkeyp = 0;
980             }
981           else if (!(keyid = *di->subkeyp++))
982             {
983               /* Send end-of-element.  See above for keyp[-1].  */
984               di->kv.eof = 1;
985               di->key = di->data->keys + di->keyp[-1];
986               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
987               di->subnum--;
988             }
989           else
990             {
991               di->key = di->data->keys + keyid;
992               di->dp = data_fetch(di->dp, &di->kv, di->key);
993               if (!di->dp)
994                 exit(1);
995             }
996         }
997       else
998         {
999           if (di->kv.eof)
1000             di->dp = 0;
1001           else
1002             di->dp = data_fetch(di->dp, &di->kv, di->key);
1003
1004           while (!di->dp)
1005             {
1006               Id keyid;
1007               if (di->keyname || !(keyid = *di->keyp++))
1008                 {
1009                   while (1)
1010                     {
1011                       Repo *repo = di->repo;
1012                       Repodata *data = ++di->data;
1013                       if (data >= repo->repodata + repo->nrepodata)
1014                         {
1015                           if (di->flags & __SEARCH_ONESOLVABLE)
1016                             return 0;
1017                           if (di->solvid >= 0)
1018                             {
1019                               while (++di->solvid < repo->end)
1020                                 if (repo->pool->solvables[di->solvid].repo == repo)
1021                                   break;
1022                               if (di->solvid >= repo->end)
1023                                 {
1024                                   if (!(di->flags & SEARCH_EXTRA))
1025                                     goto skiprepo;
1026                                   di->solvid = -1;
1027                                   if (di->solvid < -repo->nextra)
1028                                     goto skiprepo;
1029                                 }
1030                             }
1031                           else
1032                             {
1033                               --di->solvid;
1034                               if (di->solvid < -repo->nextra)
1035                                 {
1036 skiprepo:;
1037                                   Pool *pool = di->repo->pool;
1038                                   if (!(di->flags & SEARCH_ALL_REPOS)
1039                                       || di->repo == pool->repos[pool->nrepos - 1])
1040                                     return 0;
1041                                   int i;
1042                                   for (i = 0; i < pool->nrepos; i++)
1043                                     if (di->repo == pool->repos[i])
1044                                       break;
1045                                   di->repo = pool->repos[i + 1];
1046                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
1047                                   continue;
1048                                 }
1049                             }
1050                           di->data = repo->repodata - 1;
1051                           if (di->solvid < 0
1052                               || (di->flags & SEARCH_NO_STORAGE_SOLVABLE))
1053                             continue;
1054                           static Id zeroid = 0;
1055                           di->keyp = &zeroid;
1056                           di->state = 1;
1057                           goto restart;
1058                         }
1059                       if ((di->solvid < 0 && (-1 - di->solvid) >= data->extrastart && (-1 - di->solvid) < (data->extrastart + data->nextra))
1060                           || (di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
1061                         {
1062                           dataiterator_newdata(di);
1063                           if (di->nextkeydp)
1064                             break;
1065                         }
1066                     }
1067                 }
1068               else
1069                 {
1070                   di->key = di->data->keys + keyid;
1071                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1072                 }
1073               di->dp = data_fetch(di->dp, &di->kv, di->key);
1074             }
1075           if (di->key->type == REPOKEY_TYPE_COUNTED)
1076             {
1077               di->subnum = di->kv.num;
1078               di->subschema = di->kv.id;
1079               di->kv.eof = 0;
1080               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1081             }
1082         }
1083 weg2:
1084       if (!di->match
1085           || dataiterator_match_int(di))
1086         break;
1087     }
1088   return 1;
1089 }
1090
1091 void
1092 dataiterator_skip_attribute(Dataiterator *di)
1093 {
1094   if (di->state)
1095     di->idp = 0;
1096   /* This will make the next _step call to retrieve the next field.  */
1097   di->kv.eof = 1;
1098 }
1099
1100 void
1101 dataiterator_skip_solvable(Dataiterator *di)
1102 {
1103   /* We're done with this field.  */
1104   di->kv.eof = 1;
1105   /* And with solvable data.  */
1106   di->state = 0;
1107   /* And with all keys for this repodata and thing. */
1108   static Id zeroid = 0;
1109   di->keyp = &zeroid;
1110   /* And with all repodatas for this thing.  */
1111   di->data = di->repo->repodata + di->repo->nrepodata - 1;
1112   /* Hence the next call to _step will retrieve the next thing.  */
1113 }
1114
1115 void
1116 dataiterator_skip_repo(Dataiterator *di)
1117 {
1118   dataiterator_skip_solvable(di);
1119   /* We're done with all solvables and all extra things for this repo.  */
1120   di->solvid = -1 - di->repo->nextra;
1121 }
1122
1123 void
1124 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1125 {
1126   di->repo = s->repo;
1127   /* Simulate us being done with the solvable before the requested one.  */
1128   dataiterator_skip_solvable(di);
1129   di->solvid = s - s->repo->pool->solvables;
1130   di->solvid--;
1131 }
1132
1133 void
1134 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1135 {
1136   di->repo = repo;
1137   dataiterator_skip_solvable(di);
1138   di->solvid = repo->start - 1;
1139 }
1140
1141 /* extend repodata so that it includes solvables p */
1142 void
1143 repodata_extend(Repodata *data, Id p)
1144 {
1145   if (data->start == data->end)
1146     data->start = data->end = p;
1147   if (p >= data->end)
1148     {
1149       int old = data->end - data->start;
1150       int new = p - data->end + 1;
1151       if (data->attrs)
1152         {
1153           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
1154           memset(data->attrs + old, 0, new * sizeof(Id));
1155         }
1156       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1157       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1158       data->end = p + 1;
1159     }
1160   if (p < data->start)
1161     {
1162       int old = data->end - data->start;
1163       int new = data->start - p;
1164       if (data->attrs)
1165         {
1166           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
1167           memmove(data->attrs + new, data->attrs, old * sizeof(Id));
1168           memset(data->attrs, 0, new * sizeof(Id));
1169         }
1170       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1171       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1172       memset(data->incoreoffset, 0, new * sizeof(Id));
1173       data->start = p;
1174     }
1175 }
1176
1177 void
1178 repodata_extend_extra(Repodata *data, int nextra)
1179 {
1180   if (nextra <= data->nextra)
1181     return;
1182   if (data->extraattrs)
1183     {
1184       data->extraattrs = sat_extend(data->extraattrs, data->nextra, nextra - data->nextra, sizeof(Id), REPODATA_BLOCK);
1185       memset(data->extraattrs + data->nextra, 0, (nextra - data->nextra) * sizeof (Id));
1186     }
1187   data->extraoffset = sat_extend(data->extraoffset, data->nextra, nextra - data->nextra, sizeof(Id), REPODATA_BLOCK);
1188   memset(data->extraoffset + data->nextra, 0, (nextra - data->nextra) * sizeof(Id));
1189   data->nextra = nextra;
1190 }
1191
1192 void
1193 repodata_extend_block(Repodata *data, Id start, Id num)
1194 {
1195   if (!num)
1196     return;
1197   if (!data->incoreoffset)
1198     {
1199       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1200       data->start = start;
1201       data->end = start + num;
1202       return;
1203     }
1204   repodata_extend(data, start);
1205   if (num > 1)
1206     repodata_extend(data, start + num - 1);
1207 }
1208
1209 /**********************************************************************/
1210
1211 #define REPODATA_ATTRS_BLOCK 63
1212 #define REPODATA_ATTRDATA_BLOCK 1023
1213 #define REPODATA_ATTRIDDATA_BLOCK 63
1214
1215 static inline Id
1216 get_new_struct(Repodata *data)
1217 {
1218   /* Make sure to never give out struct id 0. */
1219   if (!data->structs)
1220     {
1221       data->structs = sat_extend(0, 0, 2, sizeof(Id *), REPODATA_BLOCK);
1222       data->structs[0] = 0;
1223       data->structs[1] = 0;
1224       data->nstructs = 2;
1225       return 1;
1226     }
1227   data->structs = sat_extend(data->structs, data->nstructs, 1, sizeof(Id *), REPODATA_BLOCK);
1228   data->structs[data->nstructs] = 0;
1229   return data->nstructs++;
1230 }
1231
1232 static Id
1233 repodata_get_handle_int(Repodata *data, Id entry)
1234 {
1235   Id *ap;
1236   if (!data->attrs && entry >= 0)
1237     {
1238       data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id),
1239                                      REPODATA_BLOCK);
1240     }
1241   else if (!data->extraattrs && entry < 0)
1242     data->extraattrs = sat_calloc_block(data->nextra, sizeof(Id), REPODATA_BLOCK);
1243   if (entry < 0)
1244     ap = &data->extraattrs[-1 - entry];
1245   else
1246     ap = &data->attrs[entry];
1247   if (!*ap)
1248     *ap = get_new_struct(data);
1249   return *ap;
1250 }
1251
1252 Id
1253 repodata_get_handle(Repodata *data, Id entry)
1254 {
1255   return repodata_get_handle_int(data, entry);
1256 }
1257
1258 static void
1259 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1260 {
1261   Id *pp;
1262   Id *ap;
1263   int i;
1264   ap = data->structs[handle];
1265   i = 0;
1266   if (ap)
1267     {
1268       for (pp = ap; *pp; pp += 2)
1269         /* Determine equality based on the name only, allows us to change
1270            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1271         if (data->keys[*pp].name == data->keys[keyid].name)
1272           break;
1273       if (*pp)
1274         {
1275           if (overwrite)
1276             {
1277               pp[0] = keyid;
1278               pp[1] = val;
1279             }
1280           return;
1281         }
1282       i = pp - ap;
1283     }
1284   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1285   data->structs[handle] = ap;
1286   pp = ap + i;
1287   *pp++ = keyid;
1288   *pp++ = val;
1289   *pp = 0;
1290 }
1291
1292 void
1293 repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
1294 {
1295   Id keyid;
1296
1297   /* find key in keys */
1298   for (keyid = 1; keyid < data->nkeys; keyid++)
1299     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
1300       {
1301         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
1302           continue;
1303         break;
1304       }
1305   if (keyid == data->nkeys)
1306     {
1307       /* allocate new key */
1308       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
1309       data->keys[data->nkeys++] = *key;
1310       if (data->verticaloffset)
1311         {
1312           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
1313           data->verticaloffset[data->nkeys - 1] = 0;
1314         }
1315     }
1316   repodata_insert_keyid(data, handle, keyid, val, 1);
1317 }
1318
1319 void
1320 repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
1321 {
1322   Repokey key;
1323   key.name = keyname;
1324   key.type = REPOKEY_TYPE_ID;
1325   key.size = 0;
1326   key.storage = KEY_STORAGE_INCORE;
1327   repodata_set(data, handle, &key, id);
1328 }
1329
1330 void
1331 repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
1332 {
1333   Repokey key;
1334   key.name = keyname;
1335   key.type = REPOKEY_TYPE_NUM;
1336   key.size = 0;
1337   key.storage = KEY_STORAGE_INCORE;
1338   repodata_set(data, handle, &key, (Id)num);
1339 }
1340
1341 void
1342 repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
1343 {
1344   Repokey key;
1345   Id id;
1346   if (data->localpool)
1347     id = stringpool_str2id(&data->spool, str, 1);
1348   else
1349     id = str2id(data->repo->pool, str, 1);
1350   key.name = keyname;
1351   key.type = REPOKEY_TYPE_ID;
1352   key.size = 0;
1353   key.storage = KEY_STORAGE_INCORE;
1354   repodata_set(data, handle, &key, id);
1355 }
1356
1357 void
1358 repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
1359 {
1360   Repokey key;
1361   key.name = keyname;
1362   key.type = REPOKEY_TYPE_CONSTANT;
1363   key.size = constant;
1364   key.storage = KEY_STORAGE_INCORE;
1365   repodata_set(data, handle, &key, 0);
1366 }
1367
1368 void
1369 repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
1370 {
1371   Repokey key;
1372   key.name = keyname;
1373   key.type = REPOKEY_TYPE_CONSTANTID;
1374   key.size = id;
1375   key.storage = KEY_STORAGE_INCORE;
1376   repodata_set(data, handle, &key, 0);
1377 }
1378
1379 void
1380 repodata_set_void(Repodata *data, Id handle, Id keyname)
1381 {
1382   Repokey key;
1383   key.name = keyname;
1384   key.type = REPOKEY_TYPE_VOID;
1385   key.size = 0;
1386   key.storage = KEY_STORAGE_INCORE;
1387   repodata_set(data, handle, &key, 0);
1388 }
1389
1390 void
1391 repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
1392 {
1393   Repokey key;
1394   int l;
1395
1396   l = strlen(str) + 1;
1397   key.name = keyname;
1398   key.type = REPOKEY_TYPE_STR;
1399   key.size = 0;
1400   key.storage = KEY_STORAGE_INCORE;
1401   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1402   memcpy(data->attrdata + data->attrdatalen, str, l);
1403   repodata_set(data, handle, &key, data->attrdatalen);
1404   data->attrdatalen += l;
1405 }
1406
1407 static void
1408 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1409 {
1410   int oldsize;
1411   Id *ida, *pp;
1412
1413   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1414     {
1415       /* great! just append the new data */
1416       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1417       data->attriddatalen--;    /* overwrite terminating 0  */
1418       data->lastdatalen += entrysize;
1419       return;
1420     }
1421   pp = data->structs[handle];
1422   if (pp)
1423     for (; *pp; pp += 2)
1424       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1425         break;
1426   if (!pp || !*pp)
1427     {
1428       /* not found. allocate new key */
1429       Repokey key;
1430       key.name = keyname;
1431       key.type = keytype;
1432       key.size = 0;
1433       key.storage = KEY_STORAGE_INCORE;
1434       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1435       repodata_set(data, handle, &key, data->attriddatalen);
1436       data->lasthandle = 0;     /* next time... */
1437       return;
1438     }
1439   oldsize = 0;
1440   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1441     oldsize += entrysize;
1442   if (ida + 1 == data->attriddata + data->attriddatalen)
1443     {
1444       /* this was the last entry, just append it */
1445       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1446       data->attriddatalen--;    /* overwrite terminating 0  */
1447     }
1448   else
1449     {
1450       /* too bad. move to back. */
1451       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1452       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1453       pp[1] = data->attriddatalen;
1454       data->attriddatalen += oldsize;
1455     }
1456   data->lasthandle = handle;
1457   data->lastkey = *pp;
1458   data->lastdatalen = data->attriddatalen + entrysize + 1;
1459 }
1460
1461 static inline int
1462 checksumtype2len(Id type)
1463 {
1464   switch (type)
1465     {
1466     case REPOKEY_TYPE_MD5:
1467       return SIZEOF_MD5;
1468     case REPOKEY_TYPE_SHA1:
1469       return SIZEOF_SHA1;
1470     case REPOKEY_TYPE_SHA256:
1471       return SIZEOF_SHA256;
1472     default:
1473       return 0;
1474     }
1475 }
1476
1477 void
1478 repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
1479                       const unsigned char *str)
1480 {
1481   Repokey key;
1482   int l = checksumtype2len(type);
1483
1484   if (!l)
1485     return;
1486   key.name = keyname;
1487   key.type = type;
1488   key.size = 0;
1489   key.storage = KEY_STORAGE_INCORE;
1490   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1491   memcpy(data->attrdata + data->attrdatalen, str, l);
1492   repodata_set(data, handle, &key, data->attrdatalen);
1493   data->attrdatalen += l;
1494 }
1495
1496 static int
1497 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1498 {
1499   int i;
1500   for (i = 0; i < buflen; i++)
1501     {
1502 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1503                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1504                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1505                 : -1)
1506       int v = c2h(*str);
1507       str++;
1508       if (v < 0)
1509         return 0;
1510       buf[i] = v;
1511       v = c2h(*str);
1512       str++;
1513       if (v < 0)
1514         return 0;
1515       buf[i] = (buf[i] << 4) | v;
1516 #undef c2h
1517     }
1518   return buflen;
1519 }
1520
1521 void
1522 repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
1523                       const char *str)
1524 {
1525   unsigned char buf[64];
1526   int l = checksumtype2len(type);
1527
1528   if (!l)
1529     return;
1530   if (hexstr2bytes(buf, str, l) != l)
1531     {
1532       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1533       return;
1534     }
1535   repodata_set_bin_checksum(data, handle, keyname, type, buf);
1536 }
1537
1538 const char *
1539 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1540 {
1541   int i, l;
1542   char *str, *s;
1543
1544   l = checksumtype2len(type);
1545   if (!l)
1546     return "";
1547   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1548   for (i = 0; i < l; i++)
1549     {
1550       unsigned char v = buf[i];
1551       unsigned char w = v >> 4;
1552       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1553       w = v & 15;
1554       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1555     }
1556   *s = 0;
1557   return str;
1558 }
1559
1560 Id
1561 repodata_globalize_id(Repodata *data, Id id)
1562
1563   if (!data || !data->localpool)
1564     return id;
1565   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1566 }
1567
1568 void
1569 repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
1570 {
1571   assert(dir);
1572 #if 0
1573 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
1574 #endif
1575   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1576   data->attriddata[data->attriddatalen++] = dir;
1577   data->attriddata[data->attriddatalen++] = num;
1578   data->attriddata[data->attriddatalen++] = num2;
1579   data->attriddata[data->attriddatalen++] = 0;
1580 }
1581
1582 void
1583 repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
1584 {
1585   Id stroff;
1586   int l;
1587
1588   assert(dir);
1589   l = strlen(str) + 1;
1590   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1591   memcpy(data->attrdata + data->attrdatalen, str, l);
1592   stroff = data->attrdatalen;
1593   data->attrdatalen += l;
1594
1595 #if 0
1596 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
1597 #endif
1598   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1599   data->attriddata[data->attriddatalen++] = dir;
1600   data->attriddata[data->attriddatalen++] = stroff;
1601   data->attriddata[data->attriddatalen++] = 0;
1602 }
1603
1604 void
1605 repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
1606 {
1607 #if 0
1608 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
1609 #endif
1610   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
1611   data->attriddata[data->attriddatalen++] = id;
1612   data->attriddata[data->attriddatalen++] = 0;
1613 }
1614
1615 void
1616 repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
1617                            const char *str)
1618 {
1619   Id id;
1620   if (data->localpool)
1621     id = stringpool_str2id(&data->spool, str, 1);
1622   else
1623     id = str2id(data->repo->pool, str, 1);
1624   repodata_add_idarray(data, handle, keyname, id);
1625 }
1626
1627 Id
1628 repodata_create_struct(Repodata *data, Id handle, Id keyname)
1629 {
1630   Id newhandle = get_new_struct(data);
1631   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_COUNTED, 1);
1632   data->attriddata[data->attriddatalen++] = newhandle;
1633   data->attriddata[data->attriddatalen++] = 0;
1634   return newhandle;
1635 }
1636
1637 void
1638 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1639 {
1640   Id *keyp;
1641   if (dest == src
1642       || !(keyp = data->structs[src < 0
1643                                 ? data->extraattrs[-1 - src]
1644                                 : data->attrs[src]]))
1645     return;
1646   dest = repodata_get_handle_int(data, dest);
1647   for (; *keyp; keyp += 2)
1648     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1649 }
1650
1651 /*********************************/
1652
1653 /* unify with repo_write! */
1654
1655 #define EXTDATA_BLOCK 1023
1656 #define SCHEMATA_BLOCK 31
1657 #define SCHEMATADATA_BLOCK 255
1658
1659 struct extdata {
1660   unsigned char *buf;
1661   int len;
1662 };
1663
1664 static void
1665 data_addid(struct extdata *xd, Id x)
1666 {
1667   unsigned char *dp;
1668   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1669   dp = xd->buf + xd->len;
1670
1671   if (x >= (1 << 14))
1672     {
1673       if (x >= (1 << 28))
1674         *dp++ = (x >> 28) | 128;
1675       if (x >= (1 << 21))
1676         *dp++ = (x >> 21) | 128;
1677       *dp++ = (x >> 14) | 128;
1678     }
1679   if (x >= (1 << 7))
1680     *dp++ = (x >> 7) | 128;
1681   *dp++ = x & 127;
1682   xd->len = dp - xd->buf;
1683 }
1684
1685 static void
1686 data_addideof(struct extdata *xd, Id x, int eof)
1687 {
1688   if (x >= 64)
1689     x = (x & 63) | ((x & ~63) << 1);
1690   data_addid(xd, (eof ? x: x | 64));
1691 }
1692
1693 static void
1694 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1695 {
1696   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1697   memcpy(xd->buf + xd->len, blob, len);
1698   xd->len += len;
1699 }
1700
1701 /*********************************/
1702
1703 static void
1704 addschema_prepare(Repodata *data, Id *schematacache)
1705 {
1706   int h, len, i;
1707   Id *sp;
1708
1709   memset(schematacache, 0, 256 * sizeof(Id));
1710   for (i = 0; i < data->nschemata; i++)
1711     {
1712       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1713         h = h * 7 + *sp++;
1714       h &= 255;
1715       schematacache[h] = i + 1;
1716     }
1717   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1718   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1719 }
1720
1721 static Id
1722 addschema(Repodata *data, Id *schema, Id *schematacache)
1723 {
1724   int h, len; 
1725   Id *sp, cid; 
1726
1727   for (sp = schema, len = 0, h = 0; *sp; len++)
1728     h = h * 7 + *sp++;
1729   h &= 255; 
1730   len++;
1731
1732   cid = schematacache[h];
1733   if (cid)
1734     {    
1735       cid--;
1736       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1737         return cid;
1738       /* cache conflict */
1739       for (cid = 0; cid < data->nschemata; cid++)
1740         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1741           return cid;
1742     }
1743   /* a new one. make room. */
1744   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1745   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1746   /* add schema */
1747   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1748   data->schemata[data->nschemata] = data->schemadatalen;
1749   data->schemadatalen += len;
1750   schematacache[h] = data->nschemata + 1;
1751 #if 0
1752 fprintf(stderr, "addschema: new schema\n");
1753 #endif
1754   return data->nschemata++; 
1755 }
1756
1757 static void
1758 repodata_serialize_key(Repodata *data, struct extdata *newincore,
1759                        struct extdata *newvincore,
1760                        Id *schema, Id *schematacache,
1761                        Repokey *key, Id val)
1762 {
1763   /* Otherwise we have a new value.  Parse it into the internal
1764      form.  */
1765   Id *ida;
1766   struct extdata *xd;
1767   unsigned int oldvincorelen = 0;
1768   Id schemaid, *sp;
1769
1770   xd = newincore;
1771   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1772     {
1773       xd = newvincore;
1774       oldvincorelen = xd->len;
1775     }
1776   switch (key->type)
1777     {
1778       case REPOKEY_TYPE_VOID:
1779       case REPOKEY_TYPE_CONSTANT:
1780       case REPOKEY_TYPE_CONSTANTID:
1781         break;
1782       case REPOKEY_TYPE_STR:
1783         data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
1784         break;
1785       case REPOKEY_TYPE_MD5:
1786         data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
1787         break;
1788       case REPOKEY_TYPE_SHA1:
1789         data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
1790         break;
1791       case REPOKEY_TYPE_ID:
1792       case REPOKEY_TYPE_NUM:
1793       case REPOKEY_TYPE_DIR:
1794         data_addid(xd, val);
1795         break;
1796       case REPOKEY_TYPE_IDARRAY:
1797         for (ida = data->attriddata + val; *ida; ida++)
1798           data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1799         break;
1800       case REPOKEY_TYPE_DIRNUMNUMARRAY:
1801         for (ida = data->attriddata + val; *ida; ida += 3)
1802           {
1803             data_addid(xd, ida[0]);
1804             data_addid(xd, ida[1]);
1805             data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1806           }
1807         break;
1808       case REPOKEY_TYPE_DIRSTRARRAY:
1809         for (ida = data->attriddata + val; *ida; ida += 2)
1810           {
1811             data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1812             data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1813           }
1814         break;
1815       case REPOKEY_TYPE_COUNTED:
1816         {
1817           int num = 0;
1818           schemaid = 0;
1819           for (ida = data->attriddata + val; *ida; ida++)
1820             {
1821 #if 0
1822               fprintf(stderr, "serialize struct %d\n", *ida);
1823 #endif
1824               sp = schema;
1825               Id *kp = data->structs[*ida];
1826               if (!kp)
1827                 continue;
1828               num++;
1829               for (;*kp; kp += 2)
1830                 {
1831 #if 0
1832                   fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
1833 #endif
1834                   *sp++ = *kp;
1835                 }
1836               *sp = 0;
1837               if (!schemaid)
1838                 schemaid = addschema(data, schema, schematacache);
1839               else if (schemaid != addschema(data, schema, schematacache))
1840                 {
1841                   fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
1842                   exit(1);
1843                 }
1844 #if 0
1845               fprintf(stderr, "  schema %d\n", schemaid);
1846 #endif
1847             }
1848           if (!num)
1849             break;
1850           data_addid(xd, num);
1851           data_addid(xd, schemaid);
1852           for (ida = data->attriddata + val; *ida; ida++)
1853             {
1854               Id *kp = data->structs[*ida];
1855               if (!kp)
1856                 continue;
1857               for (;*kp; kp += 2)
1858                 {
1859                   repodata_serialize_key(data, newincore, newvincore,
1860                                          schema, schematacache,
1861                                          data->keys + *kp, kp[1]);
1862                 }
1863             }
1864           break;
1865         }
1866       default:
1867         fprintf(stderr, "don't know how to handle type %d\n", key->type);
1868         exit(1);
1869     }
1870   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1871     {
1872       /* put offset/len in incore */
1873       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
1874       oldvincorelen = xd->len - oldvincorelen;
1875       data_addid(newincore, oldvincorelen);
1876     }
1877 }
1878
1879 void
1880 repodata_internalize(Repodata *data)
1881 {
1882   Repokey *key;
1883   Id entry, nentry;
1884   Id schematacache[256];
1885   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1886   unsigned char *dp, *ndp;
1887   int newschema, oldcount;
1888   struct extdata newincore;
1889   struct extdata newvincore;
1890
1891   if (!data->attrs && !data->extraattrs)
1892     return;
1893
1894   newvincore.buf = data->vincore;
1895   newvincore.len = data->vincorelen;
1896
1897   schema = sat_malloc2(data->nkeys, sizeof(Id));
1898   seen = sat_malloc2(data->nkeys, sizeof(Id));
1899
1900   /* Merge the data already existing (in data->schemata, ->incoredata and
1901      friends) with the new attributes in data->attrs[].  */
1902   nentry = data->end - data->start;
1903   addschema_prepare(data, schematacache);
1904   memset(&newincore, 0, sizeof(newincore));
1905   data_addid(&newincore, 0);
1906   if (!data->attrs)
1907     nentry = 0;
1908   for (entry = data->extraattrs ? -data->nextra : 0; entry < nentry; entry++)
1909     {
1910       Id handle;
1911       memset(seen, 0, data->nkeys * sizeof(Id));
1912       sp = schema;
1913       dp = entry2data(data, entry);
1914       if (data->incoredata)
1915         dp = data_read_id(dp, &oldschema);
1916       else
1917         oldschema = 0;
1918 #if 0
1919 fprintf(stderr, "oldschema %d\n", oldschema);
1920 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1921 fprintf(stderr, "schemadata %p\n", data->schemadata);
1922 #endif
1923       /* seen: -1: old data  0: skipped  >0: id + 1 */
1924       newschema = 0;
1925       oldcount = 0;
1926       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1927         {
1928           if (seen[*keyp])
1929             {
1930               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1931               exit(1);
1932             }
1933           seen[*keyp] = -1;
1934           *sp++ = *keyp;
1935           oldcount++;
1936         }
1937       handle = entry < 0 ? data->extraattrs[-1 - entry] : data->attrs[entry];
1938       keyp = data->structs[handle];
1939       if (keyp)
1940         for (; *keyp; keyp += 2)
1941           {
1942             if (!seen[*keyp])
1943               {
1944                 newschema = 1;
1945                 *sp++ = *keyp;
1946               }
1947             seen[*keyp] = keyp[1] + 1;
1948           }
1949       *sp++ = 0;
1950       if (newschema)
1951         /* Ideally we'd like to sort the new schema here, to ensure
1952            schema equality independend of the ordering.  We can't do that
1953            yet.  For once see below (old ids need to come before new ids).
1954            An additional difficulty is that we also need to move
1955            the values with the keys.  */
1956         schemaid = addschema(data, schema, schematacache);
1957       else
1958         schemaid = oldschema;
1959
1960
1961       /* Now create data blob.  We walk through the (possibly new) schema
1962          and either copy over old data, or insert the new.  */
1963       /* XXX Here we rely on the fact that the (new) schema has the form
1964          o1 o2 o3 o4 ... | n1 n2 n3 ...
1965          (oX being the old keyids (possibly overwritten), and nX being
1966           the new keyids).  This rules out sorting the keyids in order
1967          to ensure a small schema count.  */
1968       if (entry < 0)
1969         data->extraoffset[-1 - entry] = newincore.len;
1970       else
1971         data->incoreoffset[entry] = newincore.len;
1972       data_addid(&newincore, schemaid);
1973       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1974         {
1975           key = data->keys + *keyp;
1976 #if 0
1977           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
1978 #endif
1979           ndp = dp;
1980           if (oldcount)
1981             {
1982               /* Skip the data associated with this old key.  */
1983               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1984                 {
1985                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1986                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1987                 }
1988               else if (key->storage == KEY_STORAGE_INCORE)
1989                 ndp = data_skip_recursive(data, dp, key);
1990               oldcount--;
1991             }
1992           if (seen[*keyp] == -1)
1993             {
1994               /* If this key was an old one _and_ was not overwritten with
1995                  a different value copy over the old value (we skipped it
1996                  above).  */
1997               if (dp != ndp)
1998                 data_addblob(&newincore, dp, ndp - dp);
1999               seen[*keyp] = 0;
2000             }
2001           else if (seen[*keyp])
2002             {
2003               /* Otherwise we have a new value.  Parse it into the internal
2004                  form.  */
2005               repodata_serialize_key(data, &newincore, &newvincore,
2006                                      schema, schematacache,
2007                                      key, seen[*keyp] - 1);
2008             }
2009           dp = ndp;
2010         }
2011       if (data->structs[handle])
2012         data->structs[handle] = sat_free(data->structs[handle]);
2013     }
2014   for (entry = 0; entry < data->nstructs; entry++)
2015     if (data->structs[entry])
2016       sat_free(data->structs[entry]);
2017   data->structs = sat_free(data->structs);
2018   data->lasthandle = 0;
2019   data->lastkey = 0;
2020   data->lastdatalen = 0;
2021   sat_free(schema);
2022   sat_free(seen);
2023
2024   sat_free(data->incoredata);
2025   data->incoredata = newincore.buf;
2026   data->incoredatalen = newincore.len;
2027   data->incoredatafree = 0;
2028   
2029   sat_free(data->vincore);
2030   data->vincore = newvincore.buf;
2031   data->vincorelen = newvincore.len;
2032
2033   data->attrs = sat_free(data->attrs);
2034   data->extraattrs = sat_free(data->extraattrs);
2035   data->attrdata = sat_free(data->attrdata);
2036   data->attriddata = sat_free(data->attriddata);
2037   data->attrdatalen = 0;
2038   data->attriddatalen = 0;
2039 }
2040
2041 Id
2042 repodata_str2dir(Repodata *data, const char *dir, int create)
2043 {
2044   Id id, parent;
2045   const char *dire;
2046
2047   parent = 0;
2048   while (*dir == '/' && dir[1] == '/')
2049     dir++;
2050   if (*dir == '/' && !dir[1])
2051     return 1;
2052   while (*dir)
2053     {
2054       dire = strchrnul(dir, '/');
2055       if (data->localpool)
2056         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
2057       else
2058         id = strn2id(data->repo->pool, dir, dire - dir, create);
2059       if (!id)
2060         return 0;
2061       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
2062       if (!parent)
2063         return 0;
2064       if (!*dire)
2065         break;
2066       dir = dire + 1;
2067       while (*dir == '/')
2068         dir++;
2069     }
2070   return parent;
2071 }
2072
2073 const char *
2074 repodata_dir2str(Repodata *data, Id did, const char *suf)
2075 {
2076   Pool *pool = data->repo->pool;
2077   int l = 0;
2078   Id parent, comp;
2079   const char *comps;
2080   char *p;
2081
2082   if (!did)
2083     return suf ? suf : "";
2084   parent = did;
2085   while (parent)
2086     {
2087       comp = dirpool_compid(&data->dirpool, parent);
2088       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
2089       l += strlen(comps);
2090       parent = dirpool_parent(&data->dirpool, parent);
2091       if (parent)
2092         l++;
2093     }
2094   if (suf)
2095     l += strlen(suf) + 1;
2096   p = pool_alloctmpspace(pool, l + 1) + l;
2097   *p = 0;
2098   if (suf)
2099     {
2100       p -= strlen(suf);
2101       strcpy(p, suf);
2102       *--p = '/';
2103     }
2104   parent = did;
2105   while (parent)
2106     {
2107       comp = dirpool_compid(&data->dirpool, parent);
2108       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
2109       l = strlen(comps);
2110       p -= l;
2111       strncpy(p, comps, l);
2112       parent = dirpool_parent(&data->dirpool, parent);
2113       if (parent)
2114         *--p = '/';
2115     }
2116   return p;
2117 }
2118
2119 unsigned int
2120 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
2121 {
2122   return compress_buf(page, len, cpage, max);
2123 }
2124
2125 #define SOLV_ERROR_EOF              3
2126
2127 static inline unsigned int
2128 read_u32(FILE *fp)
2129 {
2130   int c, i;
2131   unsigned int x = 0; 
2132
2133   for (i = 0; i < 4; i++) 
2134     {    
2135       c = getc(fp);
2136       if (c == EOF) 
2137         return 0;
2138       x = (x << 8) | c; 
2139     }    
2140   return x;
2141 }
2142
2143 #define SOLV_ERROR_EOF          3
2144 #define SOLV_ERROR_CORRUPT      6
2145
2146 /* Try to either setup on-demand paging (using FP as backing
2147    file), or in case that doesn't work (FP not seekable) slurps in
2148    all pages and deactivates paging.  */
2149 void
2150 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
2151 {
2152   FILE *fp = data->fp;
2153   unsigned int npages;
2154   unsigned int i;
2155   unsigned int can_seek;
2156   long cur_file_ofs;
2157   unsigned char buf[BLOB_PAGESIZE];
2158
2159   if (pagesz != BLOB_PAGESIZE)
2160     {
2161       /* We could handle this by slurping in everything.  */
2162       data->error = SOLV_ERROR_CORRUPT;
2163       return;
2164     }
2165   can_seek = 1;
2166   if ((cur_file_ofs = ftell(fp)) < 0)
2167     can_seek = 0;
2168   clearerr(fp);
2169   if (can_seek)
2170     data->pagefd = dup(fileno(fp));
2171   if (data->pagefd == -1)
2172     can_seek = 0;
2173
2174 #ifdef DEBUG_PAGING
2175   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
2176 #endif
2177   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
2178
2179   data->num_pages = npages;
2180   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
2181
2182   /* If we can't seek on our input we have to slurp in everything.  */
2183   if (!can_seek)
2184     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
2185   for (i = 0; i < npages; i++)
2186     {
2187       unsigned int in_len = read_u32(fp);
2188       unsigned int compressed = in_len & 1;
2189       Attrblobpage *p = data->pages + i;
2190       in_len >>= 1;
2191 #ifdef DEBUG_PAGING
2192       fprintf (stderr, "page %d: len %d (%scompressed)\n",
2193                i, in_len, compressed ? "" : "not ");
2194 #endif
2195       if (can_seek)
2196         {
2197           cur_file_ofs += 4;
2198           p->mapped_at = -1;
2199           p->file_offset = cur_file_ofs;
2200           p->file_size = in_len * 2 + compressed;
2201           if (fseek(fp, in_len, SEEK_CUR) < 0)
2202             {
2203               perror ("fseek");
2204               fprintf (stderr, "can't seek after we thought we can\n");
2205               /* We can't fall back to non-seeking behaviour as we already
2206                  read over some data pages without storing them away.  */
2207               data->error = SOLV_ERROR_EOF;
2208               close(data->pagefd);
2209               data->pagefd = -1;
2210               return;
2211             }
2212           cur_file_ofs += in_len;
2213         }
2214       else
2215         {
2216           unsigned int out_len;
2217           void *dest = data->blob_store + i * BLOB_PAGESIZE;
2218           p->mapped_at = i * BLOB_PAGESIZE;
2219           p->file_offset = 0;
2220           p->file_size = 0;
2221           /* We can't seek, so suck everything in.  */
2222           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
2223             {
2224               perror("fread");
2225               data->error = SOLV_ERROR_EOF;
2226               return;
2227             }
2228           if (compressed)
2229             {
2230               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
2231               if (out_len != BLOB_PAGESIZE && i < npages - 1)
2232                 {
2233                   data->error = SOLV_ERROR_CORRUPT;
2234                   return;
2235                 }
2236             }
2237         }
2238     }
2239 }
2240
2241 void
2242 repodata_disable_paging(Repodata *data)
2243 {
2244   if (maybe_load_repodata(data, 0)
2245       && data->num_pages)
2246     load_page_range (data, 0, data->num_pages - 1);
2247 }
2248 /*
2249 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2250 */