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