satsolver 0.16.3

repodata.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2007, Novell Inc.
00003  *
00004  * This program is licensed under the BSD license, read LICENSE.BSD
00005  * for further information
00006  */
00007 
00008 /*
00009  * repodata.c
00010  *
00011  * Manage data coming from one repository
00012  *
00013  * a repository can contain multiple repodata entries, consisting of
00014  * different sets of keys and different sets of solvables
00015  */
00016 
00017 #define _GNU_SOURCE
00018 #include <string.h>
00019 #include <fnmatch.h>
00020 
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <assert.h>
00025 #include <regex.h>
00026 
00027 #include "repo.h"
00028 #include "pool.h"
00029 #include "poolid_private.h"
00030 #include "util.h"
00031 #include "hash.h"
00032 #include "chksum.h"
00033 
00034 #include "repopack.h"
00035 #include "repopage.h"
00036 
00037 extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
00038                                   unsigned char *out, unsigned int out_len);
00039 extern unsigned int unchecked_decompress_buf (const unsigned char *in,
00040                                               unsigned int in_len,
00041                                               unsigned char *out,
00042                                               unsigned int out_len);
00043 
00044 #define REPODATA_BLOCK 255
00045 
00046 
00047 void
00048 repodata_initdata(Repodata *data, Repo *repo, int localpool)
00049 {
00050   memset(data, 0, sizeof (*data));
00051   data->repo = repo;
00052   data->localpool = localpool;
00053   if (localpool)
00054     stringpool_init_empty(&data->spool);
00055   data->keys = sat_calloc(1, sizeof(Repokey));
00056   data->nkeys = 1;
00057   data->schemata = sat_calloc(1, sizeof(Id));
00058   data->schemadata = sat_calloc(1, sizeof(Id));
00059   data->nschemata = 1;
00060   data->schemadatalen = 1;
00061   repopagestore_init(&data->store);
00062 }
00063 
00064 void
00065 repodata_freedata(Repodata *data)
00066 {
00067   int i;
00068 
00069   sat_free(data->keys);
00070 
00071   sat_free(data->schemata);
00072   sat_free(data->schemadata);
00073   sat_free(data->schematahash);
00074 
00075   stringpool_free(&data->spool);
00076   dirpool_free(&data->dirpool);
00077 
00078   sat_free(data->mainschemaoffsets);
00079   sat_free(data->incoredata);
00080   sat_free(data->incoreoffset);
00081   sat_free(data->verticaloffset);
00082 
00083   repopagestore_free(&data->store);
00084 
00085   sat_free(data->vincore);
00086 
00087   if (data->attrs)
00088     for (i = 0; i < data->end - data->start; i++)
00089       sat_free(data->attrs[i]);
00090   sat_free(data->attrs);
00091   if (data->xattrs)
00092     for (i = 0; i < data->nxattrs; i++)
00093       sat_free(data->xattrs[i]);
00094   sat_free(data->xattrs);
00095 
00096   sat_free(data->attrdata);
00097   sat_free(data->attriddata);
00098 }
00099 
00100 Repodata *
00101 repodata_create(Repo *repo, int localpool)
00102 {
00103   Repodata *data;
00104 
00105   repo->nrepodata++;
00106   repo->repodata = sat_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
00107   data = repo->repodata + repo->nrepodata - 1;
00108   repodata_initdata(data, repo, localpool);
00109   return data;
00110 }
00111 
00112 void
00113 repodata_free(Repodata *data)
00114 {
00115   Repo *repo = data->repo;
00116   int i = data - repo->repodata;
00117   repodata_freedata(data);
00118   if (i < repo->nrepodata - 1)
00119     memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
00120   repo->nrepodata--;
00121 }
00122 
00123 
00124 /***************************************************************
00125  * key pool management
00126  */
00127 
00128 /* this is not so time critical that we need a hash, so we do a simple
00129  * linear search */
00130 Id
00131 repodata_key2id(Repodata *data, Repokey *key, int create)
00132 {
00133   Id keyid;
00134 
00135   for (keyid = 1; keyid < data->nkeys; keyid++)
00136     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
00137       {
00138         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
00139           continue;
00140         break;
00141       }
00142   if (keyid == data->nkeys)
00143     {
00144       if (!create)
00145         return 0;
00146       /* allocate new key */
00147       data->keys = sat_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
00148       data->keys[data->nkeys++] = *key;
00149       if (data->verticaloffset)
00150         {
00151           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
00152           data->verticaloffset[data->nkeys - 1] = 0;
00153         }
00154       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
00155     }
00156   return keyid;
00157 }
00158 
00159 
00160 /***************************************************************
00161  * schema pool management
00162  */
00163 
00164 #define SCHEMATA_BLOCK 31
00165 #define SCHEMATADATA_BLOCK 255
00166 
00167 Id
00168 repodata_schema2id(Repodata *data, Id *schema, int create)
00169 {
00170   int h, len, i;
00171   Id *sp, cid;
00172   Id *schematahash;
00173 
00174   if ((schematahash = data->schematahash) == 0)
00175     {
00176       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
00177       for (i = 0; i < data->nschemata; i++)
00178         {
00179           for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
00180             h = h * 7 + *sp++;
00181           h &= 255;
00182           schematahash[h] = i + 1;
00183         }
00184       data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
00185       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
00186     }
00187 
00188   for (sp = schema, len = 0, h = 0; *sp; len++)
00189     h = h * 7 + *sp++;
00190   h &= 255;
00191   len++;
00192 
00193   cid = schematahash[h];
00194   if (cid)
00195     {
00196       cid--;
00197       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
00198         return cid;
00199       /* cache conflict */
00200       for (cid = 0; cid < data->nschemata; cid++)
00201         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
00202           return cid;
00203     }
00204   /* a new one */
00205   if (!create)
00206     return 0;
00207   data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
00208   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
00209   /* add schema */
00210   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
00211   data->schemata[data->nschemata] = data->schemadatalen;
00212   data->schemadatalen += len;
00213   schematahash[h] = data->nschemata + 1;
00214 #if 0
00215 fprintf(stderr, "schema2id: new schema\n");
00216 #endif
00217   return data->nschemata++;
00218 }
00219 
00220 void
00221 repodata_free_schemahash(Repodata *data)
00222 {
00223   data->schematahash = sat_free(data->schematahash);
00224   /* shrink arrays */
00225   data->schemata = sat_realloc2(data->schemata, data->nschemata, sizeof(Id));
00226   data->schemadata = sat_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
00227 }
00228 
00229 
00230 /***************************************************************
00231  * dir pool management
00232  */
00233 
00234 Id
00235 repodata_str2dir(Repodata *data, const char *dir, int create)
00236 {
00237   Id id, parent;
00238   const char *dire;
00239 
00240   parent = 0;
00241   while (*dir == '/' && dir[1] == '/')
00242     dir++;
00243   if (*dir == '/' && !dir[1])
00244     {
00245       if (data->dirpool.ndirs)
00246         return 1;
00247       return dirpool_add_dir(&data->dirpool, 0, 1, create);
00248     }
00249   while (*dir)
00250     {
00251       dire = strchrnul(dir, '/');
00252       if (data->localpool)
00253         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
00254       else
00255         id = strn2id(data->repo->pool, dir, dire - dir, create);
00256       if (!id)
00257         return 0;
00258       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
00259       if (!parent)
00260         return 0;
00261       if (!*dire)
00262         break;
00263       dir = dire + 1;
00264       while (*dir == '/')
00265         dir++;
00266     }
00267   return parent;
00268 }
00269 
00270 const char *
00271 repodata_dir2str(Repodata *data, Id did, const char *suf)
00272 {
00273   Pool *pool = data->repo->pool;
00274   int l = 0;
00275   Id parent, comp;
00276   const char *comps;
00277   char *p;
00278 
00279   if (!did)
00280     return suf ? suf : "";
00281   parent = did;
00282   while (parent)
00283     {
00284       comp = dirpool_compid(&data->dirpool, parent);
00285       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
00286       l += strlen(comps);
00287       parent = dirpool_parent(&data->dirpool, parent);
00288       if (parent)
00289         l++;
00290     }
00291   if (suf)
00292     l += strlen(suf) + 1;
00293   p = pool_alloctmpspace(pool, l + 1) + l;
00294   *p = 0;
00295   if (suf)
00296     {
00297       p -= strlen(suf);
00298       strcpy(p, suf);
00299       *--p = '/';
00300     }
00301   parent = did;
00302   while (parent)
00303     {
00304       comp = dirpool_compid(&data->dirpool, parent);
00305       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
00306       l = strlen(comps);
00307       p -= l;
00308       strncpy(p, comps, l);
00309       parent = dirpool_parent(&data->dirpool, parent);
00310       if (parent)
00311         *--p = '/';
00312     }
00313   return p;
00314 }
00315 
00316 
00317 /***************************************************************
00318  * data management
00319  */
00320 
00321 static inline unsigned char *
00322 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
00323 {
00324   Id *keyp = data->schemadata + data->schemata[schema];
00325   for (; *keyp; keyp++)
00326     dp = data_skip_key(data, dp, data->keys + *keyp);
00327   return dp;
00328 }
00329 
00330 unsigned char *
00331 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
00332 {
00333   int nentries, schema;
00334   switch(key->type)
00335     {
00336     case REPOKEY_TYPE_FIXARRAY:
00337       dp = data_read_id(dp, &nentries);
00338       if (!nentries)
00339         return dp;
00340       dp = data_read_id(dp, &schema);
00341       while (nentries--)
00342         dp = data_skip_schema(data, dp, schema);
00343       return dp;
00344     case REPOKEY_TYPE_FLEXARRAY:
00345       dp = data_read_id(dp, &nentries);
00346       while (nentries--)
00347         {
00348           dp = data_read_id(dp, &schema);
00349           dp = data_skip_schema(data, dp, schema);
00350         }
00351       return dp;
00352     default:
00353       if (key->storage == KEY_STORAGE_INCORE)
00354         dp = data_skip(dp, key->type);
00355       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
00356         {
00357           dp = data_skip(dp, REPOKEY_TYPE_ID);
00358           dp = data_skip(dp, REPOKEY_TYPE_ID);
00359         }
00360       return dp;
00361     }
00362 }
00363 
00364 static unsigned char *
00365 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
00366 {
00367   Id k;
00368 
00369   if (!keyid)
00370     return 0;
00371   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
00372     {
00373       int i;
00374       for (i = 0; (k = *keyp++) != 0; i++)
00375         if (k == keyid)
00376           return data->incoredata + data->mainschemaoffsets[i];
00377       return 0;
00378     }
00379   while ((k = *keyp++) != 0)
00380     {
00381       if (k == keyid)
00382         return dp;
00383       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
00384         {
00385           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip offset */
00386           dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip length */
00387           continue;
00388         }
00389       if (data->keys[k].storage != KEY_STORAGE_INCORE)
00390         continue;
00391       dp = data_skip_key(data, dp, data->keys + k);
00392     }
00393   return 0;
00394 }
00395 
00396 static unsigned char *
00397 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
00398 {
00399   unsigned char *dp;
00400   if (!len)
00401     return 0;
00402   if (off >= data->lastverticaloffset)
00403     {
00404       off -= data->lastverticaloffset;
00405       if (off + len > data->vincorelen)
00406         return 0;
00407       return data->vincore + off;
00408     }
00409   if (off + len > key->size)
00410     return 0;
00411   /* we now have the offset, go into vertical */
00412   off += data->verticaloffset[key - data->keys];
00413   /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
00414   dp = repopagestore_load_page_range(&data->store, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
00415   if (dp)
00416     dp += off % BLOB_PAGESIZE;
00417   return dp;
00418 }
00419 
00420 static inline unsigned char *
00421 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
00422 {
00423   unsigned char *dp = *dpp;
00424 
00425   if (!dp)
00426     return 0;
00427   if (key->storage == KEY_STORAGE_INCORE)
00428     {
00429       if (advance)
00430         *dpp = data_skip_key(data, dp, key);
00431       return dp;
00432     }
00433   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
00434     {
00435       Id off, len;
00436       dp = data_read_id(dp, &off);
00437       dp = data_read_id(dp, &len);
00438       if (advance)
00439         *dpp = dp;
00440       return get_vertical_data(data, key, off, len);
00441     }
00442   return 0;
00443 }
00444 
00445 static int
00446 load_repodata(Repodata *data)
00447 {
00448   if (data->loadcallback)
00449     {
00450       data->loadcallback(data);
00451       if (data->state == REPODATA_AVAILABLE)
00452         return 1;
00453     }
00454   data->state = REPODATA_ERROR;
00455   return 0;
00456 }
00457 
00458 static inline int
00459 maybe_load_repodata(Repodata *data, Id keyname)
00460 {
00461   if (keyname && !repodata_precheck_keyname(data, keyname))
00462     return 0;   /* do not bother... */
00463   switch(data->state)
00464     {
00465     case REPODATA_STUB:
00466       if (keyname)
00467         {
00468           int i;
00469           for (i = 0; i < data->nkeys; i++)
00470             if (keyname == data->keys[i].name)
00471               break;
00472           if (i == data->nkeys)
00473             return 0;
00474         }
00475       return load_repodata(data);
00476     case REPODATA_ERROR:
00477       return 0;
00478     case REPODATA_AVAILABLE:
00479     case REPODATA_LOADING:
00480       return 1;
00481     default:
00482       data->state = REPODATA_ERROR;
00483       return 0;
00484     }
00485 }
00486 
00487 static inline unsigned char *
00488 solvid2data(Repodata *data, Id solvid, Id *schemap)
00489 {
00490   unsigned char *dp = data->incoredata;
00491   if (!dp)
00492     return 0;
00493   if (solvid == SOLVID_META)    /* META */
00494     dp += 1;
00495   else if (solvid == SOLVID_POS)        /* META */
00496     {
00497       Pool *pool = data->repo->pool;
00498       if (data->repo != pool->pos.repo)
00499         return 0;
00500       if (data != data->repo->repodata + pool->pos.repodataid)
00501         return 0;
00502       *schemap = pool->pos.schema;
00503       return data->incoredata + pool->pos.dp;
00504     }
00505   else
00506     {
00507       if (solvid < data->start || solvid >= data->end)
00508         return 0;
00509       dp += data->incoreoffset[solvid - data->start];
00510     }
00511   return data_read_id(dp, schemap);
00512 }
00513 
00514 /************************************************************************
00515  * data lookup
00516  */
00517 
00518 static inline unsigned char *
00519 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
00520 {
00521   unsigned char *dp;
00522   Id schema, *keyp, *kp;
00523   Repokey *key;
00524 
00525   if (!maybe_load_repodata(data, keyname))
00526     return 0;
00527   dp = solvid2data(data, solvid, &schema);
00528   if (!dp)
00529     return 0;
00530   keyp = data->schemadata + data->schemata[schema];
00531   for (kp = keyp; *kp; kp++)
00532     if (data->keys[*kp].name == keyname)
00533       break;
00534   if (!*kp)
00535     return 0;
00536   *keypp = key = data->keys + *kp;
00537   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
00538     return dp;  /* no need to forward... */
00539   dp = forward_to_key(data, *kp, keyp, dp);
00540   if (!dp)
00541     return 0;
00542   return get_data(data, key, &dp, 0);
00543 }
00544 
00545 
00546 Id
00547 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
00548 {
00549   unsigned char *dp;
00550   Repokey *key;
00551   Id id;
00552 
00553   dp = find_key_data(data, solvid, keyname, &key);
00554   if (!dp)
00555     return 0;
00556   if (key->type == REPOKEY_TYPE_CONSTANTID)
00557     return key->size;
00558   if (key->type != REPOKEY_TYPE_ID)
00559     return 0;
00560   dp = data_read_id(dp, &id);
00561   return id;
00562 }
00563 
00564 Id
00565 repodata_globalize_id(Repodata *data, Id id, int create)
00566 {
00567   if (!id || !data || !data->localpool)
00568     return id;
00569   return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
00570 }
00571 
00572 const char *
00573 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
00574 {
00575   unsigned char *dp;
00576   Repokey *key;
00577   Id id;
00578 
00579   dp = find_key_data(data, solvid, keyname, &key);
00580   if (!dp)
00581     return 0;
00582   if (key->type == REPOKEY_TYPE_STR)
00583     return (const char *)dp;
00584   if (key->type == REPOKEY_TYPE_CONSTANTID)
00585     return id2str(data->repo->pool, key->size);
00586   if (key->type == REPOKEY_TYPE_ID)
00587     dp = data_read_id(dp, &id);
00588   else
00589     return 0;
00590   if (data->localpool)
00591     return data->spool.stringspace + data->spool.strings[id];
00592   return id2str(data->repo->pool, id);
00593 }
00594 
00595 int
00596 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned int *value)
00597 {
00598   unsigned char *dp;
00599   Repokey *key;
00600   KeyValue kv;
00601 
00602   *value = 0;
00603   dp = find_key_data(data, solvid, keyname, &key);
00604   if (!dp)
00605     return 0;
00606   if (key->type == REPOKEY_TYPE_NUM
00607       || key->type == REPOKEY_TYPE_U32
00608       || key->type == REPOKEY_TYPE_CONSTANT)
00609     {
00610       dp = data_fetch(dp, &kv, key);
00611       *value = kv.num;
00612       return 1;
00613     }
00614   return 0;
00615 }
00616 
00617 int
00618 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
00619 {
00620   Id schema;
00621   Id *keyp;
00622   unsigned char *dp;
00623 
00624   if (!maybe_load_repodata(data, keyname))
00625     return 0;
00626   dp = solvid2data(data, solvid, &schema);
00627   if (!dp)
00628     return 0;
00629   /* can't use find_key_data as we need to test the type */
00630   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
00631     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
00632       return 1;
00633   return 0;
00634 }
00635 
00636 const unsigned char *
00637 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
00638 {
00639   unsigned char *dp;
00640   Repokey *key;
00641 
00642   dp = find_key_data(data, solvid, keyname, &key);
00643   if (!dp)
00644     return 0;
00645   *typep = key->type;
00646   return dp;
00647 }
00648 
00649 int
00650 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
00651 {
00652   unsigned char *dp;
00653   Repokey *key;
00654   Id id;
00655   int eof = 0;
00656 
00657   queue_empty(q);
00658   dp = find_key_data(data, solvid, keyname, &key);
00659   if (!dp)
00660     return 0;
00661   for (;;)
00662     {
00663       dp = data_read_ideof(dp, &id, &eof);
00664       queue_push(q, id);
00665       if (eof)
00666         break;
00667     }
00668   return 1;
00669 }
00670 
00671 /************************************************************************
00672  * data search
00673  */
00674 
00675 
00676 int
00677 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
00678 {
00679   switch (key->type)
00680     {
00681     case REPOKEY_TYPE_ID:
00682     case REPOKEY_TYPE_CONSTANTID:
00683     case REPOKEY_TYPE_IDARRAY:
00684       if (data && data->localpool)
00685         kv->str = stringpool_id2str(&data->spool, kv->id);
00686       else
00687         kv->str = id2str(pool, kv->id);
00688       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
00689         {
00690           const char *s;
00691           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
00692             ;
00693           if (*s == ':' && s > kv->str)
00694             kv->str = s + 1;
00695         }
00696       return 1;
00697     case REPOKEY_TYPE_STR:
00698       return 1;
00699     case REPOKEY_TYPE_DIRSTRARRAY:
00700       if (!(flags & SEARCH_FILES))
00701         return 1;       /* match just the basename */
00702       /* Put the full filename into kv->str.  */
00703       kv->str = repodata_dir2str(data, kv->id, kv->str);
00704       /* And to compensate for that put the "empty" directory into
00705          kv->id, so that later calls to repodata_dir2str on this data
00706          come up with the same filename again.  */
00707       kv->id = 0;
00708       return 1;
00709     case REPOKEY_TYPE_MD5:
00710     case REPOKEY_TYPE_SHA1:
00711     case REPOKEY_TYPE_SHA256:
00712       if (!(flags & SEARCH_CHECKSUMS))
00713         return 0;       /* skip em */
00714       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
00715       return 1;
00716     default:
00717       return 0;
00718     }
00719 }
00720 
00721 
00722 struct subschema_data {
00723   Solvable *s;
00724   void *cbdata;
00725   KeyValue *parent;
00726 };
00727 
00728 /* search a specific repodata */
00729 void
00730 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
00731 {
00732   Id schema;
00733   Repokey *key;
00734   Id keyid, *kp, *keyp;
00735   unsigned char *dp, *ddp;
00736   int onekey = 0;
00737   int stop;
00738   KeyValue kv;
00739   Solvable *s;
00740 
00741   if (!maybe_load_repodata(data, keyname))
00742     return;
00743   if (solvid == SOLVID_SUBSCHEMA)
00744     {
00745       struct subschema_data *subd = cbdata;
00746       cbdata = subd->cbdata;
00747       s = subd->s;
00748       schema = subd->parent->id;
00749       dp = (unsigned char *)subd->parent->str;
00750       kv.parent = subd->parent;
00751     }
00752   else
00753     {
00754       schema = 0;
00755       dp = solvid2data(data, solvid, &schema);
00756       if (!dp)
00757         return;
00758       s = data->repo->pool->solvables + solvid;
00759       kv.parent = 0;
00760     }
00761   keyp = data->schemadata + data->schemata[schema];
00762   if (keyname)
00763     {
00764       /* search for a specific key */
00765       for (kp = keyp; *kp; kp++)
00766         if (data->keys[*kp].name == keyname)
00767           break;
00768       if (!*kp)
00769         return;
00770       dp = forward_to_key(data, *kp, keyp, dp);
00771       if (!dp)
00772         return;
00773       keyp = kp;
00774       onekey = 1;
00775     }
00776   while ((keyid = *keyp++) != 0)
00777     {
00778       stop = 0;
00779       key = data->keys + keyid;
00780       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
00781 
00782       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
00783         {
00784           struct subschema_data subd;
00785           int nentries;
00786           Id schema = 0;
00787 
00788           subd.cbdata = cbdata;
00789           subd.s = s;
00790           subd.parent = &kv;
00791           ddp = data_read_id(ddp, &nentries);
00792           kv.num = nentries;
00793           kv.entry = 0;
00794           kv.eof = 0;
00795           while (ddp && nentries > 0)
00796             {
00797               if (!--nentries)
00798                 kv.eof = 1;
00799               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
00800                 ddp = data_read_id(ddp, &schema);
00801               kv.id = schema;
00802               kv.str = (char *)ddp;
00803               stop = callback(cbdata, s, data, key, &kv);
00804               if (stop > SEARCH_NEXT_KEY)
00805                 return;
00806               if (stop && stop != SEARCH_ENTERSUB)
00807                 break;
00808               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
00809                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
00810               ddp = data_skip_schema(data, ddp, schema);
00811               kv.entry++;
00812             }
00813           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
00814             {
00815               /* sentinel */
00816               kv.eof = 2;
00817               kv.str = (char *)ddp;
00818               stop = callback(cbdata, s, data, key, &kv);
00819               if (stop > SEARCH_NEXT_KEY)
00820                 return;
00821             }
00822           if (onekey)
00823             return;
00824           continue;
00825         }
00826       kv.entry = 0;
00827       do
00828         {
00829           ddp = data_fetch(ddp, &kv, key);
00830           if (!ddp)
00831             break;
00832           stop = callback(cbdata, s, data, key, &kv);
00833           kv.entry++;
00834         }
00835       while (!kv.eof && !stop);
00836       if (onekey || stop > SEARCH_NEXT_KEY)
00837         return;
00838     }
00839 }
00840 
00841 void
00842 repodata_setpos_kv(Repodata *data, KeyValue *kv)
00843 {
00844   Pool *pool = data->repo->pool;
00845   if (!kv)
00846     pool_clear_pos(pool);
00847   else
00848     {
00849       pool->pos.repo = data->repo;
00850       pool->pos.repodataid = data - data->repo->repodata;
00851       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
00852       pool->pos.schema = kv->id;
00853     }
00854 }
00855 
00856 /************************************************************************
00857  * data iterator functions
00858  */
00859 
00860 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
00861   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
00862   { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
00863   { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
00864   { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
00865   { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00866   { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00867   { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00868   { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00869   { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00870   { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00871   { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00872   { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
00873   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
00874 };
00875 
00876 static inline Id *
00877 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
00878 {
00879   kv->id = keyname;
00880   switch (keyname)
00881     {
00882     case SOLVABLE_NAME:
00883       kv->eof = 1;
00884       return &s->name;
00885     case SOLVABLE_ARCH:
00886       kv->eof = 1;
00887       return &s->arch;
00888     case SOLVABLE_EVR:
00889       kv->eof = 1;
00890       return &s->evr;
00891     case SOLVABLE_VENDOR:
00892       kv->eof = 1;
00893       return &s->vendor;
00894     case SOLVABLE_PROVIDES:
00895       kv->eof = 0;
00896       return s->provides ? s->repo->idarraydata + s->provides : 0;
00897     case SOLVABLE_OBSOLETES:
00898       kv->eof = 0;
00899       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
00900     case SOLVABLE_CONFLICTS:
00901       kv->eof = 0;
00902       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
00903     case SOLVABLE_REQUIRES:
00904       kv->eof = 0;
00905       return s->requires ? s->repo->idarraydata + s->requires : 0;
00906     case SOLVABLE_RECOMMENDS:
00907       kv->eof = 0;
00908       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
00909     case SOLVABLE_SUPPLEMENTS:
00910       kv->eof = 0;
00911       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
00912     case SOLVABLE_SUGGESTS:
00913       kv->eof = 0;
00914       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
00915     case SOLVABLE_ENHANCES:
00916       kv->eof = 0;
00917       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
00918     case RPM_RPMDBID:
00919       kv->eof = 1;
00920       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
00921     default:
00922       return 0;
00923     }
00924 }
00925 
00926 int
00927 datamatcher_init(Datamatcher *ma, const char *match, int flags)
00928 {
00929   ma->match = match;
00930   ma->flags = flags;
00931   ma->error = 0;
00932   ma->matchdata = 0;
00933   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
00934     {
00935       ma->matchdata = sat_calloc(1, sizeof(regex_t));
00936       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
00937       if (ma->error)
00938         {
00939           sat_free(ma->matchdata);
00940           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
00941         }
00942     }
00943   return ma->error;
00944 }
00945 
00946 void
00947 datamatcher_free(Datamatcher *ma)
00948 {
00949   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
00950     {
00951       regfree(ma->matchdata);
00952       ma->matchdata = sat_free(ma->matchdata);
00953     }
00954 }
00955 
00956 int
00957 datamatcher_match(Datamatcher *ma, const char *str)
00958 {
00959   int l;
00960   switch ((ma->flags & SEARCH_STRINGMASK))
00961     {
00962     case SEARCH_SUBSTRING:
00963       if (ma->flags & SEARCH_NOCASE)
00964         {
00965           if (!strcasestr(str, ma->match))
00966             return 0;
00967         }
00968       else
00969         {
00970           if (!strstr(str, ma->match))
00971             return 0;
00972         }
00973       break;
00974     case SEARCH_STRING:
00975       if (ma->flags & SEARCH_NOCASE)
00976         {
00977           if (strcasecmp(ma->match, str))
00978             return 0;
00979         }
00980       else
00981         {
00982           if (strcmp(ma->match, str))
00983             return 0;
00984         }
00985       break;
00986     case SEARCH_STRINGSTART:
00987       if (ma->flags & SEARCH_NOCASE)
00988         {
00989           if (strncasecmp(ma->match, str, strlen(ma->match)))
00990             return 0;
00991         }
00992       else
00993         {
00994           if (strncmp(ma->match, str, strlen(ma->match)))
00995             return 0;
00996         }
00997       break;
00998     case SEARCH_STRINGEND:
00999       l = strlen(str) - strlen(ma->match);
01000       if (l < 0)
01001         return 0;
01002       if (ma->flags & SEARCH_NOCASE)
01003         {
01004           if (strcasecmp(ma->match, str + l))
01005             return 0;
01006         }
01007       else
01008         {
01009           if (strcmp(ma->match, str + l))
01010             return 0;
01011         }
01012       break;
01013     case SEARCH_GLOB:
01014       if (fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
01015         return 0;
01016       break;
01017     case SEARCH_REGEX:
01018       if (regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0))
01019         return 0;
01020       break;
01021     default:
01022       return 0;
01023     }
01024   return 1;
01025 }
01026 
01027 int
01028 repodata_filelistfilter_matches(Repodata *data, const char *str)
01029 {
01030   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
01031   /* for now hardcoded */
01032   if (strstr(str, "bin/"))
01033     return 1;
01034   if (!strncmp(str, "/etc/", 5))
01035     return 1;
01036   if (!strcmp(str, "/usr/lib/sendmail"))
01037     return 1;
01038   return 0;
01039 }
01040 
01041 
01042 enum {
01043   di_bye,
01044 
01045   di_enterrepo,
01046   di_entersolvable,
01047   di_enterrepodata,
01048   di_enterschema,
01049   di_enterkey,
01050 
01051   di_nextattr,
01052   di_nextkey,
01053   di_nextrepodata,
01054   di_nextsolvable,
01055   di_nextrepo,
01056 
01057   di_enterarray,
01058   di_nextarrayelement,
01059 
01060   di_entersub,
01061   di_leavesub,
01062 
01063   di_nextsolvableattr,
01064   di_nextsolvablekey,
01065   di_entersolvablekey
01066 };
01067 
01068 /* see repo.h for documentation */
01069 int
01070 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
01071 {
01072   memset(di, 0, sizeof(*di));
01073   di->pool = pool;
01074   di->flags = flags & ~SEARCH_THISSOLVID;
01075   if (!pool || (repo && repo->pool != pool))
01076     {
01077       di->state = di_bye;
01078       return -1;
01079     }
01080   if (match)
01081     {
01082       int error;
01083       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
01084         {
01085           di->state = di_bye;
01086           return error;
01087         }
01088     }
01089   di->keyname = keyname;
01090   di->keynames[0] = keyname;
01091   dataiterator_set_search(di, repo, p);
01092   return 0;
01093 }
01094 
01095 void
01096 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
01097 {
01098   *di = *from;
01099   memset(&di->matcher, 0, sizeof(di->matcher));
01100   if (from->matcher.match)
01101     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
01102   if (di->nparents)
01103     {
01104       /* fix pointers */
01105       int i;
01106       for (i = 1; i < di->nparents; i++)
01107         di->parents[i].kv.parent = &di->parents[i - 1].kv;
01108       di->kv.parent = &di->parents[di->nparents - 1].kv;
01109     }
01110 }
01111 
01112 int
01113 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
01114 {
01115   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
01116   datamatcher_free(&di->matcher);
01117   memset(&di->matcher, 0, sizeof(di->matcher));
01118   if (match)
01119     {
01120       int error;
01121       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
01122         {
01123           di->state = di_bye;
01124           return error;
01125         }
01126     }
01127   return 0;
01128 }
01129 
01130 void
01131 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
01132 {
01133   di->repo = repo;
01134   di->repoid = -1;
01135   di->flags &= ~SEARCH_THISSOLVID;
01136   di->nparents = 0;
01137   di->rootlevel = 0;
01138   di->repodataid = 0;
01139   if (!di->pool->nrepos)
01140     {
01141       di->state = di_bye;
01142       return;
01143     }
01144   if (!repo)
01145     {
01146       di->repoid = 0;
01147       di->repo = di->pool->repos[0];
01148     }
01149   di->state = di_enterrepo;
01150   if (p)
01151     dataiterator_jump_to_solvid(di, p);
01152 }
01153 
01154 void
01155 dataiterator_set_keyname(Dataiterator *di, Id keyname)
01156 {
01157   di->nkeynames = 0;
01158   di->keyname = keyname;
01159   di->keynames[0] = keyname;
01160 }
01161 
01162 void
01163 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
01164 {
01165   int i;
01166 
01167   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
01168     {
01169       di->state = di_bye;       /* sorry */
01170       return;
01171     }
01172   for (i = di->nkeynames + 1; i > 0; i--)
01173     di->keynames[i] = di->keynames[i - 1];
01174   di->keynames[0] = di->keyname = keyname;
01175   di->nkeynames++;
01176 }
01177 
01178 void
01179 dataiterator_free(Dataiterator *di)
01180 {
01181   if (di->matcher.match)
01182     datamatcher_free(&di->matcher);
01183 }
01184 
01185 static inline unsigned char *
01186 dataiterator_find_keyname(Dataiterator *di, Id keyname)
01187 {
01188   Id *keyp = di->keyp;
01189   Repokey *keys = di->data->keys;
01190   unsigned char *dp;
01191 
01192   for (keyp = di->keyp; *keyp; keyp++)
01193     if (keys[*keyp].name == keyname)
01194       break;
01195   if (!*keyp)
01196     return 0;
01197   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
01198   if (!dp)
01199     return 0;
01200   di->keyp = keyp;
01201   return dp;
01202 }
01203 
01204 static int
01205 dataiterator_filelistcheck(Dataiterator *di)
01206 {
01207   int j;
01208   int needcomplete = 0;
01209   Repodata *data = di->data;
01210 
01211   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
01212     if (!di->matcher.match
01213        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
01214            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
01215        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
01216       needcomplete = 1;
01217   if (data->state != REPODATA_AVAILABLE)
01218     return needcomplete ? 1 : 0;
01219   for (j = 1; j < data->nkeys; j++)
01220     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
01221       break;
01222   return j == data->nkeys && !needcomplete ? 0 : 1;
01223 }
01224 
01225 int
01226 dataiterator_step(Dataiterator *di)
01227 {
01228   Id schema;
01229 
01230   for (;;)
01231     {
01232       switch (di->state)
01233         {
01234         case di_enterrepo: di_enterrepo:
01235           if (!di->repo)
01236             goto di_bye;
01237           if (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS))
01238             goto di_nextrepo;
01239           if (!(di->flags & SEARCH_THISSOLVID))
01240             {
01241               di->solvid = di->repo->start - 1; /* reset solvid iterator */
01242               goto di_nextsolvable;
01243             }
01244           /* FALLTHROUGH */
01245 
01246         case di_entersolvable: di_entersolvable:
01247           if (di->repodataid >= 0)
01248             {
01249               di->repodataid = 0;       /* reset repodata iterator */
01250               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)
01251                 {
01252                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
01253                   di->data = 0;
01254                   goto di_entersolvablekey;
01255                 }
01256             }
01257           /* FALLTHROUGH */
01258 
01259         case di_enterrepodata: di_enterrepodata:
01260           if (di->repodataid >= 0)
01261             {
01262               if (di->repodataid >= di->repo->nrepodata)
01263                 goto di_nextsolvable;
01264               di->data = di->repo->repodata + di->repodataid;
01265             }
01266           if (di->repodataid >= 0 && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
01267             goto di_nextrepodata;
01268           if (!maybe_load_repodata(di->data, di->keyname))
01269             goto di_nextrepodata;
01270           di->dp = solvid2data(di->data, di->solvid, &schema);
01271           if (!di->dp)
01272             goto di_nextrepodata;
01273           if (di->solvid == SOLVID_POS)
01274             di->solvid = di->pool->pos.solvid;
01275           /* reset key iterator */
01276           di->keyp = di->data->schemadata + di->data->schemata[schema];
01277           /* FALLTHROUGH */
01278 
01279         case di_enterschema: di_enterschema:
01280           if (di->keyname)
01281             di->dp = dataiterator_find_keyname(di, di->keyname);
01282           if (!di->dp || !*di->keyp)
01283             {
01284               if (di->kv.parent)
01285                 goto di_leavesub;
01286               goto di_nextrepodata;
01287             }
01288           /* FALLTHROUGH */
01289 
01290         case di_enterkey: di_enterkey:
01291           di->kv.entry = -1;
01292           di->key = di->data->keys + *di->keyp;
01293           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
01294           if (!di->ddp)
01295             goto di_nextkey;
01296           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
01297             goto di_enterarray;
01298           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01299             goto di_nextkey;
01300           /* FALLTHROUGH */
01301 
01302         case di_nextattr:
01303           di->kv.entry++;
01304           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
01305           if (di->kv.eof)
01306             di->state = di_nextkey;
01307           else
01308             di->state = di_nextattr;
01309           break;
01310 
01311         case di_nextkey: di_nextkey:
01312           if (!di->keyname && *++di->keyp)
01313             goto di_enterkey;
01314           if (di->kv.parent)
01315             goto di_leavesub;
01316           /* FALLTHROUGH */
01317 
01318         case di_nextrepodata: di_nextrepodata:
01319           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
01320               goto di_enterrepodata;
01321           /* FALLTHROUGH */
01322 
01323         case di_nextsolvable: di_nextsolvable:
01324           if (!(di->flags & SEARCH_THISSOLVID))
01325             {
01326               if (di->solvid < 0)
01327                 di->solvid = di->repo->start;
01328               else
01329                 di->solvid++;
01330               for (; di->solvid < di->repo->end; di->solvid++)
01331                 {
01332                   if (di->pool->solvables[di->solvid].repo == di->repo)
01333                     goto di_entersolvable;
01334                 }
01335             }
01336           /* FALLTHROUGH */
01337 
01338         case di_nextrepo: di_nextrepo:
01339           if (di->repoid >= 0)
01340             {
01341               di->repoid++;
01342               di->repodataid = 0;
01343               if (di->repoid < di->pool->nrepos)
01344                 {
01345                   di->repo = di->pool->repos[di->repoid];
01346                   goto di_enterrepo;
01347                 }
01348             }
01349         /* FALLTHROUGH */
01350 
01351         case di_bye: di_bye:
01352           di->state = di_bye;
01353           return 0;
01354 
01355         case di_enterarray: di_enterarray:
01356           if (di->key->name == REPOSITORY_SOLVABLES)
01357             goto di_nextkey;
01358           di->ddp = data_read_id(di->ddp, &di->kv.num);
01359           di->kv.eof = 0;
01360           di->kv.entry = -1;
01361           /* FALLTHROUGH */
01362 
01363         case di_nextarrayelement: di_nextarrayelement:
01364           di->kv.entry++;
01365           if (di->kv.entry)
01366             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
01367           if (di->kv.entry == di->kv.num)
01368             {
01369               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01370                 goto di_nextkey;
01371               if (!(di->flags & SEARCH_ARRAYSENTINEL))
01372                 goto di_nextkey;
01373               di->kv.str = (char *)di->ddp;
01374               di->kv.eof = 2;
01375               di->state = di_nextkey;
01376               break;
01377             }
01378           if (di->kv.entry == di->kv.num - 1)
01379             di->kv.eof = 1;
01380           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
01381             di->ddp = data_read_id(di->ddp, &di->kv.id);
01382           di->kv.str = (char *)di->ddp;
01383           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01384             goto di_entersub;
01385           if ((di->flags & SEARCH_SUB) != 0)
01386             di->state = di_entersub;
01387           else
01388             di->state = di_nextarrayelement;
01389           break;
01390 
01391         case di_entersub: di_entersub:
01392           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
01393             goto di_nextarrayelement;   /* sorry, full */
01394           di->parents[di->nparents].kv = di->kv;
01395           di->parents[di->nparents].dp = di->dp;
01396           di->parents[di->nparents].keyp = di->keyp;
01397           di->dp = (unsigned char *)di->kv.str;
01398           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
01399           memset(&di->kv, 0, sizeof(di->kv));
01400           di->kv.parent = &di->parents[di->nparents].kv;
01401           di->nparents++;
01402           di->keyname = di->keynames[di->nparents - di->rootlevel];
01403           goto di_enterschema;
01404 
01405         case di_leavesub: di_leavesub:
01406           if (di->nparents - 1 < di->rootlevel)
01407             goto di_bye;
01408           di->nparents--;
01409           di->dp = di->parents[di->nparents].dp;
01410           di->kv = di->parents[di->nparents].kv;
01411           di->keyp = di->parents[di->nparents].keyp;
01412           di->key = di->data->keys + *di->keyp;
01413           di->ddp = (unsigned char *)di->kv.str;
01414           di->keyname = di->keynames[di->nparents - di->rootlevel];
01415           goto di_nextarrayelement;
01416 
01417         /* special solvable attr handling follows */
01418 
01419         case di_nextsolvableattr:
01420           di->kv.id = *di->idp++;
01421           di->kv.entry++;
01422           if (!*di->idp)
01423             {
01424               di->kv.eof = 1;
01425               di->state = di_nextsolvablekey;
01426             }
01427           break;
01428 
01429         case di_nextsolvablekey: di_nextsolvablekey:
01430           if (di->keyname || di->key->name == RPM_RPMDBID)
01431             goto di_enterrepodata;
01432           di->key++;
01433           /* FALLTHROUGH */
01434 
01435         case di_entersolvablekey: di_entersolvablekey:
01436           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
01437           if (!di->idp || !di->idp[0])
01438             goto di_nextsolvablekey;
01439           di->kv.id = di->idp[0];
01440           di->kv.num = di->idp[0];
01441           di->idp++;
01442           if (!di->kv.eof && !di->idp[0])
01443             di->kv.eof = 1;
01444           di->kv.entry = 0;
01445           if (di->kv.eof)
01446             di->state = di_nextsolvablekey;
01447           else
01448             di->state = di_nextsolvableattr;
01449           break;
01450         }
01451 
01452       if (di->matcher.match)
01453         {
01454           /* simple pre-check so that we don't need to stringify */
01455           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))
01456             {
01457               int l = strlen(di->matcher.match) - strlen(di->kv.str);
01458               if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
01459                 continue;
01460             }
01461           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
01462             {
01463               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
01464                 return 1;
01465               continue;
01466             }
01467           if (!datamatcher_match(&di->matcher, di->kv.str))
01468             continue;
01469         }
01470       /* found something! */
01471       return 1;
01472     }
01473 }
01474 
01475 void
01476 dataiterator_entersub(Dataiterator *di)
01477 {
01478   if (di->state == di_nextarrayelement)
01479     di->state = di_entersub;
01480 }
01481 
01482 void
01483 dataiterator_setpos(Dataiterator *di)
01484 {
01485   if (di->kv.eof == 2)
01486     {
01487       pool_clear_pos(di->pool);
01488       return;
01489     }
01490   di->pool->pos.solvid = di->solvid;
01491   di->pool->pos.repo = di->repo;
01492   di->pool->pos.repodataid = di->data - di->repo->repodata;
01493   di->pool->pos.schema = di->kv.id;
01494   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
01495 }
01496 
01497 void
01498 dataiterator_setpos_parent(Dataiterator *di)
01499 {
01500   if (!di->kv.parent || di->kv.parent->eof == 2)
01501     {
01502       pool_clear_pos(di->pool);
01503       return;
01504     }
01505   di->pool->pos.solvid = di->solvid;
01506   di->pool->pos.repo = di->repo;
01507   di->pool->pos.repodataid = di->data - di->repo->repodata;
01508   di->pool->pos.schema = di->kv.parent->id;
01509   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
01510 }
01511 
01512 /* clones just the position, not the search keys/matcher */
01513 void
01514 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
01515 {
01516   di->state = from->state;
01517   di->flags &= ~SEARCH_THISSOLVID;
01518   di->flags |= (from->flags & SEARCH_THISSOLVID);
01519   di->repo = from->repo;
01520   di->data = from->data;
01521   di->dp = from->dp;
01522   di->ddp = from->ddp;
01523   di->idp = from->idp;
01524   di->keyp = from->keyp;
01525   di->key = from->key;
01526   di->kv = from->kv;
01527   di->repodataid = from->repodataid;
01528   di->solvid = from->solvid;
01529   di->repoid = from->repoid;
01530   di->rootlevel = from->rootlevel;
01531   memcpy(di->parents, from->parents, sizeof(from->parents));
01532   di->nparents = from->nparents;
01533   if (di->nparents)
01534     {
01535       int i;
01536       for (i = 1; i < di->nparents; i++)
01537         di->parents[i].kv.parent = &di->parents[i - 1].kv;
01538       di->kv.parent = &di->parents[di->nparents - 1].kv;
01539     }
01540 }
01541 
01542 void
01543 dataiterator_seek(Dataiterator *di, int whence)
01544 {
01545   if ((whence & DI_SEEK_STAY) != 0)
01546     di->rootlevel = di->nparents;
01547   switch (whence & ~DI_SEEK_STAY)
01548     {
01549     case DI_SEEK_CHILD:
01550       if (di->state != di_nextarrayelement)
01551         break;
01552       if ((whence & DI_SEEK_STAY) != 0)
01553         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
01554       di->state = di_entersub;
01555       break;
01556     case DI_SEEK_PARENT:
01557       if (!di->nparents)
01558         {
01559           di->state = di_bye;
01560           break;
01561         }
01562       di->nparents--;
01563       if (di->rootlevel > di->nparents)
01564         di->rootlevel = di->nparents;
01565       di->dp = di->parents[di->nparents].dp;
01566       di->kv = di->parents[di->nparents].kv;
01567       di->keyp = di->parents[di->nparents].keyp;
01568       di->key = di->data->keys + *di->keyp;
01569       di->ddp = (unsigned char *)di->kv.str;
01570       di->keyname = di->keynames[di->nparents - di->rootlevel];
01571       di->state = di_nextarrayelement;
01572       break;
01573     case DI_SEEK_REWIND:
01574       if (!di->nparents)
01575         {
01576           di->state = di_bye;
01577           break;
01578         }
01579       di->dp = (unsigned char *)di->kv.parent->str;
01580       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
01581       di->state = di_enterschema;
01582       break;
01583     default:
01584       break;
01585     }
01586 }
01587 
01588 void
01589 dataiterator_skip_attribute(Dataiterator *di)
01590 {
01591   if (di->state == di_nextsolvableattr)
01592     di->state = di_nextsolvablekey;
01593   else
01594     di->state = di_nextkey;
01595 }
01596 
01597 void
01598 dataiterator_skip_solvable(Dataiterator *di)
01599 {
01600   di->nparents = 0;
01601   di->kv.parent = 0;
01602   di->rootlevel = 0;
01603   di->keyname = di->keynames[0];
01604   di->state = di_nextsolvable;
01605 }
01606 
01607 void
01608 dataiterator_skip_repo(Dataiterator *di)
01609 {
01610   di->nparents = 0;
01611   di->kv.parent = 0;
01612   di->rootlevel = 0;
01613   di->keyname = di->keynames[0];
01614   di->state = di_nextrepo;
01615 }
01616 
01617 void
01618 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
01619 {
01620   di->nparents = 0;
01621   di->kv.parent = 0;
01622   di->rootlevel = 0;
01623   di->keyname = di->keynames[0];
01624   if (solvid == SOLVID_POS)
01625     {
01626       di->repo = di->pool->pos.repo;
01627       if (!di->repo)
01628         {
01629           di->state = di_bye;
01630           return;
01631         }
01632       di->repoid = -1;
01633       di->data = di->repo->repodata + di->pool->pos.repodataid;
01634       di->repodataid = -1;
01635       di->solvid = solvid;
01636       di->state = di_enterrepo;
01637       di->flags |= SEARCH_THISSOLVID;
01638       return;
01639     }
01640   if (solvid > 0)
01641     {
01642       di->repo = di->pool->solvables[solvid].repo;
01643       di->repoid = -1;
01644     }
01645   else if (di->repoid >= 0)
01646     {
01647       if (!di->pool->nrepos)
01648         {
01649           di->state = di_bye;
01650           return;
01651         }
01652       di->repo = di->pool->repos[0];
01653       di->repoid = 0;
01654     }
01655   di->repodataid = 0;
01656   di->solvid = solvid;
01657   if (solvid)
01658     di->flags |= SEARCH_THISSOLVID;
01659   di->state = di_enterrepo;
01660 }
01661 
01662 void
01663 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
01664 {
01665   di->nparents = 0;
01666   di->kv.parent = 0;
01667   di->rootlevel = 0;
01668   di->repo = repo;
01669   di->repoid = -1;
01670   di->repodataid = 0;
01671   di->solvid = 0;
01672   di->flags &= ~SEARCH_THISSOLVID;
01673   di->state = di_enterrepo;
01674 }
01675 
01676 int
01677 dataiterator_match(Dataiterator *di, Datamatcher *ma)
01678 {
01679   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
01680     return 0;
01681   if (!ma)
01682     return 1;
01683   return datamatcher_match(ma, di->kv.str);
01684 }
01685 
01686 /************************************************************************
01687  * data modify functions
01688  */
01689 
01690 /* extend repodata so that it includes solvables p */
01691 void
01692 repodata_extend(Repodata *data, Id p)
01693 {
01694   if (data->start == data->end)
01695     data->start = data->end = p;
01696   if (p >= data->end)
01697     {
01698       int old = data->end - data->start;
01699       int new = p - data->end + 1;
01700       if (data->attrs)
01701         {
01702           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
01703           memset(data->attrs + old, 0, new * sizeof(Id *));
01704         }
01705       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
01706       memset(data->incoreoffset + old, 0, new * sizeof(Id));
01707       data->end = p + 1;
01708     }
01709   if (p < data->start)
01710     {
01711       int old = data->end - data->start;
01712       int new = data->start - p;
01713       if (data->attrs)
01714         {
01715           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
01716           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
01717           memset(data->attrs, 0, new * sizeof(Id *));
01718         }
01719       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
01720       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
01721       memset(data->incoreoffset, 0, new * sizeof(Id));
01722       data->start = p;
01723     }
01724 }
01725 
01726 /* shrink end of repodata */
01727 void
01728 repodata_shrink(Repodata *data, int end)
01729 {
01730   int i;
01731 
01732   if (data->end <= end)
01733     return;
01734   if (data->start >= end)
01735     {
01736       if (data->attrs)
01737         {
01738           for (i = 0; i < data->end - data->start; i++)
01739             sat_free(data->attrs[i]);
01740           data->attrs = sat_free(data->attrs);
01741         }
01742       data->incoreoffset = sat_free(data->incoreoffset);
01743       data->start = data->end = 0;
01744       return;
01745     }
01746   if (data->attrs)
01747     {
01748       for (i = end; i < data->end; i++)
01749         sat_free(data->attrs[i - data->start]);
01750       data->attrs = sat_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
01751     }
01752   if (data->incoreoffset)
01753     data->incoreoffset = sat_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
01754   data->end = end;
01755 }
01756 
01757 /* extend repodata so that it includes solvables from start to start + num - 1 */
01758 void
01759 repodata_extend_block(Repodata *data, Id start, Id num)
01760 {
01761   if (!num)
01762     return;
01763   if (!data->incoreoffset)
01764     {
01765       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
01766       data->start = start;
01767       data->end = start + num;
01768       return;
01769     }
01770   repodata_extend(data, start);
01771   if (num > 1)
01772     repodata_extend(data, start + num - 1);
01773 }
01774 
01775 /**********************************************************************/
01776 
01777 
01778 #define REPODATA_ATTRS_BLOCK 31
01779 #define REPODATA_ATTRDATA_BLOCK 1023
01780 #define REPODATA_ATTRIDDATA_BLOCK 63
01781 
01782 
01783 Id
01784 repodata_new_handle(Repodata *data)
01785 {
01786   if (!data->nxattrs)
01787     {
01788       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
01789       data->nxattrs = 2;
01790     }
01791   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
01792   data->xattrs[data->nxattrs] = 0;
01793   return -(data->nxattrs++);
01794 }
01795 
01796 static inline Id **
01797 repodata_get_attrp(Repodata *data, Id handle)
01798 {
01799   if (handle == SOLVID_META)
01800     {
01801       if (!data->xattrs)
01802         {
01803           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
01804           data->nxattrs = 2;
01805         }
01806     }
01807   if (handle < 0)
01808     return data->xattrs - handle;
01809   if (handle < data->start || handle >= data->end)
01810     repodata_extend(data, handle);
01811   if (!data->attrs)
01812     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
01813   return data->attrs + (handle - data->start);
01814 }
01815 
01816 static void
01817 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
01818 {
01819   Id *pp;
01820   Id *ap, **app;
01821   int i;
01822 
01823   app = repodata_get_attrp(data, handle);
01824   ap = *app;
01825   i = 0;
01826   if (ap)
01827     {
01828       /* Determine equality based on the name only, allows us to change
01829          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
01830       for (pp = ap; *pp; pp += 2)
01831         if (data->keys[*pp].name == data->keys[keyid].name)
01832           break;
01833       if (*pp)
01834         {
01835           if (overwrite)
01836             {
01837               pp[0] = keyid;
01838               pp[1] = val;
01839             }
01840           return;
01841         }
01842       i = pp - ap;
01843     }
01844   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
01845   *app = ap;
01846   pp = ap + i;
01847   *pp++ = keyid;
01848   *pp++ = val;
01849   *pp = 0;
01850 }
01851 
01852 
01853 static void
01854 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
01855 {
01856   Id keyid;
01857 
01858   keyid = repodata_key2id(data, key, 1);
01859   repodata_insert_keyid(data, solvid, keyid, val, 1);
01860 }
01861 
01862 void
01863 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
01864 {
01865   Repokey key;
01866   key.name = keyname;
01867   key.type = REPOKEY_TYPE_ID;
01868   key.size = 0;
01869   key.storage = KEY_STORAGE_INCORE;
01870   repodata_set(data, solvid, &key, id);
01871 }
01872 
01873 void
01874 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
01875 {
01876   Repokey key;
01877   key.name = keyname;
01878   key.type = REPOKEY_TYPE_NUM;
01879   key.size = 0;
01880   key.storage = KEY_STORAGE_INCORE;
01881   repodata_set(data, solvid, &key, (Id)num);
01882 }
01883 
01884 void
01885 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
01886 {
01887   Repokey key;
01888   Id id;
01889   if (data->localpool)
01890     id = stringpool_str2id(&data->spool, str, 1);
01891   else
01892     id = str2id(data->repo->pool, str, 1);
01893   key.name = keyname;
01894   key.type = REPOKEY_TYPE_ID;
01895   key.size = 0;
01896   key.storage = KEY_STORAGE_INCORE;
01897   repodata_set(data, solvid, &key, id);
01898 }
01899 
01900 void
01901 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
01902 {
01903   Repokey key;
01904   key.name = keyname;
01905   key.type = REPOKEY_TYPE_CONSTANT;
01906   key.size = constant;
01907   key.storage = KEY_STORAGE_INCORE;
01908   repodata_set(data, solvid, &key, 0);
01909 }
01910 
01911 void
01912 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
01913 {
01914   Repokey key;
01915   key.name = keyname;
01916   key.type = REPOKEY_TYPE_CONSTANTID;
01917   key.size = id;
01918   key.storage = KEY_STORAGE_INCORE;
01919   repodata_set(data, solvid, &key, 0);
01920 }
01921 
01922 void
01923 repodata_set_void(Repodata *data, Id solvid, Id keyname)
01924 {
01925   Repokey key;
01926   key.name = keyname;
01927   key.type = REPOKEY_TYPE_VOID;
01928   key.size = 0;
01929   key.storage = KEY_STORAGE_INCORE;
01930   repodata_set(data, solvid, &key, 0);
01931 }
01932 
01933 void
01934 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
01935 {
01936   Repokey key;
01937   int l;
01938 
01939   l = strlen(str) + 1;
01940   key.name = keyname;
01941   key.type = REPOKEY_TYPE_STR;
01942   key.size = 0;
01943   key.storage = KEY_STORAGE_INCORE;
01944   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
01945   memcpy(data->attrdata + data->attrdatalen, str, l);
01946   repodata_set(data, solvid, &key, data->attrdatalen);
01947   data->attrdatalen += l;
01948 }
01949 
01950 void
01951 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
01952 {
01953   Repokey key;
01954   unsigned char *dp;
01955 
01956   key.name = keyname;
01957   key.type = REPOKEY_TYPE_BINARY;
01958   key.size = 0;
01959   key.storage = KEY_STORAGE_INCORE;
01960   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
01961   dp = data->attrdata + data->attrdatalen;
01962   if (len >= (1 << 14))
01963     {
01964       if (len >= (1 << 28))
01965         *dp++ = (len >> 28) | 128;
01966       if (len >= (1 << 21))
01967         *dp++ = (len >> 21) | 128;
01968       *dp++ = (len >> 14) | 128;
01969     }
01970   if (len >= (1 << 7))
01971     *dp++ = (len >> 7) | 128;
01972   *dp++ = len & 127;
01973   if (len)
01974     memcpy(dp, buf, len);
01975   repodata_set(data, solvid, &key, data->attrdatalen);
01976   data->attrdatalen = dp + len - data->attrdata;
01977 }
01978 
01979 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
01980  * so that the caller can append the new element there */
01981 static void
01982 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
01983 {
01984   int oldsize;
01985   Id *ida, *pp, **ppp;
01986 
01987   /* check if it is the same as last time, this speeds things up a lot */
01988   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
01989     {
01990       /* great! just append the new data */
01991       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
01992       data->attriddatalen--;    /* overwrite terminating 0  */
01993       data->lastdatalen += entrysize;
01994       return;
01995     }
01996 
01997   ppp = repodata_get_attrp(data, handle);
01998   pp = *ppp;
01999   if (pp)
02000     for (; *pp; pp += 2)
02001       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
02002         break;
02003   if (!pp || !*pp)
02004     {
02005       /* not found. allocate new key */
02006       Repokey key;
02007       key.name = keyname;
02008       key.type = keytype;
02009       key.size = 0;
02010       key.storage = KEY_STORAGE_INCORE;
02011       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02012       repodata_set(data, handle, &key, data->attriddatalen);
02013       data->lasthandle = 0;     /* next time... */
02014       return;
02015     }
02016   oldsize = 0;
02017   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
02018     oldsize += entrysize;
02019   if (ida + 1 == data->attriddata + data->attriddatalen)
02020     {
02021       /* this was the last entry, just append it */
02022       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02023       data->attriddatalen--;    /* overwrite terminating 0  */
02024     }
02025   else
02026     {
02027       /* too bad. move to back. */
02028       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02029       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
02030       pp[1] = data->attriddatalen;
02031       data->attriddatalen += oldsize;
02032     }
02033   data->lasthandle = handle;
02034   data->lastkey = *pp;
02035   data->lastdatalen = data->attriddatalen + entrysize + 1;
02036 }
02037 
02038 void
02039 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
02040                       const unsigned char *str)
02041 {
02042   Repokey key;
02043   int l;
02044 
02045   if (!(l = sat_chksum_len(type)))
02046     return;
02047   key.name = keyname;
02048   key.type = type;
02049   key.size = 0;
02050   key.storage = KEY_STORAGE_INCORE;
02051   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
02052   memcpy(data->attrdata + data->attrdatalen, str, l);
02053   repodata_set(data, solvid, &key, data->attrdatalen);
02054   data->attrdatalen += l;
02055 }
02056 
02057 static int
02058 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
02059 {
02060   int i;
02061   for (i = 0; i < buflen; i++)
02062     {
02063 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
02064                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
02065                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
02066                 : -1)
02067       int v = c2h(*str);
02068       str++;
02069       if (v < 0)
02070         return 0;
02071       buf[i] = v;
02072       v = c2h(*str);
02073       str++;
02074       if (v < 0)
02075         return 0;
02076       buf[i] = (buf[i] << 4) | v;
02077 #undef c2h
02078     }
02079   return buflen;
02080 }
02081 
02082 void
02083 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
02084                       const char *str)
02085 {
02086   unsigned char buf[64];
02087   int l;
02088 
02089   if (!(l = sat_chksum_len(type)))
02090     return;
02091   if (hexstr2bytes(buf, str, l) != l)
02092     return;
02093   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
02094 }
02095 
02096 const char *
02097 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
02098 {
02099   int i, l;
02100   char *str, *s;
02101 
02102   if (!(l = sat_chksum_len(type)))
02103     return "";
02104   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
02105   for (i = 0; i < l; i++)
02106     {
02107       unsigned char v = buf[i];
02108       unsigned char w = v >> 4;
02109       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
02110       w = v & 15;
02111       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
02112     }
02113   *s = 0;
02114   return str;
02115 }
02116 
02117 /* rpm filenames don't contain the epoch, so strip it */
02118 static inline const char *
02119 evrid2vrstr(Pool *pool, Id evrid)
02120 {
02121   const char *p, *evr = id2str(pool, evrid);
02122   if (!evr)
02123     return evr;
02124   for (p = evr; *p >= '0' && *p <= '9'; p++)
02125     ;
02126   return p != evr && *p == ':' ? p + 1 : evr;
02127 }
02128 
02129 void
02130 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
02131 {
02132   Pool *pool = data->repo->pool;
02133   Solvable *s;
02134   const char *str, *fp;
02135   int l = 0;
02136 
02137   if (medianr)
02138     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
02139   if (!dir)
02140     {
02141       if ((dir = strrchr(file, '/')) != 0)
02142         {
02143           l = dir - file;
02144           dir = file;
02145           file = dir + l + 1;
02146           if (!l)
02147             l++;
02148         }
02149     }
02150   else
02151     l = strlen(dir);
02152   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
02153     {
02154       dir += 2;
02155       l -= 2;
02156     }
02157   if (l == 1 && dir[0] == '.')
02158     l = 0;
02159   s = pool->solvables + solvid;
02160   if (dir && l)
02161     {
02162       str = id2str(pool, s->arch);
02163       if (!strncmp(dir, str, l) && !str[l])
02164         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
02165       else if (!dir[l])
02166         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
02167       else
02168         {
02169           char *dir2 = strdup(dir);
02170           dir2[l] = 0;
02171           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
02172           free(dir2);
02173         }
02174     }
02175   fp = file;
02176   str = id2str(pool, s->name);
02177   l = strlen(str);
02178   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
02179     {
02180       fp += l + 1;
02181       str = evrid2vrstr(pool, s->evr);
02182       l = strlen(str);
02183       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
02184         {
02185           fp += l + 1;
02186           str = id2str(pool, s->arch);
02187           l = strlen(str);
02188           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
02189             {
02190               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
02191               return;
02192             }
02193         }
02194     }
02195   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
02196 }
02197 
02198 void
02199 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
02200 {
02201   assert(dir);
02202 #if 0
02203 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
02204 #endif
02205   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
02206   data->attriddata[data->attriddatalen++] = dir;
02207   data->attriddata[data->attriddatalen++] = num;
02208   data->attriddata[data->attriddatalen++] = num2;
02209   data->attriddata[data->attriddatalen++] = 0;
02210 }
02211 
02212 void
02213 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
02214 {
02215   Id stroff;
02216   int l;
02217 
02218   assert(dir);
02219   l = strlen(str) + 1;
02220   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
02221   memcpy(data->attrdata + data->attrdatalen, str, l);
02222   stroff = data->attrdatalen;
02223   data->attrdatalen += l;
02224 
02225 #if 0
02226 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
02227 #endif
02228   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
02229   data->attriddata[data->attriddatalen++] = dir;
02230   data->attriddata[data->attriddatalen++] = stroff;
02231   data->attriddata[data->attriddatalen++] = 0;
02232 }
02233 
02234 void
02235 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
02236 {
02237 #if 0
02238 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
02239 #endif
02240   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
02241   data->attriddata[data->attriddatalen++] = id;
02242   data->attriddata[data->attriddatalen++] = 0;
02243 }
02244 
02245 void
02246 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
02247                            const char *str)
02248 {
02249   Id id;
02250   if (data->localpool)
02251     id = stringpool_str2id(&data->spool, str, 1);
02252   else
02253     id = str2id(data->repo->pool, str, 1);
02254   repodata_add_idarray(data, solvid, keyname, id);
02255 }
02256 
02257 void
02258 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
02259 {
02260   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
02261   data->attriddata[data->attriddatalen++] = ghandle;
02262   data->attriddata[data->attriddatalen++] = 0;
02263 }
02264 
02265 void
02266 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
02267 {
02268   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
02269   data->attriddata[data->attriddatalen++] = ghandle;
02270   data->attriddata[data->attriddatalen++] = 0;
02271 }
02272 
02273 void
02274 repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
02275 {
02276   Id *pp, *ap, **app;
02277   app = repodata_get_attrp(data, solvid);
02278   ap = *app;
02279   if (!ap)
02280     return;
02281   for (; *ap; ap += 2)
02282     if (data->keys[*ap].name == keyname)
02283       break;
02284   if (!*ap)
02285     return;
02286   pp = ap;
02287   ap += 2;
02288   for (; *ap; ap += 2)
02289     {
02290       if (data->keys[*ap].name == keyname)
02291         continue;
02292       *pp++ = ap[0];
02293       *pp++ = ap[1];
02294     }
02295   *pp = 0;
02296 }
02297 
02298 /* add all attrs from src to dest */
02299 void
02300 repodata_merge_attrs(Repodata *data, Id dest, Id src)
02301 {
02302   Id *keyp;
02303   if (dest == src || !(keyp = data->attrs[src - data->start]))
02304     return;
02305   for (; *keyp; keyp += 2)
02306     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
02307 }
02308 
02309 void
02310 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
02311 {
02312   Id *keyp;
02313   if (dest == src || !(keyp = data->attrs[src - data->start]))
02314     return;
02315   for (; *keyp; keyp += 2)
02316     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
02317       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
02318 }
02319 
02320 
02321 
02322 /**********************************************************************/
02323 
02324 /* TODO: unify with repo_write and repo_solv! */
02325 
02326 #define EXTDATA_BLOCK 1023
02327 
02328 struct extdata {
02329   unsigned char *buf;
02330   int len;
02331 };
02332 
02333 static void
02334 data_addid(struct extdata *xd, Id x)
02335 {
02336   unsigned char *dp;
02337 
02338   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
02339   dp = xd->buf + xd->len;
02340 
02341   if (x >= (1 << 14))
02342     {
02343       if (x >= (1 << 28))
02344         *dp++ = (x >> 28) | 128;
02345       if (x >= (1 << 21))
02346         *dp++ = (x >> 21) | 128;
02347       *dp++ = (x >> 14) | 128;
02348     }
02349   if (x >= (1 << 7))
02350     *dp++ = (x >> 7) | 128;
02351   *dp++ = x & 127;
02352   xd->len = dp - xd->buf;
02353 }
02354 
02355 static void
02356 data_addideof(struct extdata *xd, Id x, int eof)
02357 {
02358   if (x >= 64)
02359     x = (x & 63) | ((x & ~63) << 1);
02360   data_addid(xd, (eof ? x : x | 64));
02361 }
02362 
02363 static void
02364 data_addblob(struct extdata *xd, unsigned char *blob, int len)
02365 {
02366   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
02367   memcpy(xd->buf + xd->len, blob, len);
02368   xd->len += len;
02369 }
02370 
02371 /*********************************/
02372 
02373 static void
02374 repodata_serialize_key(Repodata *data, struct extdata *newincore,
02375                        struct extdata *newvincore,
02376                        Id *schema,
02377                        Repokey *key, Id val)
02378 {
02379   /* Otherwise we have a new value.  Parse it into the internal
02380      form.  */
02381   Id *ida;
02382   struct extdata *xd;
02383   unsigned int oldvincorelen = 0;
02384   Id schemaid, *sp;
02385 
02386   xd = newincore;
02387   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02388     {
02389       xd = newvincore;
02390       oldvincorelen = xd->len;
02391     }
02392   switch (key->type)
02393     {
02394     case REPOKEY_TYPE_VOID:
02395     case REPOKEY_TYPE_CONSTANT:
02396     case REPOKEY_TYPE_CONSTANTID:
02397       break;
02398     case REPOKEY_TYPE_STR:
02399       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
02400       break;
02401     case REPOKEY_TYPE_MD5:
02402       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
02403       break;
02404     case REPOKEY_TYPE_SHA1:
02405       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
02406       break;
02407     case REPOKEY_TYPE_SHA256:
02408       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
02409       break;
02410     case REPOKEY_TYPE_ID:
02411     case REPOKEY_TYPE_NUM:
02412     case REPOKEY_TYPE_DIR:
02413       data_addid(xd, val);
02414       break;
02415     case REPOKEY_TYPE_BINARY:
02416       {
02417         Id len;
02418         unsigned char *dp = data_read_id(data->attrdata + val, &len);
02419         dp += len;
02420         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
02421       }
02422       break;
02423     case REPOKEY_TYPE_IDARRAY:
02424       for (ida = data->attriddata + val; *ida; ida++)
02425         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
02426       break;
02427     case REPOKEY_TYPE_DIRNUMNUMARRAY:
02428       for (ida = data->attriddata + val; *ida; ida += 3)
02429         {
02430           data_addid(xd, ida[0]);
02431           data_addid(xd, ida[1]);
02432           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
02433         }
02434       break;
02435     case REPOKEY_TYPE_DIRSTRARRAY:
02436       for (ida = data->attriddata + val; *ida; ida += 2)
02437         {
02438           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
02439           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
02440         }
02441       break;
02442     case REPOKEY_TYPE_FIXARRAY:
02443       {
02444         int num = 0;
02445         schemaid = 0;
02446         for (ida = data->attriddata + val; *ida; ida++)
02447           {
02448 #if 0
02449             fprintf(stderr, "serialize struct %d\n", *ida);
02450 #endif
02451             sp = schema;
02452             Id *kp = data->xattrs[-*ida];
02453             if (!kp)
02454               continue;
02455             num++;
02456             for (;*kp; kp += 2)
02457               {
02458 #if 0
02459                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
02460 #endif
02461                 *sp++ = *kp;
02462               }
02463             *sp = 0;
02464             if (!schemaid)
02465               schemaid = repodata_schema2id(data, schema, 1);
02466             else if (schemaid != repodata_schema2id(data, schema, 0))
02467               {
02468                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
02469                 exit(1);
02470               }
02471 #if 0
02472             fprintf(stderr, "  schema %d\n", schemaid);
02473 #endif
02474           }
02475         if (!num)
02476           break;
02477         data_addid(xd, num);
02478         data_addid(xd, schemaid);
02479         for (ida = data->attriddata + val; *ida; ida++)
02480           {
02481             Id *kp = data->xattrs[-*ida];
02482             if (!kp)
02483               continue;
02484             for (;*kp; kp += 2)
02485               {
02486                 repodata_serialize_key(data, newincore, newvincore,
02487                                        schema, data->keys + *kp, kp[1]);
02488               }
02489           }
02490         break;
02491       }
02492     case REPOKEY_TYPE_FLEXARRAY:
02493       {
02494         int num = 0;
02495         for (ida = data->attriddata + val; *ida; ida++)
02496           num++;
02497         data_addid(xd, num);
02498         for (ida = data->attriddata + val; *ida; ida++)
02499           {
02500             Id *kp = data->xattrs[-*ida];
02501             if (!kp)
02502               {
02503                 data_addid(xd, 0);      /* XXX */
02504                 continue;
02505               }
02506             sp = schema;
02507             for (;*kp; kp += 2)
02508               *sp++ = *kp;
02509             *sp = 0;
02510             schemaid = repodata_schema2id(data, schema, 1);
02511             data_addid(xd, schemaid);
02512             kp = data->xattrs[-*ida];
02513             for (;*kp; kp += 2)
02514               {
02515                 repodata_serialize_key(data, newincore, newvincore,
02516                                        schema, data->keys + *kp, kp[1]);
02517               }
02518           }
02519         break;
02520       }
02521     default:
02522       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
02523       exit(1);
02524     }
02525   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02526     {
02527       /* put offset/len in incore */
02528       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
02529       oldvincorelen = xd->len - oldvincorelen;
02530       data_addid(newincore, oldvincorelen);
02531     }
02532 }
02533 
02534 void
02535 repodata_internalize(Repodata *data)
02536 {
02537   Repokey *key, solvkey;
02538   Id entry, nentry;
02539   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
02540   unsigned char *dp, *ndp;
02541   int newschema, oldcount;
02542   struct extdata newincore;
02543   struct extdata newvincore;
02544   Id solvkeyid;
02545 
02546   if (!data->attrs && !data->xattrs)
02547     return;
02548 
02549   newvincore.buf = data->vincore;
02550   newvincore.len = data->vincorelen;
02551 
02552   /* find the solvables key, create if needed */
02553   memset(&solvkey, 0, sizeof(solvkey));
02554   solvkey.name = REPOSITORY_SOLVABLES;
02555   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
02556   solvkey.size = 0;
02557   solvkey.storage = KEY_STORAGE_INCORE;
02558   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
02559 
02560   schema = sat_malloc2(data->nkeys, sizeof(Id));
02561   seen = sat_malloc2(data->nkeys, sizeof(Id));
02562 
02563   /* Merge the data already existing (in data->schemata, ->incoredata and
02564      friends) with the new attributes in data->attrs[].  */
02565   nentry = data->end - data->start;
02566   memset(&newincore, 0, sizeof(newincore));
02567   data_addid(&newincore, 0);    /* start data at offset 1 */
02568 
02569   data->mainschema = 0;
02570   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
02571 
02572   /* join entry data */
02573   /* we start with the meta data, entry -1 */
02574   for (entry = -1; entry < nentry; entry++)
02575     {
02576       memset(seen, 0, data->nkeys * sizeof(Id));
02577       oldschema = 0;
02578       dp = data->incoredata;
02579       if (dp)
02580         {
02581           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
02582           dp = data_read_id(dp, &oldschema);
02583         }
02584 #if 0
02585 fprintf(stderr, "oldschema %d\n", oldschema);
02586 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
02587 fprintf(stderr, "schemadata %p\n", data->schemadata);
02588 #endif
02589       /* seen: -1: old data  0: skipped  >0: id + 1 */
02590       newschema = 0;
02591       oldcount = 0;
02592       sp = schema;
02593       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
02594         {
02595           if (seen[*keyp])
02596             {
02597               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
02598               exit(1);
02599             }
02600           seen[*keyp] = -1;
02601           *sp++ = *keyp;
02602           oldcount++;
02603         }
02604       if (entry >= 0)
02605         keyp = data->attrs ? data->attrs[entry] : 0;
02606       else
02607         {
02608           /* strip solvables key */
02609           *sp = 0;
02610           for (sp = keyp = schema; *sp; sp++)
02611             if (*sp != solvkeyid)
02612               *keyp++ = *sp;
02613             else
02614               oldcount--;
02615           sp = keyp;
02616           seen[solvkeyid] = 0;
02617           keyp = data->xattrs ? data->xattrs[1] : 0;
02618         }
02619       if (keyp)
02620         for (; *keyp; keyp += 2)
02621           {
02622             if (!seen[*keyp])
02623               {
02624                 newschema = 1;
02625                 *sp++ = *keyp;
02626               }
02627             seen[*keyp] = keyp[1] + 1;
02628           }
02629       if (entry < 0 && data->end != data->start)
02630         {
02631           *sp++ = solvkeyid;
02632           newschema = 1;
02633         }
02634       *sp = 0;
02635       if (newschema)
02636         /* Ideally we'd like to sort the new schema here, to ensure
02637            schema equality independend of the ordering.  We can't do that
02638            yet.  For once see below (old ids need to come before new ids).
02639            An additional difficulty is that we also need to move
02640            the values with the keys.  */
02641         schemaid = repodata_schema2id(data, schema, 1);
02642       else
02643         schemaid = oldschema;
02644 
02645 
02646       /* Now create data blob.  We walk through the (possibly new) schema
02647          and either copy over old data, or insert the new.  */
02648       /* XXX Here we rely on the fact that the (new) schema has the form
02649          o1 o2 o3 o4 ... | n1 n2 n3 ...
02650          (oX being the old keyids (possibly overwritten), and nX being
02651           the new keyids).  This rules out sorting the keyids in order
02652          to ensure a small schema count.  */
02653       if (entry >= 0)
02654         data->incoreoffset[entry] = newincore.len;
02655       data_addid(&newincore, schemaid);
02656       if (entry == -1)
02657         {
02658           data->mainschema = schemaid;
02659           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
02660         }
02661       keypstart = data->schemadata + data->schemata[schemaid];
02662       for (keyp = keypstart; *keyp; keyp++)
02663         {
02664           if (entry == -1)
02665             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
02666           if (*keyp == solvkeyid)
02667             {
02668               /* add flexarray entry count */
02669               data_addid(&newincore, data->end - data->start);
02670               break;
02671             }
02672           key = data->keys + *keyp;
02673 #if 0
02674           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
02675 #endif
02676           ndp = dp;
02677           if (oldcount)
02678             {
02679               /* Skip the data associated with this old key.  */
02680               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02681                 {
02682                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
02683                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
02684                 }
02685               else if (key->storage == KEY_STORAGE_INCORE)
02686                 ndp = data_skip_key(data, dp, key);
02687               oldcount--;
02688             }
02689           if (seen[*keyp] == -1)
02690             {
02691               /* If this key was an old one _and_ was not overwritten with
02692                  a different value copy over the old value (we skipped it
02693                  above).  */
02694               if (dp != ndp)
02695                 data_addblob(&newincore, dp, ndp - dp);
02696               seen[*keyp] = 0;
02697             }
02698           else if (seen[*keyp])
02699             {
02700               /* Otherwise we have a new value.  Parse it into the internal
02701                  form.  */
02702               repodata_serialize_key(data, &newincore, &newvincore,
02703                                      schema, key, seen[*keyp] - 1);
02704             }
02705           dp = ndp;
02706         }
02707       if (entry >= 0 && data->attrs && data->attrs[entry])
02708         data->attrs[entry] = sat_free(data->attrs[entry]);
02709     }
02710   /* free all xattrs */
02711   for (entry = 0; entry < data->nxattrs; entry++)
02712     if (data->xattrs[entry])
02713       sat_free(data->xattrs[entry]);
02714   data->xattrs = sat_free(data->xattrs);
02715   data->nxattrs = 0;
02716 
02717   data->lasthandle = 0;
02718   data->lastkey = 0;
02719   data->lastdatalen = 0;
02720   sat_free(schema);
02721   sat_free(seen);
02722   repodata_free_schemahash(data);
02723 
02724   sat_free(data->incoredata);
02725   data->incoredata = newincore.buf;
02726   data->incoredatalen = newincore.len;
02727   data->incoredatafree = 0;
02728 
02729   sat_free(data->vincore);
02730   data->vincore = newvincore.buf;
02731   data->vincorelen = newvincore.len;
02732 
02733   data->attrs = sat_free(data->attrs);
02734   data->attrdata = sat_free(data->attrdata);
02735   data->attriddata = sat_free(data->attriddata);
02736   data->attrdatalen = 0;
02737   data->attriddatalen = 0;
02738 }
02739 
02740 void
02741 repodata_disable_paging(Repodata *data)
02742 {
02743   if (maybe_load_repodata(data, 0))
02744     repopagestore_disable_paging(&data->store);
02745 }
02746 
02747 static void
02748 repodata_load_stub(Repodata *data)
02749 {
02750   Repo *repo = data->repo;
02751   Pool *pool = repo->pool;
02752   int r;
02753 
02754   if (!pool->loadcallback)
02755     {
02756       data->state = REPODATA_ERROR;
02757       return;
02758     }
02759   data->state = REPODATA_LOADING;
02760   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
02761   if (!r)
02762     data->state = REPODATA_ERROR;
02763 }
02764 
02765 void
02766 repodata_create_stubs(Repodata *data)
02767 {
02768   Repo *repo = data->repo;
02769   Pool *pool = repo->pool;
02770   Repodata *sdata;
02771   int *stubdataids;
02772   Dataiterator di;
02773   Id xkeyname = 0;
02774   int i, cnt = 0;
02775   int repodataid;
02776   int datastart, dataend;
02777 
02778   repodataid = data - repo->repodata;
02779   datastart = data->start;
02780   dataend = data->end;
02781   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
02782   while (dataiterator_step(&di))
02783     {
02784       if (di.data - repo->repodata != repodataid)
02785         continue;
02786       cnt++;
02787     }
02788   dataiterator_free(&di);
02789   if (!cnt)
02790     return;
02791   stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
02792   for (i = 0; i < cnt; i++)
02793     {
02794       sdata = repo_add_repodata(repo, 0);
02795       if (dataend > datastart)
02796         repodata_extend_block(sdata, datastart, dataend - datastart);
02797       stubdataids[i] = sdata - repo->repodata;
02798       sdata->state = REPODATA_STUB;
02799       sdata->loadcallback = repodata_load_stub;
02800     }
02801   i = 0;
02802   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
02803   sdata = 0;
02804   while (dataiterator_step(&di))
02805     {
02806       if (di.data - repo->repodata != repodataid)
02807         continue;
02808       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
02809         {
02810           dataiterator_entersub(&di);
02811           sdata = repo->repodata + stubdataids[i++];
02812           xkeyname = 0;
02813           continue;
02814         }
02815       switch (di.key->type)
02816         {
02817         case REPOKEY_TYPE_ID:
02818           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
02819           break;
02820         case REPOKEY_TYPE_CONSTANTID:
02821           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
02822           break;
02823         case REPOKEY_TYPE_STR:
02824           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
02825           break;
02826         case REPOKEY_TYPE_VOID:
02827           repodata_set_void(sdata, SOLVID_META, di.key->name);
02828           break;
02829         case REPOKEY_TYPE_NUM:
02830           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
02831           break;
02832         case REPOKEY_TYPE_MD5:
02833         case REPOKEY_TYPE_SHA1:
02834         case REPOKEY_TYPE_SHA256:
02835           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
02836           break;
02837         case REPOKEY_TYPE_IDARRAY:
02838           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
02839           if (di.key->name == REPOSITORY_KEYS)
02840             {
02841               Repokey xkey;
02842 
02843               if (!xkeyname)
02844                 {
02845                   if (!di.kv.eof)
02846                     xkeyname = di.kv.id;
02847                   continue;
02848                 }
02849               xkey.name = xkeyname;
02850               xkey.type = di.kv.id;
02851               xkey.storage = KEY_STORAGE_INCORE;
02852               xkey.size = 0; 
02853               repodata_key2id(sdata, &xkey, 1);
02854               xkeyname = 0;
02855             }
02856         }
02857     }
02858   dataiterator_free(&di);
02859   for (i = 0; i < cnt; i++)
02860     repodata_internalize(repo->repodata + stubdataids[i]);
02861   sat_free(stubdataids);
02862 }
02863 
02864 /*
02865 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
02866 */