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