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