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