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