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