- do not enter solvable data when not in leaf level
[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
32 #include "repopack.h"
33 #include "repopage.h"
34
35 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
36                                   unsigned char *out, unsigned int out_len);
37 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
38                                               unsigned int in_len,
39                                               unsigned char *out,
40                                               unsigned int out_len);
41
42 #define REPODATA_BLOCK 255
43
44
45 void
46 repodata_init(Repodata *data, Repo *repo, int localpool)
47 {
48   memset(data, 0, sizeof (*data));
49   data->repo = repo;
50   data->localpool = localpool;
51   if (localpool)
52     stringpool_init_empty(&data->spool);
53   data->keys = sat_calloc(1, sizeof(Repokey));
54   data->nkeys = 1;
55   data->schemata = sat_calloc(1, sizeof(Id));
56   data->schemadata = sat_calloc(1, sizeof(Id));
57   data->nschemata = 1;
58   data->schemadatalen = 1;
59   repopagestore_init(&data->store);
60 }
61
62 void
63 repodata_free(Repodata *data)
64 {
65   int i;
66
67   sat_free(data->keys);
68
69   sat_free(data->schemata);
70   sat_free(data->schemadata);
71   sat_free(data->schematahash);
72
73   stringpool_free(&data->spool);
74   dirpool_free(&data->dirpool);
75
76   sat_free(data->mainschemaoffsets);
77   sat_free(data->incoredata);
78   sat_free(data->incoreoffset);
79   sat_free(data->verticaloffset);
80
81   repopagestore_free(&data->store);
82
83   sat_free(data->vincore);
84
85   if (data->attrs)
86     for (i = 0; i < data->end - data->start; i++)
87       sat_free(data->attrs[i]);
88   sat_free(data->attrs);
89   if (data->xattrs)
90     for (i = 0; i < data->nxattrs; i++)
91       sat_free(data->xattrs[i]);
92   sat_free(data->xattrs);
93
94   sat_free(data->attrdata);
95   sat_free(data->attriddata);
96 }
97
98
99 /***************************************************************
100  * key pool management
101  */
102
103 /* this is not so time critical that we need a hash, so we do a simple
104  * linear search */
105 Id
106 repodata_key2id(Repodata *data, Repokey *key, int create)
107 {
108   Id keyid;
109
110   for (keyid = 1; keyid < data->nkeys; keyid++)
111     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
112       {
113         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
114           continue;
115         break;
116       }
117   if (keyid == data->nkeys)
118     {
119       if (!create)
120         return 0;
121       /* allocate new key */
122       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
123       data->keys[data->nkeys++] = *key;
124       if (data->verticaloffset)
125         {
126           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
127           data->verticaloffset[data->nkeys - 1] = 0;
128         }
129       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
130     }
131   return keyid;
132 }
133
134
135 /***************************************************************
136  * schema pool management
137  */
138
139 #define SCHEMATA_BLOCK 31
140 #define SCHEMATADATA_BLOCK 255
141
142 Id
143 repodata_schema2id(Repodata *data, Id *schema, int create)
144 {
145   int h, len, i;
146   Id *sp, cid;
147   Id *schematahash;
148
149   if ((schematahash = data->schematahash) == 0)
150     {
151       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
152       for (i = 0; i < data->nschemata; i++)
153         {
154           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
155             h = h * 7 + *sp++;
156           h &= 255;
157           schematahash[h] = i + 1;
158         }
159       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
160       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
161     }
162
163   for (sp = schema, len = 0, h = 0; *sp; len++)
164     h = h * 7 + *sp++;
165   h &= 255;
166   len++;
167
168   cid = schematahash[h];
169   if (cid)
170     {
171       cid--;
172       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
173         return cid;
174       /* cache conflict */
175       for (cid = 0; cid < data->nschemata; cid++)
176         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
177           return cid;
178     }
179   /* a new one */
180   if (!create)
181     return 0;
182   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
183   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
184   /* add schema */
185   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
186   data->schemata[data->nschemata] = data->schemadatalen;
187   data->schemadatalen += len;
188   schematahash[h] = data->nschemata + 1;
189 #if 0
190 fprintf(stderr, "schema2id: new schema\n");
191 #endif
192   return data->nschemata++;
193 }
194
195 void
196 repodata_free_schemahash(Repodata *data)
197 {
198   data->schematahash = sat_free(data->schematahash);
199   /* shrink arrays */
200   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
201   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
202 }
203
204
205 /***************************************************************
206  * dir pool management
207  */
208
209 Id
210 repodata_str2dir(Repodata *data, const char *dir, int create)
211 {
212   Id id, parent;
213   const char *dire;
214
215   parent = 0;
216   while (*dir == '/' && dir[1] == '/')
217     dir++;
218   if (*dir == '/' && !dir[1])
219     return 1;
220   while (*dir)
221     {
222       dire = strchrnul(dir, '/');
223       if (data->localpool)
224         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
225       else
226         id = strn2id(data->repo->pool, dir, dire - dir, create);
227       if (!id)
228         return 0;
229       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
230       if (!parent)
231         return 0;
232       if (!*dire)
233         break;
234       dir = dire + 1;
235       while (*dir == '/')
236         dir++;
237     }
238   return parent;
239 }
240
241 const char *
242 repodata_dir2str(Repodata *data, Id did, const char *suf)
243 {
244   Pool *pool = data->repo->pool;
245   int l = 0;
246   Id parent, comp;
247   const char *comps;
248   char *p;
249
250   if (!did)
251     return suf ? suf : "";
252   parent = did;
253   while (parent)
254     {
255       comp = dirpool_compid(&data->dirpool, parent);
256       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
257       l += strlen(comps);
258       parent = dirpool_parent(&data->dirpool, parent);
259       if (parent)
260         l++;
261     }
262   if (suf)
263     l += strlen(suf) + 1;
264   p = pool_alloctmpspace(pool, l + 1) + l;
265   *p = 0;
266   if (suf)
267     {
268       p -= strlen(suf);
269       strcpy(p, suf);
270       *--p = '/';
271     }
272   parent = did;
273   while (parent)
274     {
275       comp = dirpool_compid(&data->dirpool, parent);
276       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
277       l = strlen(comps);
278       p -= l;
279       strncpy(p, comps, l);
280       parent = dirpool_parent(&data->dirpool, parent);
281       if (parent)
282         *--p = '/';
283     }
284   return p;
285 }
286
287
288 /***************************************************************
289  * data management
290  */
291
292 static inline unsigned char *
293 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
294 {
295   Id *keyp = data->schemadata + data->schemata[schema];
296   for (; *keyp; keyp++)
297     dp = data_skip_key(data, dp, data->keys + *keyp);
298   return dp;
299 }
300
301 unsigned char *
302 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
303 {
304   int nentries, schema;
305   switch(key->type)
306     {
307     case REPOKEY_TYPE_FIXARRAY:
308       dp = data_read_id(dp, &nentries);
309       if (!nentries)
310         return dp;
311       dp = data_read_id(dp, &schema);
312       while (nentries--)
313         dp = data_skip_schema(data, dp, schema);
314       return dp;
315     case REPOKEY_TYPE_FLEXARRAY:
316       dp = data_read_id(dp, &nentries);
317       while (nentries--)
318         {
319           dp = data_read_id(dp, &schema);
320           dp = data_skip_schema(data, dp, schema);
321         }
322       return dp;
323     default:
324       if (key->storage == KEY_STORAGE_INCORE)
325         dp = data_skip(dp, key->type);
326       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
327         {
328           dp = data_skip(dp, REPOKEY_TYPE_ID);
329           dp = data_skip(dp, REPOKEY_TYPE_ID);
330         }
331       return dp;
332     }
333 }
334
335 static unsigned char *
336 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
337 {
338   Id k;
339
340   if (!keyid)
341     return 0;
342   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
343     {
344       int i;
345       for (i = 0; (k = *keyp++) != 0; i++)
346         if (k == keyid)
347           return data->incoredata + data->mainschemaoffsets[i];
348       return 0;
349     }
350   while ((k = *keyp++) != 0)
351     {
352       if (k == keyid)
353         return dp;
354       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
355         {
356           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
357           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
358           continue;
359         }
360       if (data->keys[k].storage != KEY_STORAGE_INCORE)
361         continue;
362       dp = data_skip_key(data, dp, data->keys + k);
363     }
364   return 0;
365 }
366
367 static unsigned char *
368 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
369 {
370   unsigned char *dp;
371   if (!len)
372     return 0;
373   if (off >= data->lastverticaloffset)
374     {
375       off -= data->lastverticaloffset;
376       if (off + len > data->vincorelen)
377         return 0;
378       return data->vincore + off;
379     }
380   if (off + len > key->size)
381     return 0;
382   /* we now have the offset, go into vertical */
383   off += data->verticaloffset[key - data->keys];
384   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
385   dp = repopagestore_load_page_range(&data->store, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
386   if (dp)
387     dp += off % BLOB_PAGESIZE;
388   return dp;
389 }
390
391 static inline unsigned char *
392 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
393 {
394   unsigned char *dp = *dpp;
395
396   if (!dp)
397     return 0;
398   if (key->storage == KEY_STORAGE_INCORE)
399     {
400       if (advance)
401         *dpp = data_skip_key(data, dp, key);
402       return dp;
403     }
404   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
405     {
406       Id off, len;
407       dp = data_read_id(dp, &off);
408       dp = data_read_id(dp, &len);
409       if (advance)
410         *dpp = dp;
411       return get_vertical_data(data, key, off, len);
412     }
413   return 0;
414 }
415
416 static int
417 load_repodata(Repodata *data)
418 {
419   if (data->loadcallback)
420     {
421       data->loadcallback(data);
422       if (data->state == REPODATA_AVAILABLE)
423         return 1;
424     }
425   data->state = REPODATA_ERROR;
426   return 0;
427 }
428
429 static inline int
430 maybe_load_repodata(Repodata *data, Id keyname)
431 {
432   if (keyname && !repodata_precheck_keyname(data, keyname))
433     return 0;   /* do not bother... */
434   switch(data->state)
435     {
436     case REPODATA_STUB:
437       if (keyname)
438         {
439           int i;
440           for (i = 0; i < data->nkeys; i++)
441             if (keyname == data->keys[i].name)
442               break;
443           if (i == data->nkeys)
444             return 0;
445         }
446       return load_repodata(data);
447     case REPODATA_ERROR:
448       return 0;
449     case REPODATA_AVAILABLE:
450       return 1;
451     default:
452       data->state = REPODATA_ERROR;
453       return 0;
454     }
455 }
456
457 static inline unsigned char *
458 solvid2data(Repodata *data, Id solvid, Id *schemap)
459 {
460   unsigned char *dp = data->incoredata;
461   if (!dp)
462     return 0;
463   if (solvid == SOLVID_META)    /* META */
464     dp += 1;
465   else if (solvid == SOLVID_POS)        /* META */
466     {
467       Pool *pool = data->repo->pool;
468       if (data->repo != pool->pos.repo)
469         return 0;
470       if (data != data->repo->repodata + pool->pos.repodataid)
471         return 0;
472       *schemap = pool->pos.schema;
473       return data->incoredata + pool->pos.dp;
474     }
475   else
476     {
477       if (solvid < data->start || solvid >= data->end)
478         return 0;
479       dp += data->incoreoffset[solvid - data->start];
480     }
481   return data_read_id(dp, schemap);
482 }
483
484 /************************************************************************
485  * data lookup
486  */
487
488 static inline unsigned char *
489 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
490 {
491   unsigned char *dp;
492   Id schema, *keyp, *kp;
493   Repokey *key;
494
495   if (!maybe_load_repodata(data, keyname))
496     return 0;
497   dp = solvid2data(data, solvid, &schema);
498   if (!dp)
499     return 0;
500   keyp = data->schemadata + data->schemata[schema];
501   for (kp = keyp; *kp; kp++)
502     if (data->keys[*kp].name == keyname)
503       break;
504   if (!*kp)
505     return 0;
506   *keypp = key = data->keys + *kp;
507   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
508     return dp;  /* no need to forward... */
509   dp = forward_to_key(data, *kp, keyp, dp);
510   if (!dp)
511     return 0;
512   return get_data(data, key, &dp, 0);
513 }
514
515
516 Id
517 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
518 {
519   unsigned char *dp;
520   Repokey *key;
521   Id id;
522
523   dp = find_key_data(data, solvid, keyname, &key);
524   if (!dp)
525     return 0;
526   if (key->type == REPOKEY_TYPE_CONSTANTID)
527     return key->size;
528   if (key->type != REPOKEY_TYPE_ID)
529     return 0;
530   dp = data_read_id(dp, &id);
531   return id;
532 }
533
534 Id
535 repodata_globalize_id(Repodata *data, Id id, int create)
536 {
537   if (!id || !data || !data->localpool)
538     return id;
539   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
540 }
541
542 const char *
543 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
544 {
545   unsigned char *dp;
546   Repokey *key;
547   Id id;
548
549   dp = find_key_data(data, solvid, keyname, &key);
550   if (!dp)
551     return 0;
552   if (key->type == REPOKEY_TYPE_STR)
553     return (const char *)dp;
554   if (key->type == REPOKEY_TYPE_CONSTANTID)
555     return id2str(data->repo->pool, key->size);
556   if (key->type == REPOKEY_TYPE_ID)
557     dp = data_read_id(dp, &id);
558   else
559     return 0;
560   if (data->localpool)
561     return data->spool.stringspace + data->spool.strings[id];
562   return id2str(data->repo->pool, id);
563 }
564
565 int
566 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned int *value)
567 {
568   unsigned char *dp;
569   Repokey *key;
570   KeyValue kv;
571
572   *value = 0;
573   dp = find_key_data(data, solvid, keyname, &key);
574   if (!dp)
575     return 0;
576   if (key->type == REPOKEY_TYPE_NUM
577       || key->type == REPOKEY_TYPE_U32
578       || key->type == REPOKEY_TYPE_CONSTANT)
579     {
580       dp = data_fetch(dp, &kv, key);
581       *value = kv.num;
582       return 1;
583     }
584   return 0;
585 }
586
587 int
588 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
589 {
590   Id schema;
591   Id *keyp;
592   unsigned char *dp;
593
594   if (!maybe_load_repodata(data, keyname))
595     return 0;
596   dp = solvid2data(data, solvid, &schema);
597   if (!dp)
598     return 0;
599   /* can't use find_key_data as we need to test the type */
600   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
601     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
602       return 1;
603   return 0;
604 }
605
606 const unsigned char *
607 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
608 {
609   unsigned char *dp;
610   Repokey *key;
611
612   dp = find_key_data(data, solvid, keyname, &key);
613   if (!dp)
614     return 0;
615   *typep = key->type;
616   return dp;
617 }
618
619
620 /************************************************************************
621  * data search
622  */
623
624
625 int
626 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
627 {
628   switch (key->type)
629     {
630     case REPOKEY_TYPE_ID:
631     case REPOKEY_TYPE_CONSTANTID:
632     case REPOKEY_TYPE_IDARRAY:
633       if (data && data->localpool)
634         kv->str = stringpool_id2str(&data->spool, kv->id);
635       else
636         kv->str = id2str(pool, kv->id);
637       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
638         {
639           const char *s = strchr(kv->str, ':');
640           if (s)
641             kv->str = s + 1;
642         }
643       return 1;
644     case REPOKEY_TYPE_STR:
645       return 1;
646     case REPOKEY_TYPE_DIRSTRARRAY:
647       if (!(flags & SEARCH_FILES))
648         return 1;       /* match just the basename */
649       /* Put the full filename into kv->str.  */
650       kv->str = repodata_dir2str(data, kv->id, kv->str);
651       /* And to compensate for that put the "empty" directory into
652          kv->id, so that later calls to repodata_dir2str on this data
653          come up with the same filename again.  */
654       kv->id = 0;
655       return 1;
656     default:
657       return 0;
658     }
659 }
660
661
662 struct subschema_data {
663   Solvable *s;
664   void *cbdata;
665   KeyValue *parent;
666 };
667
668 /* search a specific repodata */
669 void
670 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
671 {
672   Id schema;
673   Repokey *key;
674   Id keyid, *kp, *keyp;
675   unsigned char *dp, *ddp;
676   int onekey = 0;
677   int stop;
678   KeyValue kv;
679   Solvable *s;
680
681   if (!maybe_load_repodata(data, keyname))
682     return;
683   if (solvid == SOLVID_SUBSCHEMA)
684     {
685       struct subschema_data *subd = cbdata;
686       cbdata = subd->cbdata;
687       s = subd->s;
688       schema = subd->parent->id;
689       dp = (unsigned char *)subd->parent->str;
690       kv.parent = subd->parent;
691     }
692   else
693     {
694       schema = 0;
695       dp = solvid2data(data, solvid, &schema);
696       if (!dp)
697         return;
698       s = data->repo->pool->solvables + solvid;
699       kv.parent = 0;
700     }
701   keyp = data->schemadata + data->schemata[schema];
702   if (keyname)
703     {
704       /* search for a specific key */
705       for (kp = keyp; *kp; kp++)
706         if (data->keys[*kp].name == keyname)
707           break;
708       if (!*kp)
709         return;
710       dp = forward_to_key(data, *kp, keyp, dp);
711       if (!dp)
712         return;
713       keyp = kp;
714       onekey = 1;
715     }
716   while ((keyid = *keyp++) != 0)
717     {
718       stop = 0;
719       key = data->keys + keyid;
720       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
721
722       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
723         {
724           struct subschema_data subd;
725           int nentries;
726           Id schema = 0;
727
728           subd.cbdata = cbdata;
729           subd.s = s;
730           subd.parent = &kv;
731           ddp = data_read_id(ddp, &nentries);
732           kv.num = nentries;
733           kv.entry = 0;
734           kv.eof = 0;
735           while (ddp && nentries > 0)
736             {
737               if (!--nentries)
738                 kv.eof = 1;
739               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
740                 ddp = data_read_id(ddp, &schema);
741               kv.id = schema;
742               kv.str = (char *)ddp;
743               stop = callback(cbdata, s, data, key, &kv);
744               if (stop > SEARCH_NEXT_KEY)
745                 return;
746               if (stop && stop != SEARCH_ENTERSUB)
747                 break;
748               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
749                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
750               ddp = data_skip_schema(data, ddp, schema);
751               kv.entry++;
752             }
753           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
754             {
755               /* sentinel */
756               kv.eof = 2;
757               kv.str = (char *)ddp;
758               stop = callback(cbdata, s, data, key, &kv);
759               if (stop > SEARCH_NEXT_KEY)
760                 return;
761             }
762           if (onekey)
763             return;
764           continue;
765         }
766       kv.entry = 0;
767       do
768         {
769           ddp = data_fetch(ddp, &kv, key);
770           if (!ddp)
771             break;
772           stop = callback(cbdata, s, data, key, &kv);
773           kv.entry++;
774         }
775       while (!kv.eof && !stop);
776       if (onekey || stop > SEARCH_NEXT_KEY)
777         return;
778     }
779 }
780
781 void
782 repodata_setpos_kv(Repodata *data, KeyValue *kv)
783 {
784   Pool *pool = data->repo->pool;
785   if (!kv)
786     pool_clear_pos(pool);
787   else
788     {
789       pool->pos.repo = data->repo;
790       pool->pos.repodataid = data - data->repo->repodata;
791       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
792       pool->pos.schema = kv->id;
793     }
794 }
795
796 /************************************************************************
797  * data iterator functions
798  */
799
800 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
801   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
802   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
803   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
804   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
805   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
806   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
807   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
808   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
809   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
810   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
811   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
812   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
813   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
814 };
815
816 static inline Id *
817 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
818 {
819   kv->id = keyname;
820   switch (keyname)
821     {
822     case SOLVABLE_NAME:
823       kv->eof = 1;
824       return &s->name;
825     case SOLVABLE_ARCH:
826       kv->eof = 1;
827       return &s->arch;
828     case SOLVABLE_EVR:
829       kv->eof = 1;
830       return &s->evr;
831     case SOLVABLE_VENDOR:
832       kv->eof = 1;
833       return &s->vendor;
834     case SOLVABLE_PROVIDES:
835       kv->eof = 0;
836       return s->provides ? s->repo->idarraydata + s->provides : 0;
837     case SOLVABLE_OBSOLETES:
838       kv->eof = 0;
839       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
840     case SOLVABLE_CONFLICTS:
841       kv->eof = 0;
842       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
843     case SOLVABLE_REQUIRES:
844       kv->eof = 0;
845       return s->requires ? s->repo->idarraydata + s->requires : 0;
846     case SOLVABLE_RECOMMENDS:
847       kv->eof = 0;
848       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
849     case SOLVABLE_SUPPLEMENTS:
850       kv->eof = 0;
851       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
852     case SOLVABLE_SUGGESTS:
853       kv->eof = 0;
854       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
855     case SOLVABLE_ENHANCES:
856       kv->eof = 0;
857       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
858     case RPM_RPMDBID:
859       kv->eof = 1;
860       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
861     default:
862       return 0;
863     }
864 }
865
866 int
867 datamatcher_init(Datamatcher *ma, const char *match, int flags)
868 {
869   ma->match = (void *)match;
870   ma->flags = flags;
871   ma->error = 0;
872   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
873     {
874       ma->match = sat_calloc(1, sizeof(regex_t));
875       ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
876       if (ma->error)
877         {
878           sat_free(ma->match);
879           ma->match = (void *)match;
880           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
881         }
882     }
883   return ma->error;
884 }
885
886 void
887 datamatcher_free(Datamatcher *ma)
888 {
889   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
890     {
891       regfree(ma->match);
892       ma->match = sat_free(ma->match);
893     }
894 }
895
896 int
897 datamatcher_match(Datamatcher *ma, const char *str)
898 {
899   switch ((ma->flags & SEARCH_STRINGMASK))
900     {
901     case SEARCH_SUBSTRING:
902       if (ma->flags & SEARCH_NOCASE)
903         {
904           if (!strcasestr(str, (const char *)ma->match))
905             return 0;
906         }
907       else
908         {
909           if (!strstr(str, (const char *)ma->match))
910             return 0;
911         }
912       break;
913     case SEARCH_STRING:
914       if (ma->flags & SEARCH_NOCASE)
915         {
916           if (strcasecmp((const char *)ma->match, str))
917             return 0;
918         }
919       else
920         {
921           if (strcmp((const char *)ma->match, str))
922             return 0;
923         }
924       break;
925     case SEARCH_GLOB:
926       if (fnmatch((const char *)ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
927         return 0;
928       break;
929     case SEARCH_REGEX:
930       if (regexec((const regex_t *)ma->match, str, 0, NULL, 0))
931         return 0;
932       break;
933     default:
934       return 0;
935     }
936   return 1;
937 }
938
939 enum {
940   di_bye,
941
942   di_enterrepo,
943   di_entersolvable,
944   di_enterrepodata,
945   di_enterschema,
946   di_enterkey,
947
948   di_nextattr,
949   di_nextkey,
950   di_nextrepodata,
951   di_nextsolvable,
952   di_nextrepo,
953
954   di_enterarray,
955   di_nextarrayelement,
956
957   di_entersub,
958   di_leavesub,
959
960   di_nextsolvableattr,
961   di_nextsolvablekey,
962   di_entersolvablekey
963 };
964
965 /* see repo.h for documentation */
966 int
967 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
968 {
969   memset(di, 0, sizeof(*di));
970   di->pool = pool;
971   if (!pool || (repo && repo->pool != pool))
972     {
973       di->state = di_bye;
974       return -1;
975     }
976   if (match)
977     {
978       int error;
979       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
980         {
981           di->state = di_bye;
982           return error;
983         }
984     }
985   di->repo = repo;
986   di->keyname = keyname;
987   di->keynames[0] = keyname;
988   di->flags = flags & ~SEARCH_THISSOLVID;
989   if (!pool->nrepos)
990     {
991       di->state = di_bye;
992       return 0;
993     }
994   if (repo)
995     di->repoid = -1;
996   if (p)
997     dataiterator_jump_to_solvid(di, p);
998   else
999     {
1000       if (!di->repo)
1001         di->repo = pool->repos[0];
1002       di->state = di_enterrepo;
1003     }
1004   return 0;
1005 }
1006
1007 void
1008 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1009 {
1010   int i;
1011
1012   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1013     {
1014       di->state = di_bye;       /* sorry */
1015       return;
1016     }
1017   for (i = di->nkeynames + 1; i > 0; i--)
1018     di->keynames[i] = di->keynames[i - 1];
1019   di->keynames[0] = di->keyname = keyname;
1020   di->nkeynames++;
1021 }
1022
1023 void
1024 dataiterator_free(Dataiterator *di)
1025 {
1026   if (di->matcher.match)
1027     datamatcher_free(&di->matcher);
1028 }
1029
1030 static inline unsigned char *
1031 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1032 {
1033   Id *keyp = di->keyp;
1034   Repokey *keys = di->data->keys;
1035   unsigned char *dp;
1036
1037   for (keyp = di->keyp; *keyp; keyp++)
1038     if (keys[*keyp].name == keyname)
1039       break;
1040   if (!*keyp)
1041     return 0;
1042   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1043   if (!dp)
1044     return 0;
1045   di->keyp = keyp;
1046   return dp;
1047 }
1048
1049 int
1050 dataiterator_step(Dataiterator *di)
1051 {
1052   Id schema;
1053
1054   for (;;)
1055     {
1056       switch (di->state)
1057         {
1058         case di_enterrepo: di_enterrepo:
1059           if (!di->repo)
1060             goto di_bye;
1061           if (!(di->flags & SEARCH_THISSOLVID))
1062             {
1063               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1064               goto di_nextsolvable;
1065             }
1066           /* FALLTHROUGH */
1067
1068         case di_entersolvable: di_entersolvable:
1069           if (di->repodataid >= 0)
1070             {
1071               di->repodataid = 0;       /* reset repodata iterator */
1072               if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents == di->nkeynames)
1073                 {
1074                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1075                   di->data = 0;
1076                   goto di_entersolvablekey;
1077                 }
1078             }
1079           /* FALLTHROUGH */
1080
1081         case di_enterrepodata: di_enterrepodata:
1082           if (di->repodataid >= 0)
1083             {
1084               if (di->repodataid >= di->repo->nrepodata)
1085                 goto di_nextsolvable;
1086               di->data = di->repo->repodata + di->repodataid;
1087             }
1088           if (!maybe_load_repodata(di->data, di->keyname))
1089             goto di_nextrepodata;
1090           di->dp = solvid2data(di->data, di->solvid, &schema);
1091           if (!di->dp)
1092             goto di_nextrepodata;
1093           if (di->solvid == SOLVID_POS)
1094             di->solvid = di->pool->pos.solvid;
1095           /* reset key iterator */
1096           di->keyp = di->data->schemadata + di->data->schemata[schema];
1097           /* FALLTHROUGH */
1098
1099         case di_enterschema: di_enterschema:
1100           if (di->keyname)
1101             di->dp = dataiterator_find_keyname(di, di->keyname);
1102           if (!di->dp || !*di->keyp)
1103             {
1104               if (di->kv.parent)
1105                 goto di_leavesub;
1106               goto di_nextrepodata;
1107             }
1108           /* FALLTHROUGH */
1109
1110         case di_enterkey: di_enterkey:
1111           di->kv.entry = -1;
1112           di->key = di->data->keys + *di->keyp;
1113           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1114           if (!di->ddp)
1115             goto di_nextkey;
1116           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1117             goto di_enterarray;
1118           if (di->nparents < di->nkeynames)
1119             goto di_nextkey;
1120           /* FALLTHROUGH */
1121
1122         case di_nextattr:
1123           di->kv.entry++;
1124           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1125           if (di->kv.eof)
1126             di->state = di_nextkey;
1127           else
1128             di->state = di_nextattr;
1129           break;
1130
1131         case di_nextkey: di_nextkey:
1132           if (!di->keyname && *++di->keyp)
1133             goto di_enterkey;
1134           if (di->kv.parent)
1135             goto di_leavesub;
1136           /* FALLTHROUGH */
1137
1138         case di_nextrepodata: di_nextrepodata:
1139           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
1140               goto di_enterrepodata;
1141           /* FALLTHROUGH */
1142
1143         case di_nextsolvable: di_nextsolvable:
1144           if (!(di->flags & SEARCH_THISSOLVID))
1145             {
1146               if (di->solvid < 0)
1147                 di->solvid = di->repo->start;
1148               else
1149                 di->solvid++;
1150               for (; di->solvid < di->repo->end; di->solvid++)
1151                 {
1152                   if (di->pool->solvables[di->solvid].repo == di->repo)
1153                     goto di_entersolvable;
1154                 }
1155             }
1156           /* FALLTHROUGH */
1157
1158         case di_nextrepo:
1159           if (di->repoid >= 0)
1160             {
1161               di->repoid++;
1162               if (di->repoid < di->pool->nrepos)
1163                 {
1164                   di->repo = di->pool->repos[di->repoid];
1165                   goto di_enterrepo;
1166                 }
1167             }
1168         /* FALLTHROUGH */
1169
1170         case di_bye: di_bye:
1171           di->state = di_bye;
1172           return 0;
1173
1174         case di_enterarray: di_enterarray:
1175           if (di->key->name == REPOSITORY_SOLVABLES)
1176             goto di_nextkey;
1177           di->ddp = data_read_id(di->ddp, &di->kv.num);
1178           di->kv.eof = 0;
1179           di->kv.entry = -1;
1180           /* FALLTHROUGH */
1181
1182         case di_nextarrayelement: di_nextarrayelement:
1183           di->kv.entry++;
1184           if (di->kv.entry)
1185             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1186           if (di->kv.entry == di->kv.num)
1187             {
1188               if (di->nparents < di->nkeynames)
1189                 goto di_nextkey;
1190               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1191                 goto di_nextkey;
1192               di->kv.str = (char *)di->ddp;
1193               di->kv.eof = 2;
1194               di->state = di_nextkey;
1195               break;
1196             }
1197           if (di->kv.entry == di->kv.num - 1)
1198             di->kv.eof = 1;
1199           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1200             di->ddp = data_read_id(di->ddp, &di->kv.id);
1201           di->kv.str = (char *)di->ddp;
1202           if (di->nparents < di->nkeynames)
1203             goto di_entersub;
1204           if ((di->flags & SEARCH_SUB) != 0)
1205             di->state = di_entersub;
1206           else
1207             di->state = di_nextarrayelement;
1208           break;
1209
1210         case di_entersub: di_entersub:
1211           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1212             goto di_nextarrayelement;   /* sorry, full */
1213           di->parents[di->nparents].kv = di->kv;
1214           di->parents[di->nparents].dp = di->dp;
1215           di->parents[di->nparents].keyp = di->keyp;
1216           di->dp = (unsigned char *)di->kv.str;
1217           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1218           memset(&di->kv, 0, sizeof(di->kv));
1219           di->kv.parent = &di->parents[di->nparents].kv;
1220           di->nparents++;
1221           di->keyname = di->keynames[di->nparents];
1222           goto di_enterschema;
1223
1224         case di_leavesub: di_leavesub:
1225           di->nparents--;
1226           di->dp = di->parents[di->nparents].dp;
1227           di->kv = di->parents[di->nparents].kv;
1228           di->keyp = di->parents[di->nparents].keyp;
1229           di->key = di->data->keys + *di->keyp;
1230           di->ddp = (unsigned char *)di->kv.str;
1231           di->keyname = di->keynames[di->nparents];
1232           goto di_nextarrayelement;
1233
1234         /* special solvable attr handling follows */
1235
1236         case di_nextsolvableattr:
1237           di->kv.id = *di->idp++;
1238           di->kv.entry++;
1239           if (!*di->idp)
1240             {
1241               di->kv.eof = 1;
1242               di->state = di_nextsolvablekey;
1243             }
1244           break;
1245
1246         case di_nextsolvablekey: di_nextsolvablekey:
1247           if (di->keyname || di->key->name == RPM_RPMDBID)
1248             goto di_enterrepodata;
1249           di->key++;
1250           /* FALLTHROUGH */
1251
1252         case di_entersolvablekey: di_entersolvablekey:
1253           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1254           if (!di->idp || !di->idp[0])
1255             goto di_nextsolvablekey;
1256           di->kv.id = di->idp[0];
1257           di->kv.num = di->idp[0];
1258           di->idp++;
1259           if (!di->kv.eof && !di->idp[0])
1260             di->kv.eof = 1;
1261           di->kv.entry = 0;
1262           if (di->kv.eof)
1263             di->state = di_nextsolvablekey;
1264           else
1265             di->state = di_nextsolvableattr;
1266           break;
1267         }
1268
1269       if (di->matcher.match)
1270         {
1271           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1272             {
1273               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1274                 return 1;
1275               continue;
1276             }
1277           if (!datamatcher_match(&di->matcher, di->kv.str))
1278             continue;
1279         }
1280       /* found something! */
1281       return 1;
1282     }
1283 }
1284
1285 void
1286 dataiterator_entersub(Dataiterator *di)
1287 {
1288   if (di->state == di_nextarrayelement)
1289     di->state = di_entersub;
1290 }
1291
1292 void
1293 dataiterator_setpos(Dataiterator *di)
1294 {
1295   if (di->kv.eof == 2)
1296     {
1297       pool_clear_pos(di->pool);
1298       return;
1299     }
1300   di->pool->pos.solvid = di->solvid;
1301   di->pool->pos.repo = di->repo;
1302   di->pool->pos.repodataid = di->data - di->repo->repodata;
1303   di->pool->pos.schema = di->kv.id;
1304   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1305 }
1306
1307 void
1308 dataiterator_setpos_parent(Dataiterator *di)
1309 {
1310   if (!di->kv.parent || di->kv.parent->eof == 2)
1311     {
1312       pool_clear_pos(di->pool);
1313       return;
1314     }
1315   di->pool->pos.solvid = di->solvid;
1316   di->pool->pos.repo = di->repo;
1317   di->pool->pos.repodataid = di->data - di->repo->repodata;
1318   di->pool->pos.schema = di->kv.parent->id;
1319   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1320 }
1321
1322 void
1323 dataiterator_skip_attribute(Dataiterator *di)
1324 {
1325   if (di->state == di_nextsolvableattr)
1326     di->state = di_nextsolvablekey;
1327   else
1328     di->state = di_nextkey;
1329 }
1330
1331 void
1332 dataiterator_skip_solvable(Dataiterator *di)
1333 {
1334   di->state = di_nextsolvable;
1335 }
1336
1337 void
1338 dataiterator_skip_repo(Dataiterator *di)
1339 {
1340   di->state = di_nextrepo;
1341 }
1342
1343 void
1344 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1345 {
1346   di->nparents = 0;
1347   if (solvid == SOLVID_POS)
1348     {
1349       di->repo = di->pool->pos.repo;
1350       if (!di->repo)
1351         {
1352           di->state = di_bye;
1353           return;
1354         }
1355       di->repoid = -1;
1356       di->data = di->repo->repodata + di->pool->pos.repodataid;
1357       di->repodataid = -1;
1358       di->solvid = solvid;
1359       di->state = di_enterrepo;
1360       di->flags |= SEARCH_THISSOLVID;
1361       return;
1362     }
1363   if (solvid > 0)
1364     {
1365       di->repo = di->pool->solvables[solvid].repo;
1366       di->repoid = -1;
1367     }
1368   else if (di->repoid >= 0)
1369     {
1370       if (!di->pool->nrepos)
1371         {
1372           di->state = di_bye;
1373           return;
1374         }
1375       di->repo = di->pool->repos[0];
1376       di->repoid = 0;
1377     }
1378   di->repodataid = 0;
1379   di->solvid = solvid;
1380   if (solvid)
1381     di->flags |= SEARCH_THISSOLVID;
1382   di->state = di_enterrepo;
1383 }
1384
1385 void
1386 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1387 {
1388   di->nparents = 0;
1389   di->repo = repo;
1390   di->repoid = -1;
1391   di->repodataid = 0;
1392   di->solvid = 0;
1393   di->flags &= ~SEARCH_THISSOLVID;
1394   di->state = di_enterrepo;
1395 }
1396
1397 int
1398 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1399 {
1400   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1401     return 0;
1402   if (!ma)
1403     return 1;
1404   return datamatcher_match(ma, di->kv.str);
1405 }
1406
1407 /************************************************************************
1408  * data modify functions
1409  */
1410
1411 /* extend repodata so that it includes solvables p */
1412 void
1413 repodata_extend(Repodata *data, Id p)
1414 {
1415   if (data->start == data->end)
1416     data->start = data->end = p;
1417   if (p >= data->end)
1418     {
1419       int old = data->end - data->start;
1420       int new = p - data->end + 1;
1421       if (data->attrs)
1422         {
1423           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1424           memset(data->attrs + old, 0, new * sizeof(Id *));
1425         }
1426       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1427       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1428       data->end = p + 1;
1429     }
1430   if (p < data->start)
1431     {
1432       int old = data->end - data->start;
1433       int new = data->start - p;
1434       if (data->attrs)
1435         {
1436           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1437           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1438           memset(data->attrs, 0, new * sizeof(Id *));
1439         }
1440       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1441       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1442       memset(data->incoreoffset, 0, new * sizeof(Id));
1443       data->start = p;
1444     }
1445 }
1446
1447 /* extend repodata so that it includes solvables from start to start + num - 1 */
1448 void
1449 repodata_extend_block(Repodata *data, Id start, Id num)
1450 {
1451   if (!num)
1452     return;
1453   if (!data->incoreoffset)
1454     {
1455       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1456       data->start = start;
1457       data->end = start + num;
1458       return;
1459     }
1460   repodata_extend(data, start);
1461   if (num > 1)
1462     repodata_extend(data, start + num - 1);
1463 }
1464
1465 /**********************************************************************/
1466
1467 #define REPODATA_ATTRS_BLOCK 63
1468 #define REPODATA_ATTRDATA_BLOCK 1023
1469 #define REPODATA_ATTRIDDATA_BLOCK 63
1470
1471
1472 Id
1473 repodata_new_handle(Repodata *data)
1474 {
1475   if (!data->nxattrs)
1476     {
1477       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1478       data->nxattrs = 2;
1479     }
1480   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1481   data->xattrs[data->nxattrs] = 0;
1482   return -(data->nxattrs++);
1483 }
1484
1485 static inline Id **
1486 repodata_get_attrp(Repodata *data, Id handle)
1487 {
1488   if (handle == SOLVID_META)
1489     {
1490       if (!data->xattrs)
1491         {
1492           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1493           data->nxattrs = 2;
1494         }
1495     }
1496   if (handle < 0)
1497     return data->xattrs - handle;
1498   if (handle < data->start || handle >= data->end)
1499     repodata_extend(data, handle);
1500   if (!data->attrs)
1501     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1502   return data->attrs + (handle - data->start);
1503 }
1504
1505 static void
1506 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1507 {
1508   Id *pp;
1509   Id *ap, **app;
1510   int i;
1511
1512   app = repodata_get_attrp(data, handle);
1513   ap = *app;
1514   i = 0;
1515   if (ap)
1516     {
1517       for (pp = ap; *pp; pp += 2)
1518         /* Determine equality based on the name only, allows us to change
1519            type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1520         if (data->keys[*pp].name == data->keys[keyid].name)
1521           break;
1522       if (*pp)
1523         {
1524           if (overwrite)
1525             {
1526               pp[0] = keyid;
1527               pp[1] = val;
1528             }
1529           return;
1530         }
1531       i = pp - ap;
1532     }
1533   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1534   *app = ap;
1535   pp = ap + i;
1536   *pp++ = keyid;
1537   *pp++ = val;
1538   *pp = 0;
1539 }
1540
1541
1542 void
1543 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1544 {
1545   Id keyid;
1546
1547   keyid = repodata_key2id(data, key, 1);
1548   repodata_insert_keyid(data, solvid, keyid, val, 1);
1549 }
1550
1551 void
1552 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1553 {
1554   Repokey key;
1555   key.name = keyname;
1556   key.type = REPOKEY_TYPE_ID;
1557   key.size = 0;
1558   key.storage = KEY_STORAGE_INCORE;
1559   repodata_set(data, solvid, &key, id);
1560 }
1561
1562 void
1563 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1564 {
1565   Repokey key;
1566   key.name = keyname;
1567   key.type = REPOKEY_TYPE_NUM;
1568   key.size = 0;
1569   key.storage = KEY_STORAGE_INCORE;
1570   repodata_set(data, solvid, &key, (Id)num);
1571 }
1572
1573 void
1574 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
1575 {
1576   Repokey key;
1577   Id id;
1578   if (data->localpool)
1579     id = stringpool_str2id(&data->spool, str, 1);
1580   else
1581     id = str2id(data->repo->pool, str, 1);
1582   key.name = keyname;
1583   key.type = REPOKEY_TYPE_ID;
1584   key.size = 0;
1585   key.storage = KEY_STORAGE_INCORE;
1586   repodata_set(data, solvid, &key, id);
1587 }
1588
1589 void
1590 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
1591 {
1592   Repokey key;
1593   key.name = keyname;
1594   key.type = REPOKEY_TYPE_CONSTANT;
1595   key.size = constant;
1596   key.storage = KEY_STORAGE_INCORE;
1597   repodata_set(data, solvid, &key, 0);
1598 }
1599
1600 void
1601 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
1602 {
1603   Repokey key;
1604   key.name = keyname;
1605   key.type = REPOKEY_TYPE_CONSTANTID;
1606   key.size = id;
1607   key.storage = KEY_STORAGE_INCORE;
1608   repodata_set(data, solvid, &key, 0);
1609 }
1610
1611 void
1612 repodata_set_void(Repodata *data, Id solvid, Id keyname)
1613 {
1614   Repokey key;
1615   key.name = keyname;
1616   key.type = REPOKEY_TYPE_VOID;
1617   key.size = 0;
1618   key.storage = KEY_STORAGE_INCORE;
1619   repodata_set(data, solvid, &key, 0);
1620 }
1621
1622 void
1623 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
1624 {
1625   Repokey key;
1626   int l;
1627
1628   l = strlen(str) + 1;
1629   key.name = keyname;
1630   key.type = REPOKEY_TYPE_STR;
1631   key.size = 0;
1632   key.storage = KEY_STORAGE_INCORE;
1633   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1634   memcpy(data->attrdata + data->attrdatalen, str, l);
1635   repodata_set(data, solvid, &key, data->attrdatalen);
1636   data->attrdatalen += l;
1637 }
1638
1639 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
1640  * so that the caller can append the new element there */
1641 static void
1642 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
1643 {
1644   int oldsize;
1645   Id *ida, *pp, **ppp;
1646
1647   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
1648     {
1649       /* great! just append the new data */
1650       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1651       data->attriddatalen--;    /* overwrite terminating 0  */
1652       data->lastdatalen += entrysize;
1653       return;
1654     }
1655   ppp = repodata_get_attrp(data, handle);
1656   pp = *ppp;
1657   if (pp)
1658     for (; *pp; pp += 2)
1659       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
1660         break;
1661   if (!pp || !*pp)
1662     {
1663       /* not found. allocate new key */
1664       Repokey key;
1665       key.name = keyname;
1666       key.type = keytype;
1667       key.size = 0;
1668       key.storage = KEY_STORAGE_INCORE;
1669       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1670       repodata_set(data, handle, &key, data->attriddatalen);
1671       data->lasthandle = 0;     /* next time... */
1672       return;
1673     }
1674   oldsize = 0;
1675   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
1676     oldsize += entrysize;
1677   if (ida + 1 == data->attriddata + data->attriddatalen)
1678     {
1679       /* this was the last entry, just append it */
1680       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1681       data->attriddatalen--;    /* overwrite terminating 0  */
1682     }
1683   else
1684     {
1685       /* too bad. move to back. */
1686       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
1687       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
1688       pp[1] = data->attriddatalen;
1689       data->attriddatalen += oldsize;
1690     }
1691   data->lasthandle = handle;
1692   data->lastkey = *pp;
1693   data->lastdatalen = data->attriddatalen + entrysize + 1;
1694 }
1695
1696 static inline int
1697 checksumtype2len(Id type)
1698 {
1699   switch (type)
1700     {
1701     case REPOKEY_TYPE_MD5:
1702       return SIZEOF_MD5;
1703     case REPOKEY_TYPE_SHA1:
1704       return SIZEOF_SHA1;
1705     case REPOKEY_TYPE_SHA256:
1706       return SIZEOF_SHA256;
1707     default:
1708       return 0;
1709     }
1710 }
1711
1712 void
1713 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1714                       const unsigned char *str)
1715 {
1716   Repokey key;
1717   int l = checksumtype2len(type);
1718
1719   if (!l)
1720     return;
1721   key.name = keyname;
1722   key.type = type;
1723   key.size = 0;
1724   key.storage = KEY_STORAGE_INCORE;
1725   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1726   memcpy(data->attrdata + data->attrdatalen, str, l);
1727   repodata_set(data, solvid, &key, data->attrdatalen);
1728   data->attrdatalen += l;
1729 }
1730
1731 static int
1732 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
1733 {
1734   int i;
1735   for (i = 0; i < buflen; i++)
1736     {
1737 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
1738                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
1739                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
1740                 : -1)
1741       int v = c2h(*str);
1742       str++;
1743       if (v < 0)
1744         return 0;
1745       buf[i] = v;
1746       v = c2h(*str);
1747       str++;
1748       if (v < 0)
1749         return 0;
1750       buf[i] = (buf[i] << 4) | v;
1751 #undef c2h
1752     }
1753   return buflen;
1754 }
1755
1756 void
1757 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
1758                       const char *str)
1759 {
1760   unsigned char buf[64];
1761   int l = checksumtype2len(type);
1762
1763   if (!l)
1764     return;
1765   if (hexstr2bytes(buf, str, l) != l)
1766     return;
1767   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
1768 }
1769
1770 const char *
1771 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
1772 {
1773   int i, l;
1774   char *str, *s;
1775
1776   l = checksumtype2len(type);
1777   if (!l)
1778     return "";
1779   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
1780   for (i = 0; i < l; i++)
1781     {
1782       unsigned char v = buf[i];
1783       unsigned char w = v >> 4;
1784       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1785       w = v & 15;
1786       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
1787     }
1788   *s = 0;
1789   return str;
1790 }
1791
1792 /* rpm filenames don't contain the epoch, so strip it */
1793 static inline const char *
1794 evrid2vrstr(Pool *pool, Id evrid)
1795 {
1796   const char *p, *evr = id2str(pool, evrid);
1797   if (!evr)
1798     return evr;
1799   for (p = evr; *p >= '0' && *p <= '9'; p++)
1800     ;
1801   return p != evr && *p == ':' ? p + 1 : evr;
1802 }
1803
1804 void
1805 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
1806 {
1807   Pool *pool = data->repo->pool;
1808   Solvable *s;
1809   const char *str, *fp;
1810   int l = 0;
1811
1812   if (medianr)
1813     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
1814   if (!dir)
1815     {
1816       if ((dir = strrchr(file, '/')) != 0)
1817         {
1818           l = dir - file;
1819           dir = file;
1820           file = dir + l + 1;
1821           if (!l)
1822             l++;
1823         }
1824     }
1825   else
1826     l = strlen(dir);
1827   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
1828     {
1829       dir += 2;
1830       l -= 2;
1831     }
1832   if (l == 1 && dir[0] == '.')
1833     l = 0;
1834   s = pool->solvables + solvid;
1835   if (dir && l)
1836     {
1837       str = id2str(pool, s->arch);
1838       if (!strncmp(dir, str, l) && !str[l])
1839         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
1840       else if (!dir[l])
1841         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
1842       else
1843         {
1844           char *dir2 = strdup(dir);
1845           dir2[l] = 0;
1846           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
1847           free(dir2);
1848         }
1849     }
1850   fp = file;
1851   str = id2str(pool, s->name);
1852   l = strlen(str);
1853   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
1854     {
1855       fp += l + 1;
1856       str = evrid2vrstr(pool, s->evr);
1857       l = strlen(str);
1858       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
1859         {
1860           fp += l + 1;
1861           str = id2str(pool, s->arch);
1862           l = strlen(str);
1863           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
1864             {
1865               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
1866               return;
1867             }
1868         }
1869     }
1870   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
1871 }
1872
1873 void
1874 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
1875 {
1876   assert(dir);
1877 #if 0
1878 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
1879 #endif
1880   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
1881   data->attriddata[data->attriddatalen++] = dir;
1882   data->attriddata[data->attriddatalen++] = num;
1883   data->attriddata[data->attriddatalen++] = num2;
1884   data->attriddata[data->attriddatalen++] = 0;
1885 }
1886
1887 void
1888 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
1889 {
1890   Id stroff;
1891   int l;
1892
1893   assert(dir);
1894   l = strlen(str) + 1;
1895   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
1896   memcpy(data->attrdata + data->attrdatalen, str, l);
1897   stroff = data->attrdatalen;
1898   data->attrdatalen += l;
1899
1900 #if 0
1901 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
1902 #endif
1903   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
1904   data->attriddata[data->attriddatalen++] = dir;
1905   data->attriddata[data->attriddatalen++] = stroff;
1906   data->attriddata[data->attriddatalen++] = 0;
1907 }
1908
1909 void
1910 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
1911 {
1912 #if 0
1913 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
1914 #endif
1915   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
1916   data->attriddata[data->attriddatalen++] = id;
1917   data->attriddata[data->attriddatalen++] = 0;
1918 }
1919
1920 void
1921 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
1922                            const char *str)
1923 {
1924   Id id;
1925   if (data->localpool)
1926     id = stringpool_str2id(&data->spool, str, 1);
1927   else
1928     id = str2id(data->repo->pool, str, 1);
1929   repodata_add_idarray(data, solvid, keyname, id);
1930 }
1931
1932 void
1933 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1934 {
1935   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
1936   data->attriddata[data->attriddatalen++] = ghandle;
1937   data->attriddata[data->attriddatalen++] = 0;
1938 }
1939
1940 void
1941 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
1942 {
1943   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
1944   data->attriddata[data->attriddatalen++] = ghandle;
1945   data->attriddata[data->attriddatalen++] = 0;
1946 }
1947
1948 /* add all attrs from src to dest */
1949 void
1950 repodata_merge_attrs(Repodata *data, Id dest, Id src)
1951 {
1952   Id *keyp;
1953   if (dest == src || !(keyp = data->attrs[src - data->start]))
1954     return;
1955   for (; *keyp; keyp += 2)
1956     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
1957 }
1958
1959
1960
1961
1962 /**********************************************************************/
1963
1964 /* unify with repo_write! */
1965
1966 #define EXTDATA_BLOCK 1023
1967
1968 struct extdata {
1969   unsigned char *buf;
1970   int len;
1971 };
1972
1973 static void
1974 data_addid(struct extdata *xd, Id x)
1975 {
1976   unsigned char *dp;
1977   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
1978   dp = xd->buf + xd->len;
1979
1980   if (x >= (1 << 14))
1981     {
1982       if (x >= (1 << 28))
1983         *dp++ = (x >> 28) | 128;
1984       if (x >= (1 << 21))
1985         *dp++ = (x >> 21) | 128;
1986       *dp++ = (x >> 14) | 128;
1987     }
1988   if (x >= (1 << 7))
1989     *dp++ = (x >> 7) | 128;
1990   *dp++ = x & 127;
1991   xd->len = dp - xd->buf;
1992 }
1993
1994 static void
1995 data_addideof(struct extdata *xd, Id x, int eof)
1996 {
1997   if (x >= 64)
1998     x = (x & 63) | ((x & ~63) << 1);
1999   data_addid(xd, (eof ? x: x | 64));
2000 }
2001
2002 static void
2003 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2004 {
2005   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2006   memcpy(xd->buf + xd->len, blob, len);
2007   xd->len += len;
2008 }
2009
2010 /*********************************/
2011
2012 static void
2013 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2014                        struct extdata *newvincore,
2015                        Id *schema,
2016                        Repokey *key, Id val)
2017 {
2018   /* Otherwise we have a new value.  Parse it into the internal
2019      form.  */
2020   Id *ida;
2021   struct extdata *xd;
2022   unsigned int oldvincorelen = 0;
2023   Id schemaid, *sp;
2024
2025   xd = newincore;
2026   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2027     {
2028       xd = newvincore;
2029       oldvincorelen = xd->len;
2030     }
2031   switch (key->type)
2032     {
2033     case REPOKEY_TYPE_VOID:
2034     case REPOKEY_TYPE_CONSTANT:
2035     case REPOKEY_TYPE_CONSTANTID:
2036       break;
2037     case REPOKEY_TYPE_STR:
2038       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2039       break;
2040     case REPOKEY_TYPE_MD5:
2041       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2042       break;
2043     case REPOKEY_TYPE_SHA1:
2044       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2045       break;
2046     case REPOKEY_TYPE_SHA256:
2047       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2048       break;
2049     case REPOKEY_TYPE_ID:
2050     case REPOKEY_TYPE_NUM:
2051     case REPOKEY_TYPE_DIR:
2052       data_addid(xd, val);
2053       break;
2054     case REPOKEY_TYPE_IDARRAY:
2055       for (ida = data->attriddata + val; *ida; ida++)
2056         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2057       break;
2058     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2059       for (ida = data->attriddata + val; *ida; ida += 3)
2060         {
2061           data_addid(xd, ida[0]);
2062           data_addid(xd, ida[1]);
2063           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2064         }
2065       break;
2066     case REPOKEY_TYPE_DIRSTRARRAY:
2067       for (ida = data->attriddata + val; *ida; ida += 2)
2068         {
2069           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2070           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2071         }
2072       break;
2073     case REPOKEY_TYPE_FIXARRAY:
2074       {
2075         int num = 0;
2076         schemaid = 0;
2077         for (ida = data->attriddata + val; *ida; ida++)
2078           {
2079 #if 0
2080             fprintf(stderr, "serialize struct %d\n", *ida);
2081 #endif
2082             sp = schema;
2083             Id *kp = data->xattrs[-*ida];
2084             if (!kp)
2085               continue;
2086             num++;
2087             for (;*kp; kp += 2)
2088               {
2089 #if 0
2090                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
2091 #endif
2092                 *sp++ = *kp;
2093               }
2094             *sp = 0;
2095             if (!schemaid)
2096               schemaid = repodata_schema2id(data, schema, 1);
2097             else if (schemaid != repodata_schema2id(data, schema, 0))
2098               {
2099                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
2100                 exit(1);
2101               }
2102 #if 0
2103             fprintf(stderr, "  schema %d\n", schemaid);
2104 #endif
2105           }
2106         if (!num)
2107           break;
2108         data_addid(xd, num);
2109         data_addid(xd, schemaid);
2110         for (ida = data->attriddata + val; *ida; ida++)
2111           {
2112             Id *kp = data->xattrs[-*ida];
2113             if (!kp)
2114               continue;
2115             for (;*kp; kp += 2)
2116               {
2117                 repodata_serialize_key(data, newincore, newvincore,
2118                                        schema, data->keys + *kp, kp[1]);
2119               }
2120           }
2121         break;
2122       }
2123     case REPOKEY_TYPE_FLEXARRAY:
2124       {
2125         int num = 0;
2126         for (ida = data->attriddata + val; *ida; ida++)
2127           num++;
2128         data_addid(xd, num);
2129         for (ida = data->attriddata + val; *ida; ida++)
2130           {
2131             Id *kp = data->xattrs[-*ida];
2132             if (!kp)
2133               {
2134                 data_addid(xd, 0);      /* XXX */
2135                 continue;
2136               }
2137             sp = schema;
2138             for (;*kp; kp += 2)
2139               *sp++ = *kp;
2140             *sp = 0;
2141             schemaid = repodata_schema2id(data, schema, 1);
2142             data_addid(xd, schemaid);
2143             kp = data->xattrs[-*ida];
2144             for (;*kp; kp += 2)
2145               {
2146                 repodata_serialize_key(data, newincore, newvincore,
2147                                        schema, data->keys + *kp, kp[1]);
2148               }
2149           }
2150         break;
2151       }
2152     default:
2153       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
2154       exit(1);
2155     }
2156   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2157     {
2158       /* put offset/len in incore */
2159       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2160       oldvincorelen = xd->len - oldvincorelen;
2161       data_addid(newincore, oldvincorelen);
2162     }
2163 }
2164
2165 void
2166 repodata_internalize(Repodata *data)
2167 {
2168   Repokey *key, solvkey;
2169   Id entry, nentry;
2170   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2171   unsigned char *dp, *ndp;
2172   int newschema, oldcount;
2173   struct extdata newincore;
2174   struct extdata newvincore;
2175   Id solvkeyid;
2176
2177   if (!data->attrs && !data->xattrs)
2178     return;
2179
2180   newvincore.buf = data->vincore;
2181   newvincore.len = data->vincorelen;
2182
2183   /* find the solvables key, create if needed */
2184   memset(&solvkey, 0, sizeof(solvkey));
2185   solvkey.name = REPOSITORY_SOLVABLES;
2186   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2187   solvkey.size = 0;
2188   solvkey.storage = KEY_STORAGE_INCORE;
2189   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2190
2191   schema = sat_malloc2(data->nkeys, sizeof(Id));
2192   seen = sat_malloc2(data->nkeys, sizeof(Id));
2193
2194   /* Merge the data already existing (in data->schemata, ->incoredata and
2195      friends) with the new attributes in data->attrs[].  */
2196   nentry = data->end - data->start;
2197   memset(&newincore, 0, sizeof(newincore));
2198   data_addid(&newincore, 0);    /* start data at offset 1 */
2199
2200   data->mainschema = 0;
2201   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
2202
2203   /* join entry data */
2204   /* we start with the meta data, entry -1 */
2205   for (entry = -1; entry < nentry; entry++)
2206     {
2207       memset(seen, 0, data->nkeys * sizeof(Id));
2208       oldschema = 0;
2209       dp = data->incoredata;
2210       if (dp)
2211         {
2212           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2213           dp = data_read_id(dp, &oldschema);
2214         }
2215 #if 0
2216 fprintf(stderr, "oldschema %d\n", oldschema);
2217 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2218 fprintf(stderr, "schemadata %p\n", data->schemadata);
2219 #endif
2220       /* seen: -1: old data  0: skipped  >0: id + 1 */
2221       newschema = 0;
2222       oldcount = 0;
2223       sp = schema;
2224       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2225         {
2226           if (seen[*keyp])
2227             {
2228               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
2229               exit(1);
2230             }
2231           seen[*keyp] = -1;
2232           *sp++ = *keyp;
2233           oldcount++;
2234         }
2235       if (entry >= 0)
2236         keyp = data->attrs ? data->attrs[entry] : 0;
2237       else
2238         {
2239           /* strip solvables key */
2240           *sp = 0;
2241           for (sp = keyp = schema; *sp; sp++)
2242             if (*sp != solvkeyid)
2243               *keyp++ = *sp;
2244             else
2245               oldcount--;
2246           sp = keyp;
2247           seen[solvkeyid] = 0;
2248           keyp = data->xattrs ? data->xattrs[1] : 0;
2249         }
2250       if (keyp)
2251         for (; *keyp; keyp += 2)
2252           {
2253             if (!seen[*keyp])
2254               {
2255                 newschema = 1;
2256                 *sp++ = *keyp;
2257               }
2258             seen[*keyp] = keyp[1] + 1;
2259           }
2260       if (entry < 0 && data->end != data->start)
2261         {
2262           *sp++ = solvkeyid;
2263           newschema = 1;
2264         }
2265       *sp = 0;
2266       if (newschema)
2267         /* Ideally we'd like to sort the new schema here, to ensure
2268            schema equality independend of the ordering.  We can't do that
2269            yet.  For once see below (old ids need to come before new ids).
2270            An additional difficulty is that we also need to move
2271            the values with the keys.  */
2272         schemaid = repodata_schema2id(data, schema, 1);
2273       else
2274         schemaid = oldschema;
2275
2276
2277       /* Now create data blob.  We walk through the (possibly new) schema
2278          and either copy over old data, or insert the new.  */
2279       /* XXX Here we rely on the fact that the (new) schema has the form
2280          o1 o2 o3 o4 ... | n1 n2 n3 ...
2281          (oX being the old keyids (possibly overwritten), and nX being
2282           the new keyids).  This rules out sorting the keyids in order
2283          to ensure a small schema count.  */
2284       if (entry >= 0)
2285         data->incoreoffset[entry] = newincore.len;
2286       data_addid(&newincore, schemaid);
2287       if (entry == -1)
2288         {
2289           data->mainschema = schemaid;
2290           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
2291         }
2292       keypstart = data->schemadata + data->schemata[schemaid];
2293       for (keyp = keypstart; *keyp; keyp++)
2294         {
2295           if (entry == -1)
2296             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2297           if (*keyp == solvkeyid)
2298             {
2299               /* add flexarray entry count */
2300               data_addid(&newincore, data->end - data->start);
2301               break;
2302             }
2303           key = data->keys + *keyp;
2304 #if 0
2305           fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
2306 #endif
2307           ndp = dp;
2308           if (oldcount)
2309             {
2310               /* Skip the data associated with this old key.  */
2311               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2312                 {
2313                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2314                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2315                 }
2316               else if (key->storage == KEY_STORAGE_INCORE)
2317                 ndp = data_skip_key(data, dp, key);
2318               oldcount--;
2319             }
2320           if (seen[*keyp] == -1)
2321             {
2322               /* If this key was an old one _and_ was not overwritten with
2323                  a different value copy over the old value (we skipped it
2324                  above).  */
2325               if (dp != ndp)
2326                 data_addblob(&newincore, dp, ndp - dp);
2327               seen[*keyp] = 0;
2328             }
2329           else if (seen[*keyp])
2330             {
2331               /* Otherwise we have a new value.  Parse it into the internal
2332                  form.  */
2333               repodata_serialize_key(data, &newincore, &newvincore,
2334                                      schema, key, seen[*keyp] - 1);
2335             }
2336           dp = ndp;
2337         }
2338       if (entry >= 0 && data->attrs && data->attrs[entry])
2339         data->attrs[entry] = sat_free(data->attrs[entry]);
2340     }
2341   /* free all xattrs */
2342   for (entry = 0; entry < data->nxattrs; entry++)
2343     if (data->xattrs[entry])
2344       sat_free(data->xattrs[entry]);
2345   data->xattrs = sat_free(data->xattrs);
2346   data->nxattrs = 0;
2347
2348   data->lasthandle = 0;
2349   data->lastkey = 0;
2350   data->lastdatalen = 0;
2351   sat_free(schema);
2352   sat_free(seen);
2353   repodata_free_schemahash(data);
2354
2355   sat_free(data->incoredata);
2356   data->incoredata = newincore.buf;
2357   data->incoredatalen = newincore.len;
2358   data->incoredatafree = 0;
2359
2360   sat_free(data->vincore);
2361   data->vincore = newvincore.buf;
2362   data->vincorelen = newvincore.len;
2363
2364   data->attrs = sat_free(data->attrs);
2365   data->attrdata = sat_free(data->attrdata);
2366   data->attriddata = sat_free(data->attriddata);
2367   data->attrdatalen = 0;
2368   data->attriddatalen = 0;
2369 }
2370
2371 void
2372 repodata_disable_paging(Repodata *data)
2373 {
2374   if (maybe_load_repodata(data, 0))
2375     repopagestore_disable_paging(&data->store);
2376 }
2377
2378 /*
2379 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
2380 */