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