60fdee53aa9dd5dae2ad352eb5a1596155246618
[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(Dataiterator *di, KeyValue *kv)
682 {
683   int flags = di->flags;
684
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, di->match))
707                   return 0;
708               }
709             else
710               {
711                 if (!strstr(kv->str, di->match))
712                   return 0;
713               }
714             break;
715           case SEARCH_STRING:
716             if (flags & SEARCH_NOCASE)
717               {
718                 if (strcasecmp(di->match, kv->str))
719                   return 0;
720               }
721             else
722               {
723                 if (strcmp(di->match, kv->str))
724                   return 0;
725               }
726             break;
727           case SEARCH_GLOB:
728             if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
729               return 0;
730             break;
731 #if 0
732           case SEARCH_REGEX:
733             if (regexec(&di->regexp, kv->str, 0, NULL, 0))
734               return 0;
735 #endif
736           default:
737             return 0;
738         }
739     }
740   return 1;
741 }
742
743 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
744   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
745   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
746   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
747   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
748   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
749   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
750   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
751   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
752   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
753   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
754   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
755   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
756   { SOLVABLE_FRESHENS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
757   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
758 };
759
760 int
761 dataiterator_step(Dataiterator *di)
762 {
763 restart:
764   while (1)
765     {
766       if (di->state)
767         {
768           if (di->idp)
769             {
770               Id *idp = di->idp;
771               if (*idp)
772                 {
773                   di->kv.id = *idp;
774                   di->idp++;
775                   di->kv.eof = idp[1] ? 0 : 1;
776                   goto weg2;
777                 }
778               else
779                 di->idp = 0;
780             }
781           Solvable *s = di->repo->pool->solvables + di->solvid;
782           int state = di->state;
783           di->key = solvablekeys + state - 1;
784           if (di->keyname)
785             di->state = RPM_RPMDBID;
786           else
787             di->state++;
788           if (state == 1)
789             {
790               di->data = 0;
791               if (di->keyname)
792                 state = di->keyname - 1;
793             }
794           switch (state + 1)
795             {
796               case SOLVABLE_NAME:
797                 if (!s->name)
798                   continue;
799                 di->kv.id = s->name;
800                 di->kv.eof = 1;
801                 break;
802               case SOLVABLE_ARCH:
803                 if (!s->arch)
804                   continue;
805                 di->kv.id = s->arch;
806                 di->kv.eof = 1;
807                 break;
808               case SOLVABLE_EVR:
809                 if (!s->evr)
810                   continue;
811                 di->kv.id = s->evr;
812                 di->kv.eof = 1;
813                 break;
814               case SOLVABLE_VENDOR:
815                 if (!s->vendor)
816                   continue;
817                 di->kv.id = s->vendor;
818                 di->kv.eof = 1;
819                 break;
820               case SOLVABLE_PROVIDES:
821                 di->idp = s->provides
822                     ? di->repo->idarraydata + s->provides : 0;
823                 continue;
824               case SOLVABLE_OBSOLETES:
825                 di->idp = s->obsoletes
826                     ? di->repo->idarraydata + s->obsoletes : 0;
827                 continue;
828               case SOLVABLE_CONFLICTS:
829                 di->idp = s->conflicts
830                     ? di->repo->idarraydata + s->conflicts : 0;
831                 continue;
832               case SOLVABLE_REQUIRES:
833                 di->idp = s->requires
834                     ? di->repo->idarraydata + s->requires : 0;
835                 continue;
836               case SOLVABLE_RECOMMENDS:
837                 di->idp = s->recommends
838                     ? di->repo->idarraydata + s->recommends : 0;
839                 continue;
840               case SOLVABLE_SUPPLEMENTS:
841                 di->idp = s->supplements
842                     ? di->repo->idarraydata + s->supplements : 0;
843                 continue;
844               case SOLVABLE_SUGGESTS:
845                 di->idp = s->suggests
846                     ? di->repo->idarraydata + s->suggests : 0;
847                 continue;
848               case SOLVABLE_ENHANCES:
849                 di->idp = s->enhances
850                     ? di->repo->idarraydata + s->enhances : 0;
851                 continue;
852               case SOLVABLE_FRESHENS:
853                 di->idp = s->freshens
854                     ? di->repo->idarraydata + s->freshens : 0;
855                 continue;
856               case RPM_RPMDBID:
857                 if (!di->repo->rpmdbid)
858                   continue;
859                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
860                 di->kv.eof = 1;
861                 break;
862               default:
863                 di->data = di->repo->repodata - 1;
864                 di->kv.eof = 1;
865                 di->state = 0;
866                 continue;
867             }
868         }
869       else
870         {
871           if (di->kv.eof)
872             di->dp = 0;
873           else
874             di->dp = data_fetch(di->dp, &di->kv, di->key);
875
876           while (!di->dp)
877             {
878               Id keyid;
879               if (di->keyname || !(keyid = *di->keyp++))
880                 {
881                   while (1)
882                     {
883                       Repo *repo = di->repo;
884                       Repodata *data = ++di->data;
885                       if (data >= repo->repodata + repo->nrepodata)
886                         {
887                           if (di->flags & __SEARCH_ONESOLVABLE)
888                             return 0;
889                           if (di->solvid >= 0)
890                             {
891                               while (++di->solvid < repo->end)
892                                 if (repo->pool->solvables[di->solvid].repo == repo)
893                                   break;
894                               if (di->solvid >= repo->end)
895                                 {
896                                   if (!(di->flags & SEARCH_EXTRA))
897                                     goto skiprepo;
898                                   di->solvid = -1;
899                                   if (di->solvid < -repo->nextra)
900                                     goto skiprepo;
901                                 }
902                             }
903                           else
904                             {
905                               --di->solvid;
906                               if (di->solvid < -repo->nextra)
907                                 {
908 skiprepo:;
909                                   Pool *pool = di->repo->pool;
910                                   if (!(di->flags & SEARCH_ALL_REPOS)
911                                       || di->repo == pool->repos[pool->nrepos - 1])
912                                     return 0;
913                                   int i;
914                                   for (i = 0; i < pool->nrepos; i++)
915                                     if (di->repo == pool->repos[i])
916                                       break;
917                                   di->repo = pool->repos[i + 1];
918                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
919                                   continue;
920                                 }
921                             }
922                           di->data = repo->repodata - 1;
923                           if (di->solvid < 0
924                               || (di->flags & SEARCH_NO_STORAGE_SOLVABLE))
925                             continue;
926                           static Id zeroid = 0;
927                           di->keyp = &zeroid;
928                           di->state = 1;
929                           goto restart;
930                         }
931                       if ((di->solvid < 0 && (-1 - di->solvid) >= data->extrastart && (-1 - di->solvid) < (data->extrastart + data->nextra))
932                           || (di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
933                         {
934                           dataiterator_newdata(di);
935                           if (di->nextkeydp)
936                             break;
937                         }
938                     }
939                 }
940               else
941                 {
942                   di->key = di->data->keys + keyid;
943                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
944                 }
945               di->dp = data_fetch(di->dp, &di->kv, di->key);
946             }
947         }
948 weg2:
949       if (!di->match
950           || dataiterator_match(di, &di->kv))
951         break;
952     }
953   return 1;
954 }
955
956 void
957 dataiterator_skip_attribute(Dataiterator *di)
958 {
959   if (di->state)
960     di->idp = 0;
961   /* This will make the next _step call to retrieve the next field.  */
962   di->kv.eof = 1;
963 }
964
965 void
966 dataiterator_skip_solvable(Dataiterator *di)
967 {
968   /* We're done with this field.  */
969   di->kv.eof = 1;
970   /* And with solvable data.  */
971   di->state = 0;
972   /* And with all keys for this repodata and thing. */
973   static Id zeroid = 0;
974   di->keyp = &zeroid;
975   /* And with all repodatas for this thing.  */
976   di->data = di->repo->repodata + di->repo->nrepodata - 1;
977   /* Hence the next call to _step will retrieve the next thing.  */
978 }
979
980 void
981 dataiterator_skip_repo(Dataiterator *di)
982 {
983   dataiterator_skip_solvable(di);
984   /* We're done with all solvables and all extra things for this repo.  */
985   di->solvid = -1 - di->repo->nextra;
986 }
987
988 void
989 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
990 {
991   di->repo = s->repo;
992   /* Simulate us being done with the solvable before the requested one.  */
993   dataiterator_skip_solvable(di);
994   di->solvid = s - s->repo->pool->solvables;
995   di->solvid--;
996 }
997
998 void
999 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1000 {
1001   di->repo = repo;
1002   dataiterator_skip_solvable(di);
1003   di->solvid = repo->start - 1;
1004 }
1005
1006 /* extend repodata so that it includes solvables p */
1007 void
1008 repodata_extend(Repodata *data, Id p)
1009 {
1010   if (data->start == data->end)
1011     data->start = data->end = p;
1012   if (p >= data->end)
1013     {
1014       int old = data->end - data->start;
1015       int new = p - data->end + 1;
1016       if (data->attrs)
1017         {
1018           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1019           memset(data->attrs + old, 0, new * sizeof(Id *));
1020         }
1021       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1022       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1023       data->end = p + 1;
1024     }
1025   if (p < data->start)
1026     {
1027       int old = data->end - data->start;
1028       int new = data->start - p;
1029       if (data->attrs)
1030         {
1031           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1032           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1033           memset(data->attrs, 0, new * sizeof(Id *));
1034         }
1035       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1036       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1037       memset(data->incoreoffset, 0, new * sizeof(Id));
1038       data->start = p;
1039     }
1040 }
1041
1042 void
1043 repodata_extend_extra(Repodata *data, int nextra)
1044 {
1045   if (nextra <= data->nextra)
1046     return;
1047   if (data->extraattrs)
1048     {
1049       data->extraattrs = sat_extend(data->extraattrs, data->nextra, nextra - data->nextra, sizeof(Id *), REPODATA_BLOCK);
1050       memset(data->extraattrs + data->nextra, 0, (nextra - data->nextra) * sizeof (Id *));
1051     }
1052   data->extraoffset = sat_extend(data->extraoffset, data->nextra, nextra - data->nextra, sizeof(Id), REPODATA_BLOCK);
1053   memset(data->extraoffset + data->nextra, 0, (nextra - data->nextra) * sizeof(Id));
1054   data->nextra = nextra;
1055 }
1056
1057 void
1058 repodata_extend_block(Repodata *data, Id start, Id num)
1059 {
1060   if (!num)
1061     return;
1062   if (!data->incoreoffset)
1063     {
1064       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1065       data->start = start;
1066       data->end = start + num;
1067       return;
1068     }
1069   repodata_extend(data, start);
1070   if (num > 1)
1071     repodata_extend(data, start + num - 1);
1072 }
1073
1074 /**********************************************************************/
1075
1076 #define REPODATA_ATTRS_BLOCK 63
1077 #define REPODATA_ATTRDATA_BLOCK 1023
1078 #define REPODATA_ATTRIDDATA_BLOCK 63
1079
1080 static void
1081 repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
1082 {
1083   Id *pp;
1084   Id *ap;
1085   int i;
1086   if (!data->attrs && entry >= 0)
1087     {
1088       data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
1089                                      REPODATA_BLOCK);
1090     }
1091   else if (!data->extraattrs && entry < 0)
1092     data->extraattrs = sat_calloc_block(data->nextra, sizeof(Id *), REPODATA_BLOCK);
1093   if (entry < 0)
1094     ap = data->extraattrs[-1 - entry];
1095   else
1096     ap = data->attrs[entry];
1097   i = 0;
1098   if (ap)
1099     {
1100       for (pp = ap; *pp; pp += 2)
1101         /* Determine equality based on the name only, allows us to change
1102            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1103         if (data->keys[*pp].name == data->keys[keyid].name)
1104           break;
1105       if (*pp)
1106         {
1107           if (overwrite)
1108             {
1109               pp[0] = keyid;
1110               pp[1] = val;
1111             }
1112           return;
1113         }
1114       i = pp - ap;
1115     }
1116   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1117   if (entry < 0)
1118     data->extraattrs[-1 - entry] = ap;
1119   else
1120     data->attrs[entry] = ap;
1121   pp = ap + i;
1122   *pp++ = keyid;
1123   *pp++ = val;
1124   *pp = 0;
1125 }
1126
1127 void
1128 repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
1129 {
1130   Id keyid;
1131
1132   /* find key in keys */
1133   for (keyid = 1; keyid < data->nkeys; keyid++)
1134     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
1135       {
1136         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
1137           continue;
1138         break;
1139       }
1140   if (keyid == data->nkeys)
1141     {
1142       /* allocate new key */
1143       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
1144       data->keys[data->nkeys++] = *key;
1145       if (data->verticaloffset)
1146         {
1147           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
1148           data->verticaloffset[data->nkeys - 1] = 0;
1149         }
1150     }
1151   repodata_insert_keyid(data, entry, keyid, val, 1);
1152 }
1153
1154 void
1155 repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
1156 {
1157   Repokey key;
1158   key.name = keyname;
1159   key.type = REPOKEY_TYPE_ID;
1160   key.size = 0;
1161   key.storage = KEY_STORAGE_INCORE;
1162   repodata_set(data, entry, &key, id);
1163 }
1164
1165 void
1166 repodata_set_num(Repodata *data, Id entry, Id keyname, unsigned int num)
1167 {
1168   Repokey key;
1169   key.name = keyname;
1170   key.type = REPOKEY_TYPE_NUM;
1171   key.size = 0;
1172   key.storage = KEY_STORAGE_INCORE;
1173   repodata_set(data, entry, &key, (Id)num);
1174 }
1175
1176 void
1177 repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
1178 {
1179   Repokey key;
1180   Id id;
1181   if (data->localpool)
1182     id = stringpool_str2id(&data->spool, str, 1);
1183   else
1184     id = str2id(data->repo->pool, str, 1);
1185   key.name = keyname;
1186   key.type = REPOKEY_TYPE_ID;
1187   key.size = 0;
1188   key.storage = KEY_STORAGE_INCORE;
1189   repodata_set(data, entry, &key, id);
1190 }
1191
1192 void
1193 repodata_set_constant(Repodata *data, Id entry, Id keyname, unsigned int constant)
1194 {
1195   Repokey key;
1196   key.name = keyname;
1197   key.type = REPOKEY_TYPE_CONSTANT;
1198   key.size = constant;
1199   key.storage = KEY_STORAGE_INCORE;
1200   repodata_set(data, entry, &key, 0);
1201 }
1202
1203 void
1204 repodata_set_constantid(Repodata *data, Id entry, Id keyname, Id id)
1205 {
1206   Repokey key;
1207   key.name = keyname;
1208   key.type = REPOKEY_TYPE_CONSTANTID;
1209   key.size = id;
1210   key.storage = KEY_STORAGE_INCORE;
1211   repodata_set(data, entry, &key, 0);
1212 }
1213
1214 void
1215 repodata_set_void(Repodata *data, Id entry, Id keyname)
1216 {
1217   Repokey key;
1218   key.name = keyname;
1219   key.type = REPOKEY_TYPE_VOID;
1220   key.size = 0;
1221   key.storage = KEY_STORAGE_INCORE;
1222   repodata_set(data, entry, &key, 0);
1223 }
1224
1225 void
1226 repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
1227 {
1228   Repokey key;
1229   int l;
1230
1231   l = strlen(str) + 1;
1232   key.name = keyname;
1233   key.type = REPOKEY_TYPE_STR;
1234   key.size = 0;
1235   key.storage = KEY_STORAGE_INCORE;
1236   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1237   memcpy(data->attrdata + data->attrdatalen, str, l);
1238   repodata_set(data, entry, &key, data->attrdatalen);
1239   data->attrdatalen += l;
1240 }
1241
1242 static void
1243 repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
1244 {
1245   int oldsize;
1246   Id *ida, *pp;
1247
1248   if (entry < 0)
1249     pp = data->extraattrs ? data->extraattrs[-1 - entry] : 0;
1250   else
1251     pp = data->attrs ? data->attrs[entry] : 0;
1252   if (pp)
1253     for (; *pp; pp += 2)
1254       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1255         break;
1256   if (!pp || !*pp)
1257     {
1258       /* not found. allocate new key */
1259       Repokey key;
1260       key.name = keyname;
1261       key.type = keytype;
1262       key.size = 0;
1263       key.storage = KEY_STORAGE_INCORE;
1264       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1265       repodata_set(data, entry, &key, data->attriddatalen);
1266       return;
1267     }
1268   oldsize = 0;
1269   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1270     oldsize += entrysize;
1271   if (ida + 1 == data->attriddata + data->attriddatalen)
1272     {
1273       /* this was the last entry, just append it */
1274       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1275       data->attriddatalen--;    /* overwrite terminating 0  */
1276     }
1277   else
1278     {
1279       /* too bad. move to back. */
1280       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1281       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1282       pp[1] = data->attriddatalen;
1283       data->attriddatalen += oldsize;
1284     }
1285 }
1286
1287 static inline int
1288 checksumtype2len(Id type)
1289 {
1290   switch (type)
1291     {
1292     case REPOKEY_TYPE_MD5:
1293       return SIZEOF_MD5;
1294     case REPOKEY_TYPE_SHA1:
1295       return SIZEOF_SHA1;
1296     case REPOKEY_TYPE_SHA256:
1297       return SIZEOF_SHA256;
1298     default:
1299       return 0;
1300     }
1301 }
1302
1303 void
1304 repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
1305                       const unsigned char *str)
1306 {
1307   Repokey key;
1308   int l = checksumtype2len(type);
1309
1310   if (!l)
1311     return;
1312   key.name = keyname;
1313   key.type = type;
1314   key.size = 0;
1315   key.storage = KEY_STORAGE_INCORE;
1316   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1317   memcpy(data->attrdata + data->attrdatalen, str, l);
1318   repodata_set(data, entry, &key, data->attrdatalen);
1319   data->attrdatalen += l;
1320 }
1321
1322 static int
1323 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1324 {
1325   int i;
1326   for (i = 0; i < buflen; i++)
1327     {
1328 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
1329                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
1330                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
1331                 : -1)
1332       int v = c2h(*str);
1333       str++;
1334       if (v < 0)
1335         return 0;
1336       buf[i] = v;
1337       v = c2h(*str);
1338       str++;
1339       if (v < 0)
1340         return 0;
1341       buf[i] = (buf[i] << 4) | v;
1342 #undef c2h
1343     }
1344   return buflen;
1345 }
1346
1347 void
1348 repodata_set_checksum(Repodata *data, Id entry, Id keyname, Id type,
1349                       const char *str)
1350 {
1351   unsigned char buf[64];
1352   int l = checksumtype2len(type);
1353
1354   if (!l)
1355     return;
1356   if (hexstr2bytes(buf, str, l) != l)
1357     {
1358       fprintf(stderr, "Invalid hex character in '%s'\n", str);
1359       return;
1360     }
1361   repodata_set_bin_checksum(data, entry, keyname, type, buf);
1362 }
1363
1364 const char *
1365 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1366 {
1367   int i, l;
1368   char *str, *s;
1369
1370   l = checksumtype2len(type);
1371   if (!l)
1372     return "";
1373   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1374   for (i = 0; i < l; i++)
1375     {
1376       unsigned char v = buf[i];
1377       unsigned char w = v >> 4;
1378       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1379       w = v & 15;
1380       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1381     }
1382   *s = 0;
1383   return str;
1384 }
1385
1386 Id
1387 repodata_globalize_id(Repodata *data, Id id)
1388
1389   if (!data || !data->localpool)
1390     return id;
1391   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
1392 }
1393
1394 void
1395 repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
1396 {
1397
1398 #if 0
1399 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
1400 #endif
1401   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1402   data->attriddata[data->attriddatalen++] = dir;
1403   data->attriddata[data->attriddatalen++] = num;
1404   data->attriddata[data->attriddatalen++] = num2;
1405   data->attriddata[data->attriddatalen++] = 0;
1406 }
1407
1408 void
1409 repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
1410 {
1411   Id stroff;
1412   int l;
1413
1414   l = strlen(str) + 1;
1415   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1416   memcpy(data->attrdata + data->attrdatalen, str, l);
1417   stroff = data->attrdatalen;
1418   data->attrdatalen += l;
1419
1420 #if 0
1421 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
1422 #endif
1423   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1424   data->attriddata[data->attriddatalen++] = dir;
1425   data->attriddata[data->attriddatalen++] = stroff;
1426   data->attriddata[data->attriddatalen++] = 0;
1427 }
1428
1429 void
1430 repodata_add_idarray(Repodata *data, Id entry, Id keyname, Id id)
1431 {
1432 #if 0
1433 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
1434 #endif
1435   repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
1436   data->attriddata[data->attriddatalen++] = id;
1437   data->attriddata[data->attriddatalen++] = 0;
1438 }
1439
1440 void
1441 repodata_add_poolstr_array(Repodata *data, Id entry, Id keyname,
1442                            const char *str)
1443 {
1444   Id id;
1445   if (data->localpool)
1446     id = stringpool_str2id(&data->spool, str, 1);
1447   else
1448     id = str2id(data->repo->pool, str, 1);
1449   repodata_add_idarray(data, entry, keyname, id);
1450 }
1451
1452 void
1453 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1454 {
1455   Id *keyp;
1456   if (dest == src
1457       || !(keyp = src < 0 ? data->extraattrs[-1 - src] : data->attrs[src]))
1458     return;
1459   for (; *keyp; keyp += 2)
1460     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1461 }
1462
1463 /*********************************/
1464
1465 /* unify with repo_write! */
1466
1467 #define EXTDATA_BLOCK 1023
1468 #define SCHEMATA_BLOCK 31
1469 #define SCHEMATADATA_BLOCK 255
1470
1471 struct extdata {
1472   unsigned char *buf;
1473   int len;
1474 };
1475
1476 static void
1477 data_addid(struct extdata *xd, Id x)
1478 {
1479   unsigned char *dp;
1480   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1481   dp = xd->buf + xd->len;
1482
1483   if (x >= (1 << 14))
1484     {
1485       if (x >= (1 << 28))
1486         *dp++ = (x >> 28) | 128;
1487       if (x >= (1 << 21))
1488         *dp++ = (x >> 21) | 128;
1489       *dp++ = (x >> 14) | 128;
1490     }
1491   if (x >= (1 << 7))
1492     *dp++ = (x >> 7) | 128;
1493   *dp++ = x & 127;
1494   xd->len = dp - xd->buf;
1495 }
1496
1497 static void
1498 data_addideof(struct extdata *xd, Id x, int eof)
1499 {
1500   if (x >= 64)
1501     x = (x & 63) | ((x & ~63) << 1);
1502   data_addid(xd, (eof ? x: x | 64));
1503 }
1504
1505 static void
1506 data_addblob(struct extdata *xd, unsigned char *blob, int len)
1507 {
1508   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
1509   memcpy(xd->buf + xd->len, blob, len);
1510   xd->len += len;
1511 }
1512
1513 /*********************************/
1514
1515 static void
1516 addschema_prepare(Repodata *data, Id *schematacache)
1517 {
1518   int h, len, i;
1519   Id *sp;
1520
1521   memset(schematacache, 0, 256 * sizeof(Id));
1522   for (i = 0; i < data->nschemata; i++)
1523     {
1524       for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
1525         h = h * 7 + *sp++;
1526       h &= 255;
1527       schematacache[h] = i + 1;
1528     }
1529   data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
1530   data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
1531 }
1532
1533 static Id
1534 addschema(Repodata *data, Id *schema, Id *schematacache)
1535 {
1536   int h, len; 
1537   Id *sp, cid; 
1538
1539   for (sp = schema, len = 0, h = 0; *sp; len++)
1540     h = h * 7 + *sp++;
1541   h &= 255; 
1542   len++;
1543
1544   cid = schematacache[h];
1545   if (cid)
1546     {    
1547       cid--;
1548       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1549         return cid;
1550       /* cache conflict */
1551       for (cid = 0; cid < data->nschemata; cid++)
1552         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
1553           return cid;
1554     }
1555   /* a new one. make room. */
1556   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
1557   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
1558   /* add schema */
1559   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
1560   data->schemata[data->nschemata] = data->schemadatalen;
1561   data->schemadatalen += len;
1562   schematacache[h] = data->nschemata + 1;
1563 #if 0
1564 fprintf(stderr, "addschema: new schema\n");
1565 #endif
1566   return data->nschemata++; 
1567 }
1568
1569
1570 void
1571 repodata_internalize(Repodata *data)
1572 {
1573   Repokey *key;
1574   Id id, entry, nentry, *ida;
1575   Id schematacache[256];
1576   Id schemaid, *schema, *sp, oldschema, *keyp, *seen;
1577   unsigned char *dp, *ndp;
1578   int newschema, oldcount;
1579   struct extdata newincore;
1580   struct extdata newvincore;
1581
1582   if (!data->attrs && !data->extraattrs)
1583     return;
1584
1585   newvincore.buf = data->vincore;
1586   newvincore.len = data->vincorelen;
1587
1588   schema = sat_malloc2(data->nkeys, sizeof(Id));
1589   seen = sat_malloc2(data->nkeys, sizeof(Id));
1590
1591   /* Merge the data already existing (in data->schemata, ->incoredata and
1592      friends) with the new attributes in data->attrs[].  */
1593   nentry = data->end - data->start;
1594   addschema_prepare(data, schematacache);
1595   memset(&newincore, 0, sizeof(newincore));
1596   data_addid(&newincore, 0);
1597   if (!data->attrs)
1598     nentry = 0;
1599   for (entry = data->extraattrs ? -data->nextra : 0; entry < nentry; entry++)
1600     {
1601       memset(seen, 0, data->nkeys * sizeof(Id));
1602       sp = schema;
1603       dp = entry2data(data, entry);
1604       if (data->incoredata)
1605         dp = data_read_id(dp, &oldschema);
1606       else
1607         oldschema = 0;
1608 #if 0
1609 fprintf(stderr, "oldschema %d\n", oldschema);
1610 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
1611 fprintf(stderr, "schemadata %p\n", data->schemadata);
1612 #endif
1613       /* seen: -1: old data  0: skipped  >0: id + 1 */
1614       newschema = 0;
1615       oldcount = 0;
1616       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
1617         {
1618           if (seen[*keyp])
1619             {
1620               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
1621               exit(1);
1622             }
1623           seen[*keyp] = -1;
1624           *sp++ = *keyp;
1625           oldcount++;
1626         }
1627       keyp = entry < 0 ? data->extraattrs[-1 - entry] : data->attrs[entry];
1628       if (keyp)
1629         for (; *keyp; keyp += 2)
1630           {
1631             if (!seen[*keyp])
1632               {
1633                 newschema = 1;
1634                 *sp++ = *keyp;
1635               }
1636             seen[*keyp] = keyp[1] + 1;
1637           }
1638       *sp++ = 0;
1639       if (newschema)
1640         /* Ideally we'd like to sort the new schema here, to ensure
1641            schema equality independend of the ordering.  We can't do that
1642            yet.  For once see below (old ids need to come before new ids).
1643            An additional difficulty is that we also need to move
1644            the values with the keys.  */
1645         schemaid = addschema(data, schema, schematacache);
1646       else
1647         schemaid = oldschema;
1648
1649
1650       /* Now create data blob.  We walk through the (possibly new) schema
1651          and either copy over old data, or insert the new.  */
1652       /* XXX Here we rely on the fact that the (new) schema has the form
1653          o1 o2 o3 o4 ... | n1 n2 n3 ...
1654          (oX being the old keyids (possibly overwritten), and nX being
1655           the new keyids).  This rules out sorting the keyids in order
1656          to ensure a small schema count.  */
1657       if (entry < 0)
1658         data->extraoffset[-1 - entry] = newincore.len;
1659       else
1660         data->incoreoffset[entry] = newincore.len;
1661       data_addid(&newincore, schemaid);
1662       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
1663         {
1664           key = data->keys + *keyp;
1665           ndp = dp;
1666           if (oldcount)
1667             {
1668               /* Skip the data associated with this old key.  */
1669               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1670                 {
1671                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
1672                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
1673                 }
1674               else if (key->storage == KEY_STORAGE_INCORE)
1675                 ndp = data_skip(dp, key->type);
1676               oldcount--;
1677             }
1678           if (seen[*keyp] == -1)
1679             {
1680               /* If this key was an old one _and_ was not overwritten with
1681                  a different value copy over the old value (we skipped it
1682                  above).  */
1683               if (dp != ndp)
1684                 data_addblob(&newincore, dp, ndp - dp);
1685               seen[*keyp] = 0;
1686             }
1687           else if (seen[*keyp])
1688             {
1689               /* Otherwise we have a new value.  Parse it into the internal
1690                  form.  */
1691               struct extdata *xd;
1692               unsigned int oldvincorelen = 0;
1693
1694               xd = &newincore;
1695               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1696                 {
1697                   xd = &newvincore;
1698                   oldvincorelen = xd->len;
1699                 }
1700               id = seen[*keyp] - 1;
1701               switch (key->type)
1702                 {
1703                 case REPOKEY_TYPE_VOID:
1704                 case REPOKEY_TYPE_CONSTANT:
1705                 case REPOKEY_TYPE_CONSTANTID:
1706                   break;
1707                 case REPOKEY_TYPE_STR:
1708                   data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
1709                   break;
1710                 case REPOKEY_TYPE_MD5:
1711                   data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
1712                   break;
1713                 case REPOKEY_TYPE_SHA1:
1714                   data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
1715                   break;
1716                 case REPOKEY_TYPE_ID:
1717                 case REPOKEY_TYPE_NUM:
1718                 case REPOKEY_TYPE_DIR:
1719                   data_addid(xd, id);
1720                   break;
1721                 case REPOKEY_TYPE_IDARRAY:
1722                   for (ida = data->attriddata + id; *ida; ida++)
1723                     data_addideof(xd, ida[0], ida[1] ? 0 : 1);
1724                   break;
1725                 case REPOKEY_TYPE_DIRNUMNUMARRAY:
1726                   for (ida = data->attriddata + id; *ida; ida += 3)
1727                     {
1728                       data_addid(xd, ida[0]);
1729                       data_addid(xd, ida[1]);
1730                       data_addideof(xd, ida[2], ida[3] ? 0 : 1);
1731                     }
1732                   break;
1733                 case REPOKEY_TYPE_DIRSTRARRAY:
1734                   for (ida = data->attriddata + id; *ida; ida += 2)
1735                     {
1736                       data_addideof(xd, ida[0], ida[2] ? 0 : 1);
1737                       data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
1738                     }
1739                   break;
1740                 default:
1741                   fprintf(stderr, "don't know how to handle type %d\n", key->type);
1742                   exit(1);
1743                 }
1744               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1745                 {
1746                   /* put offset/len in incore */
1747                   data_addid(&newincore, data->lastverticaloffset + oldvincorelen);
1748                   oldvincorelen = xd->len - oldvincorelen;
1749                   data_addid(&newincore, oldvincorelen);
1750                 }
1751             }
1752           dp = ndp;
1753         }
1754       if (entry < 0 && data->extraattrs[-1 - entry])
1755         sat_free(data->extraattrs[-1 - entry]);
1756       else if (entry >= 0 && data->attrs[entry])
1757         sat_free(data->attrs[entry]);
1758     }
1759   sat_free(schema);
1760   sat_free(seen);
1761
1762   sat_free(data->incoredata);
1763   data->incoredata = newincore.buf;
1764   data->incoredatalen = newincore.len;
1765   data->incoredatafree = 0;
1766   
1767   sat_free(data->vincore);
1768   data->vincore = newvincore.buf;
1769   data->vincorelen = newvincore.len;
1770
1771   data->attrs = sat_free(data->attrs);
1772   data->extraattrs = sat_free(data->extraattrs);
1773   data->attrdata = sat_free(data->attrdata);
1774   data->attriddata = sat_free(data->attriddata);
1775   data->attrdatalen = 0;
1776   data->attriddatalen = 0;
1777 }
1778
1779 Id
1780 repodata_str2dir(Repodata *data, const char *dir, int create)
1781 {
1782   Id id, parent;
1783   const char *dire;
1784
1785   parent = 0;
1786   while (*dir == '/' && dir[1] == '/')
1787     dir++;
1788   if (*dir == '/' && !dir[1])
1789     return 1;
1790   while (*dir)
1791     {
1792       dire = strchrnul(dir, '/');
1793       if (data->localpool)
1794         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
1795       else
1796         id = strn2id(data->repo->pool, dir, dire - dir, create);
1797       if (!id)
1798         return 0;
1799       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
1800       if (!parent)
1801         return 0;
1802       if (!*dire)
1803         break;
1804       dir = dire + 1;
1805       while (*dir == '/')
1806         dir++;
1807     }
1808   return parent;
1809 }
1810
1811 const char *
1812 repodata_dir2str(Repodata *data, Id did, const char *suf)
1813 {
1814   Pool *pool = data->repo->pool;
1815   int l = 0;
1816   Id parent, comp;
1817   const char *comps;
1818   char *p;
1819
1820   if (!did)
1821     return suf ? suf : "";
1822   parent = did;
1823   while (parent)
1824     {
1825       comp = dirpool_compid(&data->dirpool, parent);
1826       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1827       l += strlen(comps);
1828       parent = dirpool_parent(&data->dirpool, parent);
1829       if (parent)
1830         l++;
1831     }
1832   if (suf)
1833     l += strlen(suf) + 1;
1834   p = pool_alloctmpspace(pool, l + 1) + l;
1835   *p = 0;
1836   if (suf)
1837     {
1838       p -= strlen(suf);
1839       strcpy(p, suf);
1840       *--p = '/';
1841     }
1842   parent = did;
1843   while (parent)
1844     {
1845       comp = dirpool_compid(&data->dirpool, parent);
1846       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
1847       l = strlen(comps);
1848       p -= l;
1849       strncpy(p, comps, l);
1850       parent = dirpool_parent(&data->dirpool, parent);
1851       if (parent)
1852         *--p = '/';
1853     }
1854   return p;
1855 }
1856
1857 unsigned int
1858 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
1859 {
1860   return compress_buf(page, len, cpage, max);
1861 }
1862
1863 #define SOLV_ERROR_EOF              3
1864
1865 static inline unsigned int
1866 read_u32(FILE *fp)
1867 {
1868   int c, i;
1869   unsigned int x = 0; 
1870
1871   for (i = 0; i < 4; i++) 
1872     {    
1873       c = getc(fp);
1874       if (c == EOF) 
1875         return 0;
1876       x = (x << 8) | c; 
1877     }    
1878   return x;
1879 }
1880
1881 #define SOLV_ERROR_EOF          3
1882 #define SOLV_ERROR_CORRUPT      6
1883
1884 /* Try to either setup on-demand paging (using FP as backing
1885    file), or in case that doesn't work (FP not seekable) slurps in
1886    all pages and deactivates paging.  */
1887 void
1888 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
1889 {
1890   FILE *fp = data->fp;
1891   unsigned int npages;
1892   unsigned int i;
1893   unsigned int can_seek;
1894   long cur_file_ofs;
1895   unsigned char buf[BLOB_PAGESIZE];
1896
1897   if (pagesz != BLOB_PAGESIZE)
1898     {
1899       /* We could handle this by slurping in everything.  */
1900       data->error = SOLV_ERROR_CORRUPT;
1901       return;
1902     }
1903   can_seek = 1;
1904   if ((cur_file_ofs = ftell(fp)) < 0)
1905     can_seek = 0;
1906   clearerr(fp);
1907   if (can_seek)
1908     data->pagefd = dup(fileno(fp));
1909   if (data->pagefd == -1)
1910     can_seek = 0;
1911
1912 #ifdef DEBUG_PAGING
1913   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
1914 #endif
1915   npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
1916
1917   data->num_pages = npages;
1918   data->pages = sat_malloc2(npages, sizeof(data->pages[0]));
1919
1920   /* If we can't seek on our input we have to slurp in everything.  */
1921   if (!can_seek)
1922     data->blob_store = sat_malloc(npages * BLOB_PAGESIZE);
1923   for (i = 0; i < npages; i++)
1924     {
1925       unsigned int in_len = read_u32(fp);
1926       unsigned int compressed = in_len & 1;
1927       Attrblobpage *p = data->pages + i;
1928       in_len >>= 1;
1929 #ifdef DEBUG_PAGING
1930       fprintf (stderr, "page %d: len %d (%scompressed)\n",
1931                i, in_len, compressed ? "" : "not ");
1932 #endif
1933       if (can_seek)
1934         {
1935           cur_file_ofs += 4;
1936           p->mapped_at = -1;
1937           p->file_offset = cur_file_ofs;
1938           p->file_size = in_len * 2 + compressed;
1939           if (fseek(fp, in_len, SEEK_CUR) < 0)
1940             {
1941               perror ("fseek");
1942               fprintf (stderr, "can't seek after we thought we can\n");
1943               /* We can't fall back to non-seeking behaviour as we already
1944                  read over some data pages without storing them away.  */
1945               data->error = SOLV_ERROR_EOF;
1946               close(data->pagefd);
1947               data->pagefd = -1;
1948               return;
1949             }
1950           cur_file_ofs += in_len;
1951         }
1952       else
1953         {
1954           unsigned int out_len;
1955           void *dest = data->blob_store + i * BLOB_PAGESIZE;
1956           p->mapped_at = i * BLOB_PAGESIZE;
1957           p->file_offset = 0;
1958           p->file_size = 0;
1959           /* We can't seek, so suck everything in.  */
1960           if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
1961             {
1962               perror("fread");
1963               data->error = SOLV_ERROR_EOF;
1964               return;
1965             }
1966           if (compressed)
1967             {
1968               out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
1969               if (out_len != BLOB_PAGESIZE && i < npages - 1)
1970                 {
1971                   data->error = SOLV_ERROR_CORRUPT;
1972                   return;
1973                 }
1974             }
1975         }
1976     }
1977 }
1978
1979 void
1980 repodata_disable_paging(Repodata *data)
1981 {
1982   if (maybe_load_repodata(data, 0)
1983       && data->num_pages)
1984     load_page_range (data, 0, data->num_pages - 1);
1985 }
1986 /*
1987 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
1988 */