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