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