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