init matcher.pool
[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 #include <regex.h>
24
25 #include "repo.h"
26 #include "pool.h"
27 #include "poolid_private.h"
28 #include "util.h"
29
30 #include "repopack.h"
31 #include "repopage.h"
32
33 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
34                                   unsigned char *out, unsigned int out_len);
35 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
36                                               unsigned int in_len,
37                                               unsigned char *out,
38                                               unsigned int out_len);
39
40 #define REPODATA_BLOCK 255
41
42
43 void
44 repodata_init(Repodata *data, Repo *repo, int localpool)
45 {
46   memset(data, 0, sizeof (*data));
47   data->repo = repo;
48   data->localpool = localpool;
49   if (localpool)
50     stringpool_init_empty(&data->spool);
51   data->keys = sat_calloc(1, sizeof(Repokey));
52   data->nkeys = 1;
53   data->schemata = sat_calloc(1, sizeof(Id));
54   data->schemadata = sat_calloc(1, sizeof(Id));
55   data->nschemata = 1;
56   data->schemadatalen = 1;
57   repopagestore_init(&data->store);
58 }
59
60 void
61 repodata_free(Repodata *data)
62 {
63   int i;
64
65   sat_free(data->keys);
66
67   sat_free(data->schemata);
68   sat_free(data->schemadata);
69   sat_free(data->schematahash);
70
71   stringpool_free(&data->spool);
72   dirpool_free(&data->dirpool);
73
74   sat_free(data->mainschemaoffsets);
75   sat_free(data->incoredata);
76   sat_free(data->incoreoffset);
77   sat_free(data->verticaloffset);
78
79   repopagestore_free(&data->store);
80
81   sat_free(data->vincore);
82
83   if (data->attrs)
84     for (i = 0; i < data->end - data->start; i++)
85       sat_free(data->attrs[i]);
86   sat_free(data->attrs);
87   if (data->xattrs)
88     for (i = 0; i < data->nxattrs; i++)
89       sat_free(data->xattrs[i]);
90   sat_free(data->xattrs);
91
92   sat_free(data->attrdata);
93   sat_free(data->attriddata);
94 }
95
96
97 /***************************************************************
98  * key pool management
99  */
100
101 /* this is not so time critical that we need a hash, so we do a simple
102  * linear search */
103 Id
104 repodata_key2id(Repodata *data, Repokey *key, int create)
105 {
106   Id keyid;
107
108   for (keyid = 1; keyid < data->nkeys; keyid++)
109     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
110       {
111         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
112           continue;
113         break;
114       }
115   if (keyid == data->nkeys)
116     {
117       if (!create)
118         return 0;
119       /* allocate new key */
120       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
121       data->keys[data->nkeys++] = *key;
122       if (data->verticaloffset)
123         {
124           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
125           data->verticaloffset[data->nkeys - 1] = 0;
126         }
127       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
128     }
129   return keyid;
130 }
131
132
133 /***************************************************************
134  * schema pool management
135  */
136
137 #define SCHEMATA_BLOCK 31
138 #define SCHEMATADATA_BLOCK 255
139
140 Id
141 repodata_schema2id(Repodata *data, Id *schema, int create)
142 {
143   int h, len, i;
144   Id *sp, cid;
145   Id *schematahash;
146
147   if ((schematahash = data->schematahash) == 0)
148     {
149       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
150       for (i = 0; i < data->nschemata; i++)
151         {
152           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
153             h = h * 7 + *sp++;
154           h &= 255;
155           schematahash[h] = i + 1;
156         }
157       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
158       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
159     }
160
161   for (sp = schema, len = 0, h = 0; *sp; len++)
162     h = h * 7 + *sp++;
163   h &= 255;
164   len++;
165
166   cid = schematahash[h];
167   if (cid)
168     {
169       cid--;
170       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
171         return cid;
172       /* cache conflict */
173       for (cid = 0; cid < data->nschemata; cid++)
174         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
175           return cid;
176     }
177   /* a new one */
178   if (!create)
179     return 0;
180   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
181   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
182   /* add schema */
183   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
184   data->schemata[data->nschemata] = data->schemadatalen;
185   data->schemadatalen += len;
186   schematahash[h] = data->nschemata + 1;
187 #if 0
188 fprintf(stderr, "schema2id: new schema\n");
189 #endif
190   return data->nschemata++;
191 }
192
193 void
194 repodata_free_schemahash(Repodata *data)
195 {
196   data->schematahash = sat_free(data->schematahash);
197   /* shrink arrays */
198   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
199   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
200 }
201
202
203 /***************************************************************
204  * dir pool management
205  */
206
207 Id
208 repodata_str2dir(Repodata *data, const char *dir, int create)
209 {
210   Id id, parent;
211   const char *dire;
212
213   parent = 0;
214   while (*dir == '/' && dir[1] == '/')
215     dir++;
216   if (*dir == '/' && !dir[1])
217     return 1;
218   while (*dir)
219     {
220       dire = strchrnul(dir, '/');
221       if (data->localpool)
222         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
223       else
224         id = strn2id(data->repo->pool, dir, dire - dir, create);
225       if (!id)
226         return 0;
227       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
228       if (!parent)
229         return 0;
230       if (!*dire)
231         break;
232       dir = dire + 1;
233       while (*dir == '/')
234         dir++;
235     }
236   return parent;
237 }
238
239 const char *
240 repodata_dir2str(Repodata *data, Id did, const char *suf)
241 {
242   Pool *pool = data->repo->pool;
243   int l = 0;
244   Id parent, comp;
245   const char *comps;
246   char *p;
247
248   if (!did)
249     return suf ? suf : "";
250   parent = did;
251   while (parent)
252     {
253       comp = dirpool_compid(&data->dirpool, parent);
254       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
255       l += strlen(comps);
256       parent = dirpool_parent(&data->dirpool, parent);
257       if (parent)
258         l++;
259     }
260   if (suf)
261     l += strlen(suf) + 1;
262   p = pool_alloctmpspace(pool, l + 1) + l;
263   *p = 0;
264   if (suf)
265     {
266       p -= strlen(suf);
267       strcpy(p, suf);
268       *--p = '/';
269     }
270   parent = did;
271   while (parent)
272     {
273       comp = dirpool_compid(&data->dirpool, parent);
274       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
275       l = strlen(comps);
276       p -= l;
277       strncpy(p, comps, l);
278       parent = dirpool_parent(&data->dirpool, parent);
279       if (parent)
280         *--p = '/';
281     }
282   return p;
283 }
284
285
286 /***************************************************************
287  * data management
288  */
289
290 static inline unsigned char *
291 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
292 {
293   Id *keyp = data->schemadata + data->schemata[schema];
294   for (; *keyp; keyp++)
295     dp = data_skip_key(data, dp, data->keys + *keyp);
296   return dp;
297 }
298
299 unsigned char *
300 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
301 {
302   int nentries, schema;
303   switch(key->type)
304     {
305     case REPOKEY_TYPE_FIXARRAY:
306       dp = data_read_id(dp, &nentries);
307       if (!nentries)
308         return dp;
309       dp = data_read_id(dp, &schema);
310       while (nentries--)
311         dp = data_skip_schema(data, dp, schema);
312       return dp;
313     case REPOKEY_TYPE_FLEXARRAY:
314       dp = data_read_id(dp, &nentries);
315       while (nentries--)
316         {
317           dp = data_read_id(dp, &schema);
318           dp = data_skip_schema(data, dp, schema);
319         }
320       return dp;
321     default:
322       if (key->storage == KEY_STORAGE_INCORE)
323         dp = data_skip(dp, key->type);
324       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
325         {
326           dp = data_skip(dp, REPOKEY_TYPE_ID);
327           dp = data_skip(dp, REPOKEY_TYPE_ID);
328         }
329       return dp;
330     }
331 }
332
333 static unsigned char *
334 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
335 {
336   Id k;
337
338   if (!keyid)
339     return 0;
340   while ((k = *keyp++) != 0)
341     {
342       if (k == keyid)
343         return dp;
344       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
345         {
346           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
347           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
348           continue;
349         }
350       if (data->keys[k].storage != KEY_STORAGE_INCORE)
351         continue;
352       dp = data_skip_key(data, dp, data->keys + k);
353     }
354   return 0;
355 }
356
357 static unsigned char *
358 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
359 {
360   unsigned char *dp;
361   if (!len)
362     return 0;
363   if (off >= data->lastverticaloffset)
364     {
365       off -= data->lastverticaloffset;
366       if (off + len > data->vincorelen)
367         return 0;
368       return data->vincore + off;
369     }
370   if (off + len > key->size)
371     return 0;
372   /* we now have the offset, go into vertical */
373   off += data->verticaloffset[key - data->keys];
374   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
375   dp = repopagestore_load_page_range(&data->store, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
376   if (dp)
377     dp += off % BLOB_PAGESIZE;
378   return dp;
379 }
380
381 static inline unsigned char *
382 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
383 {
384   unsigned char *dp = *dpp;
385
386   if (!dp)
387     return 0;
388   if (key->storage == KEY_STORAGE_INCORE)
389     {
390       /* hmm, this is a bit expensive */
391       *dpp = data_skip_key(data, dp, key);
392       return dp;
393     }
394   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
395     {
396       Id off, len;
397       dp = data_read_id(dp, &off);
398       dp = data_read_id(dp, &len);
399       *dpp = dp;
400       return get_vertical_data(data, key, off, len);
401     }
402   return 0;
403 }
404
405 static int
406 load_repodata(Repodata *data)
407 {
408   if (data->loadcallback)
409     {
410       data->loadcallback(data);
411       if (data->state == REPODATA_AVAILABLE)
412         return 1;
413     }
414   data->state = REPODATA_ERROR;
415   return 0;
416 }
417
418 static inline int
419 maybe_load_repodata(Repodata *data, Id keyname)
420 {
421   if (keyname && !repodata_precheck_keyname(data, keyname))
422     return 0;   /* do not bother... */
423   switch(data->state)
424     {
425     case REPODATA_STUB:
426       if (keyname)
427         {
428           int i;
429           for (i = 0; i < data->nkeys; i++)
430             if (keyname == data->keys[i].name)
431               break;
432           if (i == data->nkeys)
433             return 0;
434         }
435       return load_repodata(data);
436     case REPODATA_ERROR:
437       return 0;
438     case REPODATA_AVAILABLE:
439       return 1;
440     default:
441       data->state = REPODATA_ERROR;
442       return 0;
443     }
444 }
445
446 static inline unsigned char*
447 entry2data(Repodata *data, Id entry, Id *schemap)
448 {
449   unsigned char *dp = data->incoredata;
450   if (!dp)
451     return 0;
452   if (entry == SOLVID_META)     /* META */
453     dp += 1;
454   else if (entry == SOLVID_POS) /* META */
455     {
456       Pool *pool = data->repo->pool;
457       if (data->repo != pool->pos.repo)
458         return 0;
459       if (data != data->repo->repodata + pool->pos.repodataid)
460         return 0;
461       *schemap = pool->pos.schema;
462       return data->incoredata + pool->pos.dp;
463     }
464   else
465     {
466       if (entry < data->start || entry >= data->end)
467         return 0;
468       dp += data->incoreoffset[entry - data->start];
469     }
470   return data_read_id(dp, schemap);
471 }
472
473 /************************************************************************
474  * data lookup
475  */
476
477 static inline Id
478 find_schema_key(Repodata *data, Id schema, Id keyname)
479 {
480   Id *keyp;
481   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
482     if (data->keys[*keyp].name == keyname)
483       return *keyp;
484   return 0;
485 }
486
487 static inline unsigned char *
488 find_key_data(Repodata *data, Id entry, Id keyname, Repokey **keyp)
489 {
490   unsigned char *dp, *ddp;
491   Id keyid, schema;
492   Repokey *key;
493
494   if (!maybe_load_repodata(data, keyname))
495     return 0;
496   dp = entry2data(data, entry, &schema);
497   if (!dp)
498     return 0;
499   keyid = find_schema_key(data, schema, keyname);
500   if (!keyid)
501     return 0;
502   key = data->keys + keyid;
503   *keyp = key;
504   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
505     return dp;
506   dp = forward_to_key(data, keyid, data->schemadata + data->schemata[schema], dp);
507   if (!dp)
508     return 0;
509   ddp = get_data(data, key, &dp);
510   return ddp;
511 }
512
513
514 Id
515 repodata_lookup_id(Repodata *data, Id entry, Id keyname)
516 {
517   unsigned char *dp;
518   Repokey *key;
519   Id id;
520
521   dp = find_key_data(data, entry, keyname, &key);
522   if (!dp)
523     return 0;
524   if (key->type == REPOKEY_TYPE_CONSTANTID)
525     return key->size;
526   if (key->type != REPOKEY_TYPE_ID)
527     return 0;
528   dp = data_read_id(dp, &id);
529   return id;
530 }
531
532 const char *
533 repodata_lookup_str(Repodata *data, Id entry, Id keyname)
534 {
535   unsigned char *dp;
536   Repokey *key;
537   Id id;
538
539   dp = find_key_data(data, entry, keyname, &key);
540   if (!dp)
541     return 0;
542   if (key->type == REPOKEY_TYPE_STR)
543     return (const char *)dp;
544   if (key->type == REPOKEY_TYPE_CONSTANTID)
545     return id2str(data->repo->pool, key->size);
546   if (key->type == REPOKEY_TYPE_ID)
547     dp = data_read_id(dp, &id);
548   else
549     return 0;
550   if (data->localpool)
551     return data->spool.stringspace + data->spool.strings[id];
552   return id2str(data->repo->pool, id);
553 }
554
555 int
556 repodata_lookup_num(Repodata *data, Id entry, Id keyname, unsigned int *value)
557 {
558   unsigned char *dp;
559   Repokey *key;
560   KeyValue kv;
561
562   *value = 0;
563   dp = find_key_data(data, entry, keyname, &key);
564   if (!dp)
565     return 0;
566   if (key->type == REPOKEY_TYPE_NUM
567       || key->type == REPOKEY_TYPE_U32
568       || key->type == REPOKEY_TYPE_CONSTANT)
569     {
570       dp = data_fetch(dp, &kv, key);
571       *value = kv.num;
572       return 1;
573     }
574   return 0;
575 }
576
577 int
578 repodata_lookup_void(Repodata *data, Id entry, Id keyname)
579 {
580   Id schema;
581   Id *keyp;
582   unsigned char *dp;
583
584   if (!maybe_load_repodata(data, keyname))
585     return 0;
586   dp = entry2data(data, entry, &schema);
587   if (!dp)
588     return 0;
589   /* can't use find_schema_key as we need to test the type */
590   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
591     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
592       return 1;
593   return 0;
594 }
595
596 const unsigned char *
597 repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyname, Id *typep)
598 {
599   unsigned char *dp;
600   Repokey *key;
601
602   dp = find_key_data(data, entry, keyname, &key);
603   if (!dp)
604     return 0;
605   *typep = key->type;
606   return dp;
607 }
608
609
610 /************************************************************************
611  * data search
612  */
613
614 struct subschema_data {
615   Solvable *s;
616   void *cbdata;
617   KeyValue *parent;
618 };
619
620 /* search in a specific entry */
621 void
622 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
623 {
624   Id schema;
625   Repokey *key;
626   Id k, keyid, *kp, *keyp;
627   unsigned char *dp, *ddp;
628   int onekey = 0;
629   int stop;
630   KeyValue kv;
631   Solvable *s;
632
633   if (!maybe_load_repodata(data, keyname))
634     return;
635   if (entry == SOLVID_SUBSCHEMA)
636     {
637       struct subschema_data *subd = cbdata;
638       cbdata = subd->cbdata;
639       s = subd->s;
640       schema = subd->parent->id;
641       dp = (unsigned char *)subd->parent->str;
642       kv.parent = subd->parent;
643     }
644   else
645     {
646       schema = 0;
647       dp = entry2data(data, entry, &schema);
648       if (!dp)
649         return;
650       s = data->repo->pool->solvables + entry;
651       kv.parent = 0;
652     }
653   keyp = data->schemadata + data->schemata[schema];
654   if (keyname)
655     {
656       /* search for a specific key */
657       for (kp = keyp; (k = *kp++) != 0; )
658         if (data->keys[k].name == keyname)
659           break;
660       if (k == 0)
661         return;
662       dp = forward_to_key(data, k, data->schemadata + data->schemata[schema], dp);
663       if (!dp)
664         return;
665       keyp = kp - 1;
666       onekey = 1;
667     }
668   while ((keyid = *keyp++) != 0)
669     {
670       stop = 0;
671       key = data->keys + keyid;
672       ddp = get_data(data, key, &dp);
673
674       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
675         {
676           struct subschema_data subd;
677           int nentries;
678           Id schema = 0;
679
680           subd.cbdata = cbdata;
681           subd.s = s;
682           subd.parent = &kv;
683           ddp = data_read_id(ddp, &nentries);
684           kv.num = nentries;
685           kv.entry = 0;
686           kv.eof = 0;
687           while (ddp && nentries > 0)
688             {
689               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
690                 ddp = data_read_id(ddp, &schema);
691               kv.id = schema;
692               kv.str = (char *)ddp;
693               stop = callback(cbdata, s, data, key, &kv);
694               if (stop > SEARCH_NEXT_KEY)
695                 return;
696               if (stop)
697                 break;
698               if (!keyname)
699                 repodata_search(data, SOLVID_SUBSCHEMA, 0, callback, &subd);
700               ddp = data_skip_schema(data, ddp, schema);
701               nentries--;
702               kv.entry++;
703             }
704           if (!nentries)
705             {
706               /* sentinel */
707               kv.eof = 1;
708               kv.str = (char *)ddp;
709               stop = callback(cbdata, s, data, key, &kv);
710               if (stop > SEARCH_NEXT_KEY)
711                 return;
712             }
713           if (onekey)
714             return;
715           continue;
716         }
717       kv.entry = 0;
718       do
719         {
720           ddp = data_fetch(ddp, &kv, key);
721           if (!ddp)
722             break;
723           stop = callback(cbdata, s, data, key, &kv);
724           kv.entry++;
725         }
726       while (!kv.eof && !stop);
727       if (onekey || stop > SEARCH_NEXT_KEY)
728         return;
729     }
730 }
731
732 void
733 repodata_setpos_kv(Repodata *data, KeyValue *kv)
734 {
735   Pool *pool = data->repo->pool;
736   if (!kv)
737     {
738       pool->pos.repo = 0;
739       pool->pos.repodataid = 0;
740       pool->pos.dp = 0;
741       pool->pos.schema = 0;
742     }
743   else
744     {
745       pool->pos.repo = 0;
746       pool->pos.repodataid = data - data->repo->repodata;
747       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
748       pool->pos.schema = kv->id;
749     }
750 }
751
752 /************************************************************************/
753
754 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
755   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
756   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
757   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
758   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
759   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
760   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
761   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
762   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
763   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
764   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
765   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
766   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
767   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
768 };
769
770 #if 1
771 static inline Id *
772 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
773 {
774   kv->id = keyname;
775   switch (keyname)
776     {
777     case SOLVABLE_NAME:
778       kv->eof = 1;
779       return &s->name;
780     case SOLVABLE_ARCH:
781       kv->eof = 1;
782       return &s->arch;
783     case SOLVABLE_EVR:
784       kv->eof = 1;
785       return &s->evr;
786     case SOLVABLE_VENDOR:
787       kv->eof = 1;
788       return &s->vendor;
789     case SOLVABLE_PROVIDES:
790       kv->eof = 0;
791       return s->provides ? s->repo->idarraydata + s->provides : 0;
792     case SOLVABLE_OBSOLETES:
793       kv->eof = 0;
794       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
795     case SOLVABLE_CONFLICTS:
796       kv->eof = 0;
797       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
798     case SOLVABLE_REQUIRES:
799       kv->eof = 0;
800       return s->requires ? s->repo->idarraydata + s->requires : 0;
801     case SOLVABLE_RECOMMENDS:
802       kv->eof = 0;
803       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
804     case SOLVABLE_SUPPLEMENTS:
805       kv->eof = 0;
806       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
807     case SOLVABLE_SUGGESTS:
808       kv->eof = 0;
809       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
810     case SOLVABLE_ENHANCES:
811       kv->eof = 0;
812       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
813     case RPM_RPMDBID:
814       kv->eof = 1;
815       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
816     default:
817       return 0;
818     }
819 }
820
821 void
822 datamatcher_init(Datamatcher *ma, Pool *pool, const char *match, int flags)
823 {
824   ma->pool = pool;
825   ma->match = (void *)match;
826   ma->flags = flags;
827   ma->error = 0;
828   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
829     {
830       ma->match = sat_calloc(1, sizeof(regex_t));
831       ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
832       if (ma->error)
833         {
834           sat_free(ma->match);
835           ma->match = (void *)match;
836           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
837         }
838     }
839 }
840
841 void
842 datamatcher_free(Datamatcher *ma)
843 {
844   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
845     {
846       regfree(ma->match);
847       ma->match = sat_free(ma->match);
848     }
849 }
850
851 int
852 datamatcher_match(Datamatcher *ma, Repodata *data, Repokey *key, KeyValue *kv)
853 {
854   switch (key->type)
855     {
856     case REPOKEY_TYPE_ID:
857     case REPOKEY_TYPE_IDARRAY:
858       if (data && data->localpool)
859         kv->str = stringpool_id2str(&data->spool, kv->id);
860       else
861         kv->str = id2str(ma->pool, kv->id);
862       break;
863     case REPOKEY_TYPE_STR:
864       break;
865     case REPOKEY_TYPE_DIRSTRARRAY:
866       if (!(ma->flags & SEARCH_FILES))
867         return 0;
868       /* Put the full filename into kv->str.  */
869       kv->str = repodata_dir2str(data, kv->id, kv->str);
870       /* And to compensate for that put the "empty" directory into
871          kv->id, so that later calls to repodata_dir2str on this data
872          come up with the same filename again.  */
873       kv->id = 0;
874       break;
875     default:
876       return 0;
877     }
878   /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
879      for the others we can't know if a colon separates a kind or not.  */
880   if ((ma->flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
881     {
882       const char *s = strchr(kv->str, ':');
883       if (s)
884         kv->str = s + 1;
885     }
886   switch ((ma->flags & SEARCH_STRINGMASK))
887     {
888     case SEARCH_SUBSTRING:
889       if (ma->flags & SEARCH_NOCASE)
890         {
891           if (!strcasestr(kv->str, (const char *)ma->match))
892             return 0;
893         }
894       else
895         {
896           if (!strstr(kv->str, (const char *)ma->match))
897             return 0;
898         }
899       break;
900     case SEARCH_STRING:
901       if (ma->flags & SEARCH_NOCASE)
902         {
903           if (strcasecmp((const char *)ma->match, kv->str))
904             return 0;
905         }
906       else
907         {
908           if (strcmp((const char *)ma->match, kv->str))
909             return 0;
910         }
911       break;
912     case SEARCH_GLOB:
913       if (fnmatch((const char *)ma->match, kv->str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
914         return 0;
915       break;
916     case SEARCH_REGEX:
917       if (regexec((const regex_t *)ma->match, kv->str, 0, NULL, 0))
918         return 0;
919       break;
920     default:
921       return 0;
922     }
923   return 1;
924 }
925
926 enum {
927   di_bye,
928
929   di_nextattr,
930   di_nextkey,
931   di_nextrepodata,
932   di_nextsolvable,
933   di_nextrepo,
934
935   di_enterrepo,
936   di_entersolvable,
937   di_enterrepodata,
938   di_enterkey,
939
940   di_nextarrayelement,
941   di_entersub,
942   di_leavesub,
943
944   di_nextsolvableattr,
945   di_nextsolvablekey,
946   di_entersolvablekey
947 };
948
949 void
950 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname, const char *match, int flags)
951 {
952   memset(di, 0, sizeof(*di));
953   di->repo = repo;
954   di->keyname = keyname;
955   di->solvid = p;
956   di->pool = repo->pool;
957   if (p)
958     flags |= SEARCH_THISSOLVID;
959   di->flags = flags;
960   if (repo)
961     di->repoid = -1;
962   if (match)
963     datamatcher_init(&di->matcher, di->pool, match, flags);
964   if (p == SOLVID_POS)
965     {
966       di->repo = di->pool->pos.repo;
967       if (!di->repo)
968         {
969           di->state = di_bye;
970           return;
971         }
972       di->data = di->repo->repodata + di->pool->pos.repodataid;
973       di->repoid = -1;
974       di->repodataid = -1;
975     }
976   di->state = di_enterrepo;
977 }
978
979 void
980 dataiterator_free(Dataiterator *di)
981 {
982   if (di->matcher.match)
983     datamatcher_free(&di->matcher);
984 }
985
986 int
987 dataiterator_step(Dataiterator *di)
988 {
989   Id schema;
990
991   for (;;)
992     {
993       switch (di->state)
994         {
995         case di_nextattr: di_nextattr:
996           di->kv.entry++;
997           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
998           if (di->kv.eof)
999             di->state = di_nextkey;
1000           else
1001             di->state = di_nextattr;
1002           break;
1003
1004         case di_nextkey: di_nextkey:
1005           if (!di->keyname)
1006             {
1007               if (*++di->keyp)
1008                 goto di_enterkey;
1009             }
1010           else if ((di->flags & SEARCH_SUB) != 0)
1011             {
1012               Id *keyp = di->keyp;
1013               for (keyp++; *keyp; keyp++)
1014                 if (di->data->keys[*keyp].name == di->keyname ||
1015                     di->data->keys[*keyp].type == REPOKEY_TYPE_FIXARRAY ||
1016                     di->data->keys[*keyp].type == REPOKEY_TYPE_FLEXARRAY)
1017                   break;
1018               if (*keyp && (di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp)) != 0)
1019                 {
1020                   di->keyp = keyp;
1021                   goto di_enterkey;
1022                 }
1023             }
1024
1025           if (di->kv.parent)
1026             goto di_leavesub;
1027           /* FALLTHROUGH */
1028
1029         case di_nextrepodata: di_nextrepodata:
1030           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1031               goto di_enterrepodata;
1032           /* FALLTHROUGH */
1033
1034         case di_nextsolvable:
1035           if (!(di->flags & SEARCH_THISSOLVID))
1036             {
1037               if (di->solvid < 0)
1038                 di->solvid = di->repo->start;
1039               else
1040                 di->solvid++;
1041               for (; di->solvid < di->repo->end; di->solvid++)
1042                 {
1043                   if (di->pool->solvables[di->solvid].repo == di->repo)
1044                     goto di_entersolvable;
1045                 }
1046             }
1047           /* FALLTHROUGH */
1048
1049         case di_nextrepo:
1050           if (di->repoid >= 0)
1051             {
1052               di->repoid++;
1053               if (di->repoid < di->pool->nrepos)
1054                 {
1055                   di->repo = di->pool->repos[di->repoid];
1056                   goto di_enterrepo;
1057                 }
1058             }
1059
1060         /* FALLTHROUGH */
1061         case di_bye:
1062           di->state = di_bye;
1063           return 0;
1064
1065         case di_enterrepo: di_enterrepo:
1066           if (!(di->flags & SEARCH_THISSOLVID))
1067             di->solvid = di->repo->start;
1068           /* FALLTHROUGH */
1069
1070         case di_entersolvable: di_entersolvable:
1071           if (di->repodataid >= 0)
1072             {
1073               di->repodataid = 0;
1074               if (di->solvid > 0 && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
1075                 {
1076                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1077                   di->data = 0;
1078                   goto di_entersolvablekey;
1079                 }
1080             }
1081
1082         case di_enterrepodata: di_enterrepodata:
1083           if (di->repodataid >= 0)
1084             di->data = di->repo->repodata + di->repodataid;
1085           if (!maybe_load_repodata(di->data, di->keyname))
1086             goto di_nextrepodata;
1087           di->dp = entry2data(di->data, di->solvid, &schema);
1088           if (!di->dp)
1089             goto di_nextrepodata;
1090           di->keyp = di->data->schemadata + di->data->schemata[schema];
1091           if (di->keyname)
1092             {
1093               Id *keyp;
1094               if ((di->flags & SEARCH_SUB) != 0)
1095                 {
1096                   di->keyp--;
1097                   goto di_nextkey;
1098                 }
1099               for (keyp = di->keyp; *keyp; keyp++)
1100                 if (di->data->keys[*keyp].name == di->keyname)
1101                   break;
1102               if (!*keyp)
1103                 goto di_nextrepodata;
1104               di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1105               di->keyp = keyp;
1106               if (!di->dp)
1107                 goto di_nextrepodata;
1108             }
1109
1110         case di_enterkey: di_enterkey:
1111           di->kv.entry = -1;
1112           di->key = di->data->keys + *di->keyp;
1113           di->ddp = get_data(di->data, di->key, &di->dp);
1114           if (!di->ddp)
1115             goto di_nextkey;
1116           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1117             {
1118               di->ddp = data_read_id(di->ddp, &di->kv.num);
1119               di->kv.entry = -1;
1120               di->kv.eof = 0;
1121               goto di_nextarrayelement;
1122             }
1123           goto di_nextattr;
1124
1125         case di_nextarrayelement: di_nextarrayelement:
1126           di->kv.entry++;
1127           if (di->kv.entry)
1128             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1129           if (di->kv.entry == di->kv.num)
1130             {
1131               if (di->keyname && di->key->name != di->keyname)
1132                 goto di_nextkey;
1133               di->kv.str = (char *)di->ddp;
1134               di->kv.eof = 1;
1135               di->state = di_nextkey;
1136               break;
1137             }
1138           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1139             di->ddp = data_read_id(di->ddp, &di->kv.id);
1140           di->kv.str = (char *)di->ddp;
1141           if (di->keyname && di->key->name != di->keyname)
1142             goto di_entersub;
1143           if ((di->flags & SEARCH_SUB) != 0)
1144             di->state = di_entersub;
1145           else
1146             di->state = di_nextarrayelement;
1147           break;
1148
1149         case di_entersub: di_entersub:
1150           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1151             goto di_nextarrayelement;   /* sorry, full */
1152           di->parents[di->nparents].kv = di->kv;
1153           di->parents[di->nparents].dp = di->dp;
1154           di->parents[di->nparents].keyp = di->keyp;
1155           di->dp = (unsigned char *)di->kv.str;
1156           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1157           memset(&di->kv, 0, sizeof(di->kv));
1158           di->kv.parent = &di->parents[di->nparents].kv;
1159           di->nparents++;
1160           di->keyp--;
1161           goto di_nextkey;
1162
1163         case di_leavesub: di_leavesub:
1164           di->nparents--;
1165           di->dp = di->parents[di->nparents].dp;
1166           di->kv = di->parents[di->nparents].kv;
1167           di->keyp = di->parents[di->nparents].keyp;
1168           di->key = di->data->keys + *di->keyp;
1169           di->ddp = (unsigned char *)di->kv.str;
1170           goto di_nextarrayelement;
1171
1172         /* special solvable attr handling follows */
1173
1174         case di_nextsolvableattr:
1175           di->kv.id = *di->idp++;
1176           di->kv.entry++;
1177           if (!*di->idp)
1178             {
1179               di->kv.eof = 1;
1180               di->state = di_nextsolvablekey;
1181             }
1182           break;
1183
1184         case di_nextsolvablekey: di_nextsolvablekey:
1185           if (di->keyname || di->key->name == RPM_RPMDBID)
1186             goto di_enterrepodata;
1187           di->key++;
1188           /* FALLTHROUGH */
1189
1190         case di_entersolvablekey: di_entersolvablekey:
1191           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1192           if (!di->idp || !di->idp[0])
1193             goto di_nextsolvablekey;
1194           di->kv.id = di->idp[0];
1195           di->kv.num = di->idp[0];
1196           if (!di->kv.eof && !di->idp[1])
1197             di->kv.eof = 1;
1198           di->kv.entry = 0;
1199           if (di->kv.eof)
1200             di->state = di_nextsolvablekey;
1201           else
1202             di->state = di_nextsolvableattr;
1203           break;
1204         }
1205
1206       if (di->matcher.match)
1207         if (!datamatcher_match(&di->matcher, di->data, di->key, &di->kv))
1208           continue;
1209       /* found something! */
1210       return 1;
1211     }
1212 }
1213
1214 void
1215 dataiterator_setpos(Dataiterator *di)
1216 {
1217   if (di->kv.eof)
1218     {
1219       memset(&di->pool->pos, 0, sizeof(di->pool->pos));
1220       return;
1221     }
1222   di->pool->pos.repo = di->repo;
1223   di->pool->pos.repodataid = di->data - di->repo->repodata;
1224   di->pool->pos.schema = di->kv.id;
1225   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1226 }
1227
1228 void
1229 dataiterator_setpos_parent(Dataiterator *di)
1230 {
1231   if (!di->kv.parent)
1232     {
1233       memset(&di->pool->pos, 0, sizeof(di->pool->pos));
1234       return;
1235     }
1236   di->pool->pos.repo = di->repo;
1237   di->pool->pos.repodataid = di->data - di->repo->repodata;
1238   di->pool->pos.schema = di->kv.parent->id;
1239   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1240 }
1241
1242 void
1243 dataiterator_skip_attribute(Dataiterator *di)
1244 {
1245   if (di->state == di_nextsolvableattr)
1246     di->state = di_nextsolvablekey;
1247   else
1248     di->state = di_nextkey;
1249 }
1250
1251 void
1252 dataiterator_skip_solvable(Dataiterator *di)
1253 {
1254   di->state = di_nextsolvable;
1255 }
1256
1257 void
1258 dataiterator_skip_repo(Dataiterator *di)
1259 {
1260   di->state = di_nextrepo;
1261 }
1262
1263 void
1264 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1265 {
1266   di->repo = s->repo;
1267   di->repoid = -1;
1268   di->solvid = s - di->pool->solvables;
1269   di->state = di_entersolvable;
1270 }
1271
1272 void
1273 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1274 {
1275   di->repo = repo;
1276   di->repoid = -1;
1277   di->state = di_enterrepo;
1278 }
1279
1280 int
1281 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1282 {
1283   Datamatcher matcher = di->matcher;
1284   matcher.flags = flags;
1285   matcher.match = (void *)vmatch;
1286   matcher.pool = di->pool;
1287   return datamatcher_match(&matcher, di->data, di->key, &di->kv);
1288 }
1289
1290 #else
1291
1292 /************************************************************************
1293  * data search iterator
1294  */
1295
1296 static void
1297 dataiterator_newdata(Dataiterator *di)
1298 {
1299   Id keyname = di->keyname;
1300   Repodata *data = di->data;
1301   di->nextkeydp = 0;
1302
1303   if (data->state == REPODATA_STUB)
1304     {
1305       if (keyname)
1306         {
1307           int j;
1308           for (j = 1; j < data->nkeys; j++)
1309             if (keyname == data->keys[j].name)
1310               break;
1311           if (j == data->nkeys)
1312             return;
1313         }
1314       /* load it */
1315       if (data->loadcallback)
1316         data->loadcallback(data);
1317       else
1318         data->state = REPODATA_ERROR;
1319     }
1320   if (data->state == REPODATA_ERROR)
1321     return;
1322
1323   Id schema;
1324   unsigned char *dp = data->incoredata;
1325   if (!dp)
1326     return;
1327   if (di->solvid >= 0)
1328     dp += data->incoreoffset[di->solvid - data->start];
1329   dp = data_read_id(dp, &schema);
1330   Id *keyp = data->schemadata + data->schemata[schema];
1331   if (keyname)
1332     {
1333       Id k, *kp;
1334       /* search in a specific key */
1335       for (kp = keyp; (k = *kp++) != 0; )
1336         if (data->keys[k].name == keyname)
1337           break;
1338       if (k == 0)
1339         return;
1340       dp = forward_to_key(data, k, keyp, dp);
1341       if (!dp)
1342         return;
1343       keyp = kp - 1;
1344     }
1345   Id keyid = *keyp++;
1346   if (!keyid)
1347     return;
1348
1349   di->data = data;
1350   di->key = di->data->keys + keyid;
1351   di->keyp = keyp;
1352   di->dp = 0;
1353
1354   di->nextkeydp = dp;
1355   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1356   di->kv.eof = 0;
1357 }
1358
1359 void
1360 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
1361                   const char *match, int flags)
1362 {
1363   di->flags = flags;
1364   if (p > 0)
1365     {
1366       di->solvid = p;
1367       di->flags |= __SEARCH_ONESOLVABLE;
1368       di->data = repo->repodata - 1;
1369       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
1370         di->state = 0;
1371       else
1372         di->state = 1;
1373     }
1374   else
1375     {
1376       di->solvid = repo->start - 1;
1377       if (di->solvid < 0)
1378         {
1379           fprintf(stderr, "A repo contains the NULL solvable!\n");
1380           exit(1);
1381         }
1382       di->data = repo->repodata + repo->nrepodata - 1;
1383       di->state = 0;
1384     }
1385
1386   di->match = match;
1387   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1388     {
1389       if (di->match)
1390         {
1391           /* We feed multiple lines eventually (e.g. authors or descriptions),
1392              so set REG_NEWLINE. */
1393           di->regex_err =
1394             regcomp(&di->regex, di->match,
1395               REG_EXTENDED | REG_NOSUB | REG_NEWLINE
1396               | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1397 #if 0
1398           if (di->regex_err != 0)
1399             {
1400               fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
1401               fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
1402               exit(1);
1403             }
1404 #else
1405         }
1406       else
1407         {
1408           di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
1409           di->regex_err = 0;
1410 #endif
1411         }
1412     }
1413
1414   di->keyname = keyname;
1415   static Id zeroid = 0;
1416   di->keyp = &zeroid;
1417   di->kv.eof = 1;
1418   di->repo = repo;
1419   di->idp = 0;
1420   di->subkeyp = 0;
1421 }
1422
1423 /* FIXME factor and merge with repo_matchvalue */
1424 static int
1425 dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
1426 {
1427   KeyValue *kv = &di->kv;
1428   const char *match = vmatch;
1429   if ((flags & SEARCH_STRINGMASK) != 0)
1430     {
1431       switch (di->key->type)
1432         {
1433         case REPOKEY_TYPE_ID:
1434         case REPOKEY_TYPE_IDARRAY:
1435           if (di->data && di->data->localpool)
1436             kv->str = stringpool_id2str(&di->data->spool, kv->id);
1437           else
1438             kv->str = id2str(di->repo->pool, kv->id);
1439           break;
1440         case REPOKEY_TYPE_STR:
1441           break;
1442         case REPOKEY_TYPE_DIRSTRARRAY:
1443           if (!(flags & SEARCH_FILES))
1444             return 0;
1445           /* Put the full filename into kv->str.  */
1446           kv->str = repodata_dir2str(di->data, kv->id, kv->str);
1447           /* And to compensate for that put the "empty" directory into
1448              kv->id, so that later calls to repodata_dir2str on this data
1449              come up with the same filename again.  */
1450           kv->id = 0;
1451           break;
1452         default:
1453           return 0;
1454         }
1455       /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
1456          for the others we can't know if a colon separates a kind or not.  */
1457       if ((flags & SEARCH_SKIP_KIND)
1458           && di->key->storage == KEY_STORAGE_SOLVABLE)
1459         {
1460           const char *s = strchr(kv->str, ':');
1461           if (s)
1462             kv->str = s + 1;
1463         }
1464       switch ((flags & SEARCH_STRINGMASK))
1465         {
1466           case SEARCH_SUBSTRING:
1467             if (flags & SEARCH_NOCASE)
1468               {
1469                 if (!strcasestr(kv->str, match))
1470                   return 0;
1471               }
1472             else
1473               {
1474                 if (!strstr(kv->str, match))
1475                   return 0;
1476               }
1477             break;
1478           case SEARCH_STRING:
1479             if (flags & SEARCH_NOCASE)
1480               {
1481                 if (strcasecmp(match, kv->str))
1482                   return 0;
1483               }
1484             else
1485               {
1486                 if (strcmp(match, kv->str))
1487                   return 0;
1488               }
1489             break;
1490           case SEARCH_GLOB:
1491             if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1492               return 0;
1493             break;
1494           case SEARCH_REGEX:
1495             if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
1496               return 0;
1497             break;
1498           default:
1499             return 0;
1500         }
1501     }
1502   return 1;
1503 }
1504
1505 static int
1506 dataiterator_match_int(Dataiterator *di)
1507 {
1508   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1509     return dataiterator_match_int_real(di, di->flags, &di->regex);
1510   else
1511     return dataiterator_match_int_real(di, di->flags, di->match);
1512 }
1513
1514 int
1515 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1516 {
1517   return dataiterator_match_int_real(di, flags, vmatch);
1518 }
1519
1520 int
1521 dataiterator_step(Dataiterator *di)
1522 {
1523 restart:
1524   while (1)
1525     {
1526       if (di->state)
1527         {
1528           /* we're stepping through solvable data, 1 -> SOLVABLE_NAME... */
1529           if (di->idp)
1530             {
1531               /* we're stepping through an id array */
1532               Id *idp = di->idp;
1533               if (*idp)
1534                 {
1535                   di->kv.id = *idp;
1536                   di->idp++;
1537                   di->kv.eof = idp[1] ? 0 : 1;
1538                   goto weg2;
1539                 }
1540               else
1541                 di->idp = 0;
1542             }
1543           Solvable *s = di->repo->pool->solvables + di->solvid;
1544           int state = di->state;
1545           di->key = solvablekeys + state - 1;
1546           if (di->keyname)
1547             di->state = RPM_RPMDBID;
1548           else
1549             di->state++;
1550           if (state == 1)
1551             {
1552               di->data = 0;
1553               if (di->keyname)
1554                 state = di->keyname - 1;
1555             }
1556           switch (state + 1)
1557             {
1558               case SOLVABLE_NAME:
1559                 if (!s->name)
1560                   continue;
1561                 di->kv.id = s->name;
1562                 di->kv.eof = 1;
1563                 break;
1564               case SOLVABLE_ARCH:
1565                 if (!s->arch)
1566                   continue;
1567                 di->kv.id = s->arch;
1568                 di->kv.eof = 1;
1569                 break;
1570               case SOLVABLE_EVR:
1571                 if (!s->evr)
1572                   continue;
1573                 di->kv.id = s->evr;
1574                 di->kv.eof = 1;
1575                 break;
1576               case SOLVABLE_VENDOR:
1577                 if (!s->vendor)
1578                   continue;
1579                 di->kv.id = s->vendor;
1580                 di->kv.eof = 1;
1581                 break;
1582               case SOLVABLE_PROVIDES:
1583                 di->idp = s->provides
1584                     ? di->repo->idarraydata + s->provides : 0;
1585                 continue;
1586               case SOLVABLE_OBSOLETES:
1587                 di->idp = s->obsoletes
1588                     ? di->repo->idarraydata + s->obsoletes : 0;
1589                 continue;
1590               case SOLVABLE_CONFLICTS:
1591                 di->idp = s->conflicts
1592                     ? di->repo->idarraydata + s->conflicts : 0;
1593                 continue;
1594               case SOLVABLE_REQUIRES:
1595                 di->idp = s->requires
1596                     ? di->repo->idarraydata + s->requires : 0;
1597                 continue;
1598               case SOLVABLE_RECOMMENDS:
1599                 di->idp = s->recommends
1600                     ? di->repo->idarraydata + s->recommends : 0;
1601                 continue;
1602               case SOLVABLE_SUPPLEMENTS:
1603                 di->idp = s->supplements
1604                     ? di->repo->idarraydata + s->supplements : 0;
1605                 continue;
1606               case SOLVABLE_SUGGESTS:
1607                 di->idp = s->suggests
1608                     ? di->repo->idarraydata + s->suggests : 0;
1609                 continue;
1610               case SOLVABLE_ENHANCES:
1611                 di->idp = s->enhances
1612                     ? di->repo->idarraydata + s->enhances : 0;
1613                 continue;
1614               case RPM_RPMDBID:
1615                 if (!di->repo->rpmdbid)
1616                   continue;
1617                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
1618                 di->kv.eof = 1;
1619                 break;
1620               default:
1621                 di->data = di->repo->repodata - 1;
1622                 di->kv.eof = 1;
1623                 di->state = 0;
1624                 continue;
1625             }
1626         }
1627       else if (di->subkeyp)
1628         {
1629           Id keyid;
1630           if (!di->subnum)
1631             {
1632               /* Send end-of-substruct.  We are here only when we saw a
1633                  _COUNTED key one level up.  Since then we didn't increment
1634                  ->keyp, so it still can be found at keyp[-1].  */
1635               di->kv.eof = 2;
1636               di->key = di->data->keys + di->keyp[-1];
1637               di->subkeyp = 0;
1638             }
1639           else if (!(keyid = *di->subkeyp++))
1640             {
1641               /* Send end-of-element.  See above for keyp[-1].  */
1642               di->kv.eof = 1;
1643               di->key = di->data->keys + di->keyp[-1];
1644               if (di->subschema)
1645                 di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1646               else
1647                 {
1648                   di->dp = data_read_id(di->dp, &di->subschema);
1649                   di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1650                   di->subschema = 0;
1651                 }
1652               di->subnum--;
1653             }
1654           else
1655             {
1656               di->key = di->data->keys + keyid;
1657               di->dp = data_fetch(di->dp, &di->kv, di->key);
1658               if (!di->dp)
1659                 exit(1);
1660             }
1661         }
1662       else
1663         {
1664           if (di->kv.eof)
1665             di->dp = 0;
1666           else
1667             di->dp = data_fetch(di->dp, &di->kv, di->key);
1668
1669           while (!di->dp)
1670             {
1671               Id keyid;
1672               if (di->keyname || !(keyid = *di->keyp++))
1673                 {
1674                   while (1)
1675                     {
1676                       Repo *repo = di->repo;
1677                       Repodata *data = ++di->data;
1678                       if (data >= repo->repodata + repo->nrepodata)
1679                         {
1680                           if (di->flags & __SEARCH_ONESOLVABLE)
1681                             return 0;
1682                           if (di->solvid >= 0)
1683                             {
1684                               while (++di->solvid < repo->end)
1685                                 if (repo->pool->solvables[di->solvid].repo == repo)
1686                                   break;
1687                               if (di->solvid >= repo->end)
1688                                 {
1689                                   if (!(di->flags & SEARCH_EXTRA))
1690                                     goto skiprepo;
1691                                   goto skiprepo;
1692                                 }
1693                             }
1694                           else
1695                             {
1696                                 {
1697 skiprepo:;
1698                                   Pool *pool = di->repo->pool;
1699                                   if (!(di->flags & SEARCH_ALL_REPOS)
1700                                       || di->repo == pool->repos[pool->nrepos - 1])
1701                                     return 0;
1702                                   int i;
1703                                   for (i = 0; i < pool->nrepos; i++)
1704                                     if (di->repo == pool->repos[i])
1705                                       break;
1706                                   di->repo = pool->repos[i + 1];
1707                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
1708                                   continue;
1709                                 }
1710                             }
1711                           di->data = repo->repodata - 1;
1712                           if ((di->flags & SEARCH_NO_STORAGE_SOLVABLE))
1713                             continue;
1714                           static Id zeroid = 0;
1715                           di->keyp = &zeroid;
1716                           di->state = 1;
1717                           goto restart;
1718                         }
1719                       if ((di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
1720                         {
1721                           dataiterator_newdata(di);
1722                           if (di->nextkeydp)
1723                             break;
1724                         }
1725                     }
1726                 }
1727               else
1728                 {
1729                   di->key = di->data->keys + keyid;
1730                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1731                 }
1732               di->dp = data_fetch(di->dp, &di->kv, di->key);
1733             }
1734           if (di->key->type == REPOKEY_TYPE_FIXARRAY)
1735             {
1736               di->subnum = di->kv.num;
1737               di->subschema = di->kv.id;
1738               di->kv.eof = 0;
1739               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1740             }
1741           if (di->key->type == REPOKEY_TYPE_FLEXARRAY)
1742             {
1743               di->subnum = di->kv.num;
1744               di->kv.eof = 0;
1745               di->dp = data_read_id(di->dp, &di->subschema);
1746               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1747               di->subschema = 0;
1748             }
1749         }
1750 weg2:
1751       if (!di->match
1752           || dataiterator_match_int(di))
1753         break;
1754     }
1755   return 1;
1756 }
1757
1758 void
1759 dataiterator_skip_attribute(Dataiterator *di)
1760 {
1761   if (di->state)
1762     di->idp = 0;
1763   /* This will make the next _step call to retrieve the next field.  */
1764   di->kv.eof = 1;
1765 }
1766
1767 void
1768 dataiterator_skip_solvable(Dataiterator *di)
1769 {
1770   /* We're done with this field.  */
1771   di->kv.eof = 1;
1772   /* And with solvable data.  */
1773   di->state = 0;
1774   /* And with all keys for this repodata and thing. */
1775   static Id zeroid = 0;
1776   di->keyp = &zeroid;
1777   /* And with all repodatas for this thing.  */
1778   di->data = di->repo->repodata + di->repo->nrepodata - 1;
1779   /* Hence the next call to _step will retrieve the next thing.  */
1780 }
1781
1782 void
1783 dataiterator_skip_repo(Dataiterator *di)
1784 {
1785   dataiterator_skip_solvable(di);
1786   /* We're done with all solvables and all extra things for this repo.  */
1787   di->solvid = -1;
1788 }
1789
1790 void
1791 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1792 {
1793   di->repo = s->repo;
1794   /* Simulate us being done with the solvable before the requested one.  */
1795   dataiterator_skip_solvable(di);
1796   di->solvid = s - s->repo->pool->solvables;
1797   di->solvid--;
1798 }
1799
1800 void
1801 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1802 {
1803   di->repo = repo;
1804   dataiterator_skip_solvable(di);
1805   di->solvid = repo->start - 1;
1806 }
1807
1808 #endif
1809
1810 /************************************************************************
1811  * data modify functions
1812  */
1813
1814 /* extend repodata so that it includes solvables p */
1815 void
1816 repodata_extend(Repodata *data, Id p)
1817 {
1818   if (data->start == data->end)
1819     data->start = data->end = p;
1820   if (p >= data->end)
1821     {
1822       int old = data->end - data->start;
1823       int new = p - data->end + 1;
1824       if (data->attrs)
1825         {
1826           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
1827           memset(data->attrs + old, 0, new * sizeof(Id));
1828         }
1829       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1830       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1831       data->end = p + 1;
1832     }
1833   if (p < data->start)
1834     {
1835       int old = data->end - data->start;
1836       int new = data->start - p;
1837       if (data->attrs)
1838         {
1839           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
1840           memmove(data->attrs + new, data->attrs, old * sizeof(Id));
1841           memset(data->attrs, 0, new * sizeof(Id));
1842         }
1843       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1844       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1845       memset(data->incoreoffset, 0, new * sizeof(Id));
1846       data->start = p;
1847     }
1848 }
1849
1850 void
1851 repodata_extend_block(Repodata *data, Id start, Id num)
1852 {
1853   if (!num)
1854     return;
1855   if (!data->incoreoffset)
1856     {
1857       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1858       data->start = start;
1859       data->end = start + num;
1860       return;
1861     }
1862   repodata_extend(data, start);
1863   if (num > 1)
1864     repodata_extend(data, start + num - 1);
1865 }
1866
1867 /**********************************************************************/
1868
1869 #define REPODATA_ATTRS_BLOCK 63
1870 #define REPODATA_ATTRDATA_BLOCK 1023
1871 #define REPODATA_ATTRIDDATA_BLOCK 63
1872
1873
1874 Id
1875 repodata_new_handle(Repodata *data)
1876 {
1877   if (!data->nxattrs)
1878     {
1879       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1880       data->nxattrs = 2;
1881     }
1882   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1883   data->xattrs[data->nxattrs] = 0;
1884   return -(data->nxattrs++);
1885 }
1886
1887 static inline Id **
1888 repodata_get_attrp(Repodata *data, Id handle)
1889 {
1890   if (handle == SOLVID_META)
1891     {
1892       if (!data->xattrs)
1893         {
1894           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1895           data->nxattrs = 2;
1896         }
1897     }
1898   if (handle < 0)
1899     return data->xattrs - handle;
1900   if (handle < data->start || handle >= data->end)
1901     repodata_extend(data, handle);
1902   if (!data->attrs)
1903     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1904   return data->attrs + (handle - data->start);
1905 }
1906
1907 static void
1908 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1909 {
1910   Id *pp;
1911   Id *ap, **app;
1912   int i;
1913
1914   app = repodata_get_attrp(data, handle);
1915   ap = *app;
1916   i = 0;
1917   if (ap)
1918     {
1919       for (pp = ap; *pp; pp += 2)
1920         /* Determine equality based on the name only, allows us to change
1921            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1922         if (data->keys[*pp].name == data->keys[keyid].name)
1923           break;
1924       if (*pp)
1925         {
1926           if (overwrite)
1927             {
1928               pp[0] = keyid;
1929               pp[1] = val;
1930             }
1931           return;
1932         }
1933       i = pp - ap;
1934     }
1935   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1936   *app = ap;
1937   pp = ap + i;
1938   *pp++ = keyid;
1939   *pp++ = val;
1940   *pp = 0;
1941 }
1942
1943
1944 void
1945 repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
1946 {
1947   Id keyid;
1948
1949   keyid = repodata_key2id(data, key, 1);
1950   repodata_insert_keyid(data, handle, keyid, val, 1);
1951 }
1952
1953 void
1954 repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
1955 {
1956   Repokey key;
1957   key.name = keyname;
1958   key.type = REPOKEY_TYPE_ID;
1959   key.size = 0;
1960   key.storage = KEY_STORAGE_INCORE;
1961   repodata_set(data, handle, &key, id);
1962 }
1963
1964 void
1965 repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
1966 {
1967   Repokey key;
1968   key.name = keyname;
1969   key.type = REPOKEY_TYPE_NUM;
1970   key.size = 0;
1971   key.storage = KEY_STORAGE_INCORE;
1972   repodata_set(data, handle, &key, (Id)num);
1973 }
1974
1975 void
1976 repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
1977 {
1978   Repokey key;
1979   Id id;
1980   if (data->localpool)
1981     id = stringpool_str2id(&data->spool, str, 1);
1982   else
1983     id = str2id(data->repo->pool, str, 1);
1984   key.name = keyname;
1985   key.type = REPOKEY_TYPE_ID;
1986   key.size = 0;
1987   key.storage = KEY_STORAGE_INCORE;
1988   repodata_set(data, handle, &key, id);
1989 }
1990
1991 void
1992 repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
1993 {
1994   Repokey key;
1995   key.name = keyname;
1996   key.type = REPOKEY_TYPE_CONSTANT;
1997   key.size = constant;
1998   key.storage = KEY_STORAGE_INCORE;
1999   repodata_set(data, handle, &key, 0);
2000 }
2001
2002 void
2003 repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
2004 {
2005   Repokey key;
2006   key.name = keyname;
2007   key.type = REPOKEY_TYPE_CONSTANTID;
2008   key.size = id;
2009   key.storage = KEY_STORAGE_INCORE;
2010   repodata_set(data, handle, &key, 0);
2011 }
2012
2013 void
2014 repodata_set_void(Repodata *data, Id handle, Id keyname)
2015 {
2016   Repokey key;
2017   key.name = keyname;
2018   key.type = REPOKEY_TYPE_VOID;
2019   key.size = 0;
2020   key.storage = KEY_STORAGE_INCORE;
2021   repodata_set(data, handle, &key, 0);
2022 }
2023
2024 void
2025 repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
2026 {
2027   Repokey key;
2028   int l;
2029
2030   l = strlen(str) + 1;
2031   key.name = keyname;
2032   key.type = REPOKEY_TYPE_STR;
2033   key.size = 0;
2034   key.storage = KEY_STORAGE_INCORE;
2035   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2036   memcpy(data->attrdata + data->attrdatalen, str, l);
2037   repodata_set(data, handle, &key, data->attrdatalen);
2038   data->attrdatalen += l;
2039 }
2040
2041 static void
2042 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2043 {
2044   int oldsize;
2045   Id *ida, *pp, **ppp;
2046
2047   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2048     {
2049       /* great! just append the new data */
2050       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2051       data->attriddatalen--;    /* overwrite terminating 0  */
2052       data->lastdatalen += entrysize;
2053       return;
2054     }
2055   ppp = repodata_get_attrp(data, handle);
2056   pp = *ppp;
2057   if (pp)
2058     for (; *pp; pp += 2)
2059       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
2060         break;
2061   if (!pp || !*pp)
2062     {
2063       /* not found. allocate new key */
2064       Repokey key;
2065       key.name = keyname;
2066       key.type = keytype;
2067       key.size = 0;
2068       key.storage = KEY_STORAGE_INCORE;
2069       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2070       repodata_set(data, handle, &key, data->attriddatalen);
2071       data->lasthandle = 0;     /* next time... */
2072       return;
2073     }
2074   oldsize = 0;
2075   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2076     oldsize += entrysize;
2077   if (ida + 1 == data->attriddata + data->attriddatalen)
2078     {
2079       /* this was the last entry, just append it */
2080       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2081       data->attriddatalen--;    /* overwrite terminating 0  */
2082     }
2083   else
2084     {
2085       /* too bad. move to back. */
2086       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2087       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2088       pp[1] = data->attriddatalen;
2089       data->attriddatalen += oldsize;
2090     }
2091   data->lasthandle = handle;
2092   data->lastkey = *pp;
2093   data->lastdatalen = data->attriddatalen + entrysize + 1;
2094 }
2095
2096 static inline int
2097 checksumtype2len(Id type)
2098 {
2099   switch (type)
2100     {
2101     case REPOKEY_TYPE_MD5:
2102       return SIZEOF_MD5;
2103     case REPOKEY_TYPE_SHA1:
2104       return SIZEOF_SHA1;
2105     case REPOKEY_TYPE_SHA256:
2106       return SIZEOF_SHA256;
2107     default:
2108       return 0;
2109     }
2110 }
2111
2112 void
2113 repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
2114                       const unsigned char *str)
2115 {
2116   Repokey key;
2117   int l = checksumtype2len(type);
2118
2119   if (!l)
2120     return;
2121   key.name = keyname;
2122   key.type = type;
2123   key.size = 0;
2124   key.storage = KEY_STORAGE_INCORE;
2125   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2126   memcpy(data->attrdata + data->attrdatalen, str, l);
2127   repodata_set(data, handle, &key, data->attrdatalen);
2128   data->attrdatalen += l;
2129 }
2130
2131 static int
2132 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2133 {
2134   int i;
2135   for (i = 0; i < buflen; i++)
2136     {
2137 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
2138                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
2139                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
2140                 : -1)
2141       int v = c2h(*str);
2142       str++;
2143       if (v < 0)
2144         return 0;
2145       buf[i] = v;
2146       v = c2h(*str);
2147       str++;
2148       if (v < 0)
2149         return 0;
2150       buf[i] = (buf[i] << 4) | v;
2151 #undef c2h
2152     }
2153   return buflen;
2154 }
2155
2156 void
2157 repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
2158                       const char *str)
2159 {
2160   unsigned char buf[64];
2161   int l = checksumtype2len(type);
2162
2163   if (!l)
2164     return;
2165   if (hexstr2bytes(buf, str, l) != l)
2166     {
2167       fprintf(stderr, "Invalid hex character in '%s'\n", str);
2168       return;
2169     }
2170   repodata_set_bin_checksum(data, handle, keyname, type, buf);
2171 }
2172
2173 const char *
2174 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2175 {
2176   int i, l;
2177   char *str, *s;
2178
2179   l = checksumtype2len(type);
2180   if (!l)
2181     return "";
2182   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2183   for (i = 0; i < l; i++)
2184     {
2185       unsigned char v = buf[i];
2186       unsigned char w = v >> 4;
2187       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2188       w = v & 15;
2189       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2190     }
2191   *s = 0;
2192   return str;
2193 }
2194
2195 Id
2196 repodata_globalize_id(Repodata *data, Id id)
2197 {
2198   if (!data || !data->localpool)
2199     return id;
2200   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
2201 }
2202
2203 void
2204 repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
2205 {
2206   assert(dir);
2207 #if 0
2208 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
2209 #endif
2210   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2211   data->attriddata[data->attriddatalen++] = dir;
2212   data->attriddata[data->attriddatalen++] = num;
2213   data->attriddata[data->attriddatalen++] = num2;
2214   data->attriddata[data->attriddatalen++] = 0;
2215 }
2216
2217 void
2218 repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
2219 {
2220   Id stroff;
2221   int l;
2222
2223   assert(dir);
2224   l = strlen(str) + 1;
2225   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2226   memcpy(data->attrdata + data->attrdatalen, str, l);
2227   stroff = data->attrdatalen;
2228   data->attrdatalen += l;
2229
2230 #if 0
2231 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
2232 #endif
2233   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2234   data->attriddata[data->attriddatalen++] = dir;
2235   data->attriddata[data->attriddatalen++] = stroff;
2236   data->attriddata[data->attriddatalen++] = 0;
2237 }
2238
2239 void
2240 repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
2241 {
2242 #if 0
2243 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
2244 #endif
2245   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
2246   data->attriddata[data->attriddatalen++] = id;
2247   data->attriddata[data->attriddatalen++] = 0;
2248 }
2249
2250 void
2251 repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
2252                            const char *str)
2253 {
2254   Id id;
2255   if (data->localpool)
2256     id = stringpool_str2id(&data->spool, str, 1);
2257   else
2258     id = str2id(data->repo->pool, str, 1);
2259   repodata_add_idarray(data, handle, keyname, id);
2260 }
2261
2262 void
2263 repodata_add_fixarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2264 {
2265   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2266   data->attriddata[data->attriddatalen++] = ghandle;
2267   data->attriddata[data->attriddatalen++] = 0;
2268 }
2269
2270 void
2271 repodata_add_flexarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2272 {
2273   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2274   data->attriddata[data->attriddatalen++] = ghandle;
2275   data->attriddata[data->attriddatalen++] = 0;
2276 }
2277
2278 void
2279 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2280 {
2281   Id *keyp;
2282   if (dest == src || !(keyp = data->attrs[src]))
2283     return;
2284   for (; *keyp; keyp += 2)
2285     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2286 }
2287
2288
2289
2290
2291 /**********************************************************************/
2292
2293 /* unify with repo_write! */
2294
2295 #define EXTDATA_BLOCK 1023
2296
2297 struct extdata {
2298   unsigned char *buf;
2299   int len;
2300 };
2301
2302 static void
2303 data_addid(struct extdata *xd, Id x)
2304 {
2305   unsigned char *dp;
2306   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2307   dp = xd->buf + xd->len;
2308
2309   if (x >= (1 << 14))
2310     {
2311       if (x >= (1 << 28))
2312         *dp++ = (x >> 28) | 128;
2313       if (x >= (1 << 21))
2314         *dp++ = (x >> 21) | 128;
2315       *dp++ = (x >> 14) | 128;
2316     }
2317   if (x >= (1 << 7))
2318     *dp++ = (x >> 7) | 128;
2319   *dp++ = x & 127;
2320   xd->len = dp - xd->buf;
2321 }
2322
2323 static void
2324 data_addideof(struct extdata *xd, Id x, int eof)
2325 {
2326   if (x >= 64)
2327     x = (x & 63) | ((x & ~63) << 1);
2328   data_addid(xd, (eof ? x: x | 64));
2329 }
2330
2331 static void
2332 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2333 {
2334   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2335   memcpy(xd->buf + xd->len, blob, len);
2336   xd->len += len;
2337 }
2338
2339 /*********************************/
2340
2341 static void
2342 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2343                        struct extdata *newvincore,
2344                        Id *schema,
2345                        Repokey *key, Id val)
2346 {
2347   /* Otherwise we have a new value.  Parse it into the internal
2348      form.  */
2349   Id *ida;
2350   struct extdata *xd;
2351   unsigned int oldvincorelen = 0;
2352   Id schemaid, *sp;
2353
2354   xd = newincore;
2355   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2356     {
2357       xd = newvincore;
2358       oldvincorelen = xd->len;
2359     }
2360   switch (key->type)
2361     {
2362     case REPOKEY_TYPE_VOID:
2363     case REPOKEY_TYPE_CONSTANT:
2364     case REPOKEY_TYPE_CONSTANTID:
2365       break;
2366     case REPOKEY_TYPE_STR:
2367       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2368       break;
2369     case REPOKEY_TYPE_MD5:
2370       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2371       break;
2372     case REPOKEY_TYPE_SHA1:
2373       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2374       break;
2375     case REPOKEY_TYPE_SHA256:
2376       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2377       break;
2378     case REPOKEY_TYPE_ID:
2379     case REPOKEY_TYPE_NUM:
2380     case REPOKEY_TYPE_DIR:
2381       data_addid(xd, val);
2382       break;
2383     case REPOKEY_TYPE_IDARRAY:
2384       for (ida = data->attriddata + val; *ida; ida++)
2385         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2386       break;
2387     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2388       for (ida = data->attriddata + val; *ida; ida += 3)
2389         {
2390           data_addid(xd, ida[0]);
2391           data_addid(xd, ida[1]);
2392           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2393         }
2394       break;
2395     case REPOKEY_TYPE_DIRSTRARRAY:
2396       for (ida = data->attriddata + val; *ida; ida += 2)
2397         {
2398           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2399           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2400         }
2401       break;
2402     case REPOKEY_TYPE_FIXARRAY:
2403       {
2404         int num = 0;
2405         schemaid = 0;
2406         for (ida = data->attriddata + val; *ida; ida++)
2407           {
2408 #if 0
2409             fprintf(stderr, "serialize struct %d\n", *ida);
2410 #endif
2411             sp = schema;
2412             Id *kp = data->xattrs[-*ida];
2413             if (!kp)
2414               continue;
2415             num++;
2416             for (;*kp; kp += 2)
2417               {
2418 #if 0
2419                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2420 #endif
2421                 *sp++ = *kp;
2422               }
2423             *sp = 0;
2424             if (!schemaid)
2425               schemaid = repodata_schema2id(data, schema, 1);
2426             else if (schemaid != repodata_schema2id(data, schema, 0))
2427               {
2428                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
2429                 exit(1);
2430               }
2431 #if 0
2432             fprintf(stderr, "  schema %d\n", schemaid);
2433 #endif
2434           }
2435         if (!num)
2436           break;
2437         data_addid(xd, num);
2438         data_addid(xd, schemaid);
2439         for (ida = data->attriddata + val; *ida; ida++)
2440           {
2441             Id *kp = data->xattrs[-*ida];
2442             if (!kp)
2443               continue;
2444             for (;*kp; kp += 2)
2445               {
2446                 repodata_serialize_key(data, newincore, newvincore,
2447                                        schema, data->keys + *kp, kp[1]);
2448               }
2449           }
2450         break;
2451       }
2452     case REPOKEY_TYPE_FLEXARRAY:
2453       {
2454         int num = 0;
2455         for (ida = data->attriddata + val; *ida; ida++)
2456           num++;
2457         data_addid(xd, num);
2458         for (ida = data->attriddata + val; *ida; ida++)
2459           {
2460             Id *kp = data->xattrs[-*ida];
2461             if (!kp)
2462               {
2463                 data_addid(xd, 0);      /* XXX */
2464                 continue;
2465               }
2466             sp = schema;
2467             for (;*kp; kp += 2)
2468               *sp++ = *kp;
2469             *sp = 0;
2470             schemaid = repodata_schema2id(data, schema, 1);
2471             data_addid(xd, schemaid);
2472             kp = data->xattrs[-*ida];
2473             for (;*kp; kp += 2)
2474               {
2475                 repodata_serialize_key(data, newincore, newvincore,
2476                                        schema, data->keys + *kp, kp[1]);
2477               }
2478           }
2479         break;
2480       }
2481     default:
2482       fprintf(stderr, "don't know how to handle type %d\n", key->type);
2483       exit(1);
2484     }
2485   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2486     {
2487       /* put offset/len in incore */
2488       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2489       oldvincorelen = xd->len - oldvincorelen;
2490       data_addid(newincore, oldvincorelen);
2491     }
2492 }
2493
2494 void
2495 repodata_internalize(Repodata *data)
2496 {
2497   Repokey *key, solvkey;
2498   Id entry, nentry;
2499   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2500   unsigned char *dp, *ndp;
2501   int newschema, oldcount;
2502   struct extdata newincore;
2503   struct extdata newvincore;
2504   Id solvkeyid;
2505
2506   if (!data->attrs && !data->xattrs)
2507     return;
2508
2509   newvincore.buf = data->vincore;
2510   newvincore.len = data->vincorelen;
2511
2512   /* find the solvables key, create if needed */
2513   memset(&solvkey, 0, sizeof(solvkey));
2514   solvkey.name = REPOSITORY_SOLVABLES;
2515   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2516   solvkey.size = 0;
2517   solvkey.storage = KEY_STORAGE_INCORE;
2518   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2519
2520   schema = sat_malloc2(data->nkeys, sizeof(Id));
2521   seen = sat_malloc2(data->nkeys, sizeof(Id));
2522
2523   /* Merge the data already existing (in data->schemata, ->incoredata and
2524      friends) with the new attributes in data->attrs[].  */
2525   nentry = data->end - data->start;
2526   memset(&newincore, 0, sizeof(newincore));
2527   data_addid(&newincore, 0);    /* start data at offset 1 */
2528
2529   data->mainschema = 0;
2530   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2531
2532   /* join entry data */
2533   /* we start with the meta data, entry -1 */
2534   for (entry = -1; entry < nentry; entry++)
2535     {
2536       memset(seen, 0, data->nkeys * sizeof(Id));
2537       oldschema = 0;
2538       dp = data->incoredata;
2539       if (dp)
2540         {
2541           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2542           dp = data_read_id(dp, &oldschema);
2543         }
2544 #if 0
2545 fprintf(stderr, "oldschema %d\n", oldschema);
2546 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2547 fprintf(stderr, "schemadata %p\n", data->schemadata);
2548 #endif
2549       /* seen: -1: old data  0: skipped  >0: id + 1 */
2550       newschema = 0;
2551       oldcount = 0;
2552       sp = schema;
2553       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2554         {
2555           if (seen[*keyp])
2556             {
2557               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2558               exit(1);
2559             }
2560           seen[*keyp] = -1;
2561           *sp++ = *keyp;
2562           oldcount++;
2563         }
2564       if (entry >= 0)
2565         keyp = data->attrs ? data->attrs[entry] : 0;
2566       else
2567         {
2568           /* strip solvables key */
2569           *sp = 0;
2570           for (sp = keyp = schema; *sp; sp++)
2571             if (*sp != solvkeyid)
2572               *keyp++ = *sp;
2573             else
2574               oldcount--;
2575           sp = keyp;
2576           seen[solvkeyid] = 0;
2577           keyp = data->xattrs ? data->xattrs[1] : 0;
2578         }
2579       if (keyp)
2580         for (; *keyp; keyp += 2)
2581           {
2582             if (!seen[*keyp])
2583               {
2584                 newschema = 1;
2585                 *sp++ = *keyp;
2586               }
2587             seen[*keyp] = keyp[1] + 1;
2588           }
2589       if (entry < 0 && data->end != data->start)
2590         {
2591           *sp++ = solvkeyid;
2592           newschema = 1;
2593         }
2594       *sp = 0;
2595       if (newschema)
2596         /* Ideally we'd like to sort the new schema here, to ensure
2597            schema equality independend of the ordering.  We can't do that
2598            yet.  For once see below (old ids need to come before new ids).
2599            An additional difficulty is that we also need to move
2600            the values with the keys.  */
2601         schemaid = repodata_schema2id(data, schema, 1);
2602       else
2603         schemaid = oldschema;
2604
2605
2606       /* Now create data blob.  We walk through the (possibly new) schema
2607          and either copy over old data, or insert the new.  */
2608       /* XXX Here we rely on the fact that the (new) schema has the form
2609          o1 o2 o3 o4 ... | n1 n2 n3 ...
2610          (oX being the old keyids (possibly overwritten), and nX being
2611           the new keyids).  This rules out sorting the keyids in order
2612          to ensure a small schema count.  */
2613       if (entry >= 0)
2614         data->incoreoffset[entry] = newincore.len;
2615       data_addid(&newincore, schemaid);
2616       if (entry == -1)
2617         {
2618           data->mainschema = schemaid;
2619           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2620         }
2621       keypstart = data->schemadata + data->schemata[schemaid];
2622       for (keyp = keypstart; *keyp; keyp++)
2623         {
2624           if (entry == -1)
2625             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2626           if (*keyp == solvkeyid)
2627             {
2628               /* add flexarray entry count */
2629               data_addid(&newincore, data->end - data->start);
2630               break;
2631             }
2632           key = data->keys + *keyp;
2633 #if 0
2634           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2635 #endif
2636           ndp = dp;
2637           if (oldcount)
2638             {
2639               /* Skip the data associated with this old key.  */
2640               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2641                 {
2642                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2643                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2644                 }
2645               else if (key->storage == KEY_STORAGE_INCORE)
2646                 ndp = data_skip_key(data, dp, key);
2647               oldcount--;
2648             }
2649           if (seen[*keyp] == -1)
2650             {
2651               /* If this key was an old one _and_ was not overwritten with
2652                  a different value copy over the old value (we skipped it
2653                  above).  */
2654               if (dp != ndp)
2655                 data_addblob(&newincore, dp, ndp - dp);
2656               seen[*keyp] = 0;
2657             }
2658           else if (seen[*keyp])
2659             {
2660               /* Otherwise we have a new value.  Parse it into the internal
2661                  form.  */
2662               repodata_serialize_key(data, &newincore, &newvincore,
2663                                      schema, key, seen[*keyp] - 1);
2664             }
2665           dp = ndp;
2666         }
2667       if (entry >= 0 && data->attrs && data->attrs[entry])
2668         data->attrs[entry] = sat_free(data->attrs[entry]);
2669     }
2670   /* free all xattrs */
2671   for (entry = 0; entry < data->nxattrs; entry++)
2672     if (data->xattrs[entry])
2673       sat_free(data->xattrs[entry]);
2674   data->xattrs = sat_free(data->xattrs);
2675   data->nxattrs = 0;
2676
2677   data->lasthandle = 0;
2678   data->lastkey = 0;
2679   data->lastdatalen = 0;
2680   sat_free(schema);
2681   sat_free(seen);
2682   repodata_free_schemahash(data);
2683
2684   sat_free(data->incoredata);
2685   data->incoredata = newincore.buf;
2686   data->incoredatalen = newincore.len;
2687   data->incoredatafree = 0;
2688
2689   sat_free(data->vincore);
2690   data->vincore = newvincore.buf;
2691   data->vincorelen = newvincore.len;
2692
2693   data->attrs = sat_free(data->attrs);
2694   data->attrdata = sat_free(data->attrdata);
2695   data->attriddata = sat_free(data->attriddata);
2696   data->attrdatalen = 0;
2697   data->attriddatalen = 0;
2698 }
2699
2700 void
2701 repodata_disable_paging(Repodata *data)
2702 {
2703   if (maybe_load_repodata(data, 0))
2704     repopagestore_disable_paging(&data->store);
2705 }
2706
2707 /*
2708 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2709 */