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