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