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