speed up filelist matching by pre-matching the basename first
[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   if ((flags & SEARCH_FILES) != 0 && match)
1065     {
1066       /* prepare basename check */
1067       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING)
1068         {
1069           const char *p = strrchr(match, '/');
1070           ma->matchdata = (void *)(p ? p + 1 : match);
1071         }
1072       else if ((flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1073         {
1074           const char *p = strrchr(match, '/');
1075           ma->matchdata = (void *)(p ? p + 1 : 0);
1076         }
1077       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1078         {
1079           const char *p;
1080           for (p = match + strlen(match) - 1; p >= match; p--)
1081             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1082               break;
1083           ma->matchdata = (void *)(p + 1);
1084         }
1085     }
1086   return ma->error;
1087 }
1088
1089 void
1090 datamatcher_free(Datamatcher *ma)
1091 {
1092   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1093     {
1094       regfree(ma->matchdata);
1095       solv_free(ma->matchdata);
1096     }
1097   ma->matchdata = 0;
1098 }
1099
1100 int
1101 datamatcher_match(Datamatcher *ma, const char *str)
1102 {
1103   int l;
1104   switch ((ma->flags & SEARCH_STRINGMASK))
1105     {
1106     case SEARCH_SUBSTRING:
1107       if (ma->flags & SEARCH_NOCASE)
1108         {
1109           if (!strcasestr(str, ma->match))
1110             return 0;
1111         }
1112       else
1113         {
1114           if (!strstr(str, ma->match))
1115             return 0;
1116         }
1117       break;
1118     case SEARCH_STRING:
1119       if (ma->flags & SEARCH_NOCASE)
1120         {
1121           if (strcasecmp(ma->match, str))
1122             return 0;
1123         }
1124       else
1125         {
1126           if (strcmp(ma->match, str))
1127             return 0;
1128         }
1129       break;
1130     case SEARCH_STRINGSTART:
1131       if (ma->flags & SEARCH_NOCASE)
1132         {
1133           if (strncasecmp(ma->match, str, strlen(ma->match)))
1134             return 0;
1135         }
1136       else
1137         {
1138           if (strncmp(ma->match, str, strlen(ma->match)))
1139             return 0;
1140         }
1141       break;
1142     case SEARCH_STRINGEND:
1143       l = strlen(str) - strlen(ma->match);
1144       if (l < 0)
1145         return 0;
1146       if (ma->flags & SEARCH_NOCASE)
1147         {
1148           if (strcasecmp(ma->match, str + l))
1149             return 0;
1150         }
1151       else
1152         {
1153           if (strcmp(ma->match, str + l))
1154             return 0;
1155         }
1156       break;
1157     case SEARCH_GLOB:
1158       if (fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1159         return 0;
1160       break;
1161     case SEARCH_REGEX:
1162       if (regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0))
1163         return 0;
1164       break;
1165     default:
1166       return 0;
1167     }
1168   return 1;
1169 }
1170
1171 /* check if the matcher can match the provides basename */
1172
1173 static int
1174 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1175 {
1176   int l;
1177   const char *match = ma->match;
1178   switch (ma->flags & SEARCH_STRINGMASK)
1179     {
1180     case SEARCH_STRING:
1181       match = ma->matchdata;
1182       break;
1183     case SEARCH_STRINGEND:
1184       if (ma->matchdata)
1185         {
1186           match = ma->matchdata;        /* have slash */
1187           break;
1188         }
1189       l = strlen(basename) - strlen(match);
1190       if (l < 0)
1191         return 0;
1192       basename += l;
1193       break;
1194     case SEARCH_GLOB:
1195       match = ma->matchdata;
1196       if (!match)
1197         return 1;
1198       l = strlen(basename) - strlen(match);
1199       if (l < 0)
1200         return 0;
1201       basename += l;
1202       break;
1203     default:
1204       return 1; /* maybe matches */
1205     }
1206   if (!match)
1207     return 1;
1208   if ((ma->flags & SEARCH_NOCASE) != 0)
1209     return !strcasecmp(match, basename);
1210   else
1211     return !strcmp(match, basename);
1212 }
1213
1214 int
1215 repodata_filelistfilter_matches(Repodata *data, const char *str)
1216 {
1217   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1218   /* for now hardcoded */
1219   if (strstr(str, "bin/"))
1220     return 1;
1221   if (!strncmp(str, "/etc/", 5))
1222     return 1;
1223   if (!strcmp(str, "/usr/lib/sendmail"))
1224     return 1;
1225   return 0;
1226 }
1227
1228
1229 enum {
1230   di_bye,
1231
1232   di_enterrepo,
1233   di_entersolvable,
1234   di_enterrepodata,
1235   di_enterschema,
1236   di_enterkey,
1237
1238   di_nextattr,
1239   di_nextkey,
1240   di_nextrepodata,
1241   di_nextsolvable,
1242   di_nextrepo,
1243
1244   di_enterarray,
1245   di_nextarrayelement,
1246
1247   di_entersub,
1248   di_leavesub,
1249
1250   di_nextsolvablekey,
1251   di_entersolvablekey,
1252   di_nextsolvableattr
1253 };
1254
1255 /* see dataiterator.h for documentation */
1256 int
1257 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1258 {
1259   memset(di, 0, sizeof(*di));
1260   di->pool = pool;
1261   di->flags = flags & ~SEARCH_THISSOLVID;
1262   if (!pool || (repo && repo->pool != pool))
1263     {
1264       di->state = di_bye;
1265       return -1;
1266     }
1267   if (match)
1268     {
1269       int error;
1270       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1271         {
1272           di->state = di_bye;
1273           return error;
1274         }
1275     }
1276   di->keyname = keyname;
1277   di->keynames[0] = keyname;
1278   dataiterator_set_search(di, repo, p);
1279   return 0;
1280 }
1281
1282 void
1283 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1284 {
1285   *di = *from;
1286   memset(&di->matcher, 0, sizeof(di->matcher));
1287   if (from->matcher.match)
1288     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1289   if (di->nparents)
1290     {
1291       /* fix pointers */
1292       int i;
1293       for (i = 1; i < di->nparents; i++)
1294         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1295       di->kv.parent = &di->parents[di->nparents - 1].kv;
1296     }
1297 }
1298
1299 int
1300 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1301 {
1302   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1303   datamatcher_free(&di->matcher);
1304   memset(&di->matcher, 0, sizeof(di->matcher));
1305   if (match)
1306     {
1307       int error;
1308       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1309         {
1310           di->state = di_bye;
1311           return error;
1312         }
1313     }
1314   return 0;
1315 }
1316
1317 void
1318 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1319 {
1320   di->repo = repo;
1321   di->repoid = 0;
1322   di->flags &= ~SEARCH_THISSOLVID;
1323   di->nparents = 0;
1324   di->rootlevel = 0;
1325   di->repodataid = 1;
1326   if (!di->pool->urepos)
1327     {
1328       di->state = di_bye;
1329       return;
1330     }
1331   if (!repo)
1332     {
1333       di->repoid = 1;
1334       di->repo = di->pool->repos[di->repoid];
1335     }
1336   di->state = di_enterrepo;
1337   if (p)
1338     dataiterator_jump_to_solvid(di, p);
1339 }
1340
1341 void
1342 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1343 {
1344   di->nkeynames = 0;
1345   di->keyname = keyname;
1346   di->keynames[0] = keyname;
1347 }
1348
1349 void
1350 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1351 {
1352   int i;
1353
1354   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1355     {
1356       di->state = di_bye;       /* sorry */
1357       return;
1358     }
1359   for (i = di->nkeynames + 1; i > 0; i--)
1360     di->keynames[i] = di->keynames[i - 1];
1361   di->keynames[0] = di->keyname = keyname;
1362   di->nkeynames++;
1363 }
1364
1365 void
1366 dataiterator_free(Dataiterator *di)
1367 {
1368   if (di->matcher.match)
1369     datamatcher_free(&di->matcher);
1370 }
1371
1372 static inline unsigned char *
1373 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1374 {
1375   Id *keyp = di->keyp;
1376   Repokey *keys = di->data->keys;
1377   unsigned char *dp;
1378
1379   for (keyp = di->keyp; *keyp; keyp++)
1380     if (keys[*keyp].name == keyname)
1381       break;
1382   if (!*keyp)
1383     return 0;
1384   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1385   if (!dp)
1386     return 0;
1387   di->keyp = keyp;
1388   return dp;
1389 }
1390
1391 static int
1392 dataiterator_filelistcheck(Dataiterator *di)
1393 {
1394   int j;
1395   int needcomplete = 0;
1396   Repodata *data = di->data;
1397
1398   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1399     if (!di->matcher.match
1400        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1401            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1402        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1403       needcomplete = 1;
1404   if (data->state != REPODATA_AVAILABLE)
1405     return needcomplete ? 1 : 0;
1406   for (j = 1; j < data->nkeys; j++)
1407     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1408       break;
1409   return j == data->nkeys && !needcomplete ? 0 : 1;
1410 }
1411
1412 int
1413 dataiterator_step(Dataiterator *di)
1414 {
1415   Id schema;
1416
1417   for (;;)
1418     {
1419       switch (di->state)
1420         {
1421         case di_enterrepo: di_enterrepo:
1422           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1423             goto di_nextrepo;
1424           if (!(di->flags & SEARCH_THISSOLVID))
1425             {
1426               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1427               goto di_nextsolvable;
1428             }
1429           /* FALLTHROUGH */
1430
1431         case di_entersolvable: di_entersolvable:
1432           if (di->repodataid)
1433             {
1434               di->repodataid = 1;       /* reset repodata iterator */
1435               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)
1436                 {
1437                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1438
1439                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1440                   di->data = 0;
1441                   goto di_entersolvablekey;
1442                 }
1443             }
1444           /* FALLTHROUGH */
1445
1446         case di_enterrepodata: di_enterrepodata:
1447           if (di->repodataid)
1448             {
1449               if (di->repodataid >= di->repo->nrepodata)
1450                 goto di_nextsolvable;
1451               di->data = di->repo->repodata + di->repodataid;
1452             }
1453           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1454             goto di_nextrepodata;
1455           if (!maybe_load_repodata(di->data, di->keyname))
1456             goto di_nextrepodata;
1457           di->dp = solvid2data(di->data, di->solvid, &schema);
1458           if (!di->dp)
1459             goto di_nextrepodata;
1460           if (di->solvid == SOLVID_POS)
1461             di->solvid = di->pool->pos.solvid;
1462           /* reset key iterator */
1463           di->keyp = di->data->schemadata + di->data->schemata[schema];
1464           /* FALLTHROUGH */
1465
1466         case di_enterschema: di_enterschema:
1467           if (di->keyname)
1468             di->dp = dataiterator_find_keyname(di, di->keyname);
1469           if (!di->dp || !*di->keyp)
1470             {
1471               if (di->kv.parent)
1472                 goto di_leavesub;
1473               goto di_nextrepodata;
1474             }
1475           /* FALLTHROUGH */
1476
1477         case di_enterkey: di_enterkey:
1478           di->kv.entry = -1;
1479           di->key = di->data->keys + *di->keyp;
1480           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1481           if (!di->ddp)
1482             goto di_nextkey;
1483           if (di->key->type == REPOKEY_TYPE_DELETED)
1484             goto di_nextkey;
1485           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1486             goto di_enterarray;
1487           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1488             goto di_nextkey;
1489           /* FALLTHROUGH */
1490
1491         case di_nextattr:
1492           di->kv.entry++;
1493           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1494           if (di->kv.eof)
1495             di->state = di_nextkey;
1496           else
1497             di->state = di_nextattr;
1498           break;
1499
1500         case di_nextkey: di_nextkey:
1501           if (!di->keyname && *++di->keyp)
1502             goto di_enterkey;
1503           if (di->kv.parent)
1504             goto di_leavesub;
1505           /* FALLTHROUGH */
1506
1507         case di_nextrepodata: di_nextrepodata:
1508           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1509               goto di_enterrepodata;
1510           /* FALLTHROUGH */
1511
1512         case di_nextsolvable: di_nextsolvable:
1513           if (!(di->flags & SEARCH_THISSOLVID))
1514             {
1515               if (di->solvid < 0)
1516                 di->solvid = di->repo->start;
1517               else
1518                 di->solvid++;
1519               for (; di->solvid < di->repo->end; di->solvid++)
1520                 {
1521                   if (di->pool->solvables[di->solvid].repo == di->repo)
1522                     goto di_entersolvable;
1523                 }
1524             }
1525           /* FALLTHROUGH */
1526
1527         case di_nextrepo: di_nextrepo:
1528           if (di->repoid > 0)
1529             {
1530               di->repoid++;
1531               di->repodataid = 1;
1532               if (di->repoid < di->pool->nrepos)
1533                 {
1534                   di->repo = di->pool->repos[di->repoid];
1535                   goto di_enterrepo;
1536                 }
1537             }
1538         /* FALLTHROUGH */
1539
1540         case di_bye: di_bye:
1541           di->state = di_bye;
1542           return 0;
1543
1544         case di_enterarray: di_enterarray:
1545           if (di->key->name == REPOSITORY_SOLVABLES)
1546             goto di_nextkey;
1547           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1548           di->kv.eof = 0;
1549           di->kv.entry = -1;
1550           /* FALLTHROUGH */
1551
1552         case di_nextarrayelement: di_nextarrayelement:
1553           di->kv.entry++;
1554           if (di->kv.entry)
1555             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1556           if (di->kv.entry == di->kv.num)
1557             {
1558               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1559                 goto di_nextkey;
1560               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1561                 goto di_nextkey;
1562               di->kv.str = (char *)di->ddp;
1563               di->kv.eof = 2;
1564               di->state = di_nextkey;
1565               break;
1566             }
1567           if (di->kv.entry == di->kv.num - 1)
1568             di->kv.eof = 1;
1569           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1570             di->ddp = data_read_id(di->ddp, &di->kv.id);
1571           di->kv.str = (char *)di->ddp;
1572           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1573             goto di_entersub;
1574           if ((di->flags & SEARCH_SUB) != 0)
1575             di->state = di_entersub;
1576           else
1577             di->state = di_nextarrayelement;
1578           break;
1579
1580         case di_entersub: di_entersub:
1581           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1582             goto di_nextarrayelement;   /* sorry, full */
1583           di->parents[di->nparents].kv = di->kv;
1584           di->parents[di->nparents].dp = di->dp;
1585           di->parents[di->nparents].keyp = di->keyp;
1586           di->dp = (unsigned char *)di->kv.str;
1587           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1588           memset(&di->kv, 0, sizeof(di->kv));
1589           di->kv.parent = &di->parents[di->nparents].kv;
1590           di->nparents++;
1591           di->keyname = di->keynames[di->nparents - di->rootlevel];
1592           goto di_enterschema;
1593
1594         case di_leavesub: di_leavesub:
1595           if (di->nparents - 1 < di->rootlevel)
1596             goto di_bye;
1597           di->nparents--;
1598           di->dp = di->parents[di->nparents].dp;
1599           di->kv = di->parents[di->nparents].kv;
1600           di->keyp = di->parents[di->nparents].keyp;
1601           di->key = di->data->keys + *di->keyp;
1602           di->ddp = (unsigned char *)di->kv.str;
1603           di->keyname = di->keynames[di->nparents - di->rootlevel];
1604           goto di_nextarrayelement;
1605
1606         /* special solvable attr handling follows */
1607
1608         case di_nextsolvablekey: di_nextsolvablekey:
1609           if (di->keyname || di->key->name == RPM_RPMDBID)
1610             goto di_enterrepodata;
1611           di->key++;
1612           /* FALLTHROUGH */
1613
1614         case di_entersolvablekey: di_entersolvablekey:
1615           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1616           if (!di->idp || !*di->idp)
1617             goto di_nextsolvablekey;
1618           if (di->kv.eof)
1619             {
1620               /* not an array */
1621               di->kv.id = *di->idp;
1622               di->kv.num = *di->idp;    /* for rpmdbid */
1623               di->kv.num2 = 0;          /* for rpmdbid */
1624               di->kv.entry = 0;
1625               di->state = di_nextsolvablekey;
1626               break;
1627             }
1628           di->kv.entry = -1;
1629           /* FALLTHROUGH */
1630
1631         case di_nextsolvableattr:
1632           di->state = di_nextsolvableattr;
1633           di->kv.id = *di->idp++;
1634           di->kv.entry++;
1635           if (!*di->idp)
1636             {
1637               di->kv.eof = 1;
1638               di->state = di_nextsolvablekey;
1639             }
1640           break;
1641
1642         }
1643
1644       if (di->matcher.match)
1645         {
1646           /* simple pre-check so that we don't need to stringify */
1647           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1648             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1649               continue;
1650           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1651             {
1652               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1653                 return 1;
1654               continue;
1655             }
1656           if (!datamatcher_match(&di->matcher, di->kv.str))
1657             continue;
1658         }
1659       else
1660         {
1661           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1662             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1663         }
1664       /* found something! */
1665       return 1;
1666     }
1667 }
1668
1669 void
1670 dataiterator_entersub(Dataiterator *di)
1671 {
1672   if (di->state == di_nextarrayelement)
1673     di->state = di_entersub;
1674 }
1675
1676 void
1677 dataiterator_setpos(Dataiterator *di)
1678 {
1679   if (di->kv.eof == 2)
1680     {
1681       pool_clear_pos(di->pool);
1682       return;
1683     }
1684   di->pool->pos.solvid = di->solvid;
1685   di->pool->pos.repo = di->repo;
1686   di->pool->pos.repodataid = di->data - di->repo->repodata;
1687   di->pool->pos.schema = di->kv.id;
1688   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1689 }
1690
1691 void
1692 dataiterator_setpos_parent(Dataiterator *di)
1693 {
1694   if (!di->kv.parent || di->kv.parent->eof == 2)
1695     {
1696       pool_clear_pos(di->pool);
1697       return;
1698     }
1699   di->pool->pos.solvid = di->solvid;
1700   di->pool->pos.repo = di->repo;
1701   di->pool->pos.repodataid = di->data - di->repo->repodata;
1702   di->pool->pos.schema = di->kv.parent->id;
1703   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1704 }
1705
1706 /* clones just the position, not the search keys/matcher */
1707 void
1708 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1709 {
1710   di->state = from->state;
1711   di->flags &= ~SEARCH_THISSOLVID;
1712   di->flags |= (from->flags & SEARCH_THISSOLVID);
1713   di->repo = from->repo;
1714   di->data = from->data;
1715   di->dp = from->dp;
1716   di->ddp = from->ddp;
1717   di->idp = from->idp;
1718   di->keyp = from->keyp;
1719   di->key = from->key;
1720   di->kv = from->kv;
1721   di->repodataid = from->repodataid;
1722   di->solvid = from->solvid;
1723   di->repoid = from->repoid;
1724   di->rootlevel = from->rootlevel;
1725   memcpy(di->parents, from->parents, sizeof(from->parents));
1726   di->nparents = from->nparents;
1727   if (di->nparents)
1728     {
1729       int i;
1730       for (i = 1; i < di->nparents; i++)
1731         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1732       di->kv.parent = &di->parents[di->nparents - 1].kv;
1733     }
1734 }
1735
1736 void
1737 dataiterator_seek(Dataiterator *di, int whence)
1738 {
1739   if ((whence & DI_SEEK_STAY) != 0)
1740     di->rootlevel = di->nparents;
1741   switch (whence & ~DI_SEEK_STAY)
1742     {
1743     case DI_SEEK_CHILD:
1744       if (di->state != di_nextarrayelement)
1745         break;
1746       if ((whence & DI_SEEK_STAY) != 0)
1747         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1748       di->state = di_entersub;
1749       break;
1750     case DI_SEEK_PARENT:
1751       if (!di->nparents)
1752         {
1753           di->state = di_bye;
1754           break;
1755         }
1756       di->nparents--;
1757       if (di->rootlevel > di->nparents)
1758         di->rootlevel = di->nparents;
1759       di->dp = di->parents[di->nparents].dp;
1760       di->kv = di->parents[di->nparents].kv;
1761       di->keyp = di->parents[di->nparents].keyp;
1762       di->key = di->data->keys + *di->keyp;
1763       di->ddp = (unsigned char *)di->kv.str;
1764       di->keyname = di->keynames[di->nparents - di->rootlevel];
1765       di->state = di_nextarrayelement;
1766       break;
1767     case DI_SEEK_REWIND:
1768       if (!di->nparents)
1769         {
1770           di->state = di_bye;
1771           break;
1772         }
1773       di->dp = (unsigned char *)di->kv.parent->str;
1774       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1775       di->state = di_enterschema;
1776       break;
1777     default:
1778       break;
1779     }
1780 }
1781
1782 void
1783 dataiterator_skip_attribute(Dataiterator *di)
1784 {
1785   if (di->state == di_nextsolvableattr)
1786     di->state = di_nextsolvablekey;
1787   else
1788     di->state = di_nextkey;
1789 }
1790
1791 void
1792 dataiterator_skip_solvable(Dataiterator *di)
1793 {
1794   di->nparents = 0;
1795   di->kv.parent = 0;
1796   di->rootlevel = 0;
1797   di->keyname = di->keynames[0];
1798   di->state = di_nextsolvable;
1799 }
1800
1801 void
1802 dataiterator_skip_repo(Dataiterator *di)
1803 {
1804   di->nparents = 0;
1805   di->kv.parent = 0;
1806   di->rootlevel = 0;
1807   di->keyname = di->keynames[0];
1808   di->state = di_nextrepo;
1809 }
1810
1811 void
1812 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1813 {
1814   di->nparents = 0;
1815   di->kv.parent = 0;
1816   di->rootlevel = 0;
1817   di->keyname = di->keynames[0];
1818   if (solvid == SOLVID_POS)
1819     {
1820       di->repo = di->pool->pos.repo;
1821       if (!di->repo)
1822         {
1823           di->state = di_bye;
1824           return;
1825         }
1826       di->repoid = 0;
1827       di->data = di->repo->repodata + di->pool->pos.repodataid;
1828       di->repodataid = 0;
1829       di->solvid = solvid;
1830       di->state = di_enterrepo;
1831       di->flags |= SEARCH_THISSOLVID;
1832       return;
1833     }
1834   if (solvid > 0)
1835     {
1836       di->repo = di->pool->solvables[solvid].repo;
1837       di->repoid = 0;
1838     }
1839   else if (di->repoid > 0)
1840     {
1841       if (!di->pool->urepos)
1842         {
1843           di->state = di_bye;
1844           return;
1845         }
1846       di->repoid = 1;
1847       di->repo = di->pool->repos[di->repoid];
1848     }
1849   di->repodataid = 1;
1850   di->solvid = solvid;
1851   if (solvid)
1852     di->flags |= SEARCH_THISSOLVID;
1853   di->state = di_enterrepo;
1854 }
1855
1856 void
1857 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1858 {
1859   di->nparents = 0;
1860   di->kv.parent = 0;
1861   di->rootlevel = 0;
1862   di->repo = repo;
1863   di->repoid = 0;       /* 0 means stay at repo */
1864   di->repodataid = 1;
1865   di->solvid = 0;
1866   di->flags &= ~SEARCH_THISSOLVID;
1867   di->state = di_enterrepo;
1868 }
1869
1870 int
1871 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1872 {
1873   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1874     return 0;
1875   if (!ma)
1876     return 1;
1877   return datamatcher_match(ma, di->kv.str);
1878 }
1879
1880 /************************************************************************
1881  * data modify functions
1882  */
1883
1884 /* extend repodata so that it includes solvables p */
1885 void
1886 repodata_extend(Repodata *data, Id p)
1887 {
1888   if (data->start == data->end)
1889     data->start = data->end = p;
1890   if (p >= data->end)
1891     {
1892       int old = data->end - data->start;
1893       int new = p - data->end + 1;
1894       if (data->attrs)
1895         {
1896           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1897           memset(data->attrs + old, 0, new * sizeof(Id *));
1898         }
1899       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1900       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1901       data->end = p + 1;
1902     }
1903   if (p < data->start)
1904     {
1905       int old = data->end - data->start;
1906       int new = data->start - p;
1907       if (data->attrs)
1908         {
1909           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1910           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1911           memset(data->attrs, 0, new * sizeof(Id *));
1912         }
1913       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1914       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1915       memset(data->incoreoffset, 0, new * sizeof(Id));
1916       data->start = p;
1917     }
1918 }
1919
1920 /* shrink end of repodata */
1921 void
1922 repodata_shrink(Repodata *data, int end)
1923 {
1924   int i;
1925
1926   if (data->end <= end)
1927     return;
1928   if (data->start >= end)
1929     {
1930       if (data->attrs)
1931         {
1932           for (i = 0; i < data->end - data->start; i++)
1933             solv_free(data->attrs[i]);
1934           data->attrs = solv_free(data->attrs);
1935         }
1936       data->incoreoffset = solv_free(data->incoreoffset);
1937       data->start = data->end = 0;
1938       return;
1939     }
1940   if (data->attrs)
1941     {
1942       for (i = end; i < data->end; i++)
1943         solv_free(data->attrs[i - data->start]);
1944       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
1945     }
1946   if (data->incoreoffset)
1947     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
1948   data->end = end;
1949 }
1950
1951 /* extend repodata so that it includes solvables from start to start + num - 1 */
1952 void
1953 repodata_extend_block(Repodata *data, Id start, Id num)
1954 {
1955   if (!num)
1956     return;
1957   if (!data->incoreoffset)
1958     {
1959       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1960       data->start = start;
1961       data->end = start + num;
1962       return;
1963     }
1964   repodata_extend(data, start);
1965   if (num > 1)
1966     repodata_extend(data, start + num - 1);
1967 }
1968
1969 /**********************************************************************/
1970
1971
1972 #define REPODATA_ATTRS_BLOCK 31
1973 #define REPODATA_ATTRDATA_BLOCK 1023
1974 #define REPODATA_ATTRIDDATA_BLOCK 63
1975 #define REPODATA_ATTRNUM64DATA_BLOCK 15
1976
1977
1978 Id
1979 repodata_new_handle(Repodata *data)
1980 {
1981   if (!data->nxattrs)
1982     {
1983       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1984       data->nxattrs = 2;        /* -1: SOLVID_META */
1985     }
1986   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1987   data->xattrs[data->nxattrs] = 0;
1988   return -(data->nxattrs++);
1989 }
1990
1991 static inline Id **
1992 repodata_get_attrp(Repodata *data, Id handle)
1993 {
1994   if (handle < 0)
1995     {
1996       if (handle == SOLVID_META && !data->xattrs)
1997         {
1998           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1999           data->nxattrs = 2;
2000         }
2001       return data->xattrs - handle;
2002     }
2003   if (handle < data->start || handle >= data->end)
2004     repodata_extend(data, handle);
2005   if (!data->attrs)
2006     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2007   return data->attrs + (handle - data->start);
2008 }
2009
2010 static void
2011 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2012 {
2013   Id *pp;
2014   Id *ap, **app;
2015   int i;
2016
2017   app = repodata_get_attrp(data, handle);
2018   ap = *app;
2019   i = 0;
2020   if (ap)
2021     {
2022       /* Determine equality based on the name only, allows us to change
2023          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2024       for (pp = ap; *pp; pp += 2)
2025         if (data->keys[*pp].name == data->keys[keyid].name)
2026           break;
2027       if (*pp)
2028         {
2029           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2030             {
2031               pp[0] = keyid;
2032               pp[1] = val;
2033             }
2034           return;
2035         }
2036       i = pp - ap;
2037     }
2038   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2039   *app = ap;
2040   pp = ap + i;
2041   *pp++ = keyid;
2042   *pp++ = val;
2043   *pp = 0;
2044 }
2045
2046
2047 static void
2048 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2049 {
2050   Id keyid;
2051
2052   keyid = repodata_key2id(data, key, 1);
2053   repodata_insert_keyid(data, solvid, keyid, val, 1);
2054 }
2055
2056 void
2057 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2058 {
2059   Repokey key;
2060   key.name = keyname;
2061   key.type = REPOKEY_TYPE_ID;
2062   key.size = 0;
2063   key.storage = KEY_STORAGE_INCORE;
2064   repodata_set(data, solvid, &key, id);
2065 }
2066
2067 void
2068 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2069 {
2070   Repokey key;
2071   key.name = keyname;
2072   key.type = REPOKEY_TYPE_NUM;
2073   key.size = 0;
2074   key.storage = KEY_STORAGE_INCORE;
2075   if (num >= 0x80000000)
2076     {
2077       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2078       data->attrnum64data[data->attrnum64datalen] = num;
2079       num = 0x80000000 | data->attrnum64datalen++;
2080     }
2081   repodata_set(data, solvid, &key, (Id)num);
2082 }
2083
2084 void
2085 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2086 {
2087   Repokey key;
2088   Id id;
2089   if (data->localpool)
2090     id = stringpool_str2id(&data->spool, str, 1);
2091   else
2092     id = pool_str2id(data->repo->pool, str, 1);
2093   key.name = keyname;
2094   key.type = REPOKEY_TYPE_ID;
2095   key.size = 0;
2096   key.storage = KEY_STORAGE_INCORE;
2097   repodata_set(data, solvid, &key, id);
2098 }
2099
2100 void
2101 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2102 {
2103   Repokey key;
2104   key.name = keyname;
2105   key.type = REPOKEY_TYPE_CONSTANT;
2106   key.size = constant;
2107   key.storage = KEY_STORAGE_INCORE;
2108   repodata_set(data, solvid, &key, 0);
2109 }
2110
2111 void
2112 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2113 {
2114   Repokey key;
2115   key.name = keyname;
2116   key.type = REPOKEY_TYPE_CONSTANTID;
2117   key.size = id;
2118   key.storage = KEY_STORAGE_INCORE;
2119   repodata_set(data, solvid, &key, 0);
2120 }
2121
2122 void
2123 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2124 {
2125   Repokey key;
2126   key.name = keyname;
2127   key.type = REPOKEY_TYPE_VOID;
2128   key.size = 0;
2129   key.storage = KEY_STORAGE_INCORE;
2130   repodata_set(data, solvid, &key, 0);
2131 }
2132
2133 void
2134 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2135 {
2136   Repokey key;
2137   int l;
2138
2139   l = strlen(str) + 1;
2140   key.name = keyname;
2141   key.type = REPOKEY_TYPE_STR;
2142   key.size = 0;
2143   key.storage = KEY_STORAGE_INCORE;
2144   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2145   memcpy(data->attrdata + data->attrdatalen, str, l);
2146   repodata_set(data, solvid, &key, data->attrdatalen);
2147   data->attrdatalen += l;
2148 }
2149
2150 void
2151 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2152 {
2153   Repokey key;
2154   unsigned char *dp;
2155
2156   if (len < 0)
2157     return;
2158   key.name = keyname;
2159   key.type = REPOKEY_TYPE_BINARY;
2160   key.size = 0;
2161   key.storage = KEY_STORAGE_INCORE;
2162   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2163   dp = data->attrdata + data->attrdatalen;
2164   if (len >= (1 << 14))
2165     {
2166       if (len >= (1 << 28))
2167         *dp++ = (len >> 28) | 128;
2168       if (len >= (1 << 21))
2169         *dp++ = (len >> 21) | 128;
2170       *dp++ = (len >> 14) | 128;
2171     }
2172   if (len >= (1 << 7))
2173     *dp++ = (len >> 7) | 128;
2174   *dp++ = len & 127;
2175   if (len)
2176     memcpy(dp, buf, len);
2177   repodata_set(data, solvid, &key, data->attrdatalen);
2178   data->attrdatalen = dp + len - data->attrdata;
2179 }
2180
2181 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2182  * so that the caller can append entrysize new elements plus the termination zero there */
2183 static void
2184 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2185 {
2186   int oldsize;
2187   Id *ida, *pp, **ppp;
2188
2189   /* check if it is the same as last time, this speeds things up a lot */
2190   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2191     {
2192       /* great! just append the new data */
2193       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2194       data->attriddatalen--;    /* overwrite terminating 0  */
2195       data->lastdatalen += entrysize;
2196       return;
2197     }
2198
2199   ppp = repodata_get_attrp(data, handle);
2200   pp = *ppp;
2201   if (pp)
2202     {
2203       for (; *pp; pp += 2)
2204         if (data->keys[*pp].name == keyname)
2205           break;
2206     }
2207   if (!pp || !*pp || data->keys[*pp].type != keytype)
2208     {
2209       /* not found. allocate new key */
2210       Repokey key;
2211       Id keyid;
2212       key.name = keyname;
2213       key.type = keytype;
2214       key.size = 0;
2215       key.storage = KEY_STORAGE_INCORE;
2216       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2217       keyid = repodata_key2id(data, &key, 1);
2218       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2219       data->lasthandle = handle;
2220       data->lastkey = keyid;
2221       data->lastdatalen = data->attriddatalen + entrysize + 1;
2222       return;
2223     }
2224   oldsize = 0;
2225   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2226     oldsize += entrysize;
2227   if (ida + 1 == data->attriddata + data->attriddatalen)
2228     {
2229       /* this was the last entry, just append it */
2230       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2231       data->attriddatalen--;    /* overwrite terminating 0  */
2232     }
2233   else
2234     {
2235       /* too bad. move to back. */
2236       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2237       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2238       pp[1] = data->attriddatalen;
2239       data->attriddatalen += oldsize;
2240     }
2241   data->lasthandle = handle;
2242   data->lastkey = *pp;
2243   data->lastdatalen = data->attriddatalen + entrysize + 1;
2244 }
2245
2246 void
2247 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2248                       const unsigned char *str)
2249 {
2250   Repokey key;
2251   int l;
2252
2253   if (!(l = solv_chksum_len(type)))
2254     return;
2255   key.name = keyname;
2256   key.type = type;
2257   key.size = 0;
2258   key.storage = KEY_STORAGE_INCORE;
2259   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2260   memcpy(data->attrdata + data->attrdatalen, str, l);
2261   repodata_set(data, solvid, &key, data->attrdatalen);
2262   data->attrdatalen += l;
2263 }
2264
2265 void
2266 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2267                       const char *str)
2268 {
2269   unsigned char buf[64];
2270   int l;
2271
2272   if (!(l = solv_chksum_len(type)))
2273     return;
2274   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2275     return;
2276   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2277 }
2278
2279 const char *
2280 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2281 {
2282   int l;
2283
2284   if (!(l = solv_chksum_len(type)))
2285     return "";
2286   return pool_bin2hex(data->repo->pool, buf, l);
2287 }
2288
2289 /* rpm filenames don't contain the epoch, so strip it */
2290 static inline const char *
2291 evrid2vrstr(Pool *pool, Id evrid)
2292 {
2293   const char *p, *evr = pool_id2str(pool, evrid);
2294   if (!evr)
2295     return evr;
2296   for (p = evr; *p >= '0' && *p <= '9'; p++)
2297     ;
2298   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2299 }
2300
2301 static inline void
2302 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2303 {
2304   Id id;
2305   if (data->localpool)
2306     id = stringpool_strn2id(&data->spool, str, l, 1);
2307   else
2308     id = pool_strn2id(data->repo->pool, str, l, 1);
2309   repodata_set_id(data, solvid, keyname, id);
2310 }
2311
2312 static inline void
2313 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2314 {
2315   if (!str[l])
2316     repodata_set_str(data, solvid, keyname, str);
2317   else
2318     {
2319       char *s = solv_strdup(str);
2320       s[l] = 0;
2321       repodata_set_str(data, solvid, keyname, s);
2322       free(s);
2323     }
2324 }
2325
2326 void
2327 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2328 {
2329   Pool *pool = data->repo->pool;
2330   Solvable *s;
2331   const char *str, *fp;
2332   int l = 0;
2333
2334   if (medianr)
2335     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2336   if (!dir)
2337     {
2338       if ((dir = strrchr(file, '/')) != 0)
2339         {
2340           l = dir - file;
2341           dir = file;
2342           file = dir + l + 1;
2343           if (!l)
2344             l++;
2345         }
2346     }
2347   else
2348     l = strlen(dir);
2349   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2350     {
2351       dir += 2;
2352       l -= 2;
2353     }
2354   if (l == 1 && dir[0] == '.')
2355     l = 0;
2356   s = pool->solvables + solvid;
2357   if (dir && l)
2358     {
2359       str = pool_id2str(pool, s->arch);
2360       if (!strncmp(dir, str, l) && !str[l])
2361         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2362       else
2363         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2364     }
2365   fp = file;
2366   str = pool_id2str(pool, s->name);
2367   l = strlen(str);
2368   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2369     {
2370       fp += l + 1;
2371       str = evrid2vrstr(pool, s->evr);
2372       l = strlen(str);
2373       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2374         {
2375           fp += l + 1;
2376           str = pool_id2str(pool, s->arch);
2377           l = strlen(str);
2378           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2379             {
2380               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2381               return;
2382             }
2383         }
2384     }
2385   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2386 }
2387
2388 /* XXX: medianr is currently not stored */
2389 void
2390 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2391 {
2392   int l = 0;
2393   const char *evr, *suf, *s;
2394
2395   if (!dir)
2396     {
2397       if ((dir = strrchr(file, '/')) != 0)
2398         {
2399           l = dir - file;
2400           dir = file;
2401           file = dir + l + 1;
2402           if (!l)
2403             l++;
2404         }
2405     }
2406   else
2407     l = strlen(dir);
2408   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2409     {
2410       dir += 2;
2411       l -= 2;
2412     }
2413   if (l == 1 && dir[0] == '.')
2414     l = 0;
2415   if (dir && l)
2416     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2417   evr = strchr(file, '-');
2418   if (evr)
2419     {
2420       for (s = evr - 1; s > file; s--)
2421         if (*s == '-')
2422           {
2423             evr = s;
2424             break;
2425           }
2426     }
2427   suf = strrchr(file, '.');
2428   if (suf)
2429     {
2430       for (s = suf - 1; s > file; s--)
2431         if (*s == '.')
2432           {
2433             suf = s;
2434             break;
2435           }
2436       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2437         {
2438           /* We accept one more item as suffix.  */
2439           for (s = suf - 1; s > file; s--)
2440             if (*s == '.')
2441               {
2442                 suf = s;
2443                 break;
2444               }
2445         }
2446     }
2447   if (!evr)
2448     suf = 0;
2449   if (suf && evr && suf < evr)
2450     suf = 0;
2451   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2452   if (evr)
2453     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2454   if (suf)
2455     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2456 }
2457
2458 void
2459 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2460 {
2461   Pool *pool = data->repo->pool;
2462   Solvable *s = pool->solvables + solvid;
2463   const char *p, *sevr, *sarch, *name, *evr;
2464
2465   p = strrchr(sourcepkg, '.');
2466   if (!p || strcmp(p, ".rpm") != 0)
2467     {
2468       if (*sourcepkg)
2469         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2470       return;
2471     }
2472   p--;
2473   while (p > sourcepkg && *p != '.')
2474     p--;
2475   if (*p != '.' || p == sourcepkg)
2476     return;
2477   sarch = p-- + 1;
2478   while (p > sourcepkg && *p != '-')
2479     p--;
2480   if (*p != '-' || p == sourcepkg)
2481     return;
2482   p--;
2483   while (p > sourcepkg && *p != '-')
2484     p--;
2485   if (*p != '-' || p == sourcepkg)
2486     return;
2487   sevr = p + 1;
2488   pool = s->repo->pool;
2489
2490   name = pool_id2str(pool, s->name);
2491   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2492     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2493   else
2494     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2495
2496   evr = evrid2vrstr(pool, s->evr);
2497   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2498     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2499   else
2500     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2501
2502   if (!strcmp(sarch, "src.rpm"))
2503     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2504   else if (!strcmp(sarch, "nosrc.rpm"))
2505     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2506   else
2507     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2508 }
2509
2510 void
2511 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2512 {
2513   Repokey key;
2514   int i;
2515
2516   key.name = keyname;
2517   key.type = REPOKEY_TYPE_IDARRAY;
2518   key.size = 0;
2519   key.storage = KEY_STORAGE_INCORE;
2520   repodata_set(data, solvid, &key, data->attriddatalen);
2521   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2522   for (i = 0; i < q->count; i++)
2523     data->attriddata[data->attriddatalen++] = q->elements[i];
2524   data->attriddata[data->attriddatalen++] = 0;
2525 }
2526
2527 void
2528 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2529 {
2530   assert(dir);
2531 #if 0
2532 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2533 #endif
2534   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2535   data->attriddata[data->attriddatalen++] = dir;
2536   data->attriddata[data->attriddatalen++] = num;
2537   data->attriddata[data->attriddatalen++] = num2;
2538   data->attriddata[data->attriddatalen++] = 0;
2539 }
2540
2541 void
2542 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2543 {
2544   Id stroff;
2545   int l;
2546
2547   assert(dir);
2548   l = strlen(str) + 1;
2549   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2550   memcpy(data->attrdata + data->attrdatalen, str, l);
2551   stroff = data->attrdatalen;
2552   data->attrdatalen += l;
2553
2554 #if 0
2555 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2556 #endif
2557   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2558   data->attriddata[data->attriddatalen++] = dir;
2559   data->attriddata[data->attriddatalen++] = stroff;
2560   data->attriddata[data->attriddatalen++] = 0;
2561 }
2562
2563 void
2564 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2565 {
2566 #if 0
2567 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2568 #endif
2569   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2570   data->attriddata[data->attriddatalen++] = id;
2571   data->attriddata[data->attriddatalen++] = 0;
2572 }
2573
2574 void
2575 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2576                            const char *str)
2577 {
2578   Id id;
2579   if (data->localpool)
2580     id = stringpool_str2id(&data->spool, str, 1);
2581   else
2582     id = pool_str2id(data->repo->pool, str, 1);
2583   repodata_add_idarray(data, solvid, keyname, id);
2584 }
2585
2586 void
2587 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2588 {
2589   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2590   data->attriddata[data->attriddatalen++] = ghandle;
2591   data->attriddata[data->attriddatalen++] = 0;
2592 }
2593
2594 void
2595 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2596 {
2597   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2598   data->attriddata[data->attriddatalen++] = ghandle;
2599   data->attriddata[data->attriddatalen++] = 0;
2600 }
2601
2602 void
2603 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2604 {
2605   Id *pp, *ap, **app;
2606   app = repodata_get_attrp(data, solvid);
2607   ap = *app;
2608   if (!ap)
2609     return;
2610   for (; *ap; ap += 2)
2611     if (data->keys[*ap].name == keyname)
2612       break;
2613   if (!*ap)
2614     return;
2615   pp = ap;
2616   ap += 2;
2617   for (; *ap; ap += 2)
2618     {
2619       if (data->keys[*ap].name == keyname)
2620         continue;
2621       *pp++ = ap[0];
2622       *pp++ = ap[1];
2623     }
2624   *pp = 0;
2625 }
2626
2627 /* XXX: does not work correctly, needs fix in iterators! */
2628 void
2629 repodata_unset(Repodata *data, Id solvid, Id keyname)
2630 {
2631   Repokey key;
2632   key.name = keyname;
2633   key.type = REPOKEY_TYPE_DELETED;
2634   key.size = 0;
2635   key.storage = KEY_STORAGE_INCORE;
2636   repodata_set(data, solvid, &key, 0);
2637 }
2638
2639 /* add all (uninternalized) attrs from src to dest */
2640 void
2641 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2642 {
2643   Id *keyp;
2644   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2645     return;
2646   for (; *keyp; keyp += 2)
2647     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2648 }
2649
2650 /* add some (uninternalized) attrs from src to dest */
2651 void
2652 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2653 {
2654   Id *keyp;
2655   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2656     return;
2657   for (; *keyp; keyp += 2)
2658     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2659       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2660 }
2661
2662 /* swap (uninternalized) attrs from src and dest */
2663 void
2664 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2665 {
2666   Id *tmpattrs;
2667   if (!data->attrs || dest == src)
2668     return;
2669   tmpattrs = data->attrs[dest - data->start];
2670   data->attrs[dest - data->start] = data->attrs[src - data->start];
2671   data->attrs[src - data->start] = tmpattrs;
2672 }
2673
2674
2675 /**********************************************************************/
2676
2677 /* TODO: unify with repo_write and repo_solv! */
2678
2679 #define EXTDATA_BLOCK 1023
2680
2681 struct extdata {
2682   unsigned char *buf;
2683   int len;
2684 };
2685
2686 static void
2687 data_addid(struct extdata *xd, Id sx)
2688 {
2689   unsigned int x = (unsigned int)sx;
2690   unsigned char *dp;
2691
2692   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2693   dp = xd->buf + xd->len;
2694
2695   if (x >= (1 << 14))
2696     {
2697       if (x >= (1 << 28))
2698         *dp++ = (x >> 28) | 128;
2699       if (x >= (1 << 21))
2700         *dp++ = (x >> 21) | 128;
2701       *dp++ = (x >> 14) | 128;
2702     }
2703   if (x >= (1 << 7))
2704     *dp++ = (x >> 7) | 128;
2705   *dp++ = x & 127;
2706   xd->len = dp - xd->buf;
2707 }
2708
2709 static void
2710 data_addid64(struct extdata *xd, unsigned long long x)
2711 {
2712   if (x >= 0x100000000)
2713     {
2714       if ((x >> 35) != 0)
2715         {
2716           data_addid(xd, (Id)(x >> 35));
2717           xd->buf[xd->len - 1] |= 128;
2718         }
2719       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2720       xd->buf[xd->len - 5] = (x >> 28) | 128;
2721     }
2722   else
2723     data_addid(xd, (Id)x);
2724 }
2725
2726 static void
2727 data_addideof(struct extdata *xd, Id sx, int eof)
2728 {
2729   unsigned int x = (unsigned int)sx;
2730   unsigned char *dp;
2731
2732   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2733   dp = xd->buf + xd->len;
2734
2735   if (x >= (1 << 13))
2736     {
2737       if (x >= (1 << 27))
2738         *dp++ = (x >> 27) | 128;
2739       if (x >= (1 << 20))
2740         *dp++ = (x >> 20) | 128;
2741       *dp++ = (x >> 13) | 128;
2742     }
2743   if (x >= (1 << 6))
2744     *dp++ = (x >> 6) | 128;
2745   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2746   xd->len = dp - xd->buf;
2747 }
2748
2749 static void
2750 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2751 {
2752   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2753   memcpy(xd->buf + xd->len, blob, len);
2754   xd->len += len;
2755 }
2756
2757 /*********************************/
2758
2759 /* internalalize some key into incore/vincore data */
2760
2761 static void
2762 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2763                        struct extdata *newvincore,
2764                        Id *schema,
2765                        Repokey *key, Id val)
2766 {
2767   Id *ida;
2768   struct extdata *xd;
2769   unsigned int oldvincorelen = 0;
2770   Id schemaid, *sp;
2771
2772   xd = newincore;
2773   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2774     {
2775       xd = newvincore;
2776       oldvincorelen = xd->len;
2777     }
2778   switch (key->type)
2779     {
2780     case REPOKEY_TYPE_VOID:
2781     case REPOKEY_TYPE_CONSTANT:
2782     case REPOKEY_TYPE_CONSTANTID:
2783       break;
2784     case REPOKEY_TYPE_STR:
2785       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2786       break;
2787     case REPOKEY_TYPE_MD5:
2788       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2789       break;
2790     case REPOKEY_TYPE_SHA1:
2791       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2792       break;
2793     case REPOKEY_TYPE_SHA256:
2794       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2795       break;
2796     case REPOKEY_TYPE_NUM:
2797       if (val & 0x80000000)
2798         {
2799           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2800           break;
2801         }
2802       /* FALLTHROUGH */
2803     case REPOKEY_TYPE_ID:
2804     case REPOKEY_TYPE_DIR:
2805       data_addid(xd, val);
2806       break;
2807     case REPOKEY_TYPE_BINARY:
2808       {
2809         Id len;
2810         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2811         dp += (unsigned int)len;
2812         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2813       }
2814       break;
2815     case REPOKEY_TYPE_IDARRAY:
2816       for (ida = data->attriddata + val; *ida; ida++)
2817         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2818       break;
2819     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2820       for (ida = data->attriddata + val; *ida; ida += 3)
2821         {
2822           data_addid(xd, ida[0]);
2823           data_addid(xd, ida[1]);
2824           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2825         }
2826       break;
2827     case REPOKEY_TYPE_DIRSTRARRAY:
2828       for (ida = data->attriddata + val; *ida; ida += 2)
2829         {
2830           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2831           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2832         }
2833       break;
2834     case REPOKEY_TYPE_FIXARRAY:
2835       {
2836         int num = 0;
2837         schemaid = 0;
2838         for (ida = data->attriddata + val; *ida; ida++)
2839           {
2840             Id *kp;
2841             sp = schema;
2842             kp = data->xattrs[-*ida];
2843             if (!kp)
2844               continue;
2845             num++;
2846             for (;*kp; kp += 2)
2847               *sp++ = *kp;
2848             *sp = 0;
2849             if (!schemaid)
2850               schemaid = repodata_schema2id(data, schema, 1);
2851             else if (schemaid != repodata_schema2id(data, schema, 0))
2852               {
2853                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2854                 exit(1);
2855               }
2856           }
2857         if (!num)
2858           break;
2859         data_addid(xd, num);
2860         data_addid(xd, schemaid);
2861         for (ida = data->attriddata + val; *ida; ida++)
2862           {
2863             Id *kp = data->xattrs[-*ida];
2864             if (!kp)
2865               continue;
2866             for (;*kp; kp += 2)
2867               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2868           }
2869         break;
2870       }
2871     case REPOKEY_TYPE_FLEXARRAY:
2872       {
2873         int num = 0;
2874         for (ida = data->attriddata + val; *ida; ida++)
2875           num++;
2876         data_addid(xd, num);
2877         for (ida = data->attriddata + val; *ida; ida++)
2878           {
2879             Id *kp = data->xattrs[-*ida];
2880             if (!kp)
2881               {
2882                 data_addid(xd, 0);      /* XXX */
2883                 continue;
2884               }
2885             sp = schema;
2886             for (;*kp; kp += 2)
2887               *sp++ = *kp;
2888             *sp = 0;
2889             schemaid = repodata_schema2id(data, schema, 1);
2890             data_addid(xd, schemaid);
2891             kp = data->xattrs[-*ida];
2892             for (;*kp; kp += 2)
2893               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2894           }
2895         break;
2896       }
2897     default:
2898       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2899       exit(1);
2900     }
2901   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2902     {
2903       /* put offset/len in incore */
2904       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2905       oldvincorelen = xd->len - oldvincorelen;
2906       data_addid(newincore, oldvincorelen);
2907     }
2908 }
2909
2910 void
2911 repodata_internalize(Repodata *data)
2912 {
2913   Repokey *key, solvkey;
2914   Id entry, nentry;
2915   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2916   unsigned char *dp, *ndp;
2917   int newschema, oldcount;
2918   struct extdata newincore;
2919   struct extdata newvincore;
2920   Id solvkeyid;
2921
2922   if (!data->attrs && !data->xattrs)
2923     return;
2924
2925   newvincore.buf = data->vincore;
2926   newvincore.len = data->vincorelen;
2927
2928   /* find the solvables key, create if needed */
2929   memset(&solvkey, 0, sizeof(solvkey));
2930   solvkey.name = REPOSITORY_SOLVABLES;
2931   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2932   solvkey.size = 0;
2933   solvkey.storage = KEY_STORAGE_INCORE;
2934   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2935
2936   schema = solv_malloc2(data->nkeys, sizeof(Id));
2937   seen = solv_malloc2(data->nkeys, sizeof(Id));
2938
2939   /* Merge the data already existing (in data->schemata, ->incoredata and
2940      friends) with the new attributes in data->attrs[].  */
2941   nentry = data->end - data->start;
2942   memset(&newincore, 0, sizeof(newincore));
2943   data_addid(&newincore, 0);    /* start data at offset 1 */
2944
2945   data->mainschema = 0;
2946   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
2947
2948   /* join entry data */
2949   /* we start with the meta data, entry -1 */
2950   for (entry = -1; entry < nentry; entry++)
2951     {
2952       memset(seen, 0, data->nkeys * sizeof(Id));
2953       oldschema = 0;
2954       dp = data->incoredata;
2955       if (dp)
2956         {
2957           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2958           dp = data_read_id(dp, &oldschema);
2959         }
2960 #if 0
2961 fprintf(stderr, "oldschema %d\n", oldschema);
2962 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2963 fprintf(stderr, "schemadata %p\n", data->schemadata);
2964 #endif
2965       /* seen: -1: old data  0: skipped  >0: id + 1 */
2966       newschema = 0;
2967       oldcount = 0;
2968       sp = schema;
2969       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2970         {
2971           if (seen[*keyp])
2972             {
2973               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
2974               exit(1);
2975             }
2976           seen[*keyp] = -1;
2977           *sp++ = *keyp;
2978           oldcount++;
2979         }
2980       if (entry >= 0)
2981         keyp = data->attrs ? data->attrs[entry] : 0;
2982       else
2983         {
2984           /* strip solvables key */
2985           *sp = 0;
2986           for (sp = keyp = schema; *sp; sp++)
2987             if (*sp != solvkeyid)
2988               *keyp++ = *sp;
2989             else
2990               oldcount--;
2991           sp = keyp;
2992           seen[solvkeyid] = 0;
2993           keyp = data->xattrs ? data->xattrs[1] : 0;
2994         }
2995       if (keyp)
2996         for (; *keyp; keyp += 2)
2997           {
2998             if (!seen[*keyp])
2999               {
3000                 newschema = 1;
3001                 *sp++ = *keyp;
3002               }
3003             seen[*keyp] = keyp[1] + 1;
3004           }
3005       if (entry < 0 && data->end != data->start)
3006         {
3007           *sp++ = solvkeyid;
3008           newschema = 1;
3009         }
3010       *sp = 0;
3011       if (newschema)
3012         /* Ideally we'd like to sort the new schema here, to ensure
3013            schema equality independend of the ordering.  We can't do that
3014            yet.  For once see below (old ids need to come before new ids).
3015            An additional difficulty is that we also need to move
3016            the values with the keys.  */
3017         schemaid = repodata_schema2id(data, schema, 1);
3018       else
3019         schemaid = oldschema;
3020
3021
3022       /* Now create data blob.  We walk through the (possibly new) schema
3023          and either copy over old data, or insert the new.  */
3024       /* XXX Here we rely on the fact that the (new) schema has the form
3025          o1 o2 o3 o4 ... | n1 n2 n3 ...
3026          (oX being the old keyids (possibly overwritten), and nX being
3027           the new keyids).  This rules out sorting the keyids in order
3028          to ensure a small schema count.  */
3029       if (entry >= 0)
3030         data->incoreoffset[entry] = newincore.len;
3031       data_addid(&newincore, schemaid);
3032       if (entry == -1)
3033         {
3034           data->mainschema = schemaid;
3035           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3036         }
3037       keypstart = data->schemadata + data->schemata[schemaid];
3038       for (keyp = keypstart; *keyp; keyp++)
3039         {
3040           if (entry == -1)
3041             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
3042           if (*keyp == solvkeyid)
3043             {
3044               /* add flexarray entry count */
3045               data_addid(&newincore, data->end - data->start);
3046               break;
3047             }
3048           key = data->keys + *keyp;
3049 #if 0
3050           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));
3051 #endif
3052           ndp = dp;
3053           if (oldcount)
3054             {
3055               /* Skip the data associated with this old key.  */
3056               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3057                 {
3058                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3059                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3060                 }
3061               else if (key->storage == KEY_STORAGE_INCORE)
3062                 ndp = data_skip_key(data, dp, key);
3063               oldcount--;
3064             }
3065           if (seen[*keyp] == -1)
3066             {
3067               /* If this key was an old one _and_ was not overwritten with
3068                  a different value copy over the old value (we skipped it
3069                  above).  */
3070               if (dp != ndp)
3071                 data_addblob(&newincore, dp, ndp - dp);
3072               seen[*keyp] = 0;
3073             }
3074           else if (seen[*keyp])
3075             {
3076               /* Otherwise we have a new value.  Parse it into the internal
3077                  form.  */
3078               repodata_serialize_key(data, &newincore, &newvincore,
3079                                      schema, key, seen[*keyp] - 1);
3080             }
3081           dp = ndp;
3082         }
3083       if (entry >= 0 && data->attrs && data->attrs[entry])
3084         data->attrs[entry] = solv_free(data->attrs[entry]);
3085     }
3086   /* free all xattrs */
3087   for (entry = 0; entry < data->nxattrs; entry++)
3088     if (data->xattrs[entry])
3089       solv_free(data->xattrs[entry]);
3090   data->xattrs = solv_free(data->xattrs);
3091   data->nxattrs = 0;
3092
3093   data->lasthandle = 0;
3094   data->lastkey = 0;
3095   data->lastdatalen = 0;
3096   solv_free(schema);
3097   solv_free(seen);
3098   repodata_free_schemahash(data);
3099
3100   solv_free(data->incoredata);
3101   data->incoredata = newincore.buf;
3102   data->incoredatalen = newincore.len;
3103   data->incoredatafree = 0;
3104
3105   solv_free(data->vincore);
3106   data->vincore = newvincore.buf;
3107   data->vincorelen = newvincore.len;
3108
3109   data->attrs = solv_free(data->attrs);
3110   data->attrdata = solv_free(data->attrdata);
3111   data->attriddata = solv_free(data->attriddata);
3112   data->attrnum64data = solv_free(data->attrnum64data);
3113   data->attrdatalen = 0;
3114   data->attriddatalen = 0;
3115   data->attrnum64datalen = 0;
3116 }
3117
3118 void
3119 repodata_disable_paging(Repodata *data)
3120 {
3121   if (maybe_load_repodata(data, 0))
3122     repopagestore_disable_paging(&data->store);
3123 }
3124
3125 static void
3126 repodata_load_stub(Repodata *data)
3127 {
3128   Repo *repo = data->repo;
3129   Pool *pool = repo->pool;
3130   int r, i;
3131   struct _Pool_tmpspace oldtmpspace;
3132
3133   if (!pool->loadcallback)
3134     {
3135       data->state = REPODATA_ERROR;
3136       return;
3137     }
3138   data->state = REPODATA_LOADING;
3139
3140   /* save tmp space */
3141   oldtmpspace = pool->tmpspace;
3142   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3143
3144   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3145
3146   /* restore tmp space */
3147   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3148     solv_free(pool->tmpspace.buf[i]);
3149   pool->tmpspace = oldtmpspace;
3150
3151   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3152 }
3153
3154 void
3155 repodata_create_stubs(Repodata *data)
3156 {
3157   Repo *repo = data->repo;
3158   Pool *pool = repo->pool;
3159   Repodata *sdata;
3160   int *stubdataids;
3161   Dataiterator di;
3162   Id xkeyname = 0;
3163   int i, cnt = 0;
3164   int repodataid;
3165   int datastart, dataend;
3166
3167   repodataid = data - repo->repodata;
3168   datastart = data->start;
3169   dataend = data->end;
3170   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3171   while (dataiterator_step(&di))
3172     {
3173       if (di.data - repo->repodata != repodataid)
3174         continue;
3175       cnt++;
3176     }
3177   dataiterator_free(&di);
3178   if (!cnt)
3179     return;
3180   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3181   for (i = 0; i < cnt; i++)
3182     {
3183       sdata = repo_add_repodata(repo, 0);
3184       if (dataend > datastart)
3185         repodata_extend_block(sdata, datastart, dataend - datastart);
3186       stubdataids[i] = sdata - repo->repodata;
3187       sdata->state = REPODATA_STUB;
3188       sdata->loadcallback = repodata_load_stub;
3189     }
3190   i = 0;
3191   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3192   sdata = 0;
3193   while (dataiterator_step(&di))
3194     {
3195       if (di.data - repo->repodata != repodataid)
3196         continue;
3197       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3198         {
3199           dataiterator_entersub(&di);
3200           sdata = repo->repodata + stubdataids[i++];
3201           xkeyname = 0;
3202           continue;
3203         }
3204       switch (di.key->type)
3205         {
3206         case REPOKEY_TYPE_ID:
3207           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3208           break;
3209         case REPOKEY_TYPE_CONSTANTID:
3210           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3211           break;
3212         case REPOKEY_TYPE_STR:
3213           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3214           break;
3215         case REPOKEY_TYPE_VOID:
3216           repodata_set_void(sdata, SOLVID_META, di.key->name);
3217           break;
3218         case REPOKEY_TYPE_NUM:
3219           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3220           break;
3221         case REPOKEY_TYPE_MD5:
3222         case REPOKEY_TYPE_SHA1:
3223         case REPOKEY_TYPE_SHA256:
3224           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3225           break;
3226         case REPOKEY_TYPE_IDARRAY:
3227           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3228           if (di.key->name == REPOSITORY_KEYS)
3229             {
3230               Repokey xkey;
3231
3232               if (!xkeyname)
3233                 {
3234                   if (!di.kv.eof)
3235                     xkeyname = di.kv.id;
3236                   continue;
3237                 }
3238               xkey.name = xkeyname;
3239               xkey.type = di.kv.id;
3240               xkey.storage = KEY_STORAGE_INCORE;
3241               xkey.size = 0;
3242               repodata_key2id(sdata, &xkey, 1);
3243               xkeyname = 0;
3244             }
3245         default:
3246           break;
3247         }
3248     }
3249   dataiterator_free(&di);
3250   for (i = 0; i < cnt; i++)
3251     repodata_internalize(repo->repodata + stubdataids[i]);
3252   solv_free(stubdataids);
3253 }
3254
3255 unsigned int
3256 repodata_memused(Repodata *data)
3257 {
3258   return data->incoredatalen + data->vincorelen;
3259 }
3260
3261 /*
3262 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3263 */