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