- move search position into pool
[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   di->state = di_enterrepo;
968 }
969
970 void
971 dataiterator_free(Dataiterator *di)
972 {
973   if (di->matcher.match)
974     datamatcher_free(&di->matcher);
975 }
976
977 int
978 dataiterator_step(Dataiterator *di)
979 {
980   Id schema;
981
982   for (;;)
983     {
984       switch (di->state)
985         {
986         case di_nextattr: di_nextattr:
987           di->kv.entry++;
988           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
989           if (di->kv.eof)
990             di->state = di_nextkey;
991           else
992             di->state = di_nextattr;
993           break;
994
995         case di_nextkey: di_nextkey:
996           if (!di->keyname)
997             {
998               if (*++di->keyp)
999                 goto di_enterkey;
1000             }
1001           else if ((di->flags & SEARCH_SUB) != 0)
1002             {
1003               Id *keyp = di->keyp;
1004               for (keyp++; *keyp; keyp++)
1005                 if (di->data->keys[*keyp].name == di->keyname || 
1006                     di->data->keys[*keyp].type == REPOKEY_TYPE_FIXARRAY || 
1007                     di->data->keys[*keyp].type == REPOKEY_TYPE_FLEXARRAY)
1008                   break;
1009               if (*keyp && (di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp)) != 0)
1010                 {
1011                   di->keyp = keyp;
1012                   goto di_enterkey;
1013                 }
1014             }
1015
1016           if (di->kv.parent)
1017             goto di_leavesub;
1018           /* FALLTHROUGH */
1019
1020         case di_nextrepodata: di_nextrepodata:
1021           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1022               goto di_enterrepodata;
1023           /* FALLTHROUGH */
1024
1025         case di_nextsolvable:
1026           if (!(di->flags & SEARCH_THISENTRY))
1027             {
1028               if (di->entry < 0)
1029                 di->entry = di->repo->start;
1030               else
1031                 di->entry++;
1032               for (; di->entry < di->repo->end; di->entry++)
1033                 {
1034                   if (di->pool->solvables[di->entry].repo == di->repo)
1035                     goto di_entersolvable;
1036                 }
1037             }
1038           /* FALLTHROUGH */
1039
1040         case di_nextrepo:
1041           if (di->repoid >= 0)
1042             {
1043               di->repoid++;
1044               if (di->repoid < di->pool->nrepos)
1045                 {
1046                   di->repo = di->pool->repos[di->repoid];
1047                   goto di_enterrepo;
1048                 }
1049             }
1050
1051         /* FALLTHROUGH */
1052         case di_bye:
1053           di->state = di_bye;
1054           return 0;
1055
1056         case di_enterrepo: di_enterrepo:
1057           if (!(di->flags & SEARCH_THISENTRY))
1058             di->entry = di->repo->start;
1059           /* FALLTHROUGH */
1060
1061         case di_entersolvable: di_entersolvable:
1062           if (di->repodataid >= 0)
1063             {
1064               di->repodataid = 0;
1065               if (di->entry > 0 && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
1066                 {
1067                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1068                   di->data = 0;
1069                   goto di_entersolvablekey;
1070                 }
1071             }
1072
1073         case di_enterrepodata: di_enterrepodata:
1074           if (di->repodataid >= 0)
1075             di->data = di->repo->repodata + di->repodataid;
1076           if (!maybe_load_repodata(di->data, di->keyname))
1077             goto di_nextrepodata;
1078           di->dp = entry2data(di->data, di->entry, &schema);
1079           if (!di->dp)
1080             goto di_nextrepodata;
1081           di->keyp = di->data->schemadata + di->data->schemata[schema];
1082           if (di->keyname)
1083             {
1084               Id *keyp;
1085               if ((di->flags & SEARCH_SUB) != 0)
1086                 {
1087                   di->keyp--;
1088                   goto di_nextkey;
1089                 }
1090               for (keyp = di->keyp; *keyp; keyp++)
1091                 if (di->data->keys[*keyp].name == di->keyname)
1092                   break;
1093               if (!*keyp)
1094                 goto di_nextrepodata;
1095               di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1096               di->keyp = keyp;
1097               if (!di->dp)
1098                 goto di_nextrepodata;
1099             }
1100
1101         case di_enterkey: di_enterkey:
1102           di->kv.entry = -1;
1103           di->key = di->data->keys + *di->keyp;
1104           di->ddp = get_data(di->data, di->key, &di->dp);
1105           if (!di->ddp)
1106             goto di_nextkey;
1107           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1108             {
1109               di->ddp = data_read_id(di->ddp, &di->kv.num);
1110               di->kv.entry = -1;
1111               di->kv.eof = 0;
1112               goto di_nextarrayelement;
1113             }
1114           goto di_nextattr;
1115
1116         case di_nextarrayelement: di_nextarrayelement:
1117           di->kv.entry++;
1118           if (di->kv.entry)
1119             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1120           if (di->kv.entry == di->kv.num)
1121             {
1122               if (di->keyname && di->key->name != di->keyname)
1123                 goto di_nextkey;
1124               di->kv.str = (char *)di->ddp;
1125               di->kv.eof = 1;
1126               di->state = di_nextkey;
1127               break;
1128             }
1129           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1130             di->ddp = data_read_id(di->ddp, &di->kv.id);
1131           di->kv.str = (char *)di->ddp;
1132           if (di->keyname && di->key->name != di->keyname)
1133             goto di_entersub;
1134           if ((di->flags & SEARCH_SUB) != 0)
1135             di->state = di_entersub;
1136           else
1137             di->state = di_nextarrayelement;
1138           break;
1139
1140         case di_entersub: di_entersub:
1141           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1142             goto di_nextarrayelement;   /* sorry, full */
1143           di->parents[di->nparents].kv = di->kv;
1144           di->parents[di->nparents].dp = di->dp;
1145           di->parents[di->nparents].keyp = di->keyp;
1146           di->dp = (unsigned char *)di->kv.str;
1147           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1148           memset(&di->kv, 0, sizeof(di->kv));
1149           di->kv.parent = &di->parents[di->nparents].kv;
1150           di->nparents++;
1151           di->keyp--;
1152           goto di_nextkey;
1153           
1154         case di_leavesub: di_leavesub:
1155           di->nparents--;
1156           di->dp = di->parents[di->nparents].dp;
1157           di->kv = di->parents[di->nparents].kv;
1158           di->keyp = di->parents[di->nparents].keyp;
1159           di->key = di->data->keys + *di->keyp;
1160           di->ddp = (unsigned char *)di->kv.str;
1161           goto di_nextarrayelement;
1162
1163         /* special solvable attr handling follows */
1164
1165         case di_nextsolvableattr:
1166           di->kv.id = *di->idp++;
1167           di->kv.entry++;
1168           if (!*di->idp)
1169             {
1170               di->kv.eof = 1;
1171               di->state = di_nextsolvablekey;
1172             }
1173           break;
1174
1175         case di_nextsolvablekey: di_nextsolvablekey:
1176           if (di->keyname || di->key->name == RPM_RPMDBID)
1177             goto di_enterrepodata;
1178           di->key++;
1179           /* FALLTHROUGH */
1180
1181         case di_entersolvablekey: di_entersolvablekey:
1182           di->idp = solvabledata_fetch(di->pool->solvables + di->entry, &di->kv, di->key->name);
1183           if (!di->idp || !di->idp[0])
1184             goto di_nextsolvablekey;
1185           di->kv.id = di->idp[0];
1186           di->kv.num = di->idp[0];
1187           if (!di->kv.eof && !di->idp[1])
1188             di->kv.eof = 1;
1189           di->kv.entry = 0;
1190           if (di->kv.eof)
1191             di->state = di_nextsolvablekey;
1192           else
1193             di->state = di_nextsolvableattr;
1194           break;
1195         }
1196
1197       if (di->matcher.match)
1198         if (!datamatcher_match(&di->matcher, di->data, di->key, &di->kv))
1199           continue;
1200       /* found something! */
1201       return 1;
1202     }
1203 }
1204
1205 void
1206 dataiterator_setpos(Dataiterator *di)
1207 {
1208   di->pool->pos.repo = di->repo;
1209   di->pool->pos.repodataid = di->data - di->repo->repodata;
1210   di->pool->pos.schema = di->kv.id;
1211   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1212 }
1213
1214 void
1215 dataiterator_skip_attribute(Dataiterator *di)
1216 {
1217   if (di->state == di_nextsolvableattr)
1218     di->state = di_nextsolvablekey;
1219   else
1220     di->state = di_nextkey;
1221 }
1222
1223 void
1224 dataiterator_skip_solvable(Dataiterator *di)
1225 {
1226   di->state = di_nextsolvable;
1227 }
1228
1229 void
1230 dataiterator_skip_repo(Dataiterator *di)
1231 {
1232   di->state = di_nextrepo;
1233 }
1234
1235 void
1236 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1237 {
1238   di->repo = s->repo;
1239   di->repoid = -1;
1240   di->entry = s - di->pool->solvables;
1241   di->state = di_entersolvable;
1242 }
1243
1244 void
1245 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1246 {
1247   di->repo = repo;
1248   di->repoid = -1;
1249   di->state = di_enterrepo;
1250 }
1251
1252 #else
1253
1254 /************************************************************************
1255  * data search iterator
1256  */
1257
1258 static void
1259 dataiterator_newdata(Dataiterator *di)
1260 {
1261   Id keyname = di->keyname;
1262   Repodata *data = di->data;
1263   di->nextkeydp = 0;
1264
1265   if (data->state == REPODATA_STUB)
1266     {
1267       if (keyname)
1268         {
1269           int j;
1270           for (j = 1; j < data->nkeys; j++)
1271             if (keyname == data->keys[j].name)
1272               break;
1273           if (j == data->nkeys)
1274             return;
1275         }
1276       /* load it */
1277       if (data->loadcallback)
1278         data->loadcallback(data);
1279       else
1280         data->state = REPODATA_ERROR;
1281     }
1282   if (data->state == REPODATA_ERROR)
1283     return;
1284
1285   Id schema;
1286   unsigned char *dp = data->incoredata;
1287   if (!dp)
1288     return;
1289   if (di->solvid >= 0)
1290     dp += data->incoreoffset[di->solvid - data->start];
1291   dp = data_read_id(dp, &schema);
1292   Id *keyp = data->schemadata + data->schemata[schema];
1293   if (keyname)
1294     {
1295       Id k, *kp;
1296       /* search in a specific key */
1297       for (kp = keyp; (k = *kp++) != 0; )
1298         if (data->keys[k].name == keyname)
1299           break;
1300       if (k == 0)
1301         return;
1302       dp = forward_to_key(data, k, keyp, dp);
1303       if (!dp)
1304         return;
1305       keyp = kp - 1;
1306     }
1307   Id keyid = *keyp++;
1308   if (!keyid)
1309     return;
1310
1311   di->data = data;
1312   di->key = di->data->keys + keyid;
1313   di->keyp = keyp;
1314   di->dp = 0;
1315
1316   di->nextkeydp = dp;
1317   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1318   di->kv.eof = 0;
1319 }
1320
1321 void
1322 dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
1323                   const char *match, int flags)
1324 {
1325   di->flags = flags;
1326   if (p > 0)
1327     {
1328       di->solvid = p;
1329       di->flags |= __SEARCH_ONESOLVABLE;
1330       di->data = repo->repodata - 1;
1331       if (flags & SEARCH_NO_STORAGE_SOLVABLE)
1332         di->state = 0;
1333       else
1334         di->state = 1;
1335     }
1336   else
1337     {
1338       di->solvid = repo->start - 1;
1339       if (di->solvid < 0)
1340         {
1341           fprintf(stderr, "A repo contains the NULL solvable!\n");
1342           exit(1);
1343         }
1344       di->data = repo->repodata + repo->nrepodata - 1;
1345       di->state = 0;
1346     }
1347
1348   di->match = match;
1349   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1350     {
1351       if (di->match)
1352         {
1353           /* We feed multiple lines eventually (e.g. authors or descriptions),
1354              so set REG_NEWLINE. */
1355           di->regex_err =
1356             regcomp(&di->regex, di->match,
1357               REG_EXTENDED | REG_NOSUB | REG_NEWLINE
1358               | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1359 #if 0
1360           if (di->regex_err != 0)
1361             {
1362               fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
1363               fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
1364               exit(1);
1365             }
1366 #else
1367         }
1368       else
1369         {
1370           di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
1371           di->regex_err = 0;
1372 #endif
1373         }
1374     }
1375
1376   di->keyname = keyname;
1377   static Id zeroid = 0;
1378   di->keyp = &zeroid;
1379   di->kv.eof = 1;
1380   di->repo = repo;
1381   di->idp = 0;
1382   di->subkeyp = 0;
1383 }
1384
1385 /* FIXME factor and merge with repo_matchvalue */
1386 static int
1387 dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
1388 {
1389   KeyValue *kv = &di->kv;
1390   const char *match = vmatch;
1391   if ((flags & SEARCH_STRINGMASK) != 0)
1392     {
1393       switch (di->key->type)
1394         {
1395         case REPOKEY_TYPE_ID:
1396         case REPOKEY_TYPE_IDARRAY:
1397           if (di->data && di->data->localpool)
1398             kv->str = stringpool_id2str(&di->data->spool, kv->id);
1399           else
1400             kv->str = id2str(di->repo->pool, kv->id);
1401           break;
1402         case REPOKEY_TYPE_STR:
1403           break;
1404         case REPOKEY_TYPE_DIRSTRARRAY:
1405           if (!(flags & SEARCH_FILES))
1406             return 0;
1407           /* Put the full filename into kv->str.  */
1408           kv->str = repodata_dir2str(di->data, kv->id, kv->str);
1409           /* And to compensate for that put the "empty" directory into
1410              kv->id, so that later calls to repodata_dir2str on this data
1411              come up with the same filename again.  */
1412           kv->id = 0;
1413           break;
1414         default:
1415           return 0;
1416         }
1417       /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
1418          for the others we can't know if a colon separates a kind or not.  */
1419       if ((flags & SEARCH_SKIP_KIND)
1420           && di->key->storage == KEY_STORAGE_SOLVABLE)
1421         {
1422           const char *s = strchr(kv->str, ':');
1423           if (s)
1424             kv->str = s + 1;
1425         }
1426       switch ((flags & SEARCH_STRINGMASK))
1427         {
1428           case SEARCH_SUBSTRING:
1429             if (flags & SEARCH_NOCASE)
1430               {
1431                 if (!strcasestr(kv->str, match))
1432                   return 0;
1433               }
1434             else
1435               {
1436                 if (!strstr(kv->str, match))
1437                   return 0;
1438               }
1439             break;
1440           case SEARCH_STRING:
1441             if (flags & SEARCH_NOCASE)
1442               {
1443                 if (strcasecmp(match, kv->str))
1444                   return 0;
1445               }
1446             else
1447               {
1448                 if (strcmp(match, kv->str))
1449                   return 0;
1450               }
1451             break;
1452           case SEARCH_GLOB:
1453             if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1454               return 0;
1455             break;
1456           case SEARCH_REGEX:
1457             if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
1458               return 0;
1459             break;
1460           default:
1461             return 0;
1462         }
1463     }
1464   return 1;
1465 }
1466
1467 static int
1468 dataiterator_match_int(Dataiterator *di)
1469 {
1470   if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1471     return dataiterator_match_int_real(di, di->flags, &di->regex);
1472   else
1473     return dataiterator_match_int_real(di, di->flags, di->match);
1474 }
1475
1476 int
1477 dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
1478 {
1479   return dataiterator_match_int_real(di, flags, vmatch);
1480 }
1481
1482 int
1483 dataiterator_step(Dataiterator *di)
1484 {
1485 restart:
1486   while (1)
1487     {
1488       if (di->state)
1489         {
1490           /* we're stepping through solvable data, 1 -> SOLVABLE_NAME... */
1491           if (di->idp)
1492             {
1493               /* we're stepping through an id array */
1494               Id *idp = di->idp;
1495               if (*idp)
1496                 {
1497                   di->kv.id = *idp;
1498                   di->idp++;
1499                   di->kv.eof = idp[1] ? 0 : 1;
1500                   goto weg2;
1501                 }
1502               else
1503                 di->idp = 0;
1504             }
1505           Solvable *s = di->repo->pool->solvables + di->solvid;
1506           int state = di->state;
1507           di->key = solvablekeys + state - 1;
1508           if (di->keyname)
1509             di->state = RPM_RPMDBID;
1510           else
1511             di->state++;
1512           if (state == 1)
1513             {
1514               di->data = 0;
1515               if (di->keyname)
1516                 state = di->keyname - 1;
1517             }
1518           switch (state + 1)
1519             {
1520               case SOLVABLE_NAME:
1521                 if (!s->name)
1522                   continue;
1523                 di->kv.id = s->name;
1524                 di->kv.eof = 1;
1525                 break;
1526               case SOLVABLE_ARCH:
1527                 if (!s->arch)
1528                   continue;
1529                 di->kv.id = s->arch;
1530                 di->kv.eof = 1;
1531                 break;
1532               case SOLVABLE_EVR:
1533                 if (!s->evr)
1534                   continue;
1535                 di->kv.id = s->evr;
1536                 di->kv.eof = 1;
1537                 break;
1538               case SOLVABLE_VENDOR:
1539                 if (!s->vendor)
1540                   continue;
1541                 di->kv.id = s->vendor;
1542                 di->kv.eof = 1;
1543                 break;
1544               case SOLVABLE_PROVIDES:
1545                 di->idp = s->provides
1546                     ? di->repo->idarraydata + s->provides : 0;
1547                 continue;
1548               case SOLVABLE_OBSOLETES:
1549                 di->idp = s->obsoletes
1550                     ? di->repo->idarraydata + s->obsoletes : 0;
1551                 continue;
1552               case SOLVABLE_CONFLICTS:
1553                 di->idp = s->conflicts
1554                     ? di->repo->idarraydata + s->conflicts : 0;
1555                 continue;
1556               case SOLVABLE_REQUIRES:
1557                 di->idp = s->requires
1558                     ? di->repo->idarraydata + s->requires : 0;
1559                 continue;
1560               case SOLVABLE_RECOMMENDS:
1561                 di->idp = s->recommends
1562                     ? di->repo->idarraydata + s->recommends : 0;
1563                 continue;
1564               case SOLVABLE_SUPPLEMENTS:
1565                 di->idp = s->supplements
1566                     ? di->repo->idarraydata + s->supplements : 0;
1567                 continue;
1568               case SOLVABLE_SUGGESTS:
1569                 di->idp = s->suggests
1570                     ? di->repo->idarraydata + s->suggests : 0;
1571                 continue;
1572               case SOLVABLE_ENHANCES:
1573                 di->idp = s->enhances
1574                     ? di->repo->idarraydata + s->enhances : 0;
1575                 continue;
1576               case RPM_RPMDBID:
1577                 if (!di->repo->rpmdbid)
1578                   continue;
1579                 di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
1580                 di->kv.eof = 1;
1581                 break;
1582               default:
1583                 di->data = di->repo->repodata - 1;
1584                 di->kv.eof = 1;
1585                 di->state = 0;
1586                 continue;
1587             }
1588         }
1589       else if (di->subkeyp)
1590         {
1591           Id keyid;
1592           if (!di->subnum)
1593             {
1594               /* Send end-of-substruct.  We are here only when we saw a
1595                  _COUNTED key one level up.  Since then we didn't increment
1596                  ->keyp, so it still can be found at keyp[-1].  */
1597               di->kv.eof = 2;
1598               di->key = di->data->keys + di->keyp[-1];
1599               di->subkeyp = 0;
1600             }
1601           else if (!(keyid = *di->subkeyp++))
1602             {
1603               /* Send end-of-element.  See above for keyp[-1].  */
1604               di->kv.eof = 1;
1605               di->key = di->data->keys + di->keyp[-1];
1606               if (di->subschema)
1607                 di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1608               else
1609                 {
1610                   di->dp = data_read_id(di->dp, &di->subschema);
1611                   di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1612                   di->subschema = 0;
1613                 }
1614               di->subnum--;
1615             }
1616           else
1617             {
1618               di->key = di->data->keys + keyid;
1619               di->dp = data_fetch(di->dp, &di->kv, di->key);
1620               if (!di->dp)
1621                 exit(1);
1622             }
1623         }
1624       else
1625         {
1626           if (di->kv.eof)
1627             di->dp = 0;
1628           else
1629             di->dp = data_fetch(di->dp, &di->kv, di->key);
1630
1631           while (!di->dp)
1632             {
1633               Id keyid;
1634               if (di->keyname || !(keyid = *di->keyp++))
1635                 {
1636                   while (1)
1637                     {
1638                       Repo *repo = di->repo;
1639                       Repodata *data = ++di->data;
1640                       if (data >= repo->repodata + repo->nrepodata)
1641                         {
1642                           if (di->flags & __SEARCH_ONESOLVABLE)
1643                             return 0;
1644                           if (di->solvid >= 0)
1645                             {
1646                               while (++di->solvid < repo->end)
1647                                 if (repo->pool->solvables[di->solvid].repo == repo)
1648                                   break;
1649                               if (di->solvid >= repo->end)
1650                                 {
1651                                   if (!(di->flags & SEARCH_EXTRA))
1652                                     goto skiprepo;
1653                                   goto skiprepo;
1654                                 }
1655                             }
1656                           else
1657                             {
1658                                 {
1659 skiprepo:;
1660                                   Pool *pool = di->repo->pool;
1661                                   if (!(di->flags & SEARCH_ALL_REPOS)
1662                                       || di->repo == pool->repos[pool->nrepos - 1])
1663                                     return 0;
1664                                   int i;
1665                                   for (i = 0; i < pool->nrepos; i++)
1666                                     if (di->repo == pool->repos[i])
1667                                       break;
1668                                   di->repo = pool->repos[i + 1];
1669                                   dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
1670                                   continue;
1671                                 }
1672                             }
1673                           di->data = repo->repodata - 1;
1674                           if ((di->flags & SEARCH_NO_STORAGE_SOLVABLE))
1675                             continue;
1676                           static Id zeroid = 0;
1677                           di->keyp = &zeroid;
1678                           di->state = 1;
1679                           goto restart;
1680                         }
1681                       if ((di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
1682                         {
1683                           dataiterator_newdata(di);
1684                           if (di->nextkeydp)
1685                             break;
1686                         }
1687                     }
1688                 }
1689               else
1690                 {
1691                   di->key = di->data->keys + keyid;
1692                   di->dp = get_data(di->data, di->key, &di->nextkeydp);
1693                 }
1694               di->dp = data_fetch(di->dp, &di->kv, di->key);
1695             }
1696           if (di->key->type == REPOKEY_TYPE_FIXARRAY)
1697             {
1698               di->subnum = di->kv.num;
1699               di->subschema = di->kv.id;
1700               di->kv.eof = 0;
1701               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1702             }
1703           if (di->key->type == REPOKEY_TYPE_FLEXARRAY)
1704             {
1705               di->subnum = di->kv.num;
1706               di->kv.eof = 0;
1707               di->dp = data_read_id(di->dp, &di->subschema);
1708               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
1709               di->subschema = 0;
1710             }
1711         }
1712 weg2:
1713       if (!di->match
1714           || dataiterator_match_int(di))
1715         break;
1716     }
1717   return 1;
1718 }
1719
1720 void
1721 dataiterator_skip_attribute(Dataiterator *di)
1722 {
1723   if (di->state)
1724     di->idp = 0;
1725   /* This will make the next _step call to retrieve the next field.  */
1726   di->kv.eof = 1;
1727 }
1728
1729 void
1730 dataiterator_skip_solvable(Dataiterator *di)
1731 {
1732   /* We're done with this field.  */
1733   di->kv.eof = 1;
1734   /* And with solvable data.  */
1735   di->state = 0;
1736   /* And with all keys for this repodata and thing. */
1737   static Id zeroid = 0;
1738   di->keyp = &zeroid;
1739   /* And with all repodatas for this thing.  */
1740   di->data = di->repo->repodata + di->repo->nrepodata - 1;
1741   /* Hence the next call to _step will retrieve the next thing.  */
1742 }
1743
1744 void
1745 dataiterator_skip_repo(Dataiterator *di)
1746 {
1747   dataiterator_skip_solvable(di);
1748   /* We're done with all solvables and all extra things for this repo.  */
1749   di->solvid = -1;
1750 }
1751
1752 void
1753 dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
1754 {
1755   di->repo = s->repo;
1756   /* Simulate us being done with the solvable before the requested one.  */
1757   dataiterator_skip_solvable(di);
1758   di->solvid = s - s->repo->pool->solvables;
1759   di->solvid--;
1760 }
1761
1762 void
1763 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1764 {
1765   di->repo = repo;
1766   dataiterator_skip_solvable(di);
1767   di->solvid = repo->start - 1;
1768 }
1769
1770 #endif
1771
1772 /************************************************************************
1773  * data modify functions
1774  */
1775
1776 /* extend repodata so that it includes solvables p */
1777 void
1778 repodata_extend(Repodata *data, Id p)
1779 {
1780   if (data->start == data->end)
1781     data->start = data->end = p;
1782   if (p >= data->end)
1783     {
1784       int old = data->end - data->start;
1785       int new = p - data->end + 1;
1786       if (data->attrs)
1787         {
1788           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
1789           memset(data->attrs + old, 0, new * sizeof(Id));
1790         }
1791       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1792       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1793       data->end = p + 1;
1794     }
1795   if (p < data->start)
1796     {
1797       int old = data->end - data->start;
1798       int new = data->start - p;
1799       if (data->attrs)
1800         {
1801           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
1802           memmove(data->attrs + new, data->attrs, old * sizeof(Id));
1803           memset(data->attrs, 0, new * sizeof(Id));
1804         }
1805       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1806       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1807       memset(data->incoreoffset, 0, new * sizeof(Id));
1808       data->start = p;
1809     }
1810 }
1811
1812 void
1813 repodata_extend_block(Repodata *data, Id start, Id num)
1814 {
1815   if (!num)
1816     return;
1817   if (!data->incoreoffset)
1818     {
1819       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1820       data->start = start;
1821       data->end = start + num;
1822       return;
1823     }
1824   repodata_extend(data, start);
1825   if (num > 1)
1826     repodata_extend(data, start + num - 1);
1827 }
1828
1829 /**********************************************************************/
1830
1831 #define REPODATA_ATTRS_BLOCK 63
1832 #define REPODATA_ATTRDATA_BLOCK 1023
1833 #define REPODATA_ATTRIDDATA_BLOCK 63
1834
1835
1836 Id
1837 repodata_new_handle(Repodata *data)
1838 {
1839   if (!data->nxattrs)
1840     {
1841       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1842       data->nxattrs = 2;
1843     }
1844   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1845   data->xattrs[data->nxattrs] = 0;
1846   return -(data->nxattrs++);
1847 }
1848
1849 static inline Id **
1850 repodata_get_attrp(Repodata *data, Id handle)
1851 {
1852   if (handle == REPOENTRY_META)
1853     {
1854       if (!data->xattrs)
1855         {
1856           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1857           data->nxattrs = 2;
1858         }
1859     }
1860   if (handle < 0)
1861     return data->xattrs - handle;
1862   if (handle < data->start || handle >= data->end)
1863     repodata_extend(data, handle);
1864   if (!data->attrs)
1865     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1866   return data->attrs + (handle - data->start);
1867 }
1868
1869 static void
1870 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1871 {
1872   Id *pp;
1873   Id *ap, **app;
1874   int i;
1875
1876   app = repodata_get_attrp(data, handle);
1877   ap = *app;
1878   i = 0;
1879   if (ap)
1880     {
1881       for (pp = ap; *pp; pp += 2)
1882         /* Determine equality based on the name only, allows us to change
1883            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1884         if (data->keys[*pp].name == data->keys[keyid].name)
1885           break;
1886       if (*pp)
1887         {
1888           if (overwrite)
1889             {
1890               pp[0] = keyid;
1891               pp[1] = val;
1892             }
1893           return;
1894         }
1895       i = pp - ap;
1896     }
1897   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1898   *app = ap;
1899   pp = ap + i;
1900   *pp++ = keyid;
1901   *pp++ = val;
1902   *pp = 0;
1903 }
1904
1905
1906 void
1907 repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
1908 {
1909   Id keyid;
1910
1911   keyid = repodata_key2id(data, key, 1);
1912   repodata_insert_keyid(data, handle, keyid, val, 1);
1913 }
1914
1915 void
1916 repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
1917 {
1918   Repokey key;
1919   key.name = keyname;
1920   key.type = REPOKEY_TYPE_ID;
1921   key.size = 0;
1922   key.storage = KEY_STORAGE_INCORE;
1923   repodata_set(data, handle, &key, id);
1924 }
1925
1926 void
1927 repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
1928 {
1929   Repokey key;
1930   key.name = keyname;
1931   key.type = REPOKEY_TYPE_NUM;
1932   key.size = 0;
1933   key.storage = KEY_STORAGE_INCORE;
1934   repodata_set(data, handle, &key, (Id)num);
1935 }
1936
1937 void
1938 repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
1939 {
1940   Repokey key;
1941   Id id;
1942   if (data->localpool)
1943     id = stringpool_str2id(&data->spool, str, 1);
1944   else
1945     id = str2id(data->repo->pool, str, 1);
1946   key.name = keyname;
1947   key.type = REPOKEY_TYPE_ID;
1948   key.size = 0;
1949   key.storage = KEY_STORAGE_INCORE;
1950   repodata_set(data, handle, &key, id);
1951 }
1952
1953 void
1954 repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
1955 {
1956   Repokey key;
1957   key.name = keyname;
1958   key.type = REPOKEY_TYPE_CONSTANT;
1959   key.size = constant;
1960   key.storage = KEY_STORAGE_INCORE;
1961   repodata_set(data, handle, &key, 0);
1962 }
1963
1964 void
1965 repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
1966 {
1967   Repokey key;
1968   key.name = keyname;
1969   key.type = REPOKEY_TYPE_CONSTANTID;
1970   key.size = id;
1971   key.storage = KEY_STORAGE_INCORE;
1972   repodata_set(data, handle, &key, 0);
1973 }
1974
1975 void
1976 repodata_set_void(Repodata *data, Id handle, Id keyname)
1977 {
1978   Repokey key;
1979   key.name = keyname;
1980   key.type = REPOKEY_TYPE_VOID;
1981   key.size = 0;
1982   key.storage = KEY_STORAGE_INCORE;
1983   repodata_set(data, handle, &key, 0);
1984 }
1985
1986 void
1987 repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
1988 {
1989   Repokey key;
1990   int l;
1991
1992   l = strlen(str) + 1;
1993   key.name = keyname;
1994   key.type = REPOKEY_TYPE_STR;
1995   key.size = 0;
1996   key.storage = KEY_STORAGE_INCORE;
1997   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1998   memcpy(data->attrdata + data->attrdatalen, str, l);
1999   repodata_set(data, handle, &key, data->attrdatalen);
2000   data->attrdatalen += l;
2001 }
2002
2003 static void
2004 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2005 {
2006   int oldsize;
2007   Id *ida, *pp, **ppp;
2008
2009   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2010     {
2011       /* great! just append the new data */
2012       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2013       data->attriddatalen--;    /* overwrite terminating 0  */
2014       data->lastdatalen += entrysize;
2015       return;
2016     }
2017   ppp = repodata_get_attrp(data, handle);
2018   pp = *ppp;
2019   if (pp)
2020     for (; *pp; pp += 2)
2021       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
2022         break;
2023   if (!pp || !*pp)
2024     {
2025       /* not found. allocate new key */
2026       Repokey key;
2027       key.name = keyname;
2028       key.type = keytype;
2029       key.size = 0;
2030       key.storage = KEY_STORAGE_INCORE;
2031       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2032       repodata_set(data, handle, &key, data->attriddatalen);
2033       data->lasthandle = 0;     /* next time... */
2034       return;
2035     }
2036   oldsize = 0;
2037   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2038     oldsize += entrysize;
2039   if (ida + 1 == data->attriddata + data->attriddatalen)
2040     {
2041       /* this was the last entry, just append it */
2042       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2043       data->attriddatalen--;    /* overwrite terminating 0  */
2044     }
2045   else
2046     {
2047       /* too bad. move to back. */
2048       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2049       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2050       pp[1] = data->attriddatalen;
2051       data->attriddatalen += oldsize;
2052     }
2053   data->lasthandle = handle;
2054   data->lastkey = *pp;
2055   data->lastdatalen = data->attriddatalen + entrysize + 1;
2056 }
2057
2058 static inline int
2059 checksumtype2len(Id type)
2060 {
2061   switch (type)
2062     {
2063     case REPOKEY_TYPE_MD5:
2064       return SIZEOF_MD5;
2065     case REPOKEY_TYPE_SHA1:
2066       return SIZEOF_SHA1;
2067     case REPOKEY_TYPE_SHA256:
2068       return SIZEOF_SHA256;
2069     default:
2070       return 0;
2071     }
2072 }
2073
2074 void
2075 repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
2076                       const unsigned char *str)
2077 {
2078   Repokey key;
2079   int l = checksumtype2len(type);
2080
2081   if (!l)
2082     return;
2083   key.name = keyname;
2084   key.type = type;
2085   key.size = 0;
2086   key.storage = KEY_STORAGE_INCORE;
2087   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2088   memcpy(data->attrdata + data->attrdatalen, str, l);
2089   repodata_set(data, handle, &key, data->attrdatalen);
2090   data->attrdatalen += l;
2091 }
2092
2093 static int
2094 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
2095 {
2096   int i;
2097   for (i = 0; i < buflen; i++)
2098     {
2099 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')      \
2100                 : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
2101                 : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
2102                 : -1)
2103       int v = c2h(*str);
2104       str++;
2105       if (v < 0)
2106         return 0;
2107       buf[i] = v;
2108       v = c2h(*str);
2109       str++;
2110       if (v < 0)
2111         return 0;
2112       buf[i] = (buf[i] << 4) | v;
2113 #undef c2h
2114     }
2115   return buflen;
2116 }
2117
2118 void
2119 repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
2120                       const char *str)
2121 {
2122   unsigned char buf[64];
2123   int l = checksumtype2len(type);
2124
2125   if (!l)
2126     return;
2127   if (hexstr2bytes(buf, str, l) != l)
2128     {
2129       fprintf(stderr, "Invalid hex character in '%s'\n", str);
2130       return;
2131     }
2132   repodata_set_bin_checksum(data, handle, keyname, type, buf);
2133 }
2134
2135 const char *
2136 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2137 {
2138   int i, l;
2139   char *str, *s;
2140
2141   l = checksumtype2len(type);
2142   if (!l)
2143     return "";
2144   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
2145   for (i = 0; i < l; i++)
2146     {
2147       unsigned char v = buf[i];
2148       unsigned char w = v >> 4;
2149       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2150       w = v & 15;
2151       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
2152     }
2153   *s = 0;
2154   return str;
2155 }
2156
2157 Id
2158 repodata_globalize_id(Repodata *data, Id id)
2159
2160   if (!data || !data->localpool)
2161     return id;
2162   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
2163 }
2164
2165 void
2166 repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
2167 {
2168   assert(dir);
2169 #if 0
2170 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
2171 #endif
2172   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2173   data->attriddata[data->attriddatalen++] = dir;
2174   data->attriddata[data->attriddatalen++] = num;
2175   data->attriddata[data->attriddatalen++] = num2;
2176   data->attriddata[data->attriddatalen++] = 0;
2177 }
2178
2179 void
2180 repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
2181 {
2182   Id stroff;
2183   int l;
2184
2185   assert(dir);
2186   l = strlen(str) + 1;
2187   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2188   memcpy(data->attrdata + data->attrdatalen, str, l);
2189   stroff = data->attrdatalen;
2190   data->attrdatalen += l;
2191
2192 #if 0
2193 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
2194 #endif
2195   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2196   data->attriddata[data->attriddatalen++] = dir;
2197   data->attriddata[data->attriddatalen++] = stroff;
2198   data->attriddata[data->attriddatalen++] = 0;
2199 }
2200
2201 void
2202 repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
2203 {
2204 #if 0
2205 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
2206 #endif
2207   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
2208   data->attriddata[data->attriddatalen++] = id;
2209   data->attriddata[data->attriddatalen++] = 0;
2210 }
2211
2212 void
2213 repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
2214                            const char *str)
2215 {
2216   Id id;
2217   if (data->localpool)
2218     id = stringpool_str2id(&data->spool, str, 1);
2219   else
2220     id = str2id(data->repo->pool, str, 1);
2221   repodata_add_idarray(data, handle, keyname, id);
2222 }
2223
2224 void
2225 repodata_add_fixarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2226 {
2227   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2228   data->attriddata[data->attriddatalen++] = ghandle;
2229   data->attriddata[data->attriddatalen++] = 0;
2230 }
2231
2232 void
2233 repodata_add_flexarray(Repodata *data, Id handle, Id keyname, Id ghandle)
2234 {
2235   repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2236   data->attriddata[data->attriddatalen++] = ghandle;
2237   data->attriddata[data->attriddatalen++] = 0;
2238 }
2239
2240 void
2241 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2242 {
2243   Id *keyp;
2244   if (dest == src || !(keyp = data->attrs[src]))
2245     return;
2246   for (; *keyp; keyp += 2)
2247     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2248 }
2249
2250
2251
2252
2253 /**********************************************************************/
2254
2255 /* unify with repo_write! */
2256
2257 #define EXTDATA_BLOCK 1023
2258
2259 struct extdata {
2260   unsigned char *buf;
2261   int len;
2262 };
2263
2264 static void
2265 data_addid(struct extdata *xd, Id x)
2266 {
2267   unsigned char *dp;
2268   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2269   dp = xd->buf + xd->len;
2270
2271   if (x >= (1 << 14))
2272     {
2273       if (x >= (1 << 28))
2274         *dp++ = (x >> 28) | 128;
2275       if (x >= (1 << 21))
2276         *dp++ = (x >> 21) | 128;
2277       *dp++ = (x >> 14) | 128;
2278     }
2279   if (x >= (1 << 7))
2280     *dp++ = (x >> 7) | 128;
2281   *dp++ = x & 127;
2282   xd->len = dp - xd->buf;
2283 }
2284
2285 static void
2286 data_addideof(struct extdata *xd, Id x, int eof)
2287 {
2288   if (x >= 64)
2289     x = (x & 63) | ((x & ~63) << 1);
2290   data_addid(xd, (eof ? x: x | 64));
2291 }
2292
2293 static void
2294 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2295 {
2296   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2297   memcpy(xd->buf + xd->len, blob, len);
2298   xd->len += len;
2299 }
2300
2301 /*********************************/
2302
2303 static void
2304 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2305                        struct extdata *newvincore,
2306                        Id *schema,
2307                        Repokey *key, Id val)
2308 {
2309   /* Otherwise we have a new value.  Parse it into the internal
2310      form.  */
2311   Id *ida;
2312   struct extdata *xd;
2313   unsigned int oldvincorelen = 0;
2314   Id schemaid, *sp;
2315
2316   xd = newincore;
2317   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2318     {
2319       xd = newvincore;
2320       oldvincorelen = xd->len;
2321     }
2322   switch (key->type)
2323     {
2324     case REPOKEY_TYPE_VOID:
2325     case REPOKEY_TYPE_CONSTANT:
2326     case REPOKEY_TYPE_CONSTANTID:
2327       break;
2328     case REPOKEY_TYPE_STR:
2329       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2330       break;
2331     case REPOKEY_TYPE_MD5:
2332       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2333       break;
2334     case REPOKEY_TYPE_SHA1:
2335       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2336       break;
2337     case REPOKEY_TYPE_SHA256:
2338       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2339       break;
2340     case REPOKEY_TYPE_ID:
2341     case REPOKEY_TYPE_NUM:
2342     case REPOKEY_TYPE_DIR:
2343       data_addid(xd, val);
2344       break;
2345     case REPOKEY_TYPE_IDARRAY:
2346       for (ida = data->attriddata + val; *ida; ida++)
2347         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2348       break;
2349     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2350       for (ida = data->attriddata + val; *ida; ida += 3)
2351         {
2352           data_addid(xd, ida[0]);
2353           data_addid(xd, ida[1]);
2354           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2355         }
2356       break;
2357     case REPOKEY_TYPE_DIRSTRARRAY:
2358       for (ida = data->attriddata + val; *ida; ida += 2)
2359         {
2360           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2361           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2362         }
2363       break;
2364     case REPOKEY_TYPE_FIXARRAY:
2365       {
2366         int num = 0;
2367         schemaid = 0;
2368         for (ida = data->attriddata + val; *ida; ida++)
2369           {
2370 #if 0
2371             fprintf(stderr, "serialize struct %d\n", *ida);
2372 #endif
2373             sp = schema;
2374             Id *kp = data->xattrs[-*ida];
2375             if (!kp)
2376               continue;
2377             num++;
2378             for (;*kp; kp += 2)
2379               {
2380 #if 0
2381                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2382 #endif
2383                 *sp++ = *kp;
2384               }
2385             *sp = 0;
2386             if (!schemaid)
2387               schemaid = repodata_schema2id(data, schema, 1);
2388             else if (schemaid != repodata_schema2id(data, schema, 0))
2389               {
2390                 fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
2391                 exit(1);
2392               }
2393 #if 0
2394             fprintf(stderr, "  schema %d\n", schemaid);
2395 #endif
2396           }
2397         if (!num)
2398           break;
2399         data_addid(xd, num);
2400         data_addid(xd, schemaid);
2401         for (ida = data->attriddata + val; *ida; ida++)
2402           {
2403             Id *kp = data->xattrs[-*ida];
2404             if (!kp)
2405               continue;
2406             for (;*kp; kp += 2)
2407               {
2408                 repodata_serialize_key(data, newincore, newvincore,
2409                                        schema, data->keys + *kp, kp[1]);
2410               }
2411           }
2412         break;
2413       }
2414     case REPOKEY_TYPE_FLEXARRAY:
2415       {
2416         int num = 0;
2417         for (ida = data->attriddata + val; *ida; ida++)
2418           num++;
2419         data_addid(xd, num);
2420         for (ida = data->attriddata + val; *ida; ida++)
2421           {
2422             Id *kp = data->xattrs[-*ida];
2423             if (!kp)
2424               {
2425                 data_addid(xd, 0);      /* XXX */
2426                 continue;
2427               }
2428             sp = schema;
2429             for (;*kp; kp += 2)
2430               *sp++ = *kp;
2431             *sp = 0;
2432             schemaid = repodata_schema2id(data, schema, 1);
2433             data_addid(xd, schemaid);
2434             kp = data->xattrs[-*ida];
2435             for (;*kp; kp += 2)
2436               {
2437                 repodata_serialize_key(data, newincore, newvincore,
2438                                        schema, data->keys + *kp, kp[1]);
2439               }
2440           }
2441         break;
2442       }
2443     default:
2444       fprintf(stderr, "don't know how to handle type %d\n", key->type);
2445       exit(1);
2446     }
2447   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2448     {
2449       /* put offset/len in incore */
2450       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2451       oldvincorelen = xd->len - oldvincorelen;
2452       data_addid(newincore, oldvincorelen);
2453     }
2454 }
2455
2456 void
2457 repodata_internalize(Repodata *data)
2458 {
2459   Repokey *key, solvkey;
2460   Id entry, nentry;
2461   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2462   unsigned char *dp, *ndp;
2463   int newschema, oldcount;
2464   struct extdata newincore;
2465   struct extdata newvincore;
2466   Id solvkeyid;
2467
2468   if (!data->attrs && !data->xattrs)
2469     return;
2470
2471   newvincore.buf = data->vincore;
2472   newvincore.len = data->vincorelen;
2473
2474   /* find the solvables key, create if needed */
2475   memset(&solvkey, 0, sizeof(solvkey));
2476   solvkey.name = REPOSITORY_SOLVABLES;
2477   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2478   solvkey.size = 0;
2479   solvkey.storage = KEY_STORAGE_INCORE;
2480   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2481
2482   schema = sat_malloc2(data->nkeys, sizeof(Id));
2483   seen = sat_malloc2(data->nkeys, sizeof(Id));
2484
2485   /* Merge the data already existing (in data->schemata, ->incoredata and
2486      friends) with the new attributes in data->attrs[].  */
2487   nentry = data->end - data->start;
2488   memset(&newincore, 0, sizeof(newincore));
2489   data_addid(&newincore, 0);    /* start data at offset 1 */
2490
2491   data->mainschema = 0;
2492   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2493
2494   /* join entry data */
2495   /* we start with the meta data, entry -1 */
2496   for (entry = -1; entry < nentry; entry++)
2497     {
2498       memset(seen, 0, data->nkeys * sizeof(Id));
2499       oldschema = 0;
2500       dp = data->incoredata;
2501       if (dp)
2502         {
2503           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2504           dp = data_read_id(dp, &oldschema);
2505         }
2506 #if 0
2507 fprintf(stderr, "oldschema %d\n", oldschema);
2508 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2509 fprintf(stderr, "schemadata %p\n", data->schemadata);
2510 #endif
2511       /* seen: -1: old data  0: skipped  >0: id + 1 */
2512       newschema = 0;
2513       oldcount = 0;
2514       sp = schema;
2515       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2516         {
2517           if (seen[*keyp])
2518             {
2519               fprintf(stderr, "Inconsistent old data (key occured twice).\n");
2520               exit(1);
2521             }
2522           seen[*keyp] = -1;
2523           *sp++ = *keyp;
2524           oldcount++;
2525         }
2526       if (entry >= 0)
2527         keyp = data->attrs ? data->attrs[entry] : 0;
2528       else
2529         {
2530           /* strip solvables key */
2531           *sp = 0;
2532           for (sp = keyp = schema; *sp; sp++)
2533             if (*sp != solvkeyid)
2534               *keyp++ = *sp;
2535             else
2536               oldcount--;
2537           sp = keyp;
2538           seen[solvkeyid] = 0;
2539           keyp = data->xattrs ? data->xattrs[1] : 0;
2540         }
2541       if (keyp)
2542         for (; *keyp; keyp += 2)
2543           {
2544             if (!seen[*keyp])
2545               {
2546                 newschema = 1;
2547                 *sp++ = *keyp;
2548               }
2549             seen[*keyp] = keyp[1] + 1;
2550           }
2551       if (entry < 0 && data->end != data->start)
2552         {
2553           *sp++ = solvkeyid;
2554           newschema = 1;
2555         }
2556       *sp = 0;
2557       if (newschema)
2558         /* Ideally we'd like to sort the new schema here, to ensure
2559            schema equality independend of the ordering.  We can't do that
2560            yet.  For once see below (old ids need to come before new ids).
2561            An additional difficulty is that we also need to move
2562            the values with the keys.  */
2563         schemaid = repodata_schema2id(data, schema, 1);
2564       else
2565         schemaid = oldschema;
2566
2567
2568       /* Now create data blob.  We walk through the (possibly new) schema
2569          and either copy over old data, or insert the new.  */
2570       /* XXX Here we rely on the fact that the (new) schema has the form
2571          o1 o2 o3 o4 ... | n1 n2 n3 ...
2572          (oX being the old keyids (possibly overwritten), and nX being
2573           the new keyids).  This rules out sorting the keyids in order
2574          to ensure a small schema count.  */
2575       if (entry >= 0)
2576         data->incoreoffset[entry] = newincore.len;
2577       data_addid(&newincore, schemaid);
2578       if (entry == -1)
2579         {
2580           data->mainschema = schemaid;
2581           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2582         }
2583       keypstart = data->schemadata + data->schemata[schemaid];
2584       for (keyp = keypstart; *keyp; keyp++)
2585         {
2586           if (entry == -1)
2587             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2588           if (*keyp == solvkeyid)
2589             {
2590               /* add flexarray entry count */
2591               data_addid(&newincore, data->end - data->start);
2592               break;
2593             }
2594           key = data->keys + *keyp;
2595 #if 0
2596           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2597 #endif
2598           ndp = dp;
2599           if (oldcount)
2600             {
2601               /* Skip the data associated with this old key.  */
2602               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2603                 {
2604                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2605                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2606                 }
2607               else if (key->storage == KEY_STORAGE_INCORE)
2608                 ndp = data_skip_key(data, dp, key);
2609               oldcount--;
2610             }
2611           if (seen[*keyp] == -1)
2612             {
2613               /* If this key was an old one _and_ was not overwritten with
2614                  a different value copy over the old value (we skipped it
2615                  above).  */
2616               if (dp != ndp)
2617                 data_addblob(&newincore, dp, ndp - dp);
2618               seen[*keyp] = 0;
2619             }
2620           else if (seen[*keyp])
2621             {
2622               /* Otherwise we have a new value.  Parse it into the internal
2623                  form.  */
2624               repodata_serialize_key(data, &newincore, &newvincore,
2625                                      schema, key, seen[*keyp] - 1);
2626             }
2627           dp = ndp;
2628         }
2629       if (entry >= 0 && data->attrs && data->attrs[entry])
2630         data->attrs[entry] = sat_free(data->attrs[entry]);
2631     }
2632   /* free all xattrs */
2633   for (entry = 0; entry < data->nxattrs; entry++)
2634     if (data->xattrs[entry])
2635       sat_free(data->xattrs[entry]);
2636   data->xattrs = sat_free(data->xattrs);
2637   data->nxattrs = 0;
2638
2639   data->lasthandle = 0;
2640   data->lastkey = 0;
2641   data->lastdatalen = 0;
2642   sat_free(schema);
2643   sat_free(seen);
2644   repodata_free_schemahash(data);
2645
2646   sat_free(data->incoredata);
2647   data->incoredata = newincore.buf;
2648   data->incoredatalen = newincore.len;
2649   data->incoredatafree = 0;
2650   
2651   sat_free(data->vincore);
2652   data->vincore = newvincore.buf;
2653   data->vincorelen = newvincore.len;
2654
2655   data->attrs = sat_free(data->attrs);
2656   data->attrdata = sat_free(data->attrdata);
2657   data->attriddata = sat_free(data->attriddata);
2658   data->attrdatalen = 0;
2659   data->attriddatalen = 0;
2660 }
2661
2662 void
2663 repodata_disable_paging(Repodata *data)
2664 {
2665   if (maybe_load_repodata(data, 0)
2666       && data->num_pages)
2667     repodata_load_page_range(data, 0, data->num_pages - 1);
2668 }
2669
2670 /*
2671 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2672 */