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