fix dataiterator returning random data in some cases
[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  * a repository can contain multiple repodata entries, consisting of
14  * different sets of keys and different sets of solvables
15  */
16
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <fnmatch.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <regex.h>
26
27 #include "repo.h"
28 #include "pool.h"
29 #include "poolid_private.h"
30 #include "util.h"
31 #include "hash.h"
32 #include "chksum.h"
33
34 #include "repopack.h"
35 #include "repopage.h"
36
37 #define REPODATA_BLOCK 255
38
39 static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
40
41 void
42 repodata_initdata(Repodata *data, Repo *repo, int localpool)
43 {
44   memset(data, 0, sizeof (*data));
45   data->repodataid = data - repo->repodata;
46   data->repo = repo;
47   data->localpool = localpool;
48   if (localpool)
49     stringpool_init_empty(&data->spool);
50   /* dirpool_init(&data->dirpool);      just zeros out again */
51   data->keys = solv_calloc(1, sizeof(Repokey));
52   data->nkeys = 1;
53   data->schemata = solv_calloc(1, sizeof(Id));
54   data->schemadata = solv_calloc(1, sizeof(Id));
55   data->nschemata = 1;
56   data->schemadatalen = 1;
57   repopagestore_init(&data->store);
58 }
59
60 void
61 repodata_freedata(Repodata *data)
62 {
63   int i;
64
65   solv_free(data->keys);
66
67   solv_free(data->schemata);
68   solv_free(data->schemadata);
69   solv_free(data->schematahash);
70
71   stringpool_free(&data->spool);
72   dirpool_free(&data->dirpool);
73
74   solv_free(data->mainschemaoffsets);
75   solv_free(data->incoredata);
76   solv_free(data->incoreoffset);
77   solv_free(data->verticaloffset);
78
79   repopagestore_free(&data->store);
80
81   solv_free(data->vincore);
82
83   if (data->attrs)
84     for (i = 0; i < data->end - data->start; i++)
85       solv_free(data->attrs[i]);
86   solv_free(data->attrs);
87   if (data->xattrs)
88     for (i = 0; i < data->nxattrs; i++)
89       solv_free(data->xattrs[i]);
90   solv_free(data->xattrs);
91
92   solv_free(data->attrdata);
93   solv_free(data->attriddata);
94   solv_free(data->attrnum64data);
95
96   solv_free(data->dircache);
97 }
98
99 void
100 repodata_free(Repodata *data)
101 {
102   Repo *repo = data->repo;
103   int i = data - repo->repodata;
104   if (i == 0)
105     return;
106   repodata_freedata(data);
107   if (i < repo->nrepodata - 1)
108     {
109       /* whoa! this changes the repodataids! */
110       memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
111       for (; i < repo->nrepodata - 1; i++)
112         repo->repodata[i].repodataid = i;
113     }
114   repo->nrepodata--;
115   if (repo->nrepodata == 1)
116     {
117       repo->repodata = solv_free(repo->repodata);
118       repo->nrepodata = 0;
119     }
120 }
121
122 void
123 repodata_empty(Repodata *data, int localpool)
124 {
125   void (*loadcallback)(Repodata *) = data->loadcallback;
126   int state = data->state;
127   repodata_freedata(data);
128   repodata_initdata(data, data->repo, localpool);
129   data->state = state;
130   data->loadcallback = loadcallback;
131 }
132
133
134 /***************************************************************
135  * key pool management
136  */
137
138 /* this is not so time critical that we need a hash, so we do a simple
139  * linear search */
140 Id
141 repodata_key2id(Repodata *data, Repokey *key, int create)
142 {
143   Id keyid;
144
145   for (keyid = 1; keyid < data->nkeys; keyid++)
146     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
147       {
148         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
149           continue;
150         break;
151       }
152   if (keyid == data->nkeys)
153     {
154       if (!create)
155         return 0;
156       /* allocate new key */
157       data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
158       data->keys[data->nkeys++] = *key;
159       if (data->verticaloffset)
160         {
161           data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
162           data->verticaloffset[data->nkeys - 1] = 0;
163         }
164       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
165     }
166   return keyid;
167 }
168
169
170 /***************************************************************
171  * schema pool management
172  */
173
174 #define SCHEMATA_BLOCK 31
175 #define SCHEMATADATA_BLOCK 255
176
177 Id
178 repodata_schema2id(Repodata *data, Id *schema, int create)
179 {
180   int h, len, i;
181   Id *sp, cid;
182   Id *schematahash;
183
184   if (!*schema)
185     return 0;   /* XXX: allow empty schema? */
186   if ((schematahash = data->schematahash) == 0)
187     {
188       data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
189       for (i = 1; i < data->nschemata; i++)
190         {
191           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
192             h = h * 7 + *sp++;
193           h &= 255;
194           schematahash[h] = i;
195         }
196       data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
197       data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
198     }
199
200   for (sp = schema, len = 0, h = 0; *sp; len++)
201     h = h * 7 + *sp++;
202   h &= 255;
203   len++;
204
205   cid = schematahash[h];
206   if (cid)
207     {
208       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
209         return cid;
210       /* cache conflict, do a slow search */
211       for (cid = 1; cid < data->nschemata; cid++)
212         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
213           return cid;
214     }
215   /* a new one */
216   if (!create)
217     return 0;
218   data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
219   data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
220   /* add schema */
221   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
222   data->schemata[data->nschemata] = data->schemadatalen;
223   data->schemadatalen += len;
224   schematahash[h] = data->nschemata;
225 #if 0
226 fprintf(stderr, "schema2id: new schema\n");
227 #endif
228   return data->nschemata++;
229 }
230
231 void
232 repodata_free_schemahash(Repodata *data)
233 {
234   data->schematahash = solv_free(data->schematahash);
235   /* shrink arrays */
236   data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
237   data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
238 }
239
240
241 /***************************************************************
242  * dir pool management
243  */
244
245 #ifndef HAVE_STRCHRNUL
246 static inline const char *strchrnul(const char *str, char x)
247 {
248   const char *p = strchr(str, x);
249   return p ? p : str + strlen(str);
250 }
251 #endif
252
253 #define DIRCACHE_SIZE 41        /* < 1k */
254
255 #ifdef DIRCACHE_SIZE
256 struct dircache {
257   Id ids[DIRCACHE_SIZE];
258   char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
259 };
260 #endif
261
262 Id
263 repodata_str2dir(Repodata *data, const char *dir, int create)
264 {
265   Id id, parent;
266 #ifdef DIRCACHE_SIZE
267   const char *dirs;
268 #endif
269   const char *dire;
270
271   parent = 0;
272   if (!*dir)
273     return 0;
274   while (*dir == '/' && dir[1] == '/')
275     dir++;
276   if (*dir == '/' && !dir[1])
277     {
278       if (data->dirpool.ndirs)
279         return 1;
280       return dirpool_add_dir(&data->dirpool, 0, 1, create);
281     }
282 #ifdef DIRCACHE_SIZE
283   dirs = dir;
284   if (data->dircache)
285     {
286       int l;
287       struct dircache *dircache = data->dircache;
288       l = strlen(dir);
289       while (l > 0)
290         {
291           if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
292             {
293               parent = dircache->ids[l];
294               dir += l;
295               if (!*dir)
296                 return parent;
297               while (*dir == '/')
298                 dir++;
299               break;
300             }
301           while (--l)
302             if (dir[l] == '/')
303               break;
304         }
305     }
306 #endif
307   while (*dir)
308     {
309       dire = strchrnul(dir, '/');
310       if (data->localpool)
311         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
312       else
313         id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
314       if (!id)
315         return 0;
316       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
317       if (!parent)
318         return 0;
319 #ifdef DIRCACHE_SIZE
320       if (!data->dircache)
321         data->dircache = solv_calloc(1, sizeof(struct dircache));
322       if (data->dircache)
323         {
324           int l = dire - dirs;
325           if (l < DIRCACHE_SIZE)
326             {
327               data->dircache->ids[l] = parent;
328               memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
329             }
330         }
331 #endif
332       if (!*dire)
333         break;
334       dir = dire + 1;
335       while (*dir == '/')
336         dir++;
337     }
338   return parent;
339 }
340
341 void
342 repodata_free_dircache(Repodata *data)
343 {
344   data->dircache = solv_free(data->dircache);
345 }
346
347 const char *
348 repodata_dir2str(Repodata *data, Id did, const char *suf)
349 {
350   Pool *pool = data->repo->pool;
351   int l = 0;
352   Id parent, comp;
353   const char *comps;
354   char *p;
355
356   if (!did)
357     return suf ? suf : "";
358   parent = did;
359   while (parent)
360     {
361       comp = dirpool_compid(&data->dirpool, parent);
362       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
363       l += strlen(comps);
364       parent = dirpool_parent(&data->dirpool, parent);
365       if (parent)
366         l++;
367     }
368   if (suf)
369     l += strlen(suf) + 1;
370   p = pool_alloctmpspace(pool, l + 1) + l;
371   *p = 0;
372   if (suf)
373     {
374       p -= strlen(suf);
375       strcpy(p, suf);
376       *--p = '/';
377     }
378   parent = did;
379   while (parent)
380     {
381       comp = dirpool_compid(&data->dirpool, parent);
382       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
383       l = strlen(comps);
384       p -= l;
385       strncpy(p, comps, l);
386       parent = dirpool_parent(&data->dirpool, parent);
387       if (parent)
388         *--p = '/';
389     }
390   return p;
391 }
392
393
394 /***************************************************************
395  * data management
396  */
397
398 static inline unsigned char *
399 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
400 {
401   Id *keyp = data->schemadata + data->schemata[schema];
402   for (; *keyp; keyp++)
403     dp = data_skip_key(data, dp, data->keys + *keyp);
404   return dp;
405 }
406
407 static unsigned char *
408 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
409 {
410   int nentries, schema;
411   switch(key->type)
412     {
413     case REPOKEY_TYPE_FIXARRAY:
414       dp = data_read_id(dp, &nentries);
415       if (!nentries)
416         return dp;
417       dp = data_read_id(dp, &schema);
418       while (nentries--)
419         dp = data_skip_schema(data, dp, schema);
420       return dp;
421     case REPOKEY_TYPE_FLEXARRAY:
422       dp = data_read_id(dp, &nentries);
423       while (nentries--)
424         {
425           dp = data_read_id(dp, &schema);
426           dp = data_skip_schema(data, dp, schema);
427         }
428       return dp;
429     default:
430       if (key->storage == KEY_STORAGE_INCORE)
431         dp = data_skip(dp, key->type);
432       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
433         {
434           dp = data_skip(dp, REPOKEY_TYPE_ID);
435           dp = data_skip(dp, REPOKEY_TYPE_ID);
436         }
437       return dp;
438     }
439 }
440
441 static unsigned char *
442 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
443 {
444   Id k;
445
446   if (!keyid)
447     return 0;
448   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
449     {
450       int i;
451       for (i = 0; (k = *keyp++) != 0; i++)
452         if (k == keyid)
453           return data->incoredata + data->mainschemaoffsets[i];
454       return 0;
455     }
456   while ((k = *keyp++) != 0)
457     {
458       if (k == keyid)
459         return dp;
460       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
461         {
462           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
463           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
464           continue;
465         }
466       if (data->keys[k].storage != KEY_STORAGE_INCORE)
467         continue;
468       dp = data_skip_key(data, dp, data->keys + k);
469     }
470   return 0;
471 }
472
473 static unsigned char *
474 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
475 {
476   unsigned char *dp;
477   if (len <= 0)
478     return 0;
479   if (off >= data->lastverticaloffset)
480     {
481       off -= data->lastverticaloffset;
482       if (off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if (off + len > key->size)
487     return 0;
488   /* we now have the offset, go into vertical */
489   off += data->verticaloffset[key - data->keys];
490   /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
491   dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
492   data->storestate++;
493   if (dp)
494     dp += off % REPOPAGE_BLOBSIZE;
495   return dp;
496 }
497
498 static inline unsigned char *
499 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
500 {
501   unsigned char *dp = *dpp;
502
503   if (!dp)
504     return 0;
505   if (key->storage == KEY_STORAGE_INCORE)
506     {
507       if (advance)
508         *dpp = data_skip_key(data, dp, key);
509       return dp;
510     }
511   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
512     {
513       Id off, len;
514       dp = data_read_id(dp, &off);
515       dp = data_read_id(dp, &len);
516       if (advance)
517         *dpp = dp;
518       return get_vertical_data(data, key, off, len);
519     }
520   return 0;
521 }
522
523 static int
524 load_repodata(Repodata *data)
525 {
526   if (data->loadcallback)
527     {
528       data->loadcallback(data);
529       if (data->state == REPODATA_AVAILABLE)
530         return 1;
531     }
532   data->state = REPODATA_ERROR;
533   return 0;
534 }
535
536 static inline int
537 maybe_load_repodata(Repodata *data, Id keyname)
538 {
539   if (keyname && !repodata_precheck_keyname(data, keyname))
540     return 0;   /* do not bother... */
541   switch(data->state)
542     {
543     case REPODATA_STUB:
544       if (keyname)
545         {
546           int i;
547           for (i = 1; i < data->nkeys; i++)
548             if (keyname == data->keys[i].name)
549               break;
550           if (i == data->nkeys)
551             return 0;
552         }
553       return load_repodata(data);
554     case REPODATA_ERROR:
555       return 0;
556     case REPODATA_AVAILABLE:
557     case REPODATA_LOADING:
558       return 1;
559     default:
560       data->state = REPODATA_ERROR;
561       return 0;
562     }
563 }
564
565 static inline unsigned char *
566 solvid2data(Repodata *data, Id solvid, Id *schemap)
567 {
568   unsigned char *dp = data->incoredata;
569   if (!dp)
570     return 0;
571   if (solvid == SOLVID_META)    /* META */
572     dp += 1;
573   else if (solvid == SOLVID_POS)        /* META */
574     {
575       Pool *pool = data->repo->pool;
576       if (data->repo != pool->pos.repo)
577         return 0;
578       if (data != data->repo->repodata + pool->pos.repodataid)
579         return 0;
580       *schemap = pool->pos.schema;
581       return data->incoredata + pool->pos.dp;
582     }
583   else
584     {
585       if (solvid < data->start || solvid >= data->end)
586         return 0;
587       dp += data->incoreoffset[solvid - data->start];
588     }
589   return data_read_id(dp, schemap);
590 }
591
592 /************************************************************************
593  * data lookup
594  */
595
596 static inline unsigned char *
597 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
598 {
599   unsigned char *dp;
600   Id schema, *keyp, *kp;
601   Repokey *key;
602
603   if (!maybe_load_repodata(data, keyname))
604     return 0;
605   dp = solvid2data(data, solvid, &schema);
606   if (!dp)
607     return 0;
608   keyp = data->schemadata + data->schemata[schema];
609   for (kp = keyp; *kp; kp++)
610     if (data->keys[*kp].name == keyname)
611       break;
612   if (!*kp)
613     return 0;
614   *keypp = key = data->keys + *kp;
615   if (key->type == REPOKEY_TYPE_DELETED)
616     return 0;
617   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
618     return dp;  /* no need to forward... */
619   dp = forward_to_key(data, *kp, keyp, dp);
620   if (!dp)
621     return 0;
622   return get_data(data, key, &dp, 0);
623 }
624
625 Id
626 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
627 {
628   Id schema, *keyp, *kp;
629   if (!maybe_load_repodata(data, keyname))
630     return 0;
631   if (!solvid2data(data, solvid, &schema))
632     return 0;
633   keyp = data->schemadata + data->schemata[schema];
634   for (kp = keyp; *kp; kp++)
635     if (data->keys[*kp].name == keyname)
636       return data->keys[*kp].type;
637   return 0;
638 }
639
640 Id
641 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
642 {
643   unsigned char *dp;
644   Repokey *key;
645   Id id;
646
647   dp = find_key_data(data, solvid, keyname, &key);
648   if (!dp)
649     return 0;
650   if (key->type == REPOKEY_TYPE_CONSTANTID)
651     return key->size;
652   if (key->type != REPOKEY_TYPE_ID)
653     return 0;
654   dp = data_read_id(dp, &id);
655   return id;
656 }
657
658 const char *
659 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
660 {
661   unsigned char *dp;
662   Repokey *key;
663   Id id;
664
665   dp = find_key_data(data, solvid, keyname, &key);
666   if (!dp)
667     return 0;
668   if (key->type == REPOKEY_TYPE_STR)
669     return (const char *)dp;
670   if (key->type == REPOKEY_TYPE_CONSTANTID)
671     id = key->size;
672   else if (key->type == REPOKEY_TYPE_ID)
673     dp = data_read_id(dp, &id);
674   else
675     return 0;
676   if (data->localpool)
677     return stringpool_id2str(&data->spool, id);
678   return pool_id2str(data->repo->pool, id);
679 }
680
681 int
682 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
683 {
684   unsigned char *dp;
685   Repokey *key;
686   unsigned int high, low;
687
688   *value = 0;
689   dp = find_key_data(data, solvid, keyname, &key);
690   if (!dp)
691     return 0;
692   switch (key->type)
693     {
694     case REPOKEY_TYPE_NUM:
695       data_read_num64(dp, &low, &high);
696       *value = (unsigned long long)high << 32 | low;
697       return 1;
698     case REPOKEY_TYPE_U32:
699       data_read_u32(dp, &low);
700       *value = low;
701       return 1;
702     case REPOKEY_TYPE_CONSTANT:
703       *value = key->size;
704       return 1;
705     default:
706       return 0;
707     }
708 }
709
710 int
711 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
712 {
713   Id schema;
714   Id *keyp;
715   unsigned char *dp;
716
717   if (!maybe_load_repodata(data, keyname))
718     return 0;
719   dp = solvid2data(data, solvid, &schema);
720   if (!dp)
721     return 0;
722   /* can't use find_key_data as we need to test the type */
723   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
724     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
725       return 1;
726   return 0;
727 }
728
729 const unsigned char *
730 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
731 {
732   unsigned char *dp;
733   Repokey *key;
734
735   dp = find_key_data(data, solvid, keyname, &key);
736   if (!dp)
737     return 0;
738   if (!(key->type == REPOKEY_TYPE_MD5 || key->type == REPOKEY_TYPE_SHA1 || key->type == REPOKEY_TYPE_SHA256))
739     return 0;
740   *typep = key->type;
741   return dp;
742 }
743
744 int
745 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
746 {
747   unsigned char *dp;
748   Repokey *key;
749   Id id;
750   int eof = 0;
751
752   queue_empty(q);
753   dp = find_key_data(data, solvid, keyname, &key);
754   if (!dp)
755     return 0;
756   if (key->type != REPOKEY_TYPE_IDARRAY && key->type != REPOKEY_TYPE_REL_IDARRAY)
757     return 0;
758   for (;;)
759     {
760       dp = data_read_ideof(dp, &id, &eof);
761       queue_push(q, id);
762       if (eof)
763         break;
764     }
765   return 1;
766 }
767
768 Id
769 repodata_globalize_id(Repodata *data, Id id, int create)
770 {
771   if (!id || !data || !data->localpool)
772     return id;
773   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
774 }
775
776 Id
777 repodata_localize_id(Repodata *data, Id id, int create)
778 {
779   if (!id || !data || !data->localpool)
780     return id;
781   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
782 }
783
784 Id
785 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
786 {
787   Id *ap;
788   if (!data->attrs)
789     return 0;
790   ap = data->attrs[solvid - data->start];
791   if (!ap)
792     return 0;
793   for (; *ap; ap += 2)
794     {
795       if (data->keys[*ap].name != keyname)
796         continue;
797       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
798         return voidid;
799       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
800         return ap[1];
801       return 0;
802     }
803   return 0;
804 }
805
806
807 /************************************************************************
808  * data search
809  */
810
811
812 int
813 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
814 {
815   switch (key->type)
816     {
817     case REPOKEY_TYPE_ID:
818     case REPOKEY_TYPE_CONSTANTID:
819     case REPOKEY_TYPE_IDARRAY:
820       if (data && data->localpool)
821         kv->str = stringpool_id2str(&data->spool, kv->id);
822       else
823         kv->str = pool_id2str(pool, kv->id);
824       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
825         {
826           const char *s;
827           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
828             ;
829           if (*s == ':' && s > kv->str)
830             kv->str = s + 1;
831         }
832       return 1;
833     case REPOKEY_TYPE_STR:
834       return 1;
835     case REPOKEY_TYPE_DIRSTRARRAY:
836       if (!(flags & SEARCH_FILES))
837         return 1;       /* match just the basename */
838       if (kv->num)
839         return 1;       /* already stringified */
840       /* Put the full filename into kv->str.  */
841       kv->str = repodata_dir2str(data, kv->id, kv->str);
842       kv->num = 1;      /* mark stringification */
843       return 1;
844     case REPOKEY_TYPE_MD5:
845     case REPOKEY_TYPE_SHA1:
846     case REPOKEY_TYPE_SHA256:
847       if (!(flags & SEARCH_CHECKSUMS))
848         return 0;       /* skip em */
849       if (kv->num)
850         return 1;       /* already stringified */
851       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
852       kv->num = 1;      /* mark stringification */
853       return 1;
854     default:
855       return 0;
856     }
857 }
858
859
860 struct subschema_data {
861   Solvable *s;
862   void *cbdata;
863   KeyValue *parent;
864 };
865
866 /* search a specific repodata */
867 void
868 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
869 {
870   Id schema;
871   Repokey *key;
872   Id keyid, *kp, *keyp;
873   unsigned char *dp, *ddp;
874   int onekey = 0;
875   int stop;
876   KeyValue kv;
877   Solvable *s;
878
879   if (!maybe_load_repodata(data, keyname))
880     return;
881   if (solvid == SOLVID_SUBSCHEMA)
882     {
883       struct subschema_data *subd = cbdata;
884       cbdata = subd->cbdata;
885       s = subd->s;
886       schema = subd->parent->id;
887       dp = (unsigned char *)subd->parent->str;
888       kv.parent = subd->parent;
889     }
890   else
891     {
892       schema = 0;
893       dp = solvid2data(data, solvid, &schema);
894       if (!dp)
895         return;
896       s = data->repo->pool->solvables + solvid;
897       kv.parent = 0;
898     }
899   keyp = data->schemadata + data->schemata[schema];
900   if (keyname)
901     {
902       /* search for a specific key */
903       for (kp = keyp; *kp; kp++)
904         if (data->keys[*kp].name == keyname)
905           break;
906       if (!*kp)
907         return;
908       dp = forward_to_key(data, *kp, keyp, dp);
909       if (!dp)
910         return;
911       keyp = kp;
912       onekey = 1;
913     }
914   while ((keyid = *keyp++) != 0)
915     {
916       stop = 0;
917       key = data->keys + keyid;
918       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
919
920       if (key->type == REPOKEY_TYPE_DELETED)
921         continue;
922       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
923         {
924           struct subschema_data subd;
925           int nentries;
926           Id schema = 0;
927
928           subd.cbdata = cbdata;
929           subd.s = s;
930           subd.parent = &kv;
931           ddp = data_read_id(ddp, &nentries);
932           kv.num = nentries;
933           kv.entry = 0;
934           kv.eof = 0;
935           while (ddp && nentries > 0)
936             {
937               if (!--nentries)
938                 kv.eof = 1;
939               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
940                 ddp = data_read_id(ddp, &schema);
941               kv.id = schema;
942               kv.str = (char *)ddp;
943               stop = callback(cbdata, s, data, key, &kv);
944               if (stop > SEARCH_NEXT_KEY)
945                 return;
946               if (stop && stop != SEARCH_ENTERSUB)
947                 break;
948               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
949                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
950               ddp = data_skip_schema(data, ddp, schema);
951               kv.entry++;
952             }
953           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
954             {
955               /* sentinel */
956               kv.eof = 2;
957               kv.str = (char *)ddp;
958               stop = callback(cbdata, s, data, key, &kv);
959               if (stop > SEARCH_NEXT_KEY)
960                 return;
961             }
962           if (onekey)
963             return;
964           continue;
965         }
966       kv.entry = 0;
967       do
968         {
969           ddp = data_fetch(ddp, &kv, key);
970           if (!ddp)
971             break;
972           stop = callback(cbdata, s, data, key, &kv);
973           kv.entry++;
974         }
975       while (!kv.eof && !stop);
976       if (onekey || stop > SEARCH_NEXT_KEY)
977         return;
978     }
979 }
980
981 void
982 repodata_setpos_kv(Repodata *data, KeyValue *kv)
983 {
984   Pool *pool = data->repo->pool;
985   if (!kv)
986     pool_clear_pos(pool);
987   else
988     {
989       pool->pos.repo = data->repo;
990       pool->pos.repodataid = data - data->repo->repodata;
991       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
992       pool->pos.schema = kv->id;
993     }
994 }
995
996 /************************************************************************
997  * data iterator functions
998  */
999
1000 static inline Id *
1001 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1002 {
1003   kv->id = keyname;
1004   switch (keyname)
1005     {
1006     case SOLVABLE_NAME:
1007       kv->eof = 1;
1008       return &s->name;
1009     case SOLVABLE_ARCH:
1010       kv->eof = 1;
1011       return &s->arch;
1012     case SOLVABLE_EVR:
1013       kv->eof = 1;
1014       return &s->evr;
1015     case SOLVABLE_VENDOR:
1016       kv->eof = 1;
1017       return &s->vendor;
1018     case SOLVABLE_PROVIDES:
1019       kv->eof = 0;
1020       return s->provides ? s->repo->idarraydata + s->provides : 0;
1021     case SOLVABLE_OBSOLETES:
1022       kv->eof = 0;
1023       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1024     case SOLVABLE_CONFLICTS:
1025       kv->eof = 0;
1026       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1027     case SOLVABLE_REQUIRES:
1028       kv->eof = 0;
1029       return s->requires ? s->repo->idarraydata + s->requires : 0;
1030     case SOLVABLE_RECOMMENDS:
1031       kv->eof = 0;
1032       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1033     case SOLVABLE_SUPPLEMENTS:
1034       kv->eof = 0;
1035       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1036     case SOLVABLE_SUGGESTS:
1037       kv->eof = 0;
1038       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1039     case SOLVABLE_ENHANCES:
1040       kv->eof = 0;
1041       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1042     case RPM_RPMDBID:
1043       kv->eof = 1;
1044       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1045     default:
1046       return 0;
1047     }
1048 }
1049
1050 int
1051 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1052 {
1053   ma->match = match;
1054   ma->flags = flags;
1055   ma->error = 0;
1056   ma->matchdata = 0;
1057   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1058     {
1059       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1060       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1061       if (ma->error)
1062         {
1063           solv_free(ma->matchdata);
1064           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1065         }
1066     }
1067   if ((flags & SEARCH_FILES) != 0 && match)
1068     {
1069       /* prepare basename check */
1070       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1071         {
1072           const char *p = strrchr(match, '/');
1073           ma->matchdata = (void *)(p ? p + 1 : match);
1074         }
1075       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1076         {
1077           const char *p;
1078           for (p = match + strlen(match) - 1; p >= match; p--)
1079             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1080               break;
1081           ma->matchdata = (void *)(p + 1);
1082         }
1083     }
1084   return ma->error;
1085 }
1086
1087 void
1088 datamatcher_free(Datamatcher *ma)
1089 {
1090   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1091     {
1092       regfree(ma->matchdata);
1093       solv_free(ma->matchdata);
1094     }
1095   ma->matchdata = 0;
1096 }
1097
1098 int
1099 datamatcher_match(Datamatcher *ma, const char *str)
1100 {
1101   int l;
1102   switch ((ma->flags & SEARCH_STRINGMASK))
1103     {
1104     case SEARCH_SUBSTRING:
1105       if (ma->flags & SEARCH_NOCASE)
1106         return strcasestr(str, ma->match) != 0;
1107       else
1108         return strstr(str, ma->match) != 0;
1109     case SEARCH_STRING:
1110       if (ma->flags & SEARCH_NOCASE)
1111         return !strcasecmp(ma->match, str);
1112       else
1113         return !strcmp(ma->match, str);
1114     case SEARCH_STRINGSTART:
1115       if (ma->flags & SEARCH_NOCASE)
1116         return !strncasecmp(ma->match, str, strlen(ma->match));
1117       else
1118         return !strncmp(ma->match, str, strlen(ma->match));
1119     case SEARCH_STRINGEND:
1120       l = strlen(str) - strlen(ma->match);
1121       if (l < 0)
1122         return 0;
1123       if (ma->flags & SEARCH_NOCASE)
1124         return !strcasecmp(ma->match, str + l);
1125       else
1126         return !strcmp(ma->match, str + l);
1127     case SEARCH_GLOB:
1128       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1129     case SEARCH_REGEX:
1130       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1131     default:
1132       return 0;
1133     }
1134 }
1135
1136 /* check if the matcher can match the provides basename */
1137
1138 int
1139 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1140 {
1141   int l;
1142   const char *match = ma->matchdata;
1143   if (!match)
1144     return 1;
1145   switch (ma->flags & SEARCH_STRINGMASK)
1146     {
1147     case SEARCH_STRING:
1148       break;
1149     case SEARCH_STRINGEND:
1150       if (match != ma->match)
1151         break;          /* had slash, do exact match on basename */
1152       /* FALLTHROUGH */
1153     case SEARCH_GLOB:
1154       /* check if the basename ends with match */
1155       l = strlen(basename) - strlen(match);
1156       if (l < 0)
1157         return 0;
1158       basename += l;
1159       break;
1160     default:
1161       return 1; /* maybe matches */
1162     }
1163   if ((ma->flags & SEARCH_NOCASE) != 0)
1164     return !strcasecmp(match, basename);
1165   else
1166     return !strcmp(match, basename);
1167 }
1168
1169 int
1170 repodata_filelistfilter_matches(Repodata *data, const char *str)
1171 {
1172   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1173   /* for now hardcoded */
1174   if (strstr(str, "bin/"))
1175     return 1;
1176   if (!strncmp(str, "/etc/", 5))
1177     return 1;
1178   if (!strcmp(str, "/usr/lib/sendmail"))
1179     return 1;
1180   return 0;
1181 }
1182
1183
1184 enum {
1185   di_bye,
1186
1187   di_enterrepo,
1188   di_entersolvable,
1189   di_enterrepodata,
1190   di_enterschema,
1191   di_enterkey,
1192
1193   di_nextattr,
1194   di_nextkey,
1195   di_nextrepodata,
1196   di_nextsolvable,
1197   di_nextrepo,
1198
1199   di_enterarray,
1200   di_nextarrayelement,
1201
1202   di_entersub,
1203   di_leavesub,
1204
1205   di_nextsolvablekey,
1206   di_entersolvablekey,
1207   di_nextsolvableattr
1208 };
1209
1210 /* see dataiterator.h for documentation */
1211 int
1212 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1213 {
1214   memset(di, 0, sizeof(*di));
1215   di->pool = pool;
1216   di->flags = flags & ~SEARCH_THISSOLVID;
1217   if (!pool || (repo && repo->pool != pool))
1218     {
1219       di->state = di_bye;
1220       return -1;
1221     }
1222   if (match)
1223     {
1224       int error;
1225       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1226         {
1227           di->state = di_bye;
1228           return error;
1229         }
1230     }
1231   di->keyname = keyname;
1232   di->keynames[0] = keyname;
1233   dataiterator_set_search(di, repo, p);
1234   return 0;
1235 }
1236
1237 void
1238 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1239 {
1240   *di = *from;
1241   if (di->dupstr)
1242     {
1243       if (di->dupstr == di->kv.str)
1244         {
1245           di->dupstr = solv_malloc(di->dupstrn);
1246           memcpy(di->dupstr, from->dupstr, di->dupstrn);
1247         }
1248       else
1249         {
1250           di->dupstr = 0;
1251           di->dupstrn = 0;
1252         }
1253     }
1254   memset(&di->matcher, 0, sizeof(di->matcher));
1255   if (from->matcher.match)
1256     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1257   if (di->nparents)
1258     {
1259       /* fix pointers */
1260       int i;
1261       for (i = 1; i < di->nparents; i++)
1262         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1263       di->kv.parent = &di->parents[di->nparents - 1].kv;
1264     }
1265 }
1266
1267 int
1268 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1269 {
1270   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1271   datamatcher_free(&di->matcher);
1272   memset(&di->matcher, 0, sizeof(di->matcher));
1273   if (match)
1274     {
1275       int error;
1276       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1277         {
1278           di->state = di_bye;
1279           return error;
1280         }
1281     }
1282   return 0;
1283 }
1284
1285 void
1286 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1287 {
1288   di->repo = repo;
1289   di->repoid = 0;
1290   di->flags &= ~SEARCH_THISSOLVID;
1291   di->nparents = 0;
1292   di->rootlevel = 0;
1293   di->repodataid = 1;
1294   if (!di->pool->urepos)
1295     {
1296       di->state = di_bye;
1297       return;
1298     }
1299   if (!repo)
1300     {
1301       di->repoid = 1;
1302       di->repo = di->pool->repos[di->repoid];
1303     }
1304   di->state = di_enterrepo;
1305   if (p)
1306     dataiterator_jump_to_solvid(di, p);
1307 }
1308
1309 void
1310 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1311 {
1312   di->nkeynames = 0;
1313   di->keyname = keyname;
1314   di->keynames[0] = keyname;
1315 }
1316
1317 void
1318 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1319 {
1320   int i;
1321
1322   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1323     {
1324       di->state = di_bye;       /* sorry */
1325       return;
1326     }
1327   for (i = di->nkeynames + 1; i > 0; i--)
1328     di->keynames[i] = di->keynames[i - 1];
1329   di->keynames[0] = di->keyname = keyname;
1330   di->nkeynames++;
1331 }
1332
1333 void
1334 dataiterator_free(Dataiterator *di)
1335 {
1336   if (di->matcher.match)
1337     datamatcher_free(&di->matcher);
1338   if (di->dupstr)
1339     solv_free(di->dupstr);
1340 }
1341
1342 static inline unsigned char *
1343 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1344 {
1345   Id *keyp = di->keyp;
1346   Repokey *keys = di->data->keys;
1347   unsigned char *dp;
1348
1349   for (keyp = di->keyp; *keyp; keyp++)
1350     if (keys[*keyp].name == keyname)
1351       break;
1352   if (!*keyp)
1353     return 0;
1354   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1355   if (!dp)
1356     return 0;
1357   di->keyp = keyp;
1358   return dp;
1359 }
1360
1361 static int
1362 dataiterator_filelistcheck(Dataiterator *di)
1363 {
1364   int j;
1365   int needcomplete = 0;
1366   Repodata *data = di->data;
1367
1368   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1369     if (!di->matcher.match
1370        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1371            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1372        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1373       needcomplete = 1;
1374   if (data->state != REPODATA_AVAILABLE)
1375     return needcomplete ? 1 : 0;
1376   for (j = 1; j < data->nkeys; j++)
1377     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1378       break;
1379   return j == data->nkeys && !needcomplete ? 0 : 1;
1380 }
1381
1382 int
1383 dataiterator_step(Dataiterator *di)
1384 {
1385   Id schema;
1386
1387   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
1388     unsigned int ddpoff = di->ddp - di->vert_ddp;
1389     di->vert_off += ddpoff;
1390     di->vert_len -= ddpoff;
1391     di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1392     di->vert_storestate = di->data->storestate;
1393     if (!di->ddp)
1394       di->state = di_nextkey;
1395   }
1396   for (;;)
1397     {
1398       switch (di->state)
1399         {
1400         case di_enterrepo: di_enterrepo:
1401           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1402             goto di_nextrepo;
1403           if (!(di->flags & SEARCH_THISSOLVID))
1404             {
1405               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1406               goto di_nextsolvable;
1407             }
1408           /* FALLTHROUGH */
1409
1410         case di_entersolvable: di_entersolvable:
1411           if (di->repodataid)
1412             {
1413               di->repodataid = 1;       /* reset repodata iterator */
1414               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
1415                 {
1416                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1417
1418                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1419                   di->data = 0;
1420                   goto di_entersolvablekey;
1421                 }
1422             }
1423           /* FALLTHROUGH */
1424
1425         case di_enterrepodata: di_enterrepodata:
1426           if (di->repodataid)
1427             {
1428               if (di->repodataid >= di->repo->nrepodata)
1429                 goto di_nextsolvable;
1430               di->data = di->repo->repodata + di->repodataid;
1431             }
1432           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1433             goto di_nextrepodata;
1434           if (!maybe_load_repodata(di->data, di->keyname))
1435             goto di_nextrepodata;
1436           di->dp = solvid2data(di->data, di->solvid, &schema);
1437           if (!di->dp)
1438             goto di_nextrepodata;
1439           if (di->solvid == SOLVID_POS)
1440             di->solvid = di->pool->pos.solvid;
1441           /* reset key iterator */
1442           di->keyp = di->data->schemadata + di->data->schemata[schema];
1443           /* FALLTHROUGH */
1444
1445         case di_enterschema: di_enterschema:
1446           if (di->keyname)
1447             di->dp = dataiterator_find_keyname(di, di->keyname);
1448           if (!di->dp || !*di->keyp)
1449             {
1450               if (di->kv.parent)
1451                 goto di_leavesub;
1452               goto di_nextrepodata;
1453             }
1454           /* FALLTHROUGH */
1455
1456         case di_enterkey: di_enterkey:
1457           di->kv.entry = -1;
1458           di->key = di->data->keys + *di->keyp;
1459           if (!di->dp)
1460             goto di_nextkey;
1461           /* this is get_data() modified to store vert_ data */
1462           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1463             {
1464               Id off, len;
1465               di->dp = data_read_id(di->dp, &off);
1466               di->dp = data_read_id(di->dp, &len);
1467               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1468               di->vert_off = off;
1469               di->vert_len = len;
1470               di->vert_storestate = di->data->storestate;
1471             }
1472           else if (di->key->storage == KEY_STORAGE_INCORE)
1473             {
1474               di->ddp = di->dp;
1475               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1476                 di->dp = data_skip_key(di->data, di->dp, di->key);
1477             }
1478           else
1479             di->ddp = 0;
1480           if (!di->ddp)
1481             goto di_nextkey;
1482           if (di->key->type == REPOKEY_TYPE_DELETED)
1483             goto di_nextkey;
1484           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1485             goto di_enterarray;
1486           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1487             goto di_nextkey;
1488           /* FALLTHROUGH */
1489
1490         case di_nextattr:
1491           di->kv.entry++;
1492           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1493           if (di->kv.eof)
1494             di->state = di_nextkey;
1495           else
1496             di->state = di_nextattr;
1497           break;
1498
1499         case di_nextkey: di_nextkey:
1500           if (!di->keyname && *++di->keyp)
1501             goto di_enterkey;
1502           if (di->kv.parent)
1503             goto di_leavesub;
1504           /* FALLTHROUGH */
1505
1506         case di_nextrepodata: di_nextrepodata:
1507           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1508               goto di_enterrepodata;
1509           /* FALLTHROUGH */
1510
1511         case di_nextsolvable: di_nextsolvable:
1512           if (!(di->flags & SEARCH_THISSOLVID))
1513             {
1514               if (di->solvid < 0)
1515                 di->solvid = di->repo->start;
1516               else
1517                 di->solvid++;
1518               for (; di->solvid < di->repo->end; di->solvid++)
1519                 {
1520                   if (di->pool->solvables[di->solvid].repo == di->repo)
1521                     goto di_entersolvable;
1522                 }
1523             }
1524           /* FALLTHROUGH */
1525
1526         case di_nextrepo: di_nextrepo:
1527           if (di->repoid > 0)
1528             {
1529               di->repoid++;
1530               di->repodataid = 1;
1531               if (di->repoid < di->pool->nrepos)
1532                 {
1533                   di->repo = di->pool->repos[di->repoid];
1534                   goto di_enterrepo;
1535                 }
1536             }
1537         /* FALLTHROUGH */
1538
1539         case di_bye: di_bye:
1540           di->state = di_bye;
1541           return 0;
1542
1543         case di_enterarray: di_enterarray:
1544           if (di->key->name == REPOSITORY_SOLVABLES)
1545             goto di_nextkey;
1546           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1547           di->kv.eof = 0;
1548           di->kv.entry = -1;
1549           /* FALLTHROUGH */
1550
1551         case di_nextarrayelement: di_nextarrayelement:
1552           di->kv.entry++;
1553           if (di->kv.entry)
1554             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1555           if (di->kv.entry == di->kv.num)
1556             {
1557               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1558                 goto di_nextkey;
1559               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1560                 goto di_nextkey;
1561               di->kv.str = (char *)di->ddp;
1562               di->kv.eof = 2;
1563               di->state = di_nextkey;
1564               break;
1565             }
1566           if (di->kv.entry == di->kv.num - 1)
1567             di->kv.eof = 1;
1568           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1569             di->ddp = data_read_id(di->ddp, &di->kv.id);
1570           di->kv.str = (char *)di->ddp;
1571           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1572             goto di_entersub;
1573           if ((di->flags & SEARCH_SUB) != 0)
1574             di->state = di_entersub;
1575           else
1576             di->state = di_nextarrayelement;
1577           break;
1578
1579         case di_entersub: di_entersub:
1580           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1581             goto di_nextarrayelement;   /* sorry, full */
1582           di->parents[di->nparents].kv = di->kv;
1583           di->parents[di->nparents].dp = di->dp;
1584           di->parents[di->nparents].keyp = di->keyp;
1585           di->dp = (unsigned char *)di->kv.str;
1586           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1587           memset(&di->kv, 0, sizeof(di->kv));
1588           di->kv.parent = &di->parents[di->nparents].kv;
1589           di->nparents++;
1590           di->keyname = di->keynames[di->nparents - di->rootlevel];
1591           goto di_enterschema;
1592
1593         case di_leavesub: di_leavesub:
1594           if (di->nparents - 1 < di->rootlevel)
1595             goto di_bye;
1596           di->nparents--;
1597           di->dp = di->parents[di->nparents].dp;
1598           di->kv = di->parents[di->nparents].kv;
1599           di->keyp = di->parents[di->nparents].keyp;
1600           di->key = di->data->keys + *di->keyp;
1601           di->ddp = (unsigned char *)di->kv.str;
1602           di->keyname = di->keynames[di->nparents - di->rootlevel];
1603           goto di_nextarrayelement;
1604
1605         /* special solvable attr handling follows */
1606
1607         case di_nextsolvablekey: di_nextsolvablekey:
1608           if (di->keyname || di->key->name == RPM_RPMDBID)
1609             goto di_enterrepodata;
1610           di->key++;
1611           /* FALLTHROUGH */
1612
1613         case di_entersolvablekey: di_entersolvablekey:
1614           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1615           if (!di->idp || !*di->idp)
1616             goto di_nextsolvablekey;
1617           if (di->kv.eof)
1618             {
1619               /* not an array */
1620               di->kv.id = *di->idp;
1621               di->kv.num = *di->idp;    /* for rpmdbid */
1622               di->kv.num2 = 0;          /* for rpmdbid */
1623               di->kv.entry = 0;
1624               di->state = di_nextsolvablekey;
1625               break;
1626             }
1627           di->kv.entry = -1;
1628           /* FALLTHROUGH */
1629
1630         case di_nextsolvableattr:
1631           di->state = di_nextsolvableattr;
1632           di->kv.id = *di->idp++;
1633           di->kv.entry++;
1634           if (!*di->idp)
1635             {
1636               di->kv.eof = 1;
1637               di->state = di_nextsolvablekey;
1638             }
1639           break;
1640
1641         }
1642
1643       if (di->matcher.match)
1644         {
1645           /* simple pre-check so that we don't need to stringify */
1646           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1647             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1648               continue;
1649           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1650             {
1651               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1652                 return 1;
1653               continue;
1654             }
1655           if (!datamatcher_match(&di->matcher, di->kv.str))
1656             continue;
1657         }
1658       else
1659         {
1660           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1661             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1662         }
1663       /* found something! */
1664       return 1;
1665     }
1666 }
1667
1668 void
1669 dataiterator_entersub(Dataiterator *di)
1670 {
1671   if (di->state == di_nextarrayelement)
1672     di->state = di_entersub;
1673 }
1674
1675 void
1676 dataiterator_setpos(Dataiterator *di)
1677 {
1678   if (di->kv.eof == 2)
1679     {
1680       pool_clear_pos(di->pool);
1681       return;
1682     }
1683   di->pool->pos.solvid = di->solvid;
1684   di->pool->pos.repo = di->repo;
1685   di->pool->pos.repodataid = di->data - di->repo->repodata;
1686   di->pool->pos.schema = di->kv.id;
1687   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1688 }
1689
1690 void
1691 dataiterator_setpos_parent(Dataiterator *di)
1692 {
1693   if (!di->kv.parent || di->kv.parent->eof == 2)
1694     {
1695       pool_clear_pos(di->pool);
1696       return;
1697     }
1698   di->pool->pos.solvid = di->solvid;
1699   di->pool->pos.repo = di->repo;
1700   di->pool->pos.repodataid = di->data - di->repo->repodata;
1701   di->pool->pos.schema = di->kv.parent->id;
1702   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1703 }
1704
1705 /* clones just the position, not the search keys/matcher */
1706 void
1707 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1708 {
1709   di->state = from->state;
1710   di->flags &= ~SEARCH_THISSOLVID;
1711   di->flags |= (from->flags & SEARCH_THISSOLVID);
1712   di->repo = from->repo;
1713   di->data = from->data;
1714   di->dp = from->dp;
1715   di->ddp = from->ddp;
1716   di->idp = from->idp;
1717   di->keyp = from->keyp;
1718   di->key = from->key;
1719   di->kv = from->kv;
1720   di->repodataid = from->repodataid;
1721   di->solvid = from->solvid;
1722   di->repoid = from->repoid;
1723   di->rootlevel = from->rootlevel;
1724   memcpy(di->parents, from->parents, sizeof(from->parents));
1725   di->nparents = from->nparents;
1726   if (di->nparents)
1727     {
1728       int i;
1729       for (i = 1; i < di->nparents; i++)
1730         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1731       di->kv.parent = &di->parents[di->nparents - 1].kv;
1732     }
1733   di->dupstr = 0;
1734   di->dupstrn = 0;
1735   if (from->dupstr && from->dupstr == from->kv.str)
1736     {
1737       di->dupstrn = from->dupstrn;
1738       di->dupstr = solv_malloc(from->dupstrn);
1739       memcpy(di->dupstr, from->dupstr, di->dupstrn);
1740     }
1741 }
1742
1743 void
1744 dataiterator_seek(Dataiterator *di, int whence)
1745 {
1746   if ((whence & DI_SEEK_STAY) != 0)
1747     di->rootlevel = di->nparents;
1748   switch (whence & ~DI_SEEK_STAY)
1749     {
1750     case DI_SEEK_CHILD:
1751       if (di->state != di_nextarrayelement)
1752         break;
1753       if ((whence & DI_SEEK_STAY) != 0)
1754         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1755       di->state = di_entersub;
1756       break;
1757     case DI_SEEK_PARENT:
1758       if (!di->nparents)
1759         {
1760           di->state = di_bye;
1761           break;
1762         }
1763       di->nparents--;
1764       if (di->rootlevel > di->nparents)
1765         di->rootlevel = di->nparents;
1766       di->dp = di->parents[di->nparents].dp;
1767       di->kv = di->parents[di->nparents].kv;
1768       di->keyp = di->parents[di->nparents].keyp;
1769       di->key = di->data->keys + *di->keyp;
1770       di->ddp = (unsigned char *)di->kv.str;
1771       di->keyname = di->keynames[di->nparents - di->rootlevel];
1772       di->state = di_nextarrayelement;
1773       break;
1774     case DI_SEEK_REWIND:
1775       if (!di->nparents)
1776         {
1777           di->state = di_bye;
1778           break;
1779         }
1780       di->dp = (unsigned char *)di->kv.parent->str;
1781       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1782       di->state = di_enterschema;
1783       break;
1784     default:
1785       break;
1786     }
1787 }
1788
1789 void
1790 dataiterator_skip_attribute(Dataiterator *di)
1791 {
1792   if (di->state == di_nextsolvableattr)
1793     di->state = di_nextsolvablekey;
1794   else
1795     di->state = di_nextkey;
1796 }
1797
1798 void
1799 dataiterator_skip_solvable(Dataiterator *di)
1800 {
1801   di->nparents = 0;
1802   di->kv.parent = 0;
1803   di->rootlevel = 0;
1804   di->keyname = di->keynames[0];
1805   di->state = di_nextsolvable;
1806 }
1807
1808 void
1809 dataiterator_skip_repo(Dataiterator *di)
1810 {
1811   di->nparents = 0;
1812   di->kv.parent = 0;
1813   di->rootlevel = 0;
1814   di->keyname = di->keynames[0];
1815   di->state = di_nextrepo;
1816 }
1817
1818 void
1819 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1820 {
1821   di->nparents = 0;
1822   di->kv.parent = 0;
1823   di->rootlevel = 0;
1824   di->keyname = di->keynames[0];
1825   if (solvid == SOLVID_POS)
1826     {
1827       di->repo = di->pool->pos.repo;
1828       if (!di->repo)
1829         {
1830           di->state = di_bye;
1831           return;
1832         }
1833       di->repoid = 0;
1834       di->data = di->repo->repodata + di->pool->pos.repodataid;
1835       di->repodataid = 0;
1836       di->solvid = solvid;
1837       di->state = di_enterrepo;
1838       di->flags |= SEARCH_THISSOLVID;
1839       return;
1840     }
1841   if (solvid > 0)
1842     {
1843       di->repo = di->pool->solvables[solvid].repo;
1844       di->repoid = 0;
1845     }
1846   else if (di->repoid > 0)
1847     {
1848       if (!di->pool->urepos)
1849         {
1850           di->state = di_bye;
1851           return;
1852         }
1853       di->repoid = 1;
1854       di->repo = di->pool->repos[di->repoid];
1855     }
1856   di->repodataid = 1;
1857   di->solvid = solvid;
1858   if (solvid)
1859     di->flags |= SEARCH_THISSOLVID;
1860   di->state = di_enterrepo;
1861 }
1862
1863 void
1864 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1865 {
1866   di->nparents = 0;
1867   di->kv.parent = 0;
1868   di->rootlevel = 0;
1869   di->repo = repo;
1870   di->repoid = 0;       /* 0 means stay at repo */
1871   di->repodataid = 1;
1872   di->solvid = 0;
1873   di->flags &= ~SEARCH_THISSOLVID;
1874   di->state = di_enterrepo;
1875 }
1876
1877 int
1878 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1879 {
1880   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1881     return 0;
1882   if (!ma)
1883     return 1;
1884   return datamatcher_match(ma, di->kv.str);
1885 }
1886
1887 void
1888 dataiterator_strdup(Dataiterator *di)
1889 {
1890   int l = -1;
1891
1892   if (!di->kv.str || di->kv.str == di->dupstr)
1893     return;
1894   switch (di->key->type)
1895     {
1896     case REPOKEY_TYPE_MD5:
1897     case REPOKEY_TYPE_SHA1:
1898     case REPOKEY_TYPE_SHA256:
1899     case REPOKEY_TYPE_DIRSTRARRAY:
1900       if (di->kv.num)   /* was it stringified into tmp space? */
1901         l = strlen(di->kv.str) + 1;
1902       break;
1903     default:
1904       break;
1905     }
1906   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1907     {
1908       switch (di->key->type)
1909         {
1910         case REPOKEY_TYPE_STR:
1911         case REPOKEY_TYPE_DIRSTRARRAY:
1912           l = strlen(di->kv.str) + 1;
1913           break;
1914         case REPOKEY_TYPE_MD5:
1915           l = SIZEOF_MD5;
1916           break;
1917         case REPOKEY_TYPE_SHA1:
1918           l = SIZEOF_SHA1;
1919           break;
1920         case REPOKEY_TYPE_SHA256:
1921           l = SIZEOF_SHA256;
1922           break;
1923         case REPOKEY_TYPE_BINARY:
1924           l = di->kv.num;
1925           break;
1926         }
1927     }
1928   if (l >= 0)
1929     {
1930       if (!di->dupstrn || di->dupstrn < l)
1931         {
1932           di->dupstrn = l + 16;
1933           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
1934         }
1935       if (l)
1936         memcpy(di->dupstr, di->kv.str, l);
1937       di->kv.str = di->dupstr;
1938     }
1939 }
1940
1941 /************************************************************************
1942  * data modify functions
1943  */
1944
1945 /* extend repodata so that it includes solvables p */
1946 void
1947 repodata_extend(Repodata *data, Id p)
1948 {
1949   if (data->start == data->end)
1950     data->start = data->end = p;
1951   if (p >= data->end)
1952     {
1953       int old = data->end - data->start;
1954       int new = p - data->end + 1;
1955       if (data->attrs)
1956         {
1957           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1958           memset(data->attrs + old, 0, new * sizeof(Id *));
1959         }
1960       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1961       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1962       data->end = p + 1;
1963     }
1964   if (p < data->start)
1965     {
1966       int old = data->end - data->start;
1967       int new = data->start - p;
1968       if (data->attrs)
1969         {
1970           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1971           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1972           memset(data->attrs, 0, new * sizeof(Id *));
1973         }
1974       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1975       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1976       memset(data->incoreoffset, 0, new * sizeof(Id));
1977       data->start = p;
1978     }
1979 }
1980
1981 /* shrink end of repodata */
1982 void
1983 repodata_shrink(Repodata *data, int end)
1984 {
1985   int i;
1986
1987   if (data->end <= end)
1988     return;
1989   if (data->start >= end)
1990     {
1991       if (data->attrs)
1992         {
1993           for (i = 0; i < data->end - data->start; i++)
1994             solv_free(data->attrs[i]);
1995           data->attrs = solv_free(data->attrs);
1996         }
1997       data->incoreoffset = solv_free(data->incoreoffset);
1998       data->start = data->end = 0;
1999       return;
2000     }
2001   if (data->attrs)
2002     {
2003       for (i = end; i < data->end; i++)
2004         solv_free(data->attrs[i - data->start]);
2005       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2006     }
2007   if (data->incoreoffset)
2008     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2009   data->end = end;
2010 }
2011
2012 /* extend repodata so that it includes solvables from start to start + num - 1 */
2013 void
2014 repodata_extend_block(Repodata *data, Id start, Id num)
2015 {
2016   if (!num)
2017     return;
2018   if (!data->incoreoffset)
2019     {
2020       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2021       data->start = start;
2022       data->end = start + num;
2023       return;
2024     }
2025   repodata_extend(data, start);
2026   if (num > 1)
2027     repodata_extend(data, start + num - 1);
2028 }
2029
2030 /**********************************************************************/
2031
2032
2033 #define REPODATA_ATTRS_BLOCK 31
2034 #define REPODATA_ATTRDATA_BLOCK 1023
2035 #define REPODATA_ATTRIDDATA_BLOCK 63
2036 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2037
2038
2039 Id
2040 repodata_new_handle(Repodata *data)
2041 {
2042   if (!data->nxattrs)
2043     {
2044       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2045       data->nxattrs = 2;        /* -1: SOLVID_META */
2046     }
2047   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2048   data->xattrs[data->nxattrs] = 0;
2049   return -(data->nxattrs++);
2050 }
2051
2052 static inline Id **
2053 repodata_get_attrp(Repodata *data, Id handle)
2054 {
2055   if (handle < 0)
2056     {
2057       if (handle == SOLVID_META && !data->xattrs)
2058         {
2059           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2060           data->nxattrs = 2;
2061         }
2062       return data->xattrs - handle;
2063     }
2064   if (handle < data->start || handle >= data->end)
2065     repodata_extend(data, handle);
2066   if (!data->attrs)
2067     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2068   return data->attrs + (handle - data->start);
2069 }
2070
2071 static void
2072 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2073 {
2074   Id *pp;
2075   Id *ap, **app;
2076   int i;
2077
2078   app = repodata_get_attrp(data, handle);
2079   ap = *app;
2080   i = 0;
2081   if (ap)
2082     {
2083       /* Determine equality based on the name only, allows us to change
2084          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2085       for (pp = ap; *pp; pp += 2)
2086         if (data->keys[*pp].name == data->keys[keyid].name)
2087           break;
2088       if (*pp)
2089         {
2090           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2091             {
2092               pp[0] = keyid;
2093               pp[1] = val;
2094             }
2095           return;
2096         }
2097       i = pp - ap;
2098     }
2099   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2100   *app = ap;
2101   pp = ap + i;
2102   *pp++ = keyid;
2103   *pp++ = val;
2104   *pp = 0;
2105 }
2106
2107
2108 static void
2109 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2110 {
2111   Id keyid;
2112
2113   keyid = repodata_key2id(data, key, 1);
2114   repodata_insert_keyid(data, solvid, keyid, val, 1);
2115 }
2116
2117 void
2118 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2119 {
2120   Repokey key;
2121   key.name = keyname;
2122   key.type = REPOKEY_TYPE_ID;
2123   key.size = 0;
2124   key.storage = KEY_STORAGE_INCORE;
2125   repodata_set(data, solvid, &key, id);
2126 }
2127
2128 void
2129 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2130 {
2131   Repokey key;
2132   key.name = keyname;
2133   key.type = REPOKEY_TYPE_NUM;
2134   key.size = 0;
2135   key.storage = KEY_STORAGE_INCORE;
2136   if (num >= 0x80000000)
2137     {
2138       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2139       data->attrnum64data[data->attrnum64datalen] = num;
2140       num = 0x80000000 | data->attrnum64datalen++;
2141     }
2142   repodata_set(data, solvid, &key, (Id)num);
2143 }
2144
2145 void
2146 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2147 {
2148   Repokey key;
2149   Id id;
2150   if (data->localpool)
2151     id = stringpool_str2id(&data->spool, str, 1);
2152   else
2153     id = pool_str2id(data->repo->pool, str, 1);
2154   key.name = keyname;
2155   key.type = REPOKEY_TYPE_ID;
2156   key.size = 0;
2157   key.storage = KEY_STORAGE_INCORE;
2158   repodata_set(data, solvid, &key, id);
2159 }
2160
2161 void
2162 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2163 {
2164   Repokey key;
2165   key.name = keyname;
2166   key.type = REPOKEY_TYPE_CONSTANT;
2167   key.size = constant;
2168   key.storage = KEY_STORAGE_INCORE;
2169   repodata_set(data, solvid, &key, 0);
2170 }
2171
2172 void
2173 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2174 {
2175   Repokey key;
2176   key.name = keyname;
2177   key.type = REPOKEY_TYPE_CONSTANTID;
2178   key.size = id;
2179   key.storage = KEY_STORAGE_INCORE;
2180   repodata_set(data, solvid, &key, 0);
2181 }
2182
2183 void
2184 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2185 {
2186   Repokey key;
2187   key.name = keyname;
2188   key.type = REPOKEY_TYPE_VOID;
2189   key.size = 0;
2190   key.storage = KEY_STORAGE_INCORE;
2191   repodata_set(data, solvid, &key, 0);
2192 }
2193
2194 void
2195 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2196 {
2197   Repokey key;
2198   int l;
2199
2200   l = strlen(str) + 1;
2201   key.name = keyname;
2202   key.type = REPOKEY_TYPE_STR;
2203   key.size = 0;
2204   key.storage = KEY_STORAGE_INCORE;
2205   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2206   memcpy(data->attrdata + data->attrdatalen, str, l);
2207   repodata_set(data, solvid, &key, data->attrdatalen);
2208   data->attrdatalen += l;
2209 }
2210
2211 void
2212 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2213 {
2214   Repokey key;
2215   unsigned char *dp;
2216
2217   if (len < 0)
2218     return;
2219   key.name = keyname;
2220   key.type = REPOKEY_TYPE_BINARY;
2221   key.size = 0;
2222   key.storage = KEY_STORAGE_INCORE;
2223   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2224   dp = data->attrdata + data->attrdatalen;
2225   if (len >= (1 << 14))
2226     {
2227       if (len >= (1 << 28))
2228         *dp++ = (len >> 28) | 128;
2229       if (len >= (1 << 21))
2230         *dp++ = (len >> 21) | 128;
2231       *dp++ = (len >> 14) | 128;
2232     }
2233   if (len >= (1 << 7))
2234     *dp++ = (len >> 7) | 128;
2235   *dp++ = len & 127;
2236   if (len)
2237     memcpy(dp, buf, len);
2238   repodata_set(data, solvid, &key, data->attrdatalen);
2239   data->attrdatalen = dp + len - data->attrdata;
2240 }
2241
2242 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2243  * so that the caller can append entrysize new elements plus the termination zero there */
2244 static void
2245 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2246 {
2247   int oldsize;
2248   Id *ida, *pp, **ppp;
2249
2250   /* check if it is the same as last time, this speeds things up a lot */
2251   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2252     {
2253       /* great! just append the new data */
2254       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2255       data->attriddatalen--;    /* overwrite terminating 0  */
2256       data->lastdatalen += entrysize;
2257       return;
2258     }
2259
2260   ppp = repodata_get_attrp(data, handle);
2261   pp = *ppp;
2262   if (pp)
2263     {
2264       for (; *pp; pp += 2)
2265         if (data->keys[*pp].name == keyname)
2266           break;
2267     }
2268   if (!pp || !*pp || data->keys[*pp].type != keytype)
2269     {
2270       /* not found. allocate new key */
2271       Repokey key;
2272       Id keyid;
2273       key.name = keyname;
2274       key.type = keytype;
2275       key.size = 0;
2276       key.storage = KEY_STORAGE_INCORE;
2277       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2278       keyid = repodata_key2id(data, &key, 1);
2279       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2280       data->lasthandle = handle;
2281       data->lastkey = keyid;
2282       data->lastdatalen = data->attriddatalen + entrysize + 1;
2283       return;
2284     }
2285   oldsize = 0;
2286   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2287     oldsize += entrysize;
2288   if (ida + 1 == data->attriddata + data->attriddatalen)
2289     {
2290       /* this was the last entry, just append it */
2291       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2292       data->attriddatalen--;    /* overwrite terminating 0  */
2293     }
2294   else
2295     {
2296       /* too bad. move to back. */
2297       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2298       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2299       pp[1] = data->attriddatalen;
2300       data->attriddatalen += oldsize;
2301     }
2302   data->lasthandle = handle;
2303   data->lastkey = *pp;
2304   data->lastdatalen = data->attriddatalen + entrysize + 1;
2305 }
2306
2307 void
2308 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2309                       const unsigned char *str)
2310 {
2311   Repokey key;
2312   int l;
2313
2314   if (!(l = solv_chksum_len(type)))
2315     return;
2316   key.name = keyname;
2317   key.type = type;
2318   key.size = 0;
2319   key.storage = KEY_STORAGE_INCORE;
2320   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2321   memcpy(data->attrdata + data->attrdatalen, str, l);
2322   repodata_set(data, solvid, &key, data->attrdatalen);
2323   data->attrdatalen += l;
2324 }
2325
2326 void
2327 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2328                       const char *str)
2329 {
2330   unsigned char buf[64];
2331   int l;
2332
2333   if (!(l = solv_chksum_len(type)))
2334     return;
2335   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2336     return;
2337   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2338 }
2339
2340 const char *
2341 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2342 {
2343   int l;
2344
2345   if (!(l = solv_chksum_len(type)))
2346     return "";
2347   return pool_bin2hex(data->repo->pool, buf, l);
2348 }
2349
2350 /* rpm filenames don't contain the epoch, so strip it */
2351 static inline const char *
2352 evrid2vrstr(Pool *pool, Id evrid)
2353 {
2354   const char *p, *evr = pool_id2str(pool, evrid);
2355   if (!evr)
2356     return evr;
2357   for (p = evr; *p >= '0' && *p <= '9'; p++)
2358     ;
2359   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2360 }
2361
2362 static inline void
2363 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2364 {
2365   Id id;
2366   if (data->localpool)
2367     id = stringpool_strn2id(&data->spool, str, l, 1);
2368   else
2369     id = pool_strn2id(data->repo->pool, str, l, 1);
2370   repodata_set_id(data, solvid, keyname, id);
2371 }
2372
2373 static inline void
2374 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2375 {
2376   if (!str[l])
2377     repodata_set_str(data, solvid, keyname, str);
2378   else
2379     {
2380       char *s = solv_strdup(str);
2381       s[l] = 0;
2382       repodata_set_str(data, solvid, keyname, s);
2383       free(s);
2384     }
2385 }
2386
2387 void
2388 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2389 {
2390   Pool *pool = data->repo->pool;
2391   Solvable *s;
2392   const char *str, *fp;
2393   int l = 0;
2394
2395   if (medianr)
2396     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2397   if (!dir)
2398     {
2399       if ((dir = strrchr(file, '/')) != 0)
2400         {
2401           l = dir - file;
2402           dir = file;
2403           file = dir + l + 1;
2404           if (!l)
2405             l++;
2406         }
2407     }
2408   else
2409     l = strlen(dir);
2410   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2411     {
2412       dir += 2;
2413       l -= 2;
2414     }
2415   if (l == 1 && dir[0] == '.')
2416     l = 0;
2417   s = pool->solvables + solvid;
2418   if (dir && l)
2419     {
2420       str = pool_id2str(pool, s->arch);
2421       if (!strncmp(dir, str, l) && !str[l])
2422         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2423       else
2424         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2425     }
2426   fp = file;
2427   str = pool_id2str(pool, s->name);
2428   l = strlen(str);
2429   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2430     {
2431       fp += l + 1;
2432       str = evrid2vrstr(pool, s->evr);
2433       l = strlen(str);
2434       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2435         {
2436           fp += l + 1;
2437           str = pool_id2str(pool, s->arch);
2438           l = strlen(str);
2439           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2440             {
2441               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2442               return;
2443             }
2444         }
2445     }
2446   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2447 }
2448
2449 /* XXX: medianr is currently not stored */
2450 void
2451 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2452 {
2453   int l = 0;
2454   const char *evr, *suf, *s;
2455
2456   if (!dir)
2457     {
2458       if ((dir = strrchr(file, '/')) != 0)
2459         {
2460           l = dir - file;
2461           dir = file;
2462           file = dir + l + 1;
2463           if (!l)
2464             l++;
2465         }
2466     }
2467   else
2468     l = strlen(dir);
2469   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2470     {
2471       dir += 2;
2472       l -= 2;
2473     }
2474   if (l == 1 && dir[0] == '.')
2475     l = 0;
2476   if (dir && l)
2477     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2478   evr = strchr(file, '-');
2479   if (evr)
2480     {
2481       for (s = evr - 1; s > file; s--)
2482         if (*s == '-')
2483           {
2484             evr = s;
2485             break;
2486           }
2487     }
2488   suf = strrchr(file, '.');
2489   if (suf)
2490     {
2491       for (s = suf - 1; s > file; s--)
2492         if (*s == '.')
2493           {
2494             suf = s;
2495             break;
2496           }
2497       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2498         {
2499           /* We accept one more item as suffix.  */
2500           for (s = suf - 1; s > file; s--)
2501             if (*s == '.')
2502               {
2503                 suf = s;
2504                 break;
2505               }
2506         }
2507     }
2508   if (!evr)
2509     suf = 0;
2510   if (suf && evr && suf < evr)
2511     suf = 0;
2512   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2513   if (evr)
2514     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2515   if (suf)
2516     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2517 }
2518
2519 void
2520 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2521 {
2522   Pool *pool = data->repo->pool;
2523   Solvable *s = pool->solvables + solvid;
2524   const char *p, *sevr, *sarch, *name, *evr;
2525
2526   p = strrchr(sourcepkg, '.');
2527   if (!p || strcmp(p, ".rpm") != 0)
2528     {
2529       if (*sourcepkg)
2530         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2531       return;
2532     }
2533   p--;
2534   while (p > sourcepkg && *p != '.')
2535     p--;
2536   if (*p != '.' || p == sourcepkg)
2537     return;
2538   sarch = p-- + 1;
2539   while (p > sourcepkg && *p != '-')
2540     p--;
2541   if (*p != '-' || p == sourcepkg)
2542     return;
2543   p--;
2544   while (p > sourcepkg && *p != '-')
2545     p--;
2546   if (*p != '-' || p == sourcepkg)
2547     return;
2548   sevr = p + 1;
2549   pool = s->repo->pool;
2550
2551   name = pool_id2str(pool, s->name);
2552   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2553     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2554   else
2555     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2556
2557   evr = evrid2vrstr(pool, s->evr);
2558   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2559     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2560   else
2561     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2562
2563   if (!strcmp(sarch, "src.rpm"))
2564     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2565   else if (!strcmp(sarch, "nosrc.rpm"))
2566     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2567   else
2568     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2569 }
2570
2571 void
2572 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2573 {
2574   Repokey key;
2575   int i;
2576
2577   key.name = keyname;
2578   key.type = REPOKEY_TYPE_IDARRAY;
2579   key.size = 0;
2580   key.storage = KEY_STORAGE_INCORE;
2581   repodata_set(data, solvid, &key, data->attriddatalen);
2582   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2583   for (i = 0; i < q->count; i++)
2584     data->attriddata[data->attriddatalen++] = q->elements[i];
2585   data->attriddata[data->attriddatalen++] = 0;
2586 }
2587
2588 void
2589 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2590 {
2591   assert(dir);
2592 #if 0
2593 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2594 #endif
2595   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2596   data->attriddata[data->attriddatalen++] = dir;
2597   data->attriddata[data->attriddatalen++] = num;
2598   data->attriddata[data->attriddatalen++] = num2;
2599   data->attriddata[data->attriddatalen++] = 0;
2600 }
2601
2602 void
2603 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2604 {
2605   Id stroff;
2606   int l;
2607
2608   assert(dir);
2609   l = strlen(str) + 1;
2610   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2611   memcpy(data->attrdata + data->attrdatalen, str, l);
2612   stroff = data->attrdatalen;
2613   data->attrdatalen += l;
2614
2615 #if 0
2616 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2617 #endif
2618   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2619   data->attriddata[data->attriddatalen++] = dir;
2620   data->attriddata[data->attriddatalen++] = stroff;
2621   data->attriddata[data->attriddatalen++] = 0;
2622 }
2623
2624 void
2625 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2626 {
2627 #if 0
2628 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2629 #endif
2630   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2631   data->attriddata[data->attriddatalen++] = id;
2632   data->attriddata[data->attriddatalen++] = 0;
2633 }
2634
2635 void
2636 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2637                            const char *str)
2638 {
2639   Id id;
2640   if (data->localpool)
2641     id = stringpool_str2id(&data->spool, str, 1);
2642   else
2643     id = pool_str2id(data->repo->pool, str, 1);
2644   repodata_add_idarray(data, solvid, keyname, id);
2645 }
2646
2647 void
2648 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2649 {
2650   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2651   data->attriddata[data->attriddatalen++] = ghandle;
2652   data->attriddata[data->attriddatalen++] = 0;
2653 }
2654
2655 void
2656 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2657 {
2658   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2659   data->attriddata[data->attriddatalen++] = ghandle;
2660   data->attriddata[data->attriddatalen++] = 0;
2661 }
2662
2663 void
2664 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2665 {
2666   Id *pp, *ap, **app;
2667   app = repodata_get_attrp(data, solvid);
2668   ap = *app;
2669   if (!ap)
2670     return;
2671   for (; *ap; ap += 2)
2672     if (data->keys[*ap].name == keyname)
2673       break;
2674   if (!*ap)
2675     return;
2676   pp = ap;
2677   ap += 2;
2678   for (; *ap; ap += 2)
2679     {
2680       if (data->keys[*ap].name == keyname)
2681         continue;
2682       *pp++ = ap[0];
2683       *pp++ = ap[1];
2684     }
2685   *pp = 0;
2686 }
2687
2688 /* XXX: does not work correctly, needs fix in iterators! */
2689 void
2690 repodata_unset(Repodata *data, Id solvid, Id keyname)
2691 {
2692   Repokey key;
2693   key.name = keyname;
2694   key.type = REPOKEY_TYPE_DELETED;
2695   key.size = 0;
2696   key.storage = KEY_STORAGE_INCORE;
2697   repodata_set(data, solvid, &key, 0);
2698 }
2699
2700 /* add all (uninternalized) attrs from src to dest */
2701 void
2702 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2703 {
2704   Id *keyp;
2705   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2706     return;
2707   for (; *keyp; keyp += 2)
2708     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2709 }
2710
2711 /* add some (uninternalized) attrs from src to dest */
2712 void
2713 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2714 {
2715   Id *keyp;
2716   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2717     return;
2718   for (; *keyp; keyp += 2)
2719     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2720       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2721 }
2722
2723 /* swap (uninternalized) attrs from src and dest */
2724 void
2725 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2726 {
2727   Id *tmpattrs;
2728   if (!data->attrs || dest == src)
2729     return;
2730   tmpattrs = data->attrs[dest - data->start];
2731   data->attrs[dest - data->start] = data->attrs[src - data->start];
2732   data->attrs[src - data->start] = tmpattrs;
2733 }
2734
2735
2736 /**********************************************************************/
2737
2738 /* TODO: unify with repo_write and repo_solv! */
2739
2740 #define EXTDATA_BLOCK 1023
2741
2742 struct extdata {
2743   unsigned char *buf;
2744   int len;
2745 };
2746
2747 static void
2748 data_addid(struct extdata *xd, Id sx)
2749 {
2750   unsigned int x = (unsigned int)sx;
2751   unsigned char *dp;
2752
2753   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2754   dp = xd->buf + xd->len;
2755
2756   if (x >= (1 << 14))
2757     {
2758       if (x >= (1 << 28))
2759         *dp++ = (x >> 28) | 128;
2760       if (x >= (1 << 21))
2761         *dp++ = (x >> 21) | 128;
2762       *dp++ = (x >> 14) | 128;
2763     }
2764   if (x >= (1 << 7))
2765     *dp++ = (x >> 7) | 128;
2766   *dp++ = x & 127;
2767   xd->len = dp - xd->buf;
2768 }
2769
2770 static void
2771 data_addid64(struct extdata *xd, unsigned long long x)
2772 {
2773   if (x >= 0x100000000)
2774     {
2775       if ((x >> 35) != 0)
2776         {
2777           data_addid(xd, (Id)(x >> 35));
2778           xd->buf[xd->len - 1] |= 128;
2779         }
2780       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2781       xd->buf[xd->len - 5] = (x >> 28) | 128;
2782     }
2783   else
2784     data_addid(xd, (Id)x);
2785 }
2786
2787 static void
2788 data_addideof(struct extdata *xd, Id sx, int eof)
2789 {
2790   unsigned int x = (unsigned int)sx;
2791   unsigned char *dp;
2792
2793   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2794   dp = xd->buf + xd->len;
2795
2796   if (x >= (1 << 13))
2797     {
2798       if (x >= (1 << 27))
2799         *dp++ = (x >> 27) | 128;
2800       if (x >= (1 << 20))
2801         *dp++ = (x >> 20) | 128;
2802       *dp++ = (x >> 13) | 128;
2803     }
2804   if (x >= (1 << 6))
2805     *dp++ = (x >> 6) | 128;
2806   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2807   xd->len = dp - xd->buf;
2808 }
2809
2810 static void
2811 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2812 {
2813   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2814   memcpy(xd->buf + xd->len, blob, len);
2815   xd->len += len;
2816 }
2817
2818 /*********************************/
2819
2820 /* internalalize some key into incore/vincore data */
2821
2822 static void
2823 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2824                        struct extdata *newvincore,
2825                        Id *schema,
2826                        Repokey *key, Id val)
2827 {
2828   Id *ida;
2829   struct extdata *xd;
2830   unsigned int oldvincorelen = 0;
2831   Id schemaid, *sp;
2832
2833   xd = newincore;
2834   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2835     {
2836       xd = newvincore;
2837       oldvincorelen = xd->len;
2838     }
2839   switch (key->type)
2840     {
2841     case REPOKEY_TYPE_VOID:
2842     case REPOKEY_TYPE_CONSTANT:
2843     case REPOKEY_TYPE_CONSTANTID:
2844       break;
2845     case REPOKEY_TYPE_STR:
2846       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2847       break;
2848     case REPOKEY_TYPE_MD5:
2849       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2850       break;
2851     case REPOKEY_TYPE_SHA1:
2852       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2853       break;
2854     case REPOKEY_TYPE_SHA256:
2855       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2856       break;
2857     case REPOKEY_TYPE_NUM:
2858       if (val & 0x80000000)
2859         {
2860           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2861           break;
2862         }
2863       /* FALLTHROUGH */
2864     case REPOKEY_TYPE_ID:
2865     case REPOKEY_TYPE_DIR:
2866       data_addid(xd, val);
2867       break;
2868     case REPOKEY_TYPE_BINARY:
2869       {
2870         Id len;
2871         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2872         dp += (unsigned int)len;
2873         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2874       }
2875       break;
2876     case REPOKEY_TYPE_IDARRAY:
2877       for (ida = data->attriddata + val; *ida; ida++)
2878         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2879       break;
2880     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2881       for (ida = data->attriddata + val; *ida; ida += 3)
2882         {
2883           data_addid(xd, ida[0]);
2884           data_addid(xd, ida[1]);
2885           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2886         }
2887       break;
2888     case REPOKEY_TYPE_DIRSTRARRAY:
2889       for (ida = data->attriddata + val; *ida; ida += 2)
2890         {
2891           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2892           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2893         }
2894       break;
2895     case REPOKEY_TYPE_FIXARRAY:
2896       {
2897         int num = 0;
2898         schemaid = 0;
2899         for (ida = data->attriddata + val; *ida; ida++)
2900           {
2901             Id *kp;
2902             sp = schema;
2903             kp = data->xattrs[-*ida];
2904             if (!kp)
2905               continue;
2906             num++;
2907             for (;*kp; kp += 2)
2908               *sp++ = *kp;
2909             *sp = 0;
2910             if (!schemaid)
2911               schemaid = repodata_schema2id(data, schema, 1);
2912             else if (schemaid != repodata_schema2id(data, schema, 0))
2913               {
2914                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2915                 exit(1);
2916               }
2917           }
2918         if (!num)
2919           break;
2920         data_addid(xd, num);
2921         data_addid(xd, schemaid);
2922         for (ida = data->attriddata + val; *ida; ida++)
2923           {
2924             Id *kp = data->xattrs[-*ida];
2925             if (!kp)
2926               continue;
2927             for (;*kp; kp += 2)
2928               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2929           }
2930         break;
2931       }
2932     case REPOKEY_TYPE_FLEXARRAY:
2933       {
2934         int num = 0;
2935         for (ida = data->attriddata + val; *ida; ida++)
2936           num++;
2937         data_addid(xd, num);
2938         for (ida = data->attriddata + val; *ida; ida++)
2939           {
2940             Id *kp = data->xattrs[-*ida];
2941             if (!kp)
2942               {
2943                 data_addid(xd, 0);      /* XXX */
2944                 continue;
2945               }
2946             sp = schema;
2947             for (;*kp; kp += 2)
2948               *sp++ = *kp;
2949             *sp = 0;
2950             schemaid = repodata_schema2id(data, schema, 1);
2951             data_addid(xd, schemaid);
2952             kp = data->xattrs[-*ida];
2953             for (;*kp; kp += 2)
2954               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2955           }
2956         break;
2957       }
2958     default:
2959       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2960       exit(1);
2961     }
2962   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2963     {
2964       /* put offset/len in incore */
2965       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2966       oldvincorelen = xd->len - oldvincorelen;
2967       data_addid(newincore, oldvincorelen);
2968     }
2969 }
2970
2971 void
2972 repodata_internalize(Repodata *data)
2973 {
2974   Repokey *key, solvkey;
2975   Id entry, nentry;
2976   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2977   unsigned char *dp, *ndp;
2978   int newschema, oldcount;
2979   struct extdata newincore;
2980   struct extdata newvincore;
2981   Id solvkeyid;
2982
2983   if (!data->attrs && !data->xattrs)
2984     return;
2985
2986   newvincore.buf = data->vincore;
2987   newvincore.len = data->vincorelen;
2988
2989   /* find the solvables key, create if needed */
2990   memset(&solvkey, 0, sizeof(solvkey));
2991   solvkey.name = REPOSITORY_SOLVABLES;
2992   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2993   solvkey.size = 0;
2994   solvkey.storage = KEY_STORAGE_INCORE;
2995   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2996
2997   schema = solv_malloc2(data->nkeys, sizeof(Id));
2998   seen = solv_malloc2(data->nkeys, sizeof(Id));
2999
3000   /* Merge the data already existing (in data->schemata, ->incoredata and
3001      friends) with the new attributes in data->attrs[].  */
3002   nentry = data->end - data->start;
3003   memset(&newincore, 0, sizeof(newincore));
3004   data_addid(&newincore, 0);    /* start data at offset 1 */
3005
3006   data->mainschema = 0;
3007   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3008
3009   /* join entry data */
3010   /* we start with the meta data, entry -1 */
3011   for (entry = -1; entry < nentry; entry++)
3012     {
3013       memset(seen, 0, data->nkeys * sizeof(Id));
3014       oldschema = 0;
3015       dp = data->incoredata;
3016       if (dp)
3017         {
3018           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3019           dp = data_read_id(dp, &oldschema);
3020         }
3021 #if 0
3022 fprintf(stderr, "oldschema %d\n", oldschema);
3023 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3024 fprintf(stderr, "schemadata %p\n", data->schemadata);
3025 #endif
3026       /* seen: -1: old data  0: skipped  >0: id + 1 */
3027       newschema = 0;
3028       oldcount = 0;
3029       sp = schema;
3030       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3031         {
3032           if (seen[*keyp])
3033             {
3034               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3035               exit(1);
3036             }
3037           seen[*keyp] = -1;
3038           *sp++ = *keyp;
3039           oldcount++;
3040         }
3041       if (entry >= 0)
3042         keyp = data->attrs ? data->attrs[entry] : 0;
3043       else
3044         {
3045           /* strip solvables key */
3046           *sp = 0;
3047           for (sp = keyp = schema; *sp; sp++)
3048             if (*sp != solvkeyid)
3049               *keyp++ = *sp;
3050             else
3051               oldcount--;
3052           sp = keyp;
3053           seen[solvkeyid] = 0;
3054           keyp = data->xattrs ? data->xattrs[1] : 0;
3055         }
3056       if (keyp)
3057         for (; *keyp; keyp += 2)
3058           {
3059             if (!seen[*keyp])
3060               {
3061                 newschema = 1;
3062                 *sp++ = *keyp;
3063               }
3064             seen[*keyp] = keyp[1] + 1;
3065           }
3066       if (entry < 0 && data->end != data->start)
3067         {
3068           *sp++ = solvkeyid;
3069           newschema = 1;
3070         }
3071       *sp = 0;
3072       if (newschema)
3073         /* Ideally we'd like to sort the new schema here, to ensure
3074            schema equality independend of the ordering.  We can't do that
3075            yet.  For once see below (old ids need to come before new ids).
3076            An additional difficulty is that we also need to move
3077            the values with the keys.  */
3078         schemaid = repodata_schema2id(data, schema, 1);
3079       else
3080         schemaid = oldschema;
3081
3082
3083       /* Now create data blob.  We walk through the (possibly new) schema
3084          and either copy over old data, or insert the new.  */
3085       /* XXX Here we rely on the fact that the (new) schema has the form
3086          o1 o2 o3 o4 ... | n1 n2 n3 ...
3087          (oX being the old keyids (possibly overwritten), and nX being
3088           the new keyids).  This rules out sorting the keyids in order
3089          to ensure a small schema count.  */
3090       if (entry >= 0)
3091         data->incoreoffset[entry] = newincore.len;
3092       data_addid(&newincore, schemaid);
3093       if (entry == -1)
3094         {
3095           data->mainschema = schemaid;
3096           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3097         }
3098       keypstart = data->schemadata + data->schemata[schemaid];
3099       for (keyp = keypstart; *keyp; keyp++)
3100         {
3101           if (entry == -1)
3102             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
3103           if (*keyp == solvkeyid)
3104             {
3105               /* add flexarray entry count */
3106               data_addid(&newincore, data->end - data->start);
3107               break;
3108             }
3109           key = data->keys + *keyp;
3110 #if 0
3111           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, pool_id2str(data->repo->pool, key->name), pool_id2str(data->repo->pool, key->type));
3112 #endif
3113           ndp = dp;
3114           if (oldcount)
3115             {
3116               /* Skip the data associated with this old key.  */
3117               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3118                 {
3119                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3120                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3121                 }
3122               else if (key->storage == KEY_STORAGE_INCORE)
3123                 ndp = data_skip_key(data, dp, key);
3124               oldcount--;
3125             }
3126           if (seen[*keyp] == -1)
3127             {
3128               /* If this key was an old one _and_ was not overwritten with
3129                  a different value copy over the old value (we skipped it
3130                  above).  */
3131               if (dp != ndp)
3132                 data_addblob(&newincore, dp, ndp - dp);
3133               seen[*keyp] = 0;
3134             }
3135           else if (seen[*keyp])
3136             {
3137               /* Otherwise we have a new value.  Parse it into the internal
3138                  form.  */
3139               repodata_serialize_key(data, &newincore, &newvincore,
3140                                      schema, key, seen[*keyp] - 1);
3141             }
3142           dp = ndp;
3143         }
3144       if (entry >= 0 && data->attrs && data->attrs[entry])
3145         data->attrs[entry] = solv_free(data->attrs[entry]);
3146     }
3147   /* free all xattrs */
3148   for (entry = 0; entry < data->nxattrs; entry++)
3149     if (data->xattrs[entry])
3150       solv_free(data->xattrs[entry]);
3151   data->xattrs = solv_free(data->xattrs);
3152   data->nxattrs = 0;
3153
3154   data->lasthandle = 0;
3155   data->lastkey = 0;
3156   data->lastdatalen = 0;
3157   solv_free(schema);
3158   solv_free(seen);
3159   repodata_free_schemahash(data);
3160
3161   solv_free(data->incoredata);
3162   data->incoredata = newincore.buf;
3163   data->incoredatalen = newincore.len;
3164   data->incoredatafree = 0;
3165
3166   solv_free(data->vincore);
3167   data->vincore = newvincore.buf;
3168   data->vincorelen = newvincore.len;
3169
3170   data->attrs = solv_free(data->attrs);
3171   data->attrdata = solv_free(data->attrdata);
3172   data->attriddata = solv_free(data->attriddata);
3173   data->attrnum64data = solv_free(data->attrnum64data);
3174   data->attrdatalen = 0;
3175   data->attriddatalen = 0;
3176   data->attrnum64datalen = 0;
3177 }
3178
3179 void
3180 repodata_disable_paging(Repodata *data)
3181 {
3182   if (maybe_load_repodata(data, 0))
3183     {
3184       repopagestore_disable_paging(&data->store);
3185       data->storestate++;
3186     }
3187 }
3188
3189 static void
3190 repodata_load_stub(Repodata *data)
3191 {
3192   Repo *repo = data->repo;
3193   Pool *pool = repo->pool;
3194   int r, i;
3195   struct _Pool_tmpspace oldtmpspace;
3196
3197   if (!pool->loadcallback)
3198     {
3199       data->state = REPODATA_ERROR;
3200       return;
3201     }
3202   data->state = REPODATA_LOADING;
3203
3204   /* save tmp space */
3205   oldtmpspace = pool->tmpspace;
3206   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3207
3208   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3209
3210   /* restore tmp space */
3211   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3212     solv_free(pool->tmpspace.buf[i]);
3213   pool->tmpspace = oldtmpspace;
3214
3215   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3216 }
3217
3218 void
3219 repodata_create_stubs(Repodata *data)
3220 {
3221   Repo *repo = data->repo;
3222   Pool *pool = repo->pool;
3223   Repodata *sdata;
3224   int *stubdataids;
3225   Dataiterator di;
3226   Id xkeyname = 0;
3227   int i, cnt = 0;
3228   int repodataid;
3229   int datastart, dataend;
3230
3231   repodataid = data - repo->repodata;
3232   datastart = data->start;
3233   dataend = data->end;
3234   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3235   while (dataiterator_step(&di))
3236     {
3237       if (di.data - repo->repodata != repodataid)
3238         continue;
3239       cnt++;
3240     }
3241   dataiterator_free(&di);
3242   if (!cnt)
3243     return;
3244   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3245   for (i = 0; i < cnt; i++)
3246     {
3247       sdata = repo_add_repodata(repo, 0);
3248       if (dataend > datastart)
3249         repodata_extend_block(sdata, datastart, dataend - datastart);
3250       stubdataids[i] = sdata - repo->repodata;
3251       sdata->state = REPODATA_STUB;
3252       sdata->loadcallback = repodata_load_stub;
3253     }
3254   i = 0;
3255   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3256   sdata = 0;
3257   while (dataiterator_step(&di))
3258     {
3259       if (di.data - repo->repodata != repodataid)
3260         continue;
3261       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3262         {
3263           dataiterator_entersub(&di);
3264           sdata = repo->repodata + stubdataids[i++];
3265           xkeyname = 0;
3266           continue;
3267         }
3268       switch (di.key->type)
3269         {
3270         case REPOKEY_TYPE_ID:
3271           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3272           break;
3273         case REPOKEY_TYPE_CONSTANTID:
3274           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3275           break;
3276         case REPOKEY_TYPE_STR:
3277           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3278           break;
3279         case REPOKEY_TYPE_VOID:
3280           repodata_set_void(sdata, SOLVID_META, di.key->name);
3281           break;
3282         case REPOKEY_TYPE_NUM:
3283           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3284           break;
3285         case REPOKEY_TYPE_MD5:
3286         case REPOKEY_TYPE_SHA1:
3287         case REPOKEY_TYPE_SHA256:
3288           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3289           break;
3290         case REPOKEY_TYPE_IDARRAY:
3291           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3292           if (di.key->name == REPOSITORY_KEYS)
3293             {
3294               Repokey xkey;
3295
3296               if (!xkeyname)
3297                 {
3298                   if (!di.kv.eof)
3299                     xkeyname = di.kv.id;
3300                   continue;
3301                 }
3302               xkey.name = xkeyname;
3303               xkey.type = di.kv.id;
3304               xkey.storage = KEY_STORAGE_INCORE;
3305               xkey.size = 0;
3306               repodata_key2id(sdata, &xkey, 1);
3307               xkeyname = 0;
3308             }
3309         default:
3310           break;
3311         }
3312     }
3313   dataiterator_free(&di);
3314   for (i = 0; i < cnt; i++)
3315     repodata_internalize(repo->repodata + stubdataids[i]);
3316   solv_free(stubdataids);
3317 }
3318
3319 unsigned int
3320 repodata_memused(Repodata *data)
3321 {
3322   return data->incoredatalen + data->vincorelen;
3323 }
3324
3325 /*
3326 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3327 */