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