allow SOLVID_META queries through SOLVID_POS
[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_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
786 {
787   if (!id || !data || !fromdata)
788     return id;
789   if (!data->localpool || !fromdata->localpool)
790     {
791       if (fromdata->localpool)
792         id = repodata_globalize_id(fromdata, id, create);
793       if (data->localpool)
794         id = repodata_localize_id(data, id, create);
795       return id;
796     }
797   /* localpool is set in both data and fromdata */
798   return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
799 }
800
801 Id
802 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
803 {
804   Id *ap;
805   if (!data->attrs)
806     return 0;
807   ap = data->attrs[solvid - data->start];
808   if (!ap)
809     return 0;
810   for (; *ap; ap += 2)
811     {
812       if (data->keys[*ap].name != keyname)
813         continue;
814       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
815         return voidid;
816       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
817         return ap[1];
818       return 0;
819     }
820   return 0;
821 }
822
823
824 /************************************************************************
825  * data search
826  */
827
828
829 int
830 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
831 {
832   switch (key->type)
833     {
834     case REPOKEY_TYPE_ID:
835     case REPOKEY_TYPE_CONSTANTID:
836     case REPOKEY_TYPE_IDARRAY:
837       if (data && data->localpool)
838         kv->str = stringpool_id2str(&data->spool, kv->id);
839       else
840         kv->str = pool_id2str(pool, kv->id);
841       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
842         {
843           const char *s;
844           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
845             ;
846           if (*s == ':' && s > kv->str)
847             kv->str = s + 1;
848         }
849       return 1;
850     case REPOKEY_TYPE_STR:
851       return 1;
852     case REPOKEY_TYPE_DIRSTRARRAY:
853       if (!(flags & SEARCH_FILES))
854         return 1;       /* match just the basename */
855       if (kv->num)
856         return 1;       /* already stringified */
857       /* Put the full filename into kv->str.  */
858       kv->str = repodata_dir2str(data, kv->id, kv->str);
859       kv->num = 1;      /* mark stringification */
860       return 1;
861     case REPOKEY_TYPE_MD5:
862     case REPOKEY_TYPE_SHA1:
863     case REPOKEY_TYPE_SHA256:
864       if (!(flags & SEARCH_CHECKSUMS))
865         return 0;       /* skip em */
866       if (kv->num)
867         return 1;       /* already stringified */
868       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
869       kv->num = 1;      /* mark stringification */
870       return 1;
871     default:
872       return 0;
873     }
874 }
875
876
877 struct subschema_data {
878   Solvable *s;
879   void *cbdata;
880   KeyValue *parent;
881 };
882
883 /* search a specific repodata */
884 void
885 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
886 {
887   Id schema;
888   Repokey *key;
889   Id keyid, *kp, *keyp;
890   unsigned char *dp, *ddp;
891   int onekey = 0;
892   int stop;
893   KeyValue kv;
894   Solvable *s;
895
896   if (!maybe_load_repodata(data, keyname))
897     return;
898   if (solvid == SOLVID_SUBSCHEMA)
899     {
900       struct subschema_data *subd = cbdata;
901       cbdata = subd->cbdata;
902       s = subd->s;
903       schema = subd->parent->id;
904       dp = (unsigned char *)subd->parent->str;
905       kv.parent = subd->parent;
906     }
907   else
908     {
909       schema = 0;
910       dp = solvid2data(data, solvid, &schema);
911       if (!dp)
912         return;
913       s = data->repo->pool->solvables + solvid;
914       kv.parent = 0;
915     }
916   keyp = data->schemadata + data->schemata[schema];
917   if (keyname)
918     {
919       /* search for a specific key */
920       for (kp = keyp; *kp; kp++)
921         if (data->keys[*kp].name == keyname)
922           break;
923       if (!*kp)
924         return;
925       dp = forward_to_key(data, *kp, keyp, dp);
926       if (!dp)
927         return;
928       keyp = kp;
929       onekey = 1;
930     }
931   while ((keyid = *keyp++) != 0)
932     {
933       stop = 0;
934       key = data->keys + keyid;
935       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
936
937       if (key->type == REPOKEY_TYPE_DELETED)
938         continue;
939       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
940         {
941           struct subschema_data subd;
942           int nentries;
943           Id schema = 0;
944
945           subd.cbdata = cbdata;
946           subd.s = s;
947           subd.parent = &kv;
948           ddp = data_read_id(ddp, &nentries);
949           kv.num = nentries;
950           kv.entry = 0;
951           kv.eof = 0;
952           while (ddp && nentries > 0)
953             {
954               if (!--nentries)
955                 kv.eof = 1;
956               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
957                 ddp = data_read_id(ddp, &schema);
958               kv.id = schema;
959               kv.str = (char *)ddp;
960               stop = callback(cbdata, s, data, key, &kv);
961               if (stop > SEARCH_NEXT_KEY)
962                 return;
963               if (stop && stop != SEARCH_ENTERSUB)
964                 break;
965               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
966                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
967               ddp = data_skip_schema(data, ddp, schema);
968               kv.entry++;
969             }
970           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
971             {
972               /* sentinel */
973               kv.eof = 2;
974               kv.str = (char *)ddp;
975               stop = callback(cbdata, s, data, key, &kv);
976               if (stop > SEARCH_NEXT_KEY)
977                 return;
978             }
979           if (onekey)
980             return;
981           continue;
982         }
983       kv.entry = 0;
984       do
985         {
986           ddp = data_fetch(ddp, &kv, key);
987           if (!ddp)
988             break;
989           stop = callback(cbdata, s, data, key, &kv);
990           kv.entry++;
991         }
992       while (!kv.eof && !stop);
993       if (onekey || stop > SEARCH_NEXT_KEY)
994         return;
995     }
996 }
997
998 void
999 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1000 {
1001   Pool *pool = data->repo->pool;
1002   if (!kv)
1003     pool_clear_pos(pool);
1004   else
1005     {
1006       pool->pos.repo = data->repo;
1007       pool->pos.repodataid = data - data->repo->repodata;
1008       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1009       pool->pos.schema = kv->id;
1010     }
1011 }
1012
1013 /************************************************************************
1014  * data iterator functions
1015  */
1016
1017 static inline Id *
1018 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1019 {
1020   kv->id = keyname;
1021   switch (keyname)
1022     {
1023     case SOLVABLE_NAME:
1024       kv->eof = 1;
1025       return &s->name;
1026     case SOLVABLE_ARCH:
1027       kv->eof = 1;
1028       return &s->arch;
1029     case SOLVABLE_EVR:
1030       kv->eof = 1;
1031       return &s->evr;
1032     case SOLVABLE_VENDOR:
1033       kv->eof = 1;
1034       return &s->vendor;
1035     case SOLVABLE_PROVIDES:
1036       kv->eof = 0;
1037       return s->provides ? s->repo->idarraydata + s->provides : 0;
1038     case SOLVABLE_OBSOLETES:
1039       kv->eof = 0;
1040       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1041     case SOLVABLE_CONFLICTS:
1042       kv->eof = 0;
1043       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1044     case SOLVABLE_REQUIRES:
1045       kv->eof = 0;
1046       return s->requires ? s->repo->idarraydata + s->requires : 0;
1047     case SOLVABLE_RECOMMENDS:
1048       kv->eof = 0;
1049       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1050     case SOLVABLE_SUPPLEMENTS:
1051       kv->eof = 0;
1052       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1053     case SOLVABLE_SUGGESTS:
1054       kv->eof = 0;
1055       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1056     case SOLVABLE_ENHANCES:
1057       kv->eof = 0;
1058       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1059     case RPM_RPMDBID:
1060       kv->eof = 1;
1061       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1062     default:
1063       return 0;
1064     }
1065 }
1066
1067 int
1068 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1069 {
1070   ma->match = match;
1071   ma->flags = flags;
1072   ma->error = 0;
1073   ma->matchdata = 0;
1074   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1075     {
1076       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1077       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1078       if (ma->error)
1079         {
1080           solv_free(ma->matchdata);
1081           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1082         }
1083     }
1084   if ((flags & SEARCH_FILES) != 0 && match)
1085     {
1086       /* prepare basename check */
1087       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1088         {
1089           const char *p = strrchr(match, '/');
1090           ma->matchdata = (void *)(p ? p + 1 : match);
1091         }
1092       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1093         {
1094           const char *p;
1095           for (p = match + strlen(match) - 1; p >= match; p--)
1096             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1097               break;
1098           ma->matchdata = (void *)(p + 1);
1099         }
1100     }
1101   return ma->error;
1102 }
1103
1104 void
1105 datamatcher_free(Datamatcher *ma)
1106 {
1107   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1108     {
1109       regfree(ma->matchdata);
1110       solv_free(ma->matchdata);
1111     }
1112   ma->matchdata = 0;
1113 }
1114
1115 int
1116 datamatcher_match(Datamatcher *ma, const char *str)
1117 {
1118   int l;
1119   switch ((ma->flags & SEARCH_STRINGMASK))
1120     {
1121     case SEARCH_SUBSTRING:
1122       if (ma->flags & SEARCH_NOCASE)
1123         return strcasestr(str, ma->match) != 0;
1124       else
1125         return strstr(str, ma->match) != 0;
1126     case SEARCH_STRING:
1127       if (ma->flags & SEARCH_NOCASE)
1128         return !strcasecmp(ma->match, str);
1129       else
1130         return !strcmp(ma->match, str);
1131     case SEARCH_STRINGSTART:
1132       if (ma->flags & SEARCH_NOCASE)
1133         return !strncasecmp(ma->match, str, strlen(ma->match));
1134       else
1135         return !strncmp(ma->match, str, strlen(ma->match));
1136     case SEARCH_STRINGEND:
1137       l = strlen(str) - strlen(ma->match);
1138       if (l < 0)
1139         return 0;
1140       if (ma->flags & SEARCH_NOCASE)
1141         return !strcasecmp(ma->match, str + l);
1142       else
1143         return !strcmp(ma->match, str + l);
1144     case SEARCH_GLOB:
1145       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1146     case SEARCH_REGEX:
1147       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1148     default:
1149       return 0;
1150     }
1151 }
1152
1153 /* check if the matcher can match the provides basename */
1154
1155 int
1156 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1157 {
1158   int l;
1159   const char *match = ma->matchdata;
1160   if (!match)
1161     return 1;
1162   switch (ma->flags & SEARCH_STRINGMASK)
1163     {
1164     case SEARCH_STRING:
1165       break;
1166     case SEARCH_STRINGEND:
1167       if (match != ma->match)
1168         break;          /* had slash, do exact match on basename */
1169       /* FALLTHROUGH */
1170     case SEARCH_GLOB:
1171       /* check if the basename ends with match */
1172       l = strlen(basename) - strlen(match);
1173       if (l < 0)
1174         return 0;
1175       basename += l;
1176       break;
1177     default:
1178       return 1; /* maybe matches */
1179     }
1180   if ((ma->flags & SEARCH_NOCASE) != 0)
1181     return !strcasecmp(match, basename);
1182   else
1183     return !strcmp(match, basename);
1184 }
1185
1186 int
1187 repodata_filelistfilter_matches(Repodata *data, const char *str)
1188 {
1189   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1190   /* for now hardcoded */
1191   if (strstr(str, "bin/"))
1192     return 1;
1193   if (!strncmp(str, "/etc/", 5))
1194     return 1;
1195   if (!strcmp(str, "/usr/lib/sendmail"))
1196     return 1;
1197   return 0;
1198 }
1199
1200
1201 enum {
1202   di_bye,
1203
1204   di_enterrepo,
1205   di_entersolvable,
1206   di_enterrepodata,
1207   di_enterschema,
1208   di_enterkey,
1209
1210   di_nextattr,
1211   di_nextkey,
1212   di_nextrepodata,
1213   di_nextsolvable,
1214   di_nextrepo,
1215
1216   di_enterarray,
1217   di_nextarrayelement,
1218
1219   di_entersub,
1220   di_leavesub,
1221
1222   di_nextsolvablekey,
1223   di_entersolvablekey,
1224   di_nextsolvableattr
1225 };
1226
1227 /* see dataiterator.h for documentation */
1228 int
1229 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1230 {
1231   memset(di, 0, sizeof(*di));
1232   di->pool = pool;
1233   di->flags = flags & ~SEARCH_THISSOLVID;
1234   if (!pool || (repo && repo->pool != pool))
1235     {
1236       di->state = di_bye;
1237       return -1;
1238     }
1239   if (match)
1240     {
1241       int error;
1242       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1243         {
1244           di->state = di_bye;
1245           return error;
1246         }
1247     }
1248   di->keyname = keyname;
1249   di->keynames[0] = keyname;
1250   dataiterator_set_search(di, repo, p);
1251   return 0;
1252 }
1253
1254 void
1255 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1256 {
1257   *di = *from;
1258   if (di->dupstr)
1259     {
1260       if (di->dupstr == di->kv.str)
1261         {
1262           di->dupstr = solv_malloc(di->dupstrn);
1263           memcpy(di->dupstr, from->dupstr, di->dupstrn);
1264         }
1265       else
1266         {
1267           di->dupstr = 0;
1268           di->dupstrn = 0;
1269         }
1270     }
1271   memset(&di->matcher, 0, sizeof(di->matcher));
1272   if (from->matcher.match)
1273     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1274   if (di->nparents)
1275     {
1276       /* fix pointers */
1277       int i;
1278       for (i = 1; i < di->nparents; i++)
1279         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1280       di->kv.parent = &di->parents[di->nparents - 1].kv;
1281     }
1282 }
1283
1284 int
1285 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1286 {
1287   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1288   datamatcher_free(&di->matcher);
1289   memset(&di->matcher, 0, sizeof(di->matcher));
1290   if (match)
1291     {
1292       int error;
1293       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1294         {
1295           di->state = di_bye;
1296           return error;
1297         }
1298     }
1299   return 0;
1300 }
1301
1302 void
1303 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1304 {
1305   di->repo = repo;
1306   di->repoid = 0;
1307   di->flags &= ~SEARCH_THISSOLVID;
1308   di->nparents = 0;
1309   di->rootlevel = 0;
1310   di->repodataid = 1;
1311   if (!di->pool->urepos)
1312     {
1313       di->state = di_bye;
1314       return;
1315     }
1316   if (!repo)
1317     {
1318       di->repoid = 1;
1319       di->repo = di->pool->repos[di->repoid];
1320     }
1321   di->state = di_enterrepo;
1322   if (p)
1323     dataiterator_jump_to_solvid(di, p);
1324 }
1325
1326 void
1327 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1328 {
1329   di->nkeynames = 0;
1330   di->keyname = keyname;
1331   di->keynames[0] = keyname;
1332 }
1333
1334 void
1335 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1336 {
1337   int i;
1338
1339   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1340     {
1341       di->state = di_bye;       /* sorry */
1342       return;
1343     }
1344   for (i = di->nkeynames + 1; i > 0; i--)
1345     di->keynames[i] = di->keynames[i - 1];
1346   di->keynames[0] = di->keyname = keyname;
1347   di->nkeynames++;
1348 }
1349
1350 void
1351 dataiterator_free(Dataiterator *di)
1352 {
1353   if (di->matcher.match)
1354     datamatcher_free(&di->matcher);
1355   if (di->dupstr)
1356     solv_free(di->dupstr);
1357 }
1358
1359 static inline unsigned char *
1360 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1361 {
1362   Id *keyp = di->keyp;
1363   Repokey *keys = di->data->keys;
1364   unsigned char *dp;
1365
1366   for (keyp = di->keyp; *keyp; keyp++)
1367     if (keys[*keyp].name == keyname)
1368       break;
1369   if (!*keyp)
1370     return 0;
1371   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1372   if (!dp)
1373     return 0;
1374   di->keyp = keyp;
1375   return dp;
1376 }
1377
1378 static int
1379 dataiterator_filelistcheck(Dataiterator *di)
1380 {
1381   int j;
1382   int needcomplete = 0;
1383   Repodata *data = di->data;
1384
1385   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1386     if (!di->matcher.match
1387        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1388            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1389        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1390       needcomplete = 1;
1391   if (data->state != REPODATA_AVAILABLE)
1392     return needcomplete ? 1 : 0;
1393   for (j = 1; j < data->nkeys; j++)
1394     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1395       break;
1396   return j == data->nkeys && !needcomplete ? 0 : 1;
1397 }
1398
1399 int
1400 dataiterator_step(Dataiterator *di)
1401 {
1402   Id schema;
1403
1404   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
1405     unsigned int ddpoff = di->ddp - di->vert_ddp;
1406     di->vert_off += ddpoff;
1407     di->vert_len -= ddpoff;
1408     di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1409     di->vert_storestate = di->data->storestate;
1410     if (!di->ddp)
1411       di->state = di_nextkey;
1412   }
1413   for (;;)
1414     {
1415       switch (di->state)
1416         {
1417         case di_enterrepo: di_enterrepo:
1418           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1419             goto di_nextrepo;
1420           if (!(di->flags & SEARCH_THISSOLVID))
1421             {
1422               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1423               goto di_nextsolvable;
1424             }
1425           /* FALLTHROUGH */
1426
1427         case di_entersolvable: di_entersolvable:
1428           if (di->repodataid)
1429             {
1430               di->repodataid = 1;       /* reset repodata iterator */
1431               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)
1432                 {
1433                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1434
1435                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1436                   di->data = 0;
1437                   goto di_entersolvablekey;
1438                 }
1439             }
1440           /* FALLTHROUGH */
1441
1442         case di_enterrepodata: di_enterrepodata:
1443           if (di->repodataid)
1444             {
1445               if (di->repodataid >= di->repo->nrepodata)
1446                 goto di_nextsolvable;
1447               di->data = di->repo->repodata + di->repodataid;
1448             }
1449           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1450             goto di_nextrepodata;
1451           if (!maybe_load_repodata(di->data, di->keyname))
1452             goto di_nextrepodata;
1453           di->dp = solvid2data(di->data, di->solvid, &schema);
1454           if (!di->dp)
1455             goto di_nextrepodata;
1456           if (di->solvid == SOLVID_POS)
1457             di->solvid = di->pool->pos.solvid;
1458           /* reset key iterator */
1459           di->keyp = di->data->schemadata + di->data->schemata[schema];
1460           /* FALLTHROUGH */
1461
1462         case di_enterschema: di_enterschema:
1463           if (di->keyname)
1464             di->dp = dataiterator_find_keyname(di, di->keyname);
1465           if (!di->dp || !*di->keyp)
1466             {
1467               if (di->kv.parent)
1468                 goto di_leavesub;
1469               goto di_nextrepodata;
1470             }
1471           /* FALLTHROUGH */
1472
1473         case di_enterkey: di_enterkey:
1474           di->kv.entry = -1;
1475           di->key = di->data->keys + *di->keyp;
1476           if (!di->dp)
1477             goto di_nextkey;
1478           /* this is get_data() modified to store vert_ data */
1479           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1480             {
1481               Id off, len;
1482               di->dp = data_read_id(di->dp, &off);
1483               di->dp = data_read_id(di->dp, &len);
1484               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1485               di->vert_off = off;
1486               di->vert_len = len;
1487               di->vert_storestate = di->data->storestate;
1488             }
1489           else if (di->key->storage == KEY_STORAGE_INCORE)
1490             {
1491               di->ddp = di->dp;
1492               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1493                 di->dp = data_skip_key(di->data, di->dp, di->key);
1494             }
1495           else
1496             di->ddp = 0;
1497           if (!di->ddp)
1498             goto di_nextkey;
1499           if (di->key->type == REPOKEY_TYPE_DELETED)
1500             goto di_nextkey;
1501           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1502             goto di_enterarray;
1503           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1504             goto di_nextkey;
1505           /* FALLTHROUGH */
1506
1507         case di_nextattr:
1508           di->kv.entry++;
1509           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1510           if (di->kv.eof)
1511             di->state = di_nextkey;
1512           else
1513             di->state = di_nextattr;
1514           break;
1515
1516         case di_nextkey: di_nextkey:
1517           if (!di->keyname && *++di->keyp)
1518             goto di_enterkey;
1519           if (di->kv.parent)
1520             goto di_leavesub;
1521           /* FALLTHROUGH */
1522
1523         case di_nextrepodata: di_nextrepodata:
1524           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1525               goto di_enterrepodata;
1526           /* FALLTHROUGH */
1527
1528         case di_nextsolvable: di_nextsolvable:
1529           if (!(di->flags & SEARCH_THISSOLVID))
1530             {
1531               if (di->solvid < 0)
1532                 di->solvid = di->repo->start;
1533               else
1534                 di->solvid++;
1535               for (; di->solvid < di->repo->end; di->solvid++)
1536                 {
1537                   if (di->pool->solvables[di->solvid].repo == di->repo)
1538                     goto di_entersolvable;
1539                 }
1540             }
1541           /* FALLTHROUGH */
1542
1543         case di_nextrepo: di_nextrepo:
1544           if (di->repoid > 0)
1545             {
1546               di->repoid++;
1547               di->repodataid = 1;
1548               if (di->repoid < di->pool->nrepos)
1549                 {
1550                   di->repo = di->pool->repos[di->repoid];
1551                   goto di_enterrepo;
1552                 }
1553             }
1554         /* FALLTHROUGH */
1555
1556         case di_bye: di_bye:
1557           di->state = di_bye;
1558           return 0;
1559
1560         case di_enterarray: di_enterarray:
1561           if (di->key->name == REPOSITORY_SOLVABLES)
1562             goto di_nextkey;
1563           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1564           di->kv.eof = 0;
1565           di->kv.entry = -1;
1566           /* FALLTHROUGH */
1567
1568         case di_nextarrayelement: di_nextarrayelement:
1569           di->kv.entry++;
1570           if (di->kv.entry)
1571             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1572           if (di->kv.entry == di->kv.num)
1573             {
1574               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1575                 goto di_nextkey;
1576               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1577                 goto di_nextkey;
1578               di->kv.str = (char *)di->ddp;
1579               di->kv.eof = 2;
1580               di->state = di_nextkey;
1581               break;
1582             }
1583           if (di->kv.entry == di->kv.num - 1)
1584             di->kv.eof = 1;
1585           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1586             di->ddp = data_read_id(di->ddp, &di->kv.id);
1587           di->kv.str = (char *)di->ddp;
1588           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1589             goto di_entersub;
1590           if ((di->flags & SEARCH_SUB) != 0)
1591             di->state = di_entersub;
1592           else
1593             di->state = di_nextarrayelement;
1594           break;
1595
1596         case di_entersub: di_entersub:
1597           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1598             goto di_nextarrayelement;   /* sorry, full */
1599           di->parents[di->nparents].kv = di->kv;
1600           di->parents[di->nparents].dp = di->dp;
1601           di->parents[di->nparents].keyp = di->keyp;
1602           di->dp = (unsigned char *)di->kv.str;
1603           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1604           memset(&di->kv, 0, sizeof(di->kv));
1605           di->kv.parent = &di->parents[di->nparents].kv;
1606           di->nparents++;
1607           di->keyname = di->keynames[di->nparents - di->rootlevel];
1608           goto di_enterschema;
1609
1610         case di_leavesub: di_leavesub:
1611           if (di->nparents - 1 < di->rootlevel)
1612             goto di_bye;
1613           di->nparents--;
1614           di->dp = di->parents[di->nparents].dp;
1615           di->kv = di->parents[di->nparents].kv;
1616           di->keyp = di->parents[di->nparents].keyp;
1617           di->key = di->data->keys + *di->keyp;
1618           di->ddp = (unsigned char *)di->kv.str;
1619           di->keyname = di->keynames[di->nparents - di->rootlevel];
1620           goto di_nextarrayelement;
1621
1622         /* special solvable attr handling follows */
1623
1624         case di_nextsolvablekey: di_nextsolvablekey:
1625           if (di->keyname || di->key->name == RPM_RPMDBID)
1626             goto di_enterrepodata;
1627           di->key++;
1628           /* FALLTHROUGH */
1629
1630         case di_entersolvablekey: di_entersolvablekey:
1631           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1632           if (!di->idp || !*di->idp)
1633             goto di_nextsolvablekey;
1634           if (di->kv.eof)
1635             {
1636               /* not an array */
1637               di->kv.id = *di->idp;
1638               di->kv.num = *di->idp;    /* for rpmdbid */
1639               di->kv.num2 = 0;          /* for rpmdbid */
1640               di->kv.entry = 0;
1641               di->state = di_nextsolvablekey;
1642               break;
1643             }
1644           di->kv.entry = -1;
1645           /* FALLTHROUGH */
1646
1647         case di_nextsolvableattr:
1648           di->state = di_nextsolvableattr;
1649           di->kv.id = *di->idp++;
1650           di->kv.entry++;
1651           if (!*di->idp)
1652             {
1653               di->kv.eof = 1;
1654               di->state = di_nextsolvablekey;
1655             }
1656           break;
1657
1658         }
1659
1660       if (di->matcher.match)
1661         {
1662           /* simple pre-check so that we don't need to stringify */
1663           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1664             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1665               continue;
1666           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1667             {
1668               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1669                 return 1;
1670               continue;
1671             }
1672           if (!datamatcher_match(&di->matcher, di->kv.str))
1673             continue;
1674         }
1675       else
1676         {
1677           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1678             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1679         }
1680       /* found something! */
1681       return 1;
1682     }
1683 }
1684
1685 void
1686 dataiterator_entersub(Dataiterator *di)
1687 {
1688   if (di->state == di_nextarrayelement)
1689     di->state = di_entersub;
1690 }
1691
1692 void
1693 dataiterator_setpos(Dataiterator *di)
1694 {
1695   if (di->kv.eof == 2)
1696     {
1697       pool_clear_pos(di->pool);
1698       return;
1699     }
1700   di->pool->pos.solvid = di->solvid;
1701   di->pool->pos.repo = di->repo;
1702   di->pool->pos.repodataid = di->data - di->repo->repodata;
1703   di->pool->pos.schema = di->kv.id;
1704   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1705 }
1706
1707 void
1708 dataiterator_setpos_parent(Dataiterator *di)
1709 {
1710   if (!di->kv.parent || di->kv.parent->eof == 2)
1711     {
1712       pool_clear_pos(di->pool);
1713       return;
1714     }
1715   di->pool->pos.solvid = di->solvid;
1716   di->pool->pos.repo = di->repo;
1717   di->pool->pos.repodataid = di->data - di->repo->repodata;
1718   di->pool->pos.schema = di->kv.parent->id;
1719   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1720 }
1721
1722 /* clones just the position, not the search keys/matcher */
1723 void
1724 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1725 {
1726   di->state = from->state;
1727   di->flags &= ~SEARCH_THISSOLVID;
1728   di->flags |= (from->flags & SEARCH_THISSOLVID);
1729   di->repo = from->repo;
1730   di->data = from->data;
1731   di->dp = from->dp;
1732   di->ddp = from->ddp;
1733   di->idp = from->idp;
1734   di->keyp = from->keyp;
1735   di->key = from->key;
1736   di->kv = from->kv;
1737   di->repodataid = from->repodataid;
1738   di->solvid = from->solvid;
1739   di->repoid = from->repoid;
1740   di->rootlevel = from->rootlevel;
1741   memcpy(di->parents, from->parents, sizeof(from->parents));
1742   di->nparents = from->nparents;
1743   if (di->nparents)
1744     {
1745       int i;
1746       for (i = 1; i < di->nparents; i++)
1747         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1748       di->kv.parent = &di->parents[di->nparents - 1].kv;
1749     }
1750   di->dupstr = 0;
1751   di->dupstrn = 0;
1752   if (from->dupstr && from->dupstr == from->kv.str)
1753     {
1754       di->dupstrn = from->dupstrn;
1755       di->dupstr = solv_malloc(from->dupstrn);
1756       memcpy(di->dupstr, from->dupstr, di->dupstrn);
1757     }
1758 }
1759
1760 void
1761 dataiterator_seek(Dataiterator *di, int whence)
1762 {
1763   if ((whence & DI_SEEK_STAY) != 0)
1764     di->rootlevel = di->nparents;
1765   switch (whence & ~DI_SEEK_STAY)
1766     {
1767     case DI_SEEK_CHILD:
1768       if (di->state != di_nextarrayelement)
1769         break;
1770       if ((whence & DI_SEEK_STAY) != 0)
1771         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1772       di->state = di_entersub;
1773       break;
1774     case DI_SEEK_PARENT:
1775       if (!di->nparents)
1776         {
1777           di->state = di_bye;
1778           break;
1779         }
1780       di->nparents--;
1781       if (di->rootlevel > di->nparents)
1782         di->rootlevel = di->nparents;
1783       di->dp = di->parents[di->nparents].dp;
1784       di->kv = di->parents[di->nparents].kv;
1785       di->keyp = di->parents[di->nparents].keyp;
1786       di->key = di->data->keys + *di->keyp;
1787       di->ddp = (unsigned char *)di->kv.str;
1788       di->keyname = di->keynames[di->nparents - di->rootlevel];
1789       di->state = di_nextarrayelement;
1790       break;
1791     case DI_SEEK_REWIND:
1792       if (!di->nparents)
1793         {
1794           di->state = di_bye;
1795           break;
1796         }
1797       di->dp = (unsigned char *)di->kv.parent->str;
1798       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1799       di->state = di_enterschema;
1800       break;
1801     default:
1802       break;
1803     }
1804 }
1805
1806 void
1807 dataiterator_skip_attribute(Dataiterator *di)
1808 {
1809   if (di->state == di_nextsolvableattr)
1810     di->state = di_nextsolvablekey;
1811   else
1812     di->state = di_nextkey;
1813 }
1814
1815 void
1816 dataiterator_skip_solvable(Dataiterator *di)
1817 {
1818   di->nparents = 0;
1819   di->kv.parent = 0;
1820   di->rootlevel = 0;
1821   di->keyname = di->keynames[0];
1822   di->state = di_nextsolvable;
1823 }
1824
1825 void
1826 dataiterator_skip_repo(Dataiterator *di)
1827 {
1828   di->nparents = 0;
1829   di->kv.parent = 0;
1830   di->rootlevel = 0;
1831   di->keyname = di->keynames[0];
1832   di->state = di_nextrepo;
1833 }
1834
1835 void
1836 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1837 {
1838   di->nparents = 0;
1839   di->kv.parent = 0;
1840   di->rootlevel = 0;
1841   di->keyname = di->keynames[0];
1842   if (solvid == SOLVID_POS)
1843     {
1844       di->repo = di->pool->pos.repo;
1845       if (!di->repo)
1846         {
1847           di->state = di_bye;
1848           return;
1849         }
1850       di->repoid = 0;
1851       di->data = di->repo->repodata + di->pool->pos.repodataid;
1852       di->repodataid = 0;
1853       di->solvid = solvid;
1854       di->state = di_enterrepo;
1855       di->flags |= SEARCH_THISSOLVID;
1856       return;
1857     }
1858   if (solvid > 0)
1859     {
1860       di->repo = di->pool->solvables[solvid].repo;
1861       di->repoid = 0;
1862     }
1863   else if (di->repoid > 0)
1864     {
1865       if (!di->pool->urepos)
1866         {
1867           di->state = di_bye;
1868           return;
1869         }
1870       di->repoid = 1;
1871       di->repo = di->pool->repos[di->repoid];
1872     }
1873   di->repodataid = 1;
1874   di->solvid = solvid;
1875   if (solvid)
1876     di->flags |= SEARCH_THISSOLVID;
1877   di->state = di_enterrepo;
1878 }
1879
1880 void
1881 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1882 {
1883   di->nparents = 0;
1884   di->kv.parent = 0;
1885   di->rootlevel = 0;
1886   di->repo = repo;
1887   di->repoid = 0;       /* 0 means stay at repo */
1888   di->repodataid = 1;
1889   di->solvid = 0;
1890   di->flags &= ~SEARCH_THISSOLVID;
1891   di->state = di_enterrepo;
1892 }
1893
1894 int
1895 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1896 {
1897   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1898     return 0;
1899   if (!ma)
1900     return 1;
1901   return datamatcher_match(ma, di->kv.str);
1902 }
1903
1904 void
1905 dataiterator_strdup(Dataiterator *di)
1906 {
1907   int l = -1;
1908
1909   if (!di->kv.str || di->kv.str == di->dupstr)
1910     return;
1911   switch (di->key->type)
1912     {
1913     case REPOKEY_TYPE_MD5:
1914     case REPOKEY_TYPE_SHA1:
1915     case REPOKEY_TYPE_SHA256:
1916     case REPOKEY_TYPE_DIRSTRARRAY:
1917       if (di->kv.num)   /* was it stringified into tmp space? */
1918         l = strlen(di->kv.str) + 1;
1919       break;
1920     default:
1921       break;
1922     }
1923   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1924     {
1925       switch (di->key->type)
1926         {
1927         case REPOKEY_TYPE_STR:
1928         case REPOKEY_TYPE_DIRSTRARRAY:
1929           l = strlen(di->kv.str) + 1;
1930           break;
1931         case REPOKEY_TYPE_MD5:
1932           l = SIZEOF_MD5;
1933           break;
1934         case REPOKEY_TYPE_SHA1:
1935           l = SIZEOF_SHA1;
1936           break;
1937         case REPOKEY_TYPE_SHA256:
1938           l = SIZEOF_SHA256;
1939           break;
1940         case REPOKEY_TYPE_BINARY:
1941           l = di->kv.num;
1942           break;
1943         }
1944     }
1945   if (l >= 0)
1946     {
1947       if (!di->dupstrn || di->dupstrn < l)
1948         {
1949           di->dupstrn = l + 16;
1950           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
1951         }
1952       if (l)
1953         memcpy(di->dupstr, di->kv.str, l);
1954       di->kv.str = di->dupstr;
1955     }
1956 }
1957
1958 /************************************************************************
1959  * data modify functions
1960  */
1961
1962 /* extend repodata so that it includes solvables p */
1963 void
1964 repodata_extend(Repodata *data, Id p)
1965 {
1966   if (data->start == data->end)
1967     data->start = data->end = p;
1968   if (p >= data->end)
1969     {
1970       int old = data->end - data->start;
1971       int new = p - data->end + 1;
1972       if (data->attrs)
1973         {
1974           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1975           memset(data->attrs + old, 0, new * sizeof(Id *));
1976         }
1977       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1978       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1979       data->end = p + 1;
1980     }
1981   if (p < data->start)
1982     {
1983       int old = data->end - data->start;
1984       int new = data->start - p;
1985       if (data->attrs)
1986         {
1987           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1988           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1989           memset(data->attrs, 0, new * sizeof(Id *));
1990         }
1991       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1992       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1993       memset(data->incoreoffset, 0, new * sizeof(Id));
1994       data->start = p;
1995     }
1996 }
1997
1998 /* shrink end of repodata */
1999 void
2000 repodata_shrink(Repodata *data, int end)
2001 {
2002   int i;
2003
2004   if (data->end <= end)
2005     return;
2006   if (data->start >= end)
2007     {
2008       if (data->attrs)
2009         {
2010           for (i = 0; i < data->end - data->start; i++)
2011             solv_free(data->attrs[i]);
2012           data->attrs = solv_free(data->attrs);
2013         }
2014       data->incoreoffset = solv_free(data->incoreoffset);
2015       data->start = data->end = 0;
2016       return;
2017     }
2018   if (data->attrs)
2019     {
2020       for (i = end; i < data->end; i++)
2021         solv_free(data->attrs[i - data->start]);
2022       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2023     }
2024   if (data->incoreoffset)
2025     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2026   data->end = end;
2027 }
2028
2029 /* extend repodata so that it includes solvables from start to start + num - 1 */
2030 void
2031 repodata_extend_block(Repodata *data, Id start, Id num)
2032 {
2033   if (!num)
2034     return;
2035   if (!data->incoreoffset)
2036     {
2037       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2038       data->start = start;
2039       data->end = start + num;
2040       return;
2041     }
2042   repodata_extend(data, start);
2043   if (num > 1)
2044     repodata_extend(data, start + num - 1);
2045 }
2046
2047 /**********************************************************************/
2048
2049
2050 #define REPODATA_ATTRS_BLOCK 31
2051 #define REPODATA_ATTRDATA_BLOCK 1023
2052 #define REPODATA_ATTRIDDATA_BLOCK 63
2053 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2054
2055
2056 Id
2057 repodata_new_handle(Repodata *data)
2058 {
2059   if (!data->nxattrs)
2060     {
2061       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2062       data->nxattrs = 2;        /* -1: SOLVID_META */
2063     }
2064   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2065   data->xattrs[data->nxattrs] = 0;
2066   return -(data->nxattrs++);
2067 }
2068
2069 static inline Id **
2070 repodata_get_attrp(Repodata *data, Id handle)
2071 {
2072   if (handle < 0)
2073     {
2074       if (handle == SOLVID_META && !data->xattrs)
2075         {
2076           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2077           data->nxattrs = 2;
2078         }
2079       return data->xattrs - handle;
2080     }
2081   if (handle < data->start || handle >= data->end)
2082     repodata_extend(data, handle);
2083   if (!data->attrs)
2084     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2085   return data->attrs + (handle - data->start);
2086 }
2087
2088 static void
2089 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2090 {
2091   Id *pp;
2092   Id *ap, **app;
2093   int i;
2094
2095   app = repodata_get_attrp(data, handle);
2096   ap = *app;
2097   i = 0;
2098   if (ap)
2099     {
2100       /* Determine equality based on the name only, allows us to change
2101          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2102       for (pp = ap; *pp; pp += 2)
2103         if (data->keys[*pp].name == data->keys[keyid].name)
2104           break;
2105       if (*pp)
2106         {
2107           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2108             {
2109               pp[0] = keyid;
2110               pp[1] = val;
2111             }
2112           return;
2113         }
2114       i = pp - ap;
2115     }
2116   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2117   *app = ap;
2118   pp = ap + i;
2119   *pp++ = keyid;
2120   *pp++ = val;
2121   *pp = 0;
2122 }
2123
2124
2125 static void
2126 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2127 {
2128   Id keyid;
2129
2130   keyid = repodata_key2id(data, key, 1);
2131   repodata_insert_keyid(data, solvid, keyid, val, 1);
2132 }
2133
2134 void
2135 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2136 {
2137   Repokey key;
2138   key.name = keyname;
2139   key.type = REPOKEY_TYPE_ID;
2140   key.size = 0;
2141   key.storage = KEY_STORAGE_INCORE;
2142   repodata_set(data, solvid, &key, id);
2143 }
2144
2145 void
2146 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2147 {
2148   Repokey key;
2149   key.name = keyname;
2150   key.type = REPOKEY_TYPE_NUM;
2151   key.size = 0;
2152   key.storage = KEY_STORAGE_INCORE;
2153   if (num >= 0x80000000)
2154     {
2155       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2156       data->attrnum64data[data->attrnum64datalen] = num;
2157       num = 0x80000000 | data->attrnum64datalen++;
2158     }
2159   repodata_set(data, solvid, &key, (Id)num);
2160 }
2161
2162 void
2163 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2164 {
2165   Repokey key;
2166   Id id;
2167   if (data->localpool)
2168     id = stringpool_str2id(&data->spool, str, 1);
2169   else
2170     id = pool_str2id(data->repo->pool, str, 1);
2171   key.name = keyname;
2172   key.type = REPOKEY_TYPE_ID;
2173   key.size = 0;
2174   key.storage = KEY_STORAGE_INCORE;
2175   repodata_set(data, solvid, &key, id);
2176 }
2177
2178 void
2179 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2180 {
2181   Repokey key;
2182   key.name = keyname;
2183   key.type = REPOKEY_TYPE_CONSTANT;
2184   key.size = constant;
2185   key.storage = KEY_STORAGE_INCORE;
2186   repodata_set(data, solvid, &key, 0);
2187 }
2188
2189 void
2190 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2191 {
2192   Repokey key;
2193   key.name = keyname;
2194   key.type = REPOKEY_TYPE_CONSTANTID;
2195   key.size = id;
2196   key.storage = KEY_STORAGE_INCORE;
2197   repodata_set(data, solvid, &key, 0);
2198 }
2199
2200 void
2201 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2202 {
2203   Repokey key;
2204   key.name = keyname;
2205   key.type = REPOKEY_TYPE_VOID;
2206   key.size = 0;
2207   key.storage = KEY_STORAGE_INCORE;
2208   repodata_set(data, solvid, &key, 0);
2209 }
2210
2211 void
2212 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2213 {
2214   Repokey key;
2215   int l;
2216
2217   l = strlen(str) + 1;
2218   key.name = keyname;
2219   key.type = REPOKEY_TYPE_STR;
2220   key.size = 0;
2221   key.storage = KEY_STORAGE_INCORE;
2222   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2223   memcpy(data->attrdata + data->attrdatalen, str, l);
2224   repodata_set(data, solvid, &key, data->attrdatalen);
2225   data->attrdatalen += l;
2226 }
2227
2228 void
2229 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2230 {
2231   Repokey key;
2232   unsigned char *dp;
2233
2234   if (len < 0)
2235     return;
2236   key.name = keyname;
2237   key.type = REPOKEY_TYPE_BINARY;
2238   key.size = 0;
2239   key.storage = KEY_STORAGE_INCORE;
2240   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2241   dp = data->attrdata + data->attrdatalen;
2242   if (len >= (1 << 14))
2243     {
2244       if (len >= (1 << 28))
2245         *dp++ = (len >> 28) | 128;
2246       if (len >= (1 << 21))
2247         *dp++ = (len >> 21) | 128;
2248       *dp++ = (len >> 14) | 128;
2249     }
2250   if (len >= (1 << 7))
2251     *dp++ = (len >> 7) | 128;
2252   *dp++ = len & 127;
2253   if (len)
2254     memcpy(dp, buf, len);
2255   repodata_set(data, solvid, &key, data->attrdatalen);
2256   data->attrdatalen = dp + len - data->attrdata;
2257 }
2258
2259 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2260  * so that the caller can append entrysize new elements plus the termination zero there */
2261 static void
2262 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2263 {
2264   int oldsize;
2265   Id *ida, *pp, **ppp;
2266
2267   /* check if it is the same as last time, this speeds things up a lot */
2268   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2269     {
2270       /* great! just append the new data */
2271       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2272       data->attriddatalen--;    /* overwrite terminating 0  */
2273       data->lastdatalen += entrysize;
2274       return;
2275     }
2276
2277   ppp = repodata_get_attrp(data, handle);
2278   pp = *ppp;
2279   if (pp)
2280     {
2281       for (; *pp; pp += 2)
2282         if (data->keys[*pp].name == keyname)
2283           break;
2284     }
2285   if (!pp || !*pp || data->keys[*pp].type != keytype)
2286     {
2287       /* not found. allocate new key */
2288       Repokey key;
2289       Id keyid;
2290       key.name = keyname;
2291       key.type = keytype;
2292       key.size = 0;
2293       key.storage = KEY_STORAGE_INCORE;
2294       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2295       keyid = repodata_key2id(data, &key, 1);
2296       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2297       data->lasthandle = handle;
2298       data->lastkey = keyid;
2299       data->lastdatalen = data->attriddatalen + entrysize + 1;
2300       return;
2301     }
2302   oldsize = 0;
2303   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2304     oldsize += entrysize;
2305   if (ida + 1 == data->attriddata + data->attriddatalen)
2306     {
2307       /* this was the last entry, just append it */
2308       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2309       data->attriddatalen--;    /* overwrite terminating 0  */
2310     }
2311   else
2312     {
2313       /* too bad. move to back. */
2314       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2315       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2316       pp[1] = data->attriddatalen;
2317       data->attriddatalen += oldsize;
2318     }
2319   data->lasthandle = handle;
2320   data->lastkey = *pp;
2321   data->lastdatalen = data->attriddatalen + entrysize + 1;
2322 }
2323
2324 void
2325 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2326                       const unsigned char *str)
2327 {
2328   Repokey key;
2329   int l;
2330
2331   if (!(l = solv_chksum_len(type)))
2332     return;
2333   key.name = keyname;
2334   key.type = type;
2335   key.size = 0;
2336   key.storage = KEY_STORAGE_INCORE;
2337   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2338   memcpy(data->attrdata + data->attrdatalen, str, l);
2339   repodata_set(data, solvid, &key, data->attrdatalen);
2340   data->attrdatalen += l;
2341 }
2342
2343 void
2344 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2345                       const char *str)
2346 {
2347   unsigned char buf[64];
2348   int l;
2349
2350   if (!(l = solv_chksum_len(type)))
2351     return;
2352   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2353     return;
2354   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2355 }
2356
2357 const char *
2358 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2359 {
2360   int l;
2361
2362   if (!(l = solv_chksum_len(type)))
2363     return "";
2364   return pool_bin2hex(data->repo->pool, buf, l);
2365 }
2366
2367 /* rpm filenames don't contain the epoch, so strip it */
2368 static inline const char *
2369 evrid2vrstr(Pool *pool, Id evrid)
2370 {
2371   const char *p, *evr = pool_id2str(pool, evrid);
2372   if (!evr)
2373     return evr;
2374   for (p = evr; *p >= '0' && *p <= '9'; p++)
2375     ;
2376   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2377 }
2378
2379 static inline void
2380 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2381 {
2382   Id id;
2383   if (data->localpool)
2384     id = stringpool_strn2id(&data->spool, str, l, 1);
2385   else
2386     id = pool_strn2id(data->repo->pool, str, l, 1);
2387   repodata_set_id(data, solvid, keyname, id);
2388 }
2389
2390 static inline void
2391 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2392 {
2393   if (!str[l])
2394     repodata_set_str(data, solvid, keyname, str);
2395   else
2396     {
2397       char *s = solv_strdup(str);
2398       s[l] = 0;
2399       repodata_set_str(data, solvid, keyname, s);
2400       free(s);
2401     }
2402 }
2403
2404 void
2405 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2406 {
2407   Pool *pool = data->repo->pool;
2408   Solvable *s;
2409   const char *str, *fp;
2410   int l = 0;
2411
2412   if (medianr)
2413     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2414   if (!dir)
2415     {
2416       if ((dir = strrchr(file, '/')) != 0)
2417         {
2418           l = dir - file;
2419           dir = file;
2420           file = dir + l + 1;
2421           if (!l)
2422             l++;
2423         }
2424     }
2425   else
2426     l = strlen(dir);
2427   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2428     {
2429       dir += 2;
2430       l -= 2;
2431     }
2432   if (l == 1 && dir[0] == '.')
2433     l = 0;
2434   s = pool->solvables + solvid;
2435   if (dir && l)
2436     {
2437       str = pool_id2str(pool, s->arch);
2438       if (!strncmp(dir, str, l) && !str[l])
2439         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2440       else
2441         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2442     }
2443   fp = file;
2444   str = pool_id2str(pool, s->name);
2445   l = strlen(str);
2446   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2447     {
2448       fp += l + 1;
2449       str = evrid2vrstr(pool, s->evr);
2450       l = strlen(str);
2451       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2452         {
2453           fp += l + 1;
2454           str = pool_id2str(pool, s->arch);
2455           l = strlen(str);
2456           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2457             {
2458               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2459               return;
2460             }
2461         }
2462     }
2463   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2464 }
2465
2466 /* XXX: medianr is currently not stored */
2467 void
2468 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2469 {
2470   int l = 0;
2471   const char *evr, *suf, *s;
2472
2473   if (!dir)
2474     {
2475       if ((dir = strrchr(file, '/')) != 0)
2476         {
2477           l = dir - file;
2478           dir = file;
2479           file = dir + l + 1;
2480           if (!l)
2481             l++;
2482         }
2483     }
2484   else
2485     l = strlen(dir);
2486   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2487     {
2488       dir += 2;
2489       l -= 2;
2490     }
2491   if (l == 1 && dir[0] == '.')
2492     l = 0;
2493   if (dir && l)
2494     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2495   evr = strchr(file, '-');
2496   if (evr)
2497     {
2498       for (s = evr - 1; s > file; s--)
2499         if (*s == '-')
2500           {
2501             evr = s;
2502             break;
2503           }
2504     }
2505   suf = strrchr(file, '.');
2506   if (suf)
2507     {
2508       for (s = suf - 1; s > file; s--)
2509         if (*s == '.')
2510           {
2511             suf = s;
2512             break;
2513           }
2514       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2515         {
2516           /* We accept one more item as suffix.  */
2517           for (s = suf - 1; s > file; s--)
2518             if (*s == '.')
2519               {
2520                 suf = s;
2521                 break;
2522               }
2523         }
2524     }
2525   if (!evr)
2526     suf = 0;
2527   if (suf && evr && suf < evr)
2528     suf = 0;
2529   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2530   if (evr)
2531     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2532   if (suf)
2533     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2534 }
2535
2536 void
2537 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2538 {
2539   Pool *pool = data->repo->pool;
2540   Solvable *s = pool->solvables + solvid;
2541   const char *p, *sevr, *sarch, *name, *evr;
2542
2543   p = strrchr(sourcepkg, '.');
2544   if (!p || strcmp(p, ".rpm") != 0)
2545     {
2546       if (*sourcepkg)
2547         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2548       return;
2549     }
2550   p--;
2551   while (p > sourcepkg && *p != '.')
2552     p--;
2553   if (*p != '.' || p == sourcepkg)
2554     return;
2555   sarch = p-- + 1;
2556   while (p > sourcepkg && *p != '-')
2557     p--;
2558   if (*p != '-' || p == sourcepkg)
2559     return;
2560   p--;
2561   while (p > sourcepkg && *p != '-')
2562     p--;
2563   if (*p != '-' || p == sourcepkg)
2564     return;
2565   sevr = p + 1;
2566   pool = s->repo->pool;
2567
2568   name = pool_id2str(pool, s->name);
2569   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2570     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2571   else
2572     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2573
2574   evr = evrid2vrstr(pool, s->evr);
2575   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2576     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2577   else
2578     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2579
2580   if (!strcmp(sarch, "src.rpm"))
2581     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2582   else if (!strcmp(sarch, "nosrc.rpm"))
2583     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2584   else
2585     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2586 }
2587
2588 void
2589 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2590 {
2591   Repokey key;
2592   int i;
2593
2594   key.name = keyname;
2595   key.type = REPOKEY_TYPE_IDARRAY;
2596   key.size = 0;
2597   key.storage = KEY_STORAGE_INCORE;
2598   repodata_set(data, solvid, &key, data->attriddatalen);
2599   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2600   for (i = 0; i < q->count; i++)
2601     data->attriddata[data->attriddatalen++] = q->elements[i];
2602   data->attriddata[data->attriddatalen++] = 0;
2603 }
2604
2605 void
2606 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2607 {
2608   assert(dir);
2609 #if 0
2610 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2611 #endif
2612   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2613   data->attriddata[data->attriddatalen++] = dir;
2614   data->attriddata[data->attriddatalen++] = num;
2615   data->attriddata[data->attriddatalen++] = num2;
2616   data->attriddata[data->attriddatalen++] = 0;
2617 }
2618
2619 void
2620 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2621 {
2622   Id stroff;
2623   int l;
2624
2625   assert(dir);
2626   l = strlen(str) + 1;
2627   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2628   memcpy(data->attrdata + data->attrdatalen, str, l);
2629   stroff = data->attrdatalen;
2630   data->attrdatalen += l;
2631
2632 #if 0
2633 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2634 #endif
2635   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2636   data->attriddata[data->attriddatalen++] = dir;
2637   data->attriddata[data->attriddatalen++] = stroff;
2638   data->attriddata[data->attriddatalen++] = 0;
2639 }
2640
2641 void
2642 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2643 {
2644 #if 0
2645 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2646 #endif
2647   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2648   data->attriddata[data->attriddatalen++] = id;
2649   data->attriddata[data->attriddatalen++] = 0;
2650 }
2651
2652 void
2653 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2654                            const char *str)
2655 {
2656   Id id;
2657   if (data->localpool)
2658     id = stringpool_str2id(&data->spool, str, 1);
2659   else
2660     id = pool_str2id(data->repo->pool, str, 1);
2661   repodata_add_idarray(data, solvid, keyname, id);
2662 }
2663
2664 void
2665 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2666 {
2667   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2668   data->attriddata[data->attriddatalen++] = ghandle;
2669   data->attriddata[data->attriddatalen++] = 0;
2670 }
2671
2672 void
2673 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2674 {
2675   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2676   data->attriddata[data->attriddatalen++] = ghandle;
2677   data->attriddata[data->attriddatalen++] = 0;
2678 }
2679
2680 void
2681 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2682 {
2683   Id *pp, *ap, **app;
2684   app = repodata_get_attrp(data, solvid);
2685   ap = *app;
2686   if (!ap)
2687     return;
2688   for (; *ap; ap += 2)
2689     if (data->keys[*ap].name == keyname)
2690       break;
2691   if (!*ap)
2692     return;
2693   pp = ap;
2694   ap += 2;
2695   for (; *ap; ap += 2)
2696     {
2697       if (data->keys[*ap].name == keyname)
2698         continue;
2699       *pp++ = ap[0];
2700       *pp++ = ap[1];
2701     }
2702   *pp = 0;
2703 }
2704
2705 /* XXX: does not work correctly, needs fix in iterators! */
2706 void
2707 repodata_unset(Repodata *data, Id solvid, Id keyname)
2708 {
2709   Repokey key;
2710   key.name = keyname;
2711   key.type = REPOKEY_TYPE_DELETED;
2712   key.size = 0;
2713   key.storage = KEY_STORAGE_INCORE;
2714   repodata_set(data, solvid, &key, 0);
2715 }
2716
2717 /* add all (uninternalized) attrs from src to dest */
2718 void
2719 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2720 {
2721   Id *keyp;
2722   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2723     return;
2724   for (; *keyp; keyp += 2)
2725     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2726 }
2727
2728 /* add some (uninternalized) attrs from src to dest */
2729 void
2730 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2731 {
2732   Id *keyp;
2733   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2734     return;
2735   for (; *keyp; keyp += 2)
2736     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2737       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2738 }
2739
2740 /* swap (uninternalized) attrs from src and dest */
2741 void
2742 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2743 {
2744   Id *tmpattrs;
2745   if (!data->attrs || dest == src)
2746     return;
2747   tmpattrs = data->attrs[dest - data->start];
2748   data->attrs[dest - data->start] = data->attrs[src - data->start];
2749   data->attrs[src - data->start] = tmpattrs;
2750 }
2751
2752
2753 /**********************************************************************/
2754
2755 /* TODO: unify with repo_write and repo_solv! */
2756
2757 #define EXTDATA_BLOCK 1023
2758
2759 struct extdata {
2760   unsigned char *buf;
2761   int len;
2762 };
2763
2764 static void
2765 data_addid(struct extdata *xd, Id sx)
2766 {
2767   unsigned int x = (unsigned int)sx;
2768   unsigned char *dp;
2769
2770   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2771   dp = xd->buf + xd->len;
2772
2773   if (x >= (1 << 14))
2774     {
2775       if (x >= (1 << 28))
2776         *dp++ = (x >> 28) | 128;
2777       if (x >= (1 << 21))
2778         *dp++ = (x >> 21) | 128;
2779       *dp++ = (x >> 14) | 128;
2780     }
2781   if (x >= (1 << 7))
2782     *dp++ = (x >> 7) | 128;
2783   *dp++ = x & 127;
2784   xd->len = dp - xd->buf;
2785 }
2786
2787 static void
2788 data_addid64(struct extdata *xd, unsigned long long x)
2789 {
2790   if (x >= 0x100000000)
2791     {
2792       if ((x >> 35) != 0)
2793         {
2794           data_addid(xd, (Id)(x >> 35));
2795           xd->buf[xd->len - 1] |= 128;
2796         }
2797       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2798       xd->buf[xd->len - 5] = (x >> 28) | 128;
2799     }
2800   else
2801     data_addid(xd, (Id)x);
2802 }
2803
2804 static void
2805 data_addideof(struct extdata *xd, Id sx, int eof)
2806 {
2807   unsigned int x = (unsigned int)sx;
2808   unsigned char *dp;
2809
2810   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2811   dp = xd->buf + xd->len;
2812
2813   if (x >= (1 << 13))
2814     {
2815       if (x >= (1 << 27))
2816         *dp++ = (x >> 27) | 128;
2817       if (x >= (1 << 20))
2818         *dp++ = (x >> 20) | 128;
2819       *dp++ = (x >> 13) | 128;
2820     }
2821   if (x >= (1 << 6))
2822     *dp++ = (x >> 6) | 128;
2823   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2824   xd->len = dp - xd->buf;
2825 }
2826
2827 static void
2828 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2829 {
2830   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2831   memcpy(xd->buf + xd->len, blob, len);
2832   xd->len += len;
2833 }
2834
2835 /*********************************/
2836
2837 /* internalalize some key into incore/vincore data */
2838
2839 static void
2840 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2841                        struct extdata *newvincore,
2842                        Id *schema,
2843                        Repokey *key, Id val)
2844 {
2845   Id *ida;
2846   struct extdata *xd;
2847   unsigned int oldvincorelen = 0;
2848   Id schemaid, *sp;
2849
2850   xd = newincore;
2851   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2852     {
2853       xd = newvincore;
2854       oldvincorelen = xd->len;
2855     }
2856   switch (key->type)
2857     {
2858     case REPOKEY_TYPE_VOID:
2859     case REPOKEY_TYPE_CONSTANT:
2860     case REPOKEY_TYPE_CONSTANTID:
2861       break;
2862     case REPOKEY_TYPE_STR:
2863       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2864       break;
2865     case REPOKEY_TYPE_MD5:
2866       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2867       break;
2868     case REPOKEY_TYPE_SHA1:
2869       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2870       break;
2871     case REPOKEY_TYPE_SHA256:
2872       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2873       break;
2874     case REPOKEY_TYPE_NUM:
2875       if (val & 0x80000000)
2876         {
2877           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2878           break;
2879         }
2880       /* FALLTHROUGH */
2881     case REPOKEY_TYPE_ID:
2882     case REPOKEY_TYPE_DIR:
2883       data_addid(xd, val);
2884       break;
2885     case REPOKEY_TYPE_BINARY:
2886       {
2887         Id len;
2888         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2889         dp += (unsigned int)len;
2890         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2891       }
2892       break;
2893     case REPOKEY_TYPE_IDARRAY:
2894       for (ida = data->attriddata + val; *ida; ida++)
2895         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2896       break;
2897     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2898       for (ida = data->attriddata + val; *ida; ida += 3)
2899         {
2900           data_addid(xd, ida[0]);
2901           data_addid(xd, ida[1]);
2902           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2903         }
2904       break;
2905     case REPOKEY_TYPE_DIRSTRARRAY:
2906       for (ida = data->attriddata + val; *ida; ida += 2)
2907         {
2908           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2909           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2910         }
2911       break;
2912     case REPOKEY_TYPE_FIXARRAY:
2913       {
2914         int num = 0;
2915         schemaid = 0;
2916         for (ida = data->attriddata + val; *ida; ida++)
2917           {
2918             Id *kp;
2919             sp = schema;
2920             kp = data->xattrs[-*ida];
2921             if (!kp)
2922               continue;
2923             num++;
2924             for (;*kp; kp += 2)
2925               *sp++ = *kp;
2926             *sp = 0;
2927             if (!schemaid)
2928               schemaid = repodata_schema2id(data, schema, 1);
2929             else if (schemaid != repodata_schema2id(data, schema, 0))
2930               {
2931                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2932                 exit(1);
2933               }
2934           }
2935         if (!num)
2936           break;
2937         data_addid(xd, num);
2938         data_addid(xd, schemaid);
2939         for (ida = data->attriddata + val; *ida; ida++)
2940           {
2941             Id *kp = data->xattrs[-*ida];
2942             if (!kp)
2943               continue;
2944             for (;*kp; kp += 2)
2945               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2946           }
2947         break;
2948       }
2949     case REPOKEY_TYPE_FLEXARRAY:
2950       {
2951         int num = 0;
2952         for (ida = data->attriddata + val; *ida; ida++)
2953           num++;
2954         data_addid(xd, num);
2955         for (ida = data->attriddata + val; *ida; ida++)
2956           {
2957             Id *kp = data->xattrs[-*ida];
2958             if (!kp)
2959               {
2960                 data_addid(xd, 0);      /* XXX */
2961                 continue;
2962               }
2963             sp = schema;
2964             for (;*kp; kp += 2)
2965               *sp++ = *kp;
2966             *sp = 0;
2967             schemaid = repodata_schema2id(data, schema, 1);
2968             data_addid(xd, schemaid);
2969             kp = data->xattrs[-*ida];
2970             for (;*kp; kp += 2)
2971               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2972           }
2973         break;
2974       }
2975     default:
2976       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2977       exit(1);
2978     }
2979   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2980     {
2981       /* put offset/len in incore */
2982       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2983       oldvincorelen = xd->len - oldvincorelen;
2984       data_addid(newincore, oldvincorelen);
2985     }
2986 }
2987
2988 void
2989 repodata_internalize(Repodata *data)
2990 {
2991   Repokey *key, solvkey;
2992   Id entry, nentry;
2993   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2994   unsigned char *dp, *ndp;
2995   int newschema, oldcount;
2996   struct extdata newincore;
2997   struct extdata newvincore;
2998   Id solvkeyid;
2999
3000   if (!data->attrs && !data->xattrs)
3001     return;
3002
3003   newvincore.buf = data->vincore;
3004   newvincore.len = data->vincorelen;
3005
3006   /* find the solvables key, create if needed */
3007   memset(&solvkey, 0, sizeof(solvkey));
3008   solvkey.name = REPOSITORY_SOLVABLES;
3009   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3010   solvkey.size = 0;
3011   solvkey.storage = KEY_STORAGE_INCORE;
3012   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3013
3014   schema = solv_malloc2(data->nkeys, sizeof(Id));
3015   seen = solv_malloc2(data->nkeys, sizeof(Id));
3016
3017   /* Merge the data already existing (in data->schemata, ->incoredata and
3018      friends) with the new attributes in data->attrs[].  */
3019   nentry = data->end - data->start;
3020   memset(&newincore, 0, sizeof(newincore));
3021   data_addid(&newincore, 0);    /* start data at offset 1 */
3022
3023   data->mainschema = 0;
3024   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3025
3026   /* join entry data */
3027   /* we start with the meta data, entry -1 */
3028   for (entry = -1; entry < nentry; entry++)
3029     {
3030       memset(seen, 0, data->nkeys * sizeof(Id));
3031       oldschema = 0;
3032       dp = data->incoredata;
3033       if (dp)
3034         {
3035           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3036           dp = data_read_id(dp, &oldschema);
3037         }
3038 #if 0
3039 fprintf(stderr, "oldschema %d\n", oldschema);
3040 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3041 fprintf(stderr, "schemadata %p\n", data->schemadata);
3042 #endif
3043       /* seen: -1: old data  0: skipped  >0: id + 1 */
3044       newschema = 0;
3045       oldcount = 0;
3046       sp = schema;
3047       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3048         {
3049           if (seen[*keyp])
3050             {
3051               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3052               exit(1);
3053             }
3054           seen[*keyp] = -1;
3055           *sp++ = *keyp;
3056           oldcount++;
3057         }
3058       if (entry >= 0)
3059         keyp = data->attrs ? data->attrs[entry] : 0;
3060       else
3061         {
3062           /* strip solvables key */
3063           *sp = 0;
3064           for (sp = keyp = schema; *sp; sp++)
3065             if (*sp != solvkeyid)
3066               *keyp++ = *sp;
3067             else
3068               oldcount--;
3069           sp = keyp;
3070           seen[solvkeyid] = 0;
3071           keyp = data->xattrs ? data->xattrs[1] : 0;
3072         }
3073       if (keyp)
3074         for (; *keyp; keyp += 2)
3075           {
3076             if (!seen[*keyp])
3077               {
3078                 newschema = 1;
3079                 *sp++ = *keyp;
3080               }
3081             seen[*keyp] = keyp[1] + 1;
3082           }
3083       if (entry < 0 && data->end != data->start)
3084         {
3085           *sp++ = solvkeyid;
3086           newschema = 1;
3087         }
3088       *sp = 0;
3089       if (newschema)
3090         /* Ideally we'd like to sort the new schema here, to ensure
3091            schema equality independend of the ordering.  We can't do that
3092            yet.  For once see below (old ids need to come before new ids).
3093            An additional difficulty is that we also need to move
3094            the values with the keys.  */
3095         schemaid = repodata_schema2id(data, schema, 1);
3096       else
3097         schemaid = oldschema;
3098
3099
3100       /* Now create data blob.  We walk through the (possibly new) schema
3101          and either copy over old data, or insert the new.  */
3102       /* XXX Here we rely on the fact that the (new) schema has the form
3103          o1 o2 o3 o4 ... | n1 n2 n3 ...
3104          (oX being the old keyids (possibly overwritten), and nX being
3105           the new keyids).  This rules out sorting the keyids in order
3106          to ensure a small schema count.  */
3107       if (entry >= 0)
3108         data->incoreoffset[entry] = newincore.len;
3109       data_addid(&newincore, schemaid);
3110       if (entry == -1)
3111         {
3112           data->mainschema = schemaid;
3113           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3114         }
3115       keypstart = data->schemadata + data->schemata[schemaid];
3116       for (keyp = keypstart; *keyp; keyp++)
3117         {
3118           if (entry == -1)
3119             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
3120           if (*keyp == solvkeyid)
3121             {
3122               /* add flexarray entry count */
3123               data_addid(&newincore, data->end - data->start);
3124               break;
3125             }
3126           key = data->keys + *keyp;
3127 #if 0
3128           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));
3129 #endif
3130           ndp = dp;
3131           if (oldcount)
3132             {
3133               /* Skip the data associated with this old key.  */
3134               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3135                 {
3136                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3137                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3138                 }
3139               else if (key->storage == KEY_STORAGE_INCORE)
3140                 ndp = data_skip_key(data, dp, key);
3141               oldcount--;
3142             }
3143           if (seen[*keyp] == -1)
3144             {
3145               /* If this key was an old one _and_ was not overwritten with
3146                  a different value copy over the old value (we skipped it
3147                  above).  */
3148               if (dp != ndp)
3149                 data_addblob(&newincore, dp, ndp - dp);
3150               seen[*keyp] = 0;
3151             }
3152           else if (seen[*keyp])
3153             {
3154               /* Otherwise we have a new value.  Parse it into the internal
3155                  form.  */
3156               repodata_serialize_key(data, &newincore, &newvincore,
3157                                      schema, key, seen[*keyp] - 1);
3158             }
3159           dp = ndp;
3160         }
3161       if (entry >= 0 && data->attrs && data->attrs[entry])
3162         data->attrs[entry] = solv_free(data->attrs[entry]);
3163     }
3164   /* free all xattrs */
3165   for (entry = 0; entry < data->nxattrs; entry++)
3166     if (data->xattrs[entry])
3167       solv_free(data->xattrs[entry]);
3168   data->xattrs = solv_free(data->xattrs);
3169   data->nxattrs = 0;
3170
3171   data->lasthandle = 0;
3172   data->lastkey = 0;
3173   data->lastdatalen = 0;
3174   solv_free(schema);
3175   solv_free(seen);
3176   repodata_free_schemahash(data);
3177
3178   solv_free(data->incoredata);
3179   data->incoredata = newincore.buf;
3180   data->incoredatalen = newincore.len;
3181   data->incoredatafree = 0;
3182
3183   solv_free(data->vincore);
3184   data->vincore = newvincore.buf;
3185   data->vincorelen = newvincore.len;
3186
3187   data->attrs = solv_free(data->attrs);
3188   data->attrdata = solv_free(data->attrdata);
3189   data->attriddata = solv_free(data->attriddata);
3190   data->attrnum64data = solv_free(data->attrnum64data);
3191   data->attrdatalen = 0;
3192   data->attriddatalen = 0;
3193   data->attrnum64datalen = 0;
3194 }
3195
3196 void
3197 repodata_disable_paging(Repodata *data)
3198 {
3199   if (maybe_load_repodata(data, 0))
3200     {
3201       repopagestore_disable_paging(&data->store);
3202       data->storestate++;
3203     }
3204 }
3205
3206 static void
3207 repodata_load_stub(Repodata *data)
3208 {
3209   Repo *repo = data->repo;
3210   Pool *pool = repo->pool;
3211   int r, i;
3212   struct _Pool_tmpspace oldtmpspace;
3213
3214   if (!pool->loadcallback)
3215     {
3216       data->state = REPODATA_ERROR;
3217       return;
3218     }
3219   data->state = REPODATA_LOADING;
3220
3221   /* save tmp space */
3222   oldtmpspace = pool->tmpspace;
3223   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3224
3225   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3226
3227   /* restore tmp space */
3228   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3229     solv_free(pool->tmpspace.buf[i]);
3230   pool->tmpspace = oldtmpspace;
3231
3232   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3233 }
3234
3235 void
3236 repodata_create_stubs(Repodata *data)
3237 {
3238   Repo *repo = data->repo;
3239   Pool *pool = repo->pool;
3240   Repodata *sdata;
3241   int *stubdataids;
3242   Dataiterator di;
3243   Id xkeyname = 0;
3244   int i, cnt = 0;
3245   int repodataid;
3246   int datastart, dataend;
3247
3248   repodataid = data - repo->repodata;
3249   datastart = data->start;
3250   dataend = data->end;
3251   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3252   while (dataiterator_step(&di))
3253     {
3254       if (di.data - repo->repodata != repodataid)
3255         continue;
3256       cnt++;
3257     }
3258   dataiterator_free(&di);
3259   if (!cnt)
3260     return;
3261   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3262   for (i = 0; i < cnt; i++)
3263     {
3264       sdata = repo_add_repodata(repo, 0);
3265       if (dataend > datastart)
3266         repodata_extend_block(sdata, datastart, dataend - datastart);
3267       stubdataids[i] = sdata - repo->repodata;
3268       sdata->state = REPODATA_STUB;
3269       sdata->loadcallback = repodata_load_stub;
3270     }
3271   i = 0;
3272   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3273   sdata = 0;
3274   while (dataiterator_step(&di))
3275     {
3276       if (di.data - repo->repodata != repodataid)
3277         continue;
3278       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3279         {
3280           dataiterator_entersub(&di);
3281           sdata = repo->repodata + stubdataids[i++];
3282           xkeyname = 0;
3283           continue;
3284         }
3285       switch (di.key->type)
3286         {
3287         case REPOKEY_TYPE_ID:
3288           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3289           break;
3290         case REPOKEY_TYPE_CONSTANTID:
3291           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3292           break;
3293         case REPOKEY_TYPE_STR:
3294           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3295           break;
3296         case REPOKEY_TYPE_VOID:
3297           repodata_set_void(sdata, SOLVID_META, di.key->name);
3298           break;
3299         case REPOKEY_TYPE_NUM:
3300           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3301           break;
3302         case REPOKEY_TYPE_MD5:
3303         case REPOKEY_TYPE_SHA1:
3304         case REPOKEY_TYPE_SHA256:
3305           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3306           break;
3307         case REPOKEY_TYPE_IDARRAY:
3308           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3309           if (di.key->name == REPOSITORY_KEYS)
3310             {
3311               Repokey xkey;
3312
3313               if (!xkeyname)
3314                 {
3315                   if (!di.kv.eof)
3316                     xkeyname = di.kv.id;
3317                   continue;
3318                 }
3319               xkey.name = xkeyname;
3320               xkey.type = di.kv.id;
3321               xkey.storage = KEY_STORAGE_INCORE;
3322               xkey.size = 0;
3323               repodata_key2id(sdata, &xkey, 1);
3324               xkeyname = 0;
3325             }
3326         default:
3327           break;
3328         }
3329     }
3330   dataiterator_free(&di);
3331   for (i = 0; i < cnt; i++)
3332     repodata_internalize(repo->repodata + stubdataids[i]);
3333   solv_free(stubdataids);
3334 }
3335
3336 unsigned int
3337 repodata_memused(Repodata *data)
3338 {
3339   return data->incoredatalen + data->vincorelen;
3340 }
3341
3342 /*
3343 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3344 */