de13ea44ef7795a5347ec9ca813efcd1ee8f64d9
[platform/upstream/libsolv.git] / src / repodata.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  * repodata.c
10  *
11  * Manage data coming from one repository
12  * 
13  */
14
15 #define _GNU_SOURCE
16 #include <string.h>
17 #include <fnmatch.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <assert.h>
23
24 #include "repo.h"
25 #include "pool.h"
26 #include "poolid_private.h"
27 #include "util.h"
28
29 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
30                                   unsigned char *out, unsigned int out_len);
31 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
32                                               unsigned int in_len,
33                                               unsigned char *out,
34                                               unsigned int out_len);
35
36 #define REPODATA_BLOCK 255
37
38 void
39 repodata_free(Repodata *data)
40 {
41   sat_free(data->keys);
42   sat_free(data->schemata);
43   sat_free(data->schemadata);
44
45   sat_free(data->spool.strings);
46   sat_free(data->spool.stringspace);
47   sat_free(data->spool.stringhashtbl);
48
49   sat_free(data->dirpool.dirs);
50   sat_free(data->dirpool.dirtraverse);
51
52   sat_free(data->incoredata);
53   sat_free(data->incoreoffset);
54   sat_free(data->verticaloffset);
55
56   sat_free(data->blob_store);
57   sat_free(data->pages);
58   sat_free(data->mapped);
59
60   sat_free(data->vincore);
61
62   sat_free(data->attrs);
63   sat_free(data->attrdata);
64   sat_free(data->attriddata);
65   
66   if (data->fp)
67     fclose(data->fp);
68 }
69
70 static unsigned char *
71 data_read_id(unsigned char *dp, Id *idp)
72 {
73   Id x = 0;
74   unsigned char c;
75   for (;;)
76     {
77       c = *dp++;
78       if (!(c & 0x80))
79         {
80           *idp = (x << 7) ^ c;
81           return dp;
82         }
83       x = (x << 7) ^ c ^ 128;
84     }
85 }
86
87 static unsigned char *
88 data_read_ideof(unsigned char *dp, Id *idp, int *eof)
89 {
90   Id x = 0;
91   unsigned char c;
92   for (;;)
93     {
94       c = *dp++;
95       if (!(c & 0x80))
96         {
97           if (c & 0x40)
98             {
99               c ^= 0x40;
100               *eof = 0;
101             }
102           else
103             *eof = 1;
104           *idp = (x << 6) ^ c;
105           return dp;
106         }
107       x = (x << 7) ^ c ^ 128;
108     }
109 }
110
111 static unsigned char *
112 data_skip(unsigned char *dp, int type)
113 {
114   unsigned char x;
115   switch (type)
116     {
117     case TYPE_VOID:
118     case TYPE_CONSTANT:
119       return dp;
120     case TYPE_ID:
121     case TYPE_NUM:
122     case TYPE_DIR:
123       while ((*dp & 0x80) != 0)
124         dp++;
125       return dp + 1;
126     case TYPE_IDARRAY:
127       while ((*dp & 0xc0) != 0)
128         dp++;
129       return dp + 1;
130     case TYPE_STR:
131       while ((*dp) != 0)
132         dp++;
133       return dp + 1;
134     case TYPE_DIRSTRARRAY:
135       for (;;)
136         {
137           while ((*dp & 0x80) != 0)
138             dp++;
139           x = *dp++;
140           while ((*dp) != 0)
141             dp++;
142           dp++;
143           if (!(x & 0x40))
144             return dp;
145         }
146     case TYPE_DIRNUMNUMARRAY:
147       for (;;)
148         {
149           while ((*dp & 0x80) != 0)
150             dp++;
151           dp++;
152           while ((*dp & 0x80) != 0)
153             dp++;
154           dp++;
155           while ((*dp & 0x80) != 0)
156             dp++;
157           if (!(*dp & 0x40))
158             return dp + 1;
159           dp++;
160         }
161     default:
162       fprintf(stderr, "unknown type in data_skip\n");
163       exit(1);
164     }
165 }
166
167 static unsigned char *
168 data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
169 {
170   kv->eof = 1;
171   if (!dp)
172     return 0;
173   switch (key->type)
174     {
175     case TYPE_VOID:
176       return dp;
177     case TYPE_CONSTANT:
178       kv->num = key->size;
179       return dp;
180     case TYPE_STR:
181       kv->str = (const char *)dp;
182       return dp + strlen(kv->str) + 1;
183     case TYPE_ID:
184       return data_read_id(dp, &kv->id);
185     case TYPE_NUM:
186       return data_read_id(dp, &kv->num);
187     case TYPE_IDARRAY:
188       return data_read_ideof(dp, &kv->id, &kv->eof);
189     case TYPE_DIR:
190       return data_read_id(dp, &kv->id);
191     case TYPE_DIRSTRARRAY:
192       dp = data_read_ideof(dp, &kv->id, &kv->eof);
193       kv->str = (const char *)dp;
194       return dp + strlen(kv->str) + 1;
195     case TYPE_DIRNUMNUMARRAY:
196       dp = data_read_id(dp, &kv->id);
197       dp = data_read_id(dp, &kv->num);
198       return data_read_ideof(dp, &kv->num2, &kv->eof);
199     default:
200       return 0;
201     }
202 }
203
204 static unsigned char *
205 forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
206 {
207   Id k, *keyp;
208
209   keyp = data->schemadata + data->schemata[schemaid];
210   while ((k = *keyp++) != 0)
211     {
212       if (k == key)
213         return dp;
214       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
215         {
216           dp = data_skip(dp, TYPE_ID);  /* skip that offset */
217           dp = data_skip(dp, TYPE_ID);  /* skip that length */
218           continue;
219         }
220       if (data->keys[k].storage != KEY_STORAGE_INCORE)
221         continue;
222       dp = data_skip(dp, data->keys[k].type);
223     }
224   return 0;
225 }
226
227 #define BLOB_PAGEBITS 15
228 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
229
230 static unsigned char *
231 load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
232 {
233 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
234    and are consecutive.  Return a pointer to the mapping of PSTART.  */
235   unsigned char buf[BLOB_PAGESIZE];
236   unsigned int i;
237
238   /* Quick check in case all pages are there already and consecutive.  */
239   for (i = pstart; i <= pend; i++)
240     if (data->pages[i].mapped_at == -1
241         || (i > pstart
242             && data->pages[i].mapped_at
243                != data->pages[i-1].mapped_at + BLOB_PAGESIZE))
244       break;
245   if (i > pend)
246     return data->blob_store + data->pages[pstart].mapped_at;
247
248   /* Ensure that we can map the numbers of pages we need at all.  */
249   if (pend - pstart + 1 > data->ncanmap)
250     {
251       unsigned int oldcan = data->ncanmap;
252       data->ncanmap = pend - pstart + 1;
253       if (data->ncanmap < 4)
254         data->ncanmap = 4;
255       data->mapped = sat_realloc2(data->mapped, data->ncanmap, sizeof(data->mapped[0]));
256       memset (data->mapped + oldcan, 0, (data->ncanmap - oldcan) * sizeof (data->mapped[0]));
257       data->blob_store = sat_realloc2(data->blob_store, data->ncanmap, BLOB_PAGESIZE);
258 #ifdef DEBUG_PAGING
259       fprintf (stderr, "PAGE: can map %d pages\n", data->ncanmap);
260 #endif
261     }
262
263   /* Now search for "cheap" space in our store.  Space is cheap if it's either
264      free (very cheap) or contains pages we search for anyway.  */
265
266   /* Setup cost array.  */
267   unsigned int cost[data->ncanmap];
268   for (i = 0; i < data->ncanmap; i++)
269     {
270       unsigned int pnum = data->mapped[i];
271       if (pnum == 0)
272         cost[i] = 0;
273       else
274         {
275           pnum--;
276           Attrblobpage *p = data->pages + pnum;
277           assert (p->mapped_at != -1);
278           if (pnum >= pstart && pnum <= pend)
279             cost[i] = 1;
280           else
281             cost[i] = 3;
282         }
283     }
284
285   /* And search for cheapest space.  */
286   unsigned int best_cost = -1;
287   unsigned int best = 0;
288   unsigned int same_cost = 0;
289   for (i = 0; i + pend - pstart < data->ncanmap; i++)
290     {
291       unsigned int c = cost[i];
292       unsigned int j;
293       for (j = 0; j < pend - pstart + 1; j++)
294         c += cost[i+j];
295       if (c < best_cost)
296         best_cost = c, best = i;
297       else if (c == best_cost)
298         same_cost++;
299       /* A null cost won't become better.  */
300       if (c == 0)
301         break;
302     }
303   /* If all places have the same cost we would thrash on slot 0.  Avoid
304      this by doing a round-robin strategy in this case.  */
305   if (same_cost == data->ncanmap - pend + pstart - 1)
306     best = data->rr_counter++ % (data->ncanmap - pend + pstart);
307
308   /* So we want to map our pages from [best] to [best+pend-pstart].
309      Use a very simple strategy, which doesn't make the best use of
310      our resources, but works.  Throw away all pages in that range
311      (even ours) then copy around ours (in case they were outside the 
312      range) or read them in.  */
313   for (i = best; i < best + pend - pstart + 1; i++)
314     {
315       unsigned int pnum = data->mapped[i];
316       if (pnum--
317           /* If this page is exactly at the right place already,
318              no need to evict it.  */
319           && pnum != pstart + i - best)
320         {
321           /* Evict this page.  */
322 #ifdef DEBUG_PAGING
323           fprintf (stderr, "PAGE: evict page %d from %d\n", pnum, i);
324 #endif
325           cost[i] = 0;
326           data->mapped[i] = 0;
327           data->pages[pnum].mapped_at = -1;
328         }
329     }
330
331   /* Everything is free now.  Read in the pages we want.  */
332   for (i = pstart; i <= pend; i++)
333     {
334       Attrblobpage *p = data->pages + i;
335       unsigned int pnum = i - pstart + best;
336       void *dest = data->blob_store + pnum * BLOB_PAGESIZE;
337       if (p->mapped_at != -1)
338         {
339           if (p->mapped_at != pnum * BLOB_PAGESIZE)
340             {
341 #ifdef DEBUG_PAGING
342               fprintf (stderr, "PAGECOPY: %d to %d\n", i, pnum);
343 #endif
344               /* Still mapped somewhere else, so just copy it from there.  */
345               memcpy (dest, data->blob_store + p->mapped_at, BLOB_PAGESIZE);
346               data->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
347             }
348         }
349       else
350         {
351           unsigned int in_len = p->file_size;
352           unsigned int compressed = in_len & 1;
353           in_len >>= 1;
354 #ifdef DEBUG_PAGING
355           fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
356 #endif
357           /* Not mapped, so read in this page.  */
358           if (fseek(data->fp, p->file_offset, SEEK_SET) < 0)
359             {
360               perror ("mapping fseek");
361               exit (1);
362             }
363           if (fread(compressed ? buf : dest, in_len, 1, data->fp) != 1)
364             {
365               perror ("mapping fread");
366               exit (1);
367             }
368           if (compressed)
369             {
370               unsigned int out_len;
371               out_len = unchecked_decompress_buf(buf, in_len,
372                                                   dest, BLOB_PAGESIZE);
373               if (out_len != BLOB_PAGESIZE
374                   && i < data->num_pages - 1)
375                 {
376                   fprintf (stderr, "can't decompress\n");
377                   exit (1);
378                 }
379 #ifdef DEBUG_PAGING
380               fprintf (stderr, " (expand %d to %d)", in_len, out_len);
381 #endif
382             }
383 #ifdef DEBUG_PAGING
384           fprintf (stderr, "\n");
385 #endif
386         }
387       p->mapped_at = pnum * BLOB_PAGESIZE;
388       data->mapped[pnum] = i + 1;
389     }
390   return data->blob_store + best * BLOB_PAGESIZE;
391 }
392
393 static unsigned char *
394 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
395 {
396   unsigned char *dp;
397   if (key->type == TYPE_VOID)
398     return 0;
399   if (off >= data->lastverticaloffset)
400     {
401       off -= data->lastverticaloffset;
402       if (off + len > data->vincorelen)
403         return 0;
404       return data->vincore + off;
405     }
406   if (!data->fp)
407     return 0;
408   if (off + len > key->size)
409     return 0;
410   /* we now have the offset, go into vertical */
411   off += data->verticaloffset[key - data->keys];
412   dp = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
413   if (dp)
414     dp += off % BLOB_PAGESIZE;
415   return dp;
416 }
417
418 static inline unsigned char *
419 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
420 {
421   unsigned char *dp = *dpp;
422
423   if (!dp)
424     return 0;
425   if (key->storage == KEY_STORAGE_INCORE)
426     {
427       /* hmm, this is a bit expensive */
428       *dpp = data_skip(dp, key->type);
429       return dp;
430     }
431   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
432     {
433       Id off, len;
434       dp = data_read_id(dp, &off);
435       dp = data_read_id(dp, &len);
436       *dpp = dp;
437       return make_vertical_available(data, key, off, len);
438     }
439   return 0;
440 }
441
442 static inline int
443 maybe_load_repodata(Repodata *data)
444 {
445   if (data->state == REPODATA_STUB)
446     {
447       if (data->loadcallback)
448         data->loadcallback(data);
449       else
450         data->state = REPODATA_ERROR;
451     }
452   if (data->state == REPODATA_AVAILABLE)
453     return 1;
454   data->state = REPODATA_ERROR;
455   return 0;
456 }
457
458 const char *
459 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
460 {
461   Id schema;
462   Repokey *key;
463   Id id, *keyp;
464   unsigned char *dp;
465
466   if (!maybe_load_repodata (data))
467     return 0;
468
469   dp = data->incoredata + data->incoreoffset[entry];
470   dp = data_read_id(dp, &schema);
471   /* make sure the schema of this solvable contains the key */
472   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
473     if (!*keyp)
474       return 0;
475   dp = forward_to_key(data, keyid, schema, dp);
476   key = data->keys + keyid;
477   dp = get_data(data, key, &dp);
478   if (!dp)
479     return 0;
480   if (key->type == TYPE_STR)
481     return (const char *)dp;
482   if (key->type != TYPE_ID)
483     return 0;
484   /* id type, must either use global or local string store*/
485   dp = data_read_id(dp, &id);
486   if (data->localpool)
487     return data->spool.stringspace + data->spool.strings[id];
488   return id2str(data->repo->pool, id);
489 }
490
491 int
492 repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned *value)
493 {
494   Id schema;
495   Repokey *key;
496   Id *keyp;
497   KeyValue kv;
498   unsigned char *dp;
499
500   *value = 0;
501
502   if (!maybe_load_repodata (data))
503     return 0;
504
505   dp = data->incoredata + data->incoreoffset[entry];
506   dp = data_read_id(dp, &schema);
507   /* make sure the schema of this solvable contains the key */
508   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
509     if (!*keyp)
510       return 0;
511   dp = forward_to_key(data, keyid, schema, dp);
512   key = data->keys + keyid;
513   dp = get_data(data, key, &dp);
514   if (!dp)
515     return 0;
516   if (key->type == TYPE_NUM
517       || key->type == TYPE_U32
518       || key->type == TYPE_CONSTANT)
519     {
520       dp = data_fetch(dp, &kv, key);
521       *value = kv.num;
522       return 1;
523     }
524   return 0;
525 }
526
527 void
528 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
529 {
530   Id schema;
531   Repokey *key;
532   Id k, keyid, *kp, *keyp;
533   unsigned char *dp, *ddp;
534   int onekey = 0;
535   int stop;
536   KeyValue kv;
537
538   if (!maybe_load_repodata (data))
539     return;
540
541   dp = data->incoredata + data->incoreoffset[entry];
542   dp = data_read_id(dp, &schema);
543   keyp = data->schemadata + data->schemata[schema];
544   if (keyname)
545     {
546       /* search in a specific key */
547       for (kp = keyp; (k = *kp++) != 0; )
548         if (data->keys[k].name == keyname)
549           break;
550       if (k == 0)
551         return;
552       dp = forward_to_key(data, k, schema, dp);
553       if (!dp)
554         return;
555       keyp = kp - 1;
556       onekey = 1;
557     }
558   while ((keyid = *keyp++) != 0)
559     {
560       stop = 0;
561       key = data->keys + keyid;
562       ddp = get_data(data, key, &dp);
563       do
564         {
565           ddp = data_fetch(ddp, &kv, key);
566           if (!ddp)
567             break;
568           stop = callback(cbdata, data->repo->pool->solvables + data->start + entry, data, key, &kv);
569         }
570       while (!kv.eof && !stop);
571       if (onekey || stop > SEARCH_NEXT_KEY)
572         return;
573     }
574 }
575
576 static void
577 dataiterator_newdata(Dataiterator *di)
578 {
579   Id keyname = di->keyname;
580   Repodata *data = di->data;
581   di->nextkeydp = 0;
582
583   if (data->state == REPODATA_STUB)
584     {
585       if (keyname)
586         {
587           int j;
588           for (j = 1; j < data->nkeys; j++)
589             if (keyname == data->keys[j].name)
590               break;
591           if (j == data->nkeys)
592             return;
593         }
594       /* load it */
595       if (data->loadcallback)
596         data->loadcallback(data);
597       else
598         data->state = REPODATA_ERROR;
599     }
600   if (data->state == REPODATA_ERROR)
601     return;
602
603   Id schema;
604   unsigned char *dp = data->incoredata + data->incoreoffset[di->solvid - data->start];
605   dp = data_read_id(dp, &schema);
606   Id *keyp = data->schemadata + data->schemata[schema];
607   if (keyname)
608     {
609       Id k, *kp;
610       /* search in a specific key */
611       for (kp = keyp; (k = *kp++) != 0; )
612         if (data->keys[k].name == keyname)
613           break;
614       if (k == 0)
615         return;
616       dp = forward_to_key(data, k, schema, dp);
617       if (!dp)
618         return;
619       keyp = kp - 1;
620     }
621   Id keyid = *keyp++;
622   if (!keyid)
623     return;
624
625   di->data = data;
626   di->key = di->data->keys + keyid;
627   di->keyp = keyp;
628   di->dp = 0;
629
630   di->nextkeydp = dp;
631   di->dp = get_data(di->data, di->key, &di->nextkeydp);
632   di->kv.eof = 0;
633 }
634
635 void
636 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
637                   const char *match, int flags)
638 {
639   di->flags = flags;
640   if (p)
641     {
642       di->solvid = p;
643       di->flags |= __SEARCH_ONESOLVABLE;
644       di->data = repo->repodata - 1;
645     }
646   else
647     {
648       di->solvid = repo->start - 1;
649       di->data = repo->repodata + repo->nrepodata - 1;
650     }
651   di->match = match;
652   di->keyname = keyname;
653   static Id zeroid = 0;
654   di->keyp = &zeroid;
655   di->kv.eof = 1;
656   di->repo = repo;
657 }
658
659 /* FIXME factor and merge with repo_matchvalue */
660 static int
661 dataiterator_match(Dataiterator *di, KeyValue *kv)
662 {
663   int flags = di->flags;
664
665   if ((flags & SEARCH_STRINGMASK) != 0)
666     {
667       switch (di->key->type)
668         {
669         case TYPE_ID:
670         case TYPE_IDARRAY:
671           if (di->data && di->data->localpool)
672             kv->str = stringpool_id2str(&di->data->spool, kv->id);
673           else
674             kv->str = id2str(di->repo->pool, kv->id);
675           break;
676         case TYPE_STR:
677           break;
678         default:
679           return 0;
680         }
681       switch ((flags & SEARCH_STRINGMASK))
682         {
683           case SEARCH_SUBSTRING:
684             if (flags & SEARCH_NOCASE)
685               {
686                 if (!strcasestr(kv->str, di->match))
687                   return 0;
688               }
689             else
690               {
691                 if (!strstr(kv->str, di->match))
692                   return 0;
693               }
694             break;
695           case SEARCH_STRING:
696             if (flags & SEARCH_NOCASE)
697               {
698                 if (strcasecmp(di->match, kv->str))
699                   return 0;
700               }
701             else
702               {
703                 if (strcmp(di->match, kv->str))
704                   return 0;
705               }
706             break;
707           case SEARCH_GLOB:
708             if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
709               return 0;
710             break;
711 #if 0
712           case SEARCH_REGEX:
713             if (regexec(&di->regexp, kv->str, 0, NULL, 0))
714               return 0;
715 #endif
716           default:
717             return 0;
718         }
719     }
720   return 1;
721 }
722
723 int
724 dataiterator_step(Dataiterator *di)
725 {
726   /* FIXME add solvable data */
727   while (1)
728     {
729       if (di->kv.eof)
730         di->dp = 0;
731       else
732         di->dp = data_fetch(di->dp, &di->kv, di->key);
733
734       while (!di->dp)
735         {
736           Id keyid;
737           if (di->keyname || !(keyid = *di->keyp++))
738             {
739               while (1)
740                 {
741                   Repo *repo = di->repo;
742                   Repodata *data = ++di->data;
743                   if (data >= repo->repodata + repo->nrepodata)
744                     {
745                       if (di->flags & __SEARCH_ONESOLVABLE)
746                         return 0;
747                       while (++di->solvid < repo->end)
748                         if (repo->pool->solvables[di->solvid].repo == repo)
749                           break;
750                       if (di->solvid >= repo->end)
751                         return 0;
752                       di->data = repo->repodata - 1;
753                       continue;
754                     }
755                   if (di->solvid >= data->start && di->solvid < data->end)
756                     {
757                       dataiterator_newdata(di);
758                       if (di->nextkeydp)
759                         break;
760                     }
761                 }
762             }
763           else
764             {
765               di->key = di->data->keys + keyid;
766               di->dp = get_data(di->data, di->key, &di->nextkeydp);
767             }
768           di->dp = data_fetch(di->dp, &di->kv, di->key);
769         }
770       if (!di->match
771           || dataiterator_match(di, &di->kv))
772         break;
773     }
774   return 1;
775 }
776
777 void
778 repodata_init(Repodata *data, Repo *repo, int localpool)
779 {
780   memset(data, 0, sizeof (*data));
781   data->repo = repo;
782   data->localpool = localpool;
783   if (localpool)
784     stringpool_init_empty(&data->spool);
785   data->keys = sat_calloc(1, sizeof(Repokey));
786   data->nkeys = 1;
787   data->schemata = sat_calloc(1, sizeof(Id));
788   data->schemadata = sat_calloc(1, sizeof(Id));
789   data->nschemata = 1;
790   data->schemadatalen = 1;
791   data->start = repo->start;
792   data->end = repo->end;
793   data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
794 }
795
796 /* extend repodata so that it includes solvables p */
797 void
798 repodata_extend(Repodata *data, Id p)
799 {
800   if (data->start == data->end)
801     data->start = data->end = p;
802   if (p >= data->end)
803     {
804       int old = data->end - data->start;
805       int new = p - data->end + 1;
806       if (data->attrs)
807         {
808           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
809           memset(data->attrs + old, 0, new * sizeof(Id *));
810         }
811       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
812       memset(data->incoreoffset + old, 0, new * sizeof(Id));
813       data->end = p + 1;
814     }
815   if (p < data->start)
816     {
817       int old = data->end - data->start;
818       int new = data->start - p;
819       if (data->attrs)
820         {
821           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
822           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
823           memset(data->attrs, 0, new * sizeof(Id *));
824         }
825       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
826       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
827       memset(data->incoreoffset, 0, new * sizeof(Id));
828       data->start = p;
829     }
830 }
831
832 void
833 repodata_extend_block(Repodata *data, Id start, Id num)
834 {
835   if (!num)
836     return;
837   if (!data->incoreoffset)
838     {
839       data->incoreoffset = sat_extend_resize(data->incoreoffset, num, sizeof(Id), REPODATA_BLOCK);
840       memset(data->incoreoffset, 0, num * sizeof(Id));
841       data->start = start;
842       data->end = start + num;
843       return;
844     }
845   repodata_extend(data, start);
846   if (num > 1)
847     repodata_extend(data, start + num - 1);
848 }
849
850 #define REPODATA_ATTRS_BLOCK 63
851 #define REPODATA_ATTRDATA_BLOCK 1023
852 #define REPODATA_ATTRIDDATA_BLOCK 63
853
854 static void
855 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
856 {
857   Id *pp;
858   int i;
859   if (!data->attrs)
860     {
861       data->attrs = sat_extend_resize(0, data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
862       memset(data->attrs, 0, (data->end - data->start) * sizeof(Id *));
863     }
864   i = 0;
865   if (data->attrs[entry])
866     {
867       for (pp = data->attrs[entry]; *pp; pp += 2)
868         /* Determine equality based on the name only, allows us to change
869            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
870         if (data->keys[*pp].name == data->keys[keyid].name)
871           break;
872       if (*pp)
873         {
874           if (overwrite)
875             {
876               pp[0] = keyid;
877               pp[1] = val;
878             }
879           return;
880         }
881       i = pp - data->attrs[entry];
882     }
883   data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
884   pp = data->attrs[entry] + i;
885   *pp++ = keyid;
886   *pp++ = val;
887   *pp = 0;
888 }
889
890 void
891 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
892 {
893   Id keyid;
894
895   /* find key in keys */
896   for (keyid = 1; keyid < data->nkeys; keyid++)
897     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
898       {
899         if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
900           continue;
901         break;
902       }
903   if (keyid == data->nkeys)
904     {
905       /* allocate new key */
906       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
907       data->keys[data->nkeys++] = *key;
908       if (data->verticaloffset)
909         {
910           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
911           data->verticaloffset[data->nkeys - 1] = 0;
912         }
913     }
914   repodata_insert_keyid(data, entry, keyid, val, 1);
915 }
916
917 void
918 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
919 {
920   Repokey key;
921   key.name = keyname;
922   key.type = TYPE_ID;
923   key.size = 0;
924   key.storage = KEY_STORAGE_INCORE;
925   repodata_set(data, entry, &key, id);
926 }
927
928 void
929 repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
930 {
931   Repokey key;
932   key.name = keyname;
933   key.type = TYPE_NUM;
934   key.size = 0;
935   key.storage = KEY_STORAGE_INCORE;
936   repodata_set(data, entry, &key, num);
937 }
938
939 void
940 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
941 {
942   Repokey key;
943   Id id;
944   if (data->localpool)
945     id = stringpool_str2id(&data->spool, str, 1);
946   else
947     id = str2id(data->repo->pool, str, 1);
948   key.name = keyname;
949   key.type = TYPE_ID;
950   key.size = 0;
951   key.storage = KEY_STORAGE_INCORE;
952   repodata_set(data, entry, &key, id);
953 }
954
955 void
956 repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
957 {
958   Repokey key;
959   key.name = keyname;
960   key.type = TYPE_CONSTANT;
961   key.size = constant;
962   key.storage = KEY_STORAGE_INCORE;
963   repodata_set(data, entry, &key, 0);
964 }
965
966 void
967 repodata_set_void(Repodata *data, Id entry, Id keyname)
968 {
969   Repokey key;
970   key.name = keyname;
971   key.type = TYPE_VOID;
972   key.size = 0;
973   key.storage = KEY_STORAGE_INCORE;
974   repodata_set(data, entry, &key, 0);
975 }
976
977 void
978 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
979 {
980   Repokey key;
981   int l;
982
983   l = strlen(str) + 1;
984   key.name = keyname;
985   key.type = TYPE_STR;
986   key.size = 0;
987   key.storage = KEY_STORAGE_INCORE;
988   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
989   memcpy(data->attrdata + data->attrdatalen, str, l);
990   repodata_set(data, entry, &key, data->attrdatalen);
991   data->attrdatalen += l;
992 }
993
994 void
995 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
996 {
997   Id *ida, *pp;
998   Repokey key;
999
1000 #if 0
1001 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1002 #endif
1003   if (data->attrs && data->attrs[entry])
1004     {
1005       for (pp = data->attrs[entry]; *pp; pp += 2)
1006         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
1007           break;
1008       if (*pp)
1009         {
1010           int oldsize = 0;
1011           for (ida = data->attriddata + pp[1]; *ida; ida += 3)
1012             oldsize += 3;
1013           if (ida + 1 == data->attriddata + data->attriddatalen)
1014             {
1015               /* this was the last entry, just append it */
1016               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1017               data->attriddatalen--;    /* overwrite terminating 0  */
1018             }
1019           else
1020             {
1021               /* too bad. move to back. */
1022               data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1023               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1024               pp[1] = data->attriddatalen;
1025               data->attriddatalen += oldsize;
1026             }
1027           data->attriddata[data->attriddatalen++] = dir;
1028           data->attriddata[data->attriddatalen++] = num;
1029           data->attriddata[data->attriddatalen++] = num2;
1030           data->attriddata[data->attriddatalen++] = 0;
1031           return;
1032         }
1033     }
1034   key.name = keyname;
1035   key.type = TYPE_DIRNUMNUMARRAY;
1036   key.size = 0;
1037   key.storage = KEY_STORAGE_INCORE;
1038   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 4, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1039   repodata_set(data, entry, &key, data->attriddatalen);
1040   data->attriddata[data->attriddatalen++] = dir;
1041   data->attriddata[data->attriddatalen++] = num;
1042   data->attriddata[data->attriddatalen++] = num2;
1043   data->attriddata[data->attriddatalen++] = 0;
1044 }
1045
1046 void
1047 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1048 {
1049   Id *ida, *pp, stroff;
1050   Repokey key;
1051   int l;
1052
1053   l = strlen(str) + 1;
1054   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1055   memcpy(data->attrdata + data->attrdatalen, str, l);
1056   stroff = data->attrdatalen;
1057   data->attrdatalen += l;
1058
1059 #if 0
1060 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1061 #endif
1062   if (data->attrs && data->attrs[entry])
1063     {
1064       for (pp = data->attrs[entry]; *pp; pp += 2)
1065         if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRSTRARRAY)
1066           break;
1067       if (*pp)
1068         {
1069           int oldsize = 0;
1070           for (ida = data->attriddata + pp[1]; *ida; ida += 2)
1071             oldsize += 2;
1072           if (ida + 1 == data->attriddata + data->attriddatalen)
1073             {
1074               /* this was the last entry, just append it */
1075               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 2, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1076               data->attriddatalen--;    /* overwrite terminating 0  */
1077             }
1078           else
1079             {
1080               /* too bad. move to back. */
1081               data->attriddata = sat_extend(data->attriddata, data->attriddatalen, oldsize + 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1082               memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1083               pp[1] = data->attriddatalen;
1084               data->attriddatalen += oldsize;
1085             }
1086           data->attriddata[data->attriddatalen++] = dir;
1087           data->attriddata[data->attriddatalen++] = stroff;
1088           data->attriddata[data->attriddatalen++] = 0;
1089           return;
1090         }
1091     }
1092   key.name = keyname;
1093   key.type = TYPE_DIRSTRARRAY;
1094   key.size = 0;
1095   key.storage = KEY_STORAGE_INCORE;
1096   data->attriddata = sat_extend(data->attriddata, data->attriddatalen, 3, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1097   repodata_set(data, entry, &key, data->attriddatalen);
1098   data->attriddata[data->attriddatalen++] = dir;
1099   data->attriddata[data->attriddatalen++] = stroff;
1100   data->attriddata[data->attriddatalen++] = 0;
1101 }
1102
1103 void
1104 repodata_merge_attrs (Repodata *data, Id dest, Id src)
1105 {
1106   Id *keyp;
1107   for (keyp = data->attrs[src]; *keyp; keyp += 2)
1108     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1109 }
1110
1111 /*********************************/
1112
1113 /* unify with repo_write! */
1114
1115 #define EXTDATA_BLOCK 1023
1116 #define SCHEMATA_BLOCK 31
1117 #define SCHEMATADATA_BLOCK 255
1118
1119 struct extdata {
1120   unsigned char *buf;
1121   int len;
1122 };
1123
1124 static void
1125 data_addid(struct extdata *xd, Id x)
1126 {
1127   unsigned char *dp;
1128   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1129   dp = xd->buf + xd->len;
1130
1131   if (x >= (1 << 14))
1132     {
1133       if (x >= (1 << 28))
1134         *dp++ = (x >> 28) | 128;
1135       if (x >= (1 << 21))
1136         *dp++ = (x >> 21) | 128;
1137       *dp++ = (x >> 14) | 128;
1138     }
1139   if (x >= (1 << 7))
1140     *dp++ = (x >> 7) | 128;
1141   *dp++ = x & 127;
1142   xd->len = dp - xd->buf;
1143 }
1144
1145 static void
1146 data_addideof(struct extdata *xd, Id x, int eof)
1147 {
1148   if (x >= 64)
1149     x = (x & 63) | ((x & ~63) << 1);
1150   data_addid(xd, (eof ? x: x | 64));
1151 }
1152
1153 static void
1154 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1155 {
1156   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1157   memcpy(xd->buf + xd->len, blob, len);
1158   xd->len += len;
1159 }
1160
1161 /*********************************/
1162
1163 static void
1164 addschema_prepare(Repodata *data, Id *schematacache)
1165 {
1166   int h, len, i;
1167   Id *sp;
1168
1169   memset(schematacache, 0, 256 * sizeof(Id));
1170   for (i = 0; i < data->nschemata; i++)
1171     {
1172       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1173         h = h * 7 + *sp++;
1174       h &= 255;
1175       schematacache[h] = i + 1;
1176     }
1177   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1178   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1179 }
1180
1181 static Id
1182 addschema(Repodata *data, Id *schema, Id *schematacache)
1183 {
1184   int h, len; 
1185   Id *sp, cid; 
1186
1187   for (sp = schema, len = 0, h = 0; *sp; len++)
1188     h = h * 7 + *sp++;
1189   h &= 255; 
1190   len++;
1191
1192   cid = schematacache[h];
1193   if (cid)
1194     {    
1195       cid--;
1196       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1197         return cid;
1198       /* cache conflict */
1199       for (cid = 0; cid < data->nschemata; cid++)
1200         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1201           return cid;
1202     }
1203   /* a new one. make room. */
1204   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1205   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1206   /* add schema */
1207   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1208   data->schemata[data->nschemata] = data->schemadatalen;
1209   data->schemadatalen += len;
1210   schematacache[h] = data->nschemata + 1;
1211 #if 0
1212 fprintf(stderr, "addschema: new schema\n");
1213 #endif
1214   return data->nschemata++; 
1215 }
1216
1217
1218 void
1219 repodata_internalize(Repodata *data)
1220 {
1221   Repokey *key;
1222   Id id, entry, nentry, *ida;
1223   Id schematacache[256];
1224   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1225   unsigned char *dp, *ndp;
1226   int newschema, oldcount;
1227   struct extdata newincore;
1228   struct extdata newvincore;
1229
1230   if (!data->attrs)
1231     return;
1232
1233   newvincore.buf = data->vincore;
1234   newvincore.len = data->vincorelen;
1235
1236   schema = sat_malloc2(data->nkeys, sizeof(Id));
1237   seen = sat_malloc2(data->nkeys, sizeof(Id));
1238
1239   /* Merge the data already existing (in data->schemata, ->incoredata and
1240      friends) with the new attributes in data->attrs[].  */
1241   nentry = data->end - data->start;
1242   addschema_prepare(data, schematacache);
1243   memset(&newincore, 0, sizeof(newincore));
1244   for (entry = 0; entry < nentry; entry++)
1245     {
1246       memset(seen, 0, data->nkeys * sizeof(Id));
1247       sp = schema;
1248       dp = data->incoredata + data->incoreoffset[entry];
1249       if (data->incoredata)
1250         dp = data_read_id(dp, &oldschema);
1251       else
1252         oldschema = 0;
1253 #if 0
1254 fprintf(stderr, "oldschema %d\n", oldschema);
1255 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1256 fprintf(stderr, "schemadata %p\n", data->schemadata);
1257 #endif
1258       /* seen: -1: old data  0: skipped  >0: id + 1 */
1259       newschema = 0;
1260       oldcount = 0;
1261       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1262         {
1263           if (seen[*keyp])
1264             {
1265               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1266               exit(1);
1267             }
1268           seen[*keyp] = -1;
1269           *sp++ = *keyp;
1270           oldcount++;
1271         }
1272       if (data->attrs[entry])
1273         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1274           {
1275             if (!seen[*keyp])
1276               {
1277                 newschema = 1;
1278                 *sp++ = *keyp;
1279               }
1280             seen[*keyp] = keyp[1] + 1;
1281           }
1282       *sp++ = 0;
1283       if (newschema)
1284         /* Ideally we'd like to sort the new schema here, to ensure
1285            schema equality independend of the ordering.  We can't do that
1286            yet.  For once see below (old ids need to come before new ids).
1287            An additional difficulty is that we also need to move
1288            the values with the keys.  */
1289         schemaid = addschema(data, schema, schematacache);
1290       else
1291         schemaid = oldschema;
1292
1293
1294       /* Now create data blob.  We walk through the (possibly new) schema
1295          and either copy over old data, or insert the new.  */
1296       /* XXX Here we rely on the fact that the (new) schema has the form
1297          o1 o2 o3 o4 ... | n1 n2 n3 ...
1298          (oX being the old keyids (possibly overwritten), and nX being
1299           the new keyids).  This rules out sorting the keyids in order
1300          to ensure a small schema count.  */
1301       data->incoreoffset[entry] = newincore.len;
1302       data_addid(&newincore, schemaid);
1303       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1304         {
1305           key = data->keys + *keyp;
1306           ndp = dp;
1307           if (oldcount)
1308             {
1309               /* Skip the data associated with this old key.  */
1310               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1311                 {
1312                   ndp = data_skip(dp, TYPE_ID);
1313                   ndp = data_skip(ndp, TYPE_ID);
1314                 }
1315               else if (key->storage == KEY_STORAGE_INCORE)
1316                 ndp = data_skip(dp, key->type);
1317               oldcount--;
1318             }
1319           if (seen[*keyp] == -1)
1320             {
1321               /* If this key was an old one _and_ was not overwritten with
1322                  a different value copy over the old value (we skipped it
1323                  above).  */
1324               if (dp != ndp)
1325                 data_addblob(&newincore, dp, ndp - dp);
1326               seen[*keyp] = 0;
1327             }
1328           else if (seen[*keyp])
1329             {
1330               /* Otherwise we have a new value.  Parse it into the internal
1331                  form.  */
1332               struct extdata *xd;
1333               unsigned int oldvincorelen = 0;
1334
1335               xd = &newincore;
1336               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1337                 {
1338                   xd = &newvincore;
1339                   oldvincorelen = xd->len;
1340                 }
1341               id = seen[*keyp] - 1;
1342               switch (key->type)
1343                 {
1344                 case TYPE_VOID:
1345                 case TYPE_CONSTANT:
1346                   break;
1347                 case TYPE_STR:
1348                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1349                   break;
1350                 case TYPE_ID:
1351                 case TYPE_NUM:
1352                 case TYPE_DIR:
1353                   data_addid(xd, id);
1354                   break;
1355                 case TYPE_DIRNUMNUMARRAY:
1356                   for (ida = data->attriddata + id; *ida; ida += 3)
1357                     {
1358                       data_addid(xd, ida[0]);
1359                       data_addid(xd, ida[1]);
1360                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1361                     }
1362                   break;
1363                 case TYPE_DIRSTRARRAY:
1364                   for (ida = data->attriddata + id; *ida; ida += 2)
1365                     {
1366                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1367                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1368                     }
1369                   break;
1370                 default:
1371                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1372                   exit(1);
1373                 }
1374               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1375                 {
1376                   /* put offset/len in incore */
1377                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1378                   oldvincorelen = xd->len - oldvincorelen;
1379                   data_addid(&newincore, oldvincorelen);
1380                 }
1381             }
1382           dp = ndp;
1383         }
1384       if (data->attrs[entry])
1385         sat_free(data->attrs[entry]);
1386     }
1387   sat_free(schema);
1388   sat_free(seen);
1389
1390   sat_free(data->incoredata);
1391   data->incoredata = newincore.buf;
1392   data->incoredatalen = newincore.len;
1393   data->incoredatafree = 0;
1394   
1395   sat_free(data->vincore);
1396   data->vincore = newvincore.buf;
1397   data->vincorelen = newvincore.len;
1398
1399   data->attrs = sat_free(data->attrs);
1400   data->attrdata = sat_free(data->attrdata);
1401   data->attriddata = sat_free(data->attriddata);
1402   data->attrdatalen = 0;
1403   data->attriddatalen = 0;
1404 }
1405
1406 Id
1407 repodata_str2dir(Repodata *data, const char *dir, int create)
1408 {
1409   Id id, parent;
1410   const char *dire;
1411
1412   parent = 0;
1413   while (*dir == '/' && dir[1] == '/')
1414     dir++;
1415   while (*dir)
1416     {
1417       dire = strchrnul(dir, '/');
1418       if (data->localpool)
1419         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1420       else
1421         id = strn2id(data->repo->pool, dir, dire - dir, create);
1422       if (!id)
1423         return 0;
1424       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1425       if (!parent)
1426         return 0;
1427       if (!*dire)
1428         break;
1429       dir = dire + 1;
1430       while (*dir == '/')
1431         dir++;
1432     }
1433   return parent;
1434 }
1435
1436 unsigned int
1437 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1438 {
1439   return compress_buf(page, len, cpage, max);
1440 }
1441
1442 #define SOLV_ERROR_EOF              3
1443
1444 static inline unsigned int
1445 read_u32(FILE *fp)
1446 {
1447   int c, i;
1448   unsigned int x = 0; 
1449
1450   for (i = 0; i < 4; i++) 
1451     {    
1452       c = getc(fp);
1453       if (c == EOF) 
1454         return 0;
1455       x = (x << 8) | c; 
1456     }    
1457   return x;
1458 }
1459
1460 /* Try to either setup on-demand paging (using FP as backing
1461    file), or in case that doesn't work (FP not seekable) slurps in
1462    all pages and deactivates paging.  */
1463
1464 void
1465 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1466 {
1467   FILE *fp = data->fp;
1468   unsigned int npages;
1469   unsigned int i;
1470   unsigned int can_seek;
1471   long cur_file_ofs;
1472   unsigned char buf[BLOB_PAGESIZE];
1473   if (pagesz != BLOB_PAGESIZE)
1474     {
1475       /* We could handle this by slurping in everything.  */
1476       fprintf (stderr, "non matching page size\n");
1477       exit (1);
1478     }
1479   can_seek = 1;
1480   if ((cur_file_ofs = ftell(fp)) < 0)
1481     can_seek = 0;
1482   clearerr (fp);
1483 #ifdef DEBUG_PAGING
1484   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1485 #endif
1486   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1487
1488   data->num_pages = npages;
1489   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1490
1491   /* If we can't seek on our input we have to slurp in everything.  */
1492   if (!can_seek)
1493     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1494   for (i = 0; i < npages; i++)
1495     {
1496       unsigned int in_len = read_u32(fp);
1497       unsigned int compressed = in_len & 1;
1498       Attrblobpage *p = data->pages + i;
1499       in_len >>= 1;
1500 #ifdef DEBUG_PAGING
1501       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1502                i, in_len, compressed ? "" : "not ");
1503 #endif
1504       if (can_seek)
1505         {
1506           cur_file_ofs += 4;
1507           p->mapped_at = -1;
1508           p->file_offset = cur_file_ofs;
1509           p->file_size = in_len * 2 + compressed;
1510           if (fseek(fp, in_len, SEEK_CUR) < 0)
1511             {
1512               perror ("fseek");
1513               fprintf (stderr, "can't seek after we thought we can\n");
1514               /* We can't fall back to non-seeking behaviour as we already
1515                  read over some data pages without storing them away.  */
1516               exit (1);
1517             }
1518           cur_file_ofs += in_len;
1519         }
1520       else
1521         {
1522           unsigned int out_len;
1523           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1524           p->mapped_at = i * BLOB_PAGESIZE;
1525           p->file_offset = 0;
1526           p->file_size = 0;
1527           /* We can't seek, so suck everything in.  */
1528           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1529             {
1530               perror ("fread");
1531               exit (1);
1532             }
1533           if (compressed)
1534             {
1535               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1536               if (out_len != BLOB_PAGESIZE
1537                   && i < npages - 1)
1538                 {
1539                   fprintf (stderr, "can't decompress\n");
1540                   exit (1);
1541                 }
1542             }
1543         }
1544     }
1545
1546   if (can_seek)
1547     {
1548       /* If we are here we were able to seek to all page
1549          positions, so activate paging by copying FP into our structure.
1550          We dup() the file, so that our callers can fclose() it and we
1551          still have it open.  But this means that we share file positions
1552          with the input filedesc.  So in case our caller reads it after us,
1553          and calls back into us we might change the file position unexpectedly
1554          to him.  */
1555       int fd = dup (fileno (fp));
1556       if (fd < 0)
1557         {
1558           /* Jeez!  What a bloody system, we can't dup() anymore.  */
1559           perror ("dup");
1560           exit (1);
1561         }
1562       /* XXX we don't close this yet anywhere.  */
1563       data->fp = fdopen (fd, "r");
1564       if (!data->fp)
1565         {
1566           /* My God!  What happened now?  */
1567           perror ("fdopen");
1568           exit (1);
1569         }
1570     }
1571 }
1572 /*
1573 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1574 */