- fix attribute merge when there's nothing to merge
[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   if (dest == src || !(keyp = data->attrs[src]))
1153     return;
1154   for (; *keyp; keyp += 2)
1155     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1156 }
1157
1158 /*********************************/
1159
1160 /* unify with repo_write! */
1161
1162 #define EXTDATA_BLOCK 1023
1163 #define SCHEMATA_BLOCK 31
1164 #define SCHEMATADATA_BLOCK 255
1165
1166 struct extdata {
1167   unsigned char *buf;
1168   int len;
1169 };
1170
1171 static void
1172 data_addid(struct extdata *xd, Id x)
1173 {
1174   unsigned char *dp;
1175   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1176   dp = xd->buf + xd->len;
1177
1178   if (x >= (1 << 14))
1179     {
1180       if (x >= (1 << 28))
1181         *dp++ = (x >> 28) | 128;
1182       if (x >= (1 << 21))
1183         *dp++ = (x >> 21) | 128;
1184       *dp++ = (x >> 14) | 128;
1185     }
1186   if (x >= (1 << 7))
1187     *dp++ = (x >> 7) | 128;
1188   *dp++ = x & 127;
1189   xd->len = dp - xd->buf;
1190 }
1191
1192 static void
1193 data_addideof(struct extdata *xd, Id x, int eof)
1194 {
1195   if (x >= 64)
1196     x = (x & 63) | ((x & ~63) << 1);
1197   data_addid(xd, (eof ? x: x | 64));
1198 }
1199
1200 static void
1201 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1202 {
1203   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1204   memcpy(xd->buf + xd->len, blob, len);
1205   xd->len += len;
1206 }
1207
1208 /*********************************/
1209
1210 static void
1211 addschema_prepare(Repodata *data, Id *schematacache)
1212 {
1213   int h, len, i;
1214   Id *sp;
1215
1216   memset(schematacache, 0, 256 * sizeof(Id));
1217   for (i = 0; i < data->nschemata; i++)
1218     {
1219       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1220         h = h * 7 + *sp++;
1221       h &= 255;
1222       schematacache[h] = i + 1;
1223     }
1224   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1225   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1226 }
1227
1228 static Id
1229 addschema(Repodata *data, Id *schema, Id *schematacache)
1230 {
1231   int h, len; 
1232   Id *sp, cid; 
1233
1234   for (sp = schema, len = 0, h = 0; *sp; len++)
1235     h = h * 7 + *sp++;
1236   h &= 255; 
1237   len++;
1238
1239   cid = schematacache[h];
1240   if (cid)
1241     {    
1242       cid--;
1243       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1244         return cid;
1245       /* cache conflict */
1246       for (cid = 0; cid < data->nschemata; cid++)
1247         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1248           return cid;
1249     }
1250   /* a new one. make room. */
1251   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1252   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1253   /* add schema */
1254   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1255   data->schemata[data->nschemata] = data->schemadatalen;
1256   data->schemadatalen += len;
1257   schematacache[h] = data->nschemata + 1;
1258 #if 0
1259 fprintf(stderr, "addschema: new schema\n");
1260 #endif
1261   return data->nschemata++; 
1262 }
1263
1264
1265 void
1266 repodata_internalize(Repodata *data)
1267 {
1268   Repokey *key;
1269   Id id, entry, nentry, *ida;
1270   Id schematacache[256];
1271   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1272   unsigned char *dp, *ndp;
1273   int newschema, oldcount;
1274   struct extdata newincore;
1275   struct extdata newvincore;
1276
1277   if (!data->attrs)
1278     return;
1279
1280   newvincore.buf = data->vincore;
1281   newvincore.len = data->vincorelen;
1282
1283   schema = sat_malloc2(data->nkeys, sizeof(Id));
1284   seen = sat_malloc2(data->nkeys, sizeof(Id));
1285
1286   /* Merge the data already existing (in data->schemata, ->incoredata and
1287      friends) with the new attributes in data->attrs[].  */
1288   nentry = data->end - data->start;
1289   addschema_prepare(data, schematacache);
1290   memset(&newincore, 0, sizeof(newincore));
1291   for (entry = 0; entry < nentry; entry++)
1292     {
1293       memset(seen, 0, data->nkeys * sizeof(Id));
1294       sp = schema;
1295       dp = data->incoredata + data->incoreoffset[entry];
1296       if (data->incoredata)
1297         dp = data_read_id(dp, &oldschema);
1298       else
1299         oldschema = 0;
1300 #if 0
1301 fprintf(stderr, "oldschema %d\n", oldschema);
1302 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1303 fprintf(stderr, "schemadata %p\n", data->schemadata);
1304 #endif
1305       /* seen: -1: old data  0: skipped  >0: id + 1 */
1306       newschema = 0;
1307       oldcount = 0;
1308       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1309         {
1310           if (seen[*keyp])
1311             {
1312               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1313               exit(1);
1314             }
1315           seen[*keyp] = -1;
1316           *sp++ = *keyp;
1317           oldcount++;
1318         }
1319       if (data->attrs[entry])
1320         for (keyp = data->attrs[entry]; *keyp; keyp += 2)
1321           {
1322             if (!seen[*keyp])
1323               {
1324                 newschema = 1;
1325                 *sp++ = *keyp;
1326               }
1327             seen[*keyp] = keyp[1] + 1;
1328           }
1329       *sp++ = 0;
1330       if (newschema)
1331         /* Ideally we'd like to sort the new schema here, to ensure
1332            schema equality independend of the ordering.  We can't do that
1333            yet.  For once see below (old ids need to come before new ids).
1334            An additional difficulty is that we also need to move
1335            the values with the keys.  */
1336         schemaid = addschema(data, schema, schematacache);
1337       else
1338         schemaid = oldschema;
1339
1340
1341       /* Now create data blob.  We walk through the (possibly new) schema
1342          and either copy over old data, or insert the new.  */
1343       /* XXX Here we rely on the fact that the (new) schema has the form
1344          o1 o2 o3 o4 ... | n1 n2 n3 ...
1345          (oX being the old keyids (possibly overwritten), and nX being
1346           the new keyids).  This rules out sorting the keyids in order
1347          to ensure a small schema count.  */
1348       data->incoreoffset[entry] = newincore.len;
1349       data_addid(&newincore, schemaid);
1350       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1351         {
1352           key = data->keys + *keyp;
1353           ndp = dp;
1354           if (oldcount)
1355             {
1356               /* Skip the data associated with this old key.  */
1357               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1358                 {
1359                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1360                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1361                 }
1362               else if (key->storage == KEY_STORAGE_INCORE)
1363                 ndp = data_skip(dp, key->type);
1364               oldcount--;
1365             }
1366           if (seen[*keyp] == -1)
1367             {
1368               /* If this key was an old one _and_ was not overwritten with
1369                  a different value copy over the old value (we skipped it
1370                  above).  */
1371               if (dp != ndp)
1372                 data_addblob(&newincore, dp, ndp - dp);
1373               seen[*keyp] = 0;
1374             }
1375           else if (seen[*keyp])
1376             {
1377               /* Otherwise we have a new value.  Parse it into the internal
1378                  form.  */
1379               struct extdata *xd;
1380               unsigned int oldvincorelen = 0;
1381
1382               xd = &newincore;
1383               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1384                 {
1385                   xd = &newvincore;
1386                   oldvincorelen = xd->len;
1387                 }
1388               id = seen[*keyp] - 1;
1389               switch (key->type)
1390                 {
1391                 case REPOKEY_TYPE_VOID:
1392                 case REPOKEY_TYPE_CONSTANT:
1393                 case REPOKEY_TYPE_CONSTANTID:
1394                   break;
1395                 case REPOKEY_TYPE_STR:
1396                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1397                   break;
1398                 case REPOKEY_TYPE_ID:
1399                 case REPOKEY_TYPE_NUM:
1400                 case REPOKEY_TYPE_DIR:
1401                   data_addid(xd, id);
1402                   break;
1403                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1404                   for (ida = data->attriddata + id; *ida; ida += 3)
1405                     {
1406                       data_addid(xd, ida[0]);
1407                       data_addid(xd, ida[1]);
1408                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1409                     }
1410                   break;
1411                 case REPOKEY_TYPE_DIRSTRARRAY:
1412                   for (ida = data->attriddata + id; *ida; ida += 2)
1413                     {
1414                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1415                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1416                     }
1417                   break;
1418                 default:
1419                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1420                   exit(1);
1421                 }
1422               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1423                 {
1424                   /* put offset/len in incore */
1425                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1426                   oldvincorelen = xd->len - oldvincorelen;
1427                   data_addid(&newincore, oldvincorelen);
1428                 }
1429             }
1430           dp = ndp;
1431         }
1432       if (data->attrs[entry])
1433         sat_free(data->attrs[entry]);
1434     }
1435   sat_free(schema);
1436   sat_free(seen);
1437
1438   sat_free(data->incoredata);
1439   data->incoredata = newincore.buf;
1440   data->incoredatalen = newincore.len;
1441   data->incoredatafree = 0;
1442   
1443   sat_free(data->vincore);
1444   data->vincore = newvincore.buf;
1445   data->vincorelen = newvincore.len;
1446
1447   data->attrs = sat_free(data->attrs);
1448   data->attrdata = sat_free(data->attrdata);
1449   data->attriddata = sat_free(data->attriddata);
1450   data->attrdatalen = 0;
1451   data->attriddatalen = 0;
1452 }
1453
1454 Id
1455 repodata_str2dir(Repodata *data, const char *dir, int create)
1456 {
1457   Id id, parent;
1458   const char *dire;
1459
1460   parent = 0;
1461   while (*dir == '/' && dir[1] == '/')
1462     dir++;
1463   if (*dir == '/' && !dir[1])
1464     return 1;
1465   while (*dir)
1466     {
1467       dire = strchrnul(dir, '/');
1468       if (data->localpool)
1469         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1470       else
1471         id = strn2id(data->repo->pool, dir, dire - dir, create);
1472       if (!id)
1473         return 0;
1474       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1475       if (!parent)
1476         return 0;
1477       if (!*dire)
1478         break;
1479       dir = dire + 1;
1480       while (*dir == '/')
1481         dir++;
1482     }
1483   return parent;
1484 }
1485
1486 unsigned int
1487 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1488 {
1489   return compress_buf(page, len, cpage, max);
1490 }
1491
1492 #define SOLV_ERROR_EOF              3
1493
1494 static inline unsigned int
1495 read_u32(FILE *fp)
1496 {
1497   int c, i;
1498   unsigned int x = 0; 
1499
1500   for (i = 0; i < 4; i++) 
1501     {    
1502       c = getc(fp);
1503       if (c == EOF) 
1504         return 0;
1505       x = (x << 8) | c; 
1506     }    
1507   return x;
1508 }
1509
1510 #define SOLV_ERROR_EOF          3
1511 #define SOLV_ERROR_CORRUPT      6
1512
1513 /* Try to either setup on-demand paging (using FP as backing
1514    file), or in case that doesn't work (FP not seekable) slurps in
1515    all pages and deactivates paging.  */
1516 void
1517 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1518 {
1519   FILE *fp = data->fp;
1520   unsigned int npages;
1521   unsigned int i;
1522   unsigned int can_seek;
1523   long cur_file_ofs;
1524   unsigned char buf[BLOB_PAGESIZE];
1525
1526   if (pagesz != BLOB_PAGESIZE)
1527     {
1528       /* We could handle this by slurping in everything.  */
1529       data->error = SOLV_ERROR_CORRUPT;
1530       return;
1531     }
1532   can_seek = 1;
1533   if ((cur_file_ofs = ftell(fp)) < 0)
1534     can_seek = 0;
1535   clearerr(fp);
1536   if (can_seek)
1537     data->pagefd = dup(fileno(fp));
1538   if (data->pagefd == -1)
1539     can_seek = 0;
1540
1541 #ifdef DEBUG_PAGING
1542   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1543 #endif
1544   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1545
1546   data->num_pages = npages;
1547   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1548
1549   /* If we can't seek on our input we have to slurp in everything.  */
1550   if (!can_seek)
1551     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1552   for (i = 0; i < npages; i++)
1553     {
1554       unsigned int in_len = read_u32(fp);
1555       unsigned int compressed = in_len & 1;
1556       Attrblobpage *p = data->pages + i;
1557       in_len >>= 1;
1558 #ifdef DEBUG_PAGING
1559       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1560                i, in_len, compressed ? "" : "not ");
1561 #endif
1562       if (can_seek)
1563         {
1564           cur_file_ofs += 4;
1565           p->mapped_at = -1;
1566           p->file_offset = cur_file_ofs;
1567           p->file_size = in_len * 2 + compressed;
1568           if (fseek(fp, in_len, SEEK_CUR) < 0)
1569             {
1570               perror ("fseek");
1571               fprintf (stderr, "can't seek after we thought we can\n");
1572               /* We can't fall back to non-seeking behaviour as we already
1573                  read over some data pages without storing them away.  */
1574               data->error = SOLV_ERROR_EOF;
1575               close(data->pagefd);
1576               data->pagefd = -1;
1577               return;
1578             }
1579           cur_file_ofs += in_len;
1580         }
1581       else
1582         {
1583           unsigned int out_len;
1584           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1585           p->mapped_at = i * BLOB_PAGESIZE;
1586           p->file_offset = 0;
1587           p->file_size = 0;
1588           /* We can't seek, so suck everything in.  */
1589           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1590             {
1591               perror("fread");
1592               data->error = SOLV_ERROR_EOF;
1593               return;
1594             }
1595           if (compressed)
1596             {
1597               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1598               if (out_len != BLOB_PAGESIZE && i < npages - 1)
1599                 {
1600                   data->error = SOLV_ERROR_CORRUPT;
1601                   return;
1602                 }
1603             }
1604         }
1605     }
1606 }
1607
1608 /*
1609 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1610 */