da3cea168953ffd3e9ec462d249fef5383617e59
[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 #include "repopage.h"
31
32 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
33                                   unsigned char *out, unsigned int out_len);
34 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
35                                               unsigned int in_len,
36                                               unsigned char *out,
37                                               unsigned int out_len);
38
39 #define REPODATA_BLOCK 255
40
41
42 void
43 repodata_init(Repodata *data, Repo *repo, int localpool)
44 {
45   memset(data, 0, sizeof (*data));
46   data->repo = repo;
47   data->localpool = localpool;
48   if (localpool)
49     stringpool_init_empty(&data->spool);
50   data->keys = sat_calloc(1, sizeof(Repokey));
51   data->nkeys = 1;
52   data->schemata = sat_calloc(1, sizeof(Id));
53   data->schemadata = sat_calloc(1, sizeof(Id));
54   data->nschemata = 1;
55   data->schemadatalen = 1;
56   data->pagefd = -1;
57 }
58
59 void
60 repodata_free(Repodata *data)
61 {
62   int i;
63
64   sat_free(data->keys);
65
66   sat_free(data->schemata);
67   sat_free(data->schemadata);
68   sat_free(data->schematahash);
69
70   stringpool_free(&data->spool);
71   dirpool_free(&data->dirpool);
72
73   sat_free(data->mainschemaoffsets);
74   sat_free(data->incoredata);
75   sat_free(data->incoreoffset);
76   sat_free(data->verticaloffset);
77
78   sat_free(data->blob_store);
79   sat_free(data->pages);
80   sat_free(data->mapped);
81
82   sat_free(data->vincore);
83
84   if (data->attrs)
85     for (i = 0; i < data->end - data->start; i++)
86       sat_free(data->attrs[i]);
87   sat_free(data->attrs);
88   if (data->xattrs)
89     for (i = 0; i < data->nxattrs; i++)
90       sat_free(data->xattrs[i]);
91   sat_free(data->xattrs);
92
93   sat_free(data->attrdata);
94   sat_free(data->attriddata);
95   
96   if (data->pagefd != -1)
97     close(data->pagefd);
98 }
99
100
101 /***************************************************************
102  * key pool management
103  */
104
105 /* this is not so time critical that we need a hash, so we do a simple
106  * linear search */
107 Id
108 repodata_key2id(Repodata *data, Repokey *key, int create)
109 {
110   Id keyid;
111
112   for (keyid = 1; keyid < data->nkeys; keyid++)
113     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
114       {    
115         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
116           continue;
117         break;
118       }
119   if (keyid == data->nkeys)
120     {    
121       if (!create)
122         return 0;
123       /* allocate new key */
124       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
125       data->keys[data->nkeys++] = *key;
126       if (data->verticaloffset)
127         {
128           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
129           data->verticaloffset[data->nkeys - 1] = 0; 
130         }
131       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
132     }
133   return keyid;
134 }
135
136
137 /***************************************************************
138  * schema pool management
139  */
140
141 #define SCHEMATA_BLOCK 31
142 #define SCHEMATADATA_BLOCK 255
143
144 Id
145 repodata_schema2id(Repodata *data, Id *schema, int create)
146 {
147   int h, len, i; 
148   Id *sp, cid; 
149   Id *schematahash;
150
151   if ((schematahash = data->schematahash) == 0)
152     {
153       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
154       for (i = 0; i < data->nschemata; i++)
155         {
156           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
157             h = h * 7 + *sp++;
158           h &= 255;
159           schematahash[h] = i + 1;
160         }
161       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
162       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
163     }
164
165   for (sp = schema, len = 0, h = 0; *sp; len++)
166     h = h * 7 + *sp++;
167   h &= 255; 
168   len++;
169
170   cid = schematahash[h];
171   if (cid)
172     {    
173       cid--;
174       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
175         return cid;
176       /* cache conflict */
177       for (cid = 0; cid < data->nschemata; cid++)
178         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
179           return cid;
180     }
181   /* a new one */
182   if (!create)
183     return 0;
184   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
185   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
186   /* add schema */
187   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
188   data->schemata[data->nschemata] = data->schemadatalen;
189   data->schemadatalen += len;
190   schematahash[h] = data->nschemata + 1;
191 #if 0
192 fprintf(stderr, "schema2id: new schema\n");
193 #endif
194   return data->nschemata++; 
195 }
196
197 void
198 repodata_free_schemahash(Repodata *data)
199 {
200   data->schematahash = sat_free(data->schematahash);
201   /* shrink arrays */
202   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
203   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
204 }
205
206
207 /***************************************************************
208  * dir pool management
209  */
210
211 Id
212 repodata_str2dir(Repodata *data, const char *dir, int create)
213 {
214   Id id, parent;
215   const char *dire;
216
217   parent = 0;
218   while (*dir == '/' && dir[1] == '/')
219     dir++;
220   if (*dir == '/' && !dir[1])
221     return 1;
222   while (*dir)
223     {
224       dire = strchrnul(dir, '/');
225       if (data->localpool)
226         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
227       else
228         id = strn2id(data->repo->pool, dir, dire - dir, create);
229       if (!id)
230         return 0;
231       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
232       if (!parent)
233         return 0;
234       if (!*dire)
235         break;
236       dir = dire + 1;
237       while (*dir == '/')
238         dir++;
239     }
240   return parent;
241 }
242
243 const char *
244 repodata_dir2str(Repodata *data, Id did, const char *suf)
245 {
246   Pool *pool = data->repo->pool;
247   int l = 0;
248   Id parent, comp;
249   const char *comps;
250   char *p;
251
252   if (!did)
253     return suf ? suf : "";
254   parent = did;
255   while (parent)
256     {
257       comp = dirpool_compid(&data->dirpool, parent);
258       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
259       l += strlen(comps);
260       parent = dirpool_parent(&data->dirpool, parent);
261       if (parent)
262         l++;
263     }
264   if (suf)
265     l += strlen(suf) + 1;
266   p = pool_alloctmpspace(pool, l + 1) + l;
267   *p = 0;
268   if (suf)
269     {
270       p -= strlen(suf);
271       strcpy(p, suf);
272       *--p = '/';
273     }
274   parent = did;
275   while (parent)
276     {
277       comp = dirpool_compid(&data->dirpool, parent);
278       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
279       l = strlen(comps);
280       p -= l;
281       strncpy(p, comps, l);
282       parent = dirpool_parent(&data->dirpool, parent);
283       if (parent)
284         *--p = '/';
285     }
286   return p;
287 }
288
289
290 /***************************************************************
291  * data management
292  */
293
294 static inline unsigned char *
295 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
296 {
297   Id *keyp = data->schemadata + data->schemata[schema];
298   for (; *keyp; keyp++)
299     dp = data_skip_key(data, dp, data->keys + *keyp);
300   return dp;
301 }
302
303 unsigned char *
304 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
305 {
306   int nentries, schema;
307   switch(key->type)
308     {
309     case REPOKEY_TYPE_FIXARRAY:
310       dp = data_read_id(dp, &nentries);
311       if (!nentries)
312         return dp;
313       dp = data_read_id(dp, &schema);
314       while (nentries--)
315         dp = data_skip_schema(data, dp, schema);
316       return dp;
317     case REPOKEY_TYPE_FLEXARRAY:
318       dp = data_read_id(dp, &nentries);
319       while (nentries--)
320         {
321           dp = data_read_id(dp, &schema);
322           dp = data_skip_schema(data, dp, schema);
323         }
324       return dp;
325     default:
326       if (key->storage == KEY_STORAGE_INCORE)
327         dp = data_skip(dp, key->type);
328       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
329         {
330           dp = data_skip(dp, REPOKEY_TYPE_ID);
331           dp = data_skip(dp, REPOKEY_TYPE_ID);
332         }
333       return dp;
334     }
335 }
336
337 static unsigned char *
338 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
339 {
340   Id k;
341
342   if (!keyid)
343     return 0;
344   while ((k = *keyp++) != 0)
345     {
346       if (k == keyid)
347         return dp;
348       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
349         {
350           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
351           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
352           continue;
353         }
354       if (data->keys[k].storage != KEY_STORAGE_INCORE)
355         continue;
356       dp = data_skip_key(data, dp, data->keys + k);
357     }
358   return 0;
359 }
360
361 static unsigned char *
362 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
363 {
364   unsigned char *dp;
365   if (!len)
366     return 0;
367   if (off >= data->lastverticaloffset)
368     {
369       off -= data->lastverticaloffset;
370       if (off + len > data->vincorelen)
371         return 0;
372       return data->vincore + off;
373     }
374   if (off + len > key->size)
375     return 0;
376   /* we now have the offset, go into vertical */
377   off += data->verticaloffset[key - data->keys];
378   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
379   dp = repodata_load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
380   if (dp)
381     dp += off % BLOB_PAGESIZE;
382   return dp;
383 }
384
385 static inline unsigned char *
386 get_data(Repodata *data, Repokey *key, unsigned char **dpp)
387 {
388   unsigned char *dp = *dpp;
389
390   if (!dp)
391     return 0;
392   if (key->storage == KEY_STORAGE_INCORE)
393     {
394       /* hmm, this is a bit expensive */
395       *dpp = data_skip_key(data, dp, key);
396       return dp;
397     }
398   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
399     {
400       Id off, len;
401       dp = data_read_id(dp, &off);
402       dp = data_read_id(dp, &len);
403       *dpp = dp;
404       return get_vertical_data(data, key, off, len);
405     }
406   return 0;
407 }
408
409 static int
410 load_repodata(Repodata *data)
411 {
412   if (data->loadcallback)
413     {
414       data->loadcallback(data);
415       if (data->state == REPODATA_AVAILABLE)
416         return 1;
417     }
418   data->state = REPODATA_ERROR;
419   return 0;
420 }
421
422 static inline int
423 maybe_load_repodata(Repodata *data, Id keyname)
424 {
425   if (keyname && !repodata_precheck_keyname(data, keyname))
426     return 0;   /* do not bother... */
427   switch(data->state)
428     {
429     case REPODATA_STUB:
430       if (keyname)
431         {
432           int i;
433           for (i = 0; i < data->nkeys; i++)
434             if (keyname == data->keys[i].name)
435               break;
436           if (i == data->nkeys)
437             return 0;
438         }
439       return load_repodata(data);
440     case REPODATA_ERROR:
441       return 0;
442     case REPODATA_AVAILABLE:
443       return 1;
444     default:
445       data->state = REPODATA_ERROR;
446       return 0;
447     }
448 }
449
450 static inline unsigned char*
451 entry2data(Repodata *data, Id entry, Id *schemap)
452 {
453   unsigned char *dp = data->incoredata;
454   if (!dp)
455     return 0;
456   if (entry == REPOENTRY_META)  /* META */
457     dp += 1;
458   else if (entry == REPOENTRY_POS)      /* META */
459     {
460       Pool *pool = data->repo->pool;
461       if (data->repo != pool->pos.repo)
462         return 0;
463       if (data != data->repo->repodata + pool->pos.repodataid)
464         return 0;
465       *schemap = pool->pos.schema;
466       return data->incoredata + pool->pos.dp;
467     }
468   else
469     {
470       if (entry < data->start || entry >= data->end)
471         return 0;
472       dp += data->incoreoffset[entry - data->start];
473     }
474   return data_read_id(dp, schemap);
475 }
476
477 /************************************************************************
478  * data lookup
479  */
480
481 static inline Id
482 find_schema_key(Repodata *data, Id schema, Id keyname)
483 {
484   Id *keyp;
485   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
486     if (data->keys[*keyp].name == keyname)
487       return *keyp;
488   return 0;
489 }
490
491 static inline unsigned char *
492 find_key_data(Repodata *data, Id entry, Id keyname, Repokey **keyp)
493 {
494   unsigned char *dp, *ddp;
495   Id keyid, schema;
496   Repokey *key;
497
498   if (!maybe_load_repodata(data, keyname))
499     return 0;
500   dp = entry2data(data, entry, &schema);
501   if (!dp)
502     return 0;
503   keyid = find_schema_key(data, schema, keyname);
504   if (!keyid)
505     return 0;
506   key = data->keys + keyid;
507   *keyp = key;
508   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
509     return dp;
510   dp = forward_to_key(data, keyid, data->schemadata + data->schemata[schema], dp);
511   if (!dp)
512     return 0;
513   ddp = get_data(data, key, &dp);
514   return ddp;
515 }
516
517
518 Id
519 repodata_lookup_id(Repodata *data, Id entry, Id keyname)
520 {
521   unsigned char *dp;
522   Repokey *key;
523   Id id;
524
525   dp = find_key_data(data, entry, keyname, &key);
526   if (!dp)
527     return 0;
528   if (key->type == REPOKEY_TYPE_CONSTANTID)
529     return key->size;
530   if (key->type != REPOKEY_TYPE_ID)
531     return 0;
532   dp = data_read_id(dp, &id);
533   return id;
534 }
535
536 const char *
537 repodata_lookup_str(Repodata *data, Id entry, Id keyname)
538 {
539   unsigned char *dp;
540   Repokey *key;
541   Id id;
542
543   dp = find_key_data(data, entry, keyname, &key);
544   if (!dp)
545     return 0;
546   if (key->type == REPOKEY_TYPE_STR)
547     return (const char *)dp;
548   if (key->type == REPOKEY_TYPE_CONSTANTID)
549     return id2str(data->repo->pool, key->size);
550   if (key->type == REPOKEY_TYPE_ID)
551     dp = data_read_id(dp, &id);
552   else
553     return 0;
554   if (data->localpool)
555     return data->spool.stringspace + data->spool.strings[id];
556   return id2str(data->repo->pool, id);
557 }
558
559 int
560 repodata_lookup_num(Repodata *data, Id entry, Id keyname, unsigned int *value)
561 {
562   unsigned char *dp;
563   Repokey *key;
564   KeyValue kv;
565
566   *value = 0;
567   dp = find_key_data(data, entry, keyname, &key);
568   if (!dp)
569     return 0;
570   if (key->type == REPOKEY_TYPE_NUM
571       || key->type == REPOKEY_TYPE_U32
572       || key->type == REPOKEY_TYPE_CONSTANT)
573     {
574       dp = data_fetch(dp, &kv, key);
575       *value = kv.num;
576       return 1;
577     }
578   return 0;
579 }
580
581 int
582 repodata_lookup_void(Repodata *data, Id entry, Id keyname)
583 {
584   Id schema;
585   Id *keyp;
586   unsigned char *dp;
587
588   if (!maybe_load_repodata(data, keyname))
589     return 0;
590   dp = entry2data(data, entry, &schema);
591   if (!dp)
592     return 0;
593   /* can't use find_schema_key as we need to test the type */
594   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
595     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
596       return 1;
597   return 0;
598 }
599
600 const unsigned char *
601 repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyname, Id *typep)
602 {
603   unsigned char *dp;
604   Repokey *key;
605
606   dp = find_key_data(data, entry, keyname, &key);
607   if (!dp)
608     return 0;
609   *typep = key->type;
610   return dp;
611 }
612
613
614 /************************************************************************
615  * data search
616  */
617
618 struct subschema_data {
619   Solvable *s;
620   void *cbdata;
621   KeyValue *parent;
622 };
623
624 /* search in a specific entry */
625 void
626 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
627 {
628   Id schema;
629   Repokey *key;
630   Id k, keyid, *kp, *keyp;
631   unsigned char *dp, *ddp;
632   int onekey = 0;
633   int stop;
634   KeyValue kv;
635   Solvable *s;
636
637   if (!maybe_load_repodata(data, keyname))
638     return;
639   if (entry == REPOENTRY_SUBSCHEMA)
640     {
641       struct subschema_data *subd = cbdata;
642       cbdata = subd->cbdata;
643       s = subd->s;
644       schema = subd->parent->id;
645       dp = (unsigned char *)subd->parent->str;
646       kv.parent = subd->parent;
647     }
648   else
649     {
650       schema = 0;
651       dp = entry2data(data, entry, &schema);
652       if (!dp)
653         return;
654       s = data->repo->pool->solvables + entry;
655       kv.parent = 0;
656     }
657   keyp = data->schemadata + data->schemata[schema];
658   if (keyname)
659     {
660       /* search for a specific key */
661       for (kp = keyp; (k = *kp++) != 0; )
662         if (data->keys[k].name == keyname)
663           break;
664       if (k == 0)
665         return;
666       dp = forward_to_key(data, k, data->schemadata + data->schemata[schema], dp);
667       if (!dp)
668         return;
669       keyp = kp - 1;
670       onekey = 1;
671     }
672   while ((keyid = *keyp++) != 0)
673     {
674       stop = 0;
675       key = data->keys + keyid;
676       ddp = get_data(data, key, &dp);
677
678       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
679         {
680           struct subschema_data subd;
681           int nentries;
682           Id schema = 0;
683
684           subd.cbdata = cbdata;
685           subd.s = s;
686           subd.parent = &kv;
687           ddp = data_read_id(ddp, &nentries);
688           kv.num = nentries;
689           kv.entry = 0;
690           while (ddp && nentries > 0)
691             {
692               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
693                 ddp = data_read_id(ddp, &schema);
694               kv.id = schema;
695               kv.str = (char *)ddp;
696               stop = callback(cbdata, s, data, key, &kv);
697               if (stop > SEARCH_NEXT_KEY)
698                 return;
699               if (stop)
700                 break;
701               if (!keyname)
702                 repodata_search(data, REPOENTRY_SUBSCHEMA, 0, callback, &subd);
703               ddp = data_skip_schema(data, ddp, schema);
704               nentries--;
705               kv.entry++;
706             }
707           if (!nentries)
708             {
709               /* sentinel */
710               kv.eof = 1;
711               kv.str = (char *)ddp;
712               stop = callback(cbdata, s, data, key, &kv);
713               if (stop > SEARCH_NEXT_KEY)
714                 return;
715             }
716           if (onekey)
717             return;
718           continue;
719         }
720       kv.entry = 0;
721       do
722         {
723           ddp = data_fetch(ddp, &kv, key);
724           if (!ddp)
725             break;
726           stop = callback(cbdata, s, data, key, &kv);
727           kv.entry++;
728         }
729       while (!kv.eof && !stop);
730       if (onekey || stop > SEARCH_NEXT_KEY)
731         return;
732     }
733 }
734
735 void
736 repodata_setpos_kv(Repodata *data, KeyValue *kv)
737 {
738   Pool *pool = data->repo->pool;
739   if (!kv)
740     {
741       pool->pos.repo = 0;
742       pool->pos.repodataid = 0;
743       pool->pos.dp = 0;
744       pool->pos.schema = 0;
745     }
746   else
747     {
748       pool->pos.repo = 0;
749       pool->pos.repodataid = data - data->repo->repodata;
750       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
751       pool->pos.schema = kv->id;
752     }
753 }
754
755 /************************************************************************/
756
757 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
758   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
759   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
760   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
761   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
762   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
763   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
764   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
765   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
766   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
767   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
768   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
769   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
770   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
771 };
772
773 #if 1
774 static inline Id *
775 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
776 {
777   kv->id = keyname;
778   switch (keyname)
779     {
780     case SOLVABLE_NAME:
781       kv->eof = 1;
782       return &s->name;
783     case SOLVABLE_ARCH:
784       kv->eof = 1;
785       return &s->arch;
786     case SOLVABLE_EVR:
787       kv->eof = 1;
788       return &s->evr;
789     case SOLVABLE_VENDOR:
790       kv->eof = 1;
791       return &s->vendor;
792     case SOLVABLE_PROVIDES:
793       kv->eof = 0;
794       return s->provides ? s->repo->idarraydata + s->provides : 0;
795     case SOLVABLE_OBSOLETES:
796       kv->eof = 0;
797       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
798     case SOLVABLE_CONFLICTS:
799       kv->eof = 0;
800       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
801     case SOLVABLE_REQUIRES:
802       kv->eof = 0;
803       return s->requires ? s->repo->idarraydata + s->requires : 0;
804     case SOLVABLE_RECOMMENDS:
805       kv->eof = 0;
806       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
807     case SOLVABLE_SUPPLEMENTS:
808       kv->eof = 0;
809       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
810     case SOLVABLE_SUGGESTS:
811       kv->eof = 0;
812       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
813     case SOLVABLE_ENHANCES:
814       kv->eof = 0;
815       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
816     case RPM_RPMDBID:
817       kv->eof = 1;
818       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
819     default:
820       return 0;
821     }
822 }
823
824 void
825 datamatcher_init(Datamatcher *ma, Pool *pool, const char *match, int flags)
826 {
827   ma->pool = pool;
828   ma->match = (void *)match;
829   ma->flags = flags;
830   ma->error = 0;
831   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
832     {
833       ma->match = sat_calloc(1, sizeof(regex_t));
834       ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
835       if (ma->error)
836         {
837           sat_free(ma->match);
838           ma->match = (void *)match;
839           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
840         }
841     }
842 }
843
844 void
845 datamatcher_free(Datamatcher *ma)
846 {
847   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
848     {
849       regfree(ma->match);
850       ma->match = sat_free(ma->match);
851     }
852 }
853
854 int
855 datamatcher_match(Datamatcher *ma, Repodata *data, Repokey *key, KeyValue *kv)
856 {
857   switch (key->type)
858     {
859     case REPOKEY_TYPE_ID:
860     case REPOKEY_TYPE_IDARRAY:
861       if (data && data->localpool)
862         kv->str = stringpool_id2str(&data->spool, kv->id);
863       else
864         kv->str = id2str(ma->pool, kv->id);
865       break;
866     case REPOKEY_TYPE_STR:
867       break;
868     case REPOKEY_TYPE_DIRSTRARRAY:
869       if (!(ma->flags & SEARCH_FILES))
870         return 0;
871       /* Put the full filename into kv->str.  */
872       kv->str = repodata_dir2str(data, kv->id, kv->str);
873       /* And to compensate for that put the "empty" directory into
874          kv->id, so that later calls to repodata_dir2str on this data
875          come up with the same filename again.  */
876       kv->id = 0;
877       break;
878     default:
879       return 0;
880     }
881   /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
882      for the others we can't know if a colon separates a kind or not.  */
883   if ((ma->flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
884     {
885       const char *s = strchr(kv->str, ':');
886       if (s)
887         kv->str = s + 1;
888     }
889   switch ((ma->flags & SEARCH_STRINGMASK))
890     {
891     case SEARCH_SUBSTRING:
892       if (ma->flags & SEARCH_NOCASE)
893         {
894           if (!strcasestr(kv->str, (const char *)ma->match))
895             return 0;
896         }
897       else
898         {
899           if (!strstr(kv->str, (const char *)ma->match))
900             return 0;
901         }
902       break;
903     case SEARCH_STRING:
904       if (ma->flags & SEARCH_NOCASE)
905         {
906           if (strcasecmp((const char *)ma->match, kv->str))
907             return 0;
908         }
909       else
910         {
911           if (strcmp((const char *)ma->match, kv->str))
912             return 0;
913         }
914       break;
915     case SEARCH_GLOB:
916       if (fnmatch((const char *)ma->match, kv->str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
917         return 0;
918       break;
919     case SEARCH_REGEX:
920       if (regexec((const regex_t *)ma->match, kv->str, 0, NULL, 0))
921         return 0;
922       break;
923     default:
924       return 0;
925     }
926   return 1;
927 }
928
929 enum {
930   di_bye,
931
932   di_nextattr,
933   di_nextkey,
934   di_nextrepodata,
935   di_nextsolvable,
936   di_nextrepo,
937
938   di_enterrepo,
939   di_entersolvable,
940   di_enterrepodata,
941   di_enterkey,
942
943   di_nextarrayelement,
944   di_entersub,
945   di_leavesub,
946
947   di_nextsolvableattr,
948   di_nextsolvablekey,
949   di_entersolvablekey
950 };
951
952 void
953 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname, const char *match, int flags)
954 {
955   memset(di, 0, sizeof(*di));
956   di->repo = repo;
957   di->keyname = keyname;
958   di->entry = p;
959   di->pool = repo->pool;
960   if (p)
961     flags |= SEARCH_THISENTRY;
962   di->flags = flags;
963   if (repo)
964     di->repoid = -1;
965   if (match)
966     datamatcher_init(&di->matcher, di->pool, match, flags);
967   if (p == REPOENTRY_POS)
968     {
969       di->repo = di->pool->pos.repo;
970       di->data = di->repo->repodata + di->pool->pos.repodataid;
971       di->repoid = -1;
972       di->repodataid = -1;
973     }
974   di->state = di_enterrepo;
975 }
976
977 void
978 dataiterator_free(Dataiterator *di)
979 {
980   if (di->matcher.match)
981     datamatcher_free(&di->matcher);
982 }
983
984 int
985 dataiterator_step(Dataiterator *di)
986 {
987   Id schema;
988
989   for (;;)
990     {
991       switch (di->state)
992         {
993         case di_nextattr: di_nextattr:
994           di->kv.entry++;
995           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
996           if (di->kv.eof)
997             di->state = di_nextkey;
998           else
999             di->state = di_nextattr;
1000           break;
1001
1002         case di_nextkey: di_nextkey:
1003           if (!di->keyname)
1004             {
1005               if (*++di->keyp)
1006                 goto di_enterkey;
1007             }
1008           else if ((di->flags & SEARCH_SUB) != 0)
1009             {
1010               Id *keyp = di->keyp;
1011               for (keyp++; *keyp; keyp++)
1012                 if (di->data->keys[*keyp].name == di->keyname || 
1013                     di->data->keys[*keyp].type == REPOKEY_TYPE_FIXARRAY || 
1014                     di->data->keys[*keyp].type == REPOKEY_TYPE_FLEXARRAY)
1015                   break;
1016               if (*keyp && (di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp)) != 0)
1017                 {
1018                   di->keyp = keyp;
1019                   goto di_enterkey;
1020                 }
1021             }
1022
1023           if (di->kv.parent)
1024             goto di_leavesub;
1025           /* FALLTHROUGH */
1026
1027         case di_nextrepodata: di_nextrepodata:
1028           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1029               goto di_enterrepodata;
1030           /* FALLTHROUGH */
1031
1032         case di_nextsolvable:
1033           if (!(di->flags & SEARCH_THISENTRY))
1034             {
1035               if (di->entry < 0)
1036                 di->entry = di->repo->start;
1037               else
1038                 di->entry++;
1039               for (; di->entry < di->repo->end; di->entry++)
1040                 {
1041                   if (di->pool->solvables[di->entry].repo == di->repo)
1042                     goto di_entersolvable;
1043                 }
1044             }
1045           /* FALLTHROUGH */
1046
1047         case di_nextrepo:
1048           if (di->repoid >= 0)
1049             {
1050               di->repoid++;
1051               if (di->repoid < di->pool->nrepos)
1052                 {
1053                   di->repo = di->pool->repos[di->repoid];
1054                   goto di_enterrepo;
1055                 }
1056             }
1057
1058         /* FALLTHROUGH */
1059         case di_bye:
1060           di->state = di_bye;
1061           return 0;
1062
1063         case di_enterrepo: di_enterrepo:
1064           if (!(di->flags & SEARCH_THISENTRY))
1065             di->entry = di->repo->start;
1066           /* FALLTHROUGH */
1067
1068         case di_entersolvable: di_entersolvable:
1069           if (di->repodataid >= 0)
1070             {
1071               di->repodataid = 0;
1072               if (di->entry > 0 && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
1073                 {
1074                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1075                   di->data = 0;
1076                   goto di_entersolvablekey;
1077                 }
1078             }
1079
1080         case di_enterrepodata: di_enterrepodata:
1081           if (di->repodataid >= 0)
1082             di->data = di->repo->repodata + di->repodataid;
1083           if (!maybe_load_repodata(di->data, di->keyname))
1084             goto di_nextrepodata;
1085           di->dp = entry2data(di->data, di->entry, &schema);
1086           if (!di->dp)
1087             goto di_nextrepodata;
1088           di->keyp = di->data->schemadata + di->data->schemata[schema];
1089           if (di->keyname)
1090             {
1091               Id *keyp;
1092               if ((di->flags & SEARCH_SUB) != 0)
1093                 {
1094                   di->keyp--;
1095                   goto di_nextkey;
1096                 }
1097               for (keyp = di->keyp; *keyp; keyp++)
1098                 if (di->data->keys[*keyp].name == di->keyname)
1099                   break;
1100               if (!*keyp)
1101                 goto di_nextrepodata;
1102               di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1103               di->keyp = keyp;
1104               if (!di->dp)
1105                 goto di_nextrepodata;
1106             }
1107
1108         case di_enterkey: di_enterkey:
1109           di->kv.entry = -1;
1110           di->key = di->data->keys + *di->keyp;
1111           di->ddp = get_data(di->data, di->key, &di->dp);
1112           if (!di->ddp)
1113             goto di_nextkey;
1114           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1115             {
1116               di->ddp = data_read_id(di->ddp, &di->kv.num);
1117               di->kv.entry = -1;
1118               di->kv.eof = 0;
1119               goto di_nextarrayelement;
1120             }
1121           goto di_nextattr;
1122
1123         case di_nextarrayelement: di_nextarrayelement:
1124           di->kv.entry++;
1125           if (di->kv.entry)
1126             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1127           if (di->kv.entry == di->kv.num)
1128             {
1129               if (di->keyname && di->key->name != di->keyname)
1130                 goto di_nextkey;
1131               di->kv.str = (char *)di->ddp;
1132               di->kv.eof = 1;
1133               di->state = di_nextkey;
1134               break;
1135             }
1136           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1137             di->ddp = data_read_id(di->ddp, &di->kv.id);
1138           di->kv.str = (char *)di->ddp;
1139           if (di->keyname && di->key->name != di->keyname)
1140             goto di_entersub;
1141           if ((di->flags & SEARCH_SUB) != 0)
1142             di->state = di_entersub;
1143           else
1144             di->state = di_nextarrayelement;
1145           break;
1146
1147         case di_entersub: di_entersub:
1148           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1149             goto di_nextarrayelement;   /* sorry, full */
1150           di->parents[di->nparents].kv = di->kv;
1151           di->parents[di->nparents].dp = di->dp;
1152           di->parents[di->nparents].keyp = di->keyp;
1153           di->dp = (unsigned char *)di->kv.str;
1154           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1155           memset(&di->kv, 0, sizeof(di->kv));
1156           di->kv.parent = &di->parents[di->nparents].kv;
1157           di->nparents++;
1158           di->keyp--;
1159           goto di_nextkey;
1160           
1161         case di_leavesub: di_leavesub:
1162           di->nparents--;
1163           di->dp = di->parents[di->nparents].dp;
1164           di->kv = di->parents[di->nparents].kv;
1165           di->keyp = di->parents[di->nparents].keyp;
1166           di->key = di->data->keys + *di->keyp;
1167           di->ddp = (unsigned char *)di->kv.str;
1168           goto di_nextarrayelement;
1169
1170         /* special solvable attr handling follows */
1171
1172         case di_nextsolvableattr:
1173           di->kv.id = *di->idp++;
1174           di->kv.entry++;
1175           if (!*di->idp)
1176             {
1177               di->kv.eof = 1;
1178               di->state = di_nextsolvablekey;
1179             }
1180           break;
1181
1182         case di_nextsolvablekey: di_nextsolvablekey:
1183           if (di->keyname || di->key->name == RPM_RPMDBID)
1184             goto di_enterrepodata;
1185           di->key++;
1186           /* FALLTHROUGH */
1187
1188         case di_entersolvablekey: di_entersolvablekey:
1189           di->idp = solvabledata_fetch(di->pool->solvables + di->entry, &di->kv, di->key->name);
1190           if (!di->idp || !di->idp[0])
1191             goto di_nextsolvablekey;
1192           di->kv.id = di->idp[0];
1193           di->kv.num = di->idp[0];
1194           if (!di->kv.eof && !di->idp[1])
1195             di->kv.eof = 1;
1196           di->kv.entry = 0;
1197           if (di->kv.eof)
1198             di->state = di_nextsolvablekey;
1199           else
1200             di->state = di_nextsolvableattr;
1201           break;
1202         }
1203
1204       if (di->matcher.match)
1205         if (!datamatcher_match(&di->matcher, di->data, di->key, &di->kv))
1206           continue;
1207       /* found something! */
1208       return 1;
1209     }
1210 }
1211
1212 void
1213 dataiterator_setpos(Dataiterator *di)
1214 {
1215   di->pool->pos.repo = di->repo;
1216   di->pool->pos.repodataid = di->data - di->repo->repodata;
1217   di->pool->pos.schema = di->kv.id;
1218   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1219 }
1220
1221 void
1222 dataiterator_skip_attribute(Dataiterator *di)
1223 {
1224   if (di->state == di_nextsolvableattr)
1225     di->state = di_nextsolvablekey;
1226   else
1227     di->state = di_nextkey;
1228 }
1229
1230 void
1231 dataiterator_skip_solvable(Dataiterator *di)
1232 {
1233   di->state = di_nextsolvable;
1234 }
1235
1236 void
1237 dataiterator_skip_repo(Dataiterator *di)
1238 {
1239   di->state = di_nextrepo;
1240 }
1241
1242 void
1243 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1244 {
1245   di->repo = s->repo;
1246   di->repoid = -1;
1247   di->entry = s - di->pool->solvables;
1248   di->state = di_entersolvable;
1249 }
1250
1251 void
1252 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1253 {
1254   di->repo = repo;
1255   di->repoid = -1;
1256   di->state = di_enterrepo;
1257 }
1258
1259 #else
1260
1261 /************************************************************************
1262  * data search iterator
1263  */
1264
1265 static void
1266 dataiterator_newdata(Dataiterator *di)
1267 {
1268   Id keyname = di->keyname;
1269   Repodata *data = di->data;
1270   di->nextkeydp = 0;
1271
1272   if (data->state == REPODATA_STUB)
1273     {
1274       if (keyname)
1275         {
1276           int j;
1277           for (j = 1; j < data->nkeys; j++)
1278             if (keyname == data->keys[j].name)
1279               break;
1280           if (j == data->nkeys)
1281             return;
1282         }
1283       /* load it */
1284       if (data->loadcallback)
1285         data->loadcallback(data);
1286       else
1287         data->state = REPODATA_ERROR;
1288     }
1289   if (data->state == REPODATA_ERROR)
1290     return;
1291
1292   Id schema;
1293   unsigned char *dp = data->incoredata;
1294   if (!dp)
1295     return;
1296   if (di->solvid >= 0)
1297     dp += data->incoreoffset[di->solvid - data->start];
1298   dp = data_read_id(dp, &schema);
1299   Id *keyp = data->schemadata + data->schemata[schema];
1300   if (keyname)
1301     {
1302       Id k, *kp;
1303       /* search in a specific key */
1304       for (kp = keyp; (k = *kp++) != 0; )
1305         if (data->keys[k].name == keyname)
1306           break;
1307       if (k == 0)
1308         return;
1309       dp = forward_to_key(data, k, keyp, dp);
1310       if (!dp)
1311         return;
1312       keyp = kp - 1;
1313     }
1314   Id keyid = *keyp++;
1315   if (!keyid)
1316     return;
1317
1318   di->data = data;
1319   di->key = di->data->keys + keyid;
1320   di->keyp = keyp;
1321   di->dp = 0;
1322
1323   di->nextkeydp = dp;
1324   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1325   di->kv.eof = 0;
1326 }
1327
1328 void
1329 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
1330                   const char *match, int flags)
1331 {
1332   di->flags = flags;
1333   if (p > 0)
1334     {
1335       di->solvid = p;
1336       di->flags |= __SEARCH_ONESOLVABLE;
1337       di->data = repo->repodata - 1;
1338       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
1339         di->state = 0;
1340       else
1341         di->state = 1;
1342     }
1343   else
1344     {
1345       di->solvid = repo->start - 1;
1346       if (di->solvid < 0)
1347         {
1348           fprintf(stderr, "A repo contains the NULL solvable!\n");
1349           exit(1);
1350         }
1351       di->data = repo->repodata + repo->nrepodata - 1;
1352       di->state = 0;
1353     }
1354
1355   di->match = match;
1356   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1357     {
1358       if (di->match)
1359         {
1360           /* We feed multiple lines eventually (e.g. authors or descriptions),
1361              so set REG_NEWLINE. */
1362           di->regex_err =
1363             regcomp(&di->regex, di->match,
1364               REG_EXTENDED | REG_NOSUB | REG_NEWLINE
1365               | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1366 #if 0
1367           if (di->regex_err != 0)
1368             {
1369               fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
1370               fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
1371               exit(1);
1372             }
1373 #else
1374         }
1375       else
1376         {
1377           di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
1378           di->regex_err = 0;
1379 #endif
1380         }
1381     }
1382
1383   di->keyname = keyname;
1384   static Id zeroid = 0;
1385   di->keyp = &zeroid;
1386   di->kv.eof = 1;
1387   di->repo = repo;
1388   di->idp = 0;
1389   di->subkeyp = 0;
1390 }
1391
1392 /* FIXME factor and merge with repo_matchvalue */
1393 static int
1394 dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
1395 {
1396   KeyValue *kv = &di->kv;
1397   const char *match = vmatch;
1398   if ((flags & SEARCH_STRINGMASK) != 0)
1399     {
1400       switch (di->key->type)
1401         {
1402         case REPOKEY_TYPE_ID:
1403         case REPOKEY_TYPE_IDARRAY:
1404           if (di->data && di->data->localpool)
1405             kv->str = stringpool_id2str(&di->data->spool, kv->id);
1406           else
1407             kv->str = id2str(di->repo->pool, kv->id);
1408           break;
1409         case REPOKEY_TYPE_STR:
1410           break;
1411         case REPOKEY_TYPE_DIRSTRARRAY:
1412           if (!(flags & SEARCH_FILES))
1413             return 0;
1414           /* Put the full filename into kv->str.  */
1415           kv->str = repodata_dir2str(di->data, kv->id, kv->str);
1416           /* And to compensate for that put the "empty" directory into
1417              kv->id, so that later calls to repodata_dir2str on this data
1418              come up with the same filename again.  */
1419           kv->id = 0;
1420           break;
1421         default:
1422           return 0;
1423         }
1424       /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
1425          for the others we can't know if a colon separates a kind or not.  */
1426       if ((flags & SEARCH_SKIP_KIND)
1427           && di->key->storage == KEY_STORAGE_SOLVABLE)
1428         {
1429           const char *s = strchr(kv->str, ':');
1430           if (s)
1431             kv->str = s + 1;
1432         }
1433       switch ((flags & SEARCH_STRINGMASK))
1434         {
1435           case SEARCH_SUBSTRING:
1436             if (flags & SEARCH_NOCASE)
1437               {
1438                 if (!strcasestr(kv->str, match))
1439                   return 0;
1440               }
1441             else
1442               {
1443                 if (!strstr(kv->str, match))
1444                   return 0;
1445               }
1446             break;
1447           case SEARCH_STRING:
1448             if (flags & SEARCH_NOCASE)
1449               {
1450                 if (strcasecmp(match, kv->str))
1451                   return 0;
1452               }
1453             else
1454               {
1455                 if (strcmp(match, kv->str))
1456                   return 0;
1457               }
1458             break;
1459           case SEARCH_GLOB:
1460             if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1461               return 0;
1462             break;
1463           case SEARCH_REGEX:
1464             if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
1465               return 0;
1466             break;
1467           default:
1468             return 0;
1469         }
1470     }
1471   return 1;
1472 }
1473
1474 static int
1475 dataiterator_match_int(Dataiterator *di)
1476 {
1477   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1478     return dataiterator_match_int_real(di, di->flags, &di->regex);
1479   else
1480     return dataiterator_match_int_real(di, di->flags, di->match);
1481 }
1482
1483 int
1484 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1485 {
1486   return dataiterator_match_int_real(di, flags, vmatch);
1487 }
1488
1489 int
1490 dataiterator_step(Dataiterator *di)
1491 {
1492 restart:
1493   while (1)
1494     {
1495       if (di->state)
1496         {
1497           /* we're stepping through solvable data, 1 -> SOLVABLE_NAME... */
1498           if (di->idp)
1499             {
1500               /* we're stepping through an id array */
1501               Id *idp = di->idp;
1502               if (*idp)
1503                 {
1504                   di->kv.id = *idp;
1505                   di->idp++;
1506                   di->kv.eof = idp[1] ? 0 : 1;
1507                   goto weg2;
1508                 }
1509               else
1510                 di->idp = 0;
1511             }
1512           Solvable *s = di->repo->pool->solvables + di->solvid;
1513           int state = di->state;
1514           di->key = solvablekeys + state - 1;
1515           if (di->keyname)
1516             di->state = RPM_RPMDBID;
1517           else
1518             di->state++;
1519           if (state == 1)
1520             {
1521               di->data = 0;
1522               if (di->keyname)
1523                 state = di->keyname - 1;
1524             }
1525           switch (state + 1)
1526             {
1527               case SOLVABLE_NAME:
1528                 if (!s->name)
1529                   continue;
1530                 di->kv.id = s->name;
1531                 di->kv.eof = 1;
1532                 break;
1533               case SOLVABLE_ARCH:
1534                 if (!s->arch)
1535                   continue;
1536                 di->kv.id = s->arch;
1537                 di->kv.eof = 1;
1538                 break;
1539               case SOLVABLE_EVR:
1540                 if (!s->evr)
1541                   continue;
1542                 di->kv.id = s->evr;
1543                 di->kv.eof = 1;
1544                 break;
1545               case SOLVABLE_VENDOR:
1546                 if (!s->vendor)
1547                   continue;
1548                 di->kv.id = s->vendor;
1549                 di->kv.eof = 1;
1550                 break;
1551               case SOLVABLE_PROVIDES:
1552                 di->idp = s->provides
1553                     ? di->repo->idarraydata + s->provides : 0;
1554                 continue;
1555               case SOLVABLE_OBSOLETES:
1556                 di->idp = s->obsoletes
1557                     ? di->repo->idarraydata + s->obsoletes : 0;
1558                 continue;
1559               case SOLVABLE_CONFLICTS:
1560                 di->idp = s->conflicts
1561                     ? di->repo->idarraydata + s->conflicts : 0;
1562                 continue;
1563               case SOLVABLE_REQUIRES:
1564                 di->idp = s->requires
1565                     ? di->repo->idarraydata + s->requires : 0;
1566                 continue;
1567               case SOLVABLE_RECOMMENDS:
1568                 di->idp = s->recommends
1569                     ? di->repo->idarraydata + s->recommends : 0;
1570                 continue;
1571               case SOLVABLE_SUPPLEMENTS:
1572                 di->idp = s->supplements
1573                     ? di->repo->idarraydata + s->supplements : 0;
1574                 continue;
1575               case SOLVABLE_SUGGESTS:
1576                 di->idp = s->suggests
1577                     ? di->repo->idarraydata + s->suggests : 0;
1578                 continue;
1579               case SOLVABLE_ENHANCES:
1580                 di->idp = s->enhances
1581                     ? di->repo->idarraydata + s->enhances : 0;
1582                 continue;
1583               case RPM_RPMDBID:
1584                 if (!di->repo->rpmdbid)
1585                   continue;
1586                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
1587                 di->kv.eof = 1;
1588                 break;
1589               default:
1590                 di->data = di->repo->repodata - 1;
1591                 di->kv.eof = 1;
1592                 di->state = 0;
1593                 continue;
1594             }
1595         }
1596       else if (di->subkeyp)
1597         {
1598           Id keyid;
1599           if (!di->subnum)
1600             {
1601               /* Send end-of-substruct.  We are here only when we saw a
1602                  _COUNTED key one level up.  Since then we didn't increment
1603                  ->keyp, so it still can be found at keyp[-1].  */
1604               di->kv.eof = 2;
1605               di->key = di->data->keys + di->keyp[-1];
1606               di->subkeyp = 0;
1607             }
1608           else if (!(keyid = *di->subkeyp++))
1609             {
1610               /* Send end-of-element.  See above for keyp[-1].  */
1611               di->kv.eof = 1;
1612               di->key = di->data->keys + di->keyp[-1];
1613               if (di->subschema)
1614                 di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1615               else
1616                 {
1617                   di->dp = data_read_id(di->dp, &di->subschema);
1618                   di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1619                   di->subschema = 0;
1620                 }
1621               di->subnum--;
1622             }
1623           else
1624             {
1625               di->key = di->data->keys + keyid;
1626               di->dp = data_fetch(di->dp, &di->kv, di->key);
1627               if (!di->dp)
1628                 exit(1);
1629             }
1630         }
1631       else
1632         {
1633           if (di->kv.eof)
1634             di->dp = 0;
1635           else
1636             di->dp = data_fetch(di->dp, &di->kv, di->key);
1637
1638           while (!di->dp)
1639             {
1640               Id keyid;
1641               if (di->keyname || !(keyid = *di->keyp++))
1642                 {
1643                   while (1)
1644                     {
1645                       Repo *repo = di->repo;
1646                       Repodata *data = ++di->data;
1647                       if (data >= repo->repodata + repo->nrepodata)
1648                         {
1649                           if (di->flags & __SEARCH_ONESOLVABLE)
1650                             return 0;
1651                           if (di->solvid >= 0)
1652                             {
1653                               while (++di->solvid < repo->end)
1654                                 if (repo->pool->solvables[di->solvid].repo == repo)
1655                                   break;
1656                               if (di->solvid >= repo->end)
1657                                 {
1658                                   if (!(di->flags & SEARCH_EXTRA))
1659                                     goto skiprepo;
1660                                   goto skiprepo;
1661                                 }
1662                             }
1663                           else
1664                             {
1665                                 {
1666 skiprepo:;
1667                                   Pool *pool = di->repo->pool;
1668                                   if (!(di->flags & SEARCH_ALL_REPOS)
1669                                       || di->repo == pool->repos[pool->nrepos - 1])
1670                                     return 0;
1671                                   int i;
1672                                   for (i = 0; i < pool->nrepos; i++)
1673                                     if (di->repo == pool->repos[i])
1674                                       break;
1675                                   di->repo = pool->repos[i + 1];
1676                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
1677                                   continue;
1678                                 }
1679                             }
1680                           di->data = repo->repodata - 1;
1681                           if ((di->flags & SEARCH_NO_STORAGE_SOLVABLE))
1682                             continue;
1683                           static Id zeroid = 0;
1684                           di->keyp = &zeroid;
1685                           di->state = 1;
1686                           goto restart;
1687                         }
1688                       if ((di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
1689                         {
1690                           dataiterator_newdata(di);
1691                           if (di->nextkeydp)
1692                             break;
1693                         }
1694                     }
1695                 }
1696               else
1697                 {
1698                   di->key = di->data->keys + keyid;
1699                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1700                 }
1701               di->dp = data_fetch(di->dp, &di->kv, di->key);
1702             }
1703           if (di->key->type == REPOKEY_TYPE_FIXARRAY)
1704             {
1705               di->subnum = di->kv.num;
1706               di->subschema = di->kv.id;
1707               di->kv.eof = 0;
1708               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1709             }
1710           if (di->key->type == REPOKEY_TYPE_FLEXARRAY)
1711             {
1712               di->subnum = di->kv.num;
1713               di->kv.eof = 0;
1714               di->dp = data_read_id(di->dp, &di->subschema);
1715               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1716               di->subschema = 0;
1717             }
1718         }
1719 weg2:
1720       if (!di->match
1721           || dataiterator_match_int(di))
1722         break;
1723     }
1724   return 1;
1725 }
1726
1727 void
1728 dataiterator_skip_attribute(Dataiterator *di)
1729 {
1730   if (di->state)
1731     di->idp = 0;
1732   /* This will make the next _step call to retrieve the next field.  */
1733   di->kv.eof = 1;
1734 }
1735
1736 void
1737 dataiterator_skip_solvable(Dataiterator *di)
1738 {
1739   /* We're done with this field.  */
1740   di->kv.eof = 1;
1741   /* And with solvable data.  */
1742   di->state = 0;
1743   /* And with all keys for this repodata and thing. */
1744   static Id zeroid = 0;
1745   di->keyp = &zeroid;
1746   /* And with all repodatas for this thing.  */
1747   di->data = di->repo->repodata + di->repo->nrepodata - 1;
1748   /* Hence the next call to _step will retrieve the next thing.  */
1749 }
1750
1751 void
1752 dataiterator_skip_repo(Dataiterator *di)
1753 {
1754   dataiterator_skip_solvable(di);
1755   /* We're done with all solvables and all extra things for this repo.  */
1756   di->solvid = -1;
1757 }
1758
1759 void
1760 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1761 {
1762   di->repo = s->repo;
1763   /* Simulate us being done with the solvable before the requested one.  */
1764   dataiterator_skip_solvable(di);
1765   di->solvid = s - s->repo->pool->solvables;
1766   di->solvid--;
1767 }
1768
1769 void
1770 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1771 {
1772   di->repo = repo;
1773   dataiterator_skip_solvable(di);
1774   di->solvid = repo->start - 1;
1775 }
1776
1777 #endif
1778
1779 /************************************************************************
1780  * data modify functions
1781  */
1782
1783 /* extend repodata so that it includes solvables p */
1784 void
1785 repodata_extend(Repodata *data, Id p)
1786 {
1787   if (data->start == data->end)
1788     data->start = data->end = p;
1789   if (p >= data->end)
1790     {
1791       int old = data->end - data->start;
1792       int new = p - data->end + 1;
1793       if (data->attrs)
1794         {
1795           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
1796           memset(data->attrs + old, 0, new * sizeof(Id));
1797         }
1798       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1799       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1800       data->end = p + 1;
1801     }
1802   if (p < data->start)
1803     {
1804       int old = data->end - data->start;
1805       int new = data->start - p;
1806       if (data->attrs)
1807         {
1808           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
1809           memmove(data->attrs + new, data->attrs, old * sizeof(Id));
1810           memset(data->attrs, 0, new * sizeof(Id));
1811         }
1812       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1813       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1814       memset(data->incoreoffset, 0, new * sizeof(Id));
1815       data->start = p;
1816     }
1817 }
1818
1819 void
1820 repodata_extend_block(Repodata *data, Id start, Id num)
1821 {
1822   if (!num)
1823     return;
1824   if (!data->incoreoffset)
1825     {
1826       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1827       data->start = start;
1828       data->end = start + num;
1829       return;
1830     }
1831   repodata_extend(data, start);
1832   if (num > 1)
1833     repodata_extend(data, start + num - 1);
1834 }
1835
1836 /**********************************************************************/
1837
1838 #define REPODATA_ATTRS_BLOCK 63
1839 #define REPODATA_ATTRDATA_BLOCK 1023
1840 #define REPODATA_ATTRIDDATA_BLOCK 63
1841
1842
1843 Id
1844 repodata_new_handle(Repodata *data)
1845 {
1846   if (!data->nxattrs)
1847     {
1848       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1849       data->nxattrs = 2;
1850     }
1851   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1852   data->xattrs[data->nxattrs] = 0;
1853   return -(data->nxattrs++);
1854 }
1855
1856 static inline Id **
1857 repodata_get_attrp(Repodata *data, Id handle)
1858 {
1859   if (handle == REPOENTRY_META)
1860     {
1861       if (!data->xattrs)
1862         {
1863           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1864           data->nxattrs = 2;
1865         }
1866     }
1867   if (handle < 0)
1868     return data->xattrs - handle;
1869   if (handle < data->start || handle >= data->end)
1870     repodata_extend(data, handle);
1871   if (!data->attrs)
1872     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1873   return data->attrs + (handle - data->start);
1874 }
1875
1876 static void
1877 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1878 {
1879   Id *pp;
1880   Id *ap, **app;
1881   int i;
1882
1883   app = repodata_get_attrp(data, handle);
1884   ap = *app;
1885   i = 0;
1886   if (ap)
1887     {
1888       for (pp = ap; *pp; pp += 2)
1889         /* Determine equality based on the name only, allows us to change
1890            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1891         if (data->keys[*pp].name == data->keys[keyid].name)
1892           break;
1893       if (*pp)
1894         {
1895           if (overwrite)
1896             {
1897               pp[0] = keyid;
1898               pp[1] = val;
1899             }
1900           return;
1901         }
1902       i = pp - ap;
1903     }
1904   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1905   *app = ap;
1906   pp = ap + i;
1907   *pp++ = keyid;
1908   *pp++ = val;
1909   *pp = 0;
1910 }
1911
1912
1913 void
1914 repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
1915 {
1916   Id keyid;
1917
1918   keyid = repodata_key2id(data, key, 1);
1919   repodata_insert_keyid(data, handle, keyid, val, 1);
1920 }
1921
1922 void
1923 repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
1924 {
1925   Repokey key;
1926   key.name = keyname;
1927   key.type = REPOKEY_TYPE_ID;
1928   key.size = 0;
1929   key.storage = KEY_STORAGE_INCORE;
1930   repodata_set(data, handle, &key, id);
1931 }
1932
1933 void
1934 repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
1935 {
1936   Repokey key;
1937   key.name = keyname;
1938   key.type = REPOKEY_TYPE_NUM;
1939   key.size = 0;
1940   key.storage = KEY_STORAGE_INCORE;
1941   repodata_set(data, handle, &key, (Id)num);
1942 }
1943
1944 void
1945 repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
1946 {
1947   Repokey key;
1948   Id id;
1949   if (data->localpool)
1950     id = stringpool_str2id(&data->spool, str, 1);
1951   else
1952     id = str2id(data->repo->pool, str, 1);
1953   key.name = keyname;
1954   key.type = REPOKEY_TYPE_ID;
1955   key.size = 0;
1956   key.storage = KEY_STORAGE_INCORE;
1957   repodata_set(data, handle, &key, id);
1958 }
1959
1960 void
1961 repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
1962 {
1963   Repokey key;
1964   key.name = keyname;
1965   key.type = REPOKEY_TYPE_CONSTANT;
1966   key.size = constant;
1967   key.storage = KEY_STORAGE_INCORE;
1968   repodata_set(data, handle, &key, 0);
1969 }
1970
1971 void
1972 repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
1973 {
1974   Repokey key;
1975   key.name = keyname;
1976   key.type = REPOKEY_TYPE_CONSTANTID;
1977   key.size = id;
1978   key.storage = KEY_STORAGE_INCORE;
1979   repodata_set(data, handle, &key, 0);
1980 }
1981
1982 void
1983 repodata_set_void(Repodata *data, Id handle, Id keyname)
1984 {
1985   Repokey key;
1986   key.name = keyname;
1987   key.type = REPOKEY_TYPE_VOID;
1988   key.size = 0;
1989   key.storage = KEY_STORAGE_INCORE;
1990   repodata_set(data, handle, &key, 0);
1991 }
1992
1993 void
1994 repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
1995 {
1996   Repokey key;
1997   int l;
1998
1999   l = strlen(str) + 1;
2000   key.name = keyname;
2001   key.type = REPOKEY_TYPE_STR;
2002   key.size = 0;
2003   key.storage = KEY_STORAGE_INCORE;
2004   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2005   memcpy(data->attrdata + data->attrdatalen, str, l);
2006   repodata_set(data, handle, &key, data->attrdatalen);
2007   data->attrdatalen += l;
2008 }
2009
2010 static void
2011 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2012 {
2013   int oldsize;
2014   Id *ida, *pp, **ppp;
2015
2016   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2017     {
2018       /* great! just append the new data */
2019       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2020       data->attriddatalen--;    /* overwrite terminating 0  */
2021       data->lastdatalen += entrysize;
2022       return;
2023     }
2024   ppp = repodata_get_attrp(data, handle);
2025   pp = *ppp;
2026   if (pp)
2027     for (; *pp; pp += 2)
2028       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
2029         break;
2030   if (!pp || !*pp)
2031     {
2032       /* not found. allocate new key */
2033       Repokey key;
2034       key.name = keyname;
2035       key.type = keytype;
2036       key.size = 0;
2037       key.storage = KEY_STORAGE_INCORE;
2038       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2039       repodata_set(data, handle, &key, data->attriddatalen);
2040       data->lasthandle = 0;     /* next time... */
2041       return;
2042     }
2043   oldsize = 0;
2044   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2045     oldsize += entrysize;
2046   if (ida + 1 == data->attriddata + data->attriddatalen)
2047     {
2048       /* this was the last entry, just append it */
2049       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2050       data->attriddatalen--;    /* overwrite terminating 0  */
2051     }
2052   else
2053     {
2054       /* too bad. move to back. */
2055       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2056       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2057       pp[1] = data->attriddatalen;
2058       data->attriddatalen += oldsize;
2059     }
2060   data->lasthandle = handle;
2061   data->lastkey = *pp;
2062   data->lastdatalen = data->attriddatalen + entrysize + 1;
2063 }
2064
2065 static inline int
2066 checksumtype2len(Id type)
2067 {
2068   switch (type)
2069     {
2070     case REPOKEY_TYPE_MD5:
2071       return SIZEOF_MD5;
2072     case REPOKEY_TYPE_SHA1:
2073       return SIZEOF_SHA1;
2074     case REPOKEY_TYPE_SHA256:
2075       return SIZEOF_SHA256;
2076     default:
2077       return 0;
2078     }
2079 }
2080
2081 void
2082 repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
2083                       const unsigned char *str)
2084 {
2085   Repokey key;
2086   int l = checksumtype2len(type);
2087
2088   if (!l)
2089     return;
2090   key.name = keyname;
2091   key.type = type;
2092   key.size = 0;
2093   key.storage = KEY_STORAGE_INCORE;
2094   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2095   memcpy(data->attrdata + data->attrdatalen, str, l);
2096   repodata_set(data, handle, &key, data->attrdatalen);
2097   data->attrdatalen += l;
2098 }
2099
2100 static int
2101 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2102 {
2103   int i;
2104   for (i = 0; i < buflen; i++)
2105     {
2106 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
2107                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
2108                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
2109                 : -1)
2110       int v = c2h(*str);
2111       str++;
2112       if (v < 0)
2113         return 0;
2114       buf[i] = v;
2115       v = c2h(*str);
2116       str++;
2117       if (v < 0)
2118         return 0;
2119       buf[i] = (buf[i] << 4) | v;
2120 #undef c2h
2121     }
2122   return buflen;
2123 }
2124
2125 void
2126 repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
2127                       const char *str)
2128 {
2129   unsigned char buf[64];
2130   int l = checksumtype2len(type);
2131
2132   if (!l)
2133     return;
2134   if (hexstr2bytes(buf, str, l) != l)
2135     {
2136       fprintf(stderr, "Invalid hex character in '%s'\n", str);
2137       return;
2138     }
2139   repodata_set_bin_checksum(data, handle, keyname, type, buf);
2140 }
2141
2142 const char *
2143 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2144 {
2145   int i, l;
2146   char *str, *s;
2147
2148   l = checksumtype2len(type);
2149   if (!l)
2150     return "";
2151   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2152   for (i = 0; i < l; i++)
2153     {
2154       unsigned char v = buf[i];
2155       unsigned char w = v >> 4;
2156       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2157       w = v & 15;
2158       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2159     }
2160   *s = 0;
2161   return str;
2162 }
2163
2164 Id
2165 repodata_globalize_id(Repodata *data, Id id)
2166
2167   if (!data || !data->localpool)
2168     return id;
2169   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
2170 }
2171
2172 void
2173 repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
2174 {
2175   assert(dir);
2176 #if 0
2177 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
2178 #endif
2179   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2180   data->attriddata[data->attriddatalen++] = dir;
2181   data->attriddata[data->attriddatalen++] = num;
2182   data->attriddata[data->attriddatalen++] = num2;
2183   data->attriddata[data->attriddatalen++] = 0;
2184 }
2185
2186 void
2187 repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
2188 {
2189   Id stroff;
2190   int l;
2191
2192   assert(dir);
2193   l = strlen(str) + 1;
2194   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2195   memcpy(data->attrdata + data->attrdatalen, str, l);
2196   stroff = data->attrdatalen;
2197   data->attrdatalen += l;
2198
2199 #if 0
2200 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
2201 #endif
2202   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2203   data->attriddata[data->attriddatalen++] = dir;
2204   data->attriddata[data->attriddatalen++] = stroff;
2205   data->attriddata[data->attriddatalen++] = 0;
2206 }
2207
2208 void
2209 repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
2210 {
2211 #if 0
2212 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
2213 #endif
2214   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
2215   data->attriddata[data->attriddatalen++] = id;
2216   data->attriddata[data->attriddatalen++] = 0;
2217 }
2218
2219 void
2220 repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
2221                            const char *str)
2222 {
2223   Id id;
2224   if (data->localpool)
2225     id = stringpool_str2id(&data->spool, str, 1);
2226   else
2227     id = str2id(data->repo->pool, str, 1);
2228   repodata_add_idarray(data, handle, keyname, id);
2229 }
2230
2231 void
2232 repodata_add_fixarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2233 {
2234   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2235   data->attriddata[data->attriddatalen++] = ghandle;
2236   data->attriddata[data->attriddatalen++] = 0;
2237 }
2238
2239 void
2240 repodata_add_flexarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2241 {
2242   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2243   data->attriddata[data->attriddatalen++] = ghandle;
2244   data->attriddata[data->attriddatalen++] = 0;
2245 }
2246
2247 void
2248 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2249 {
2250   Id *keyp;
2251   if (dest == src || !(keyp = data->attrs[src]))
2252     return;
2253   for (; *keyp; keyp += 2)
2254     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2255 }
2256
2257
2258
2259
2260 /**********************************************************************/
2261
2262 /* unify with repo_write! */
2263
2264 #define EXTDATA_BLOCK 1023
2265
2266 struct extdata {
2267   unsigned char *buf;
2268   int len;
2269 };
2270
2271 static void
2272 data_addid(struct extdata *xd, Id x)
2273 {
2274   unsigned char *dp;
2275   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2276   dp = xd->buf + xd->len;
2277
2278   if (x >= (1 << 14))
2279     {
2280       if (x >= (1 << 28))
2281         *dp++ = (x >> 28) | 128;
2282       if (x >= (1 << 21))
2283         *dp++ = (x >> 21) | 128;
2284       *dp++ = (x >> 14) | 128;
2285     }
2286   if (x >= (1 << 7))
2287     *dp++ = (x >> 7) | 128;
2288   *dp++ = x & 127;
2289   xd->len = dp - xd->buf;
2290 }
2291
2292 static void
2293 data_addideof(struct extdata *xd, Id x, int eof)
2294 {
2295   if (x >= 64)
2296     x = (x & 63) | ((x & ~63) << 1);
2297   data_addid(xd, (eof ? x: x | 64));
2298 }
2299
2300 static void
2301 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2302 {
2303   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2304   memcpy(xd->buf + xd->len, blob, len);
2305   xd->len += len;
2306 }
2307
2308 /*********************************/
2309
2310 static void
2311 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2312                        struct extdata *newvincore,
2313                        Id *schema,
2314                        Repokey *key, Id val)
2315 {
2316   /* Otherwise we have a new value.  Parse it into the internal
2317      form.  */
2318   Id *ida;
2319   struct extdata *xd;
2320   unsigned int oldvincorelen = 0;
2321   Id schemaid, *sp;
2322
2323   xd = newincore;
2324   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2325     {
2326       xd = newvincore;
2327       oldvincorelen = xd->len;
2328     }
2329   switch (key->type)
2330     {
2331     case REPOKEY_TYPE_VOID:
2332     case REPOKEY_TYPE_CONSTANT:
2333     case REPOKEY_TYPE_CONSTANTID:
2334       break;
2335     case REPOKEY_TYPE_STR:
2336       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2337       break;
2338     case REPOKEY_TYPE_MD5:
2339       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2340       break;
2341     case REPOKEY_TYPE_SHA1:
2342       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2343       break;
2344     case REPOKEY_TYPE_SHA256:
2345       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2346       break;
2347     case REPOKEY_TYPE_ID:
2348     case REPOKEY_TYPE_NUM:
2349     case REPOKEY_TYPE_DIR:
2350       data_addid(xd, val);
2351       break;
2352     case REPOKEY_TYPE_IDARRAY:
2353       for (ida = data->attriddata + val; *ida; ida++)
2354         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2355       break;
2356     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2357       for (ida = data->attriddata + val; *ida; ida += 3)
2358         {
2359           data_addid(xd, ida[0]);
2360           data_addid(xd, ida[1]);
2361           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2362         }
2363       break;
2364     case REPOKEY_TYPE_DIRSTRARRAY:
2365       for (ida = data->attriddata + val; *ida; ida += 2)
2366         {
2367           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2368           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2369         }
2370       break;
2371     case REPOKEY_TYPE_FIXARRAY:
2372       {
2373         int num = 0;
2374         schemaid = 0;
2375         for (ida = data->attriddata + val; *ida; ida++)
2376           {
2377 #if 0
2378             fprintf(stderr, "serialize struct %d\n", *ida);
2379 #endif
2380             sp = schema;
2381             Id *kp = data->xattrs[-*ida];
2382             if (!kp)
2383               continue;
2384             num++;
2385             for (;*kp; kp += 2)
2386               {
2387 #if 0
2388                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2389 #endif
2390                 *sp++ = *kp;
2391               }
2392             *sp = 0;
2393             if (!schemaid)
2394               schemaid = repodata_schema2id(data, schema, 1);
2395             else if (schemaid != repodata_schema2id(data, schema, 0))
2396               {
2397                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
2398                 exit(1);
2399               }
2400 #if 0
2401             fprintf(stderr, "  schema %d\n", schemaid);
2402 #endif
2403           }
2404         if (!num)
2405           break;
2406         data_addid(xd, num);
2407         data_addid(xd, schemaid);
2408         for (ida = data->attriddata + val; *ida; ida++)
2409           {
2410             Id *kp = data->xattrs[-*ida];
2411             if (!kp)
2412               continue;
2413             for (;*kp; kp += 2)
2414               {
2415                 repodata_serialize_key(data, newincore, newvincore,
2416                                        schema, data->keys + *kp, kp[1]);
2417               }
2418           }
2419         break;
2420       }
2421     case REPOKEY_TYPE_FLEXARRAY:
2422       {
2423         int num = 0;
2424         for (ida = data->attriddata + val; *ida; ida++)
2425           num++;
2426         data_addid(xd, num);
2427         for (ida = data->attriddata + val; *ida; ida++)
2428           {
2429             Id *kp = data->xattrs[-*ida];
2430             if (!kp)
2431               {
2432                 data_addid(xd, 0);      /* XXX */
2433                 continue;
2434               }
2435             sp = schema;
2436             for (;*kp; kp += 2)
2437               *sp++ = *kp;
2438             *sp = 0;
2439             schemaid = repodata_schema2id(data, schema, 1);
2440             data_addid(xd, schemaid);
2441             kp = data->xattrs[-*ida];
2442             for (;*kp; kp += 2)
2443               {
2444                 repodata_serialize_key(data, newincore, newvincore,
2445                                        schema, data->keys + *kp, kp[1]);
2446               }
2447           }
2448         break;
2449       }
2450     default:
2451       fprintf(stderr, "don't know how to handle type %d\n", key->type);
2452       exit(1);
2453     }
2454   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2455     {
2456       /* put offset/len in incore */
2457       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2458       oldvincorelen = xd->len - oldvincorelen;
2459       data_addid(newincore, oldvincorelen);
2460     }
2461 }
2462
2463 void
2464 repodata_internalize(Repodata *data)
2465 {
2466   Repokey *key, solvkey;
2467   Id entry, nentry;
2468   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2469   unsigned char *dp, *ndp;
2470   int newschema, oldcount;
2471   struct extdata newincore;
2472   struct extdata newvincore;
2473   Id solvkeyid;
2474
2475   if (!data->attrs && !data->xattrs)
2476     return;
2477
2478   newvincore.buf = data->vincore;
2479   newvincore.len = data->vincorelen;
2480
2481   /* find the solvables key, create if needed */
2482   memset(&solvkey, 0, sizeof(solvkey));
2483   solvkey.name = REPOSITORY_SOLVABLES;
2484   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2485   solvkey.size = 0;
2486   solvkey.storage = KEY_STORAGE_INCORE;
2487   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2488
2489   schema = sat_malloc2(data->nkeys, sizeof(Id));
2490   seen = sat_malloc2(data->nkeys, sizeof(Id));
2491
2492   /* Merge the data already existing (in data->schemata, ->incoredata and
2493      friends) with the new attributes in data->attrs[].  */
2494   nentry = data->end - data->start;
2495   memset(&newincore, 0, sizeof(newincore));
2496   data_addid(&newincore, 0);    /* start data at offset 1 */
2497
2498   data->mainschema = 0;
2499   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2500
2501   /* join entry data */
2502   /* we start with the meta data, entry -1 */
2503   for (entry = -1; entry < nentry; entry++)
2504     {
2505       memset(seen, 0, data->nkeys * sizeof(Id));
2506       oldschema = 0;
2507       dp = data->incoredata;
2508       if (dp)
2509         {
2510           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2511           dp = data_read_id(dp, &oldschema);
2512         }
2513 #if 0
2514 fprintf(stderr, "oldschema %d\n", oldschema);
2515 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2516 fprintf(stderr, "schemadata %p\n", data->schemadata);
2517 #endif
2518       /* seen: -1: old data  0: skipped  >0: id + 1 */
2519       newschema = 0;
2520       oldcount = 0;
2521       sp = schema;
2522       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2523         {
2524           if (seen[*keyp])
2525             {
2526               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2527               exit(1);
2528             }
2529           seen[*keyp] = -1;
2530           *sp++ = *keyp;
2531           oldcount++;
2532         }
2533       if (entry >= 0)
2534         keyp = data->attrs ? data->attrs[entry] : 0;
2535       else
2536         {
2537           /* strip solvables key */
2538           *sp = 0;
2539           for (sp = keyp = schema; *sp; sp++)
2540             if (*sp != solvkeyid)
2541               *keyp++ = *sp;
2542             else
2543               oldcount--;
2544           sp = keyp;
2545           seen[solvkeyid] = 0;
2546           keyp = data->xattrs ? data->xattrs[1] : 0;
2547         }
2548       if (keyp)
2549         for (; *keyp; keyp += 2)
2550           {
2551             if (!seen[*keyp])
2552               {
2553                 newschema = 1;
2554                 *sp++ = *keyp;
2555               }
2556             seen[*keyp] = keyp[1] + 1;
2557           }
2558       if (entry < 0 && data->end != data->start)
2559         {
2560           *sp++ = solvkeyid;
2561           newschema = 1;
2562         }
2563       *sp = 0;
2564       if (newschema)
2565         /* Ideally we'd like to sort the new schema here, to ensure
2566            schema equality independend of the ordering.  We can't do that
2567            yet.  For once see below (old ids need to come before new ids).
2568            An additional difficulty is that we also need to move
2569            the values with the keys.  */
2570         schemaid = repodata_schema2id(data, schema, 1);
2571       else
2572         schemaid = oldschema;
2573
2574
2575       /* Now create data blob.  We walk through the (possibly new) schema
2576          and either copy over old data, or insert the new.  */
2577       /* XXX Here we rely on the fact that the (new) schema has the form
2578          o1 o2 o3 o4 ... | n1 n2 n3 ...
2579          (oX being the old keyids (possibly overwritten), and nX being
2580           the new keyids).  This rules out sorting the keyids in order
2581          to ensure a small schema count.  */
2582       if (entry >= 0)
2583         data->incoreoffset[entry] = newincore.len;
2584       data_addid(&newincore, schemaid);
2585       if (entry == -1)
2586         {
2587           data->mainschema = schemaid;
2588           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2589         }
2590       keypstart = data->schemadata + data->schemata[schemaid];
2591       for (keyp = keypstart; *keyp; keyp++)
2592         {
2593           if (entry == -1)
2594             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2595           if (*keyp == solvkeyid)
2596             {
2597               /* add flexarray entry count */
2598               data_addid(&newincore, data->end - data->start);
2599               break;
2600             }
2601           key = data->keys + *keyp;
2602 #if 0
2603           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2604 #endif
2605           ndp = dp;
2606           if (oldcount)
2607             {
2608               /* Skip the data associated with this old key.  */
2609               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2610                 {
2611                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2612                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2613                 }
2614               else if (key->storage == KEY_STORAGE_INCORE)
2615                 ndp = data_skip_key(data, dp, key);
2616               oldcount--;
2617             }
2618           if (seen[*keyp] == -1)
2619             {
2620               /* If this key was an old one _and_ was not overwritten with
2621                  a different value copy over the old value (we skipped it
2622                  above).  */
2623               if (dp != ndp)
2624                 data_addblob(&newincore, dp, ndp - dp);
2625               seen[*keyp] = 0;
2626             }
2627           else if (seen[*keyp])
2628             {
2629               /* Otherwise we have a new value.  Parse it into the internal
2630                  form.  */
2631               repodata_serialize_key(data, &newincore, &newvincore,
2632                                      schema, key, seen[*keyp] - 1);
2633             }
2634           dp = ndp;
2635         }
2636       if (entry >= 0 && data->attrs && data->attrs[entry])
2637         data->attrs[entry] = sat_free(data->attrs[entry]);
2638     }
2639   /* free all xattrs */
2640   for (entry = 0; entry < data->nxattrs; entry++)
2641     if (data->xattrs[entry])
2642       sat_free(data->xattrs[entry]);
2643   data->xattrs = sat_free(data->xattrs);
2644   data->nxattrs = 0;
2645
2646   data->lasthandle = 0;
2647   data->lastkey = 0;
2648   data->lastdatalen = 0;
2649   sat_free(schema);
2650   sat_free(seen);
2651   repodata_free_schemahash(data);
2652
2653   sat_free(data->incoredata);
2654   data->incoredata = newincore.buf;
2655   data->incoredatalen = newincore.len;
2656   data->incoredatafree = 0;
2657   
2658   sat_free(data->vincore);
2659   data->vincore = newvincore.buf;
2660   data->vincorelen = newvincore.len;
2661
2662   data->attrs = sat_free(data->attrs);
2663   data->attrdata = sat_free(data->attrdata);
2664   data->attriddata = sat_free(data->attriddata);
2665   data->attrdatalen = 0;
2666   data->attriddatalen = 0;
2667 }
2668
2669 void
2670 repodata_disable_paging(Repodata *data)
2671 {
2672   if (maybe_load_repodata(data, 0)
2673       && data->num_pages)
2674     repodata_load_page_range(data, 0, data->num_pages - 1);
2675 }
2676
2677 /*
2678 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2679 */