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