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