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