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