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