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