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