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