fix compiler warning about uninitialized var
[opensuse:sat-solver-moved-to-github.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 || key->name == REPOSITORY_TOOLVERSION)
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 || key->name == REPOSITORY_TOOLVERSION)
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_BINARY:
825         data_addid(xd, kv->num);
826         if (kv->num)
827           data_addblob(xd, (unsigned char *)kv->str, kv->num);
828         break;
829       case REPOKEY_TYPE_DIRNUMNUMARRAY:
830         id = kv->id;
831         if (cbdata->owndirpool)
832           id = putinowndirpool(cbdata, data, &data->dirpool, id);
833         id = cbdata->dirused[id];
834         data_addid(xd, id);
835         data_addid(xd, kv->num);
836         data_addideof(xd, kv->num2, kv->eof);
837         break;
838       case REPOKEY_TYPE_DIRSTRARRAY:
839         id = kv->id;
840         if (cbdata->owndirpool)
841           id = putinowndirpool(cbdata, data, &data->dirpool, id);
842         id = cbdata->dirused[id];
843         data_addideof(xd, id, kv->eof);
844         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
845         break;
846       case REPOKEY_TYPE_FIXARRAY:
847         if (kv->eof == 0)
848           {
849             if (kv->num)
850               {
851                 data_addid(xd, kv->num);
852                 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
853 #if 0
854                 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
855 #endif
856               }
857           }
858         else if (kv->eof == 1)
859           {
860             cbdata->current_sub++;
861           }
862         break;
863       case REPOKEY_TYPE_FLEXARRAY:
864         if (!kv->entry)
865           data_addid(xd, kv->num);
866         if (kv->eof != 2)
867           data_addid(xd, cbdata->subschemata[cbdata->current_sub++]);
868         if (xd == cbdata->extdata + 0 && !kv->parent && !cbdata->doingsolvables)
869           {
870             if (xd->len - cbdata->lastlen > cbdata->maxdata)
871               cbdata->maxdata = xd->len - cbdata->lastlen;
872             cbdata->lastlen = xd->len;
873           }
874         break;
875       default:
876         fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
877         exit(1);
878     }
879   if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
880     {
881       /* we can re-use old data in the blob here! */
882       data_addid(cbdata->extdata + 0, cbdata->vstart);                  /* add offset into incore data */
883       data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart);        /* add length into incore data */
884       cbdata->vstart = -1;
885     }
886   return 0;
887 }
888
889 static int
890 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
891 {
892   struct cbdata *cbdata = vcbdata;
893   return repo_write_adddata(cbdata, data, key, kv);
894 }
895
896 static int
897 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
898 {
899   Id sib, child;
900   Id parent, lastn;
901
902   parent = n;
903   /* special case for '/', which has to come first */
904   if (parent == 1)
905     dirmap[n++] = 1;
906   for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
907     {
908       if (used && !used[sib])
909         continue;
910       if (sib == 1 && parent == 1)
911         continue;       /* already did that one above */
912       dirmap[n++] = sib;
913     }
914   lastn = n;
915   for (; parent < lastn; parent++)
916     {
917       sib = dirmap[parent];
918       if (used && used[sib] != 2)
919         continue;
920       child = dirpool_child(dp, sib);
921       if (child)
922         {
923           dirmap[n++] = -parent;
924           n = traverse_dirs(dp, dirmap, n, child, used);
925         }
926     }
927   return n;
928 }
929
930 static void
931 write_compressed_page(FILE *fp, unsigned char *page, int len)
932 {
933   int clen;
934   unsigned char cpage[BLOB_PAGESIZE];
935
936   clen = repopagestore_compress_page(page, len, cpage, len - 1);
937   if (!clen)
938     {
939       write_u32(fp, len * 2);
940       write_blob(fp, page, len);
941     }
942   else
943     {
944       write_u32(fp, clen * 2 + 1);
945       write_blob(fp, cpage, clen);
946     }
947 }
948
949
950 #if 0
951 static Id subfilekeys[] = {
952   REPODATA_INFO, REPOKEY_TYPE_VOID,
953   REPODATA_EXTERNAL, REPOKEY_TYPE_VOID,
954   REPODATA_KEYS, REPOKEY_TYPE_IDARRAY,
955   REPODATA_LOCATION, REPOKEY_TYPE_STR,
956   REPODATA_ADDEDFILEPROVIDES, REPOKEY_TYPE_REL_IDARRAY,
957   REPODATA_RPMDBCOOKIE, REPOKEY_TYPE_SHA256,
958   0,
959 };
960 #endif
961
962 static Id verticals[] = {
963   SOLVABLE_AUTHORS,
964   SOLVABLE_DESCRIPTION,
965   SOLVABLE_MESSAGEDEL,
966   SOLVABLE_MESSAGEINS,
967   SOLVABLE_EULA,
968   SOLVABLE_DISKUSAGE,
969   SOLVABLE_FILELIST,
970   0
971 };
972
973 static char *languagetags[] = {
974   "solvable:summary:",
975   "solvable:description:",
976   "solvable:messageins:",
977   "solvable:messagedel:",
978   "solvable:eula:",
979   0
980 };
981
982 int
983 repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
984 {
985   const char *keyname;
986   int i;
987
988   for (i = 0; verticals[i]; i++)
989     if (key->name == verticals[i])
990       return KEY_STORAGE_VERTICAL_OFFSET;
991   keyname = id2str(repo->pool, key->name);
992   for (i = 0; languagetags[i] != 0; i++)
993     if (!strncmp(keyname, languagetags[i], strlen(languagetags[i])))
994       return KEY_STORAGE_VERTICAL_OFFSET;
995   return KEY_STORAGE_INCORE;
996 }
997
998 /*
999  * Repo
1000  */
1001
1002 void
1003 repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Id **keyarrayp)
1004 {
1005   Pool *pool = repo->pool;
1006   int i, j, k, n;
1007   Solvable *s;
1008   NeedId *needid;
1009   int nstrings, nrels;
1010   unsigned int sizeid;
1011   unsigned int solv_flags;
1012   Reldep *ran;
1013   Id *idarraydata;
1014
1015   Id id, *sp;
1016
1017   Id *dirmap;
1018   int ndirmap;
1019   Id *keyused;
1020   unsigned char *repodataused;
1021   int anyrepodataused = 0;
1022   
1023   struct cbdata cbdata;
1024   int needrels;
1025   Repokey *key;
1026   int poolusage, dirpoolusage, idused, dirused;
1027   int reloff;
1028
1029   Repodata *data, *dirpooldata = 0;
1030   Stringpool ownspool, *spool;
1031   Dirpool owndirpool, *dirpool;
1032
1033   Id *repodataschemata = 0;
1034   Id mainschema;
1035
1036   struct extdata *xd;
1037
1038   Id type_constantid = 0;
1039
1040   memset(&cbdata, 0, sizeof(cbdata));
1041   cbdata.repo = repo;
1042
1043   /* go through all repodata and find the keys we need */
1044   /* also unify keys */
1045   /* creates: mykeys      - key array, still has global pool ids */
1046   /*          keymapstart - maps repo number to keymap offset */
1047   /*          keymap      - maps repo key to my key, 0 -> not used */
1048
1049   /* start with all KEY_STORAGE_SOLVABLE ids */
1050
1051   n = ID_NUM_INTERNAL;
1052   for (i = 0; i < repo->nrepodata; i++)
1053     n += repo->repodata[i].nkeys;
1054   cbdata.mykeys = sat_calloc(n, sizeof(Repokey));
1055   cbdata.keymap = sat_calloc(n, sizeof(Id));
1056   cbdata.keymapstart = sat_calloc(repo->nrepodata, sizeof(Id));
1057   repodataused = sat_calloc(repo->nrepodata, 1);
1058
1059   cbdata.nmykeys = 1;
1060   needrels = 0;
1061   poolusage = 0;
1062   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
1063     {
1064       key = cbdata.mykeys + i;
1065       key->name = i;
1066       if (i < SOLVABLE_PROVIDES)
1067         key->type = REPOKEY_TYPE_ID;
1068       else if (i < RPM_RPMDBID)
1069         key->type = REPOKEY_TYPE_REL_IDARRAY;
1070       else
1071         key->type = REPOKEY_TYPE_U32;
1072       key->size = 0;
1073       key->storage = KEY_STORAGE_SOLVABLE;
1074       if (keyfilter)
1075         {
1076           key->storage = keyfilter(repo, key, kfdata);
1077           if (key->storage == KEY_STORAGE_DROPPED)
1078             continue;
1079           key->storage = KEY_STORAGE_SOLVABLE;
1080         }
1081       poolusage = 1;
1082       if (key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1083         needrels = 1;
1084       cbdata.keymap[i] = i;
1085     }
1086   cbdata.nmykeys = i;
1087
1088   if (repo->nsolvables)
1089     {
1090       key = cbdata.mykeys + cbdata.nmykeys;
1091       key->name = REPOSITORY_SOLVABLES;
1092       key->type = REPOKEY_TYPE_FLEXARRAY;
1093       key->size = 0;
1094       key->storage = KEY_STORAGE_INCORE;
1095       cbdata.keymap[key->name] = cbdata.nmykeys++;
1096     }
1097
1098 #if 0
1099   /* If we store subfile info, generate the necessary keys.  */
1100   if (nsubfiles)
1101     {
1102       for (i = 0; subfilekeys[i]; i += 2)
1103         {
1104           key = cbdata.mykeys + cbdata.nmykeys;
1105           key->name = subfilekeys[i];
1106           key->type = subfilekeys[i + 1];
1107           key->size = 0;
1108           key->storage = KEY_STORAGE_SOLVABLE;
1109           cbdata.keymap[key->name] = cbdata.nmykeys++;
1110         }
1111     }
1112 #endif
1113
1114   dirpoolusage = 0;
1115
1116   spool = 0;
1117   dirpool = 0;
1118   n = ID_NUM_INTERNAL;
1119   for (i = 0; i < repo->nrepodata; i++)
1120     {
1121       data = repo->repodata + i;
1122       cbdata.keymapstart[i] = n;
1123       cbdata.keymap[n++] = 0;   /* key 0 */
1124       idused = 0;
1125       dirused = 0;
1126       if (keyfilter)
1127         {
1128           Repokey zerokey;
1129           /* check if we want this repodata */
1130           memset(&zerokey, 0, sizeof(zerokey));
1131           zerokey.name = 1;
1132           zerokey.type = 1;
1133           zerokey.size = i;
1134           if (keyfilter(repo, &zerokey, kfdata) == -1)
1135             continue;
1136         }
1137       for (j = 1; j < data->nkeys; j++, n++)
1138         {
1139           key = data->keys + j;
1140           if (key->name == REPOSITORY_SOLVABLES && key->type == REPOKEY_TYPE_FLEXARRAY)
1141             {
1142               cbdata.keymap[n] = cbdata.keymap[key->name];
1143               continue;
1144             }
1145           /* see if we already had this one, should use hash for fast miss */
1146           for (k = 0; k < cbdata.nmykeys; k++)
1147             {
1148               if (key->name == cbdata.mykeys[k].name && key->type == cbdata.mykeys[k].type)
1149                 {
1150                   if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != cbdata.mykeys[k].size)
1151                     continue;
1152                   break;
1153                 }
1154             }
1155           if (k < cbdata.nmykeys)
1156             cbdata.keymap[n] = k;
1157           else
1158             {
1159               /* found a new key! */
1160               cbdata.mykeys[cbdata.nmykeys] = *key;
1161               key = cbdata.mykeys + cbdata.nmykeys;
1162               key->storage = KEY_STORAGE_INCORE;
1163               if (key->type != REPOKEY_TYPE_CONSTANT && key->type != REPOKEY_TYPE_CONSTANTID)
1164                 key->size = 0;
1165               if (keyfilter)
1166                 {
1167                   key->storage = keyfilter(repo, key, kfdata);
1168                   if (key->storage == KEY_STORAGE_DROPPED)
1169                     {
1170                       cbdata.keymap[n] = 0;
1171                       continue;
1172                     }
1173                 }
1174               cbdata.keymap[n] = cbdata.nmykeys++;
1175             }
1176           /* load repodata if not already loaded */
1177           if (data->state == REPODATA_STUB)
1178             {
1179               if (data->loadcallback)
1180                 data->loadcallback(data);
1181               else
1182                 data->state = REPODATA_ERROR;
1183               if (data->state != REPODATA_ERROR)
1184                 {
1185                   /* redo this repodata! */
1186                   j = 0;
1187                   n = cbdata.keymapstart[i];
1188                   continue;
1189                 }
1190             }
1191           if (data->state == REPODATA_ERROR)
1192             {
1193               /* too bad! */
1194               cbdata.keymap[n] = 0;
1195               continue;
1196             }
1197
1198           repodataused[i] = 1;
1199           anyrepodataused = 1;
1200           if (key->type != REPOKEY_TYPE_STR
1201               && key->type != REPOKEY_TYPE_U32
1202               && key->type != REPOKEY_TYPE_MD5
1203               && key->type != REPOKEY_TYPE_SHA1)
1204             idused = 1;
1205           if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1206             dirused = 1;
1207           /* make sure we know that key */
1208           if (data->localpool)
1209             {
1210               stringpool_str2id(&data->spool, id2str(pool, key->name), 1);
1211               stringpool_str2id(&data->spool, id2str(pool, key->type), 1);
1212               if (key->type == REPOKEY_TYPE_CONSTANTID)
1213                 stringpool_str2id(&data->spool, id2str(pool, key->size), 1);
1214             }
1215         }
1216       if (idused)
1217         {
1218           if (data->localpool)
1219             {
1220               if (poolusage)
1221                 poolusage = 3;  /* need local pool */
1222               else
1223                 {
1224                   poolusage = 2;
1225                   spool = &data->spool;
1226                 }
1227             }
1228           else
1229             {
1230               if (poolusage == 0)
1231                 poolusage = 1;
1232               else if (poolusage != 1)
1233                 poolusage = 3;  /* need local pool */
1234             }
1235         }
1236       if (dirused)
1237         {
1238           if (dirpoolusage)
1239             dirpoolusage = 3;   /* need local dirpool */
1240           else
1241             {
1242               dirpoolusage = 2;
1243               dirpool = &data->dirpool;
1244               dirpooldata = data;
1245             }
1246         }
1247     }
1248   cbdata.nkeymap = n;
1249
1250   /* 0: no pool needed at all */
1251   /* 1: use global pool */
1252   /* 2: use repodata local pool */
1253   /* 3: need own pool */
1254   if (poolusage == 3)
1255     {
1256       spool = &ownspool;
1257       if (needrels)
1258         {
1259           /* hack: reuse global pool so we don't have to map rel ids */
1260           stringpool_clone(spool, &repo->pool->ss);
1261         }
1262       else
1263         stringpool_init_empty(spool);
1264       cbdata.ownspool = spool;
1265     }
1266   else if (poolusage == 0 || poolusage == 1)
1267     {
1268       poolusage = 1;
1269       spool = &repo->pool->ss;
1270     }
1271   if (dirpoolusage == 3)
1272     {
1273       dirpool = &owndirpool;
1274       dirpooldata = 0;
1275       dirpool_init(dirpool);
1276       cbdata.owndirpool = dirpool;
1277     }
1278   else if (dirpool)
1279     cbdata.dirused = sat_calloc(dirpool->ndirs, sizeof(Id));
1280
1281
1282 /********************************************************************/
1283 #if 0
1284 fprintf(stderr, "poolusage: %d\n", poolusage);
1285 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1286 fprintf(stderr, "nmykeys: %d\n", cbdata.nmykeys);
1287 for (i = 1; i < cbdata.nmykeys; i++)
1288   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);
1289 #endif
1290
1291 /********************************************************************/
1292
1293   /* set needed count of all strings and rels,
1294    * find which keys are used in the solvables
1295    * put all strings in own spool
1296    */
1297
1298   reloff = spool->nstrings;
1299   if (poolusage == 3)
1300     reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1301
1302   needid = calloc(reloff + pool->nrels, sizeof(*needid));
1303   needid[0].map = reloff;
1304
1305   cbdata.needid = needid;
1306   cbdata.schema = sat_calloc(cbdata.nmykeys, sizeof(Id));
1307   cbdata.sp = cbdata.schema;
1308   cbdata.solvschemata = sat_calloc(repo->nsolvables, sizeof(Id));
1309 #if 0
1310   cbdata.extraschemata = sat_calloc(repo->nextra, sizeof(Id));
1311 #endif
1312
1313   /* create main schema */
1314   cbdata.sp = cbdata.schema;
1315   /* collect all other data from all repodatas */
1316   /* XXX: merge arrays of equal keys? */
1317   for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1318     {
1319       if (!repodataused[j])
1320         continue;
1321       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1322     }
1323   sp = cbdata.sp;
1324   /* add solvables if needed */
1325   if (repo->nsolvables)
1326     {
1327       *sp++ = cbdata.keymap[REPOSITORY_SOLVABLES];
1328       cbdata.mykeys[cbdata.keymap[REPOSITORY_SOLVABLES]].size++;
1329     }
1330   *sp = 0;
1331   mainschema = addschema(&cbdata, cbdata.schema);
1332
1333
1334   idarraydata = repo->idarraydata;
1335
1336   cbdata.doingsolvables = 1;
1337   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1338     {
1339       if (s->repo != repo)
1340         continue;
1341
1342       /* set schema info, keep in sync with further down */
1343       sp = cbdata.schema;
1344       if (cbdata.keymap[SOLVABLE_NAME])
1345         {
1346           *sp++ = SOLVABLE_NAME;
1347           needid[s->name].need++;
1348         }
1349       if (cbdata.keymap[SOLVABLE_ARCH])
1350         {
1351           *sp++ = SOLVABLE_ARCH;
1352           needid[s->arch].need++;
1353         }
1354       if (cbdata.keymap[SOLVABLE_EVR])
1355         {
1356           *sp++ = SOLVABLE_EVR;
1357           needid[s->evr].need++;
1358         }
1359       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1360         {
1361           *sp++ = SOLVABLE_VENDOR;
1362           needid[s->vendor].need++;
1363         }
1364       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1365         {
1366           *sp++ = SOLVABLE_PROVIDES;
1367           cbdata.mykeys[SOLVABLE_PROVIDES].size += incneedidarray(pool, idarraydata + s->provides, needid);
1368         }
1369       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1370         {
1371           *sp++ = SOLVABLE_OBSOLETES;
1372           cbdata.mykeys[SOLVABLE_OBSOLETES].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1373         }
1374       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1375         {
1376           *sp++ = SOLVABLE_CONFLICTS;
1377           cbdata.mykeys[SOLVABLE_CONFLICTS].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1378         }
1379       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1380         {
1381           *sp++ = SOLVABLE_REQUIRES;
1382           cbdata.mykeys[SOLVABLE_REQUIRES].size += incneedidarray(pool, idarraydata + s->requires, needid);
1383         }
1384       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1385         {
1386           *sp++ = SOLVABLE_RECOMMENDS;
1387           cbdata.mykeys[SOLVABLE_RECOMMENDS].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1388         }
1389       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1390         {
1391           *sp++ = SOLVABLE_SUGGESTS;
1392           cbdata.mykeys[SOLVABLE_SUGGESTS].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1393         }
1394       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1395         {
1396           *sp++ = SOLVABLE_SUPPLEMENTS;
1397           cbdata.mykeys[SOLVABLE_SUPPLEMENTS].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1398         }
1399       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1400         {
1401           *sp++ = SOLVABLE_ENHANCES;
1402           cbdata.mykeys[SOLVABLE_ENHANCES].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1403         }
1404       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1405         {
1406           *sp++ = RPM_RPMDBID;
1407           cbdata.mykeys[RPM_RPMDBID].size++;
1408         }
1409       cbdata.sp = sp;
1410
1411       if (anyrepodataused)
1412         {
1413           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1414             {
1415               if (!repodataused[j])
1416                 continue;
1417               if (i < data->start || i >= data->end)
1418                 continue;
1419               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_needed, &cbdata);
1420               needid = cbdata.needid;
1421             }
1422         }
1423       *cbdata.sp = 0;
1424       cbdata.solvschemata[n] = addschema(&cbdata, cbdata.schema);
1425       n++;
1426     }
1427   cbdata.doingsolvables = 0;
1428   assert(n == repo->nsolvables);
1429
1430 #if 0
1431   if (repo->nextra && anyrepodataused)
1432     for (i = -1; i >= -repo->nextra; i--)
1433       {
1434         Dataiterator di;
1435         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1436         cbdata.sp = cbdata.schema;
1437         while (dataiterator_step(&di))
1438           repo_write_collect_needed(&cbdata, repo, di.data, di.key, &di.kv);
1439         *cbdata.sp = 0;
1440         cbdata.extraschemata[-1 - i] = addschema(&cbdata, cbdata.schema);
1441       }
1442
1443   /* If we have fileinfos to write, setup schemas and increment needid[]
1444      of the right strings.  */
1445   for (i = 0; i < nsubfiles; i++)
1446     {
1447       int j;
1448       Id schema[4], *sp;
1449
1450       sp = schema;
1451       if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1452         {
1453           /* extra info about this file */
1454           *sp++ = cbdata.keymap[REPODATA_INFO];
1455           if (fileinfo[i].addedfileprovides)
1456             {
1457               *sp++ = cbdata.keymap[REPODATA_ADDEDFILEPROVIDES];
1458               for (j = 0; fileinfo[i].addedfileprovides[j]; j++)
1459                 ;
1460               cbdata.mykeys[cbdata.keymap[REPODATA_ADDEDFILEPROVIDES]].size += j + 1;
1461             }
1462           if (fileinfo[i].rpmdbcookie)
1463             *sp++ = cbdata.keymap[REPODATA_RPMDBCOOKIE];
1464         }
1465       else
1466         {
1467           *sp++ = cbdata.keymap[REPODATA_EXTERNAL];
1468           *sp++ = cbdata.keymap[REPODATA_KEYS];
1469           if (fileinfo[i].location)
1470             *sp++ = cbdata.keymap[REPODATA_LOCATION];
1471         }
1472       *sp = 0;
1473       repodataschemata[i] = addschema(&cbdata, schema);
1474       cbdata.mykeys[cbdata.keymap[REPODATA_KEYS]].size += 2 * fileinfo[i].nkeys + 1;
1475       for (j = 1; j < fileinfo[i].nkeys; j++)
1476         {
1477           needid[fileinfo[i].keys[j].type].need++;
1478           needid[fileinfo[i].keys[j].name].need++;
1479         }
1480     }
1481 #endif
1482
1483 /********************************************************************/
1484
1485   /* remove unused keys, convert ids to local ids and increment their needid */
1486   keyused = sat_calloc(cbdata.nmykeys, sizeof(Id));
1487   for (i = 0; i < cbdata.myschemadatalen; i++)
1488     keyused[cbdata.myschemadata[i]] = 1;
1489   keyused[0] = 0;
1490   for (n = i = 1; i < cbdata.nmykeys; i++)
1491     {
1492       if (!keyused[i])
1493         continue;
1494       keyused[i] = n;
1495       if (i != n)
1496         cbdata.mykeys[n] = cbdata.mykeys[i];
1497       if (cbdata.mykeys[n].type == REPOKEY_TYPE_CONSTANTID)
1498         {
1499           if (!type_constantid)
1500             type_constantid = poolusage > 1 ? stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1) : REPOKEY_TYPE_CONSTANTID;
1501           if (poolusage > 1)
1502             cbdata.mykeys[n].size = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].size), 1);
1503           needid[cbdata.mykeys[n].size].need++;
1504         }
1505       if (poolusage > 1)
1506         {
1507           cbdata.mykeys[n].name = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].name), 1);
1508           cbdata.mykeys[n].type = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1);
1509         }
1510       needid[cbdata.mykeys[n].name].need++;
1511       needid[cbdata.mykeys[n].type].need++;
1512       n++;
1513     }
1514   cbdata.nmykeys = n;
1515   for (i = 0; i < cbdata.myschemadatalen; i++)
1516     cbdata.myschemadata[i] = keyused[cbdata.myschemadata[i]];
1517   for (i = 0; i < cbdata.nkeymap; i++)
1518     cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1519   keyused = sat_free(keyused);
1520
1521 /********************************************************************/
1522
1523   /* increment need id for used dir components */
1524   if (cbdata.dirused && !cbdata.dirused[0])
1525     {
1526       /* no dirs used at all */
1527       cbdata.dirused = sat_free(cbdata.dirused);
1528       dirpool = 0;
1529     }
1530   if (dirpool)
1531     {
1532       for (i = 1; i < dirpool->ndirs; i++)
1533         {
1534 #if 0
1535 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1536 #endif
1537           id = dirpool->dirs[i];
1538           if (id <= 0)
1539             continue;
1540           if (cbdata.dirused && !cbdata.dirused[i])
1541             continue;
1542           if (cbdata.ownspool && dirpooldata && id > 1)
1543             {
1544               id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1545               needid = cbdata.needid;
1546             }
1547           needid[id].need++;
1548         }
1549     }
1550
1551   reloff = needid[0].map;
1552
1553
1554 /********************************************************************/
1555
1556   /*
1557    * create mapping table, new keys are sorted by needid[].need
1558    *
1559    * needid[key].need : old key -> new key
1560    * needid[key].map  : new key -> old key
1561    */
1562
1563   /* zero out id 0 and rel 0 just in case */
1564
1565   needid[0].need = 0;
1566   needid[reloff].need = 0;
1567
1568   for (i = 1; i < reloff + pool->nrels; i++)
1569     needid[i].map = i;
1570
1571 #if 0
1572   sat_sort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s, spool);
1573 #else
1574   /* make first entry '' */
1575   needid[1].need = 1;
1576   sat_sort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s, spool);
1577 #endif
1578   sat_sort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need, 0);
1579
1580   sizeid = 0;
1581   for (i = 1; i < reloff; i++)
1582     {
1583       if (!needid[i].need)
1584         break;
1585       needid[i].need = 0;
1586       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1587     }
1588
1589   nstrings = i;
1590   for (i = 1; i < nstrings; i++)
1591     needid[needid[i].map].need = i;
1592
1593   for (i = 0; i < pool->nrels; i++)
1594     {
1595       if (!needid[reloff + i].need)
1596         break;
1597       else
1598         needid[reloff + i].need = 0;
1599     }
1600
1601   nrels = i;
1602   for (i = 0; i < nrels; i++)
1603     needid[needid[reloff + i].map].need = nstrings + i;
1604
1605
1606 /********************************************************************/
1607
1608   /* create dir map */
1609   ndirmap = 0;
1610   dirmap = 0;
1611   if (dirpool)
1612     {
1613       if (cbdata.dirused && !cbdata.dirused[1])
1614         cbdata.dirused[1] = 1;  /* always want / entry */
1615       dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1616       dirmap[0] = 0;
1617       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1618       if (!cbdata.dirused)
1619         cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1620       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1621       for (i = 1; i < ndirmap; i++)
1622         {
1623           if (dirmap[i] <= 0)
1624             continue;
1625           cbdata.dirused[dirmap[i]] = i;
1626           id = dirpool->dirs[dirmap[i]];
1627           if (cbdata.ownspool && dirpooldata && id > 1)
1628             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1629           dirmap[i] = needid[id].need;
1630         }
1631     }
1632
1633 /********************************************************************/
1634   cbdata.extdata = sat_calloc(cbdata.nmykeys, sizeof(struct extdata));
1635
1636   xd = cbdata.extdata;
1637   cbdata.current_sub = 0;
1638   /* write main schema */
1639   cbdata.lastlen = 0;
1640   data_addid(xd, mainschema);
1641
1642 #if 1
1643   for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1644     {
1645       if (!repodataused[j])
1646         continue;
1647       repodata_search(data, SOLVID_META, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1648     }
1649 #endif
1650
1651   if (xd->len - cbdata.lastlen > cbdata.maxdata)
1652     cbdata.maxdata = xd->len - cbdata.lastlen;
1653   cbdata.lastlen = xd->len;
1654
1655   if (repo->nsolvables)
1656     data_addid(xd, repo->nsolvables);   /* FLEXARRAY nentries */
1657   cbdata.doingsolvables = 1;
1658   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1659     {
1660       if (s->repo != repo)
1661         continue;
1662       data_addid(xd, cbdata.solvschemata[n]);
1663       if (cbdata.keymap[SOLVABLE_NAME])
1664         data_addid(xd, needid[s->name].need);
1665       if (cbdata.keymap[SOLVABLE_ARCH])
1666         data_addid(xd, needid[s->arch].need);
1667       if (cbdata.keymap[SOLVABLE_EVR])
1668         data_addid(xd, needid[s->evr].need);
1669       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1670         data_addid(xd, needid[s->vendor].need);
1671       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1672         data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1673       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1674         data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1675       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1676         data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1677       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1678         data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1679       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1680         data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1681       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1682         data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1683       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1684         data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1685       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1686         data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1687       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1688         data_addu32(xd, repo->rpmdbid[i - repo->start]);
1689       if (anyrepodataused)
1690         {
1691           cbdata.vstart = -1;
1692           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1693             {
1694               if (!repodataused[j])
1695                 continue;
1696               if (i < data->start || i >= data->end)
1697                 continue;
1698               repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
1699             }
1700         }
1701       if (xd->len - cbdata.lastlen > cbdata.maxdata)
1702         cbdata.maxdata = xd->len - cbdata.lastlen;
1703       cbdata.lastlen = xd->len;
1704       n++;
1705     }
1706   cbdata.doingsolvables = 0;
1707
1708   assert(cbdata.current_sub == cbdata.nsubschemata);
1709   if (cbdata.subschemata)
1710     {
1711       cbdata.subschemata = sat_free(cbdata.subschemata);
1712       cbdata.nsubschemata = 0;
1713     }
1714
1715 #if 0
1716   if (repo->nextra && anyrepodataused)
1717     for (i = -1; i >= -repo->nextra; i--)
1718       {
1719         Dataiterator di;
1720         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1721         entrysize = xd->len;
1722         data_addid(xd, cbdata.extraschemata[-1 - i]);
1723         cbdata.vstart = -1;
1724         while (dataiterator_step(&di))
1725           repo_write_adddata(&cbdata, di.data, di.key, &di.kv);
1726         entrysize = xd->len - entrysize;
1727         if (entrysize > maxentrysize)
1728           maxentrysize = entrysize;
1729       }
1730 #endif
1731
1732 /********************************************************************/
1733
1734   /* write header */
1735
1736   /* write file header */
1737   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1738   write_u32(fp, SOLV_VERSION_8);
1739
1740
1741   /* write counts */
1742   write_u32(fp, nstrings);
1743   write_u32(fp, nrels);
1744   write_u32(fp, ndirmap);
1745   write_u32(fp, repo->nsolvables);
1746   write_u32(fp, cbdata.nmykeys);
1747   write_u32(fp, cbdata.nmyschemata);
1748   solv_flags = 0;
1749   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1750   write_u32(fp, solv_flags);
1751
1752   /*
1753    * calculate prefix encoding of the strings
1754    */
1755   unsigned char *prefixcomp = sat_malloc(nstrings);
1756   unsigned int compsum = 0;
1757   char *old_str = "";
1758   
1759   prefixcomp[0] = 0;
1760   for (i = 1; i < nstrings; i++)
1761     {
1762       char *str = spool->stringspace + spool->strings[needid[i].map];
1763       int same;
1764       for (same = 0; same < 255; same++)
1765         if (!old_str[same] || old_str[same] != str[same])
1766           break;
1767       prefixcomp[i] = same;
1768       compsum += same;
1769       old_str = str;
1770     }
1771
1772   /*
1773    * write strings
1774    */
1775   write_u32(fp, sizeid);
1776   /* we save compsum bytes but need 1 extra byte for every string */
1777   write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
1778   if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
1779     {
1780       for (i = 1; i < nstrings; i++)
1781         {
1782           char *str = spool->stringspace + spool->strings[needid[i].map];
1783           write_u8(fp, prefixcomp[i]);
1784           write_str(fp, str + prefixcomp[i]);
1785         }
1786     }
1787   sat_free(prefixcomp);
1788
1789 #if 0
1790   /* Build the prefix-encoding of the string pool.  We need to know
1791      the size of that before writing it to the file, so we have to
1792      build a separate buffer for that.  As it's temporarily possible
1793      that this actually is an expansion we can't easily reuse the 
1794      stringspace for this.  The max expansion per string is 1 byte,
1795      so it will fit into sizeid+nstrings bytes.  */
1796   char *prefix = sat_malloc(sizeid + nstrings);
1797   char *pp = prefix;
1798   char *old_str = "";
1799   for (i = 1; i < nstrings; i++)
1800     {
1801       char *str = spool->stringspace + spool->strings[needid[i].map];
1802       int same;
1803       size_t len;
1804       for (same = 0; same < 255; same++)
1805         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1806           break;
1807       *pp++ = same;
1808       len = strlen(str + same) + 1;
1809       memcpy(pp, str + same, len);
1810       pp += len;
1811       old_str = str;
1812     }
1813
1814   /*
1815    * write strings
1816    */
1817   write_u32(fp, sizeid);
1818   write_u32(fp, pp - prefix);
1819   if (pp != prefix)
1820     {
1821       if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1822         {
1823           perror("write error prefix");
1824           exit(1);
1825         }
1826     }
1827   sat_free(prefix);
1828 #endif
1829
1830   /*
1831    * write RelDeps
1832    */
1833   for (i = 0; i < nrels; i++)
1834     {
1835       ran = pool->rels + (needid[reloff + i].map - reloff);
1836       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1837       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1838       write_u8(fp, ran->flags);
1839     }
1840
1841   /*
1842    * write dirs (skip both root and / entry)
1843    */
1844   for (i = 2; i < ndirmap; i++)
1845     {
1846       if (dirmap[i] > 0)
1847         write_id(fp, dirmap[i]);
1848       else
1849         write_id(fp, nstrings - dirmap[i]);
1850     }
1851   sat_free(dirmap);
1852
1853   /*
1854    * write keys
1855    */
1856   if (keyarrayp)
1857     *keyarrayp = sat_calloc(2 * cbdata.nmykeys + 1, sizeof(Id));
1858   for (i = 1; i < cbdata.nmykeys; i++)
1859     {
1860       write_id(fp, needid[cbdata.mykeys[i].name].need);
1861       write_id(fp, needid[cbdata.mykeys[i].type].need);
1862       if (cbdata.mykeys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1863         {
1864           if (cbdata.mykeys[i].type == type_constantid)
1865             write_id(fp, needid[cbdata.mykeys[i].size].need);
1866           else
1867             write_id(fp, cbdata.mykeys[i].size);
1868         }
1869       else
1870         write_id(fp, cbdata.extdata[i].len);
1871       write_id(fp, cbdata.mykeys[i].storage);
1872       if (keyarrayp)
1873         {
1874           (*keyarrayp)[2 * i - 2] = cbdata.mykeys[i].name;
1875           (*keyarrayp)[2 * i - 1] = cbdata.mykeys[i].type;
1876         }
1877     }
1878
1879   /*
1880    * write schemata
1881    */
1882   write_id(fp, cbdata.myschemadatalen);
1883   if (cbdata.nmyschemata)
1884     {
1885       for (i = 1; i < cbdata.nmyschemata; i++)
1886         write_idarray(fp, pool, 0, cbdata.myschemadata + cbdata.myschemata[i]);
1887     }
1888
1889 #if 0
1890   /*
1891    * write info block
1892    */
1893   if (nsubfiles)
1894     {
1895       struct extdata xd;
1896       xd.buf = 0;
1897       xd.len = 0;
1898       int max = 0;
1899       int cur;
1900
1901       for (i = 0; i < nsubfiles; i++)
1902         {
1903           int j;
1904
1905           cur = xd.len;
1906           data_addid(&xd, repodataschemata[i]);
1907           if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1908             {
1909               if (fileinfo[i].addedfileprovides)
1910                 data_addidarray_sort(&xd, pool, needid, fileinfo[i].addedfileprovides, 0);
1911               if (fileinfo[i].rpmdbcookie)
1912                 data_addblob(&xd, fileinfo[i].rpmdbcookie, 32);
1913             }
1914           else
1915             {
1916               /* key,type array + location, write idarray */
1917               for (j = 1; j < fileinfo[i].nkeys; j++)
1918                 {
1919                   data_addideof(&xd, needid[fileinfo[i].keys[j].name].need, 0);
1920                   data_addideof(&xd, needid[fileinfo[i].keys[j].type].need, j == fileinfo[i].nkeys - 1);
1921                 }
1922               if (fileinfo[i].location)
1923                 data_addblob(&xd, (unsigned char *)fileinfo[i].location, strlen(fileinfo[i].location) + 1);
1924             }
1925           cur = xd.len - cur;
1926           if (cur > max)
1927             max = cur;
1928         }
1929       write_id(fp, max);
1930       write_id(fp, xd.len);
1931       write_blob(fp, xd.buf, xd.len);
1932       sat_free(xd.buf);
1933     }
1934 #endif
1935
1936 /********************************************************************/
1937
1938   write_id(fp, cbdata.maxdata);
1939   write_id(fp, cbdata.extdata[0].len);
1940   if (cbdata.extdata[0].len)
1941     write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1942   sat_free(cbdata.extdata[0].buf);
1943
1944 #if 0
1945   /*
1946    * write Solvable data
1947    */
1948   if (repo->nsolvables || repo->nextra)
1949     {
1950       write_id(fp, maxentrysize);
1951       write_id(fp, cbdata.extdata[0].len);
1952       write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1953     }
1954   sat_free(cbdata.extdata[0].buf);
1955 #endif
1956
1957   /* write vertical data */
1958   for (i = 1; i < cbdata.nmykeys; i++)
1959     if (cbdata.extdata[i].len)
1960       break;
1961   if (i < cbdata.nmykeys)
1962     {
1963       unsigned char *dp, vpage[BLOB_PAGESIZE];
1964       int l, ll, lpage = 0;
1965
1966       write_u32(fp, BLOB_PAGESIZE);
1967       for (i = 1; i < cbdata.nmykeys; i++)
1968         {
1969           if (!cbdata.extdata[i].len)
1970             continue;
1971           l = cbdata.extdata[i].len;
1972           dp = cbdata.extdata[i].buf;
1973           while (l)
1974             {
1975               ll = BLOB_PAGESIZE - lpage;
1976               if (l < ll)
1977                 ll = l;
1978               memcpy(vpage + lpage, dp, ll);
1979               dp += ll;
1980               lpage += ll;
1981               l -= ll;
1982               if (lpage == BLOB_PAGESIZE)
1983                 {
1984                   write_compressed_page(fp, vpage, lpage);
1985                   lpage = 0;
1986                 }
1987             }
1988         }
1989       if (lpage)
1990         write_compressed_page(fp, vpage, lpage);
1991     }
1992
1993 #if 0
1994   /* write vertical_offset entries */
1995   write_u32(fp, 0);     /* no paging */
1996   for (i = 1; i < cbdata.nmykeys; i++)
1997     if (cbdata.extdata[i].len)
1998       write_blob(fp, cbdata.extdata[i].buf, cbdata.extdata[i].len);
1999
2000   /* Fill fileinfo for our caller.  */
2001   if (setfileinfo)
2002     {
2003       fileinfo->checksum = 0;
2004       fileinfo->nchecksum = 0;
2005       fileinfo->checksumtype = 0;
2006       fileinfo->location = 0;
2007     }
2008 #endif
2009
2010   for (i = 1; i < cbdata.nmykeys; i++)
2011     sat_free(cbdata.extdata[i].buf);
2012   sat_free(cbdata.extdata);
2013
2014   if (cbdata.ownspool)
2015     {
2016       sat_free(cbdata.ownspool->strings);
2017       sat_free(cbdata.ownspool->stringspace);
2018       sat_free(cbdata.ownspool->stringhashtbl);
2019     }
2020   if (cbdata.owndirpool)
2021     {
2022       sat_free(cbdata.owndirpool->dirs);
2023       sat_free(cbdata.owndirpool->dirtraverse);
2024     }
2025   sat_free(needid);
2026   sat_free(cbdata.extraschemata);
2027   sat_free(cbdata.solvschemata);
2028   sat_free(cbdata.myschemadata);
2029   sat_free(cbdata.myschemata);
2030   sat_free(cbdata.schema);
2031
2032   sat_free(cbdata.mykeys);
2033   sat_free(cbdata.keymap);
2034   sat_free(cbdata.keymapstart);
2035   sat_free(cbdata.dirused);
2036   sat_free(repodataused);
2037   sat_free(repodataschemata);
2038 }
2039
2040 struct repodata_write_data {
2041   int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata);
2042   void *kfdata;
2043   int repodataid;
2044 };
2045
2046 static int
2047 repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
2048 {
2049   struct repodata_write_data *wd = kfdata;
2050
2051   /* XXX: special repodata selection hack */
2052   if (key->name == 1 && key->size != wd->repodataid)
2053     return -1;
2054   if (key->storage == KEY_STORAGE_SOLVABLE)
2055     return KEY_STORAGE_DROPPED; /* not part of this repodata */
2056   if (wd->keyfilter)
2057     return (*wd->keyfilter)(repo, key, wd->kfdata);
2058   return key->storage;
2059 }
2060
2061 void
2062 repodata_write(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
2063 {
2064   struct repodata_write_data wd;
2065
2066   wd.keyfilter = keyfilter;
2067   wd.kfdata = kfdata;
2068   wd.repodataid = data - data->repo->repodata;
2069   repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);
2070 }