re-add parsing of /etc/<xyz>-release (got lost in transistion from
[platform/upstream/libsolv.git] / tools / repo_write.c
1 /*
2  * Copyright (c) 2007, 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 binary file
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
26 #include "pool.h"
27 #include "util.h"
28 #include "repo_write.h"
29
30 /*------------------------------------------------------------------*/
31 /* Id map optimizations */
32
33 typedef struct needid {
34   Id need;
35   Id map;
36 } NeedId;
37
38
39 #define RELOFF(id) (needid[0].map + GETRELID(id))
40
41 /*
42  * increment need Id
43  * idarray: array of Ids, ID_NULL terminated
44  * needid: array of Id->NeedId
45  * 
46  * return count
47  * 
48  */
49
50 static void
51 incneedid(Pool *pool, Id id, NeedId *needid)
52 {
53   while (ISRELDEP(id))
54     {
55       Reldep *rd = GETRELDEP(pool, id);
56       needid[RELOFF(id)].need++;
57       if (ISRELDEP(rd->evr))
58         incneedid(pool, rd->evr, needid);
59       else
60         needid[rd->evr].need++;
61       id = rd->name;
62     }
63   needid[id].need++;
64 }
65
66 static int
67 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
68 {
69   Id id;
70   int n = 0;
71
72   if (!idarray)
73     return 0;
74   while ((id = *idarray++) != 0)
75     {
76       n++;
77       while (ISRELDEP(id))
78         {
79           Reldep *rd = GETRELDEP(pool, id);
80           needid[RELOFF(id)].need++;
81           if (ISRELDEP(rd->evr))
82             incneedid(pool, rd->evr, needid);
83           else
84             needid[rd->evr].need++;
85           id = rd->name;
86         }
87       needid[id].need++;
88     }
89   return n + 1;
90 }
91
92
93 /*
94  * 
95  */
96
97 static int
98 needid_cmp_need(const void *ap, const void *bp)
99 {
100   const NeedId *a = ap;
101   const NeedId *b = bp;
102   int r;
103   r = b->need - a->need;
104   if (r)
105     return r;
106   return a->map - b->map;
107 }
108
109 static Stringpool *cmp_spool;
110
111 static int
112 needid_cmp_need_s(const void *ap, const void *bp)
113 {
114   const NeedId *a = ap;
115   const NeedId *b = bp;
116   int r;
117   r = b->need - a->need;
118   if (r)
119     return r;
120   const char *as = cmp_spool->stringspace + cmp_spool->strings[a->map];
121   const char *bs = cmp_spool->stringspace + cmp_spool->strings[b->map];
122   return strcmp(as, bs);
123 }
124
125
126 /*------------------------------------------------------------------*/
127 /* output helper routines */
128
129 /*
130  * unsigned 32-bit
131  */
132
133 static void
134 write_u32(FILE *fp, unsigned int x)
135 {
136   if (putc(x >> 24, fp) == EOF ||
137       putc(x >> 16, fp) == EOF ||
138       putc(x >> 8, fp) == EOF ||
139       putc(x, fp) == EOF)
140     {
141       perror("write error u32");
142       exit(1);
143     }
144 }
145
146
147 /*
148  * unsigned 8-bit
149  */
150
151 static void
152 write_u8(FILE *fp, unsigned int x)
153 {
154   if (putc(x, fp) == EOF)
155     {
156       perror("write error u8");
157       exit(1);
158     }
159 }
160
161 /*
162  * data blob
163  */
164
165 static void
166 write_blob(FILE *fp, void *data, int len)
167 {
168   if (len && fwrite(data, len, 1, fp) != 1)
169     {
170       perror("write error blob");
171       exit(1);
172     }
173 }
174
175 /*
176  * Id
177  */
178
179 static void
180 write_id(FILE *fp, Id x)
181 {
182   if (x >= (1 << 14))
183     {
184       if (x >= (1 << 28))
185         putc((x >> 28) | 128, fp);
186       if (x >= (1 << 21))
187         putc((x >> 21) | 128, fp);
188       putc((x >> 14) | 128, fp);
189     }
190   if (x >= (1 << 7))
191     putc((x >> 7) | 128, fp);
192   if (putc(x & 127, fp) == EOF)
193     {
194       perror("write error id");
195       exit(1);
196     }
197 }
198
199 static inline void
200 write_id_eof(FILE *fp, Id x, int eof)
201 {
202   if (x >= 64)
203     x = (x & 63) | ((x & ~63) << 1);
204   write_id(fp, x | (eof ? 0 : 64));
205 }
206
207
208
209 #if 0
210 static void
211 write_str(FILE *fp, const char *str)
212 {
213   if (fputs (str, fp) == EOF || putc (0, fp) == EOF)
214     {
215       perror("write error");
216       exit(1);
217     }
218 }
219 #endif
220
221 /*
222  * Array of Ids
223  */
224
225 static void
226 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
227 {
228   Id id;
229   if (!ids)
230     return;
231   if (!*ids)
232     {
233       write_u8(fp, 0);
234       return;
235     }
236   for (;;)
237     {
238       id = *ids++;
239       if (needid)
240         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
241       if (id >= 64)
242         id = (id & 63) | ((id & ~63) << 1);
243       if (!*ids)
244         {
245           write_id(fp, id);
246           return;
247         }
248       write_id(fp, id | 64);
249     }
250 }
251
252 static int
253 cmp_ids (const void *pa, const void *pb)
254 {
255   Id a = *(Id *)pa;
256   Id b = *(Id *)pb;
257   return a - b;
258 }
259
260 #if 0
261 static void
262 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
263 {
264   int len, i;
265   Id lids[64], *sids;
266
267   if (!ids)
268     return;
269   if (!*ids)
270     {
271       write_u8(fp, 0);
272       return;
273     }
274   for (len = 0; len < 64 && ids[len]; len++)
275     {
276       Id id = ids[len];
277       if (needid)
278         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
279       lids[len] = id;
280     }
281   if (ids[len])
282     {
283       for (i = len + 1; ids[i]; i++)
284         ;
285       sids = sat_malloc2(i, sizeof(Id));
286       memcpy(sids, lids, 64 * sizeof(Id));
287       for (; ids[len]; len++)
288         {
289           Id id = ids[len];
290           if (needid)
291             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
292           sids[len] = id;
293         }
294     }
295   else
296     sids = lids;
297
298   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
299   if (needid)
300     marker = needid[marker].need;
301   for (i = 0; i < len; i++)
302     if (sids[i] == marker)
303       break;
304   if (i > 1)
305     qsort(sids, i, sizeof (Id), cmp_ids);
306   if ((len - i) > 2)
307     qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
308
309   Id id, old = 0;
310
311   /* The differencing above produces many runs of ones and twos.  I tried
312      fairly elaborate schemes to RLE those, but they give only very mediocre
313      improvements in compression, as coding the escapes costs quite some
314      space.  Even if they are coded only as bits in IDs.  The best improvement
315      was about 2.7% for the whole .solv file.  It's probably better to
316      invest some complexity into sharing idarrays, than RLEing.  */
317   for (i = 0; i < len - 1; i++)
318     {
319       id = sids[i];
320     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
321        hence all real differences are offsetted by 1.  Otherwise we would
322        have to handle negative differences, which would cost code space for
323        the encoding of the sign.  We loose the exact mapping of prereq here,
324        but we know the result, so we can recover from that in the reader.  */
325       if (id == marker)
326         id = old = 0;
327       else
328         {
329           id = id - old + 1;
330           old = sids[i];
331         }
332       /* XXX If difference is zero we have multiple equal elements,
333          we might want to skip writing them out.  */
334       if (id >= 64)
335         id = (id & 63) | ((id & ~63) << 1);
336       write_id(fp, id | 64);
337     }
338   id = sids[i];
339   if (id == marker)
340     id = 0;
341   else
342     id = id - old + 1;
343   if (id >= 64)
344     id = (id & 63) | ((id & ~63) << 1);
345   write_id(fp, id);
346   if (sids != lids)
347     sat_free(sids);
348 }
349 #endif
350
351
352 struct extdata {
353   unsigned char *buf;
354   int len;
355 };
356
357 struct cbdata {
358   Repo *repo;
359
360   Stringpool *ownspool;
361   Dirpool *owndirpool;
362
363   Repokey *mykeys;
364   int nmykeys;
365
366   Id *keymap;
367   int nkeymap;
368   Id *keymapstart;
369
370   NeedId *needid;
371
372   Id *schema;           /* schema construction space */
373   Id *sp;               /* pointer in above */
374   Id *oldschema, *oldsp;
375
376   Id *myschemata;
377   int nmyschemata;
378
379   Id *myschemadata;
380   int myschemadatalen;
381
382   Id schematacache[256];
383
384   Id *solvschemata;
385   Id *extraschemata;
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
397 #define NEEDED_BLOCK 1023
398 #define SCHEMATA_BLOCK 31
399 #define SCHEMATADATA_BLOCK 255
400 #define EXTDATA_BLOCK 4095
401
402 static inline void
403 data_addid(struct extdata *xd, Id x)
404 {
405   unsigned char *dp;
406   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
407   dp = xd->buf + xd->len;
408
409   if (x >= (1 << 14))
410     {
411       if (x >= (1 << 28))
412         *dp++ = (x >> 28) | 128;
413       if (x >= (1 << 21))
414         *dp++ = (x >> 21) | 128;
415       *dp++ = (x >> 14) | 128;
416     }
417   if (x >= (1 << 7))
418     *dp++ = (x >> 7) | 128;
419   *dp++ = x & 127;
420   xd->len = dp - xd->buf;
421 }
422
423 static inline void
424 data_addideof(struct extdata *xd, Id x, int eof)
425 {
426   if (x >= 64)
427     x = (x & 63) | ((x & ~63) << 1);
428   data_addid(xd, (eof ? x: x | 64));
429 }
430
431 static void
432 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
433 {
434   int len, i;
435   Id lids[64], *sids;
436
437   if (!ids)
438     return;
439   if (!*ids)
440     {
441       data_addid(xd, 0);
442       return;
443     }
444   for (len = 0; len < 64 && ids[len]; len++)
445     {
446       Id id = ids[len];
447       if (needid)
448         id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
449       lids[len] = id;
450     }
451   if (ids[len])
452     {
453       for (i = len + 1; ids[i]; i++)
454         ;
455       sids = sat_malloc2(i, sizeof(Id));
456       memcpy(sids, lids, 64 * sizeof(Id));
457       for (; ids[len]; len++)
458         {
459           Id id = ids[len];
460           if (needid)
461             id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
462           sids[len] = id;
463         }
464     }
465   else
466     sids = lids;
467
468   /* That bloody solvable:prereqmarker needs to stay in position :-(  */
469   if (needid)
470     marker = needid[marker].need;
471   for (i = 0; i < len; i++)
472     if (sids[i] == marker)
473       break;
474   if (i > 1)
475     qsort(sids, i, sizeof (Id), cmp_ids);
476   if ((len - i) > 2)
477     qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
478
479   Id id, old = 0;
480
481   /* The differencing above produces many runs of ones and twos.  I tried
482      fairly elaborate schemes to RLE those, but they give only very mediocre
483      improvements in compression, as coding the escapes costs quite some
484      space.  Even if they are coded only as bits in IDs.  The best improvement
485      was about 2.7% for the whole .solv file.  It's probably better to
486      invest some complexity into sharing idarrays, than RLEing.  */
487   for (i = 0; i < len - 1; i++)
488     {
489       id = sids[i];
490     /* Ugly PREREQ handling.  A "difference" of 0 is the prereq marker,
491        hence all real differences are offsetted by 1.  Otherwise we would
492        have to handle negative differences, which would cost code space for
493        the encoding of the sign.  We loose the exact mapping of prereq here,
494        but we know the result, so we can recover from that in the reader.  */
495       if (id == marker)
496         id = old = 0;
497       else
498         {
499           id = id - old + 1;
500           old = sids[i];
501         }
502       /* XXX If difference is zero we have multiple equal elements,
503          we might want to skip writing them out.  */
504       if (id >= 64)
505         id = (id & 63) | ((id & ~63) << 1);
506       data_addid(xd, id | 64);
507     }
508   id = sids[i];
509   if (id == marker)
510     id = 0;
511   else
512     id = id - old + 1;
513   if (id >= 64)
514     id = (id & 63) | ((id & ~63) << 1);
515   data_addid(xd, id);
516   if (sids != lids)
517     sat_free(sids);
518 }
519
520 static inline void
521 data_addblob(struct extdata *xd, unsigned char *blob, int len)
522 {
523   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
524   memcpy(xd->buf + xd->len, blob, len);
525   xd->len += len;
526 }
527
528 static inline void
529 data_addu32(struct extdata *xd, unsigned int num)
530 {
531   unsigned char d[4];
532   d[0] = num >> 24;
533   d[1] = num >> 16;
534   d[2] = num >> 8;
535   d[3] = num;
536   data_addblob(xd, d, 4);
537 }
538
539 static Id
540 addschema(struct cbdata *cbdata, Id *schema)
541 {
542   int h, len;
543   Id *sp, cid;
544
545   for (sp = schema, len = 0, h = 0; *sp; len++)
546     h = h * 7 + *sp++;
547   h &= 255;
548   len++;
549
550   cid = cbdata->schematacache[h];
551   if (cid)
552     {
553       if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
554         return cid;
555       /* cache conflict */
556       for (cid = 1; cid < cbdata->nmyschemata; cid++)
557         if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
558           return cid;
559     }
560   /* a new one. make room. */
561   if (!cbdata->nmyschemata)
562     {
563       /* allocate schema 0, it is always empty */
564       cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, 1, sizeof(Id), SCHEMATADATA_BLOCK);
565       cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
566       cbdata->myschemata[0] = 0;
567       cbdata->myschemadata[0] = 0;
568       cbdata->nmyschemata = 1;
569       cbdata->myschemadatalen = 1;
570     }
571   /* add schema */
572   cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
573   cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
574   memcpy(cbdata->myschemadata + cbdata->myschemadatalen, schema, len * sizeof(Id));
575   cbdata->myschemata[cbdata->nmyschemata] = cbdata->myschemadatalen;
576   cbdata->myschemadatalen += len;
577   cbdata->schematacache[h] = cbdata->nmyschemata;
578   return cbdata->nmyschemata++;
579 }
580
581 static Id
582 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
583 {
584   const char *str = stringpool_id2str(ss, id);
585   id = stringpool_str2id(cbdata->ownspool, str, 1);
586   if (id >= cbdata->needid[0].map)
587     {
588       int oldoff = cbdata->needid[0].map;
589       int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
590       int nrels = cbdata->repo->pool->nrels;
591       cbdata->needid = sat_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
592       if (nrels)
593         memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
594       memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
595       cbdata->needid[0].map = newoff;
596     }
597   return id;
598 }
599
600 static Id
601 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
602 {
603   Id compid, parent;
604
605   parent = dirpool_parent(dp, dir);
606   if (parent)
607     parent = putinowndirpool(cbdata, data, dp, parent);
608   compid = dp->dirs[dir];
609   if (cbdata->ownspool && compid > 1)
610     compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
611   return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
612 }
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 static int
635 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
636 {
637   Id id;
638   int rm;
639
640   rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
641   if (!rm)
642     return SEARCH_NEXT_KEY;     /* we do not want this one */
643   /* record key in schema */
644   if ((key->type != REPOKEY_TYPE_COUNTED || kv->eof == 0)
645       && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
646     *cbdata->sp++ = rm;
647   switch(key->type)
648     {
649       case REPOKEY_TYPE_ID:
650       case REPOKEY_TYPE_IDARRAY:
651         id = kv->id;
652         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
653           id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
654         incneedid(repo->pool, id, cbdata->needid);
655         break;
656       case REPOKEY_TYPE_DIR:
657       case REPOKEY_TYPE_DIRNUMNUMARRAY:
658       case REPOKEY_TYPE_DIRSTRARRAY:
659         id = kv->id;
660         if (cbdata->owndirpool)
661           putinowndirpool(cbdata, data, &data->dirpool, id);
662         else
663           setdirused(cbdata, &data->dirpool, id);
664         break;
665       case REPOKEY_TYPE_COUNTED:
666         if (kv->eof == 0)
667           {
668             if (cbdata->oldschema)
669               {
670                 fprintf(stderr, "nested structs not yet implemented\n");
671                 exit(1);
672               }
673             cbdata->oldschema = cbdata->schema;
674             cbdata->oldsp = cbdata->sp;
675             cbdata->schema = sat_calloc(cbdata->nmykeys, sizeof(Id));
676             cbdata->sp = cbdata->schema;
677           }
678         else if (kv->eof == 1)
679           {
680             cbdata->current_sub++;
681             *cbdata->sp = 0;
682             cbdata->subschemata = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
683             cbdata->subschemata[cbdata->nsubschemata++] = addschema(cbdata, cbdata->schema);
684 #if 0
685             fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
686 #endif
687             cbdata->sp = cbdata->schema;
688           }
689         else
690           {
691             sat_free(cbdata->schema);
692             cbdata->schema = cbdata->oldschema;
693             cbdata->sp = cbdata->oldsp;
694             cbdata->oldsp = cbdata->oldschema = 0;
695           }
696         break;
697       default:
698         break;
699     }
700   return 0;
701 }
702
703 static int
704 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
705 {
706   struct cbdata *cbdata = vcbdata;
707   Repo *repo = s ? s->repo : 0;
708 #if 0
709   fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - s->repo->pool->solvables : 0, s ? id2str(s->repo->pool, s->name) : "", key->name, id2str(repo->pool, key->name), key->type);
710 #endif
711   return repo_write_collect_needed(cbdata, repo, data, key, kv);
712 }
713
714 static int
715 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
716 {
717   int rm;
718   Id id;
719   unsigned int u32;
720   unsigned char v[4];
721   struct extdata *xd;
722
723   rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
724   if (!rm)
725     return 0;   /* we do not want this one */
726   
727   if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
728     {
729       xd = cbdata->extdata + rm;        /* vertical buffer */
730       if (cbdata->vstart == -1)
731         cbdata->vstart = xd->len;
732     }
733   else
734     xd = cbdata->extdata + 0;           /* incore buffer */
735   switch(key->type)
736     {
737       case REPOKEY_TYPE_VOID:
738       case REPOKEY_TYPE_CONSTANT:
739       case REPOKEY_TYPE_CONSTANTID:
740         break;
741       case REPOKEY_TYPE_ID:
742         id = kv->id;
743         if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
744           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
745         id = cbdata->needid[id].need;
746         data_addid(xd, id);
747         break;
748       case REPOKEY_TYPE_IDARRAY:
749         id = kv->id;
750         if (cbdata->ownspool && id > 1)
751           id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
752         id = cbdata->needid[id].need;
753         data_addideof(xd, id, kv->eof);
754         break;
755       case REPOKEY_TYPE_STR:
756         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
757         break;
758       case REPOKEY_TYPE_MD5:
759         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
760         break;
761       case REPOKEY_TYPE_SHA1:
762         data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
763         break;
764       case REPOKEY_TYPE_U32:
765         u32 = kv->num;
766         v[0] = u32 >> 24;
767         v[1] = u32 >> 16;
768         v[2] = u32 >> 8;
769         v[3] = u32;
770         data_addblob(xd, v, 4);
771         break;
772       case REPOKEY_TYPE_NUM:
773         data_addid(xd, kv->num);
774         break;
775       case REPOKEY_TYPE_DIR:
776         id = kv->id;
777         if (cbdata->owndirpool)
778           id = putinowndirpool(cbdata, data, &data->dirpool, id);
779         id = cbdata->dirused[id];
780         data_addid(xd, id);
781         break;
782       case REPOKEY_TYPE_DIRNUMNUMARRAY:
783         id = kv->id;
784         if (cbdata->owndirpool)
785           id = putinowndirpool(cbdata, data, &data->dirpool, id);
786         id = cbdata->dirused[id];
787         data_addid(xd, id);
788         data_addid(xd, kv->num);
789         data_addideof(xd, kv->num2, kv->eof);
790         break;
791       case REPOKEY_TYPE_DIRSTRARRAY:
792         id = kv->id;
793         if (cbdata->owndirpool)
794           id = putinowndirpool(cbdata, data, &data->dirpool, id);
795         id = cbdata->dirused[id];
796         data_addideof(xd, id, kv->eof);
797         data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
798         break;
799       case REPOKEY_TYPE_COUNTED:
800         if (kv->eof == 0)
801           {
802             if (kv->num)
803               {
804                 data_addid(xd, kv->num);
805                 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
806 #if 0
807                 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
808 #endif
809               }
810           }
811         else if (kv->eof == 1)
812           {
813             cbdata->current_sub++;
814           }
815         else
816           {
817           }
818         break;
819       default:
820         fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
821         exit(1);
822     }
823   if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
824     {
825       /* we can re-use old data in the blob here! */
826       data_addid(cbdata->extdata + 0, cbdata->vstart);                  /* add offset into incore data */
827       data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart);        /* add length into incore data */
828       cbdata->vstart = -1;
829     }
830   return 0;
831 }
832
833 static int
834 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
835 {
836   struct cbdata *cbdata = vcbdata;
837   return repo_write_adddata(cbdata, data, key, kv);
838 }
839
840 static int
841 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
842 {
843   Id sib, child;
844   Id parent, lastn;
845
846   parent = n;
847   /* special case for '/', which has to come first */
848   if (parent == 1)
849     dirmap[n++] = 1;
850   for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
851     {
852       if (used && !used[sib])
853         continue;
854       if (sib == 1 && parent == 1)
855         continue;       /* already did that one above */
856       dirmap[n++] = sib;
857     }
858   lastn = n;
859   for (; parent < lastn; parent++)
860     {
861       sib = dirmap[parent];
862       if (used && used[sib] != 2)
863         continue;
864       child = dirpool_child(dp, sib);
865       if (child)
866         {
867           dirmap[n++] = -parent;
868           n = traverse_dirs(dp, dirmap, n, child, used);
869         }
870     }
871   return n;
872 }
873
874 #define BLOB_PAGEBITS 15
875 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
876
877 static void
878 write_compressed_page(FILE *fp, unsigned char *page, int len)
879 {
880   int clen;
881   unsigned char cpage[BLOB_PAGESIZE];
882
883   clen = repodata_compress_page(page, len, cpage, len - 1);
884   if (!clen)
885     {
886       write_u32(fp, len * 2);
887       write_blob(fp, page, len);
888     }
889   else
890     {
891       write_u32(fp, clen * 2 + 1);
892       write_blob(fp, cpage, clen);
893     }
894 }
895
896
897 static Id subfilekeys[] = {
898   REPODATA_INFO, REPOKEY_TYPE_VOID,
899   REPODATA_EXTERNAL, REPOKEY_TYPE_VOID,
900   REPODATA_KEYS, REPOKEY_TYPE_IDARRAY,
901   REPODATA_LOCATION, REPOKEY_TYPE_STR,
902   REPODATA_ADDEDFILEPROVIDES, REPOKEY_TYPE_REL_IDARRAY,
903   REPODATA_RPMDBCOOKIE, REPOKEY_TYPE_SHA256,
904   0,
905 };
906
907 /*
908  * Repo
909  */
910
911 void
912 repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Repodatafile *fileinfo, int nsubfiles)
913 {
914   Pool *pool = repo->pool;
915   int i, j, k, n;
916   Solvable *s;
917   NeedId *needid;
918   int nstrings, nrels;
919   unsigned int sizeid;
920   unsigned int solv_flags;
921   Reldep *ran;
922   Id *idarraydata;
923
924   Id id, *sp;
925
926   Id *dirmap;
927   int ndirmap;
928   Id *keyused;
929   unsigned char *repodataused;
930   int anyrepodataused;
931   
932   struct cbdata cbdata;
933   int needrels;
934   Repokey *key;
935   int poolusage, dirpoolusage, idused, dirused;
936   int reloff;
937
938   Repodata *data, *dirpooldata = 0;
939   Stringpool ownspool, *spool;
940   Dirpool owndirpool, *dirpool;
941
942   int setfileinfo = 0;
943   Id *repodataschemata = 0;
944
945   struct extdata *xd;
946
947   int entrysize;
948   Id maxentrysize;
949   Id type_constantid = 0;
950
951   /* If we're given a fileinfo structure, but have no subfiles, then we're
952      writing a subfile and our callers wants info about it.  */
953   if (fileinfo && nsubfiles == 0)
954     setfileinfo = 1;
955   if (nsubfiles)
956     repodataschemata = sat_calloc(nsubfiles, sizeof(Id));
957
958   memset(&cbdata, 0, sizeof(cbdata));
959   cbdata.repo = repo;
960
961   /* go through all repodata and find the keys we need */
962   /* also unify keys */
963   /* creates: mykeys      - key array, still has global pool ids */
964   /*          keymapstart - maps repo number to keymap offset */
965   /*          keymap      - maps repo key to my key, 0 -> not used */
966
967   /* start with all KEY_STORAGE_SOLVABLE ids */
968
969   n = ID_NUM_INTERNAL;
970   for (i = 0; i < repo->nrepodata; i++)
971     n += repo->repodata[i].nkeys;
972   cbdata.mykeys = sat_calloc(n, sizeof(Repokey));
973   cbdata.keymap = sat_calloc(n, sizeof(Id));
974   cbdata.keymapstart = sat_calloc(repo->nrepodata, sizeof(Id));
975   repodataused = sat_calloc(repo->nrepodata, 1);
976
977   cbdata.nmykeys = 1;
978   needrels = 0;
979   poolusage = 0;
980   for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
981     {
982       key = cbdata.mykeys + i;
983       key->name = i;
984       if (i < SOLVABLE_PROVIDES)
985         key->type = REPOKEY_TYPE_ID;
986       else if (i < RPM_RPMDBID)
987         key->type = REPOKEY_TYPE_REL_IDARRAY;
988       else
989         key->type = REPOKEY_TYPE_U32;
990       key->size = 0;
991       key->storage = KEY_STORAGE_SOLVABLE;
992       if (keyfilter)
993         {
994           key->storage = keyfilter(repo, key, kfdata);
995           if (key->storage == KEY_STORAGE_DROPPED)
996             continue;
997           key->storage = KEY_STORAGE_SOLVABLE;
998         }
999       poolusage = 1;
1000       if (key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1001         needrels = 1;
1002       cbdata.keymap[i] = i;
1003     }
1004   cbdata.nmykeys = i;
1005
1006   /* If we store subfile info, generate the necessary keys.  */
1007   if (nsubfiles)
1008     {
1009       for (i = 0; subfilekeys[i]; i += 2)
1010         {
1011           key = cbdata.mykeys + cbdata.nmykeys;
1012           key->name = subfilekeys[i];
1013           key->type = subfilekeys[i + 1];
1014           key->size = 0;
1015           key->storage = KEY_STORAGE_SOLVABLE;
1016           cbdata.keymap[key->name] = cbdata.nmykeys++;
1017         }
1018     }
1019
1020   dirpoolusage = 0;
1021
1022   spool = 0;
1023   dirpool = 0;
1024   n = ID_NUM_INTERNAL;
1025   for (i = 0; i < repo->nrepodata; i++)
1026     {
1027       data = repo->repodata + i;
1028       cbdata.keymapstart[i] = n;
1029       cbdata.keymap[n++] = 0;   /* key 0 */
1030       idused = 0;
1031       dirused = 0;
1032       for (j = 1; j < data->nkeys; j++, n++)
1033         {
1034           key = data->keys + j;
1035           /* see if we already had this one, should use hash for fast miss */
1036           for (k = 0; k < cbdata.nmykeys; k++)
1037             {
1038               if (key->name == cbdata.mykeys[k].name && key->type == cbdata.mykeys[k].type)
1039                 {
1040                   if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != cbdata.mykeys[k].size)
1041                     continue;
1042                   break;
1043                 }
1044             }
1045           if (k < cbdata.nmykeys)
1046             cbdata.keymap[n] = k;
1047           else
1048             {
1049               /* found a new key! */
1050               cbdata.mykeys[cbdata.nmykeys] = *key;
1051               key = cbdata.mykeys + cbdata.nmykeys;
1052               key->storage = KEY_STORAGE_INCORE;
1053               if (key->type != REPOKEY_TYPE_CONSTANT && key->type != REPOKEY_TYPE_CONSTANTID)
1054                 key->size = 0;
1055               if (keyfilter)
1056                 {
1057                   key->storage = keyfilter(repo, key, kfdata);
1058                   if (key->storage == KEY_STORAGE_DROPPED)
1059                     {
1060                       cbdata.keymap[n] = 0;
1061                       continue;
1062                     }
1063                 }
1064               cbdata.keymap[n] = cbdata.nmykeys++;
1065             }
1066           /* load repodata if not already loaded */
1067           if (data->state == REPODATA_STUB)
1068             {
1069               if (data->loadcallback)
1070                 data->loadcallback(data);
1071               else
1072                 data->state = REPODATA_ERROR;
1073               if (data->state != REPODATA_ERROR)
1074                 {
1075                   /* redo this repodata! */
1076                   j = 0;
1077                   n = cbdata.keymapstart[i];
1078                   continue;
1079                 }
1080             }
1081           if (data->state == REPODATA_ERROR)
1082             {
1083               /* too bad! */
1084               cbdata.keymap[n] = 0;
1085               continue;
1086             }
1087
1088           repodataused[i] = 1;
1089           anyrepodataused = 1;
1090           if (key->type != REPOKEY_TYPE_STR
1091               && key->type != REPOKEY_TYPE_U32
1092               && key->type != REPOKEY_TYPE_MD5
1093               && key->type != REPOKEY_TYPE_SHA1)
1094             idused = 1;
1095           if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1096             dirused = 1;
1097           /* make sure we know that key */
1098           if (data->localpool)
1099             {
1100               stringpool_str2id(&data->spool, id2str(pool, key->name), 1);
1101               stringpool_str2id(&data->spool, id2str(pool, key->type), 1);
1102               if (key->type == REPOKEY_TYPE_CONSTANTID)
1103                 stringpool_str2id(&data->spool, id2str(pool, key->size), 1);
1104             }
1105         }
1106       if (idused)
1107         {
1108           if (data->localpool)
1109             {
1110               if (poolusage)
1111                 poolusage = 3;  /* need local pool */
1112               else
1113                 {
1114                   poolusage = 2;
1115                   spool = &data->spool;
1116                 }
1117             }
1118           else
1119             {
1120               if (poolusage == 0)
1121                 poolusage = 1;
1122               else if (poolusage != 1)
1123                 poolusage = 3;  /* need local pool */
1124             }
1125         }
1126       if (dirused)
1127         {
1128           if (dirpoolusage)
1129             dirpoolusage = 3;   /* need local dirpool */
1130           else
1131             {
1132               dirpoolusage = 2;
1133               dirpool = &data->dirpool;
1134               dirpooldata = data;
1135             }
1136         }
1137     }
1138   cbdata.nkeymap = n;
1139
1140   /* 0: no pool needed at all */
1141   /* 1: use global pool */
1142   /* 2: use repodata local pool */
1143   /* 3: need own pool */
1144   if (poolusage == 3)
1145     {
1146       spool = &ownspool;
1147       if (needrels)
1148         {
1149           /* hack: reuse global pool so we don't have to map rel ids */
1150           stringpool_clone(spool, &repo->pool->ss);
1151         }
1152       else
1153         stringpool_init_empty(spool);
1154       cbdata.ownspool = spool;
1155     }
1156   else if (poolusage == 0 || poolusage == 1)
1157     {
1158       poolusage = 1;
1159       spool = &repo->pool->ss;
1160     }
1161   if (dirpoolusage == 3)
1162     {
1163       dirpool = &owndirpool;
1164       dirpooldata = 0;
1165       dirpool_create(dirpool);
1166       cbdata.owndirpool = dirpool;
1167     }
1168   else if (dirpool)
1169     cbdata.dirused = sat_calloc(dirpool->ndirs, sizeof(Id));
1170
1171
1172 /********************************************************************/
1173 #if 0
1174 fprintf(stderr, "poolusage: %d\n", poolusage);
1175 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1176 fprintf(stderr, "nmykeys: %d\n", cbdata.nmykeys);
1177 for (i = 1; i < cbdata.nmykeys; i++)
1178   fprintf(stderr, "  %2d: %s[%d] %d %d %d\n", i, id2str(pool, cbdata.mykeys[i].name), cbdata.mykeys[i].name, cbdata.mykeys[i].type, cbdata.mykeys[i].size, cbdata.mykeys[i].storage);
1179 #endif
1180
1181 /********************************************************************/
1182
1183   /* set needed count of all strings and rels,
1184    * find which keys are used in the solvables
1185    * put all strings in own spool
1186    */
1187
1188   reloff = spool->nstrings;
1189   if (poolusage == 3)
1190     reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1191
1192   needid = calloc(reloff + pool->nrels, sizeof(*needid));
1193   needid[0].map = reloff;
1194
1195   cbdata.needid = needid;
1196   cbdata.schema = sat_calloc(cbdata.nmykeys, sizeof(Id));
1197   cbdata.sp = cbdata.schema;
1198   cbdata.solvschemata = sat_calloc(repo->nsolvables, sizeof(Id));
1199   cbdata.extraschemata = sat_calloc(repo->nextra, sizeof(Id));
1200
1201   idarraydata = repo->idarraydata;
1202
1203   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1204     {
1205       if (s->repo != repo)
1206         continue;
1207
1208       /* set schema info, keep in sync with further down */
1209       sp = cbdata.schema;
1210       if (cbdata.keymap[SOLVABLE_NAME])
1211         {
1212           *sp++ = SOLVABLE_NAME;
1213           needid[s->name].need++;
1214         }
1215       if (cbdata.keymap[SOLVABLE_ARCH])
1216         {
1217           *sp++ = SOLVABLE_ARCH;
1218           needid[s->arch].need++;
1219         }
1220       if (cbdata.keymap[SOLVABLE_EVR])
1221         {
1222           *sp++ = SOLVABLE_EVR;
1223           needid[s->evr].need++;
1224         }
1225       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1226         {
1227           *sp++ = SOLVABLE_VENDOR;
1228           needid[s->vendor].need++;
1229         }
1230       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1231         {
1232           *sp++ = SOLVABLE_PROVIDES;
1233           cbdata.mykeys[SOLVABLE_PROVIDES].size += incneedidarray(pool, idarraydata + s->provides, needid);
1234         }
1235       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1236         {
1237           *sp++ = SOLVABLE_OBSOLETES;
1238           cbdata.mykeys[SOLVABLE_OBSOLETES].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1239         }
1240       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1241         {
1242           *sp++ = SOLVABLE_CONFLICTS;
1243           cbdata.mykeys[SOLVABLE_CONFLICTS].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1244         }
1245       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1246         {
1247           *sp++ = SOLVABLE_REQUIRES;
1248           cbdata.mykeys[SOLVABLE_REQUIRES].size += incneedidarray(pool, idarraydata + s->requires, needid);
1249         }
1250       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1251         {
1252           *sp++ = SOLVABLE_RECOMMENDS;
1253           cbdata.mykeys[SOLVABLE_RECOMMENDS].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1254         }
1255       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1256         {
1257           *sp++ = SOLVABLE_SUGGESTS;
1258           cbdata.mykeys[SOLVABLE_SUGGESTS].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1259         }
1260       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1261         {
1262           *sp++ = SOLVABLE_SUPPLEMENTS;
1263           cbdata.mykeys[SOLVABLE_SUPPLEMENTS].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1264         }
1265       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1266         {
1267           *sp++ = SOLVABLE_ENHANCES;
1268           cbdata.mykeys[SOLVABLE_ENHANCES].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1269         }
1270       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1271         {
1272           *sp++ = RPM_RPMDBID;
1273           cbdata.mykeys[RPM_RPMDBID].size++;
1274         }
1275       cbdata.sp = sp;
1276
1277       if (anyrepodataused)
1278         {
1279           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1280             {
1281               if (!repodataused[j])
1282                 continue;
1283               if (i < data->start || i >= data->end)
1284                 continue;
1285               repodata_search(data, i - data->start, 0, repo_write_cb_needed, &cbdata);
1286               needid = cbdata.needid;
1287             }
1288         }
1289       *cbdata.sp = 0;
1290       cbdata.solvschemata[n] = addschema(&cbdata, cbdata.schema);
1291       n++;
1292     }
1293   if (repo->nextra && anyrepodataused)
1294     for (i = -1; i >= -repo->nextra; i--)
1295       {
1296         Dataiterator di;
1297         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1298         cbdata.sp = cbdata.schema;
1299         while (dataiterator_step(&di))
1300           repo_write_collect_needed(&cbdata, repo, di.data, di.key, &di.kv);
1301         *cbdata.sp = 0;
1302         cbdata.extraschemata[-1 - i] = addschema(&cbdata, cbdata.schema);
1303       }
1304
1305   /* If we have fileinfos to write, setup schemas and increment needid[]
1306      of the right strings.  */
1307   for (i = 0; i < nsubfiles; i++)
1308     {
1309       int j;
1310       Id schema[4], *sp;
1311
1312       sp = schema;
1313       if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1314         {
1315           /* extra info about this file */
1316           *sp++ = cbdata.keymap[REPODATA_INFO];
1317           if (fileinfo[i].addedfileprovides)
1318             {
1319               *sp++ = cbdata.keymap[REPODATA_ADDEDFILEPROVIDES];
1320               for (j = 0; fileinfo[i].addedfileprovides[j]; j++)
1321                 ;
1322               cbdata.mykeys[cbdata.keymap[REPODATA_ADDEDFILEPROVIDES]].size += j + 1;
1323             }
1324           if (fileinfo[i].rpmdbcookie)
1325             *sp++ = cbdata.keymap[REPODATA_RPMDBCOOKIE];
1326         }
1327       else
1328         {
1329           *sp++ = cbdata.keymap[REPODATA_EXTERNAL];
1330           *sp++ = cbdata.keymap[REPODATA_KEYS];
1331           if (fileinfo[i].location)
1332             *sp++ = cbdata.keymap[REPODATA_LOCATION];
1333         }
1334       *sp = 0;
1335       repodataschemata[i] = addschema(&cbdata, schema);
1336       cbdata.mykeys[cbdata.keymap[REPODATA_KEYS]].size += 2 * fileinfo[i].nkeys + 1;
1337       for (j = 1; j < fileinfo[i].nkeys; j++)
1338         {
1339           needid[fileinfo[i].keys[j].type].need++;
1340           needid[fileinfo[i].keys[j].name].need++;
1341         }
1342     }
1343
1344 /********************************************************************/
1345
1346   /* remove unused keys, convert ids to local ids and increment their needid */
1347   keyused = sat_calloc(cbdata.nmykeys, sizeof(Id));
1348   for (i = 0; i < cbdata.myschemadatalen; i++)
1349     keyused[cbdata.myschemadata[i]] = 1;
1350   keyused[0] = 0;
1351   for (n = i = 1; i < cbdata.nmykeys; i++)
1352     {
1353       if (!keyused[i])
1354         continue;
1355       keyused[i] = n;
1356       if (i != n)
1357         cbdata.mykeys[n] = cbdata.mykeys[i];
1358       if (cbdata.mykeys[n].type == REPOKEY_TYPE_CONSTANTID)
1359         {
1360           if (!type_constantid)
1361             type_constantid = poolusage > 1 ? stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1) : REPOKEY_TYPE_CONSTANTID;
1362           if (poolusage > 1)
1363             cbdata.mykeys[n].size = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].size), 1);
1364           needid[cbdata.mykeys[n].size].need++;
1365         }
1366       if (poolusage > 1)
1367         {
1368           cbdata.mykeys[n].name = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].name), 1);
1369           cbdata.mykeys[n].type = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1);
1370         }
1371       needid[cbdata.mykeys[n].name].need++;
1372       needid[cbdata.mykeys[n].type].need++;
1373       n++;
1374     }
1375   cbdata.nmykeys = n;
1376   for (i = 0; i < cbdata.myschemadatalen; i++)
1377     cbdata.myschemadata[i] = keyused[cbdata.myschemadata[i]];
1378   for (i = 0; i < cbdata.nkeymap; i++)
1379     cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1380   keyused = sat_free(keyused);
1381
1382 /********************************************************************/
1383
1384   /* increment need id for used dir components */
1385   if (cbdata.dirused && !cbdata.dirused[0])
1386     {
1387       /* no dirs used at all */
1388       cbdata.dirused = sat_free(cbdata.dirused);
1389       dirpool = 0;
1390     }
1391   if (dirpool)
1392     {
1393       for (i = 1; i < dirpool->ndirs; i++)
1394         {
1395 #if 0
1396 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1397 #endif
1398           id = dirpool->dirs[i];
1399           if (id <= 0)
1400             continue;
1401           if (cbdata.dirused && !cbdata.dirused[i])
1402             continue;
1403           if (cbdata.ownspool && dirpooldata && id > 1)
1404             {
1405               id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1406               needid = cbdata.needid;
1407             }
1408           needid[id].need++;
1409         }
1410     }
1411
1412   reloff = needid[0].map;
1413
1414
1415 /********************************************************************/
1416
1417   /*
1418    * create mapping table, new keys are sorted by needid[].need
1419    *
1420    * needid[key].need : old key -> new key
1421    * needid[key].map  : new key -> old key
1422    */
1423
1424   /* zero out id 0 and rel 0 just in case */
1425
1426   needid[0].need = 0;
1427   needid[reloff].need = 0;
1428
1429   for (i = 1; i < reloff + pool->nrels; i++)
1430     needid[i].map = i;
1431
1432   cmp_spool = spool;
1433 #if 0
1434   qsort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s);
1435 #else
1436   /* make first entry '' */
1437   needid[1].need = 1;
1438   qsort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s);
1439 #endif
1440   qsort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need);
1441
1442   sizeid = 0;
1443   for (i = 1; i < reloff; i++)
1444     {
1445       if (!needid[i].need)
1446         break;
1447       needid[i].need = 0;
1448       sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1449     }
1450
1451   nstrings = i;
1452   for (i = 1; i < nstrings; i++)
1453     needid[needid[i].map].need = i;
1454
1455   for (i = 0; i < pool->nrels; i++)
1456     {
1457       if (!needid[reloff + i].need)
1458         break;
1459       else
1460         needid[reloff + i].need = 0;
1461     }
1462
1463   nrels = i;
1464   for (i = 0; i < nrels; i++)
1465     needid[needid[reloff + i].map].need = nstrings + i;
1466
1467
1468 /********************************************************************/
1469
1470   /* create dir map */
1471   ndirmap = 0;
1472   dirmap = 0;
1473   if (dirpool)
1474     {
1475       if (cbdata.dirused && !cbdata.dirused[1])
1476         cbdata.dirused[1] = 1;  /* always want / entry */
1477       dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1478       dirmap[0] = 0;
1479       ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1480       if (!cbdata.dirused)
1481         cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1482       memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1483       for (i = 1; i < ndirmap; i++)
1484         {
1485           if (dirmap[i] <= 0)
1486             continue;
1487           cbdata.dirused[dirmap[i]] = i;
1488           id = dirpool->dirs[dirmap[i]];
1489           if (cbdata.ownspool && dirpooldata && id > 1)
1490             id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1491           dirmap[i] = needid[id].need;
1492         }
1493     }
1494
1495 /********************************************************************/
1496   cbdata.extdata = sat_calloc(cbdata.nmykeys, sizeof(struct extdata));
1497   
1498   cbdata.current_sub = 0;
1499   xd = cbdata.extdata;
1500   maxentrysize = 0;
1501   for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1502     {
1503       if (s->repo != repo)
1504         continue;
1505       entrysize = xd->len;
1506       data_addid(xd, cbdata.solvschemata[n]);
1507       if (cbdata.keymap[SOLVABLE_NAME])
1508         data_addid(xd, needid[s->name].need);
1509       if (cbdata.keymap[SOLVABLE_ARCH])
1510         data_addid(xd, needid[s->arch].need);
1511       if (cbdata.keymap[SOLVABLE_EVR])
1512         data_addid(xd, needid[s->evr].need);
1513       if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1514         data_addid(xd, needid[s->vendor].need);
1515       if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1516         data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1517       if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1518         data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1519       if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1520         data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1521       if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1522         data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1523       if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1524         data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1525       if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1526         data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1527       if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1528         data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1529       if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1530         data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1531       if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1532         data_addu32(xd, repo->rpmdbid[i - repo->start]);
1533       if (anyrepodataused)
1534         {
1535           cbdata.vstart = -1;
1536           for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1537             {
1538               if (!repodataused[j])
1539                 continue;
1540               if (i < data->start || i >= data->end)
1541                 continue;
1542               repodata_search(data, i - data->start, 0, repo_write_cb_adddata, &cbdata);
1543             }
1544         }
1545       entrysize = xd->len - entrysize;
1546       if (entrysize > maxentrysize)
1547         maxentrysize = entrysize;
1548       n++;
1549     }
1550
1551   if (repo->nextra && anyrepodataused)
1552     for (i = -1; i >= -repo->nextra; i--)
1553       {
1554         Dataiterator di;
1555         dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1556         entrysize = xd->len;
1557         data_addid(xd, cbdata.extraschemata[-1 - i]);
1558         cbdata.vstart = -1;
1559         while (dataiterator_step(&di))
1560           repo_write_adddata(&cbdata, di.data, di.key, &di.kv);
1561         entrysize = xd->len - entrysize;
1562         if (entrysize > maxentrysize)
1563           maxentrysize = entrysize;
1564       }
1565
1566 /********************************************************************/
1567
1568   /* write header */
1569
1570   /* write file header */
1571   write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1572   write_u32(fp, repo->nextra ? SOLV_VERSION_7 : SOLV_VERSION_6);
1573
1574
1575   /* write counts */
1576   write_u32(fp, nstrings);
1577   write_u32(fp, nrels);
1578   write_u32(fp, ndirmap);
1579   write_u32(fp, repo->nsolvables);
1580   write_u32(fp, cbdata.nmykeys);
1581   write_u32(fp, cbdata.nmyschemata);
1582   write_u32(fp, nsubfiles);     /* info blocks.  */
1583   if (repo->nextra)
1584     {
1585       write_u32(fp, repo->nextra);
1586       write_u32(fp, SOLV_CONTENT_VERSION);
1587     }
1588   solv_flags = 0;
1589   solv_flags |= SOLV_FLAG_PREFIX_POOL;
1590   write_u32(fp, solv_flags);
1591
1592   /* Build the prefix-encoding of the string pool.  We need to know
1593      the size of that before writing it to the file, so we have to
1594      build a separate buffer for that.  As it's temporarily possible
1595      that this actually is an expansion we can't easily reuse the 
1596      stringspace for this.  The max expansion per string is 1 byte,
1597      so it will fit into sizeid+nstrings bytes.  */
1598   char *prefix = sat_malloc(sizeid + nstrings);
1599   char *pp = prefix;
1600   char *old_str = "";
1601   for (i = 1; i < nstrings; i++)
1602     {
1603       char *str = spool->stringspace + spool->strings[needid[i].map];
1604       int same;
1605       size_t len;
1606       for (same = 0; same < 255; same++)
1607         if (!old_str[same] || !str[same] || old_str[same] != str[same])
1608           break;
1609       *pp++ = same;
1610       len = strlen(str + same) + 1;
1611       memcpy (pp, str + same, len);
1612       pp += len;
1613       old_str = str;
1614     }
1615
1616   /*
1617    * write strings
1618    */
1619   write_u32(fp, sizeid);
1620   write_u32(fp, pp - prefix);
1621   if (pp != prefix)
1622     {
1623       if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1624         {
1625           perror("write error prefix");
1626           exit(1);
1627         }
1628     }
1629   sat_free(prefix);
1630
1631   /*
1632    * write RelDeps
1633    */
1634   for (i = 0; i < nrels; i++)
1635     {
1636       ran = pool->rels + (needid[reloff + i].map - reloff);
1637       write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1638       write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1639       write_u8(fp, ran->flags);
1640     }
1641
1642   /*
1643    * write dirs (skip both root and / entry)
1644    */
1645   for (i = 2; i < ndirmap; i++)
1646     {
1647       if (dirmap[i] > 0)
1648         write_id(fp, dirmap[i]);
1649       else
1650         write_id(fp, nstrings - dirmap[i]);
1651     }
1652   sat_free(dirmap);
1653
1654   /*
1655    * write keys
1656    */
1657   if (setfileinfo)
1658     {
1659       fileinfo->nkeys = cbdata.nmykeys;
1660       fileinfo->keys = sat_calloc(fileinfo->nkeys, sizeof (*fileinfo->keys));
1661     }
1662   for (i = 1; i < cbdata.nmykeys; i++)
1663     {
1664       write_id(fp, needid[cbdata.mykeys[i].name].need);
1665       write_id(fp, needid[cbdata.mykeys[i].type].need);
1666       if (cbdata.mykeys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1667         {
1668           if (cbdata.mykeys[i].type == type_constantid)
1669             write_id(fp, needid[cbdata.mykeys[i].size].need);
1670           else
1671             write_id(fp, cbdata.mykeys[i].size);
1672         }
1673       else
1674         write_id(fp, cbdata.extdata[i].len);
1675       write_id(fp, cbdata.mykeys[i].storage);
1676       if (setfileinfo)
1677         fileinfo->keys[i] = cbdata.mykeys[i];
1678     }
1679
1680   /*
1681    * write schemata
1682    */
1683   write_id(fp, cbdata.myschemadatalen);
1684   if (cbdata.nmyschemata)
1685     {
1686       for (i = 1; i < cbdata.nmyschemata; i++)
1687         write_idarray(fp, pool, 0, cbdata.myschemadata + cbdata.myschemata[i]);
1688     }
1689
1690   /*
1691    * write info block
1692    */
1693   if (nsubfiles)
1694     {
1695       struct extdata xd;
1696       xd.buf = 0;
1697       xd.len = 0;
1698       int max = 0;
1699       int cur;
1700
1701       for (i = 0; i < nsubfiles; i++)
1702         {
1703           int j;
1704
1705           cur = xd.len;
1706           data_addid(&xd, repodataschemata[i]);
1707           if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1708             {
1709               if (fileinfo[i].addedfileprovides)
1710                 data_addidarray_sort(&xd, pool, needid, fileinfo[i].addedfileprovides, 0);
1711               if (fileinfo[i].rpmdbcookie)
1712                 data_addblob(&xd, fileinfo[i].rpmdbcookie, 32);
1713             }
1714           else
1715             {
1716               /* key,type array + location, write idarray */
1717               for (j = 1; j < fileinfo[i].nkeys; j++)
1718                 {
1719                   data_addideof(&xd, needid[fileinfo[i].keys[j].name].need, 0);
1720                   data_addideof(&xd, needid[fileinfo[i].keys[j].type].need, j == fileinfo[i].nkeys - 1);
1721                 }
1722               if (fileinfo[i].location)
1723                 data_addblob(&xd, (unsigned char *)fileinfo[i].location, strlen(fileinfo[i].location) + 1);
1724             }
1725           cur = xd.len - cur;
1726           if (cur > max)
1727             max = cur;
1728         }
1729       write_id(fp, max);
1730       write_id(fp, xd.len);
1731       write_blob(fp, xd.buf, xd.len);
1732       sat_free(xd.buf);
1733     }
1734
1735 /********************************************************************/
1736
1737
1738   /*
1739    * write Solvable data
1740    */
1741   if (repo->nsolvables || repo->nextra)
1742     {
1743       write_id(fp, maxentrysize);
1744       write_id(fp, cbdata.extdata[0].len);
1745       write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1746     }
1747   sat_free(cbdata.extdata[0].buf);
1748
1749   /* write vertical data */
1750   for (i = 1; i < cbdata.nmykeys; i++)
1751     if (cbdata.extdata[i].len)
1752       break;
1753   if (i < cbdata.nmykeys)
1754     {
1755       unsigned char *dp, vpage[BLOB_PAGESIZE];
1756       int l, ll, lpage = 0;
1757
1758       write_u32(fp, BLOB_PAGESIZE);
1759       for (i = 1; i < cbdata.nmykeys; i++)
1760         {
1761           if (!cbdata.extdata[i].len)
1762             continue;
1763           l = cbdata.extdata[i].len;
1764           dp = cbdata.extdata[i].buf;
1765           while (l)
1766             {
1767               ll = BLOB_PAGESIZE - lpage;
1768               if (l < ll)
1769                 ll = l;
1770               memcpy(vpage + lpage, dp, ll);
1771               dp += ll;
1772               lpage += ll;
1773               l -= ll;
1774               if (lpage == BLOB_PAGESIZE)
1775                 {
1776                   write_compressed_page(fp, vpage, lpage);
1777                   lpage = 0;
1778                 }
1779             }
1780         }
1781       if (lpage)
1782         write_compressed_page(fp, vpage, lpage);
1783     }
1784
1785 #if 0
1786   /* write vertical_offset entries */
1787   write_u32(fp, 0);     /* no paging */
1788   for (i = 1; i < cbdata.nmykeys; i++)
1789     if (cbdata.extdata[i].len)
1790       write_blob(fp, cbdata.extdata[i].buf, cbdata.extdata[i].len);
1791 #endif
1792
1793   /* Fill fileinfo for our caller.  */
1794   if (setfileinfo)
1795     {
1796       fileinfo->checksum = 0;
1797       fileinfo->nchecksum = 0;
1798       fileinfo->checksumtype = 0;
1799       fileinfo->location = 0;
1800     }
1801
1802   for (i = 1; i < cbdata.nmykeys; i++)
1803     sat_free(cbdata.extdata[i].buf);
1804   sat_free(cbdata.extdata);
1805
1806   if (cbdata.ownspool)
1807     {
1808       sat_free(cbdata.ownspool->strings);
1809       sat_free(cbdata.ownspool->stringspace);
1810       sat_free(cbdata.ownspool->stringhashtbl);
1811     }
1812   if (cbdata.owndirpool)
1813     {
1814       sat_free(cbdata.owndirpool->dirs);
1815       sat_free(cbdata.owndirpool->dirtraverse);
1816     }
1817   sat_free(needid);
1818   sat_free(cbdata.extraschemata);
1819   sat_free(cbdata.solvschemata);
1820   sat_free(cbdata.myschemadata);
1821   sat_free(cbdata.myschemata);
1822   sat_free(cbdata.schema);
1823
1824   sat_free(cbdata.mykeys);
1825   sat_free(cbdata.keymap);
1826   sat_free(cbdata.keymapstart);
1827   sat_free(cbdata.dirused);
1828   sat_free(repodataused);
1829   sat_free(repodataschemata);
1830 }