- add dataiterator_setpos_parent
[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   return datamatcher_match(&matcher, di->data, di->key, &di->kv);
1287 }
1288
1289 #else
1290
1291 /************************************************************************
1292  * data search iterator
1293  */
1294
1295 static void
1296 dataiterator_newdata(Dataiterator *di)
1297 {
1298   Id keyname = di->keyname;
1299   Repodata *data = di->data;
1300   di->nextkeydp = 0;
1301
1302   if (data->state == REPODATA_STUB)
1303     {
1304       if (keyname)
1305         {
1306           int j;
1307           for (j = 1; j < data->nkeys; j++)
1308             if (keyname == data->keys[j].name)
1309               break;
1310           if (j == data->nkeys)
1311             return;
1312         }
1313       /* load it */
1314       if (data->loadcallback)
1315         data->loadcallback(data);
1316       else
1317         data->state = REPODATA_ERROR;
1318     }
1319   if (data->state == REPODATA_ERROR)
1320     return;
1321
1322   Id schema;
1323   unsigned char *dp = data->incoredata;
1324   if (!dp)
1325     return;
1326   if (di->solvid >= 0)
1327     dp += data->incoreoffset[di->solvid - data->start];
1328   dp = data_read_id(dp, &schema);
1329   Id *keyp = data->schemadata + data->schemata[schema];
1330   if (keyname)
1331     {
1332       Id k, *kp;
1333       /* search in a specific key */
1334       for (kp = keyp; (k = *kp++) != 0; )
1335         if (data->keys[k].name == keyname)
1336           break;
1337       if (k == 0)
1338         return;
1339       dp = forward_to_key(data, k, keyp, dp);
1340       if (!dp)
1341         return;
1342       keyp = kp - 1;
1343     }
1344   Id keyid = *keyp++;
1345   if (!keyid)
1346     return;
1347
1348   di->data = data;
1349   di->key = di->data->keys + keyid;
1350   di->keyp = keyp;
1351   di->dp = 0;
1352
1353   di->nextkeydp = dp;
1354   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1355   di->kv.eof = 0;
1356 }
1357
1358 void
1359 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
1360                   const char *match, int flags)
1361 {
1362   di->flags = flags;
1363   if (p > 0)
1364     {
1365       di->solvid = p;
1366       di->flags |= __SEARCH_ONESOLVABLE;
1367       di->data = repo->repodata - 1;
1368       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
1369         di->state = 0;
1370       else
1371         di->state = 1;
1372     }
1373   else
1374     {
1375       di->solvid = repo->start - 1;
1376       if (di->solvid < 0)
1377         {
1378           fprintf(stderr, "A repo contains the NULL solvable!\n");
1379           exit(1);
1380         }
1381       di->data = repo->repodata + repo->nrepodata - 1;
1382       di->state = 0;
1383     }
1384
1385   di->match = match;
1386   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1387     {
1388       if (di->match)
1389         {
1390           /* We feed multiple lines eventually (e.g. authors or descriptions),
1391              so set REG_NEWLINE. */
1392           di->regex_err =
1393             regcomp(&di->regex, di->match,
1394               REG_EXTENDED | REG_NOSUB | REG_NEWLINE
1395               | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1396 #if 0
1397           if (di->regex_err != 0)
1398             {
1399               fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
1400               fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
1401               exit(1);
1402             }
1403 #else
1404         }
1405       else
1406         {
1407           di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
1408           di->regex_err = 0;
1409 #endif
1410         }
1411     }
1412
1413   di->keyname = keyname;
1414   static Id zeroid = 0;
1415   di->keyp = &zeroid;
1416   di->kv.eof = 1;
1417   di->repo = repo;
1418   di->idp = 0;
1419   di->subkeyp = 0;
1420 }
1421
1422 /* FIXME factor and merge with repo_matchvalue */
1423 static int
1424 dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
1425 {
1426   KeyValue *kv = &di->kv;
1427   const char *match = vmatch;
1428   if ((flags & SEARCH_STRINGMASK) != 0)
1429     {
1430       switch (di->key->type)
1431         {
1432         case REPOKEY_TYPE_ID:
1433         case REPOKEY_TYPE_IDARRAY:
1434           if (di->data && di->data->localpool)
1435             kv->str = stringpool_id2str(&di->data->spool, kv->id);
1436           else
1437             kv->str = id2str(di->repo->pool, kv->id);
1438           break;
1439         case REPOKEY_TYPE_STR:
1440           break;
1441         case REPOKEY_TYPE_DIRSTRARRAY:
1442           if (!(flags & SEARCH_FILES))
1443             return 0;
1444           /* Put the full filename into kv->str.  */
1445           kv->str = repodata_dir2str(di->data, kv->id, kv->str);
1446           /* And to compensate for that put the "empty" directory into
1447              kv->id, so that later calls to repodata_dir2str on this data
1448              come up with the same filename again.  */
1449           kv->id = 0;
1450           break;
1451         default:
1452           return 0;
1453         }
1454       /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
1455          for the others we can't know if a colon separates a kind or not.  */
1456       if ((flags & SEARCH_SKIP_KIND)
1457           && di->key->storage == KEY_STORAGE_SOLVABLE)
1458         {
1459           const char *s = strchr(kv->str, ':');
1460           if (s)
1461             kv->str = s + 1;
1462         }
1463       switch ((flags & SEARCH_STRINGMASK))
1464         {
1465           case SEARCH_SUBSTRING:
1466             if (flags & SEARCH_NOCASE)
1467               {
1468                 if (!strcasestr(kv->str, match))
1469                   return 0;
1470               }
1471             else
1472               {
1473                 if (!strstr(kv->str, match))
1474                   return 0;
1475               }
1476             break;
1477           case SEARCH_STRING:
1478             if (flags & SEARCH_NOCASE)
1479               {
1480                 if (strcasecmp(match, kv->str))
1481                   return 0;
1482               }
1483             else
1484               {
1485                 if (strcmp(match, kv->str))
1486                   return 0;
1487               }
1488             break;
1489           case SEARCH_GLOB:
1490             if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1491               return 0;
1492             break;
1493           case SEARCH_REGEX:
1494             if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
1495               return 0;
1496             break;
1497           default:
1498             return 0;
1499         }
1500     }
1501   return 1;
1502 }
1503
1504 static int
1505 dataiterator_match_int(Dataiterator *di)
1506 {
1507   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1508     return dataiterator_match_int_real(di, di->flags, &di->regex);
1509   else
1510     return dataiterator_match_int_real(di, di->flags, di->match);
1511 }
1512
1513 int
1514 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1515 {
1516   return dataiterator_match_int_real(di, flags, vmatch);
1517 }
1518
1519 int
1520 dataiterator_step(Dataiterator *di)
1521 {
1522 restart:
1523   while (1)
1524     {
1525       if (di->state)
1526         {
1527           /* we're stepping through solvable data, 1 -> SOLVABLE_NAME... */
1528           if (di->idp)
1529             {
1530               /* we're stepping through an id array */
1531               Id *idp = di->idp;
1532               if (*idp)
1533                 {
1534                   di->kv.id = *idp;
1535                   di->idp++;
1536                   di->kv.eof = idp[1] ? 0 : 1;
1537                   goto weg2;
1538                 }
1539               else
1540                 di->idp = 0;
1541             }
1542           Solvable *s = di->repo->pool->solvables + di->solvid;
1543           int state = di->state;
1544           di->key = solvablekeys + state - 1;
1545           if (di->keyname)
1546             di->state = RPM_RPMDBID;
1547           else
1548             di->state++;
1549           if (state == 1)
1550             {
1551               di->data = 0;
1552               if (di->keyname)
1553                 state = di->keyname - 1;
1554             }
1555           switch (state + 1)
1556             {
1557               case SOLVABLE_NAME:
1558                 if (!s->name)
1559                   continue;
1560                 di->kv.id = s->name;
1561                 di->kv.eof = 1;
1562                 break;
1563               case SOLVABLE_ARCH:
1564                 if (!s->arch)
1565                   continue;
1566                 di->kv.id = s->arch;
1567                 di->kv.eof = 1;
1568                 break;
1569               case SOLVABLE_EVR:
1570                 if (!s->evr)
1571                   continue;
1572                 di->kv.id = s->evr;
1573                 di->kv.eof = 1;
1574                 break;
1575               case SOLVABLE_VENDOR:
1576                 if (!s->vendor)
1577                   continue;
1578                 di->kv.id = s->vendor;
1579                 di->kv.eof = 1;
1580                 break;
1581               case SOLVABLE_PROVIDES:
1582                 di->idp = s->provides
1583                     ? di->repo->idarraydata + s->provides : 0;
1584                 continue;
1585               case SOLVABLE_OBSOLETES:
1586                 di->idp = s->obsoletes
1587                     ? di->repo->idarraydata + s->obsoletes : 0;
1588                 continue;
1589               case SOLVABLE_CONFLICTS:
1590                 di->idp = s->conflicts
1591                     ? di->repo->idarraydata + s->conflicts : 0;
1592                 continue;
1593               case SOLVABLE_REQUIRES:
1594                 di->idp = s->requires
1595                     ? di->repo->idarraydata + s->requires : 0;
1596                 continue;
1597               case SOLVABLE_RECOMMENDS:
1598                 di->idp = s->recommends
1599                     ? di->repo->idarraydata + s->recommends : 0;
1600                 continue;
1601               case SOLVABLE_SUPPLEMENTS:
1602                 di->idp = s->supplements
1603                     ? di->repo->idarraydata + s->supplements : 0;
1604                 continue;
1605               case SOLVABLE_SUGGESTS:
1606                 di->idp = s->suggests
1607                     ? di->repo->idarraydata + s->suggests : 0;
1608                 continue;
1609               case SOLVABLE_ENHANCES:
1610                 di->idp = s->enhances
1611                     ? di->repo->idarraydata + s->enhances : 0;
1612                 continue;
1613               case RPM_RPMDBID:
1614                 if (!di->repo->rpmdbid)
1615                   continue;
1616                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
1617                 di->kv.eof = 1;
1618                 break;
1619               default:
1620                 di->data = di->repo->repodata - 1;
1621                 di->kv.eof = 1;
1622                 di->state = 0;
1623                 continue;
1624             }
1625         }
1626       else if (di->subkeyp)
1627         {
1628           Id keyid;
1629           if (!di->subnum)
1630             {
1631               /* Send end-of-substruct.  We are here only when we saw a
1632                  _COUNTED key one level up.  Since then we didn't increment
1633                  ->keyp, so it still can be found at keyp[-1].  */
1634               di->kv.eof = 2;
1635               di->key = di->data->keys + di->keyp[-1];
1636               di->subkeyp = 0;
1637             }
1638           else if (!(keyid = *di->subkeyp++))
1639             {
1640               /* Send end-of-element.  See above for keyp[-1].  */
1641               di->kv.eof = 1;
1642               di->key = di->data->keys + di->keyp[-1];
1643               if (di->subschema)
1644                 di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1645               else
1646                 {
1647                   di->dp = data_read_id(di->dp, &di->subschema);
1648                   di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1649                   di->subschema = 0;
1650                 }
1651               di->subnum--;
1652             }
1653           else
1654             {
1655               di->key = di->data->keys + keyid;
1656               di->dp = data_fetch(di->dp, &di->kv, di->key);
1657               if (!di->dp)
1658                 exit(1);
1659             }
1660         }
1661       else
1662         {
1663           if (di->kv.eof)
1664             di->dp = 0;
1665           else
1666             di->dp = data_fetch(di->dp, &di->kv, di->key);
1667
1668           while (!di->dp)
1669             {
1670               Id keyid;
1671               if (di->keyname || !(keyid = *di->keyp++))
1672                 {
1673                   while (1)
1674                     {
1675                       Repo *repo = di->repo;
1676                       Repodata *data = ++di->data;
1677                       if (data >= repo->repodata + repo->nrepodata)
1678                         {
1679                           if (di->flags & __SEARCH_ONESOLVABLE)
1680                             return 0;
1681                           if (di->solvid >= 0)
1682                             {
1683                               while (++di->solvid < repo->end)
1684                                 if (repo->pool->solvables[di->solvid].repo == repo)
1685                                   break;
1686                               if (di->solvid >= repo->end)
1687                                 {
1688                                   if (!(di->flags & SEARCH_EXTRA))
1689                                     goto skiprepo;
1690                                   goto skiprepo;
1691                                 }
1692                             }
1693                           else
1694                             {
1695                                 {
1696 skiprepo:;
1697                                   Pool *pool = di->repo->pool;
1698                                   if (!(di->flags & SEARCH_ALL_REPOS)
1699                                       || di->repo == pool->repos[pool->nrepos - 1])
1700                                     return 0;
1701                                   int i;
1702                                   for (i = 0; i < pool->nrepos; i++)
1703                                     if (di->repo == pool->repos[i])
1704                                       break;
1705                                   di->repo = pool->repos[i + 1];
1706                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
1707                                   continue;
1708                                 }
1709                             }
1710                           di->data = repo->repodata - 1;
1711                           if ((di->flags & SEARCH_NO_STORAGE_SOLVABLE))
1712                             continue;
1713                           static Id zeroid = 0;
1714                           di->keyp = &zeroid;
1715                           di->state = 1;
1716                           goto restart;
1717                         }
1718                       if ((di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
1719                         {
1720                           dataiterator_newdata(di);
1721                           if (di->nextkeydp)
1722                             break;
1723                         }
1724                     }
1725                 }
1726               else
1727                 {
1728                   di->key = di->data->keys + keyid;
1729                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1730                 }
1731               di->dp = data_fetch(di->dp, &di->kv, di->key);
1732             }
1733           if (di->key->type == REPOKEY_TYPE_FIXARRAY)
1734             {
1735               di->subnum = di->kv.num;
1736               di->subschema = di->kv.id;
1737               di->kv.eof = 0;
1738               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1739             }
1740           if (di->key->type == REPOKEY_TYPE_FLEXARRAY)
1741             {
1742               di->subnum = di->kv.num;
1743               di->kv.eof = 0;
1744               di->dp = data_read_id(di->dp, &di->subschema);
1745               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1746               di->subschema = 0;
1747             }
1748         }
1749 weg2:
1750       if (!di->match
1751           || dataiterator_match_int(di))
1752         break;
1753     }
1754   return 1;
1755 }
1756
1757 void
1758 dataiterator_skip_attribute(Dataiterator *di)
1759 {
1760   if (di->state)
1761     di->idp = 0;
1762   /* This will make the next _step call to retrieve the next field.  */
1763   di->kv.eof = 1;
1764 }
1765
1766 void
1767 dataiterator_skip_solvable(Dataiterator *di)
1768 {
1769   /* We're done with this field.  */
1770   di->kv.eof = 1;
1771   /* And with solvable data.  */
1772   di->state = 0;
1773   /* And with all keys for this repodata and thing. */
1774   static Id zeroid = 0;
1775   di->keyp = &zeroid;
1776   /* And with all repodatas for this thing.  */
1777   di->data = di->repo->repodata + di->repo->nrepodata - 1;
1778   /* Hence the next call to _step will retrieve the next thing.  */
1779 }
1780
1781 void
1782 dataiterator_skip_repo(Dataiterator *di)
1783 {
1784   dataiterator_skip_solvable(di);
1785   /* We're done with all solvables and all extra things for this repo.  */
1786   di->solvid = -1;
1787 }
1788
1789 void
1790 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1791 {
1792   di->repo = s->repo;
1793   /* Simulate us being done with the solvable before the requested one.  */
1794   dataiterator_skip_solvable(di);
1795   di->solvid = s - s->repo->pool->solvables;
1796   di->solvid--;
1797 }
1798
1799 void
1800 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1801 {
1802   di->repo = repo;
1803   dataiterator_skip_solvable(di);
1804   di->solvid = repo->start - 1;
1805 }
1806
1807 #endif
1808
1809 /************************************************************************
1810  * data modify functions
1811  */
1812
1813 /* extend repodata so that it includes solvables p */
1814 void
1815 repodata_extend(Repodata *data, Id p)
1816 {
1817   if (data->start == data->end)
1818     data->start = data->end = p;
1819   if (p >= data->end)
1820     {
1821       int old = data->end - data->start;
1822       int new = p - data->end + 1;
1823       if (data->attrs)
1824         {
1825           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
1826           memset(data->attrs + old, 0, new * sizeof(Id));
1827         }
1828       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1829       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1830       data->end = p + 1;
1831     }
1832   if (p < data->start)
1833     {
1834       int old = data->end - data->start;
1835       int new = data->start - p;
1836       if (data->attrs)
1837         {
1838           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
1839           memmove(data->attrs + new, data->attrs, old * sizeof(Id));
1840           memset(data->attrs, 0, new * sizeof(Id));
1841         }
1842       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1843       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1844       memset(data->incoreoffset, 0, new * sizeof(Id));
1845       data->start = p;
1846     }
1847 }
1848
1849 void
1850 repodata_extend_block(Repodata *data, Id start, Id num)
1851 {
1852   if (!num)
1853     return;
1854   if (!data->incoreoffset)
1855     {
1856       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1857       data->start = start;
1858       data->end = start + num;
1859       return;
1860     }
1861   repodata_extend(data, start);
1862   if (num > 1)
1863     repodata_extend(data, start + num - 1);
1864 }
1865
1866 /**********************************************************************/
1867
1868 #define REPODATA_ATTRS_BLOCK 63
1869 #define REPODATA_ATTRDATA_BLOCK 1023
1870 #define REPODATA_ATTRIDDATA_BLOCK 63
1871
1872
1873 Id
1874 repodata_new_handle(Repodata *data)
1875 {
1876   if (!data->nxattrs)
1877     {
1878       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1879       data->nxattrs = 2;
1880     }
1881   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1882   data->xattrs[data->nxattrs] = 0;
1883   return -(data->nxattrs++);
1884 }
1885
1886 static inline Id **
1887 repodata_get_attrp(Repodata *data, Id handle)
1888 {
1889   if (handle == SOLVID_META)
1890     {
1891       if (!data->xattrs)
1892         {
1893           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1894           data->nxattrs = 2;
1895         }
1896     }
1897   if (handle < 0)
1898     return data->xattrs - handle;
1899   if (handle < data->start || handle >= data->end)
1900     repodata_extend(data, handle);
1901   if (!data->attrs)
1902     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1903   return data->attrs + (handle - data->start);
1904 }
1905
1906 static void
1907 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1908 {
1909   Id *pp;
1910   Id *ap, **app;
1911   int i;
1912
1913   app = repodata_get_attrp(data, handle);
1914   ap = *app;
1915   i = 0;
1916   if (ap)
1917     {
1918       for (pp = ap; *pp; pp += 2)
1919         /* Determine equality based on the name only, allows us to change
1920            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1921         if (data->keys[*pp].name == data->keys[keyid].name)
1922           break;
1923       if (*pp)
1924         {
1925           if (overwrite)
1926             {
1927               pp[0] = keyid;
1928               pp[1] = val;
1929             }
1930           return;
1931         }
1932       i = pp - ap;
1933     }
1934   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1935   *app = ap;
1936   pp = ap + i;
1937   *pp++ = keyid;
1938   *pp++ = val;
1939   *pp = 0;
1940 }
1941
1942
1943 void
1944 repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
1945 {
1946   Id keyid;
1947
1948   keyid = repodata_key2id(data, key, 1);
1949   repodata_insert_keyid(data, handle, keyid, val, 1);
1950 }
1951
1952 void
1953 repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
1954 {
1955   Repokey key;
1956   key.name = keyname;
1957   key.type = REPOKEY_TYPE_ID;
1958   key.size = 0;
1959   key.storage = KEY_STORAGE_INCORE;
1960   repodata_set(data, handle, &key, id);
1961 }
1962
1963 void
1964 repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
1965 {
1966   Repokey key;
1967   key.name = keyname;
1968   key.type = REPOKEY_TYPE_NUM;
1969   key.size = 0;
1970   key.storage = KEY_STORAGE_INCORE;
1971   repodata_set(data, handle, &key, (Id)num);
1972 }
1973
1974 void
1975 repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
1976 {
1977   Repokey key;
1978   Id id;
1979   if (data->localpool)
1980     id = stringpool_str2id(&data->spool, str, 1);
1981   else
1982     id = str2id(data->repo->pool, str, 1);
1983   key.name = keyname;
1984   key.type = REPOKEY_TYPE_ID;
1985   key.size = 0;
1986   key.storage = KEY_STORAGE_INCORE;
1987   repodata_set(data, handle, &key, id);
1988 }
1989
1990 void
1991 repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
1992 {
1993   Repokey key;
1994   key.name = keyname;
1995   key.type = REPOKEY_TYPE_CONSTANT;
1996   key.size = constant;
1997   key.storage = KEY_STORAGE_INCORE;
1998   repodata_set(data, handle, &key, 0);
1999 }
2000
2001 void
2002 repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
2003 {
2004   Repokey key;
2005   key.name = keyname;
2006   key.type = REPOKEY_TYPE_CONSTANTID;
2007   key.size = id;
2008   key.storage = KEY_STORAGE_INCORE;
2009   repodata_set(data, handle, &key, 0);
2010 }
2011
2012 void
2013 repodata_set_void(Repodata *data, Id handle, Id keyname)
2014 {
2015   Repokey key;
2016   key.name = keyname;
2017   key.type = REPOKEY_TYPE_VOID;
2018   key.size = 0;
2019   key.storage = KEY_STORAGE_INCORE;
2020   repodata_set(data, handle, &key, 0);
2021 }
2022
2023 void
2024 repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
2025 {
2026   Repokey key;
2027   int l;
2028
2029   l = strlen(str) + 1;
2030   key.name = keyname;
2031   key.type = REPOKEY_TYPE_STR;
2032   key.size = 0;
2033   key.storage = KEY_STORAGE_INCORE;
2034   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2035   memcpy(data->attrdata + data->attrdatalen, str, l);
2036   repodata_set(data, handle, &key, data->attrdatalen);
2037   data->attrdatalen += l;
2038 }
2039
2040 static void
2041 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2042 {
2043   int oldsize;
2044   Id *ida, *pp, **ppp;
2045
2046   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2047     {
2048       /* great! just append the new data */
2049       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2050       data->attriddatalen--;    /* overwrite terminating 0  */
2051       data->lastdatalen += entrysize;
2052       return;
2053     }
2054   ppp = repodata_get_attrp(data, handle);
2055   pp = *ppp;
2056   if (pp)
2057     for (; *pp; pp += 2)
2058       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
2059         break;
2060   if (!pp || !*pp)
2061     {
2062       /* not found. allocate new key */
2063       Repokey key;
2064       key.name = keyname;
2065       key.type = keytype;
2066       key.size = 0;
2067       key.storage = KEY_STORAGE_INCORE;
2068       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2069       repodata_set(data, handle, &key, data->attriddatalen);
2070       data->lasthandle = 0;     /* next time... */
2071       return;
2072     }
2073   oldsize = 0;
2074   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2075     oldsize += entrysize;
2076   if (ida + 1 == data->attriddata + data->attriddatalen)
2077     {
2078       /* this was the last entry, just append it */
2079       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2080       data->attriddatalen--;    /* overwrite terminating 0  */
2081     }
2082   else
2083     {
2084       /* too bad. move to back. */
2085       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2086       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2087       pp[1] = data->attriddatalen;
2088       data->attriddatalen += oldsize;
2089     }
2090   data->lasthandle = handle;
2091   data->lastkey = *pp;
2092   data->lastdatalen = data->attriddatalen + entrysize + 1;
2093 }
2094
2095 static inline int
2096 checksumtype2len(Id type)
2097 {
2098   switch (type)
2099     {
2100     case REPOKEY_TYPE_MD5:
2101       return SIZEOF_MD5;
2102     case REPOKEY_TYPE_SHA1:
2103       return SIZEOF_SHA1;
2104     case REPOKEY_TYPE_SHA256:
2105       return SIZEOF_SHA256;
2106     default:
2107       return 0;
2108     }
2109 }
2110
2111 void
2112 repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
2113                       const unsigned char *str)
2114 {
2115   Repokey key;
2116   int l = checksumtype2len(type);
2117
2118   if (!l)
2119     return;
2120   key.name = keyname;
2121   key.type = type;
2122   key.size = 0;
2123   key.storage = KEY_STORAGE_INCORE;
2124   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2125   memcpy(data->attrdata + data->attrdatalen, str, l);
2126   repodata_set(data, handle, &key, data->attrdatalen);
2127   data->attrdatalen += l;
2128 }
2129
2130 static int
2131 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2132 {
2133   int i;
2134   for (i = 0; i < buflen; i++)
2135     {
2136 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
2137                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
2138                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
2139                 : -1)
2140       int v = c2h(*str);
2141       str++;
2142       if (v < 0)
2143         return 0;
2144       buf[i] = v;
2145       v = c2h(*str);
2146       str++;
2147       if (v < 0)
2148         return 0;
2149       buf[i] = (buf[i] << 4) | v;
2150 #undef c2h
2151     }
2152   return buflen;
2153 }
2154
2155 void
2156 repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
2157                       const char *str)
2158 {
2159   unsigned char buf[64];
2160   int l = checksumtype2len(type);
2161
2162   if (!l)
2163     return;
2164   if (hexstr2bytes(buf, str, l) != l)
2165     {
2166       fprintf(stderr, "Invalid hex character in '%s'\n", str);
2167       return;
2168     }
2169   repodata_set_bin_checksum(data, handle, keyname, type, buf);
2170 }
2171
2172 const char *
2173 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2174 {
2175   int i, l;
2176   char *str, *s;
2177
2178   l = checksumtype2len(type);
2179   if (!l)
2180     return "";
2181   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2182   for (i = 0; i < l; i++)
2183     {
2184       unsigned char v = buf[i];
2185       unsigned char w = v >> 4;
2186       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2187       w = v & 15;
2188       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2189     }
2190   *s = 0;
2191   return str;
2192 }
2193
2194 Id
2195 repodata_globalize_id(Repodata *data, Id id)
2196
2197   if (!data || !data->localpool)
2198     return id;
2199   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
2200 }
2201
2202 void
2203 repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
2204 {
2205   assert(dir);
2206 #if 0
2207 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
2208 #endif
2209   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2210   data->attriddata[data->attriddatalen++] = dir;
2211   data->attriddata[data->attriddatalen++] = num;
2212   data->attriddata[data->attriddatalen++] = num2;
2213   data->attriddata[data->attriddatalen++] = 0;
2214 }
2215
2216 void
2217 repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
2218 {
2219   Id stroff;
2220   int l;
2221
2222   assert(dir);
2223   l = strlen(str) + 1;
2224   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2225   memcpy(data->attrdata + data->attrdatalen, str, l);
2226   stroff = data->attrdatalen;
2227   data->attrdatalen += l;
2228
2229 #if 0
2230 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
2231 #endif
2232   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2233   data->attriddata[data->attriddatalen++] = dir;
2234   data->attriddata[data->attriddatalen++] = stroff;
2235   data->attriddata[data->attriddatalen++] = 0;
2236 }
2237
2238 void
2239 repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
2240 {
2241 #if 0
2242 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
2243 #endif
2244   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
2245   data->attriddata[data->attriddatalen++] = id;
2246   data->attriddata[data->attriddatalen++] = 0;
2247 }
2248
2249 void
2250 repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
2251                            const char *str)
2252 {
2253   Id id;
2254   if (data->localpool)
2255     id = stringpool_str2id(&data->spool, str, 1);
2256   else
2257     id = str2id(data->repo->pool, str, 1);
2258   repodata_add_idarray(data, handle, keyname, id);
2259 }
2260
2261 void
2262 repodata_add_fixarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2263 {
2264   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2265   data->attriddata[data->attriddatalen++] = ghandle;
2266   data->attriddata[data->attriddatalen++] = 0;
2267 }
2268
2269 void
2270 repodata_add_flexarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2271 {
2272   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2273   data->attriddata[data->attriddatalen++] = ghandle;
2274   data->attriddata[data->attriddatalen++] = 0;
2275 }
2276
2277 void
2278 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2279 {
2280   Id *keyp;
2281   if (dest == src || !(keyp = data->attrs[src]))
2282     return;
2283   for (; *keyp; keyp += 2)
2284     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2285 }
2286
2287
2288
2289
2290 /**********************************************************************/
2291
2292 /* unify with repo_write! */
2293
2294 #define EXTDATA_BLOCK 1023
2295
2296 struct extdata {
2297   unsigned char *buf;
2298   int len;
2299 };
2300
2301 static void
2302 data_addid(struct extdata *xd, Id x)
2303 {
2304   unsigned char *dp;
2305   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2306   dp = xd->buf + xd->len;
2307
2308   if (x >= (1 << 14))
2309     {
2310       if (x >= (1 << 28))
2311         *dp++ = (x >> 28) | 128;
2312       if (x >= (1 << 21))
2313         *dp++ = (x >> 21) | 128;
2314       *dp++ = (x >> 14) | 128;
2315     }
2316   if (x >= (1 << 7))
2317     *dp++ = (x >> 7) | 128;
2318   *dp++ = x & 127;
2319   xd->len = dp - xd->buf;
2320 }
2321
2322 static void
2323 data_addideof(struct extdata *xd, Id x, int eof)
2324 {
2325   if (x >= 64)
2326     x = (x & 63) | ((x & ~63) << 1);
2327   data_addid(xd, (eof ? x: x | 64));
2328 }
2329
2330 static void
2331 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2332 {
2333   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2334   memcpy(xd->buf + xd->len, blob, len);
2335   xd->len += len;
2336 }
2337
2338 /*********************************/
2339
2340 static void
2341 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2342                        struct extdata *newvincore,
2343                        Id *schema,
2344                        Repokey *key, Id val)
2345 {
2346   /* Otherwise we have a new value.  Parse it into the internal
2347      form.  */
2348   Id *ida;
2349   struct extdata *xd;
2350   unsigned int oldvincorelen = 0;
2351   Id schemaid, *sp;
2352
2353   xd = newincore;
2354   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2355     {
2356       xd = newvincore;
2357       oldvincorelen = xd->len;
2358     }
2359   switch (key->type)
2360     {
2361     case REPOKEY_TYPE_VOID:
2362     case REPOKEY_TYPE_CONSTANT:
2363     case REPOKEY_TYPE_CONSTANTID:
2364       break;
2365     case REPOKEY_TYPE_STR:
2366       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2367       break;
2368     case REPOKEY_TYPE_MD5:
2369       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2370       break;
2371     case REPOKEY_TYPE_SHA1:
2372       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2373       break;
2374     case REPOKEY_TYPE_SHA256:
2375       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2376       break;
2377     case REPOKEY_TYPE_ID:
2378     case REPOKEY_TYPE_NUM:
2379     case REPOKEY_TYPE_DIR:
2380       data_addid(xd, val);
2381       break;
2382     case REPOKEY_TYPE_IDARRAY:
2383       for (ida = data->attriddata + val; *ida; ida++)
2384         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2385       break;
2386     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2387       for (ida = data->attriddata + val; *ida; ida += 3)
2388         {
2389           data_addid(xd, ida[0]);
2390           data_addid(xd, ida[1]);
2391           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2392         }
2393       break;
2394     case REPOKEY_TYPE_DIRSTRARRAY:
2395       for (ida = data->attriddata + val; *ida; ida += 2)
2396         {
2397           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2398           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2399         }
2400       break;
2401     case REPOKEY_TYPE_FIXARRAY:
2402       {
2403         int num = 0;
2404         schemaid = 0;
2405         for (ida = data->attriddata + val; *ida; ida++)
2406           {
2407 #if 0
2408             fprintf(stderr, "serialize struct %d\n", *ida);
2409 #endif
2410             sp = schema;
2411             Id *kp = data->xattrs[-*ida];
2412             if (!kp)
2413               continue;
2414             num++;
2415             for (;*kp; kp += 2)
2416               {
2417 #if 0
2418                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2419 #endif
2420                 *sp++ = *kp;
2421               }
2422             *sp = 0;
2423             if (!schemaid)
2424               schemaid = repodata_schema2id(data, schema, 1);
2425             else if (schemaid != repodata_schema2id(data, schema, 0))
2426               {
2427                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
2428                 exit(1);
2429               }
2430 #if 0
2431             fprintf(stderr, "  schema %d\n", schemaid);
2432 #endif
2433           }
2434         if (!num)
2435           break;
2436         data_addid(xd, num);
2437         data_addid(xd, schemaid);
2438         for (ida = data->attriddata + val; *ida; ida++)
2439           {
2440             Id *kp = data->xattrs[-*ida];
2441             if (!kp)
2442               continue;
2443             for (;*kp; kp += 2)
2444               {
2445                 repodata_serialize_key(data, newincore, newvincore,
2446                                        schema, data->keys + *kp, kp[1]);
2447               }
2448           }
2449         break;
2450       }
2451     case REPOKEY_TYPE_FLEXARRAY:
2452       {
2453         int num = 0;
2454         for (ida = data->attriddata + val; *ida; ida++)
2455           num++;
2456         data_addid(xd, num);
2457         for (ida = data->attriddata + val; *ida; ida++)
2458           {
2459             Id *kp = data->xattrs[-*ida];
2460             if (!kp)
2461               {
2462                 data_addid(xd, 0);      /* XXX */
2463                 continue;
2464               }
2465             sp = schema;
2466             for (;*kp; kp += 2)
2467               *sp++ = *kp;
2468             *sp = 0;
2469             schemaid = repodata_schema2id(data, schema, 1);
2470             data_addid(xd, schemaid);
2471             kp = data->xattrs[-*ida];
2472             for (;*kp; kp += 2)
2473               {
2474                 repodata_serialize_key(data, newincore, newvincore,
2475                                        schema, data->keys + *kp, kp[1]);
2476               }
2477           }
2478         break;
2479       }
2480     default:
2481       fprintf(stderr, "don't know how to handle type %d\n", key->type);
2482       exit(1);
2483     }
2484   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2485     {
2486       /* put offset/len in incore */
2487       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2488       oldvincorelen = xd->len - oldvincorelen;
2489       data_addid(newincore, oldvincorelen);
2490     }
2491 }
2492
2493 void
2494 repodata_internalize(Repodata *data)
2495 {
2496   Repokey *key, solvkey;
2497   Id entry, nentry;
2498   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2499   unsigned char *dp, *ndp;
2500   int newschema, oldcount;
2501   struct extdata newincore;
2502   struct extdata newvincore;
2503   Id solvkeyid;
2504
2505   if (!data->attrs && !data->xattrs)
2506     return;
2507
2508   newvincore.buf = data->vincore;
2509   newvincore.len = data->vincorelen;
2510
2511   /* find the solvables key, create if needed */
2512   memset(&solvkey, 0, sizeof(solvkey));
2513   solvkey.name = REPOSITORY_SOLVABLES;
2514   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2515   solvkey.size = 0;
2516   solvkey.storage = KEY_STORAGE_INCORE;
2517   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2518
2519   schema = sat_malloc2(data->nkeys, sizeof(Id));
2520   seen = sat_malloc2(data->nkeys, sizeof(Id));
2521
2522   /* Merge the data already existing (in data->schemata, ->incoredata and
2523      friends) with the new attributes in data->attrs[].  */
2524   nentry = data->end - data->start;
2525   memset(&newincore, 0, sizeof(newincore));
2526   data_addid(&newincore, 0);    /* start data at offset 1 */
2527
2528   data->mainschema = 0;
2529   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2530
2531   /* join entry data */
2532   /* we start with the meta data, entry -1 */
2533   for (entry = -1; entry < nentry; entry++)
2534     {
2535       memset(seen, 0, data->nkeys * sizeof(Id));
2536       oldschema = 0;
2537       dp = data->incoredata;
2538       if (dp)
2539         {
2540           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2541           dp = data_read_id(dp, &oldschema);
2542         }
2543 #if 0
2544 fprintf(stderr, "oldschema %d\n", oldschema);
2545 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2546 fprintf(stderr, "schemadata %p\n", data->schemadata);
2547 #endif
2548       /* seen: -1: old data  0: skipped  >0: id + 1 */
2549       newschema = 0;
2550       oldcount = 0;
2551       sp = schema;
2552       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2553         {
2554           if (seen[*keyp])
2555             {
2556               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2557               exit(1);
2558             }
2559           seen[*keyp] = -1;
2560           *sp++ = *keyp;
2561           oldcount++;
2562         }
2563       if (entry >= 0)
2564         keyp = data->attrs ? data->attrs[entry] : 0;
2565       else
2566         {
2567           /* strip solvables key */
2568           *sp = 0;
2569           for (sp = keyp = schema; *sp; sp++)
2570             if (*sp != solvkeyid)
2571               *keyp++ = *sp;
2572             else
2573               oldcount--;
2574           sp = keyp;
2575           seen[solvkeyid] = 0;
2576           keyp = data->xattrs ? data->xattrs[1] : 0;
2577         }
2578       if (keyp)
2579         for (; *keyp; keyp += 2)
2580           {
2581             if (!seen[*keyp])
2582               {
2583                 newschema = 1;
2584                 *sp++ = *keyp;
2585               }
2586             seen[*keyp] = keyp[1] + 1;
2587           }
2588       if (entry < 0 && data->end != data->start)
2589         {
2590           *sp++ = solvkeyid;
2591           newschema = 1;
2592         }
2593       *sp = 0;
2594       if (newschema)
2595         /* Ideally we'd like to sort the new schema here, to ensure
2596            schema equality independend of the ordering.  We can't do that
2597            yet.  For once see below (old ids need to come before new ids).
2598            An additional difficulty is that we also need to move
2599            the values with the keys.  */
2600         schemaid = repodata_schema2id(data, schema, 1);
2601       else
2602         schemaid = oldschema;
2603
2604
2605       /* Now create data blob.  We walk through the (possibly new) schema
2606          and either copy over old data, or insert the new.  */
2607       /* XXX Here we rely on the fact that the (new) schema has the form
2608          o1 o2 o3 o4 ... | n1 n2 n3 ...
2609          (oX being the old keyids (possibly overwritten), and nX being
2610           the new keyids).  This rules out sorting the keyids in order
2611          to ensure a small schema count.  */
2612       if (entry >= 0)
2613         data->incoreoffset[entry] = newincore.len;
2614       data_addid(&newincore, schemaid);
2615       if (entry == -1)
2616         {
2617           data->mainschema = schemaid;
2618           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2619         }
2620       keypstart = data->schemadata + data->schemata[schemaid];
2621       for (keyp = keypstart; *keyp; keyp++)
2622         {
2623           if (entry == -1)
2624             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2625           if (*keyp == solvkeyid)
2626             {
2627               /* add flexarray entry count */
2628               data_addid(&newincore, data->end - data->start);
2629               break;
2630             }
2631           key = data->keys + *keyp;
2632 #if 0
2633           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2634 #endif
2635           ndp = dp;
2636           if (oldcount)
2637             {
2638               /* Skip the data associated with this old key.  */
2639               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2640                 {
2641                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2642                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2643                 }
2644               else if (key->storage == KEY_STORAGE_INCORE)
2645                 ndp = data_skip_key(data, dp, key);
2646               oldcount--;
2647             }
2648           if (seen[*keyp] == -1)
2649             {
2650               /* If this key was an old one _and_ was not overwritten with
2651                  a different value copy over the old value (we skipped it
2652                  above).  */
2653               if (dp != ndp)
2654                 data_addblob(&newincore, dp, ndp - dp);
2655               seen[*keyp] = 0;
2656             }
2657           else if (seen[*keyp])
2658             {
2659               /* Otherwise we have a new value.  Parse it into the internal
2660                  form.  */
2661               repodata_serialize_key(data, &newincore, &newvincore,
2662                                      schema, key, seen[*keyp] - 1);
2663             }
2664           dp = ndp;
2665         }
2666       if (entry >= 0 && data->attrs && data->attrs[entry])
2667         data->attrs[entry] = sat_free(data->attrs[entry]);
2668     }
2669   /* free all xattrs */
2670   for (entry = 0; entry < data->nxattrs; entry++)
2671     if (data->xattrs[entry])
2672       sat_free(data->xattrs[entry]);
2673   data->xattrs = sat_free(data->xattrs);
2674   data->nxattrs = 0;
2675
2676   data->lasthandle = 0;
2677   data->lastkey = 0;
2678   data->lastdatalen = 0;
2679   sat_free(schema);
2680   sat_free(seen);
2681   repodata_free_schemahash(data);
2682
2683   sat_free(data->incoredata);
2684   data->incoredata = newincore.buf;
2685   data->incoredatalen = newincore.len;
2686   data->incoredatafree = 0;
2687   
2688   sat_free(data->vincore);
2689   data->vincore = newvincore.buf;
2690   data->vincorelen = newvincore.len;
2691
2692   data->attrs = sat_free(data->attrs);
2693   data->attrdata = sat_free(data->attrdata);
2694   data->attriddata = sat_free(data->attriddata);
2695   data->attrdatalen = 0;
2696   data->attriddatalen = 0;
2697 }
2698
2699 void
2700 repodata_disable_paging(Repodata *data)
2701 {
2702   if (maybe_load_repodata(data, 0))
2703     repopagestore_disable_paging(&data->store);
2704 }
2705
2706 /*
2707 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2708 */