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