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