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