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