Imported Upstream version 0.7.0
[platform/upstream/libsolv.git] / src / repodata.c
1 /*
2  * Copyright (c) 2018, SUSE LLC.
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   repodata_free_filelistfilter(data);
99 }
100
101 void
102 repodata_free(Repodata *data)
103 {
104   Repo *repo = data->repo;
105   int i = data - repo->repodata;
106   if (i == 0)
107     return;
108   repodata_freedata(data);
109   if (i < repo->nrepodata - 1)
110     {
111       /* whoa! this changes the repodataids! */
112       memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
113       for (; i < repo->nrepodata - 1; i++)
114         repo->repodata[i].repodataid = i;
115     }
116   repo->nrepodata--;
117   if (repo->nrepodata == 1)
118     {
119       repo->repodata = solv_free(repo->repodata);
120       repo->nrepodata = 0;
121     }
122 }
123
124 void
125 repodata_empty(Repodata *data, int localpool)
126 {
127   void (*loadcallback)(Repodata *) = data->loadcallback;
128   int state = data->state;
129   repodata_freedata(data);
130   repodata_initdata(data, data->repo, localpool);
131   data->state = state;
132   data->loadcallback = loadcallback;
133 }
134
135
136 /***************************************************************
137  * key pool management
138  */
139
140 /* this is not so time critical that we need a hash, so we do a simple
141  * linear search */
142 Id
143 repodata_key2id(Repodata *data, Repokey *key, int create)
144 {
145   Id keyid;
146
147   for (keyid = 1; keyid < data->nkeys; keyid++)
148     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
149       {
150         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
151           continue;
152         break;
153       }
154   if (keyid == data->nkeys)
155     {
156       if (!create)
157         return 0;
158       /* allocate new key */
159       data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
160       data->keys[data->nkeys++] = *key;
161       if (data->verticaloffset)
162         {
163           data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
164           data->verticaloffset[data->nkeys - 1] = 0;
165         }
166       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
167     }
168   return keyid;
169 }
170
171
172 /***************************************************************
173  * schema pool management
174  */
175
176 #define SCHEMATA_BLOCK 31
177 #define SCHEMATADATA_BLOCK 255
178
179 Id
180 repodata_schema2id(Repodata *data, Id *schema, int create)
181 {
182   int h, len, i;
183   Id *sp, cid;
184   Id *schematahash;
185
186   if (!*schema)
187     return 0;   /* XXX: allow empty schema? */
188   if ((schematahash = data->schematahash) == 0)
189     {
190       data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
191       for (i = 1; i < data->nschemata; i++)
192         {
193           for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
194             h = h * 7 + *sp++;
195           h &= 255;
196           schematahash[h] = i;
197         }
198       data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
199       data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
200     }
201
202   for (sp = schema, len = 0, h = 0; *sp; len++)
203     h = h * 7 + *sp++;
204   h &= 255;
205   len++;
206
207   cid = schematahash[h];
208   if (cid)
209     {
210       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
211         return cid;
212       /* cache conflict, do a slow search */
213       for (cid = 1; cid < data->nschemata; cid++)
214         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
215           return cid;
216     }
217   /* a new one */
218   if (!create)
219     return 0;
220   data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
221   data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
222   /* add schema */
223   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
224   data->schemata[data->nschemata] = data->schemadatalen;
225   data->schemadatalen += len;
226   schematahash[h] = data->nschemata;
227 #if 0
228 fprintf(stderr, "schema2id: new schema\n");
229 #endif
230   return data->nschemata++;
231 }
232
233 void
234 repodata_free_schemahash(Repodata *data)
235 {
236   data->schematahash = solv_free(data->schematahash);
237   /* shrink arrays */
238   data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
239   data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
240 }
241
242
243 /***************************************************************
244  * dir pool management
245  */
246
247 #ifndef HAVE_STRCHRNUL
248 static inline const char *strchrnul(const char *str, char x)
249 {
250   const char *p = strchr(str, x);
251   return p ? p : str + strlen(str);
252 }
253 #endif
254
255 #define DIRCACHE_SIZE 41        /* < 1k */
256
257 #ifdef DIRCACHE_SIZE
258 struct dircache {
259   Id ids[DIRCACHE_SIZE];
260   char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
261 };
262 #endif
263
264 Id
265 repodata_str2dir(Repodata *data, const char *dir, int create)
266 {
267   Id id, parent;
268 #ifdef DIRCACHE_SIZE
269   const char *dirs;
270 #endif
271   const char *dire;
272
273   if (!*dir)
274     return data->dirpool.ndirs ? 0 : dirpool_add_dir(&data->dirpool, 0, 0, create);
275   while (*dir == '/' && dir[1] == '/')
276     dir++;
277   if (*dir == '/' && !dir[1])
278     return data->dirpool.ndirs ? 1 : dirpool_add_dir(&data->dirpool, 0, 1, create);
279   parent = 0;
280 #ifdef DIRCACHE_SIZE
281   dirs = dir;
282   if (data->dircache)
283     {
284       int l;
285       struct dircache *dircache = data->dircache;
286       l = strlen(dir);
287       while (l > 0)
288         {
289           if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
290             {
291               parent = dircache->ids[l];
292               dir += l;
293               if (!*dir)
294                 return parent;
295               while (*dir == '/')
296                 dir++;
297               break;
298             }
299           while (--l)
300             if (dir[l] == '/')
301               break;
302         }
303     }
304 #endif
305   while (*dir)
306     {
307       dire = strchrnul(dir, '/');
308       if (data->localpool)
309         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
310       else
311         id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
312       if (!id)
313         return 0;
314       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
315       if (!parent)
316         return 0;
317 #ifdef DIRCACHE_SIZE
318       if (!data->dircache)
319         data->dircache = solv_calloc(1, sizeof(struct dircache));
320       if (data->dircache)
321         {
322           int l = dire - dirs;
323           if (l < DIRCACHE_SIZE)
324             {
325               data->dircache->ids[l] = parent;
326               memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
327             }
328         }
329 #endif
330       if (!*dire)
331         break;
332       dir = dire + 1;
333       while (*dir == '/')
334         dir++;
335     }
336   return parent;
337 }
338
339 void
340 repodata_free_dircache(Repodata *data)
341 {
342   data->dircache = solv_free(data->dircache);
343 }
344
345 const char *
346 repodata_dir2str(Repodata *data, Id did, const char *suf)
347 {
348   Pool *pool = data->repo->pool;
349   int l = 0;
350   Id parent, comp;
351   const char *comps;
352   char *p;
353
354   if (!did)
355     return suf ? suf : "";
356   if (did == 1 && !suf)
357     return "/";
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 <= 0)
478     return 0;
479   if (off >= data->lastverticaloffset)
480     {
481       off -= data->lastverticaloffset;
482       if ((unsigned int)off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if ((unsigned int)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   data->storestate++;
493   if (dp)
494     dp += off % REPOPAGE_BLOBSIZE;
495   return dp;
496 }
497
498 static inline unsigned char *
499 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
500 {
501   unsigned char *dp = *dpp;
502
503   if (!dp)
504     return 0;
505   if (key->storage == KEY_STORAGE_INCORE)
506     {
507       if (advance)
508         *dpp = data_skip_key(data, dp, key);
509       return dp;
510     }
511   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
512     {
513       Id off, len;
514       dp = data_read_id(dp, &off);
515       dp = data_read_id(dp, &len);
516       if (advance)
517         *dpp = dp;
518       return get_vertical_data(data, key, off, len);
519     }
520   return 0;
521 }
522
523 void
524 repodata_load(Repodata *data)
525 {
526   if (data->state != REPODATA_STUB)
527     return;
528   if (data->loadcallback)
529     data->loadcallback(data);
530   else
531     data->state = REPODATA_ERROR;
532 }
533
534 static int
535 maybe_load_repodata_stub(Repodata *data, Id keyname)
536 {
537   if (data->state != REPODATA_STUB)
538     {
539       data->state = REPODATA_ERROR;
540       return 0;
541     }
542   if (keyname)
543     {
544       int i;
545       for (i = 1; i < data->nkeys; i++)
546         if (keyname == data->keys[i].name)
547           break;
548       if (i == data->nkeys)
549         return 0;
550     }
551   repodata_load(data);
552   return data->state == REPODATA_AVAILABLE ? 1 : 0;
553 }
554
555 static inline int
556 maybe_load_repodata(Repodata *data, Id keyname)
557 {
558   if (keyname && !repodata_precheck_keyname(data, keyname))
559     return 0;   /* do not bother... */
560   if (data->state == REPODATA_AVAILABLE || data->state == REPODATA_LOADING)
561     return 1;
562   if (data->state == REPODATA_ERROR)
563     return 0;
564   return maybe_load_repodata_stub(data, keyname);
565 }
566
567 static inline unsigned char *
568 solvid2data(Repodata *data, Id solvid, Id *schemap)
569 {
570   unsigned char *dp = data->incoredata;
571   if (!dp)
572     return 0;
573   if (solvid == SOLVID_META)
574     dp += 1;    /* offset of "meta" solvable */
575   else if (solvid == SOLVID_POS)
576     {
577       Pool *pool = data->repo->pool;
578       if (data->repo != pool->pos.repo)
579         return 0;
580       if (data != data->repo->repodata + pool->pos.repodataid)
581         return 0;
582       dp += pool->pos.dp;
583       if (pool->pos.dp != 1)
584         {
585           *schemap = pool->pos.schema;
586           return dp;
587         }
588     }
589   else
590     {
591       if (solvid < data->start || solvid >= data->end)
592         return 0;
593       dp += data->incoreoffset[solvid - data->start];
594     }
595   return data_read_id(dp, schemap);
596 }
597
598 /************************************************************************
599  * data lookup
600  */
601
602 static unsigned char *
603 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
604 {
605   unsigned char *dp;
606   Id schema, *keyp, *kp;
607   Repokey *key;
608
609   if (!maybe_load_repodata(data, keyname))
610     return 0;
611   dp = solvid2data(data, solvid, &schema);
612   if (!dp)
613     return 0;
614   keyp = data->schemadata + data->schemata[schema];
615   for (kp = keyp; *kp; kp++)
616     if (data->keys[*kp].name == keyname)
617       break;
618   if (!*kp)
619     return 0;
620   *keypp = key = data->keys + *kp;
621   if (key->type == REPOKEY_TYPE_DELETED)
622     return 0;
623   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
624     return dp;  /* no need to forward... */
625   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
626     return 0;   /* get_data will not work, no need to forward */
627   dp = forward_to_key(data, *kp, keyp, dp);
628   if (!dp)
629     return 0;
630   return get_data(data, key, &dp, 0);
631 }
632
633 static const Id *
634 repodata_lookup_schemakeys(Repodata *data, Id solvid)
635 {
636   Id schema;
637   if (!maybe_load_repodata(data, 0))
638     return 0;
639   if (!solvid2data(data, solvid, &schema))
640     return 0;
641   return data->schemadata + data->schemata[schema];
642 }
643
644 static Id *
645 alloc_keyskip()
646 {
647   Id *keyskip = solv_calloc(3 + 256, sizeof(Id));
648   keyskip[0] = 256; 
649   keyskip[1] = keyskip[2] = 1; 
650   return keyskip;
651 }
652
653 Id *
654 repodata_fill_keyskip(Repodata *data, Id solvid, Id *keyskip)
655 {
656   const Id *keyp;
657   Id maxkeyname, value;
658   keyp = repodata_lookup_schemakeys(data, solvid);
659   if (!keyp)
660     return keyskip;     /* no keys for this solvid */
661   if (!keyskip)
662     keyskip = alloc_keyskip();
663   maxkeyname = keyskip[0];
664   value = keyskip[1] + data->repodataid;
665   for (; *keyp; keyp++)
666     {
667       Id keyname = data->keys[*keyp].name;
668       if (keyname >= maxkeyname)
669         {
670           int newmax = (keyname | 255) + 1; 
671           keyskip = solv_realloc2(keyskip, 3 + newmax, sizeof(Id));
672           memset(keyskip + (3 + maxkeyname), 0, (newmax - maxkeyname) * sizeof(Id));
673           keyskip[0] = maxkeyname = newmax;
674         }
675       keyskip[3 + keyname] = value;
676     }
677   return keyskip;
678 }
679
680 Id
681 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
682 {
683   Id schema, *keyp, *kp;
684   if (!maybe_load_repodata(data, keyname))
685     return 0;
686   if (!solvid2data(data, solvid, &schema))
687     return 0;
688   keyp = data->schemadata + data->schemata[schema];
689   for (kp = keyp; *kp; kp++)
690     if (data->keys[*kp].name == keyname)
691       return data->keys[*kp].type;
692   return 0;
693 }
694
695 Id
696 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
697 {
698   unsigned char *dp;
699   Repokey *key;
700   Id id;
701
702   dp = find_key_data(data, solvid, keyname, &key);
703   if (!dp)
704     return 0;
705   if (key->type == REPOKEY_TYPE_CONSTANTID)
706     return key->size;
707   if (key->type != REPOKEY_TYPE_ID)
708     return 0;
709   dp = data_read_id(dp, &id);
710   return id;
711 }
712
713 const char *
714 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
715 {
716   unsigned char *dp;
717   Repokey *key;
718   Id id;
719
720   dp = find_key_data(data, solvid, keyname, &key);
721   if (!dp)
722     return 0;
723   if (key->type == REPOKEY_TYPE_STR)
724     return (const char *)dp;
725   if (key->type == REPOKEY_TYPE_CONSTANTID)
726     id = key->size;
727   else if (key->type == REPOKEY_TYPE_ID)
728     dp = data_read_id(dp, &id);
729   else
730     return 0;
731   if (data->localpool)
732     return stringpool_id2str(&data->spool, id);
733   return pool_id2str(data->repo->pool, id);
734 }
735
736 unsigned long long
737 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long notfound)
738 {
739   unsigned char *dp;
740   Repokey *key;
741   unsigned int high, low;
742
743   dp = find_key_data(data, solvid, keyname, &key);
744   if (!dp)
745     return notfound;
746   switch (key->type)
747     {
748     case REPOKEY_TYPE_NUM:
749       data_read_num64(dp, &low, &high);
750       return (unsigned long long)high << 32 | low;
751     case REPOKEY_TYPE_CONSTANT:
752       return key->size;
753     default:
754       return notfound;
755     }
756 }
757
758 int
759 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
760 {
761   return repodata_lookup_type(data, solvid, keyname) == REPOKEY_TYPE_VOID ? 1 : 0;
762 }
763
764 const unsigned char *
765 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
766 {
767   unsigned char *dp;
768   Repokey *key;
769
770   dp = find_key_data(data, solvid, keyname, &key);
771   if (!dp)
772     return 0;
773   switch (key->type)
774     {
775     case_CHKSUM_TYPES:
776       break;
777     default:
778       return 0;
779     }
780   *typep = key->type;
781   return dp;
782 }
783
784 int
785 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
786 {
787   unsigned char *dp;
788   Repokey *key;
789   Id id;
790   int eof = 0;
791
792   queue_empty(q);
793   dp = find_key_data(data, solvid, keyname, &key);
794   if (!dp || key->type != REPOKEY_TYPE_IDARRAY)
795     return 0;
796   for (;;)
797     {
798       dp = data_read_ideof(dp, &id, &eof);
799       queue_push(q, id);
800       if (eof)
801         break;
802     }
803   return 1;
804 }
805
806 const void *
807 repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
808 {
809   unsigned char *dp;
810   Repokey *key;
811   Id len;
812
813   dp = find_key_data(data, solvid, keyname, &key);
814   if (!dp || key->type != REPOKEY_TYPE_BINARY)
815     {
816       *lenp = 0;
817       return 0;
818     }
819   dp = data_read_id(dp, &len);
820   *lenp = len;
821   return dp;
822 }
823
824 /* highly specialized function to speed up fileprovides adding.
825  * - repodata must be available
826  * - solvid must be >= data->start and < data->end
827  * - returns NULL is not found, a "" entry if wrong type
828  * - also returns wrong type for REPOKEY_TYPE_DELETED
829  */
830 const unsigned char *
831 repodata_lookup_packed_dirstrarray(Repodata *data, Id solvid, Id keyname)
832 {
833   static unsigned char wrongtype[2] = { 0x00 /* dir id 0 */, 0 /* "" */ };
834   unsigned char *dp;
835   Id schema, *keyp, *kp;
836   Repokey *key;
837
838   if (!data->incoredata || !data->incoreoffset[solvid - data->start])
839     return 0;
840   dp = data->incoredata + data->incoreoffset[solvid - data->start];
841   dp = data_read_id(dp, &schema);
842   keyp = data->schemadata + data->schemata[schema];
843   for (kp = keyp; *kp; kp++)
844     if (data->keys[*kp].name == keyname)
845       break;
846   if (!*kp)
847     return 0;
848   key = data->keys + *kp;
849   if (key->type != REPOKEY_TYPE_DIRSTRARRAY)
850     return wrongtype;
851   dp = forward_to_key(data, *kp, keyp, dp);
852   if (key->storage == KEY_STORAGE_INCORE)
853     return dp;
854   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET && dp)
855     {
856       Id off, len;
857       dp = data_read_id(dp, &off);
858       data_read_id(dp, &len);
859       return get_vertical_data(data, key, off, len);
860     }
861   return 0;
862 }
863
864 /* id translation functions */
865
866 Id
867 repodata_globalize_id(Repodata *data, Id id, int create)
868 {
869   if (!id || !data || !data->localpool)
870     return id;
871   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
872 }
873
874 Id
875 repodata_localize_id(Repodata *data, Id id, int create)
876 {
877   if (!id || !data || !data->localpool)
878     return id;
879   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
880 }
881
882 Id
883 repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
884 {
885   const char *s;
886   if (!id || !data || !fromdata)
887     return id;
888   if (data == fromdata || (!data->localpool && !fromdata->localpool))
889     return id;
890   if (fromdata->localpool)
891     s = stringpool_id2str(&fromdata->spool, id);
892   else
893     s = pool_id2str(data->repo->pool, id);
894   if (data->localpool)
895     return stringpool_str2id(&data->spool, s, create);
896   else
897     return pool_str2id(data->repo->pool, s, create);
898 }
899
900 Id
901 repodata_translate_dir_slow(Repodata *data, Repodata *fromdata, Id dir, int create, Id *cache)
902 {
903   Id parent, compid;
904   if (!dir)
905     {
906       /* make sure that the dirpool has an entry */
907       if (create && !data->dirpool.ndirs)
908         dirpool_add_dir(&data->dirpool, 0, 0, create);
909       return 0;
910     }
911   parent = dirpool_parent(&fromdata->dirpool, dir);
912   if (parent)
913     {
914       if (!(parent = repodata_translate_dir(data, fromdata, parent, create, cache)))
915         return 0;
916     }
917   compid = dirpool_compid(&fromdata->dirpool, dir);
918   if (compid > 1 && (data->localpool || fromdata->localpool))
919     {
920       if (!(compid = repodata_translate_id(data, fromdata, compid, create)))
921         return 0;
922     }
923   if (!(compid = dirpool_add_dir(&data->dirpool, parent, compid, create)))
924     return 0;
925   if (cache)
926     {
927       cache[(dir & 255) * 2] = dir;
928       cache[(dir & 255) * 2 + 1] = compid;
929     }
930   return compid;
931 }
932
933 /************************************************************************
934  * uninternalized lookup / search
935  */
936
937 static void
938 data_fetch_uninternalized(Repodata *data, Repokey *key, Id value, KeyValue *kv)
939 {
940   Id *array;
941   kv->eof = 1;
942   switch (key->type)
943     {
944     case REPOKEY_TYPE_STR:
945       kv->str = (const char *)data->attrdata + value;
946       return;
947     case REPOKEY_TYPE_CONSTANT:
948       kv->num2 = 0;
949       kv->num = key->size;
950       return;
951     case REPOKEY_TYPE_CONSTANTID:
952       kv->id = key->size;
953       return;
954     case REPOKEY_TYPE_NUM:
955       kv->num2 = 0;
956       kv->num = value;
957       if (value & 0x80000000)
958         {
959           kv->num = (unsigned int)data->attrnum64data[value ^ 0x80000000];
960           kv->num2 = (unsigned int)(data->attrnum64data[value ^ 0x80000000] >> 32);
961         }
962       return;
963     case_CHKSUM_TYPES:
964       kv->num = 0;      /* not stringified */
965       kv->str = (const char *)data->attrdata + value;
966       return;
967     case REPOKEY_TYPE_BINARY:
968       kv->str = (const char *)data_read_id(data->attrdata + value, (Id *)&kv->num);
969       return;
970     case REPOKEY_TYPE_IDARRAY:
971       array = data->attriddata + (value + kv->entry);
972       kv->id = array[0];
973       kv->eof = array[1] ? 0 : 1;
974       return;
975     case REPOKEY_TYPE_DIRSTRARRAY:
976       kv->num = 0;      /* not stringified */
977       array = data->attriddata + (value + kv->entry * 2);
978       kv->id = array[0];
979       kv->str = (const char *)data->attrdata + array[1];
980       kv->eof = array[2] ? 0 : 1;
981       return;
982     case REPOKEY_TYPE_DIRNUMNUMARRAY:
983       array = data->attriddata + (value + kv->entry * 3);
984       kv->id = array[0];
985       kv->num = array[1];
986       kv->num2 = array[2];
987       kv->eof = array[3] ? 0 : 1;
988       return;
989     case REPOKEY_TYPE_FIXARRAY:
990     case REPOKEY_TYPE_FLEXARRAY:
991       array = data->attriddata + (value + kv->entry);
992       kv->id = array[0];                /* the handle */
993       kv->eof = array[1] ? 0 : 1;
994       return;
995     default:
996       kv->id = value;
997       return;
998     }
999 }
1000
1001 Repokey *
1002 repodata_lookup_kv_uninternalized(Repodata *data, Id solvid, Id keyname, KeyValue *kv)
1003 {
1004   Id *ap;
1005   if (!data->attrs || solvid < data->start || solvid >= data->end)
1006     return 0;
1007   ap = data->attrs[solvid - data->start];
1008   if (!ap)
1009     return 0;
1010   for (; *ap; ap += 2)
1011     {
1012       Repokey *key = data->keys + *ap;
1013       if (key->name != keyname)
1014         continue;
1015       data_fetch_uninternalized(data, key, ap[1], kv);
1016       return key;
1017     }
1018   return 0;
1019 }
1020
1021 void
1022 repodata_search_uninternalized(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1023 {
1024   Id *ap;
1025   int stop;
1026   Solvable *s;
1027   KeyValue kv;
1028
1029   if (!data->attrs || solvid < data->start || solvid >= data->end)
1030     return;
1031   ap = data->attrs[solvid - data->start];
1032   if (!ap)
1033     return;
1034   for (; *ap; ap += 2)
1035     {
1036       Repokey *key = data->keys + *ap;
1037       if (keyname && key->name != keyname)
1038         continue;
1039       s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1040       kv.entry = 0;
1041       do
1042         {
1043           data_fetch_uninternalized(data, key, ap[1], &kv);
1044           stop = callback(cbdata, s, data, key, &kv);
1045           kv.entry++;
1046         }
1047       while (!kv.eof && !stop);
1048       if (keyname || stop > SEARCH_NEXT_KEY)
1049         return;
1050     }
1051 }
1052
1053 /************************************************************************
1054  * data search
1055  */
1056
1057
1058 const char *
1059 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
1060 {
1061   switch (key->type)
1062     {
1063     case REPOKEY_TYPE_ID:
1064     case REPOKEY_TYPE_CONSTANTID:
1065     case REPOKEY_TYPE_IDARRAY:
1066       if (data && data->localpool)
1067         kv->str = stringpool_id2str(&data->spool, kv->id);
1068       else
1069         kv->str = pool_id2str(pool, kv->id);
1070       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE && (key->name == SOLVABLE_NAME || key->type == REPOKEY_TYPE_IDARRAY))
1071         {
1072           const char *s;
1073           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
1074             ;
1075           if (*s == ':' && s > kv->str)
1076             kv->str = s + 1;
1077         }
1078       return kv->str;
1079     case REPOKEY_TYPE_STR:
1080       return kv->str;
1081     case REPOKEY_TYPE_DIRSTRARRAY:
1082       if (!(flags & SEARCH_FILES))
1083         return kv->str; /* match just the basename */
1084       if (kv->num)
1085         return kv->str; /* already stringified */
1086       /* Put the full filename into kv->str.  */
1087       kv->str = repodata_dir2str(data, kv->id, kv->str);
1088       kv->num = 1;      /* mark stringification */
1089       return kv->str;
1090     case_CHKSUM_TYPES:
1091       if (!(flags & SEARCH_CHECKSUMS))
1092         return 0;       /* skip em */
1093       if (kv->num)
1094         return kv->str; /* already stringified */
1095       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
1096       kv->num = 1;      /* mark stringification */
1097       return kv->str;
1098     default:
1099       return 0;
1100     }
1101 }
1102
1103
1104 /* this is an internal hack to pass the parent kv to repodata_search_keyskip */
1105 struct subschema_data {
1106   void *cbdata;
1107   Id solvid;
1108   KeyValue *parent;
1109 };
1110
1111 void
1112 repodata_search_arrayelement(Repodata *data, Id solvid, Id keyname, int flags, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1113 {
1114   repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
1115 }
1116
1117 static int
1118 repodata_search_array(Repodata *data, Id solvid, Id keyname, int flags, Repokey *key, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1119 {
1120   Solvable *s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1121   unsigned char *dp = (unsigned char *)kv->str;
1122   int stop;
1123   Id schema = 0;
1124
1125   if (!dp || kv->entry != -1)
1126     return 0;
1127   while (++kv->entry < kv->num)
1128     {
1129       if (kv->entry)
1130         dp = data_skip_schema(data, dp, schema);
1131       if (kv->entry == 0 || key->type == REPOKEY_TYPE_FLEXARRAY)
1132         dp = data_read_id(dp, &schema);
1133       kv->id = schema;
1134       kv->str = (const char *)dp;
1135       kv->eof = kv->entry == kv->num - 1 ? 1 : 0;
1136       stop = callback(cbdata, s, data, key, kv);
1137       if (stop && stop != SEARCH_ENTERSUB)
1138         return stop;
1139       if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
1140         repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
1141     }
1142   if ((flags & SEARCH_ARRAYSENTINEL) != 0)
1143     {
1144       if (kv->entry)
1145         dp = data_skip_schema(data, dp, schema);
1146       kv->id = 0;
1147       kv->str = (const char *)dp;
1148       kv->eof = 2;
1149       return callback(cbdata, s, data, key, kv);
1150     }
1151   return 0;
1152 }
1153
1154 /* search a specific repodata */
1155 void
1156 repodata_search_keyskip(Repodata *data, Id solvid, Id keyname, int flags, Id *keyskip, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1157 {
1158   Id schema;
1159   Repokey *key;
1160   Id keyid, *kp, *keyp;
1161   unsigned char *dp, *ddp;
1162   int onekey = 0;
1163   int stop;
1164   KeyValue kv;
1165   Solvable *s;
1166
1167   if (!maybe_load_repodata(data, keyname))
1168     return;
1169   if ((flags & SEARCH_SUBSCHEMA) != 0)
1170     {
1171       flags ^= SEARCH_SUBSCHEMA;
1172       kv.parent = (KeyValue *)keyskip;
1173       keyskip = 0;
1174       schema = kv.parent->id;
1175       dp = (unsigned char *)kv.parent->str;
1176     }
1177   else
1178     {
1179       schema = 0;
1180       dp = solvid2data(data, solvid, &schema);
1181       if (!dp)
1182         return;
1183       kv.parent = 0;
1184     }
1185   s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1186   keyp = data->schemadata + data->schemata[schema];
1187   if (keyname)
1188     {
1189       /* search for a specific key */
1190       for (kp = keyp; *kp; kp++)
1191         if (data->keys[*kp].name == keyname)
1192           break;
1193       if (!*kp)
1194         return;
1195       dp = forward_to_key(data, *kp, keyp, dp);
1196       if (!dp)
1197         return;
1198       keyp = kp;
1199       onekey = 1;
1200     }
1201   while ((keyid = *keyp++) != 0)
1202     {
1203       stop = 0;
1204       key = data->keys + keyid;
1205       ddp = get_data(data, key, &dp, *keyp && !onekey ? 1 : 0);
1206
1207       if (keyskip && (key->name >= keyskip[0] || keyskip[3 + key->name] != keyskip[1] + data->repodataid))
1208         {
1209           if (onekey)
1210             return;
1211           continue;
1212         }
1213       if (key->type == REPOKEY_TYPE_DELETED && !(flags & SEARCH_KEEP_TYPE_DELETED))
1214         {
1215           if (onekey)
1216             return;
1217           continue;
1218         }
1219       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
1220         {
1221           kv.entry = -1;
1222           ddp = data_read_id(ddp, (Id *)&kv.num);
1223           kv.str = (const char *)ddp;
1224           stop = repodata_search_array(data, solvid, 0, flags, key, &kv, callback, cbdata);
1225           if (onekey || stop > SEARCH_NEXT_KEY)
1226             return;
1227           continue;
1228         }
1229       kv.entry = 0;
1230       do
1231         {
1232           ddp = data_fetch(ddp, &kv, key);
1233           if (!ddp)
1234             break;
1235           stop = callback(cbdata, s, data, key, &kv);
1236           kv.entry++;
1237         }
1238       while (!kv.eof && !stop);
1239       if (onekey || stop > SEARCH_NEXT_KEY)
1240         return;
1241     }
1242 }
1243
1244 void
1245 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1246 {
1247   repodata_search_keyskip(data, solvid, keyname, flags, 0, callback, cbdata);
1248 }
1249
1250 void
1251 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1252 {
1253   Pool *pool = data->repo->pool;
1254   if (!kv)
1255     pool_clear_pos(pool);
1256   else
1257     {
1258       pool->pos.repo = data->repo;
1259       pool->pos.repodataid = data - data->repo->repodata;
1260       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1261       pool->pos.schema = kv->id;
1262     }
1263 }
1264
1265 /************************************************************************
1266  * data iterator functions
1267  */
1268
1269 static inline Id *
1270 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1271 {
1272   kv->id = keyname;
1273   switch (keyname)
1274     {
1275     case SOLVABLE_NAME:
1276       kv->eof = 1;
1277       return &s->name;
1278     case SOLVABLE_ARCH:
1279       kv->eof = 1;
1280       return &s->arch;
1281     case SOLVABLE_EVR:
1282       kv->eof = 1;
1283       return &s->evr;
1284     case SOLVABLE_VENDOR:
1285       kv->eof = 1;
1286       return &s->vendor;
1287     case SOLVABLE_PROVIDES:
1288       kv->eof = 0;
1289       return s->provides ? s->repo->idarraydata + s->provides : 0;
1290     case SOLVABLE_OBSOLETES:
1291       kv->eof = 0;
1292       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1293     case SOLVABLE_CONFLICTS:
1294       kv->eof = 0;
1295       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1296     case SOLVABLE_REQUIRES:
1297       kv->eof = 0;
1298       return s->requires ? s->repo->idarraydata + s->requires : 0;
1299     case SOLVABLE_RECOMMENDS:
1300       kv->eof = 0;
1301       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1302     case SOLVABLE_SUPPLEMENTS:
1303       kv->eof = 0;
1304       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1305     case SOLVABLE_SUGGESTS:
1306       kv->eof = 0;
1307       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1308     case SOLVABLE_ENHANCES:
1309       kv->eof = 0;
1310       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1311     case RPM_RPMDBID:
1312       kv->eof = 1;
1313       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1314     default:
1315       return 0;
1316     }
1317 }
1318
1319 int
1320 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1321 {
1322   match = match ? solv_strdup(match) : 0;
1323   ma->match = match;
1324   ma->flags = flags;
1325   ma->error = 0;
1326   ma->matchdata = 0;
1327   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1328     {
1329       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1330       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1331       if (ma->error)
1332         {
1333           solv_free(ma->matchdata);
1334           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1335         }
1336     }
1337   if ((flags & SEARCH_FILES) != 0 && match)
1338     {
1339       /* prepare basename check */
1340       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1341         {
1342           const char *p = strrchr(match, '/');
1343           ma->matchdata = (void *)(p ? p + 1 : match);
1344         }
1345       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1346         {
1347           const char *p;
1348           for (p = match + strlen(match) - 1; p >= match; p--)
1349             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1350               break;
1351           ma->matchdata = (void *)(p + 1);
1352         }
1353     }
1354   return ma->error;
1355 }
1356
1357 void
1358 datamatcher_free(Datamatcher *ma)
1359 {
1360   if (ma->match)
1361     ma->match = solv_free((char *)ma->match);
1362   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1363     {
1364       regfree(ma->matchdata);
1365       solv_free(ma->matchdata);
1366     }
1367   ma->matchdata = 0;
1368 }
1369
1370 int
1371 datamatcher_match(Datamatcher *ma, const char *str)
1372 {
1373   int l;
1374   switch ((ma->flags & SEARCH_STRINGMASK))
1375     {
1376     case SEARCH_SUBSTRING:
1377       if (ma->flags & SEARCH_NOCASE)
1378         return strcasestr(str, ma->match) != 0;
1379       else
1380         return strstr(str, ma->match) != 0;
1381     case SEARCH_STRING:
1382       if (ma->flags & SEARCH_NOCASE)
1383         return !strcasecmp(ma->match, str);
1384       else
1385         return !strcmp(ma->match, str);
1386     case SEARCH_STRINGSTART:
1387       if (ma->flags & SEARCH_NOCASE)
1388         return !strncasecmp(ma->match, str, strlen(ma->match));
1389       else
1390         return !strncmp(ma->match, str, strlen(ma->match));
1391     case SEARCH_STRINGEND:
1392       l = strlen(str) - strlen(ma->match);
1393       if (l < 0)
1394         return 0;
1395       if (ma->flags & SEARCH_NOCASE)
1396         return !strcasecmp(ma->match, str + l);
1397       else
1398         return !strcmp(ma->match, str + l);
1399     case SEARCH_GLOB:
1400       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1401     case SEARCH_REGEX:
1402       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1403     default:
1404       return 0;
1405     }
1406 }
1407
1408 /* check if the matcher can match the provides basename */
1409
1410 int
1411 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1412 {
1413   int l;
1414   const char *match = ma->matchdata;
1415   if (!match)
1416     return 1;
1417   switch (ma->flags & SEARCH_STRINGMASK)
1418     {
1419     case SEARCH_STRING:
1420       break;
1421     case SEARCH_STRINGEND:
1422       if (match != ma->match)
1423         break;          /* had slash, do exact match on basename */
1424       /* FALLTHROUGH */
1425     case SEARCH_GLOB:
1426       /* check if the basename ends with match */
1427       l = strlen(basename) - strlen(match);
1428       if (l < 0)
1429         return 0;
1430       basename += l;
1431       break;
1432     default:
1433       return 1; /* maybe matches */
1434     }
1435   if ((ma->flags & SEARCH_NOCASE) != 0)
1436     return !strcasecmp(match, basename);
1437   else
1438     return !strcmp(match, basename);
1439 }
1440
1441 enum {
1442   di_bye,
1443
1444   di_enterrepo,
1445   di_entersolvable,
1446   di_enterrepodata,
1447   di_enterschema,
1448   di_enterkey,
1449
1450   di_nextattr,
1451   di_nextkey,
1452   di_nextrepodata,
1453   di_nextsolvable,
1454   di_nextrepo,
1455
1456   di_enterarray,
1457   di_nextarrayelement,
1458
1459   di_entersub,
1460   di_leavesub,
1461
1462   di_nextsolvablekey,
1463   di_entersolvablekey,
1464   di_nextsolvableattr
1465 };
1466
1467 /* see dataiterator.h for documentation */
1468 int
1469 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1470 {
1471   memset(di, 0, sizeof(*di));
1472   di->pool = pool;
1473   di->flags = flags & ~SEARCH_THISSOLVID;
1474   if (!pool || (repo && repo->pool != pool))
1475     {
1476       di->state = di_bye;
1477       return -1;
1478     }
1479   if (match)
1480     {
1481       int error;
1482       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1483         {
1484           di->state = di_bye;
1485           return error;
1486         }
1487     }
1488   di->keyname = keyname;
1489   di->keynames[0] = keyname;
1490   dataiterator_set_search(di, repo, p);
1491   return 0;
1492 }
1493
1494 void
1495 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1496 {
1497   *di = *from;
1498   if (di->dupstr)
1499     {
1500       if (di->dupstr == di->kv.str)
1501         di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
1502       else
1503         {
1504           di->dupstr = 0;
1505           di->dupstrn = 0;
1506         }
1507     }
1508   memset(&di->matcher, 0, sizeof(di->matcher));
1509   if (from->matcher.match)
1510     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1511   if (di->nparents)
1512     {
1513       /* fix pointers */
1514       int i;
1515       for (i = 1; i < di->nparents; i++)
1516         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1517       di->kv.parent = &di->parents[di->nparents - 1].kv;
1518     }
1519   if (di->oldkeyskip)
1520     di->oldkeyskip = solv_memdup2(di->oldkeyskip, 3 + di->oldkeyskip[0], sizeof(Id));
1521   if (di->keyskip)
1522     di->keyskip = di->oldkeyskip;
1523 }
1524
1525 int
1526 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1527 {
1528   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1529   datamatcher_free(&di->matcher);
1530   memset(&di->matcher, 0, sizeof(di->matcher));
1531   if (match)
1532     {
1533       int error;
1534       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1535         {
1536           di->state = di_bye;
1537           return error;
1538         }
1539     }
1540   return 0;
1541 }
1542
1543 void
1544 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1545 {
1546   di->repo = repo;
1547   di->repoid = 0;
1548   di->flags &= ~SEARCH_THISSOLVID;
1549   di->nparents = 0;
1550   di->rootlevel = 0;
1551   di->repodataid = 1;
1552   if (!di->pool->urepos)
1553     {
1554       di->state = di_bye;
1555       return;
1556     }
1557   if (!repo)
1558     {
1559       di->repoid = 1;
1560       di->repo = di->pool->repos[di->repoid];
1561     }
1562   di->state = di_enterrepo;
1563   if (p)
1564     dataiterator_jump_to_solvid(di, p);
1565 }
1566
1567 void
1568 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1569 {
1570   di->nkeynames = 0;
1571   di->keyname = keyname;
1572   di->keynames[0] = keyname;
1573 }
1574
1575 void
1576 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1577 {
1578   int i;
1579
1580   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1581     {
1582       di->state = di_bye;       /* sorry */
1583       return;
1584     }
1585   for (i = di->nkeynames + 1; i > 0; i--)
1586     di->keynames[i] = di->keynames[i - 1];
1587   di->keynames[0] = di->keyname = keyname;
1588   di->nkeynames++;
1589 }
1590
1591 void
1592 dataiterator_free(Dataiterator *di)
1593 {
1594   if (di->matcher.match)
1595     datamatcher_free(&di->matcher);
1596   if (di->dupstr)
1597     solv_free(di->dupstr);
1598   if (di->oldkeyskip)
1599     solv_free(di->oldkeyskip);
1600 }
1601
1602 static unsigned char *
1603 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1604 {
1605   Id *keyp;
1606   Repokey *keys = di->data->keys, *key;
1607   unsigned char *dp;
1608
1609   for (keyp = di->keyp; *keyp; keyp++)
1610     if (keys[*keyp].name == keyname)
1611       break;
1612   if (!*keyp)
1613     return 0;
1614   key = keys + *keyp;
1615   if (key->type == REPOKEY_TYPE_DELETED)
1616     return 0;
1617   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
1618     return 0;           /* get_data will not work, no need to forward */
1619   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1620   if (!dp)
1621     return 0;
1622   di->keyp = keyp;
1623   return dp;
1624 }
1625
1626 int
1627 dataiterator_step(Dataiterator *di)
1628 {
1629   Id schema;
1630
1631   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate)
1632     {
1633       unsigned int ddpoff = di->ddp - di->vert_ddp;
1634       di->vert_off += ddpoff;
1635       di->vert_len -= ddpoff;
1636       di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1637       di->vert_storestate = di->data->storestate;
1638       if (!di->ddp)
1639         di->state = di_nextkey;
1640     }
1641   for (;;)
1642     {
1643       switch (di->state)
1644         {
1645         case di_enterrepo: di_enterrepo:
1646           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1647             goto di_nextrepo;
1648           if (!(di->flags & SEARCH_THISSOLVID))
1649             {
1650               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1651               goto di_nextsolvable;
1652             }
1653           /* FALLTHROUGH */
1654
1655         case di_entersolvable: di_entersolvable:
1656           if (!di->repodataid)
1657             goto di_enterrepodata;      /* POS case, repodata is set */
1658           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)
1659             {
1660               extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1661               di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1662               di->data = 0;
1663               goto di_entersolvablekey;
1664             }
1665
1666           if (di->keyname)
1667             {
1668               di->data = di->keyname == SOLVABLE_FILELIST ? repo_lookup_filelist_repodata(di->repo, di->solvid, &di->matcher) : repo_lookup_repodata_opt(di->repo, di->solvid, di->keyname);
1669               if (!di->data)
1670                 goto di_nextsolvable;
1671               di->repodataid = di->data - di->repo->repodata;
1672               di->keyskip = 0;
1673               goto di_enterrepodata;
1674             }
1675         di_leavesolvablekey:
1676           di->repodataid = 1;   /* reset repodata iterator */
1677           di->keyskip = repo_create_keyskip(di->repo, di->solvid, &di->oldkeyskip);
1678           /* FALLTHROUGH */
1679
1680         case di_enterrepodata: di_enterrepodata:
1681           if (di->repodataid)
1682             {
1683               if (di->repodataid >= di->repo->nrepodata)
1684                 goto di_nextsolvable;
1685               di->data = di->repo->repodata + di->repodataid;
1686             }
1687           if (!maybe_load_repodata(di->data, di->keyname))
1688             goto di_nextrepodata;
1689           di->dp = solvid2data(di->data, di->solvid, &schema);
1690           if (!di->dp)
1691             goto di_nextrepodata;
1692           if (di->solvid == SOLVID_POS)
1693             di->solvid = di->pool->pos.solvid;
1694           /* reset key iterator */
1695           di->keyp = di->data->schemadata + di->data->schemata[schema];
1696           /* FALLTHROUGH */
1697
1698         case di_enterschema: di_enterschema:
1699           if (di->keyname)
1700             di->dp = dataiterator_find_keyname(di, di->keyname);
1701           if (!di->dp || !*di->keyp)
1702             {
1703               if (di->kv.parent)
1704                 goto di_leavesub;
1705               goto di_nextrepodata;
1706             }
1707           /* FALLTHROUGH */
1708
1709         case di_enterkey: di_enterkey:
1710           di->kv.entry = -1;
1711           di->key = di->data->keys + *di->keyp;
1712           if (!di->dp)
1713             goto di_nextkey;
1714           /* this is get_data() modified to store vert_ data */
1715           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1716             {
1717               Id off, len;
1718               di->dp = data_read_id(di->dp, &off);
1719               di->dp = data_read_id(di->dp, &len);
1720               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1721               di->vert_off = off;
1722               di->vert_len = len;
1723               di->vert_storestate = di->data->storestate;
1724             }
1725           else if (di->key->storage == KEY_STORAGE_INCORE)
1726             {
1727               di->ddp = di->dp;         /* start of data */
1728               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1729                 di->dp = data_skip_key(di->data, di->dp, di->key);      /* advance to next key */
1730             }
1731           else
1732             di->ddp = 0;
1733           if (!di->ddp)
1734             goto di_nextkey;
1735           if (di->keyskip && (di->key->name >= di->keyskip[0] || di->keyskip[3 + di->key->name] != di->keyskip[1] + di->data->repodataid))
1736             goto di_nextkey;
1737           if (di->key->type == REPOKEY_TYPE_DELETED && !(di->flags & SEARCH_KEEP_TYPE_DELETED))
1738             goto di_nextkey;
1739           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1740             goto di_enterarray;
1741           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1742             goto di_nextkey;
1743           /* FALLTHROUGH */
1744
1745         case di_nextattr:
1746           di->kv.entry++;
1747           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1748           di->state = di->kv.eof ? di_nextkey : di_nextattr;
1749           break;
1750
1751         case di_nextkey: di_nextkey:
1752           if (!di->keyname && *++di->keyp)
1753             goto di_enterkey;
1754           if (di->kv.parent)
1755             goto di_leavesub;
1756           /* FALLTHROUGH */
1757
1758         case di_nextrepodata: di_nextrepodata:
1759           if (!di->keyname && di->repodataid && ++di->repodataid < di->repo->nrepodata)
1760               goto di_enterrepodata;
1761           /* FALLTHROUGH */
1762
1763         case di_nextsolvable: di_nextsolvable:
1764           if (!(di->flags & SEARCH_THISSOLVID))
1765             {
1766               if (di->solvid < 0)
1767                 di->solvid = di->repo->start;
1768               else
1769                 di->solvid++;
1770               for (; di->solvid < di->repo->end; di->solvid++)
1771                 {
1772                   if (di->pool->solvables[di->solvid].repo == di->repo)
1773                     goto di_entersolvable;
1774                 }
1775             }
1776           /* FALLTHROUGH */
1777
1778         case di_nextrepo: di_nextrepo:
1779           if (di->repoid > 0)
1780             {
1781               di->repoid++;
1782               di->repodataid = 1;
1783               if (di->repoid < di->pool->nrepos)
1784                 {
1785                   di->repo = di->pool->repos[di->repoid];
1786                   goto di_enterrepo;
1787                 }
1788             }
1789         /* FALLTHROUGH */
1790
1791         case di_bye: di_bye:
1792           di->state = di_bye;
1793           return 0;
1794
1795         case di_enterarray: di_enterarray:
1796           if (di->key->name == REPOSITORY_SOLVABLES)
1797             goto di_nextkey;
1798           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1799           di->kv.eof = 0;
1800           di->kv.entry = -1;
1801           /* FALLTHROUGH */
1802
1803         case di_nextarrayelement: di_nextarrayelement:
1804           di->kv.entry++;
1805           if (di->kv.entry)
1806             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1807           if (di->kv.entry == di->kv.num)
1808             {
1809               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1810                 goto di_nextkey;
1811               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1812                 goto di_nextkey;
1813               di->kv.str = (char *)di->ddp;
1814               di->kv.eof = 2;
1815               di->state = di_nextkey;
1816               break;
1817             }
1818           if (di->kv.entry == di->kv.num - 1)
1819             di->kv.eof = 1;
1820           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1821             di->ddp = data_read_id(di->ddp, &di->kv.id);
1822           di->kv.str = (char *)di->ddp;
1823           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1824             goto di_entersub;
1825           if ((di->flags & SEARCH_SUB) != 0)
1826             di->state = di_entersub;
1827           else
1828             di->state = di_nextarrayelement;
1829           break;
1830
1831         case di_entersub: di_entersub:
1832           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1833             goto di_nextarrayelement;   /* sorry, full */
1834           di->parents[di->nparents].kv = di->kv;
1835           di->parents[di->nparents].dp = di->dp;
1836           di->parents[di->nparents].keyp = di->keyp;
1837           di->dp = (unsigned char *)di->kv.str;
1838           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1839           memset(&di->kv, 0, sizeof(di->kv));
1840           di->kv.parent = &di->parents[di->nparents].kv;
1841           di->nparents++;
1842           di->keyname = di->keynames[di->nparents - di->rootlevel];
1843           goto di_enterschema;
1844
1845         case di_leavesub: di_leavesub:
1846           if (di->nparents - 1 < di->rootlevel)
1847             goto di_bye;
1848           di->nparents--;
1849           di->dp = di->parents[di->nparents].dp;
1850           di->kv = di->parents[di->nparents].kv;
1851           di->keyp = di->parents[di->nparents].keyp;
1852           di->key = di->data->keys + *di->keyp;
1853           di->ddp = (unsigned char *)di->kv.str;
1854           di->keyname = di->keynames[di->nparents - di->rootlevel];
1855           goto di_nextarrayelement;
1856
1857         /* special solvable attr handling follows */
1858
1859         case di_nextsolvablekey: di_nextsolvablekey:
1860           if (di->keyname)
1861             goto di_nextsolvable;
1862           if (di->key->name == RPM_RPMDBID)     /* reached end of list? */
1863             goto di_leavesolvablekey;
1864           di->key++;
1865           /* FALLTHROUGH */
1866
1867         case di_entersolvablekey: di_entersolvablekey:
1868           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1869           if (!di->idp || !*di->idp)
1870             goto di_nextsolvablekey;
1871           if (di->kv.eof)
1872             {
1873               /* not an array */
1874               di->kv.id = *di->idp;
1875               di->kv.num = *di->idp;    /* for rpmdbid */
1876               di->kv.num2 = 0;          /* for rpmdbid */
1877               di->kv.entry = 0;
1878               di->state = di_nextsolvablekey;
1879               break;
1880             }
1881           di->kv.entry = -1;
1882           /* FALLTHROUGH */
1883
1884         case di_nextsolvableattr:
1885           di->state = di_nextsolvableattr;
1886           di->kv.id = *di->idp++;
1887           di->kv.entry++;
1888           if (!*di->idp)
1889             {
1890               di->kv.eof = 1;
1891               di->state = di_nextsolvablekey;
1892             }
1893           break;
1894
1895         }
1896
1897       /* we have a potential match */
1898       if (di->matcher.match)
1899         {
1900           const char *str;
1901           /* simple pre-check so that we don't need to stringify */
1902           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1903             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1904               continue;
1905           /* now stringify so that we can do the matching */
1906           if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
1907             {
1908               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1909                 return 1;
1910               continue;
1911             }
1912           if (!datamatcher_match(&di->matcher, str))
1913             continue;
1914         }
1915       else
1916         {
1917           /* stringify filelist if requested */
1918           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1919             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1920         }
1921       /* found something! */
1922       return 1;
1923     }
1924 }
1925
1926 void
1927 dataiterator_entersub(Dataiterator *di)
1928 {
1929   if (di->state == di_nextarrayelement)
1930     di->state = di_entersub;
1931 }
1932
1933 void
1934 dataiterator_setpos(Dataiterator *di)
1935 {
1936   if (di->kv.eof == 2)
1937     {
1938       pool_clear_pos(di->pool);
1939       return;
1940     }
1941   di->pool->pos.solvid = di->solvid;
1942   di->pool->pos.repo = di->repo;
1943   di->pool->pos.repodataid = di->data - di->repo->repodata;
1944   di->pool->pos.schema = di->kv.id;
1945   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1946 }
1947
1948 void
1949 dataiterator_setpos_parent(Dataiterator *di)
1950 {
1951   if (!di->kv.parent || di->kv.parent->eof == 2)
1952     {
1953       pool_clear_pos(di->pool);
1954       return;
1955     }
1956   di->pool->pos.solvid = di->solvid;
1957   di->pool->pos.repo = di->repo;
1958   di->pool->pos.repodataid = di->data - di->repo->repodata;
1959   di->pool->pos.schema = di->kv.parent->id;
1960   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1961 }
1962
1963 /* clones just the position, not the search keys/matcher */
1964 void
1965 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1966 {
1967   di->state = from->state;
1968   di->flags &= ~SEARCH_THISSOLVID;
1969   di->flags |= (from->flags & SEARCH_THISSOLVID);
1970   di->repo = from->repo;
1971   di->data = from->data;
1972   di->dp = from->dp;
1973   di->ddp = from->ddp;
1974   di->idp = from->idp;
1975   di->keyp = from->keyp;
1976   di->key = from->key;
1977   di->kv = from->kv;
1978   di->repodataid = from->repodataid;
1979   di->solvid = from->solvid;
1980   di->repoid = from->repoid;
1981   di->rootlevel = from->rootlevel;
1982   memcpy(di->parents, from->parents, sizeof(from->parents));
1983   di->nparents = from->nparents;
1984   if (di->nparents)
1985     {
1986       int i;
1987       for (i = 1; i < di->nparents; i++)
1988         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1989       di->kv.parent = &di->parents[di->nparents - 1].kv;
1990     }
1991   di->dupstr = 0;
1992   di->dupstrn = 0;
1993   if (from->dupstr && from->dupstr == from->kv.str)
1994     {
1995       di->dupstrn = from->dupstrn;
1996       di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
1997     }
1998 }
1999
2000 void
2001 dataiterator_seek(Dataiterator *di, int whence)
2002 {
2003   if ((whence & DI_SEEK_STAY) != 0)
2004     di->rootlevel = di->nparents;
2005   switch (whence & ~DI_SEEK_STAY)
2006     {
2007     case DI_SEEK_CHILD:
2008       if (di->state != di_nextarrayelement)
2009         break;
2010       if ((whence & DI_SEEK_STAY) != 0)
2011         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
2012       di->state = di_entersub;
2013       break;
2014     case DI_SEEK_PARENT:
2015       if (!di->nparents)
2016         {
2017           di->state = di_bye;
2018           break;
2019         }
2020       di->nparents--;
2021       if (di->rootlevel > di->nparents)
2022         di->rootlevel = di->nparents;
2023       di->dp = di->parents[di->nparents].dp;
2024       di->kv = di->parents[di->nparents].kv;
2025       di->keyp = di->parents[di->nparents].keyp;
2026       di->key = di->data->keys + *di->keyp;
2027       di->ddp = (unsigned char *)di->kv.str;
2028       di->keyname = di->keynames[di->nparents - di->rootlevel];
2029       di->state = di_nextarrayelement;
2030       break;
2031     case DI_SEEK_REWIND:
2032       if (!di->nparents)
2033         {
2034           di->state = di_bye;
2035           break;
2036         }
2037       di->dp = (unsigned char *)di->kv.parent->str;
2038       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
2039       di->state = di_enterschema;
2040       break;
2041     default:
2042       break;
2043     }
2044 }
2045
2046 void
2047 dataiterator_skip_attribute(Dataiterator *di)
2048 {
2049   if (di->state == di_nextsolvableattr)
2050     di->state = di_nextsolvablekey;
2051   else
2052     di->state = di_nextkey;
2053 }
2054
2055 void
2056 dataiterator_skip_solvable(Dataiterator *di)
2057 {
2058   di->nparents = 0;
2059   di->kv.parent = 0;
2060   di->rootlevel = 0;
2061   di->keyname = di->keynames[0];
2062   di->state = di_nextsolvable;
2063 }
2064
2065 void
2066 dataiterator_skip_repo(Dataiterator *di)
2067 {
2068   di->nparents = 0;
2069   di->kv.parent = 0;
2070   di->rootlevel = 0;
2071   di->keyname = di->keynames[0];
2072   di->state = di_nextrepo;
2073 }
2074
2075 void
2076 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
2077 {
2078   di->nparents = 0;
2079   di->kv.parent = 0;
2080   di->rootlevel = 0;
2081   di->keyname = di->keynames[0];
2082   if (solvid == SOLVID_POS)
2083     {
2084       di->repo = di->pool->pos.repo;
2085       if (!di->repo)
2086         {
2087           di->state = di_bye;
2088           return;
2089         }
2090       di->repoid = 0;
2091       if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
2092         solvid = SOLVID_META;           /* META pos hack */
2093       } else {
2094         di->data = di->repo->repodata + di->pool->pos.repodataid;
2095         di->repodataid = 0;
2096       }
2097     }
2098   else if (solvid > 0)
2099     {
2100       di->repo = di->pool->solvables[solvid].repo;
2101       di->repoid = 0;
2102     }
2103   if (di->repoid > 0)
2104     {
2105       if (!di->pool->urepos)
2106         {
2107           di->state = di_bye;
2108           return;
2109         }
2110       di->repoid = 1;
2111       di->repo = di->pool->repos[di->repoid];
2112     }
2113   if (solvid != SOLVID_POS)
2114     di->repodataid = 1;
2115   di->solvid = solvid;
2116   if (solvid)
2117     di->flags |= SEARCH_THISSOLVID;
2118   di->state = di_enterrepo;
2119 }
2120
2121 void
2122 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
2123 {
2124   di->nparents = 0;
2125   di->kv.parent = 0;
2126   di->rootlevel = 0;
2127   di->repo = repo;
2128   di->repoid = 0;       /* 0 means stay at repo */
2129   di->repodataid = 1;
2130   di->solvid = 0;
2131   di->flags &= ~SEARCH_THISSOLVID;
2132   di->state = di_enterrepo;
2133 }
2134
2135 int
2136 dataiterator_match(Dataiterator *di, Datamatcher *ma)
2137 {
2138   const char *str;
2139   if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
2140     return 0;
2141   return ma ? datamatcher_match(ma, str) : 1;
2142 }
2143
2144 void
2145 dataiterator_strdup(Dataiterator *di)
2146 {
2147   int l = -1;
2148
2149   if (!di->kv.str || di->kv.str == di->dupstr)
2150     return;
2151   switch (di->key->type)
2152     {
2153     case_CHKSUM_TYPES:
2154     case REPOKEY_TYPE_DIRSTRARRAY:
2155       if (di->kv.num)   /* was it stringified into tmp space? */
2156         l = strlen(di->kv.str) + 1;
2157       break;
2158     default:
2159       break;
2160     }
2161   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2162     {
2163       switch (di->key->type)
2164         {
2165         case REPOKEY_TYPE_STR:
2166         case REPOKEY_TYPE_DIRSTRARRAY:
2167           l = strlen(di->kv.str) + 1;
2168           break;
2169         case_CHKSUM_TYPES:
2170           l = solv_chksum_len(di->key->type);
2171           break;
2172         case REPOKEY_TYPE_BINARY:
2173           l = di->kv.num;
2174           break;
2175         }
2176     }
2177   if (l >= 0)
2178     {
2179       if (!di->dupstrn || di->dupstrn < l)
2180         {
2181           di->dupstrn = l + 16;
2182           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
2183         }
2184       if (l)
2185         memcpy(di->dupstr, di->kv.str, l);
2186       di->kv.str = di->dupstr;
2187     }
2188 }
2189
2190 /************************************************************************
2191  * data modify functions
2192  */
2193
2194 /* extend repodata so that it includes solvables p */
2195 void
2196 repodata_extend(Repodata *data, Id p)
2197 {
2198   if (data->start == data->end)
2199     data->start = data->end = p;
2200   if (p >= data->end)
2201     {
2202       int old = data->end - data->start;
2203       int new = p - data->end + 1;
2204       if (data->attrs)
2205         {
2206           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
2207           memset(data->attrs + old, 0, new * sizeof(Id *));
2208         }
2209       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
2210       memset(data->incoreoffset + old, 0, new * sizeof(Id));
2211       data->end = p + 1;
2212     }
2213   if (p < data->start)
2214     {
2215       int old = data->end - data->start;
2216       int new = data->start - p;
2217       if (data->attrs)
2218         {
2219           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
2220           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
2221           memset(data->attrs, 0, new * sizeof(Id *));
2222         }
2223       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
2224       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
2225       memset(data->incoreoffset, 0, new * sizeof(Id));
2226       data->start = p;
2227     }
2228 }
2229
2230 /* shrink end of repodata */
2231 void
2232 repodata_shrink(Repodata *data, int end)
2233 {
2234   int i;
2235
2236   if (data->end <= end)
2237     return;
2238   if (data->start >= end)
2239     {
2240       if (data->attrs)
2241         {
2242           for (i = 0; i < data->end - data->start; i++)
2243             solv_free(data->attrs[i]);
2244           data->attrs = solv_free(data->attrs);
2245         }
2246       data->incoreoffset = solv_free(data->incoreoffset);
2247       data->start = data->end = 0;
2248       return;
2249     }
2250   if (data->attrs)
2251     {
2252       for (i = end; i < data->end; i++)
2253         solv_free(data->attrs[i - data->start]);
2254       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2255     }
2256   if (data->incoreoffset)
2257     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2258   data->end = end;
2259 }
2260
2261 /* extend repodata so that it includes solvables from start to start + num - 1 */
2262 void
2263 repodata_extend_block(Repodata *data, Id start, Id num)
2264 {
2265   if (!num)
2266     return;
2267   if (!data->incoreoffset)
2268     {
2269       /* this also means that data->attrs is NULL */
2270       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2271       data->start = start;
2272       data->end = start + num;
2273       return;
2274     }
2275   repodata_extend(data, start);
2276   if (num > 1)
2277     repodata_extend(data, start + num - 1);
2278 }
2279
2280 /**********************************************************************/
2281
2282
2283 #define REPODATA_ATTRS_BLOCK 31
2284 #define REPODATA_ATTRDATA_BLOCK 1023
2285 #define REPODATA_ATTRIDDATA_BLOCK 63
2286 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2287
2288
2289 Id
2290 repodata_new_handle(Repodata *data)
2291 {
2292   if (!data->nxattrs)
2293     {
2294       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2295       data->nxattrs = 2;        /* -1: SOLVID_META */
2296     }
2297   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2298   data->xattrs[data->nxattrs] = 0;
2299   return -(data->nxattrs++);
2300 }
2301
2302 static inline Id **
2303 repodata_get_attrp(Repodata *data, Id handle)
2304 {
2305   if (handle < 0)
2306     {
2307       if (handle == SOLVID_META && !data->xattrs)
2308         {
2309           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2310           data->nxattrs = 2;
2311         }
2312       return data->xattrs - handle;
2313     }
2314   if (handle < data->start || handle >= data->end)
2315     repodata_extend(data, handle);
2316   if (!data->attrs)
2317     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2318   return data->attrs + (handle - data->start);
2319 }
2320
2321 static void
2322 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2323 {
2324   Id *pp;
2325   Id *ap, **app;
2326   int i;
2327
2328   app = repodata_get_attrp(data, handle);
2329   ap = *app;
2330   i = 0;
2331   if (ap)
2332     {
2333       /* Determine equality based on the name only, allows us to change
2334          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2335       for (pp = ap; *pp; pp += 2)
2336         if (data->keys[*pp].name == data->keys[keyid].name)
2337           break;
2338       if (*pp)
2339         {
2340           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2341             {
2342               pp[0] = keyid;
2343               pp[1] = val;
2344             }
2345           return;
2346         }
2347       i = pp - ap;
2348     }
2349   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2350   *app = ap;
2351   pp = ap + i;
2352   *pp++ = keyid;
2353   *pp++ = val;
2354   *pp = 0;
2355 }
2356
2357
2358 static void
2359 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2360 {
2361   Id keyid;
2362
2363   keyid = repodata_key2id(data, key, 1);
2364   repodata_insert_keyid(data, solvid, keyid, val, 1);
2365 }
2366
2367 void
2368 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2369 {
2370   Repokey key;
2371   key.name = keyname;
2372   key.type = REPOKEY_TYPE_ID;
2373   key.size = 0;
2374   key.storage = KEY_STORAGE_INCORE;
2375   repodata_set(data, solvid, &key, id);
2376 }
2377
2378 void
2379 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2380 {
2381   Repokey key;
2382   key.name = keyname;
2383   key.type = REPOKEY_TYPE_NUM;
2384   key.size = 0;
2385   key.storage = KEY_STORAGE_INCORE;
2386   if (num >= 0x80000000)
2387     {
2388       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2389       data->attrnum64data[data->attrnum64datalen] = num;
2390       num = 0x80000000 | data->attrnum64datalen++;
2391     }
2392   repodata_set(data, solvid, &key, (Id)num);
2393 }
2394
2395 void
2396 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2397 {
2398   Repokey key;
2399   Id id;
2400   if (data->localpool)
2401     id = stringpool_str2id(&data->spool, str, 1);
2402   else
2403     id = pool_str2id(data->repo->pool, str, 1);
2404   key.name = keyname;
2405   key.type = REPOKEY_TYPE_ID;
2406   key.size = 0;
2407   key.storage = KEY_STORAGE_INCORE;
2408   repodata_set(data, solvid, &key, id);
2409 }
2410
2411 void
2412 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2413 {
2414   Repokey key;
2415   key.name = keyname;
2416   key.type = REPOKEY_TYPE_CONSTANT;
2417   key.size = constant;
2418   key.storage = KEY_STORAGE_INCORE;
2419   repodata_set(data, solvid, &key, 0);
2420 }
2421
2422 void
2423 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2424 {
2425   Repokey key;
2426   key.name = keyname;
2427   key.type = REPOKEY_TYPE_CONSTANTID;
2428   key.size = id;
2429   key.storage = KEY_STORAGE_INCORE;
2430   repodata_set(data, solvid, &key, 0);
2431 }
2432
2433 void
2434 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2435 {
2436   Repokey key;
2437   key.name = keyname;
2438   key.type = REPOKEY_TYPE_VOID;
2439   key.size = 0;
2440   key.storage = KEY_STORAGE_INCORE;
2441   repodata_set(data, solvid, &key, 0);
2442 }
2443
2444 void
2445 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2446 {
2447   Repokey key;
2448   int l;
2449
2450   l = strlen(str) + 1;
2451   key.name = keyname;
2452   key.type = REPOKEY_TYPE_STR;
2453   key.size = 0;
2454   key.storage = KEY_STORAGE_INCORE;
2455   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2456   memcpy(data->attrdata + data->attrdatalen, str, l);
2457   repodata_set(data, solvid, &key, data->attrdatalen);
2458   data->attrdatalen += l;
2459 }
2460
2461 void
2462 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2463 {
2464   Repokey key;
2465   unsigned char *dp;
2466
2467   if (len < 0)
2468     return;
2469   key.name = keyname;
2470   key.type = REPOKEY_TYPE_BINARY;
2471   key.size = 0;
2472   key.storage = KEY_STORAGE_INCORE;
2473   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2474   dp = data->attrdata + data->attrdatalen;
2475   if (len >= (1 << 14))
2476     {
2477       if (len >= (1 << 28))
2478         *dp++ = (len >> 28) | 128;
2479       if (len >= (1 << 21))
2480         *dp++ = (len >> 21) | 128;
2481       *dp++ = (len >> 14) | 128;
2482     }
2483   if (len >= (1 << 7))
2484     *dp++ = (len >> 7) | 128;
2485   *dp++ = len & 127;
2486   if (len)
2487     memcpy(dp, buf, len);
2488   repodata_set(data, solvid, &key, data->attrdatalen);
2489   data->attrdatalen = dp + len - data->attrdata;
2490 }
2491
2492 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2493  * so that the caller can append entrysize new elements plus the termination zero there */
2494 static void
2495 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2496 {
2497   int oldsize;
2498   Id *ida, *pp, **ppp;
2499
2500   /* check if it is the same as last time, this speeds things up a lot */
2501   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2502     {
2503       /* great! just append the new data */
2504       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2505       data->attriddatalen--;    /* overwrite terminating 0  */
2506       data->lastdatalen += entrysize;
2507       return;
2508     }
2509
2510   ppp = repodata_get_attrp(data, handle);
2511   pp = *ppp;
2512   if (pp)
2513     {
2514       for (; *pp; pp += 2)
2515         if (data->keys[*pp].name == keyname)
2516           break;
2517     }
2518   if (!pp || !*pp || data->keys[*pp].type != keytype)
2519     {
2520       /* not found. allocate new key */
2521       Repokey key;
2522       Id keyid;
2523       key.name = keyname;
2524       key.type = keytype;
2525       key.size = 0;
2526       key.storage = KEY_STORAGE_INCORE;
2527       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2528       keyid = repodata_key2id(data, &key, 1);
2529       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2530       data->lasthandle = handle;
2531       data->lastkey = keyid;
2532       data->lastdatalen = data->attriddatalen + entrysize + 1;
2533       return;
2534     }
2535   oldsize = 0;
2536   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2537     oldsize += entrysize;
2538   if (ida + 1 == data->attriddata + data->attriddatalen)
2539     {
2540       /* this was the last entry, just append it */
2541       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2542       data->attriddatalen--;    /* overwrite terminating 0  */
2543     }
2544   else
2545     {
2546       /* too bad. move to back. */
2547       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2548       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2549       pp[1] = data->attriddatalen;
2550       data->attriddatalen += oldsize;
2551     }
2552   data->lasthandle = handle;
2553   data->lastkey = *pp;
2554   data->lastdatalen = data->attriddatalen + entrysize + 1;
2555 }
2556
2557 void
2558 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2559                       const unsigned char *str)
2560 {
2561   Repokey key;
2562   int l;
2563
2564   if (!(l = solv_chksum_len(type)))
2565     return;
2566   key.name = keyname;
2567   key.type = type;
2568   key.size = 0;
2569   key.storage = KEY_STORAGE_INCORE;
2570   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2571   memcpy(data->attrdata + data->attrdatalen, str, l);
2572   repodata_set(data, solvid, &key, data->attrdatalen);
2573   data->attrdatalen += l;
2574 }
2575
2576 void
2577 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2578                       const char *str)
2579 {
2580   unsigned char buf[64];
2581   int l;
2582
2583   if (!(l = solv_chksum_len(type)))
2584     return;
2585   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2586     return;
2587   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2588 }
2589
2590 const char *
2591 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2592 {
2593   int l;
2594
2595   if (!(l = solv_chksum_len(type)))
2596     return "";
2597   return pool_bin2hex(data->repo->pool, buf, l);
2598 }
2599
2600 /* rpm filenames don't contain the epoch, so strip it */
2601 static inline const char *
2602 evrid2vrstr(Pool *pool, Id evrid)
2603 {
2604   const char *p, *evr = pool_id2str(pool, evrid);
2605   if (!evr)
2606     return evr;
2607   for (p = evr; *p >= '0' && *p <= '9'; p++)
2608     ;
2609   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2610 }
2611
2612 static inline void
2613 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2614 {
2615   Id id;
2616   if (data->localpool)
2617     id = stringpool_strn2id(&data->spool, str, l, 1);
2618   else
2619     id = pool_strn2id(data->repo->pool, str, l, 1);
2620   repodata_set_id(data, solvid, keyname, id);
2621 }
2622
2623 static inline void
2624 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2625 {
2626   if (!str[l])
2627     repodata_set_str(data, solvid, keyname, str);
2628   else
2629     {
2630       char *s = solv_strdup(str);
2631       s[l] = 0;
2632       repodata_set_str(data, solvid, keyname, s);
2633       free(s);
2634     }
2635 }
2636
2637 void
2638 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2639 {
2640   Pool *pool = data->repo->pool;
2641   Solvable *s;
2642   const char *str, *fp;
2643   int l = 0;
2644
2645   if (medianr)
2646     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2647   if (!dir)
2648     {
2649       if ((dir = strrchr(file, '/')) != 0)
2650         {
2651           l = dir - file;
2652           dir = file;
2653           file = dir + l + 1;
2654           if (!l)
2655             l++;
2656         }
2657     }
2658   else
2659     l = strlen(dir);
2660   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2661     {
2662       dir += 2;
2663       l -= 2;
2664     }
2665   if (l == 1 && dir[0] == '.')
2666     l = 0;
2667   s = pool->solvables + solvid;
2668   if (dir && l)
2669     {
2670       str = pool_id2str(pool, s->arch);
2671       if (!strncmp(dir, str, l) && !str[l])
2672         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2673       else
2674         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2675     }
2676   fp = file;
2677   str = pool_id2str(pool, s->name);
2678   l = strlen(str);
2679   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2680     {
2681       fp += l + 1;
2682       str = evrid2vrstr(pool, s->evr);
2683       l = strlen(str);
2684       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2685         {
2686           fp += l + 1;
2687           str = pool_id2str(pool, s->arch);
2688           l = strlen(str);
2689           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2690             {
2691               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2692               return;
2693             }
2694         }
2695     }
2696   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2697 }
2698
2699 /* XXX: medianr is currently not stored */
2700 void
2701 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2702 {
2703   int l = 0;
2704   const char *evr, *suf, *s;
2705
2706   if (!dir)
2707     {
2708       if ((dir = strrchr(file, '/')) != 0)
2709         {
2710           l = dir - file;
2711           dir = file;
2712           file = dir + l + 1;
2713           if (!l)
2714             l++;
2715         }
2716     }
2717   else
2718     l = strlen(dir);
2719   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2720     {
2721       dir += 2;
2722       l -= 2;
2723     }
2724   if (l == 1 && dir[0] == '.')
2725     l = 0;
2726   if (dir && l)
2727     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2728   evr = strchr(file, '-');
2729   if (evr)
2730     {
2731       for (s = evr - 1; s > file; s--)
2732         if (*s == '-')
2733           {
2734             evr = s;
2735             break;
2736           }
2737     }
2738   suf = strrchr(file, '.');
2739   if (suf)
2740     {
2741       for (s = suf - 1; s > file; s--)
2742         if (*s == '.')
2743           {
2744             suf = s;
2745             break;
2746           }
2747       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2748         {
2749           /* We accept one more item as suffix.  */
2750           for (s = suf - 1; s > file; s--)
2751             if (*s == '.')
2752               {
2753                 suf = s;
2754                 break;
2755               }
2756         }
2757     }
2758   if (!evr)
2759     suf = 0;
2760   if (suf && evr && suf < evr)
2761     suf = 0;
2762   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2763   if (evr)
2764     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2765   if (suf)
2766     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2767 }
2768
2769 void
2770 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2771 {
2772   Pool *pool = data->repo->pool;
2773   Solvable *s = pool->solvables + solvid;
2774   const char *p, *sevr, *sarch, *name, *evr;
2775
2776   p = strrchr(sourcepkg, '.');
2777   if (!p || strcmp(p, ".rpm") != 0)
2778     {
2779       if (*sourcepkg)
2780         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2781       return;
2782     }
2783   p--;
2784   while (p > sourcepkg && *p != '.')
2785     p--;
2786   if (*p != '.' || p == sourcepkg)
2787     return;
2788   sarch = p-- + 1;
2789   while (p > sourcepkg && *p != '-')
2790     p--;
2791   if (*p != '-' || p == sourcepkg)
2792     return;
2793   p--;
2794   while (p > sourcepkg && *p != '-')
2795     p--;
2796   if (*p != '-' || p == sourcepkg)
2797     return;
2798   sevr = p + 1;
2799   pool = s->repo->pool;
2800
2801   name = pool_id2str(pool, s->name);
2802   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2803     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2804   else
2805     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2806
2807   evr = evrid2vrstr(pool, s->evr);
2808   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2809     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2810   else
2811     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2812
2813   if (!strcmp(sarch, "src.rpm"))
2814     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2815   else if (!strcmp(sarch, "nosrc.rpm"))
2816     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2817   else
2818     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2819 }
2820
2821 void
2822 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2823 {
2824   Repokey key;
2825   int i;
2826
2827   key.name = keyname;
2828   key.type = REPOKEY_TYPE_IDARRAY;
2829   key.size = 0;
2830   key.storage = KEY_STORAGE_INCORE;
2831   repodata_set(data, solvid, &key, data->attriddatalen);
2832   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2833   for (i = 0; i < q->count; i++)
2834     data->attriddata[data->attriddatalen++] = q->elements[i];
2835   data->attriddata[data->attriddatalen++] = 0;
2836 }
2837
2838 void
2839 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2840 {
2841   assert(dir);
2842 #if 0
2843 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2844 #endif
2845   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2846   data->attriddata[data->attriddatalen++] = dir;
2847   data->attriddata[data->attriddatalen++] = num;
2848   data->attriddata[data->attriddatalen++] = num2;
2849   data->attriddata[data->attriddatalen++] = 0;
2850 }
2851
2852 void
2853 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2854 {
2855   Id stroff;
2856   int l;
2857
2858   assert(dir);
2859   l = strlen(str) + 1;
2860   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2861   memcpy(data->attrdata + data->attrdatalen, str, l);
2862   stroff = data->attrdatalen;
2863   data->attrdatalen += l;
2864
2865 #if 0
2866 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2867 #endif
2868   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2869   data->attriddata[data->attriddatalen++] = dir;
2870   data->attriddata[data->attriddatalen++] = stroff;
2871   data->attriddata[data->attriddatalen++] = 0;
2872 }
2873
2874 void
2875 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2876 {
2877 #if 0
2878 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2879 #endif
2880   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2881   data->attriddata[data->attriddatalen++] = id;
2882   data->attriddata[data->attriddatalen++] = 0;
2883 }
2884
2885 void
2886 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2887                            const char *str)
2888 {
2889   Id id;
2890   if (data->localpool)
2891     id = stringpool_str2id(&data->spool, str, 1);
2892   else
2893     id = pool_str2id(data->repo->pool, str, 1);
2894   repodata_add_idarray(data, solvid, keyname, id);
2895 }
2896
2897 void
2898 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id handle)
2899 {
2900   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2901   data->attriddata[data->attriddatalen++] = handle;
2902   data->attriddata[data->attriddatalen++] = 0;
2903 }
2904
2905 void
2906 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id handle)
2907 {
2908   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2909   data->attriddata[data->attriddatalen++] = handle;
2910   data->attriddata[data->attriddatalen++] = 0;
2911 }
2912
2913 void
2914 repodata_set_kv(Repodata *data, Id solvid, Id keyname, Id keytype, KeyValue *kv)
2915 {
2916   switch (keytype)
2917     {
2918     case REPOKEY_TYPE_ID:
2919       repodata_set_id(data, solvid, keyname, kv->id);
2920       break;
2921     case REPOKEY_TYPE_CONSTANTID:
2922       repodata_set_constantid(data, solvid, keyname, kv->id);
2923       break;
2924     case REPOKEY_TYPE_IDARRAY:
2925       repodata_add_idarray(data, solvid, keyname, kv->id);
2926       break;
2927     case REPOKEY_TYPE_STR:
2928       repodata_set_str(data, solvid, keyname, kv->str);
2929       break;
2930     case REPOKEY_TYPE_VOID:
2931       repodata_set_void(data, solvid, keyname);
2932       break;
2933     case REPOKEY_TYPE_NUM:
2934       repodata_set_num(data, solvid, keyname, SOLV_KV_NUM64(kv));
2935       break;
2936     case REPOKEY_TYPE_CONSTANT:
2937       repodata_set_constant(data, solvid, keyname, kv->num);
2938       break;
2939     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2940       if (kv->id)
2941         repodata_add_dirnumnum(data, solvid, keyname, kv->id, kv->num, kv->num2);
2942       break;
2943     case REPOKEY_TYPE_DIRSTRARRAY:
2944       repodata_add_dirstr(data, solvid, keyname, kv->id, kv->str);
2945       break;
2946     case_CHKSUM_TYPES:
2947       repodata_set_bin_checksum(data, solvid, keyname, keytype, (const unsigned char *)kv->str);
2948       break;
2949     default:
2950       break;
2951     }
2952 }
2953
2954 void
2955 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2956 {
2957   Id *pp, *ap, **app;
2958   app = repodata_get_attrp(data, solvid);
2959   ap = *app;
2960   if (!ap)
2961     return;
2962   for (; *ap; ap += 2)
2963     if (data->keys[*ap].name == keyname)
2964       break;
2965   if (!*ap)
2966     return;
2967   pp = ap;
2968   ap += 2;
2969   for (; *ap; ap += 2)
2970     {
2971       if (data->keys[*ap].name == keyname)
2972         continue;
2973       *pp++ = ap[0];
2974       *pp++ = ap[1];
2975     }
2976   *pp = 0;
2977 }
2978
2979 void
2980 repodata_unset(Repodata *data, Id solvid, Id keyname)
2981 {
2982   Repokey key;
2983   key.name = keyname;
2984   key.type = REPOKEY_TYPE_DELETED;
2985   key.size = 0;
2986   key.storage = KEY_STORAGE_INCORE;
2987   repodata_set(data, solvid, &key, 0);
2988 }
2989
2990 /* add all (uninternalized) attrs from src to dest */
2991 void
2992 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2993 {
2994   Id *keyp;
2995   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2996     return;
2997   for (; *keyp; keyp += 2)
2998     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2999 }
3000
3001 /* add some (uninternalized) attrs from src to dest */
3002 void
3003 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
3004 {
3005   Id *keyp;
3006   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
3007     return;
3008   for (; *keyp; keyp += 2)
3009     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
3010       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
3011 }
3012
3013 /* swap (uninternalized) attrs from src and dest */
3014 void
3015 repodata_swap_attrs(Repodata *data, Id dest, Id src)
3016 {
3017   Id *tmpattrs;
3018   if (!data->attrs || dest == src)
3019     return;
3020   if (dest < data->start || dest >= data->end)
3021     repodata_extend(data, dest);
3022   if (src < data->start || src >= data->end)
3023     repodata_extend(data, src);
3024   tmpattrs = data->attrs[dest - data->start];
3025   data->attrs[dest - data->start] = data->attrs[src - data->start];
3026   data->attrs[src - data->start] = tmpattrs;
3027 }
3028
3029
3030 /**********************************************************************/
3031
3032 /* TODO: unify with repo_write and repo_solv! */
3033
3034 #define EXTDATA_BLOCK 1023
3035
3036 struct extdata {
3037   unsigned char *buf;
3038   int len;
3039 };
3040
3041 static void
3042 data_addid(struct extdata *xd, Id sx)
3043 {
3044   unsigned int x = (unsigned int)sx;
3045   unsigned char *dp;
3046
3047   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
3048   dp = xd->buf + xd->len;
3049
3050   if (x >= (1 << 14))
3051     {
3052       if (x >= (1 << 28))
3053         *dp++ = (x >> 28) | 128;
3054       if (x >= (1 << 21))
3055         *dp++ = (x >> 21) | 128;
3056       *dp++ = (x >> 14) | 128;
3057     }
3058   if (x >= (1 << 7))
3059     *dp++ = (x >> 7) | 128;
3060   *dp++ = x & 127;
3061   xd->len = dp - xd->buf;
3062 }
3063
3064 static void
3065 data_addid64(struct extdata *xd, unsigned long long x)
3066 {
3067   if (x >= 0x100000000)
3068     {
3069       if ((x >> 35) != 0)
3070         {
3071           data_addid(xd, (Id)(x >> 35));
3072           xd->buf[xd->len - 1] |= 128;
3073         }
3074       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
3075       xd->buf[xd->len - 5] = (x >> 28) | 128;
3076     }
3077   else
3078     data_addid(xd, (Id)x);
3079 }
3080
3081 static void
3082 data_addideof(struct extdata *xd, Id sx, int eof)
3083 {
3084   unsigned int x = (unsigned int)sx;
3085   unsigned char *dp;
3086
3087   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
3088   dp = xd->buf + xd->len;
3089
3090   if (x >= (1 << 13))
3091     {
3092       if (x >= (1 << 27))
3093         *dp++ = (x >> 27) | 128;
3094       if (x >= (1 << 20))
3095         *dp++ = (x >> 20) | 128;
3096       *dp++ = (x >> 13) | 128;
3097     }
3098   if (x >= (1 << 6))
3099     *dp++ = (x >> 6) | 128;
3100   *dp++ = eof ? (x & 63) : (x & 63) | 64;
3101   xd->len = dp - xd->buf;
3102 }
3103
3104 static void
3105 data_addblob(struct extdata *xd, unsigned char *blob, int len)
3106 {
3107   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
3108   memcpy(xd->buf + xd->len, blob, len);
3109   xd->len += len;
3110 }
3111
3112 /*********************************/
3113
3114 /* this is to reduct memory usage when internalizing oversized repos */
3115 static void
3116 compact_attrdata(Repodata *data, int entry, int nentry)
3117 {
3118   int i;
3119   unsigned int attrdatastart = data->attrdatalen;
3120   unsigned int attriddatastart = data->attriddatalen;
3121   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
3122     return;
3123   for (i = entry; i < nentry; i++)
3124     {
3125       Id v, *attrs = data->attrs[i];
3126       if (!attrs)
3127         continue;
3128       for (; *attrs; attrs += 2)
3129         {
3130           switch (data->keys[*attrs].type)
3131             {
3132             case REPOKEY_TYPE_STR:
3133             case REPOKEY_TYPE_BINARY:
3134             case_CHKSUM_TYPES:
3135               if ((unsigned int)attrs[1] < attrdatastart)
3136                  attrdatastart = attrs[1];
3137               break;
3138             case REPOKEY_TYPE_DIRSTRARRAY:
3139               for (v = attrs[1]; data->attriddata[v] ; v += 2)
3140                 if (data->attriddata[v + 1] < attrdatastart)
3141                   attrdatastart = data->attriddata[v + 1];
3142               /* FALLTHROUGH */
3143             case REPOKEY_TYPE_IDARRAY:
3144             case REPOKEY_TYPE_DIRNUMNUMARRAY:
3145               if ((unsigned int)attrs[1] < attriddatastart)
3146                 attriddatastart = attrs[1];
3147               break;
3148             case REPOKEY_TYPE_FIXARRAY:
3149             case REPOKEY_TYPE_FLEXARRAY:
3150               return;
3151             default:
3152               break;
3153             }
3154         }
3155     }
3156 #if 0
3157   printf("compact_attrdata %d %d\n", entry, nentry);
3158   printf("attrdatastart: %d\n", attrdatastart);
3159   printf("attriddatastart: %d\n", attriddatastart);
3160 #endif
3161   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
3162     return;
3163   for (i = entry; i < nentry; i++)
3164     {
3165       Id v, *attrs = data->attrs[i];
3166       if (!attrs)
3167         continue;
3168       for (; *attrs; attrs += 2)
3169         {
3170           switch (data->keys[*attrs].type)
3171             {
3172             case REPOKEY_TYPE_STR:
3173             case REPOKEY_TYPE_BINARY:
3174             case_CHKSUM_TYPES:
3175               attrs[1] -= attrdatastart;
3176               break;
3177             case REPOKEY_TYPE_DIRSTRARRAY:
3178               for (v = attrs[1]; data->attriddata[v] ; v += 2)
3179                 data->attriddata[v + 1] -= attrdatastart;
3180               /* FALLTHROUGH */
3181             case REPOKEY_TYPE_IDARRAY:
3182             case REPOKEY_TYPE_DIRNUMNUMARRAY:
3183               attrs[1] -= attriddatastart;
3184               break;
3185             default:
3186               break;
3187             }
3188         }
3189     }
3190   if (attrdatastart)
3191     {
3192       data->attrdatalen -= attrdatastart;
3193       memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
3194       data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
3195     }
3196   if (attriddatastart)
3197     {
3198       data->attriddatalen -= attriddatastart;
3199       memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
3200       data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
3201     }
3202 }
3203
3204 /* internalalize some key into incore/vincore data */
3205
3206 static void
3207 repodata_serialize_key(Repodata *data, struct extdata *newincore,
3208                        struct extdata *newvincore,
3209                        Id *schema,
3210                        Repokey *key, Id val)
3211 {
3212   Id *ida;
3213   struct extdata *xd;
3214   unsigned int oldvincorelen = 0;
3215   Id schemaid, *sp;
3216
3217   xd = newincore;
3218   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3219     {
3220       xd = newvincore;
3221       oldvincorelen = xd->len;
3222     }
3223   switch (key->type)
3224     {
3225     case REPOKEY_TYPE_VOID:
3226     case REPOKEY_TYPE_CONSTANT:
3227     case REPOKEY_TYPE_CONSTANTID:
3228     case REPOKEY_TYPE_DELETED:
3229       break;
3230     case REPOKEY_TYPE_STR:
3231       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
3232       break;
3233     case REPOKEY_TYPE_MD5:
3234       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
3235       break;
3236     case REPOKEY_TYPE_SHA1:
3237       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
3238       break;
3239     case REPOKEY_TYPE_SHA224:
3240       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
3241       break;
3242     case REPOKEY_TYPE_SHA256:
3243       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
3244       break;
3245     case REPOKEY_TYPE_SHA384:
3246       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
3247       break;
3248     case REPOKEY_TYPE_SHA512:
3249       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
3250       break;
3251     case REPOKEY_TYPE_NUM:
3252       if (val & 0x80000000)
3253         {
3254           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
3255           break;
3256         }
3257       /* FALLTHROUGH */
3258     case REPOKEY_TYPE_ID:
3259     case REPOKEY_TYPE_DIR:
3260       data_addid(xd, val);
3261       break;
3262     case REPOKEY_TYPE_BINARY:
3263       {
3264         Id len;
3265         unsigned char *dp = data_read_id(data->attrdata + val, &len);
3266         dp += (unsigned int)len;
3267         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
3268       }
3269       break;
3270     case REPOKEY_TYPE_IDARRAY:
3271       for (ida = data->attriddata + val; *ida; ida++)
3272         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3273       break;
3274     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3275       for (ida = data->attriddata + val; *ida; ida += 3)
3276         {
3277           data_addid(xd, ida[0]);
3278           data_addid(xd, ida[1]);
3279           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3280         }
3281       break;
3282     case REPOKEY_TYPE_DIRSTRARRAY:
3283       for (ida = data->attriddata + val; *ida; ida += 2)
3284         {
3285           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3286           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3287         }
3288       break;
3289     case REPOKEY_TYPE_FIXARRAY:
3290       {
3291         int num = 0;
3292         schemaid = 0;
3293         for (ida = data->attriddata + val; *ida; ida++)
3294           {
3295             Id *kp;
3296             sp = schema;
3297             kp = data->xattrs[-*ida];
3298             if (!kp)
3299               continue;         /* ignore empty elements */
3300             num++;
3301             for (; *kp; kp += 2)
3302               *sp++ = *kp;
3303             *sp = 0;
3304             if (!schemaid)
3305               schemaid = repodata_schema2id(data, schema, 1);
3306             else if (schemaid != repodata_schema2id(data, schema, 0))
3307               {
3308                 pool_debug(data->repo->pool, SOLV_ERROR, "repodata_serialize_key: fixarray substructs with different schemas\n");
3309                 num = 0;
3310                 break;
3311               }
3312           }
3313         data_addid(xd, num);
3314         if (!num)
3315           break;
3316         data_addid(xd, schemaid);
3317         for (ida = data->attriddata + val; *ida; ida++)
3318           {
3319             Id *kp = data->xattrs[-*ida];
3320             if (!kp)
3321               continue;
3322             for (; *kp; kp += 2)
3323               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3324           }
3325         break;
3326       }
3327     case REPOKEY_TYPE_FLEXARRAY:
3328       {
3329         int num = 0;
3330         for (ida = data->attriddata + val; *ida; ida++)
3331           num++;
3332         data_addid(xd, num);
3333         for (ida = data->attriddata + val; *ida; ida++)
3334           {
3335             Id *kp = data->xattrs[-*ida];
3336             if (!kp)
3337               {
3338                 data_addid(xd, 0);      /* XXX */
3339                 continue;
3340               }
3341             sp = schema;
3342             for (;*kp; kp += 2)
3343               *sp++ = *kp;
3344             *sp = 0;
3345             schemaid = repodata_schema2id(data, schema, 1);
3346             data_addid(xd, schemaid);
3347             kp = data->xattrs[-*ida];
3348             for (;*kp; kp += 2)
3349               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3350           }
3351         break;
3352       }
3353     default:
3354       pool_debug(data->repo->pool, SOLV_FATAL, "repodata_serialize_key: don't know how to handle type %d\n", key->type);
3355       exit(1);
3356     }
3357   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3358     {
3359       /* put offset/len in incore */
3360       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3361       oldvincorelen = xd->len - oldvincorelen;
3362       data_addid(newincore, oldvincorelen);
3363     }
3364 }
3365
3366 /* create a circular linked list of all keys that share
3367  * the same keyname */
3368 static Id *
3369 calculate_keylink(Repodata *data)
3370 {
3371   int i, j;
3372   Id *link;
3373   Id maxkeyname = 0, *keytable = 0;
3374   link = solv_calloc(data->nkeys, sizeof(Id));
3375   if (data->nkeys <= 2)
3376     return link;
3377   for (i = 1; i < data->nkeys; i++)
3378     {
3379       Id n = data->keys[i].name;
3380       if (n >= maxkeyname)
3381         {
3382           keytable = solv_realloc2(keytable, n + 128, sizeof(Id));
3383           memset(keytable + maxkeyname, 0, (n + 128 - maxkeyname) * sizeof(Id));
3384           maxkeyname = n + 128;
3385         }
3386       j = keytable[n];
3387       if (j)
3388         link[i] = link[j];
3389       else
3390         j = i;
3391       link[j] = i;
3392       keytable[n] = i;
3393     }
3394   /* remove links that just point to themselfs */
3395   for (i = 1; i < data->nkeys; i++)
3396     if (link[i] == i)
3397       link[i] = 0;
3398   solv_free(keytable);
3399   return link;
3400 }
3401
3402 void
3403 repodata_internalize(Repodata *data)
3404 {
3405   Repokey *key, solvkey;
3406   Id entry, nentry;
3407   Id schemaid, keyid, *schema, *sp, oldschemaid, *keyp, *seen;
3408   Offset *oldincoreoffs = 0;
3409   int schemaidx;
3410   unsigned char *dp, *ndp;
3411   int neednewschema;
3412   struct extdata newincore;
3413   struct extdata newvincore;
3414   Id solvkeyid;
3415   Id *keylink;
3416   int haveoldkl;
3417
3418   if (!data->attrs && !data->xattrs)
3419     return;
3420
3421 #if 0
3422   printf("repodata_internalize %d\n", data->repodataid);
3423   printf("  attr data: %d K\n", data->attrdatalen / 1024);
3424   printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3425 #endif
3426   newvincore.buf = data->vincore;
3427   newvincore.len = data->vincorelen;
3428
3429   /* find the solvables key, create if needed */
3430   memset(&solvkey, 0, sizeof(solvkey));
3431   solvkey.name = REPOSITORY_SOLVABLES;
3432   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3433   solvkey.size = 0;
3434   solvkey.storage = KEY_STORAGE_INCORE;
3435   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3436
3437   schema = solv_malloc2(data->nkeys, sizeof(Id));
3438   seen = solv_malloc2(data->nkeys, sizeof(Id));
3439
3440   /* Merge the data already existing (in data->schemata, ->incoredata and
3441      friends) with the new attributes in data->attrs[].  */
3442   nentry = data->end - data->start;
3443   memset(&newincore, 0, sizeof(newincore));
3444   data_addid(&newincore, 0);    /* start data at offset 1 */
3445
3446   data->mainschema = 0;
3447   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3448
3449   keylink = calculate_keylink(data);
3450   /* join entry data */
3451   /* we start with the meta data, entry -1 */
3452   for (entry = -1; entry < nentry; entry++)
3453     {
3454       oldschemaid = 0;
3455       dp = data->incoredata;
3456       if (dp)
3457         {
3458           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3459           dp = data_read_id(dp, &oldschemaid);
3460         }
3461       memset(seen, 0, data->nkeys * sizeof(Id));
3462 #if 0
3463 fprintf(stderr, "oldschemaid %d\n", oldschemaid);
3464 fprintf(stderr, "schemata %d\n", data->schemata[oldschemaid]);
3465 fprintf(stderr, "schemadata %p\n", data->schemadata);
3466 #endif
3467
3468       /* seen: -1: old data,  0: skipped,  >0: id + 1 */
3469       neednewschema = 0;
3470       sp = schema;
3471       haveoldkl = 0;
3472       for (keyp = data->schemadata + data->schemata[oldschemaid]; *keyp; keyp++)
3473         {
3474           if (seen[*keyp])
3475             {
3476               /* oops, should not happen */
3477               neednewschema = 1;
3478               continue;
3479             }
3480           seen[*keyp] = -1;     /* use old marker */
3481           *sp++ = *keyp;
3482           if (keylink[*keyp])
3483             haveoldkl = 1;      /* potential keylink conflict */
3484         }
3485
3486       /* strip solvables key */
3487       if (entry < 0 && solvkeyid && seen[solvkeyid])
3488         {
3489           *sp = 0;
3490           for (sp = keyp = schema; *sp; sp++)
3491             if (*sp != solvkeyid)
3492               *keyp++ = *sp;
3493           sp = keyp;
3494           seen[solvkeyid] = 0;
3495           neednewschema = 1;
3496         }
3497
3498       /* add new entries */
3499       if (entry >= 0)
3500         keyp = data->attrs ? data->attrs[entry] : 0;
3501       else
3502         keyp = data->xattrs ? data->xattrs[1] : 0;
3503       if (keyp)
3504         for (; *keyp; keyp += 2)
3505           {
3506             if (!seen[*keyp])
3507               {
3508                 neednewschema = 1;
3509                 *sp++ = *keyp;
3510                 if (haveoldkl && keylink[*keyp])                /* this should be pretty rare */
3511                   {
3512                     Id kl;
3513                     for (kl = keylink[*keyp]; kl != *keyp; kl = keylink[kl])
3514                       if (seen[kl] == -1)
3515                         {
3516                           /* replacing old key kl, remove from schema and seen */
3517                           Id *osp;
3518                           for (osp = schema; osp < sp; osp++)
3519                             if (*osp == kl)
3520                               {
3521                                 memmove(osp, osp + 1, (sp - osp) * sizeof(Id));
3522                                 sp--;
3523                                 seen[kl] = 0;
3524                                 break;
3525                               }
3526                         }
3527                   }
3528               }
3529             seen[*keyp] = keyp[1] + 1;
3530           }
3531
3532       /* add solvables key if needed */
3533       if (entry < 0 && data->end != data->start)
3534         {
3535           *sp++ = solvkeyid;    /* always last in schema */
3536           neednewschema = 1;
3537         }
3538
3539       /* commit schema */
3540       *sp = 0;
3541       if (neednewschema)
3542         /* Ideally we'd like to sort the new schema here, to ensure
3543            schema equality independend of the ordering. */
3544         schemaid = repodata_schema2id(data, schema, 1);
3545       else
3546         schemaid = oldschemaid;
3547
3548       if (entry < 0)
3549         {
3550           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3551           data->mainschema = schemaid;
3552         }
3553
3554       /* find offsets in old incore data */
3555       if (oldschemaid)
3556         {
3557           Id *lastneeded = 0;
3558           for (sp = data->schemadata + data->schemata[oldschemaid]; *sp; sp++)
3559             if (seen[*sp] == -1)
3560               lastneeded = sp + 1;
3561           if (lastneeded)
3562             {
3563               if (!oldincoreoffs)
3564                 oldincoreoffs = solv_malloc2(data->nkeys, 2 * sizeof(Offset));
3565               for (sp = data->schemadata + data->schemata[oldschemaid]; sp != lastneeded; sp++)
3566                 {
3567                   /* Skip the data associated with this old key.  */
3568                   key = data->keys + *sp;
3569                   ndp = dp;
3570                   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3571                     {
3572                       ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3573                       ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3574                     }
3575                   else if (key->storage == KEY_STORAGE_INCORE)
3576                     ndp = data_skip_key(data, ndp, key);
3577                   oldincoreoffs[*sp * 2] = dp - data->incoredata;
3578                   oldincoreoffs[*sp * 2 + 1] = ndp - dp;
3579                   dp = ndp;
3580                 }
3581             }
3582         }
3583
3584       /* just copy over the complete old entry (including the schemaid) if there was no new data */
3585       if (entry >= 0 && !neednewschema && oldschemaid && (!data->attrs || !data->attrs[entry]) && dp)
3586         {
3587           ndp = data->incoredata + data->incoreoffset[entry];
3588           data->incoreoffset[entry] = newincore.len;
3589           data_addblob(&newincore, ndp, dp - ndp);
3590           goto entrydone;
3591         }
3592
3593       /* Now create data blob.  We walk through the (possibly new) schema
3594          and either copy over old data, or insert the new.  */
3595       if (entry >= 0)
3596         data->incoreoffset[entry] = newincore.len;
3597       data_addid(&newincore, schemaid);
3598
3599       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3600        * may call repodata_schema2id() which might realloc our schemadata */
3601       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3602         {
3603           if (entry < 0)
3604             {
3605               data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3606               if (keyid == solvkeyid)
3607                 {
3608                   /* add flexarray entry count */
3609                   data_addid(&newincore, data->end - data->start);
3610                   break;        /* always the last entry */
3611                 }
3612             }
3613           if (seen[keyid] == -1)
3614             {
3615               if (oldincoreoffs[keyid * 2 + 1])
3616                 data_addblob(&newincore, data->incoredata + oldincoreoffs[keyid * 2], oldincoreoffs[keyid * 2 + 1]);
3617             }
3618           else if (seen[keyid])
3619             repodata_serialize_key(data, &newincore, &newvincore, schema, data->keys + keyid, seen[keyid] - 1);
3620         }
3621
3622 entrydone:
3623       /* free memory */
3624       if (entry >= 0 && data->attrs)
3625         {
3626           if (data->attrs[entry])
3627             data->attrs[entry] = solv_free(data->attrs[entry]);
3628           if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
3629             {
3630               compact_attrdata(data, entry + 1, nentry);        /* try to free some memory */
3631 #if 0
3632               printf("  attr data: %d K\n", data->attrdatalen / 1024);
3633               printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3634               printf("  incore data: %d K\n", newincore.len / 1024);
3635               printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
3636               /* malloc_stats(); */
3637 #endif
3638             }
3639         }
3640     }
3641   /* free all xattrs */
3642   for (entry = 0; entry < data->nxattrs; entry++)
3643     if (data->xattrs[entry])
3644       solv_free(data->xattrs[entry]);
3645   data->xattrs = solv_free(data->xattrs);
3646   data->nxattrs = 0;
3647
3648   data->lasthandle = 0;
3649   data->lastkey = 0;
3650   data->lastdatalen = 0;
3651   solv_free(schema);
3652   solv_free(seen);
3653   solv_free(keylink);
3654   solv_free(oldincoreoffs);
3655   repodata_free_schemahash(data);
3656
3657   solv_free(data->incoredata);
3658   data->incoredata = newincore.buf;
3659   data->incoredatalen = newincore.len;
3660   data->incoredatafree = 0;
3661
3662   data->vincore = newvincore.buf;
3663   data->vincorelen = newvincore.len;
3664
3665   data->attrs = solv_free(data->attrs);
3666   data->attrdata = solv_free(data->attrdata);
3667   data->attriddata = solv_free(data->attriddata);
3668   data->attrnum64data = solv_free(data->attrnum64data);
3669   data->attrdatalen = 0;
3670   data->attriddatalen = 0;
3671   data->attrnum64datalen = 0;
3672 #if 0
3673   printf("repodata_internalize %d done\n", data->repodataid);
3674   printf("  incore data: %d K\n", data->incoredatalen / 1024);
3675 #endif
3676 }
3677
3678 void
3679 repodata_disable_paging(Repodata *data)
3680 {
3681   if (maybe_load_repodata(data, 0))
3682     {
3683       repopagestore_disable_paging(&data->store);
3684       data->storestate++;
3685     }
3686 }
3687
3688 /* call the pool's loadcallback to load a stub repodata */
3689 static void
3690 repodata_stub_loader(Repodata *data)
3691 {
3692   Repo *repo = data->repo;
3693   Pool *pool = repo->pool;
3694   int r, i;
3695   struct s_Pool_tmpspace oldtmpspace;
3696   Datapos oldpos;
3697
3698   if (!pool->loadcallback)
3699     {
3700       data->state = REPODATA_ERROR;
3701       return;
3702     }
3703   data->state = REPODATA_LOADING;
3704
3705   /* save tmp space and pos */
3706   oldtmpspace = pool->tmpspace;
3707   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3708   oldpos = pool->pos;
3709
3710   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3711
3712   /* restore tmp space and pos */
3713   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3714     solv_free(pool->tmpspace.buf[i]);
3715   pool->tmpspace = oldtmpspace;
3716   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3717     memset(&oldpos, 0, sizeof(oldpos));
3718   pool->pos = oldpos;
3719
3720   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3721 }
3722
3723 static inline void
3724 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3725 {
3726   Repokey xkey;
3727
3728   xkey.name = keyname;
3729   xkey.type = keytype;
3730   xkey.storage = KEY_STORAGE_INCORE;
3731   xkey.size = 0;
3732   repodata_key2id(data, &xkey, 1);
3733 }
3734
3735 static Repodata *
3736 repodata_add_stub(Repodata **datap)
3737 {
3738   Repodata *data = *datap;
3739   Repo *repo = data->repo;
3740   Id repodataid = data - repo->repodata;
3741   Repodata *sdata = repo_add_repodata(repo, 0);
3742   data = repo->repodata + repodataid;
3743   if (data->end > data->start)
3744     repodata_extend_block(sdata, data->start, data->end - data->start);
3745   sdata->state = REPODATA_STUB;
3746   sdata->loadcallback = repodata_stub_loader;
3747   *datap = data;
3748   return sdata;
3749 }
3750
3751 Repodata *
3752 repodata_create_stubs(Repodata *data)
3753 {
3754   Repo *repo = data->repo;
3755   Pool *pool = repo->pool;
3756   Repodata *sdata;
3757   int *stubdataids;
3758   Dataiterator di;
3759   Id xkeyname = 0;
3760   int i, cnt = 0;
3761
3762   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3763   while (dataiterator_step(&di))
3764     if (di.data == data)
3765       cnt++;
3766   dataiterator_free(&di);
3767   if (!cnt)
3768     return data;
3769   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3770   for (i = 0; i < cnt; i++)
3771     {
3772       sdata = repodata_add_stub(&data);
3773       stubdataids[i] = sdata - repo->repodata;
3774     }
3775   i = 0;
3776   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3777   sdata = 0;
3778   while (dataiterator_step(&di))
3779     {
3780       if (di.data != data)
3781         continue;
3782       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3783         {
3784           dataiterator_entersub(&di);
3785           sdata = repo->repodata + stubdataids[i++];
3786           xkeyname = 0;
3787           continue;
3788         }
3789       repodata_set_kv(sdata, SOLVID_META, di.key->name, di.key->type, &di.kv);
3790       if (di.key->name == REPOSITORY_KEYS && di.key->type == REPOKEY_TYPE_IDARRAY)
3791         {
3792           if (!xkeyname)
3793             {
3794               if (!di.kv.eof)
3795                 xkeyname = di.kv.id;
3796             }
3797           else
3798             {
3799               repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3800               if (xkeyname == SOLVABLE_FILELIST)
3801                 repodata_set_filelisttype(sdata, REPODATA_FILELIST_EXTENSION);
3802               xkeyname = 0;
3803             }
3804         }
3805     }
3806   dataiterator_free(&di);
3807   for (i = 0; i < cnt; i++)
3808     repodata_internalize(repo->repodata + stubdataids[i]);
3809   solv_free(stubdataids);
3810   return data;
3811 }
3812
3813 void
3814 repodata_set_filelisttype(Repodata *data, int type)
3815 {
3816   data->filelisttype = type;
3817 }
3818
3819 unsigned int
3820 repodata_memused(Repodata *data)
3821 {
3822   return data->incoredatalen + data->vincorelen;
3823 }
3824