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