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