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