- enable regex matching in Dataiterator
[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 void
1123 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
1124 {
1125   Id *pp;
1126   Id *ap;
1127   int i;
1128   if (!data->attrs && entry >= 0)
1129     {
1130       data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
1131                                      REPODATA_BLOCK);
1132     }
1133   else if (!data->extraattrs && entry < 0)
1134     data->extraattrs = sat_calloc_block(data->nextra, sizeof(Id *), REPODATA_BLOCK);
1135   if (entry < 0)
1136     ap = data->extraattrs[-1 - entry];
1137   else
1138     ap = data->attrs[entry];
1139   i = 0;
1140   if (ap)
1141     {
1142       for (pp = ap; *pp; pp += 2)
1143         /* Determine equality based on the name only, allows us to change
1144            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1145         if (data->keys[*pp].name == data->keys[keyid].name)
1146           break;
1147       if (*pp)
1148         {
1149           if (overwrite)
1150             {
1151               pp[0] = keyid;
1152               pp[1] = val;
1153             }
1154           return;
1155         }
1156       i = pp - ap;
1157     }
1158   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1159   if (entry < 0)
1160     data->extraattrs[-1 - entry] = ap;
1161   else
1162     data->attrs[entry] = ap;
1163   pp = ap + i;
1164   *pp++ = keyid;
1165   *pp++ = val;
1166   *pp = 0;
1167 }
1168
1169 void
1170 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
1171 {
1172   Id keyid;
1173
1174   /* find key in keys */
1175   for (keyid = 1; keyid < data->nkeys; keyid++)
1176     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
1177       {
1178         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
1179           continue;
1180         break;
1181       }
1182   if (keyid == data->nkeys)
1183     {
1184       /* allocate new key */
1185       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
1186       data->keys[data->nkeys++] = *key;
1187       if (data->verticaloffset)
1188         {
1189           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
1190           data->verticaloffset[data->nkeys - 1] = 0;
1191         }
1192     }
1193   repodata_insert_keyid(data, entry, keyid, val, 1);
1194 }
1195
1196 void
1197 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
1198 {
1199   Repokey key;
1200   key.name = keyname;
1201   key.type = REPOKEY_TYPE_ID;
1202   key.size = 0;
1203   key.storage = KEY_STORAGE_INCORE;
1204   repodata_set(data, entry, &key, id);
1205 }
1206
1207 void
1208 repodata_set_num(Repodata *data, Id entry, Id keyname, unsigned int num)
1209 {
1210   Repokey key;
1211   key.name = keyname;
1212   key.type = REPOKEY_TYPE_NUM;
1213   key.size = 0;
1214   key.storage = KEY_STORAGE_INCORE;
1215   repodata_set(data, entry, &key, (Id)num);
1216 }
1217
1218 void
1219 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
1220 {
1221   Repokey key;
1222   Id id;
1223   if (data->localpool)
1224     id = stringpool_str2id(&data->spool, str, 1);
1225   else
1226     id = str2id(data->repo->pool, str, 1);
1227   key.name = keyname;
1228   key.type = REPOKEY_TYPE_ID;
1229   key.size = 0;
1230   key.storage = KEY_STORAGE_INCORE;
1231   repodata_set(data, entry, &key, id);
1232 }
1233
1234 void
1235 repodata_set_constant(Repodata *data, Id entry, Id keyname, unsigned int constant)
1236 {
1237   Repokey key;
1238   key.name = keyname;
1239   key.type = REPOKEY_TYPE_CONSTANT;
1240   key.size = constant;
1241   key.storage = KEY_STORAGE_INCORE;
1242   repodata_set(data, entry, &key, 0);
1243 }
1244
1245 void
1246 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1247 {
1248   Repokey key;
1249   key.name = keyname;
1250   key.type = REPOKEY_TYPE_CONSTANTID;
1251   key.size = id;
1252   key.storage = KEY_STORAGE_INCORE;
1253   repodata_set(data, entry, &key, 0);
1254 }
1255
1256 void
1257 repodata_set_void(Repodata *data, Id entry, Id keyname)
1258 {
1259   Repokey key;
1260   key.name = keyname;
1261   key.type = REPOKEY_TYPE_VOID;
1262   key.size = 0;
1263   key.storage = KEY_STORAGE_INCORE;
1264   repodata_set(data, entry, &key, 0);
1265 }
1266
1267 void
1268 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1269 {
1270   Repokey key;
1271   int l;
1272
1273   l = strlen(str) + 1;
1274   key.name = keyname;
1275   key.type = REPOKEY_TYPE_STR;
1276   key.size = 0;
1277   key.storage = KEY_STORAGE_INCORE;
1278   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1279   memcpy(data->attrdata + data->attrdatalen, str, l);
1280   repodata_set(data, entry, &key, data->attrdatalen);
1281   data->attrdatalen += l;
1282 }
1283
1284 static void
1285 repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
1286 {
1287   int oldsize;
1288   Id *ida, *pp;
1289
1290   if (entry < 0)
1291     pp = data->extraattrs ? data->extraattrs[-1 - entry] : 0;
1292   else
1293     pp = data->attrs ? data->attrs[entry] : 0;
1294   if (pp)
1295     for (; *pp; pp += 2)
1296       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1297         break;
1298   if (!pp || !*pp)
1299     {
1300       /* not found. allocate new key */
1301       Repokey key;
1302       key.name = keyname;
1303       key.type = keytype;
1304       key.size = 0;
1305       key.storage = KEY_STORAGE_INCORE;
1306       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1307       repodata_set(data, entry, &key, data->attriddatalen);
1308       return;
1309     }
1310   oldsize = 0;
1311   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1312     oldsize += entrysize;
1313   if (ida + 1 == data->attriddata + data->attriddatalen)
1314     {
1315       /* this was the last entry, just append it */
1316       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1317       data->attriddatalen--;    /* overwrite terminating 0  */
1318     }
1319   else
1320     {
1321       /* too bad. move to back. */
1322       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1323       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1324       pp[1] = data->attriddatalen;
1325       data->attriddatalen += oldsize;
1326     }
1327 }
1328
1329 static inline int
1330 checksumtype2len(Id type)
1331 {
1332   switch (type)
1333     {
1334     case REPOKEY_TYPE_MD5:
1335       return SIZEOF_MD5;
1336     case REPOKEY_TYPE_SHA1:
1337       return SIZEOF_SHA1;
1338     case REPOKEY_TYPE_SHA256:
1339       return SIZEOF_SHA256;
1340     default:
1341       return 0;
1342     }
1343 }
1344
1345 void
1346 repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1347                       const unsigned char *str)
1348 {
1349   Repokey key;
1350   int l = checksumtype2len(type);
1351
1352   if (!l)
1353     return;
1354   key.name = keyname;
1355   key.type = type;
1356   key.size = 0;
1357   key.storage = KEY_STORAGE_INCORE;
1358   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1359   memcpy(data->attrdata + data->attrdatalen, str, l);
1360   repodata_set(data, entry, &key, data->attrdatalen);
1361   data->attrdatalen += l;
1362 }
1363
1364 static int
1365 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1366 {
1367   int i;
1368   for (i = 0; i < buflen; i++)
1369     {
1370 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1371                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1372                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1373                 : -1)
1374       int v = c2h(*str);
1375       str++;
1376       if (v < 0)
1377         return 0;
1378       buf[i] = v;
1379       v = c2h(*str);
1380       str++;
1381       if (v < 0)
1382         return 0;
1383       buf[i] = (buf[i] << 4) | v;
1384 #undef c2h
1385     }
1386   return buflen;
1387 }
1388
1389 void
1390 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1391                       const char *str)
1392 {
1393   unsigned char buf[64];
1394   int l = checksumtype2len(type);
1395
1396   if (!l)
1397     return;
1398   if (hexstr2bytes(buf, str, l) != l)
1399     {
1400       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1401       return;
1402     }
1403   repodata_set_bin_checksum(data, entry, keyname, type, buf);
1404 }
1405
1406 const char *
1407 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1408 {
1409   int i, l;
1410   char *str, *s;
1411
1412   l = checksumtype2len(type);
1413   if (!l)
1414     return "";
1415   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1416   for (i = 0; i < l; i++)
1417     {
1418       unsigned char v = buf[i];
1419       unsigned char w = v >> 4;
1420       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1421       w = v & 15;
1422       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1423     }
1424   *s = 0;
1425   return str;
1426 }
1427
1428 Id
1429 repodata_globalize_id(Repodata *data, Id id)
1430
1431   if (!data || !data->localpool)
1432     return id;
1433   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1434 }
1435
1436 void
1437 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1438 {
1439
1440 #if 0
1441 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1442 #endif
1443   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1444   data->attriddata[data->attriddatalen++] = dir;
1445   data->attriddata[data->attriddatalen++] = num;
1446   data->attriddata[data->attriddatalen++] = num2;
1447   data->attriddata[data->attriddatalen++] = 0;
1448 }
1449
1450 void
1451 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1452 {
1453   Id stroff;
1454   int l;
1455
1456   l = strlen(str) + 1;
1457   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1458   memcpy(data->attrdata + data->attrdatalen, str, l);
1459   stroff = data->attrdatalen;
1460   data->attrdatalen += l;
1461
1462 #if 0
1463 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1464 #endif
1465   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1466   data->attriddata[data->attriddatalen++] = dir;
1467   data->attriddata[data->attriddatalen++] = stroff;
1468   data->attriddata[data->attriddatalen++] = 0;
1469 }
1470
1471 void
1472 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1473 {
1474 #if 0
1475 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1476 #endif
1477   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1478   data->attriddata[data->attriddatalen++] = id;
1479   data->attriddata[data->attriddatalen++] = 0;
1480 }
1481
1482 void
1483 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1484                            const char *str)
1485 {
1486   Id id;
1487   if (data->localpool)
1488     id = stringpool_str2id(&data->spool, str, 1);
1489   else
1490     id = str2id(data->repo->pool, str, 1);
1491   repodata_add_idarray(data, entry, keyname, id);
1492 }
1493
1494 void
1495 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1496 {
1497   Id *keyp;
1498   if (dest == src
1499       || !(keyp = src < 0 ? data->extraattrs[-1 - src] : data->attrs[src]))
1500     return;
1501   for (; *keyp; keyp += 2)
1502     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1503 }
1504
1505 /*********************************/
1506
1507 /* unify with repo_write! */
1508
1509 #define EXTDATA_BLOCK 1023
1510 #define SCHEMATA_BLOCK 31
1511 #define SCHEMATADATA_BLOCK 255
1512
1513 struct extdata {
1514   unsigned char *buf;
1515   int len;
1516 };
1517
1518 static void
1519 data_addid(struct extdata *xd, Id x)
1520 {
1521   unsigned char *dp;
1522   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1523   dp = xd->buf + xd->len;
1524
1525   if (x >= (1 << 14))
1526     {
1527       if (x >= (1 << 28))
1528         *dp++ = (x >> 28) | 128;
1529       if (x >= (1 << 21))
1530         *dp++ = (x >> 21) | 128;
1531       *dp++ = (x >> 14) | 128;
1532     }
1533   if (x >= (1 << 7))
1534     *dp++ = (x >> 7) | 128;
1535   *dp++ = x & 127;
1536   xd->len = dp - xd->buf;
1537 }
1538
1539 static void
1540 data_addideof(struct extdata *xd, Id x, int eof)
1541 {
1542   if (x >= 64)
1543     x = (x & 63) | ((x & ~63) << 1);
1544   data_addid(xd, (eof ? x: x | 64));
1545 }
1546
1547 static void
1548 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1549 {
1550   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1551   memcpy(xd->buf + xd->len, blob, len);
1552   xd->len += len;
1553 }
1554
1555 /*********************************/
1556
1557 static void
1558 addschema_prepare(Repodata *data, Id *schematacache)
1559 {
1560   int h, len, i;
1561   Id *sp;
1562
1563   memset(schematacache, 0, 256 * sizeof(Id));
1564   for (i = 0; i < data->nschemata; i++)
1565     {
1566       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1567         h = h * 7 + *sp++;
1568       h &= 255;
1569       schematacache[h] = i + 1;
1570     }
1571   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1572   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1573 }
1574
1575 static Id
1576 addschema(Repodata *data, Id *schema, Id *schematacache)
1577 {
1578   int h, len; 
1579   Id *sp, cid; 
1580
1581   for (sp = schema, len = 0, h = 0; *sp; len++)
1582     h = h * 7 + *sp++;
1583   h &= 255; 
1584   len++;
1585
1586   cid = schematacache[h];
1587   if (cid)
1588     {    
1589       cid--;
1590       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1591         return cid;
1592       /* cache conflict */
1593       for (cid = 0; cid < data->nschemata; cid++)
1594         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1595           return cid;
1596     }
1597   /* a new one. make room. */
1598   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1599   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1600   /* add schema */
1601   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1602   data->schemata[data->nschemata] = data->schemadatalen;
1603   data->schemadatalen += len;
1604   schematacache[h] = data->nschemata + 1;
1605 #if 0
1606 fprintf(stderr, "addschema: new schema\n");
1607 #endif
1608   return data->nschemata++; 
1609 }
1610
1611
1612 void
1613 repodata_internalize(Repodata *data)
1614 {
1615   Repokey *key;
1616   Id id, entry, nentry, *ida;
1617   Id schematacache[256];
1618   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1619   unsigned char *dp, *ndp;
1620   int newschema, oldcount;
1621   struct extdata newincore;
1622   struct extdata newvincore;
1623
1624   if (!data->attrs && !data->extraattrs)
1625     return;
1626
1627   newvincore.buf = data->vincore;
1628   newvincore.len = data->vincorelen;
1629
1630   schema = sat_malloc2(data->nkeys, sizeof(Id));
1631   seen = sat_malloc2(data->nkeys, sizeof(Id));
1632
1633   /* Merge the data already existing (in data->schemata, ->incoredata and
1634      friends) with the new attributes in data->attrs[].  */
1635   nentry = data->end - data->start;
1636   addschema_prepare(data, schematacache);
1637   memset(&newincore, 0, sizeof(newincore));
1638   data_addid(&newincore, 0);
1639   if (!data->attrs)
1640     nentry = 0;
1641   for (entry = data->extraattrs ? -data->nextra : 0; entry < nentry; entry++)
1642     {
1643       memset(seen, 0, data->nkeys * sizeof(Id));
1644       sp = schema;
1645       dp = entry2data(data, entry);
1646       if (data->incoredata)
1647         dp = data_read_id(dp, &oldschema);
1648       else
1649         oldschema = 0;
1650 #if 0
1651 fprintf(stderr, "oldschema %d\n", oldschema);
1652 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1653 fprintf(stderr, "schemadata %p\n", data->schemadata);
1654 #endif
1655       /* seen: -1: old data  0: skipped  >0: id + 1 */
1656       newschema = 0;
1657       oldcount = 0;
1658       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1659         {
1660           if (seen[*keyp])
1661             {
1662               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1663               exit(1);
1664             }
1665           seen[*keyp] = -1;
1666           *sp++ = *keyp;
1667           oldcount++;
1668         }
1669       keyp = entry < 0 ? data->extraattrs[-1 - entry] : data->attrs[entry];
1670       if (keyp)
1671         for (; *keyp; keyp += 2)
1672           {
1673             if (!seen[*keyp])
1674               {
1675                 newschema = 1;
1676                 *sp++ = *keyp;
1677               }
1678             seen[*keyp] = keyp[1] + 1;
1679           }
1680       *sp++ = 0;
1681       if (newschema)
1682         /* Ideally we'd like to sort the new schema here, to ensure
1683            schema equality independend of the ordering.  We can't do that
1684            yet.  For once see below (old ids need to come before new ids).
1685            An additional difficulty is that we also need to move
1686            the values with the keys.  */
1687         schemaid = addschema(data, schema, schematacache);
1688       else
1689         schemaid = oldschema;
1690
1691
1692       /* Now create data blob.  We walk through the (possibly new) schema
1693          and either copy over old data, or insert the new.  */
1694       /* XXX Here we rely on the fact that the (new) schema has the form
1695          o1 o2 o3 o4 ... | n1 n2 n3 ...
1696          (oX being the old keyids (possibly overwritten), and nX being
1697           the new keyids).  This rules out sorting the keyids in order
1698          to ensure a small schema count.  */
1699       if (entry < 0)
1700         data->extraoffset[-1 - entry] = newincore.len;
1701       else
1702         data->incoreoffset[entry] = newincore.len;
1703       data_addid(&newincore, schemaid);
1704       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1705         {
1706           key = data->keys + *keyp;
1707           ndp = dp;
1708           if (oldcount)
1709             {
1710               /* Skip the data associated with this old key.  */
1711               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1712                 {
1713                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1714                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1715                 }
1716               else if (key->storage == KEY_STORAGE_INCORE)
1717                 ndp = data_skip(dp, key->type);
1718               oldcount--;
1719             }
1720           if (seen[*keyp] == -1)
1721             {
1722               /* If this key was an old one _and_ was not overwritten with
1723                  a different value copy over the old value (we skipped it
1724                  above).  */
1725               if (dp != ndp)
1726                 data_addblob(&newincore, dp, ndp - dp);
1727               seen[*keyp] = 0;
1728             }
1729           else if (seen[*keyp])
1730             {
1731               /* Otherwise we have a new value.  Parse it into the internal
1732                  form.  */
1733               struct extdata *xd;
1734               unsigned int oldvincorelen = 0;
1735
1736               xd = &newincore;
1737               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1738                 {
1739                   xd = &newvincore;
1740                   oldvincorelen = xd->len;
1741                 }
1742               id = seen[*keyp] - 1;
1743               switch (key->type)
1744                 {
1745                 case REPOKEY_TYPE_VOID:
1746                 case REPOKEY_TYPE_CONSTANT:
1747                 case REPOKEY_TYPE_CONSTANTID:
1748                   break;
1749                 case REPOKEY_TYPE_STR:
1750                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1751                   break;
1752                 case REPOKEY_TYPE_MD5:
1753                   data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1754                   break;
1755                 case REPOKEY_TYPE_SHA1:
1756                   data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1757                   break;
1758                 case REPOKEY_TYPE_ID:
1759                 case REPOKEY_TYPE_NUM:
1760                 case REPOKEY_TYPE_DIR:
1761                   data_addid(xd, id);
1762                   break;
1763                 case REPOKEY_TYPE_IDARRAY:
1764                   for (ida = data->attriddata + id; *ida; ida++)
1765                     data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1766                   break;
1767                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1768                   for (ida = data->attriddata + id; *ida; ida += 3)
1769                     {
1770                       data_addid(xd, ida[0]);
1771                       data_addid(xd, ida[1]);
1772                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1773                     }
1774                   break;
1775                 case REPOKEY_TYPE_DIRSTRARRAY:
1776                   for (ida = data->attriddata + id; *ida; ida += 2)
1777                     {
1778                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1779                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1780                     }
1781                   break;
1782                 default:
1783                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1784                   exit(1);
1785                 }
1786               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1787                 {
1788                   /* put offset/len in incore */
1789                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1790                   oldvincorelen = xd->len - oldvincorelen;
1791                   data_addid(&newincore, oldvincorelen);
1792                 }
1793             }
1794           dp = ndp;
1795         }
1796       if (entry < 0 && data->extraattrs[-1 - entry])
1797         sat_free(data->extraattrs[-1 - entry]);
1798       else if (entry >= 0 && data->attrs[entry])
1799         sat_free(data->attrs[entry]);
1800     }
1801   sat_free(schema);
1802   sat_free(seen);
1803
1804   sat_free(data->incoredata);
1805   data->incoredata = newincore.buf;
1806   data->incoredatalen = newincore.len;
1807   data->incoredatafree = 0;
1808   
1809   sat_free(data->vincore);
1810   data->vincore = newvincore.buf;
1811   data->vincorelen = newvincore.len;
1812
1813   data->attrs = sat_free(data->attrs);
1814   data->extraattrs = sat_free(data->extraattrs);
1815   data->attrdata = sat_free(data->attrdata);
1816   data->attriddata = sat_free(data->attriddata);
1817   data->attrdatalen = 0;
1818   data->attriddatalen = 0;
1819 }
1820
1821 Id
1822 repodata_str2dir(Repodata *data, const char *dir, int create)
1823 {
1824   Id id, parent;
1825   const char *dire;
1826
1827   parent = 0;
1828   while (*dir == '/' && dir[1] == '/')
1829     dir++;
1830   if (*dir == '/' && !dir[1])
1831     return 1;
1832   while (*dir)
1833     {
1834       dire = strchrnul(dir, '/');
1835       if (data->localpool)
1836         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1837       else
1838         id = strn2id(data->repo->pool, dir, dire - dir, create);
1839       if (!id)
1840         return 0;
1841       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1842       if (!parent)
1843         return 0;
1844       if (!*dire)
1845         break;
1846       dir = dire + 1;
1847       while (*dir == '/')
1848         dir++;
1849     }
1850   return parent;
1851 }
1852
1853 const char *
1854 repodata_dir2str(Repodata *data, Id did, const char *suf)
1855 {
1856   Pool *pool = data->repo->pool;
1857   int l = 0;
1858   Id parent, comp;
1859   const char *comps;
1860   char *p;
1861
1862   if (!did)
1863     return suf ? suf : "";
1864   parent = did;
1865   while (parent)
1866     {
1867       comp = dirpool_compid(&data->dirpool, parent);
1868       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1869       l += strlen(comps);
1870       parent = dirpool_parent(&data->dirpool, parent);
1871       if (parent)
1872         l++;
1873     }
1874   if (suf)
1875     l += strlen(suf) + 1;
1876   p = pool_alloctmpspace(pool, l + 1) + l;
1877   *p = 0;
1878   if (suf)
1879     {
1880       p -= strlen(suf);
1881       strcpy(p, suf);
1882       *--p = '/';
1883     }
1884   parent = did;
1885   while (parent)
1886     {
1887       comp = dirpool_compid(&data->dirpool, parent);
1888       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1889       l = strlen(comps);
1890       p -= l;
1891       strncpy(p, comps, l);
1892       parent = dirpool_parent(&data->dirpool, parent);
1893       if (parent)
1894         *--p = '/';
1895     }
1896   return p;
1897 }
1898
1899 unsigned int
1900 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1901 {
1902   return compress_buf(page, len, cpage, max);
1903 }
1904
1905 #define SOLV_ERROR_EOF              3
1906
1907 static inline unsigned int
1908 read_u32(FILE *fp)
1909 {
1910   int c, i;
1911   unsigned int x = 0; 
1912
1913   for (i = 0; i < 4; i++) 
1914     {    
1915       c = getc(fp);
1916       if (c == EOF) 
1917         return 0;
1918       x = (x << 8) | c; 
1919     }    
1920   return x;
1921 }
1922
1923 #define SOLV_ERROR_EOF          3
1924 #define SOLV_ERROR_CORRUPT      6
1925
1926 /* Try to either setup on-demand paging (using FP as backing
1927    file), or in case that doesn't work (FP not seekable) slurps in
1928    all pages and deactivates paging.  */
1929 void
1930 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1931 {
1932   FILE *fp = data->fp;
1933   unsigned int npages;
1934   unsigned int i;
1935   unsigned int can_seek;
1936   long cur_file_ofs;
1937   unsigned char buf[BLOB_PAGESIZE];
1938
1939   if (pagesz != BLOB_PAGESIZE)
1940     {
1941       /* We could handle this by slurping in everything.  */
1942       data->error = SOLV_ERROR_CORRUPT;
1943       return;
1944     }
1945   can_seek = 1;
1946   if ((cur_file_ofs = ftell(fp)) < 0)
1947     can_seek = 0;
1948   clearerr(fp);
1949   if (can_seek)
1950     data->pagefd = dup(fileno(fp));
1951   if (data->pagefd == -1)
1952     can_seek = 0;
1953
1954 #ifdef DEBUG_PAGING
1955   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1956 #endif
1957   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1958
1959   data->num_pages = npages;
1960   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1961
1962   /* If we can't seek on our input we have to slurp in everything.  */
1963   if (!can_seek)
1964     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1965   for (i = 0; i < npages; i++)
1966     {
1967       unsigned int in_len = read_u32(fp);
1968       unsigned int compressed = in_len & 1;
1969       Attrblobpage *p = data->pages + i;
1970       in_len >>= 1;
1971 #ifdef DEBUG_PAGING
1972       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1973                i, in_len, compressed ? "" : "not ");
1974 #endif
1975       if (can_seek)
1976         {
1977           cur_file_ofs += 4;
1978           p->mapped_at = -1;
1979           p->file_offset = cur_file_ofs;
1980           p->file_size = in_len * 2 + compressed;
1981           if (fseek(fp, in_len, SEEK_CUR) < 0)
1982             {
1983               perror ("fseek");
1984               fprintf (stderr, "can't seek after we thought we can\n");
1985               /* We can't fall back to non-seeking behaviour as we already
1986                  read over some data pages without storing them away.  */
1987               data->error = SOLV_ERROR_EOF;
1988               close(data->pagefd);
1989               data->pagefd = -1;
1990               return;
1991             }
1992           cur_file_ofs += in_len;
1993         }
1994       else
1995         {
1996           unsigned int out_len;
1997           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1998           p->mapped_at = i * BLOB_PAGESIZE;
1999           p->file_offset = 0;
2000           p->file_size = 0;
2001           /* We can't seek, so suck everything in.  */
2002           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
2003             {
2004               perror("fread");
2005               data->error = SOLV_ERROR_EOF;
2006               return;
2007             }
2008           if (compressed)
2009             {
2010               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
2011               if (out_len != BLOB_PAGESIZE && i < npages - 1)
2012                 {
2013                   data->error = SOLV_ERROR_CORRUPT;
2014                   return;
2015                 }
2016             }
2017         }
2018     }
2019 }
2020
2021 void
2022 repodata_disable_paging(Repodata *data)
2023 {
2024   if (maybe_load_repodata(data, 0)
2025       && data->num_pages)
2026     load_page_range (data, 0, data->num_pages - 1);
2027 }
2028 /*
2029 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2030 */