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