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