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