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