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