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