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