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