- also look at triggers when ordering packages
[platform/upstream/libsolv.git] / ext / repo_write.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  * repo_write.c
10  * 
11  * Write Repo data out to binary file
12  * 
13  * See doc/README.format for a description 
14  * of the binary file format
15  * 
16  */
17
18 #include <sys/types.h>
19 #include <limits.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "pool.h"
27 #include "util.h"
28 #include "repo_write.h"
29 #include "repopage.h"
30
31 /*------------------------------------------------------------------*/
32 /* Id map optimizations */
33
34 typedef struct needid {
35   Id need;
36   Id map;
37 } NeedId;
38
39
40 #define RELOFF(id) (needid[0].map + GETRELID(id))
41
42 /*
43  * increment need Id
44  * idarray: array of Ids, ID_NULL terminated
45  * needid: array of Id->NeedId
46  * 
47  * return count
48  * 
49  */
50
51 static void
52 incneedid(Pool *pool, Id id, NeedId *needid)
53 {
54   while (ISRELDEP(id))
55     {
56       Reldep *rd = GETRELDEP(pool, id);
57       needid[RELOFF(id)].need++;
58       if (ISRELDEP(rd->evr))
59         incneedid(pool, rd->evr, needid);
60       else
61         needid[rd->evr].need++;
62       id = rd->name;
63     }
64   needid[id].need++;
65 }
66
67 static int
68 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
69 {
70   Id id;
71   int n = 0;
72
73   if (!idarray)
74     return 0;
75   while ((id = *idarray++) != 0)
76     {
77       n++;
78       while (ISRELDEP(id))
79         {
80           Reldep *rd = GETRELDEP(pool, id);
81           needid[RELOFF(id)].need++;
82           if (ISRELDEP(rd->evr))
83             incneedid(pool, rd->evr, needid);
84           else
85             needid[rd->evr].need++;
86           id = rd->name;
87         }
88       needid[id].need++;
89     }
90   return n + 1;
91 }
92
93
94 /*
95  * 
96  */
97
98 static int
99 needid_cmp_need(const void *ap, const void *bp, void *dp)
100 {
101   const NeedId *a = ap;
102   const NeedId *b = bp;
103   int r;
104   r = b->need - a->need;
105   if (r)
106     return r;
107   return a->map - b->map;
108 }
109
110 static int
111 needid_cmp_need_s(const void *ap, const void *bp, void *dp)
112 {
113   const NeedId *a = ap;
114   const NeedId *b = bp;
115   Stringpool *spool = dp;
116
117   int r;
118   r = b->need - a->need;
119   if (r)
120     return r;
121   const char *as = spool->stringspace + spool->strings[a->map];
122   const char *bs = spool->stringspace + spool->strings[b->map];
123   return strcmp(as, bs);
124 }
125
126
127 /*------------------------------------------------------------------*/
128 /* output helper routines */
129
130 /*
131  * unsigned 32-bit
132  */
133
134 static void
135 write_u32(FILE *fp, unsigned int x)
136 {
137   if (putc(x >> 24, fp) == EOF ||
138       putc(x >> 16, fp) == EOF ||
139       putc(x >> 8, fp) == EOF ||
140       putc(x, fp) == EOF)
141     {
142       perror("write error u32");
143       exit(1);
144     }
145 }
146
147
148 /*
149  * unsigned 8-bit
150  */
151
152 static void
153 write_u8(FILE *fp, unsigned int x)
154 {
155   if (putc(x, fp) == EOF)
156     {
157       perror("write error u8");
158       exit(1);
159     }
160 }
161
162 /*
163  * data blob
164  */
165
166 static void
167 write_blob(FILE *fp, void *data, int len)
168 {
169   if (len && fwrite(data, len, 1, fp) != 1)
170     {
171       perror("write error blob");
172       exit(1);
173     }
174 }
175
176 /*
177  * Id
178  */
179
180 static void
181 write_id(FILE *fp, Id x)
182 {
183   if (x >= (1 << 14))
184     {
185       if (x >= (1 << 28))
186         putc((x >> 28) | 128, fp);
187       if (x >= (1 << 21))
188         putc((x >> 21) | 128, fp);
189       putc((x >> 14) | 128, fp);
190     }
191   if (x >= (1 << 7))
192     putc((x >> 7) | 128, fp);
193   if (putc(x & 127, fp) == EOF)
194     {
195       perror("write error id");
196       exit(1);
197     }
198 }
199
200 static inline void
201 write_id_eof(FILE *fp, Id x, int eof)
202 {
203   if (x >= 64)
204     x = (x & 63) | ((x & ~63) << 1);
205   write_id(fp, x | (eof ? 0 : 64));
206 }
207
208
209
210 static inline void
211 write_str(FILE *fp, const char *str)
212 {
213   if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
214     {
215       perror("write error str");
216       exit(1);
217     }
218 }
219
220 /*
221  * Array of Ids
222  */
223
224 static void
225 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
226 {
227   Id id;
228   if (!ids)
229     return;
230   if (!*ids)
231     {
232       write_u8(fp, 0);
233       return;
234     }
235   for (;;)
236     {
237       id = *ids++;
238       if (needid)
239         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
240       if (id >= 64)
241         id = (id & 63) | ((id & ~63) << 1);
242       if (!*ids)
243         {
244           write_id(fp, id);
245           return;
246         }
247       write_id(fp, id | 64);
248     }
249 }
250
251 static int
252 cmp_ids(const void *pa, const void *pb, void *dp)
253 {
254   Id a = *(Id *)pa;
255   Id b = *(Id *)pb;
256   return a - b;
257 }
258
259 #if 0
260 static void
261 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
262 {
263   int len, i;
264   Id lids[64], *sids;
265
266   if (!ids)
267     return;
268   if (!*ids)
269     {
270       write_u8(fp, 0);
271       return;
272     }
273   for (len = 0; len < 64 && ids[len]; len++)
274     {
275       Id id = ids[len];
276       if (needid)
277         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
278       lids[len] = id;
279     }
280   if (ids[len])
281     {
282       for (i = len + 1; ids[i]; i++)
283         ;
284       sids = sat_malloc2(i, sizeof(Id));
285       memcpy(sids, lids, 64 * sizeof(Id));
286       for (; ids[len]; len++)
287         {
288           Id id = ids[len];
289           if (needid)
290             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
291           sids[len] = id;
292         }
293     }
294   else
295     sids = lids;
296
297   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
298   if (needid)
299     marker = needid[marker].need;
300   for (i = 0; i < len; i++)
301     if (sids[i] == marker)
302       break;
303   if (i > 1)
304     sat_sort(sids, i, sizeof(Id), cmp_ids, 0);
305   if ((len - i) > 2)
306     sat_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
307
308   Id id, old = 0;
309
310   /* The differencing above produces many runs of ones and twos.  I tried
311      fairly elaborate schemes to RLE those, but they give only very mediocre
312      improvements in compression, as coding the escapes costs quite some
313      space.  Even if they are coded only as bits in IDs.  The best improvement
314      was about 2.7% for the whole .solv file.  It's probably better to
315      invest some complexity into sharing idarrays, than RLEing.  */
316   for (i = 0; i < len - 1; i++)
317     {
318       id = sids[i];
319     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
320        hence all real differences are offsetted by 1.  Otherwise we would
321        have to handle negative differences, which would cost code space for
322        the encoding of the sign.  We loose the exact mapping of prereq here,
323        but we know the result, so we can recover from that in the reader.  */
324       if (id == marker)
325         id = old = 0;
326       else
327         {
328           id = id - old + 1;
329           old = sids[i];
330         }
331       /* XXX If difference is zero we have multiple equal elements,
332          we might want to skip writing them out.  */
333       if (id >= 64)
334         id = (id & 63) | ((id & ~63) << 1);
335       write_id(fp, id | 64);
336     }
337   id = sids[i];
338   if (id == marker)
339     id = 0;
340   else
341     id = id - old + 1;
342   if (id >= 64)
343     id = (id & 63) | ((id & ~63) << 1);
344   write_id(fp, id);
345   if (sids != lids)
346     sat_free(sids);
347 }
348 #endif
349
350
351 struct extdata {
352   unsigned char *buf;
353   int len;
354 };
355
356 struct cbdata {
357   Repo *repo;
358
359   Stringpool *ownspool;
360   Dirpool *owndirpool;
361
362   Repokey *mykeys;
363   int nmykeys;
364
365   Id *keymap;
366   int nkeymap;
367   Id *keymapstart;
368
369   NeedId *needid;
370
371   Id *schema;           /* schema construction space */
372   Id *sp;               /* pointer in above */
373   Id *oldschema, *oldsp;
374
375   Id *myschemata;
376   int nmyschemata;
377
378   Id *myschemadata;
379   int myschemadatalen;
380
381   Id schematacache[256];
382
383   Id *solvschemata;
384   Id *extraschemata;
385   Id *subschemata;
386   int nsubschemata;
387   int current_sub;
388
389   struct extdata *extdata;
390
391   Id *dirused;
392
393   Id vstart;
394
395   Id maxdata;
396   Id lastlen;
397
398   int doingsolvables;   /* working on solvables data */
399 };
400
401 #define NEEDED_BLOCK 1023
402 #define SCHEMATA_BLOCK 31
403 #define SCHEMATADATA_BLOCK 255
404 #define EXTDATA_BLOCK 4095
405
406 static inline void
407 data_addid(struct extdata *xd, Id x)
408 {
409   unsigned char *dp;
410   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
411   dp = xd->buf + xd->len;
412
413   if (x >= (1 << 14))
414     {
415       if (x >= (1 << 28))
416         *dp++ = (x >> 28) | 128;
417       if (x >= (1 << 21))
418         *dp++ = (x >> 21) | 128;
419       *dp++ = (x >> 14) | 128;
420     }
421   if (x >= (1 << 7))
422     *dp++ = (x >> 7) | 128;
423   *dp++ = x & 127;
424   xd->len = dp - xd->buf;
425 }
426
427 static inline void
428 data_addideof(struct extdata *xd, Id x, int eof)
429 {
430   if (x >= 64)
431     x = (x & 63) | ((x & ~63) << 1);
432   data_addid(xd, (eof ? x: x | 64));
433 }
434
435 static void
436 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
437 {
438   int len, i;
439   Id lids[64], *sids;
440
441   if (!ids)
442     return;
443   if (!*ids)
444     {
445       data_addid(xd, 0);
446       return;
447     }
448   for (len = 0; len < 64 && ids[len]; len++)
449     {
450       Id id = ids[len];
451       if (needid)
452         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
453       lids[len] = id;
454     }
455   if (ids[len])
456     {
457       for (i = len + 1; ids[i]; i++)
458         ;
459       sids = sat_malloc2(i, sizeof(Id));
460       memcpy(sids, lids, 64 * sizeof(Id));
461       for (; ids[len]; len++)
462         {
463           Id id = ids[len];
464           if (needid)
465             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
466           sids[len] = id;
467         }
468     }
469   else
470     sids = lids;
471
472   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
473   if (needid)
474     marker = needid[marker].need;
475   for (i = 0; i < len; i++)
476     if (sids[i] == marker)
477       break;
478   if (i > 1)
479     sat_sort(sids, i, sizeof(Id), cmp_ids, 0);
480   if ((len - i) > 2)
481     sat_sort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids, 0);
482
483   Id id, old = 0;
484
485   /* The differencing above produces many runs of ones and twos.  I tried
486      fairly elaborate schemes to RLE those, but they give only very mediocre
487      improvements in compression, as coding the escapes costs quite some
488      space.  Even if they are coded only as bits in IDs.  The best improvement
489      was about 2.7% for the whole .solv file.  It's probably better to
490      invest some complexity into sharing idarrays, than RLEing.  */
491   for (i = 0; i < len - 1; i++)
492     {
493       id = sids[i];
494     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
495        hence all real differences are offsetted by 1.  Otherwise we would
496        have to handle negative differences, which would cost code space for
497        the encoding of the sign.  We loose the exact mapping of prereq here,
498        but we know the result, so we can recover from that in the reader.  */
499       if (id == marker)
500         id = old = 0;
501       else
502         {
503           id = id - old + 1;
504           old = sids[i];
505         }
506       /* XXX If difference is zero we have multiple equal elements,
507          we might want to skip writing them out.  */
508       if (id >= 64)
509         id = (id & 63) | ((id & ~63) << 1);
510       data_addid(xd, id | 64);
511     }
512   id = sids[i];
513   if (id == marker)
514     id = 0;
515   else
516     id = id - old + 1;
517   if (id >= 64)
518     id = (id & 63) | ((id & ~63) << 1);
519   data_addid(xd, id);
520   if (sids != lids)
521     sat_free(sids);
522 }
523
524 static inline void
525 data_addblob(struct extdata *xd, unsigned char *blob, int len)
526 {
527   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
528   memcpy(xd->buf + xd->len, blob, len);
529   xd->len += len;
530 }
531
532 static inline void
533 data_addu32(struct extdata *xd, unsigned int num)
534 {
535   unsigned char d[4];
536   d[0] = num >> 24;
537   d[1] = num >> 16;
538   d[2] = num >> 8;
539   d[3] = num;
540   data_addblob(xd, d, 4);
541 }
542
543 static Id
544 addschema(struct cbdata *cbdata, Id *schema)
545 {
546   int h, len;
547   Id *sp, cid;
548
549   for (sp = schema, len = 0, h = 0; *sp; len++)
550     h = h * 7 + *sp++;
551   h &= 255;
552   len++;
553
554   cid = cbdata->schematacache[h];
555   if (cid)
556     {
557       if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
558         return cid;
559       /* cache conflict */
560       for (cid = 1; cid < cbdata->nmyschemata; cid++)
561         if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
562           return cid;
563     }
564   /* a new one. make room. */
565   if (!cbdata->nmyschemata)
566     {
567       /* allocate schema 0, it is always empty */
568       cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, 1, sizeof(Id), SCHEMATADATA_BLOCK);
569       cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
570       cbdata->myschemata[0] = 0;
571       cbdata->myschemadata[0] = 0;
572       cbdata->nmyschemata = 1;
573       cbdata->myschemadatalen = 1;
574     }
575   /* add schema */
576   cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
577   cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
578   memcpy(cbdata->myschemadata + cbdata->myschemadatalen, schema, len * sizeof(Id));
579   cbdata->myschemata[cbdata->nmyschemata] = cbdata->myschemadatalen;
580   cbdata->myschemadatalen += len;
581   cbdata->schematacache[h] = cbdata->nmyschemata;
582   return cbdata->nmyschemata++;
583 }
584
585 static Id
586 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
587 {
588   const char *str = stringpool_id2str(ss, id);
589   id = stringpool_str2id(cbdata->ownspool, str, 1);
590   if (id >= cbdata->needid[0].map)
591     {
592       int oldoff = cbdata->needid[0].map;
593       int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
594       int nrels = cbdata->repo->pool->nrels;
595       cbdata->needid = sat_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
596       if (nrels)
597         memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
598       memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
599       cbdata->needid[0].map = newoff;
600     }
601   return id;
602 }
603
604 static Id
605 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
606 {
607   Id compid, parent;
608
609   parent = dirpool_parent(dp, dir);
610   if (parent)
611     parent = putinowndirpool(cbdata, data, dp, parent);
612   compid = dp->dirs[dir];
613   if (cbdata->ownspool && compid > 1)
614     compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
615   return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
616 }
617
618 static inline void
619 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
620 {
621   if (cbdata->dirused[dir])
622     return;
623   cbdata->dirused[dir] = 1;
624   while ((dir = dirpool_parent(dp, dir)) != 0)
625     {
626       if (cbdata->dirused[dir] == 2)
627         return;
628       if (cbdata->dirused[dir])
629         {
630           cbdata->dirused[dir] = 2;
631           return;
632         }
633       cbdata->dirused[dir] = 2;
634     }
635   cbdata->dirused[0] = 2;
636 }
637
638 static int
639 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
640 {
641   Id id;
642   int rm;
643
644   if (key->name == REPOSITORY_SOLVABLES)
645     return SEARCH_NEXT_KEY;     /* we do not want this one */
646   if (data != data->repo->repodata + data->repo->nrepodata - 1)
647     if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS)
648       return SEARCH_NEXT_KEY;
649
650   rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
651   if (!rm)
652     return SEARCH_NEXT_KEY;     /* we do not want this one */
653   /* record key in schema */
654   if ((key->type != REPOKEY_TYPE_FIXARRAY || kv->eof == 0)
655       && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
656     *cbdata->sp++ = rm;
657   switch(key->type)
658     {
659       case REPOKEY_TYPE_ID:
660       case REPOKEY_TYPE_IDARRAY:
661         id = kv->id;
662         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
663           id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
664         incneedid(repo->pool, id, cbdata->needid);
665         break;
666       case REPOKEY_TYPE_DIR:
667       case REPOKEY_TYPE_DIRNUMNUMARRAY:
668       case REPOKEY_TYPE_DIRSTRARRAY:
669         id = kv->id;
670         if (cbdata->owndirpool)
671           putinowndirpool(cbdata, data, &data->dirpool, id);
672         else
673           setdirused(cbdata, &data->dirpool, id);
674         break;
675       case REPOKEY_TYPE_FIXARRAY:
676         if (kv->eof == 0)
677           {
678             if (cbdata->oldschema)
679               {
680                 fprintf(stderr, "nested structs not yet implemented\n");
681                 exit(1);
682               }
683             cbdata->oldschema = cbdata->schema;
684             cbdata->oldsp = cbdata->sp;
685             cbdata->schema = sat_calloc(cbdata->nmykeys, sizeof(Id));
686             cbdata->sp = cbdata->schema;
687           }
688         else if (kv->eof == 1)
689           {
690             cbdata->current_sub++;
691             *cbdata->sp = 0;
692             cbdata->subschemata = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
693             cbdata->subschemata[cbdata->nsubschemata++] = addschema(cbdata, cbdata->schema);
694 #if 0
695             fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
696 #endif
697             cbdata->sp = cbdata->schema;
698           }
699         else
700           {
701             sat_free(cbdata->schema);
702             cbdata->schema = cbdata->oldschema;
703             cbdata->sp = cbdata->oldsp;
704             cbdata->oldsp = cbdata->oldschema = 0;
705           }
706         break;
707       case REPOKEY_TYPE_FLEXARRAY:
708         if (kv->entry == 0)
709           {
710             if (kv->eof != 2)
711               *cbdata->sp++ = 0;        /* mark start */
712           }
713         else
714           {
715             /* just finished a schema, rewind */
716             Id *sp = cbdata->sp - 1;
717             *sp = 0;
718             while (sp[-1])
719               sp--;
720             cbdata->subschemata = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
721             cbdata->subschemata[cbdata->nsubschemata++] = addschema(cbdata, sp);
722             cbdata->sp = kv->eof == 2 ? sp - 1: sp;
723           }
724         break;
725       default:
726         break;
727     }
728   return 0;
729 }
730
731 static int
732 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
733 {
734   struct cbdata *cbdata = vcbdata;
735   Repo *repo = data->repo;
736
737 #if 0
738   if (s)
739     fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - repo->pool->solvables : 0, s ? id2str(repo->pool, s->name) : "", key->name, id2str(repo->pool, key->name), key->type);
740 #endif
741   return repo_write_collect_needed(cbdata, repo, data, key, kv);
742 }
743
744 static int
745 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
746 {
747   int rm;
748   Id id;
749   unsigned int u32;
750   unsigned char v[4];
751   struct extdata *xd;
752   NeedId *needid;
753
754   if (key->name == REPOSITORY_SOLVABLES)
755     return SEARCH_NEXT_KEY;
756   if (data != data->repo->repodata + data->repo->nrepodata - 1)
757     if (key->name == REPOSITORY_ADDEDFILEPROVIDES || key->name == REPOSITORY_EXTERNAL || key->name == REPOSITORY_LOCATION || key->name == REPOSITORY_KEYS)
758       return SEARCH_NEXT_KEY;
759
760   rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
761   if (!rm)
762     return SEARCH_NEXT_KEY;     /* we do not want this one */
763   
764   if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
765     {
766       xd = cbdata->extdata + rm;        /* vertical buffer */
767       if (cbdata->vstart == -1)
768         cbdata->vstart = xd->len;
769     }
770   else
771     xd = cbdata->extdata + 0;           /* incore buffer */
772   switch(key->type)
773     {
774       case REPOKEY_TYPE_VOID:
775       case REPOKEY_TYPE_CONSTANT:
776       case REPOKEY_TYPE_CONSTANTID:
777         break;
778       case REPOKEY_TYPE_ID:
779         id = kv->id;
780         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
781           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
782         needid = cbdata->needid;
783         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
784         data_addid(xd, id);
785         break;
786       case REPOKEY_TYPE_IDARRAY:
787         id = kv->id;
788         if (cbdata->ownspool && id > 1)
789           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
790         needid = cbdata->needid;
791         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
792         data_addideof(xd, id, kv->eof);
793         break;
794       case REPOKEY_TYPE_STR:
795         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
796         break;
797       case REPOKEY_TYPE_MD5:
798         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
799         break;
800       case REPOKEY_TYPE_SHA1:
801         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
802         break;
803       case REPOKEY_TYPE_SHA256:
804         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
805         break;
806       case REPOKEY_TYPE_U32:
807         u32 = kv->num;
808         v[0] = u32 >> 24;
809         v[1] = u32 >> 16;
810         v[2] = u32 >> 8;
811         v[3] = u32;
812         data_addblob(xd, v, 4);
813         break;
814       case REPOKEY_TYPE_NUM:
815         data_addid(xd, kv->num);
816         break;
817       case REPOKEY_TYPE_DIR:
818         id = kv->id;
819         if (cbdata->owndirpool)
820           id = putinowndirpool(cbdata, data, &data->dirpool, id);
821         id = cbdata->dirused[id];
822         data_addid(xd, id);
823         break;
824       case REPOKEY_TYPE_DIRNUMNUMARRAY:
825         id = kv->id;
826         if (cbdata->owndirpool)
827           id = putinowndirpool(cbdata, data, &data->dirpool, id);
828         id = cbdata->dirused[id];
829         data_addid(xd, id);
830         data_addid(xd, kv->num);
831         data_addideof(xd, kv->num2, kv->eof);
832         break;
833       case REPOKEY_TYPE_DIRSTRARRAY:
834         id = kv->id;
835         if (cbdata->owndirpool)
836           id = putinowndirpool(cbdata, data, &data->dirpool, id);
837         id = cbdata->dirused[id];
838         data_addideof(xd, id, kv->eof);
839         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
840         break;
841       case REPOKEY_TYPE_FIXARRAY:
842         if (kv->eof == 0)
843           {
844             if (kv->num)
845               {
846                 data_addid(xd, kv->num);
847                 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
848 #if 0
849                 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
850 #endif
851               }
852           }
853         else if (kv->eof == 1)
854           {
855             cbdata->current_sub++;
856           }
857         else
858           {
859           }
860         break;
861       case REPOKEY_TYPE_FLEXARRAY:
862         if (!kv->entry)
863           data_addid(xd, kv->num);
864         if (kv->eof != 2)
865           data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
866         if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
867           {
868             if (xd->len - cbdata->lastlen > cbdata->maxdata)
869               cbdata->maxdata = xd->len - cbdata->lastlen;
870             cbdata->lastlen = xd->len;
871           }
872         break;
873       default:
874         fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
875         exit(1);
876     }
877   if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
878     {
879       /* we can re-use old data in the blob here! */
880       data_addid(cbdata->extdata + 0, cbdata->vstart);                  /* add offset into incore data */
881       data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart);        /* add length into incore data */
882       cbdata->vstart = -1;
883     }
884   return 0;
885 }
886
887 static int
888 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
889 {
890   struct cbdata *cbdata = vcbdata;
891   return repo_write_adddata(cbdata, data, key, kv);
892 }
893
894 static int
895 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
896 {
897   Id sib, child;
898   Id parent, lastn;
899
900   parent = n;
901   /* special case for '/', which has to come first */
902   if (parent == 1)
903     dirmap[n++] = 1;
904   for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
905     {
906       if (used && !used[sib])
907         continue;
908       if (sib == 1 && parent == 1)
909         continue;       /* already did that one above */
910       dirmap[n++] = sib;
911     }
912   lastn = n;
913   for (; parent < lastn; parent++)
914     {
915       sib = dirmap[parent];
916       if (used && used[sib] != 2)
917         continue;
918       child = dirpool_child(dp, sib);
919       if (child)
920         {
921           dirmap[n++] = -parent;
922           n = traverse_dirs(dp, dirmap, n, child, used);
923         }
924     }
925   return n;
926 }
927
928 static void
929 write_compressed_page(FILE *fp, unsigned char *page, int len)
930 {
931   int clen;
932   unsigned char cpage[BLOB_PAGESIZE];
933
934   clen = repopagestore_compress_page(page, len, cpage, len - 1);
935   if (!clen)
936     {
937       write_u32(fp, len * 2);
938       write_blob(fp, page, len);
939     }
940   else
941     {
942       write_u32(fp, clen * 2 + 1);
943       write_blob(fp, cpage, clen);
944     }
945 }
946
947
948 #if 0
949 static Id subfilekeys[] = {
950   REPODATA_INFO, REPOKEY_TYPE_VOID,
951   REPODATA_EXTERNAL, REPOKEY_TYPE_VOID,
952   REPODATA_KEYS, REPOKEY_TYPE_IDARRAY,
953   REPODATA_LOCATION, REPOKEY_TYPE_STR,
954   REPODATA_ADDEDFILEPROVIDES, REPOKEY_TYPE_REL_IDARRAY,
955   REPODATA_RPMDBCOOKIE, REPOKEY_TYPE_SHA256,
956   0,
957 };
958 #endif
959
960 static Id verticals[] = {
961   SOLVABLE_AUTHORS,
962   SOLVABLE_DESCRIPTION,
963   SOLVABLE_MESSAGEDEL,
964   SOLVABLE_MESSAGEINS,
965   SOLVABLE_EULA,
966   SOLVABLE_DISKUSAGE,
967   SOLVABLE_FILELIST,
968   0
969 };
970
971 static char *languagetags[] = {
972   "solvable:summary:",
973   "solvable:description:",
974   "solvable:messageins:",
975   "solvable:messagedel:",
976   "solvable:eula:",
977   0
978 };
979
980 int
981 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
982 {
983   const char *keyname;
984   int i;
985
986   for (i = 0; verticals[i]; i++)
987     if (key->name == verticals[i])
988       return KEY_STORAGE_VERTICAL_OFFSET;
989   keyname = id2str(repo->pool, key->name);
990   for (i = 0; languagetags[i] != 0; i++)
991     if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
992       return KEY_STORAGE_VERTICAL_OFFSET;
993   return KEY_STORAGE_INCORE;
994 }
995
996 /*
997  * Repo
998  */
999
1000 void
1001 repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Id **keyarrayp)
1002 {
1003   Pool *pool = repo->pool;
1004   int i, j, k, n;
1005   Solvable *s;
1006   NeedId *needid;
1007   int nstrings, nrels;
1008   unsigned int sizeid;
1009   unsigned int solv_flags;
1010   Reldep *ran;
1011   Id *idarraydata;
1012
1013   Id id, *sp;
1014
1015   Id *dirmap;
1016   int ndirmap;
1017   Id *keyused;
1018   unsigned char *repodataused;
1019   int anyrepodataused;
1020   
1021   struct cbdata cbdata;
1022   int needrels;
1023   Repokey *key;
1024   int poolusage, dirpoolusage, idused, dirused;
1025   int reloff;
1026
1027   Repodata *data, *dirpooldata = 0;
1028   Stringpool ownspool, *spool;
1029   Dirpool owndirpool, *dirpool;
1030
1031   Id *repodataschemata = 0;
1032   Id mainschema;
1033
1034   struct extdata *xd;
1035
1036   Id type_constantid = 0;
1037
1038   memset(&cbdata, 0, sizeof(cbdata));
1039   cbdata.repo = repo;
1040
1041   /* go through all repodata and find the keys we need */
1042   /* also unify keys */
1043   /* creates: mykeys      - key array, still has global pool ids */
1044   /*          keymapstart - maps repo number to keymap offset */
1045   /*          keymap      - maps repo key to my key, 0 -> not used */
1046
1047   /* start with all KEY_STORAGE_SOLVABLE ids */
1048
1049   n = ID_NUM_INTERNAL;
1050   for (i = 0; i < repo->nrepodata; i++)
1051     n += repo->repodata[i].nkeys;
1052   cbdata.mykeys = sat_calloc(n, sizeof(Repokey));
1053   cbdata.keymap = sat_calloc(n, sizeof(Id));
1054   cbdata.keymapstart = sat_calloc(repo->nrepodata, sizeof(Id));
1055   repodataused = sat_calloc(repo->nrepodata, 1);
1056
1057   cbdata.nmykeys = 1;
1058   needrels = 0;
1059   poolusage = 0;
1060   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1061     {
1062       key = cbdata.mykeys + i;
1063       key->name = i;
1064       if (i < SOLVABLE_PROVIDES)
1065         key->type = REPOKEY_TYPE_ID;
1066       else if (i < RPM_RPMDBID)
1067         key->type = REPOKEY_TYPE_REL_IDARRAY;
1068       else
1069         key->type = REPOKEY_TYPE_U32;
1070       key->size = 0;
1071       key->storage = KEY_STORAGE_SOLVABLE;
1072       if (keyfilter)
1073         {
1074           key->storage = keyfilter(repo, key, kfdata);
1075           if (key->storage == KEY_STORAGE_DROPPED)
1076             continue;
1077           key->storage = KEY_STORAGE_SOLVABLE;
1078         }
1079       poolusage = 1;
1080       if (key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1081         needrels = 1;
1082       cbdata.keymap[i] = i;
1083     }
1084   cbdata.nmykeys = i;
1085
1086   if (repo->nsolvables)
1087     {
1088       key = cbdata.mykeys + cbdata.nmykeys;
1089       key->name = REPOSITORY_SOLVABLES;
1090       key->type = REPOKEY_TYPE_FLEXARRAY;
1091       key->size = 0;
1092       key->storage = KEY_STORAGE_INCORE;
1093       cbdata.keymap[key->name] = cbdata.nmykeys++;
1094     }
1095
1096 #if 0
1097   /* If we store subfile info, generate the necessary keys.  */
1098   if (nsubfiles)
1099     {
1100       for (i = 0; subfilekeys[i]; i += 2)
1101         {
1102           key = cbdata.mykeys + cbdata.nmykeys;
1103           key->name = subfilekeys[i];
1104           key->type = subfilekeys[i + 1];
1105           key->size = 0;
1106           key->storage = KEY_STORAGE_SOLVABLE;
1107           cbdata.keymap[key->name] = cbdata.nmykeys++;
1108         }
1109     }
1110 #endif
1111
1112   dirpoolusage = 0;
1113
1114   spool = 0;
1115   dirpool = 0;
1116   n = ID_NUM_INTERNAL;
1117   for (i = 0; i < repo->nrepodata; i++)
1118     {
1119       data = repo->repodata + i;
1120       cbdata.keymapstart[i] = n;
1121       cbdata.keymap[n++] = 0;   /* key 0 */
1122       idused = 0;
1123       dirused = 0;
1124       if (keyfilter)
1125         {
1126           Repokey zerokey;
1127           /* check if we want this repodata */
1128           memset(&zerokey, 0, sizeof(zerokey));
1129           zerokey.name = 1;
1130           zerokey.type = 1;
1131           zerokey.size = i;
1132           if (keyfilter(repo, &zerokey, kfdata) == -1)
1133             continue;
1134         }
1135       for (j = 1; j < data->nkeys; j++, n++)
1136         {
1137           key = data->keys + j;
1138           if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1139             {
1140               cbdata.keymap[n] = cbdata.keymap[key->name];
1141               continue;
1142             }
1143           /* see if we already had this one, should use hash for fast miss */
1144           for (k = 0; k < cbdata.nmykeys; k++)
1145             {
1146               if (key->name == cbdata.mykeys[k].name && key->type == cbdata.mykeys[k].type)
1147                 {
1148                   if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != cbdata.mykeys[k].size)
1149                     continue;
1150                   break;
1151                 }
1152             }
1153           if (k < cbdata.nmykeys)
1154             cbdata.keymap[n] = k;
1155           else
1156             {
1157               /* found a new key! */
1158               cbdata.mykeys[cbdata.nmykeys] = *key;
1159               key = cbdata.mykeys + cbdata.nmykeys;
1160               key->storage = KEY_STORAGE_INCORE;
1161               if (key->type != REPOKEY_TYPE_CONSTANT && key->type != REPOKEY_TYPE_CONSTANTID)
1162                 key->size = 0;
1163               if (keyfilter)
1164                 {
1165                   key->storage = keyfilter(repo, key, kfdata);
1166                   if (key->storage == KEY_STORAGE_DROPPED)
1167                     {
1168                       cbdata.keymap[n] = 0;
1169                       continue;
1170                     }
1171                 }
1172               cbdata.keymap[n] = cbdata.nmykeys++;
1173             }
1174           /* load repodata if not already loaded */
1175           if (data->state == REPODATA_STUB)
1176             {
1177               if (data->loadcallback)
1178                 data->loadcallback(data);
1179               else
1180                 data->state = REPODATA_ERROR;
1181               if (data->state != REPODATA_ERROR)
1182                 {
1183                   /* redo this repodata! */
1184                   j = 0;
1185                   n = cbdata.keymapstart[i];
1186                   continue;
1187                 }
1188             }
1189           if (data->state == REPODATA_ERROR)
1190             {
1191               /* too bad! */
1192               cbdata.keymap[n] = 0;
1193               continue;
1194             }
1195
1196           repodataused[i] = 1;
1197           anyrepodataused = 1;
1198           if (key->type != REPOKEY_TYPE_STR
1199               && key->type != REPOKEY_TYPE_U32
1200               && key->type != REPOKEY_TYPE_MD5
1201               && key->type != REPOKEY_TYPE_SHA1)
1202             idused = 1;
1203           if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1204             dirused = 1;
1205           /* make sure we know that key */
1206           if (data->localpool)
1207             {
1208               stringpool_str2id(&data->spool, id2str(pool, key->name), 1);
1209               stringpool_str2id(&data->spool, id2str(pool, key->type), 1);
1210               if (key->type == REPOKEY_TYPE_CONSTANTID)
1211                 stringpool_str2id(&data->spool, id2str(pool, key->size), 1);
1212             }
1213         }
1214       if (idused)
1215         {
1216           if (data->localpool)
1217             {
1218               if (poolusage)
1219                 poolusage = 3;  /* need local pool */
1220               else
1221                 {
1222                   poolusage = 2;
1223                   spool = &data->spool;
1224                 }
1225             }
1226           else
1227             {
1228               if (poolusage == 0)
1229                 poolusage = 1;
1230               else if (poolusage != 1)
1231                 poolusage = 3;  /* need local pool */
1232             }
1233         }
1234       if (dirused)
1235         {
1236           if (dirpoolusage)
1237             dirpoolusage = 3;   /* need local dirpool */
1238           else
1239             {
1240               dirpoolusage = 2;
1241               dirpool = &data->dirpool;
1242               dirpooldata = data;
1243             }
1244         }
1245     }
1246   cbdata.nkeymap = n;
1247
1248   /* 0: no pool needed at all */
1249   /* 1: use global pool */
1250   /* 2: use repodata local pool */
1251   /* 3: need own pool */
1252   if (poolusage == 3)
1253     {
1254       spool = &ownspool;
1255       if (needrels)
1256         {
1257           /* hack: reuse global pool so we don't have to map rel ids */
1258           stringpool_clone(spool, &repo->pool->ss);
1259         }
1260       else
1261         stringpool_init_empty(spool);
1262       cbdata.ownspool = spool;
1263     }
1264   else if (poolusage == 0 || poolusage == 1)
1265     {
1266       poolusage = 1;
1267       spool = &repo->pool->ss;
1268     }
1269   if (dirpoolusage == 3)
1270     {
1271       dirpool = &owndirpool;
1272       dirpooldata = 0;
1273       dirpool_init(dirpool);
1274       cbdata.owndirpool = dirpool;
1275     }
1276   else if (dirpool)
1277     cbdata.dirused = sat_calloc(dirpool->ndirs, sizeof(Id));
1278
1279
1280 /********************************************************************/
1281 #if 0
1282 fprintf(stderr, "poolusage: %d\n", poolusage);
1283 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1284 fprintf(stderr, "nmykeys: %d\n", cbdata.nmykeys);
1285 for (i = 1; i < cbdata.nmykeys; i++)
1286   fprintf(stderr, "  %2d: %s[%d] %d %d %d\n", i, id2str(pool, cbdata.mykeys[i].name), cbdata.mykeys[i].name, cbdata.mykeys[i].type, cbdata.mykeys[i].size, cbdata.mykeys[i].storage);
1287 #endif
1288
1289 /********************************************************************/
1290
1291   /* set needed count of all strings and rels,
1292    * find which keys are used in the solvables
1293    * put all strings in own spool
1294    */
1295
1296   reloff = spool->nstrings;
1297   if (poolusage == 3)
1298     reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1299
1300   needid = calloc(reloff + pool->nrels, sizeof(*needid));
1301   needid[0].map = reloff;
1302
1303   cbdata.needid = needid;
1304   cbdata.schema = sat_calloc(cbdata.nmykeys, sizeof(Id));
1305   cbdata.sp = cbdata.schema;
1306   cbdata.solvschemata = sat_calloc(repo->nsolvables, sizeof(Id));
1307 #if 0
1308   cbdata.extraschemata = sat_calloc(repo->nextra, sizeof(Id));
1309 #endif
1310
1311   /* create main schema */
1312   cbdata.sp = cbdata.schema;
1313   /* collect all other data from all repodatas */
1314   /* XXX: merge arrays of equal keys? */
1315   for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1316     {
1317       if (!repodataused[j])
1318         continue;
1319       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1320     }
1321   sp = cbdata.sp;
1322   /* add solvables if needed */
1323   if (repo->nsolvables)
1324     {
1325       *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1326       cbdata.mykeys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1327     }
1328   *sp = 0;
1329   mainschema = addschema(&cbdata, cbdata.schema);
1330
1331
1332   idarraydata = repo->idarraydata;
1333
1334   cbdata.doingsolvables = 1;
1335   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1336     {
1337       if (s->repo != repo)
1338         continue;
1339
1340       /* set schema info, keep in sync with further down */
1341       sp = cbdata.schema;
1342       if (cbdata.keymap[SOLVABLE_NAME])
1343         {
1344           *sp++ = SOLVABLE_NAME;
1345           needid[s->name].need++;
1346         }
1347       if (cbdata.keymap[SOLVABLE_ARCH])
1348         {
1349           *sp++ = SOLVABLE_ARCH;
1350           needid[s->arch].need++;
1351         }
1352       if (cbdata.keymap[SOLVABLE_EVR])
1353         {
1354           *sp++ = SOLVABLE_EVR;
1355           needid[s->evr].need++;
1356         }
1357       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1358         {
1359           *sp++ = SOLVABLE_VENDOR;
1360           needid[s->vendor].need++;
1361         }
1362       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1363         {
1364           *sp++ = SOLVABLE_PROVIDES;
1365           cbdata.mykeys[SOLVABLE_PROVIDES].size += incneedidarray(pool, idarraydata + s->provides, needid);
1366         }
1367       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1368         {
1369           *sp++ = SOLVABLE_OBSOLETES;
1370           cbdata.mykeys[SOLVABLE_OBSOLETES].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1371         }
1372       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1373         {
1374           *sp++ = SOLVABLE_CONFLICTS;
1375           cbdata.mykeys[SOLVABLE_CONFLICTS].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1376         }
1377       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1378         {
1379           *sp++ = SOLVABLE_REQUIRES;
1380           cbdata.mykeys[SOLVABLE_REQUIRES].size += incneedidarray(pool, idarraydata + s->requires, needid);
1381         }
1382       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1383         {
1384           *sp++ = SOLVABLE_RECOMMENDS;
1385           cbdata.mykeys[SOLVABLE_RECOMMENDS].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1386         }
1387       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1388         {
1389           *sp++ = SOLVABLE_SUGGESTS;
1390           cbdata.mykeys[SOLVABLE_SUGGESTS].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1391         }
1392       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1393         {
1394           *sp++ = SOLVABLE_SUPPLEMENTS;
1395           cbdata.mykeys[SOLVABLE_SUPPLEMENTS].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1396         }
1397       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1398         {
1399           *sp++ = SOLVABLE_ENHANCES;
1400           cbdata.mykeys[SOLVABLE_ENHANCES].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1401         }
1402       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1403         {
1404           *sp++ = RPM_RPMDBID;
1405           cbdata.mykeys[RPM_RPMDBID].size++;
1406         }
1407       cbdata.sp = sp;
1408
1409       if (anyrepodataused)
1410         {
1411           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1412             {
1413               if (!repodataused[j])
1414                 continue;
1415               if (i < data->start || i >= data->end)
1416                 continue;
1417               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1418               needid = cbdata.needid;
1419             }
1420         }
1421       *cbdata.sp = 0;
1422       cbdata.solvschemata[n] = addschema(&cbdata, cbdata.schema);
1423       n++;
1424     }
1425   cbdata.doingsolvables = 0;
1426   assert(n == repo->nsolvables);
1427
1428 #if 0
1429   if (repo->nextra && anyrepodataused)
1430     for (i = -1; i >= -repo->nextra; i--)
1431       {
1432         Dataiterator di;
1433         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1434         cbdata.sp = cbdata.schema;
1435         while (dataiterator_step(&di))
1436           repo_write_collect_needed(&cbdata, repo, di.data, di.key, &di.kv);
1437         *cbdata.sp = 0;
1438         cbdata.extraschemata[-1 - i] = addschema(&cbdata, cbdata.schema);
1439       }
1440
1441   /* If we have fileinfos to write, setup schemas and increment needid[]
1442      of the right strings.  */
1443   for (i = 0; i < nsubfiles; i++)
1444     {
1445       int j;
1446       Id schema[4], *sp;
1447
1448       sp = schema;
1449       if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1450         {
1451           /* extra info about this file */
1452           *sp++ = cbdata.keymap[REPODATA_INFO];
1453           if (fileinfo[i].addedfileprovides)
1454             {
1455               *sp++ = cbdata.keymap[REPODATA_ADDEDFILEPROVIDES];
1456               for (j = 0; fileinfo[i].addedfileprovides[j]; j++)
1457                 ;
1458               cbdata.mykeys[cbdata.keymap[REPODATA_ADDEDFILEPROVIDES]].size += j + 1;
1459             }
1460           if (fileinfo[i].rpmdbcookie)
1461             *sp++ = cbdata.keymap[REPODATA_RPMDBCOOKIE];
1462         }
1463       else
1464         {
1465           *sp++ = cbdata.keymap[REPODATA_EXTERNAL];
1466           *sp++ = cbdata.keymap[REPODATA_KEYS];
1467           if (fileinfo[i].location)
1468             *sp++ = cbdata.keymap[REPODATA_LOCATION];
1469         }
1470       *sp = 0;
1471       repodataschemata[i] = addschema(&cbdata, schema);
1472       cbdata.mykeys[cbdata.keymap[REPODATA_KEYS]].size += 2 * fileinfo[i].nkeys + 1;
1473       for (j = 1; j < fileinfo[i].nkeys; j++)
1474         {
1475           needid[fileinfo[i].keys[j].type].need++;
1476           needid[fileinfo[i].keys[j].name].need++;
1477         }
1478     }
1479 #endif
1480
1481 /********************************************************************/
1482
1483   /* remove unused keys, convert ids to local ids and increment their needid */
1484   keyused = sat_calloc(cbdata.nmykeys, sizeof(Id));
1485   for (i = 0; i < cbdata.myschemadatalen; i++)
1486     keyused[cbdata.myschemadata[i]] = 1;
1487   keyused[0] = 0;
1488   for (n = i = 1; i < cbdata.nmykeys; i++)
1489     {
1490       if (!keyused[i])
1491         continue;
1492       keyused[i] = n;
1493       if (i != n)
1494         cbdata.mykeys[n] = cbdata.mykeys[i];
1495       if (cbdata.mykeys[n].type == REPOKEY_TYPE_CONSTANTID)
1496         {
1497           if (!type_constantid)
1498             type_constantid = poolusage > 1 ? stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1) : REPOKEY_TYPE_CONSTANTID;
1499           if (poolusage > 1)
1500             cbdata.mykeys[n].size = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].size), 1);
1501           needid[cbdata.mykeys[n].size].need++;
1502         }
1503       if (poolusage > 1)
1504         {
1505           cbdata.mykeys[n].name = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].name), 1);
1506           cbdata.mykeys[n].type = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1);
1507         }
1508       needid[cbdata.mykeys[n].name].need++;
1509       needid[cbdata.mykeys[n].type].need++;
1510       n++;
1511     }
1512   cbdata.nmykeys = n;
1513   for (i = 0; i < cbdata.myschemadatalen; i++)
1514     cbdata.myschemadata[i] = keyused[cbdata.myschemadata[i]];
1515   for (i = 0; i < cbdata.nkeymap; i++)
1516     cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1517   keyused = sat_free(keyused);
1518
1519 /********************************************************************/
1520
1521   /* increment need id for used dir components */
1522   if (cbdata.dirused && !cbdata.dirused[0])
1523     {
1524       /* no dirs used at all */
1525       cbdata.dirused = sat_free(cbdata.dirused);
1526       dirpool = 0;
1527     }
1528   if (dirpool)
1529     {
1530       for (i = 1; i < dirpool->ndirs; i++)
1531         {
1532 #if 0
1533 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1534 #endif
1535           id = dirpool->dirs[i];
1536           if (id <= 0)
1537             continue;
1538           if (cbdata.dirused && !cbdata.dirused[i])
1539             continue;
1540           if (cbdata.ownspool && dirpooldata && id > 1)
1541             {
1542               id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1543               needid = cbdata.needid;
1544             }
1545           needid[id].need++;
1546         }
1547     }
1548
1549   reloff = needid[0].map;
1550
1551
1552 /********************************************************************/
1553
1554   /*
1555    * create mapping table, new keys are sorted by needid[].need
1556    *
1557    * needid[key].need : old key -> new key
1558    * needid[key].map  : new key -> old key
1559    */
1560
1561   /* zero out id 0 and rel 0 just in case */
1562
1563   needid[0].need = 0;
1564   needid[reloff].need = 0;
1565
1566   for (i = 1; i < reloff + pool->nrels; i++)
1567     needid[i].map = i;
1568
1569 #if 0
1570   sat_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1571 #else
1572   /* make first entry '' */
1573   needid[1].need = 1;
1574   sat_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1575 #endif
1576   sat_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1577
1578   sizeid = 0;
1579   for (i = 1; i < reloff; i++)
1580     {
1581       if (!needid[i].need)
1582         break;
1583       needid[i].need = 0;
1584       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1585     }
1586
1587   nstrings = i;
1588   for (i = 1; i < nstrings; i++)
1589     needid[needid[i].map].need = i;
1590
1591   for (i = 0; i < pool->nrels; i++)
1592     {
1593       if (!needid[reloff + i].need)
1594         break;
1595       else
1596         needid[reloff + i].need = 0;
1597     }
1598
1599   nrels = i;
1600   for (i = 0; i < nrels; i++)
1601     needid[needid[reloff + i].map].need = nstrings + i;
1602
1603
1604 /********************************************************************/
1605
1606   /* create dir map */
1607   ndirmap = 0;
1608   dirmap = 0;
1609   if (dirpool)
1610     {
1611       if (cbdata.dirused && !cbdata.dirused[1])
1612         cbdata.dirused[1] = 1;  /* always want / entry */
1613       dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1614       dirmap[0] = 0;
1615       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1616       if (!cbdata.dirused)
1617         cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1618       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1619       for (i = 1; i < ndirmap; i++)
1620         {
1621           if (dirmap[i] <= 0)
1622             continue;
1623           cbdata.dirused[dirmap[i]] = i;
1624           id = dirpool->dirs[dirmap[i]];
1625           if (cbdata.ownspool && dirpooldata && id > 1)
1626             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1627           dirmap[i] = needid[id].need;
1628         }
1629     }
1630
1631 /********************************************************************/
1632   cbdata.extdata = sat_calloc(cbdata.nmykeys, sizeof(struct extdata));
1633
1634   xd = cbdata.extdata;
1635   cbdata.current_sub = 0;
1636   /* write main schema */
1637   cbdata.lastlen = 0;
1638   data_addid(xd, mainschema);
1639
1640 #if 1
1641   for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1642     {
1643       if (!repodataused[j])
1644         continue;
1645       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1646     }
1647 #endif
1648
1649   if (xd->len - cbdata.lastlen > cbdata.maxdata)
1650     cbdata.maxdata = xd->len - cbdata.lastlen;
1651   cbdata.lastlen = xd->len;
1652
1653   if (repo->nsolvables)
1654     data_addid(xd, repo->nsolvables);   /* FLEXARRAY nentries */
1655   cbdata.doingsolvables = 1;
1656   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1657     {
1658       if (s->repo != repo)
1659         continue;
1660       data_addid(xd, cbdata.solvschemata[n]);
1661       if (cbdata.keymap[SOLVABLE_NAME])
1662         data_addid(xd, needid[s->name].need);
1663       if (cbdata.keymap[SOLVABLE_ARCH])
1664         data_addid(xd, needid[s->arch].need);
1665       if (cbdata.keymap[SOLVABLE_EVR])
1666         data_addid(xd, needid[s->evr].need);
1667       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1668         data_addid(xd, needid[s->vendor].need);
1669       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1670         data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1671       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1672         data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1673       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1674         data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1675       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1676         data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1677       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1678         data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1679       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1680         data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1681       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1682         data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1683       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1684         data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1685       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1686         data_addu32(xd, repo->rpmdbid[i - repo->start]);
1687       if (anyrepodataused)
1688         {
1689           cbdata.vstart = -1;
1690           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1691             {
1692               if (!repodataused[j])
1693                 continue;
1694               if (i < data->start || i >= data->end)
1695                 continue;
1696               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1697             }
1698         }
1699       if (xd->len - cbdata.lastlen > cbdata.maxdata)
1700         cbdata.maxdata = xd->len - cbdata.lastlen;
1701       cbdata.lastlen = xd->len;
1702       n++;
1703     }
1704   cbdata.doingsolvables = 0;
1705
1706   assert(cbdata.current_sub == cbdata.nsubschemata);
1707   if (cbdata.subschemata)
1708     {
1709       cbdata.subschemata = sat_free(cbdata.subschemata);
1710       cbdata.nsubschemata = 0;
1711     }
1712
1713 #if 0
1714   if (repo->nextra && anyrepodataused)
1715     for (i = -1; i >= -repo->nextra; i--)
1716       {
1717         Dataiterator di;
1718         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1719         entrysize = xd->len;
1720         data_addid(xd, cbdata.extraschemata[-1 - i]);
1721         cbdata.vstart = -1;
1722         while (dataiterator_step(&di))
1723           repo_write_adddata(&cbdata, di.data, di.key, &di.kv);
1724         entrysize = xd->len - entrysize;
1725         if (entrysize > maxentrysize)
1726           maxentrysize = entrysize;
1727       }
1728 #endif
1729
1730 /********************************************************************/
1731
1732   /* write header */
1733
1734   /* write file header */
1735   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1736   write_u32(fp, SOLV_VERSION_8);
1737
1738
1739   /* write counts */
1740   write_u32(fp, nstrings);
1741   write_u32(fp, nrels);
1742   write_u32(fp, ndirmap);
1743   write_u32(fp, repo->nsolvables);
1744   write_u32(fp, cbdata.nmykeys);
1745   write_u32(fp, cbdata.nmyschemata);
1746   solv_flags = 0;
1747   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1748   write_u32(fp, solv_flags);
1749
1750   /*
1751    * calculate prefix encoding of the strings
1752    */
1753   unsigned char *prefixcomp = sat_malloc(nstrings);
1754   unsigned int compsum = 0;
1755   char *old_str = "";
1756   
1757   prefixcomp[0] = 0;
1758   for (i = 1; i < nstrings; i++)
1759     {
1760       char *str = spool->stringspace + spool->strings[needid[i].map];
1761       int same;
1762       for (same = 0; same < 255; same++)
1763         if (!old_str[same] || old_str[same] != str[same])
1764           break;
1765       prefixcomp[i] = same;
1766       compsum += same;
1767       old_str = str;
1768     }
1769
1770   /*
1771    * write strings
1772    */
1773   write_u32(fp, sizeid);
1774   /* we save compsum bytes but need 1 extra byte for every string */
1775   write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1776   if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1777     {
1778       for (i = 1; i < nstrings; i++)
1779         {
1780           char *str = spool->stringspace + spool->strings[needid[i].map];
1781           write_u8(fp, prefixcomp[i]);
1782           write_str(fp, str + prefixcomp[i]);
1783         }
1784     }
1785   sat_free(prefixcomp);
1786
1787 #if 0
1788   /* Build the prefix-encoding of the string pool.  We need to know
1789      the size of that before writing it to the file, so we have to
1790      build a separate buffer for that.  As it's temporarily possible
1791      that this actually is an expansion we can't easily reuse the 
1792      stringspace for this.  The max expansion per string is 1 byte,
1793      so it will fit into sizeid+nstrings bytes.  */
1794   char *prefix = sat_malloc(sizeid + nstrings);
1795   char *pp = prefix;
1796   char *old_str = "";
1797   for (i = 1; i < nstrings; i++)
1798     {
1799       char *str = spool->stringspace + spool->strings[needid[i].map];
1800       int same;
1801       size_t len;
1802       for (same = 0; same < 255; same++)
1803         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1804           break;
1805       *pp++ = same;
1806       len = strlen(str + same) + 1;
1807       memcpy(pp, str + same, len);
1808       pp += len;
1809       old_str = str;
1810     }
1811
1812   /*
1813    * write strings
1814    */
1815   write_u32(fp, sizeid);
1816   write_u32(fp, pp - prefix);
1817   if (pp != prefix)
1818     {
1819       if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1820         {
1821           perror("write error prefix");
1822           exit(1);
1823         }
1824     }
1825   sat_free(prefix);
1826 #endif
1827
1828   /*
1829    * write RelDeps
1830    */
1831   for (i = 0; i < nrels; i++)
1832     {
1833       ran = pool->rels + (needid[reloff + i].map - reloff);
1834       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1835       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1836       write_u8(fp, ran->flags);
1837     }
1838
1839   /*
1840    * write dirs (skip both root and / entry)
1841    */
1842   for (i = 2; i < ndirmap; i++)
1843     {
1844       if (dirmap[i] > 0)
1845         write_id(fp, dirmap[i]);
1846       else
1847         write_id(fp, nstrings - dirmap[i]);
1848     }
1849   sat_free(dirmap);
1850
1851   /*
1852    * write keys
1853    */
1854   if (keyarrayp)
1855     *keyarrayp = sat_calloc(2 * cbdata.nmykeys + 1, sizeof(Id));
1856   for (i = 1; i < cbdata.nmykeys; i++)
1857     {
1858       write_id(fp, needid[cbdata.mykeys[i].name].need);
1859       write_id(fp, needid[cbdata.mykeys[i].type].need);
1860       if (cbdata.mykeys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1861         {
1862           if (cbdata.mykeys[i].type == type_constantid)
1863             write_id(fp, needid[cbdata.mykeys[i].size].need);
1864           else
1865             write_id(fp, cbdata.mykeys[i].size);
1866         }
1867       else
1868         write_id(fp, cbdata.extdata[i].len);
1869       write_id(fp, cbdata.mykeys[i].storage);
1870       if (keyarrayp)
1871         {
1872           (*keyarrayp)[2 * i - 2] = cbdata.mykeys[i].name;
1873           (*keyarrayp)[2 * i - 1] = cbdata.mykeys[i].type;
1874         }
1875     }
1876
1877   /*
1878    * write schemata
1879    */
1880   write_id(fp, cbdata.myschemadatalen);
1881   if (cbdata.nmyschemata)
1882     {
1883       for (i = 1; i < cbdata.nmyschemata; i++)
1884         write_idarray(fp, pool, 0, cbdata.myschemadata + cbdata.myschemata[i]);
1885     }
1886
1887 #if 0
1888   /*
1889    * write info block
1890    */
1891   if (nsubfiles)
1892     {
1893       struct extdata xd;
1894       xd.buf = 0;
1895       xd.len = 0;
1896       int max = 0;
1897       int cur;
1898
1899       for (i = 0; i < nsubfiles; i++)
1900         {
1901           int j;
1902
1903           cur = xd.len;
1904           data_addid(&xd, repodataschemata[i]);
1905           if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1906             {
1907               if (fileinfo[i].addedfileprovides)
1908                 data_addidarray_sort(&xd, pool, needid, fileinfo[i].addedfileprovides, 0);
1909               if (fileinfo[i].rpmdbcookie)
1910                 data_addblob(&xd, fileinfo[i].rpmdbcookie, 32);
1911             }
1912           else
1913             {
1914               /* key,type array + location, write idarray */
1915               for (j = 1; j < fileinfo[i].nkeys; j++)
1916                 {
1917                   data_addideof(&xd, needid[fileinfo[i].keys[j].name].need, 0);
1918                   data_addideof(&xd, needid[fileinfo[i].keys[j].type].need, j == fileinfo[i].nkeys - 1);
1919                 }
1920               if (fileinfo[i].location)
1921                 data_addblob(&xd, (unsigned char *)fileinfo[i].location, strlen(fileinfo[i].location) + 1);
1922             }
1923           cur = xd.len - cur;
1924           if (cur > max)
1925             max = cur;
1926         }
1927       write_id(fp, max);
1928       write_id(fp, xd.len);
1929       write_blob(fp, xd.buf, xd.len);
1930       sat_free(xd.buf);
1931     }
1932 #endif
1933
1934 /********************************************************************/
1935
1936   write_id(fp, cbdata.maxdata);
1937   write_id(fp, cbdata.extdata[0].len);
1938   if (cbdata.extdata[0].len)
1939     write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1940   sat_free(cbdata.extdata[0].buf);
1941
1942 #if 0
1943   /*
1944    * write Solvable data
1945    */
1946   if (repo->nsolvables || repo->nextra)
1947     {
1948       write_id(fp, maxentrysize);
1949       write_id(fp, cbdata.extdata[0].len);
1950       write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1951     }
1952   sat_free(cbdata.extdata[0].buf);
1953 #endif
1954
1955   /* write vertical data */
1956   for (i = 1; i < cbdata.nmykeys; i++)
1957     if (cbdata.extdata[i].len)
1958       break;
1959   if (i < cbdata.nmykeys)
1960     {
1961       unsigned char *dp, vpage[BLOB_PAGESIZE];
1962       int l, ll, lpage = 0;
1963
1964       write_u32(fp, BLOB_PAGESIZE);
1965       for (i = 1; i < cbdata.nmykeys; i++)
1966         {
1967           if (!cbdata.extdata[i].len)
1968             continue;
1969           l = cbdata.extdata[i].len;
1970           dp = cbdata.extdata[i].buf;
1971           while (l)
1972             {
1973               ll = BLOB_PAGESIZE - lpage;
1974               if (l < ll)
1975                 ll = l;
1976               memcpy(vpage + lpage, dp, ll);
1977               dp += ll;
1978               lpage += ll;
1979               l -= ll;
1980               if (lpage == BLOB_PAGESIZE)
1981                 {
1982                   write_compressed_page(fp, vpage, lpage);
1983                   lpage = 0;
1984                 }
1985             }
1986         }
1987       if (lpage)
1988         write_compressed_page(fp, vpage, lpage);
1989     }
1990
1991 #if 0
1992   /* write vertical_offset entries */
1993   write_u32(fp, 0);     /* no paging */
1994   for (i = 1; i < cbdata.nmykeys; i++)
1995     if (cbdata.extdata[i].len)
1996       write_blob(fp, cbdata.extdata[i].buf, cbdata.extdata[i].len);
1997
1998   /* Fill fileinfo for our caller.  */
1999   if (setfileinfo)
2000     {
2001       fileinfo->checksum = 0;
2002       fileinfo->nchecksum = 0;
2003       fileinfo->checksumtype = 0;
2004       fileinfo->location = 0;
2005     }
2006 #endif
2007
2008   for (i = 1; i < cbdata.nmykeys; i++)
2009     sat_free(cbdata.extdata[i].buf);
2010   sat_free(cbdata.extdata);
2011
2012   if (cbdata.ownspool)
2013     {
2014       sat_free(cbdata.ownspool->strings);
2015       sat_free(cbdata.ownspool->stringspace);
2016       sat_free(cbdata.ownspool->stringhashtbl);
2017     }
2018   if (cbdata.owndirpool)
2019     {
2020       sat_free(cbdata.owndirpool->dirs);
2021       sat_free(cbdata.owndirpool->dirtraverse);
2022     }
2023   sat_free(needid);
2024   sat_free(cbdata.extraschemata);
2025   sat_free(cbdata.solvschemata);
2026   sat_free(cbdata.myschemadata);
2027   sat_free(cbdata.myschemata);
2028   sat_free(cbdata.schema);
2029
2030   sat_free(cbdata.mykeys);
2031   sat_free(cbdata.keymap);
2032   sat_free(cbdata.keymapstart);
2033   sat_free(cbdata.dirused);
2034   sat_free(repodataused);
2035   sat_free(repodataschemata);
2036 }
2037
2038 struct repodata_write_data {
2039   int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
2040   void *kfdata;
2041   int repodataid;
2042 };
2043
2044 static int
2045 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
2046 {
2047   struct repodata_write_data *wd = kfdata;
2048
2049   /* XXX: special repodata selection hack */
2050   if (key->name == 1 && key->size != wd->repodataid)
2051     return -1;
2052   if (key->storage == KEY_STORAGE_SOLVABLE)
2053     return KEY_STORAGE_DROPPED; /* not part of this repodata */
2054   if (wd->keyfilter)
2055     return (*wd->keyfilter)(repo, key, wd->kfdata);
2056   return key->storage;
2057 }
2058
2059 void
2060 repodata_write(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
2061 {
2062   struct repodata_write_data wd;
2063
2064   wd.keyfilter = keyfilter;
2065   wd.kfdata = kfdata;
2066   wd.repodataid = data - data->repo->repodata;
2067   repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);
2068 }