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