add dataiterator_clonepos and dataiterator_seek
[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->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->nparents < 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->nparents < 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->nparents < 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];
1222           goto di_enterschema;
1223
1224         case di_leavesub: di_leavesub:
1225           di->nparents--;
1226           di->dp = di->parents[di->nparents].dp;
1227           di->kv = di->parents[di->nparents].kv;
1228           di->keyp = di->parents[di->nparents].keyp;
1229           di->key = di->data->keys + *di->keyp;
1230           di->ddp = (unsigned char *)di->kv.str;
1231           di->keyname = di->keynames[di->nparents];
1232           if (!di->ddp)
1233             goto di_bye;
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   memcpy(di->parents, from->parents, sizeof(from->parents));
1343   if (di->nparents)
1344     {
1345       int i;
1346       for (i = 1; i < di->nparents; i++)
1347         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1348       di->kv.parent = &di->parents[di->nparents - 1].kv;
1349     }
1350 }
1351
1352 void
1353 dataiterator_seek(Dataiterator *di, int whence)
1354 {
1355   const char *lastparentstr = 0;
1356   int i;
1357
1358   if ((whence & DI_SEEK_STAY) != 0)
1359     {
1360       for (i = 0; i < di->nparents; i++)
1361         di->parents[i].kv.str = 0;
1362       lastparentstr = di->nparents ? di->parents[di->nparents - 1].kv.str : 0;
1363     }
1364   switch (whence & ~DI_SEEK_STAY)
1365     {
1366     case DI_SEEK_CHILD:
1367       if (di->state != di_nextarrayelement)
1368         break;
1369       if ((whence & DI_SEEK_STAY) != 0)
1370         di->kv.str = 0;
1371       di->state = di_entersub;
1372       break;
1373     case DI_SEEK_PARENT:
1374       if (!di->nparents)
1375         break;
1376       if ((whence & DI_SEEK_STAY) != 0)
1377         di->parents[di->nparents - 1].kv.str = lastparentstr;
1378       di->nparents--;
1379       di->dp = di->parents[di->nparents].dp;
1380       di->kv = di->parents[di->nparents].kv;
1381       di->keyp = di->parents[di->nparents].keyp;
1382       di->key = di->data->keys + *di->keyp;
1383       di->ddp = (unsigned char *)di->kv.str;
1384       di->keyname = di->keynames[di->nparents];
1385       di->state = di->ddp ? di_nextarrayelement : di_bye;
1386       break;
1387     case DI_SEEK_REWIND:
1388       if (!di->nparents)
1389         break;
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   if (solvid == SOLVID_POS)
1425     {
1426       di->repo = di->pool->pos.repo;
1427       if (!di->repo)
1428         {
1429           di->state = di_bye;
1430           return;
1431         }
1432       di->repoid = -1;
1433       di->data = di->repo->repodata + di->pool->pos.repodataid;
1434       di->repodataid = -1;
1435       di->solvid = solvid;
1436       di->state = di_enterrepo;
1437       di->flags |= SEARCH_THISSOLVID;
1438       return;
1439     }
1440   if (solvid > 0)
1441     {
1442       di->repo = di->pool->solvables[solvid].repo;
1443       di->repoid = -1;
1444     }
1445   else if (di->repoid >= 0)
1446     {
1447       if (!di->pool->nrepos)
1448         {
1449           di->state = di_bye;
1450           return;
1451         }
1452       di->repo = di->pool->repos[0];
1453       di->repoid = 0;
1454     }
1455   di->repodataid = 0;
1456   di->solvid = solvid;
1457   if (solvid)
1458     di->flags |= SEARCH_THISSOLVID;
1459   di->state = di_enterrepo;
1460 }
1461
1462 void
1463 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1464 {
1465   di->nparents = 0;
1466   di->repo = repo;
1467   di->repoid = -1;
1468   di->repodataid = 0;
1469   di->solvid = 0;
1470   di->flags &= ~SEARCH_THISSOLVID;
1471   di->state = di_enterrepo;
1472 }
1473
1474 int
1475 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1476 {
1477   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1478     return 0;
1479   if (!ma)
1480     return 1;
1481   return datamatcher_match(ma, di->kv.str);
1482 }
1483
1484 /************************************************************************
1485  * data modify functions
1486  */
1487
1488 /* extend repodata so that it includes solvables p */
1489 void
1490 repodata_extend(Repodata *data, Id p)
1491 {
1492   if (data->start == data->end)
1493     data->start = data->end = p;
1494   if (p >= data->end)
1495     {
1496       int old = data->end - data->start;
1497       int new = p - data->end + 1;
1498       if (data->attrs)
1499         {
1500           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1501           memset(data->attrs + old, 0, new * sizeof(Id *));
1502         }
1503       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1504       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1505       data->end = p + 1;
1506     }
1507   if (p < data->start)
1508     {
1509       int old = data->end - data->start;
1510       int new = data->start - p;
1511       if (data->attrs)
1512         {
1513           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1514           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1515           memset(data->attrs, 0, new * sizeof(Id *));
1516         }
1517       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1518       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1519       memset(data->incoreoffset, 0, new * sizeof(Id));
1520       data->start = p;
1521     }
1522 }
1523
1524 /* extend repodata so that it includes solvables from start to start + num - 1 */
1525 void
1526 repodata_extend_block(Repodata *data, Id start, Id num)
1527 {
1528   if (!num)
1529     return;
1530   if (!data->incoreoffset)
1531     {
1532       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1533       data->start = start;
1534       data->end = start + num;
1535       return;
1536     }
1537   repodata_extend(data, start);
1538   if (num > 1)
1539     repodata_extend(data, start + num - 1);
1540 }
1541
1542 /**********************************************************************/
1543
1544 #define REPODATA_ATTRS_BLOCK 63
1545 #define REPODATA_ATTRDATA_BLOCK 1023
1546 #define REPODATA_ATTRIDDATA_BLOCK 63
1547
1548
1549 Id
1550 repodata_new_handle(Repodata *data)
1551 {
1552   if (!data->nxattrs)
1553     {
1554       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1555       data->nxattrs = 2;
1556     }
1557   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1558   data->xattrs[data->nxattrs] = 0;
1559   return -(data->nxattrs++);
1560 }
1561
1562 static inline Id **
1563 repodata_get_attrp(Repodata *data, Id handle)
1564 {
1565   if (handle == SOLVID_META)
1566     {
1567       if (!data->xattrs)
1568         {
1569           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1570           data->nxattrs = 2;
1571         }
1572     }
1573   if (handle < 0)
1574     return data->xattrs - handle;
1575   if (handle < data->start || handle >= data->end)
1576     repodata_extend(data, handle);
1577   if (!data->attrs)
1578     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1579   return data->attrs + (handle - data->start);
1580 }
1581
1582 static void
1583 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1584 {
1585   Id *pp;
1586   Id *ap, **app;
1587   int i;
1588
1589   app = repodata_get_attrp(data, handle);
1590   ap = *app;
1591   i = 0;
1592   if (ap)
1593     {
1594       for (pp = ap; *pp; pp += 2)
1595         /* Determine equality based on the name only, allows us to change
1596            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1597         if (data->keys[*pp].name == data->keys[keyid].name)
1598           break;
1599       if (*pp)
1600         {
1601           if (overwrite)
1602             {
1603               pp[0] = keyid;
1604               pp[1] = val;
1605             }
1606           return;
1607         }
1608       i = pp - ap;
1609     }
1610   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1611   *app = ap;
1612   pp = ap + i;
1613   *pp++ = keyid;
1614   *pp++ = val;
1615   *pp = 0;
1616 }
1617
1618
1619 void
1620 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1621 {
1622   Id keyid;
1623
1624   keyid = repodata_key2id(data, key, 1);
1625   repodata_insert_keyid(data, solvid, keyid, val, 1);
1626 }
1627
1628 void
1629 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1630 {
1631   Repokey key;
1632   key.name = keyname;
1633   key.type = REPOKEY_TYPE_ID;
1634   key.size = 0;
1635   key.storage = KEY_STORAGE_INCORE;
1636   repodata_set(data, solvid, &key, id);
1637 }
1638
1639 void
1640 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1641 {
1642   Repokey key;
1643   key.name = keyname;
1644   key.type = REPOKEY_TYPE_NUM;
1645   key.size = 0;
1646   key.storage = KEY_STORAGE_INCORE;
1647   repodata_set(data, solvid, &key, (Id)num);
1648 }
1649
1650 void
1651 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1652 {
1653   Repokey key;
1654   Id id;
1655   if (data->localpool)
1656     id = stringpool_str2id(&data->spool, str, 1);
1657   else
1658     id = str2id(data->repo->pool, str, 1);
1659   key.name = keyname;
1660   key.type = REPOKEY_TYPE_ID;
1661   key.size = 0;
1662   key.storage = KEY_STORAGE_INCORE;
1663   repodata_set(data, solvid, &key, id);
1664 }
1665
1666 void
1667 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1668 {
1669   Repokey key;
1670   key.name = keyname;
1671   key.type = REPOKEY_TYPE_CONSTANT;
1672   key.size = constant;
1673   key.storage = KEY_STORAGE_INCORE;
1674   repodata_set(data, solvid, &key, 0);
1675 }
1676
1677 void
1678 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1679 {
1680   Repokey key;
1681   key.name = keyname;
1682   key.type = REPOKEY_TYPE_CONSTANTID;
1683   key.size = id;
1684   key.storage = KEY_STORAGE_INCORE;
1685   repodata_set(data, solvid, &key, 0);
1686 }
1687
1688 void
1689 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1690 {
1691   Repokey key;
1692   key.name = keyname;
1693   key.type = REPOKEY_TYPE_VOID;
1694   key.size = 0;
1695   key.storage = KEY_STORAGE_INCORE;
1696   repodata_set(data, solvid, &key, 0);
1697 }
1698
1699 void
1700 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1701 {
1702   Repokey key;
1703   int l;
1704
1705   l = strlen(str) + 1;
1706   key.name = keyname;
1707   key.type = REPOKEY_TYPE_STR;
1708   key.size = 0;
1709   key.storage = KEY_STORAGE_INCORE;
1710   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1711   memcpy(data->attrdata + data->attrdatalen, str, l);
1712   repodata_set(data, solvid, &key, data->attrdatalen);
1713   data->attrdatalen += l;
1714 }
1715
1716 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
1717  * so that the caller can append the new element there */
1718 static void
1719 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1720 {
1721   int oldsize;
1722   Id *ida, *pp, **ppp;
1723
1724   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1725     {
1726       /* great! just append the new data */
1727       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1728       data->attriddatalen--;    /* overwrite terminating 0  */
1729       data->lastdatalen += entrysize;
1730       return;
1731     }
1732   ppp = repodata_get_attrp(data, handle);
1733   pp = *ppp;
1734   if (pp)
1735     for (; *pp; pp += 2)
1736       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1737         break;
1738   if (!pp || !*pp)
1739     {
1740       /* not found. allocate new key */
1741       Repokey key;
1742       key.name = keyname;
1743       key.type = keytype;
1744       key.size = 0;
1745       key.storage = KEY_STORAGE_INCORE;
1746       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1747       repodata_set(data, handle, &key, data->attriddatalen);
1748       data->lasthandle = 0;     /* next time... */
1749       return;
1750     }
1751   oldsize = 0;
1752   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1753     oldsize += entrysize;
1754   if (ida + 1 == data->attriddata + data->attriddatalen)
1755     {
1756       /* this was the last entry, just append it */
1757       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1758       data->attriddatalen--;    /* overwrite terminating 0  */
1759     }
1760   else
1761     {
1762       /* too bad. move to back. */
1763       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1764       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1765       pp[1] = data->attriddatalen;
1766       data->attriddatalen += oldsize;
1767     }
1768   data->lasthandle = handle;
1769   data->lastkey = *pp;
1770   data->lastdatalen = data->attriddatalen + entrysize + 1;
1771 }
1772
1773 static inline int
1774 checksumtype2len(Id type)
1775 {
1776   switch (type)
1777     {
1778     case REPOKEY_TYPE_MD5:
1779       return SIZEOF_MD5;
1780     case REPOKEY_TYPE_SHA1:
1781       return SIZEOF_SHA1;
1782     case REPOKEY_TYPE_SHA256:
1783       return SIZEOF_SHA256;
1784     default:
1785       return 0;
1786     }
1787 }
1788
1789 void
1790 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1791                       const unsigned char *str)
1792 {
1793   Repokey key;
1794   int l = checksumtype2len(type);
1795
1796   if (!l)
1797     return;
1798   key.name = keyname;
1799   key.type = type;
1800   key.size = 0;
1801   key.storage = KEY_STORAGE_INCORE;
1802   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1803   memcpy(data->attrdata + data->attrdatalen, str, l);
1804   repodata_set(data, solvid, &key, data->attrdatalen);
1805   data->attrdatalen += l;
1806 }
1807
1808 static int
1809 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1810 {
1811   int i;
1812   for (i = 0; i < buflen; i++)
1813     {
1814 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
1815                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
1816                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
1817                 : -1)
1818       int v = c2h(*str);
1819       str++;
1820       if (v < 0)
1821         return 0;
1822       buf[i] = v;
1823       v = c2h(*str);
1824       str++;
1825       if (v < 0)
1826         return 0;
1827       buf[i] = (buf[i] << 4) | v;
1828 #undef c2h
1829     }
1830   return buflen;
1831 }
1832
1833 void
1834 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1835                       const char *str)
1836 {
1837   unsigned char buf[64];
1838   int l = checksumtype2len(type);
1839
1840   if (!l)
1841     return;
1842   if (hexstr2bytes(buf, str, l) != l)
1843     return;
1844   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
1845 }
1846
1847 const char *
1848 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1849 {
1850   int i, l;
1851   char *str, *s;
1852
1853   l = checksumtype2len(type);
1854   if (!l)
1855     return "";
1856   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1857   for (i = 0; i < l; i++)
1858     {
1859       unsigned char v = buf[i];
1860       unsigned char w = v >> 4;
1861       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1862       w = v & 15;
1863       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1864     }
1865   *s = 0;
1866   return str;
1867 }
1868
1869 /* rpm filenames don't contain the epoch, so strip it */
1870 static inline const char *
1871 evrid2vrstr(Pool *pool, Id evrid)
1872 {
1873   const char *p, *evr = id2str(pool, evrid);
1874   if (!evr)
1875     return evr;
1876   for (p = evr; *p >= '0' && *p <= '9'; p++)
1877     ;
1878   return p != evr && *p == ':' ? p + 1 : evr;
1879 }
1880
1881 void
1882 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
1883 {
1884   Pool *pool = data->repo->pool;
1885   Solvable *s;
1886   const char *str, *fp;
1887   int l = 0;
1888
1889   if (medianr)
1890     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
1891   if (!dir)
1892     {
1893       if ((dir = strrchr(file, '/')) != 0)
1894         {
1895           l = dir - file;
1896           dir = file;
1897           file = dir + l + 1;
1898           if (!l)
1899             l++;
1900         }
1901     }
1902   else
1903     l = strlen(dir);
1904   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
1905     {
1906       dir += 2;
1907       l -= 2;
1908     }
1909   if (l == 1 && dir[0] == '.')
1910     l = 0;
1911   s = pool->solvables + solvid;
1912   if (dir && l)
1913     {
1914       str = id2str(pool, s->arch);
1915       if (!strncmp(dir, str, l) && !str[l])
1916         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
1917       else if (!dir[l])
1918         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
1919       else
1920         {
1921           char *dir2 = strdup(dir);
1922           dir2[l] = 0;
1923           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
1924           free(dir2);
1925         }
1926     }
1927   fp = file;
1928   str = id2str(pool, s->name);
1929   l = strlen(str);
1930   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
1931     {
1932       fp += l + 1;
1933       str = evrid2vrstr(pool, s->evr);
1934       l = strlen(str);
1935       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
1936         {
1937           fp += l + 1;
1938           str = id2str(pool, s->arch);
1939           l = strlen(str);
1940           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
1941             {
1942               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
1943               return;
1944             }
1945         }
1946     }
1947   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
1948 }
1949
1950 void
1951 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
1952 {
1953   assert(dir);
1954 #if 0
1955 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
1956 #endif
1957   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1958   data->attriddata[data->attriddatalen++] = dir;
1959   data->attriddata[data->attriddatalen++] = num;
1960   data->attriddata[data->attriddatalen++] = num2;
1961   data->attriddata[data->attriddatalen++] = 0;
1962 }
1963
1964 void
1965 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
1966 {
1967   Id stroff;
1968   int l;
1969
1970   assert(dir);
1971   l = strlen(str) + 1;
1972   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1973   memcpy(data->attrdata + data->attrdatalen, str, l);
1974   stroff = data->attrdatalen;
1975   data->attrdatalen += l;
1976
1977 #if 0
1978 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
1979 #endif
1980   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1981   data->attriddata[data->attriddatalen++] = dir;
1982   data->attriddata[data->attriddatalen++] = stroff;
1983   data->attriddata[data->attriddatalen++] = 0;
1984 }
1985
1986 void
1987 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
1988 {
1989 #if 0
1990 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
1991 #endif
1992   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
1993   data->attriddata[data->attriddatalen++] = id;
1994   data->attriddata[data->attriddatalen++] = 0;
1995 }
1996
1997 void
1998 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
1999                            const char *str)
2000 {
2001   Id id;
2002   if (data->localpool)
2003     id = stringpool_str2id(&data->spool, str, 1);
2004   else
2005     id = str2id(data->repo->pool, str, 1);
2006   repodata_add_idarray(data, solvid, keyname, id);
2007 }
2008
2009 void
2010 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2011 {
2012   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2013   data->attriddata[data->attriddatalen++] = ghandle;
2014   data->attriddata[data->attriddatalen++] = 0;
2015 }
2016
2017 void
2018 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2019 {
2020   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2021   data->attriddata[data->attriddatalen++] = ghandle;
2022   data->attriddata[data->attriddatalen++] = 0;
2023 }
2024
2025 /* add all attrs from src to dest */
2026 void
2027 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2028 {
2029   Id *keyp;
2030   if (dest == src || !(keyp = data->attrs[src - data->start]))
2031     return;
2032   for (; *keyp; keyp += 2)
2033     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2034 }
2035
2036
2037
2038
2039 /**********************************************************************/
2040
2041 /* unify with repo_write! */
2042
2043 #define EXTDATA_BLOCK 1023
2044
2045 struct extdata {
2046   unsigned char *buf;
2047   int len;
2048 };
2049
2050 static void
2051 data_addid(struct extdata *xd, Id x)
2052 {
2053   unsigned char *dp;
2054   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2055   dp = xd->buf + xd->len;
2056
2057   if (x >= (1 << 14))
2058     {
2059       if (x >= (1 << 28))
2060         *dp++ = (x >> 28) | 128;
2061       if (x >= (1 << 21))
2062         *dp++ = (x >> 21) | 128;
2063       *dp++ = (x >> 14) | 128;
2064     }
2065   if (x >= (1 << 7))
2066     *dp++ = (x >> 7) | 128;
2067   *dp++ = x & 127;
2068   xd->len = dp - xd->buf;
2069 }
2070
2071 static void
2072 data_addideof(struct extdata *xd, Id x, int eof)
2073 {
2074   if (x >= 64)
2075     x = (x & 63) | ((x & ~63) << 1);
2076   data_addid(xd, (eof ? x: x | 64));
2077 }
2078
2079 static void
2080 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2081 {
2082   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2083   memcpy(xd->buf + xd->len, blob, len);
2084   xd->len += len;
2085 }
2086
2087 /*********************************/
2088
2089 static void
2090 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2091                        struct extdata *newvincore,
2092                        Id *schema,
2093                        Repokey *key, Id val)
2094 {
2095   /* Otherwise we have a new value.  Parse it into the internal
2096      form.  */
2097   Id *ida;
2098   struct extdata *xd;
2099   unsigned int oldvincorelen = 0;
2100   Id schemaid, *sp;
2101
2102   xd = newincore;
2103   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2104     {
2105       xd = newvincore;
2106       oldvincorelen = xd->len;
2107     }
2108   switch (key->type)
2109     {
2110     case REPOKEY_TYPE_VOID:
2111     case REPOKEY_TYPE_CONSTANT:
2112     case REPOKEY_TYPE_CONSTANTID:
2113       break;
2114     case REPOKEY_TYPE_STR:
2115       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2116       break;
2117     case REPOKEY_TYPE_MD5:
2118       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2119       break;
2120     case REPOKEY_TYPE_SHA1:
2121       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2122       break;
2123     case REPOKEY_TYPE_SHA256:
2124       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2125       break;
2126     case REPOKEY_TYPE_ID:
2127     case REPOKEY_TYPE_NUM:
2128     case REPOKEY_TYPE_DIR:
2129       data_addid(xd, val);
2130       break;
2131     case REPOKEY_TYPE_IDARRAY:
2132       for (ida = data->attriddata + val; *ida; ida++)
2133         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2134       break;
2135     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2136       for (ida = data->attriddata + val; *ida; ida += 3)
2137         {
2138           data_addid(xd, ida[0]);
2139           data_addid(xd, ida[1]);
2140           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2141         }
2142       break;
2143     case REPOKEY_TYPE_DIRSTRARRAY:
2144       for (ida = data->attriddata + val; *ida; ida += 2)
2145         {
2146           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2147           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2148         }
2149       break;
2150     case REPOKEY_TYPE_FIXARRAY:
2151       {
2152         int num = 0;
2153         schemaid = 0;
2154         for (ida = data->attriddata + val; *ida; ida++)
2155           {
2156 #if 0
2157             fprintf(stderr, "serialize struct %d\n", *ida);
2158 #endif
2159             sp = schema;
2160             Id *kp = data->xattrs[-*ida];
2161             if (!kp)
2162               continue;
2163             num++;
2164             for (;*kp; kp += 2)
2165               {
2166 #if 0
2167                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2168 #endif
2169                 *sp++ = *kp;
2170               }
2171             *sp = 0;
2172             if (!schemaid)
2173               schemaid = repodata_schema2id(data, schema, 1);
2174             else if (schemaid != repodata_schema2id(data, schema, 0))
2175               {
2176                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
2177                 exit(1);
2178               }
2179 #if 0
2180             fprintf(stderr, "  schema %d\n", schemaid);
2181 #endif
2182           }
2183         if (!num)
2184           break;
2185         data_addid(xd, num);
2186         data_addid(xd, schemaid);
2187         for (ida = data->attriddata + val; *ida; ida++)
2188           {
2189             Id *kp = data->xattrs[-*ida];
2190             if (!kp)
2191               continue;
2192             for (;*kp; kp += 2)
2193               {
2194                 repodata_serialize_key(data, newincore, newvincore,
2195                                        schema, data->keys + *kp, kp[1]);
2196               }
2197           }
2198         break;
2199       }
2200     case REPOKEY_TYPE_FLEXARRAY:
2201       {
2202         int num = 0;
2203         for (ida = data->attriddata + val; *ida; ida++)
2204           num++;
2205         data_addid(xd, num);
2206         for (ida = data->attriddata + val; *ida; ida++)
2207           {
2208             Id *kp = data->xattrs[-*ida];
2209             if (!kp)
2210               {
2211                 data_addid(xd, 0);      /* XXX */
2212                 continue;
2213               }
2214             sp = schema;
2215             for (;*kp; kp += 2)
2216               *sp++ = *kp;
2217             *sp = 0;
2218             schemaid = repodata_schema2id(data, schema, 1);
2219             data_addid(xd, schemaid);
2220             kp = data->xattrs[-*ida];
2221             for (;*kp; kp += 2)
2222               {
2223                 repodata_serialize_key(data, newincore, newvincore,
2224                                        schema, data->keys + *kp, kp[1]);
2225               }
2226           }
2227         break;
2228       }
2229     default:
2230       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2231       exit(1);
2232     }
2233   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2234     {
2235       /* put offset/len in incore */
2236       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2237       oldvincorelen = xd->len - oldvincorelen;
2238       data_addid(newincore, oldvincorelen);
2239     }
2240 }
2241
2242 void
2243 repodata_internalize(Repodata *data)
2244 {
2245   Repokey *key, solvkey;
2246   Id entry, nentry;
2247   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2248   unsigned char *dp, *ndp;
2249   int newschema, oldcount;
2250   struct extdata newincore;
2251   struct extdata newvincore;
2252   Id solvkeyid;
2253
2254   if (!data->attrs && !data->xattrs)
2255     return;
2256
2257   newvincore.buf = data->vincore;
2258   newvincore.len = data->vincorelen;
2259
2260   /* find the solvables key, create if needed */
2261   memset(&solvkey, 0, sizeof(solvkey));
2262   solvkey.name = REPOSITORY_SOLVABLES;
2263   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2264   solvkey.size = 0;
2265   solvkey.storage = KEY_STORAGE_INCORE;
2266   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2267
2268   schema = sat_malloc2(data->nkeys, sizeof(Id));
2269   seen = sat_malloc2(data->nkeys, sizeof(Id));
2270
2271   /* Merge the data already existing (in data->schemata, ->incoredata and
2272      friends) with the new attributes in data->attrs[].  */
2273   nentry = data->end - data->start;
2274   memset(&newincore, 0, sizeof(newincore));
2275   data_addid(&newincore, 0);    /* start data at offset 1 */
2276
2277   data->mainschema = 0;
2278   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2279
2280   /* join entry data */
2281   /* we start with the meta data, entry -1 */
2282   for (entry = -1; entry < nentry; entry++)
2283     {
2284       memset(seen, 0, data->nkeys * sizeof(Id));
2285       oldschema = 0;
2286       dp = data->incoredata;
2287       if (dp)
2288         {
2289           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2290           dp = data_read_id(dp, &oldschema);
2291         }
2292 #if 0
2293 fprintf(stderr, "oldschema %d\n", oldschema);
2294 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2295 fprintf(stderr, "schemadata %p\n", data->schemadata);
2296 #endif
2297       /* seen: -1: old data  0: skipped  >0: id + 1 */
2298       newschema = 0;
2299       oldcount = 0;
2300       sp = schema;
2301       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2302         {
2303           if (seen[*keyp])
2304             {
2305               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2306               exit(1);
2307             }
2308           seen[*keyp] = -1;
2309           *sp++ = *keyp;
2310           oldcount++;
2311         }
2312       if (entry >= 0)
2313         keyp = data->attrs ? data->attrs[entry] : 0;
2314       else
2315         {
2316           /* strip solvables key */
2317           *sp = 0;
2318           for (sp = keyp = schema; *sp; sp++)
2319             if (*sp != solvkeyid)
2320               *keyp++ = *sp;
2321             else
2322               oldcount--;
2323           sp = keyp;
2324           seen[solvkeyid] = 0;
2325           keyp = data->xattrs ? data->xattrs[1] : 0;
2326         }
2327       if (keyp)
2328         for (; *keyp; keyp += 2)
2329           {
2330             if (!seen[*keyp])
2331               {
2332                 newschema = 1;
2333                 *sp++ = *keyp;
2334               }
2335             seen[*keyp] = keyp[1] + 1;
2336           }
2337       if (entry < 0 && data->end != data->start)
2338         {
2339           *sp++ = solvkeyid;
2340           newschema = 1;
2341         }
2342       *sp = 0;
2343       if (newschema)
2344         /* Ideally we'd like to sort the new schema here, to ensure
2345            schema equality independend of the ordering.  We can't do that
2346            yet.  For once see below (old ids need to come before new ids).
2347            An additional difficulty is that we also need to move
2348            the values with the keys.  */
2349         schemaid = repodata_schema2id(data, schema, 1);
2350       else
2351         schemaid = oldschema;
2352
2353
2354       /* Now create data blob.  We walk through the (possibly new) schema
2355          and either copy over old data, or insert the new.  */
2356       /* XXX Here we rely on the fact that the (new) schema has the form
2357          o1 o2 o3 o4 ... | n1 n2 n3 ...
2358          (oX being the old keyids (possibly overwritten), and nX being
2359           the new keyids).  This rules out sorting the keyids in order
2360          to ensure a small schema count.  */
2361       if (entry >= 0)
2362         data->incoreoffset[entry] = newincore.len;
2363       data_addid(&newincore, schemaid);
2364       if (entry == -1)
2365         {
2366           data->mainschema = schemaid;
2367           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2368         }
2369       keypstart = data->schemadata + data->schemata[schemaid];
2370       for (keyp = keypstart; *keyp; keyp++)
2371         {
2372           if (entry == -1)
2373             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2374           if (*keyp == solvkeyid)
2375             {
2376               /* add flexarray entry count */
2377               data_addid(&newincore, data->end - data->start);
2378               break;
2379             }
2380           key = data->keys + *keyp;
2381 #if 0
2382           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2383 #endif
2384           ndp = dp;
2385           if (oldcount)
2386             {
2387               /* Skip the data associated with this old key.  */
2388               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2389                 {
2390                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2391                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2392                 }
2393               else if (key->storage == KEY_STORAGE_INCORE)
2394                 ndp = data_skip_key(data, dp, key);
2395               oldcount--;
2396             }
2397           if (seen[*keyp] == -1)
2398             {
2399               /* If this key was an old one _and_ was not overwritten with
2400                  a different value copy over the old value (we skipped it
2401                  above).  */
2402               if (dp != ndp)
2403                 data_addblob(&newincore, dp, ndp - dp);
2404               seen[*keyp] = 0;
2405             }
2406           else if (seen[*keyp])
2407             {
2408               /* Otherwise we have a new value.  Parse it into the internal
2409                  form.  */
2410               repodata_serialize_key(data, &newincore, &newvincore,
2411                                      schema, key, seen[*keyp] - 1);
2412             }
2413           dp = ndp;
2414         }
2415       if (entry >= 0 && data->attrs && data->attrs[entry])
2416         data->attrs[entry] = sat_free(data->attrs[entry]);
2417     }
2418   /* free all xattrs */
2419   for (entry = 0; entry < data->nxattrs; entry++)
2420     if (data->xattrs[entry])
2421       sat_free(data->xattrs[entry]);
2422   data->xattrs = sat_free(data->xattrs);
2423   data->nxattrs = 0;
2424
2425   data->lasthandle = 0;
2426   data->lastkey = 0;
2427   data->lastdatalen = 0;
2428   sat_free(schema);
2429   sat_free(seen);
2430   repodata_free_schemahash(data);
2431
2432   sat_free(data->incoredata);
2433   data->incoredata = newincore.buf;
2434   data->incoredatalen = newincore.len;
2435   data->incoredatafree = 0;
2436
2437   sat_free(data->vincore);
2438   data->vincore = newvincore.buf;
2439   data->vincorelen = newvincore.len;
2440
2441   data->attrs = sat_free(data->attrs);
2442   data->attrdata = sat_free(data->attrdata);
2443   data->attriddata = sat_free(data->attriddata);
2444   data->attrdatalen = 0;
2445   data->attriddatalen = 0;
2446 }
2447
2448 void
2449 repodata_disable_paging(Repodata *data)
2450 {
2451   if (maybe_load_repodata(data, 0))
2452     repopagestore_disable_paging(&data->store);
2453 }
2454
2455 /*
2456 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2457 */