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