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