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 || (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING || !repodata_filelistfilter_matches(di->data, di->matcher.match))
01213       needcomplete = 1;
01214   if (data->state != REPODATA_AVAILABLE)
01215     return needcomplete ? 1 : 0;
01216   for (j = 1; j < data->nkeys; j++)
01217     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
01218       break;
01219   return j == data->nkeys && !needcomplete ? 0 : 1;
01220 }
01221 
01222 int
01223 dataiterator_step(Dataiterator *di)
01224 {
01225   Id schema;
01226 
01227   for (;;)
01228     {
01229       switch (di->state)
01230         {
01231         case di_enterrepo: di_enterrepo:
01232           if (!di->repo)
01233             goto di_bye;
01234           if (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS))
01235             goto di_nextrepo;
01236           if (!(di->flags & SEARCH_THISSOLVID))
01237             {
01238               di->solvid = di->repo->start - 1; /* reset solvid iterator */
01239               goto di_nextsolvable;
01240             }
01241           /* FALLTHROUGH */
01242 
01243         case di_entersolvable: di_entersolvable:
01244           if (di->repodataid >= 0)
01245             {
01246               di->repodataid = 0;       /* reset repodata iterator */
01247               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)
01248                 {
01249                   di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
01250                   di->data = 0;
01251                   goto di_entersolvablekey;
01252                 }
01253             }
01254           /* FALLTHROUGH */
01255 
01256         case di_enterrepodata: di_enterrepodata:
01257           if (di->repodataid >= 0)
01258             {
01259               if (di->repodataid >= di->repo->nrepodata)
01260                 goto di_nextsolvable;
01261               di->data = di->repo->repodata + di->repodataid;
01262             }
01263           if (di->repodataid >= 0 && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
01264             goto di_nextrepodata;
01265           if (!maybe_load_repodata(di->data, di->keyname))
01266             goto di_nextrepodata;
01267           di->dp = solvid2data(di->data, di->solvid, &schema);
01268           if (!di->dp)
01269             goto di_nextrepodata;
01270           if (di->solvid == SOLVID_POS)
01271             di->solvid = di->pool->pos.solvid;
01272           /* reset key iterator */
01273           di->keyp = di->data->schemadata + di->data->schemata[schema];
01274           /* FALLTHROUGH */
01275 
01276         case di_enterschema: di_enterschema:
01277           if (di->keyname)
01278             di->dp = dataiterator_find_keyname(di, di->keyname);
01279           if (!di->dp || !*di->keyp)
01280             {
01281               if (di->kv.parent)
01282                 goto di_leavesub;
01283               goto di_nextrepodata;
01284             }
01285           /* FALLTHROUGH */
01286 
01287         case di_enterkey: di_enterkey:
01288           di->kv.entry = -1;
01289           di->key = di->data->keys + *di->keyp;
01290           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
01291           if (!di->ddp)
01292             goto di_nextkey;
01293           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
01294             goto di_enterarray;
01295           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01296             goto di_nextkey;
01297           /* FALLTHROUGH */
01298 
01299         case di_nextattr:
01300           di->kv.entry++;
01301           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
01302           if (di->kv.eof)
01303             di->state = di_nextkey;
01304           else
01305             di->state = di_nextattr;
01306           break;
01307 
01308         case di_nextkey: di_nextkey:
01309           if (!di->keyname && *++di->keyp)
01310             goto di_enterkey;
01311           if (di->kv.parent)
01312             goto di_leavesub;
01313           /* FALLTHROUGH */
01314 
01315         case di_nextrepodata: di_nextrepodata:
01316           if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
01317               goto di_enterrepodata;
01318           /* FALLTHROUGH */
01319 
01320         case di_nextsolvable: di_nextsolvable:
01321           if (!(di->flags & SEARCH_THISSOLVID))
01322             {
01323               if (di->solvid < 0)
01324                 di->solvid = di->repo->start;
01325               else
01326                 di->solvid++;
01327               for (; di->solvid < di->repo->end; di->solvid++)
01328                 {
01329                   if (di->pool->solvables[di->solvid].repo == di->repo)
01330                     goto di_entersolvable;
01331                 }
01332             }
01333           /* FALLTHROUGH */
01334 
01335         case di_nextrepo: di_nextrepo:
01336           if (di->repoid >= 0)
01337             {
01338               di->repoid++;
01339               di->repodataid = 0;
01340               if (di->repoid < di->pool->nrepos)
01341                 {
01342                   di->repo = di->pool->repos[di->repoid];
01343                   goto di_enterrepo;
01344                 }
01345             }
01346         /* FALLTHROUGH */
01347 
01348         case di_bye: di_bye:
01349           di->state = di_bye;
01350           return 0;
01351 
01352         case di_enterarray: di_enterarray:
01353           if (di->key->name == REPOSITORY_SOLVABLES)
01354             goto di_nextkey;
01355           di->ddp = data_read_id(di->ddp, &di->kv.num);
01356           di->kv.eof = 0;
01357           di->kv.entry = -1;
01358           /* FALLTHROUGH */
01359 
01360         case di_nextarrayelement: di_nextarrayelement:
01361           di->kv.entry++;
01362           if (di->kv.entry)
01363             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
01364           if (di->kv.entry == di->kv.num)
01365             {
01366               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01367                 goto di_nextkey;
01368               if (!(di->flags & SEARCH_ARRAYSENTINEL))
01369                 goto di_nextkey;
01370               di->kv.str = (char *)di->ddp;
01371               di->kv.eof = 2;
01372               di->state = di_nextkey;
01373               break;
01374             }
01375           if (di->kv.entry == di->kv.num - 1)
01376             di->kv.eof = 1;
01377           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
01378             di->ddp = data_read_id(di->ddp, &di->kv.id);
01379           di->kv.str = (char *)di->ddp;
01380           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
01381             goto di_entersub;
01382           if ((di->flags & SEARCH_SUB) != 0)
01383             di->state = di_entersub;
01384           else
01385             di->state = di_nextarrayelement;
01386           break;
01387 
01388         case di_entersub: di_entersub:
01389           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
01390             goto di_nextarrayelement;   /* sorry, full */
01391           di->parents[di->nparents].kv = di->kv;
01392           di->parents[di->nparents].dp = di->dp;
01393           di->parents[di->nparents].keyp = di->keyp;
01394           di->dp = (unsigned char *)di->kv.str;
01395           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
01396           memset(&di->kv, 0, sizeof(di->kv));
01397           di->kv.parent = &di->parents[di->nparents].kv;
01398           di->nparents++;
01399           di->keyname = di->keynames[di->nparents - di->rootlevel];
01400           goto di_enterschema;
01401 
01402         case di_leavesub: di_leavesub:
01403           if (di->nparents - 1 < di->rootlevel)
01404             goto di_bye;
01405           di->nparents--;
01406           di->dp = di->parents[di->nparents].dp;
01407           di->kv = di->parents[di->nparents].kv;
01408           di->keyp = di->parents[di->nparents].keyp;
01409           di->key = di->data->keys + *di->keyp;
01410           di->ddp = (unsigned char *)di->kv.str;
01411           di->keyname = di->keynames[di->nparents - di->rootlevel];
01412           goto di_nextarrayelement;
01413 
01414         /* special solvable attr handling follows */
01415 
01416         case di_nextsolvableattr:
01417           di->kv.id = *di->idp++;
01418           di->kv.entry++;
01419           if (!*di->idp)
01420             {
01421               di->kv.eof = 1;
01422               di->state = di_nextsolvablekey;
01423             }
01424           break;
01425 
01426         case di_nextsolvablekey: di_nextsolvablekey:
01427           if (di->keyname || di->key->name == RPM_RPMDBID)
01428             goto di_enterrepodata;
01429           di->key++;
01430           /* FALLTHROUGH */
01431 
01432         case di_entersolvablekey: di_entersolvablekey:
01433           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
01434           if (!di->idp || !di->idp[0])
01435             goto di_nextsolvablekey;
01436           di->kv.id = di->idp[0];
01437           di->kv.num = di->idp[0];
01438           di->idp++;
01439           if (!di->kv.eof && !di->idp[0])
01440             di->kv.eof = 1;
01441           di->kv.entry = 0;
01442           if (di->kv.eof)
01443             di->state = di_nextsolvablekey;
01444           else
01445             di->state = di_nextsolvableattr;
01446           break;
01447         }
01448 
01449       if (di->matcher.match)
01450         {
01451           /* simple pre-check so that we don't need to stringify */
01452           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))
01453             {
01454               int l = strlen(di->matcher.match) - strlen(di->kv.str);
01455               if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
01456                 continue;
01457             }
01458           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
01459             {
01460               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
01461                 return 1;
01462               continue;
01463             }
01464           if (!datamatcher_match(&di->matcher, di->kv.str))
01465             continue;
01466         }
01467       /* found something! */
01468       return 1;
01469     }
01470 }
01471 
01472 void
01473 dataiterator_entersub(Dataiterator *di)
01474 {
01475   if (di->state == di_nextarrayelement)
01476     di->state = di_entersub;
01477 }
01478 
01479 void
01480 dataiterator_setpos(Dataiterator *di)
01481 {
01482   if (di->kv.eof == 2)
01483     {
01484       pool_clear_pos(di->pool);
01485       return;
01486     }
01487   di->pool->pos.solvid = di->solvid;
01488   di->pool->pos.repo = di->repo;
01489   di->pool->pos.repodataid = di->data - di->repo->repodata;
01490   di->pool->pos.schema = di->kv.id;
01491   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
01492 }
01493 
01494 void
01495 dataiterator_setpos_parent(Dataiterator *di)
01496 {
01497   if (!di->kv.parent || di->kv.parent->eof == 2)
01498     {
01499       pool_clear_pos(di->pool);
01500       return;
01501     }
01502   di->pool->pos.solvid = di->solvid;
01503   di->pool->pos.repo = di->repo;
01504   di->pool->pos.repodataid = di->data - di->repo->repodata;
01505   di->pool->pos.schema = di->kv.parent->id;
01506   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
01507 }
01508 
01509 /* clones just the position, not the search keys/matcher */
01510 void
01511 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
01512 {
01513   di->state = from->state;
01514   di->flags &= ~SEARCH_THISSOLVID;
01515   di->flags |= (from->flags & SEARCH_THISSOLVID);
01516   di->repo = from->repo;
01517   di->data = from->data;
01518   di->dp = from->dp;
01519   di->ddp = from->ddp;
01520   di->idp = from->idp;
01521   di->keyp = from->keyp;
01522   di->key = from->key;
01523   di->kv = from->kv;
01524   di->repodataid = from->repodataid;
01525   di->solvid = from->solvid;
01526   di->repoid = from->repoid;
01527   di->rootlevel = from->rootlevel;
01528   memcpy(di->parents, from->parents, sizeof(from->parents));
01529   di->nparents = from->nparents;
01530   if (di->nparents)
01531     {
01532       int i;
01533       for (i = 1; i < di->nparents; i++)
01534         di->parents[i].kv.parent = &di->parents[i - 1].kv;
01535       di->kv.parent = &di->parents[di->nparents - 1].kv;
01536     }
01537 }
01538 
01539 void
01540 dataiterator_seek(Dataiterator *di, int whence)
01541 {
01542   if ((whence & DI_SEEK_STAY) != 0)
01543     di->rootlevel = di->nparents;
01544   switch (whence & ~DI_SEEK_STAY)
01545     {
01546     case DI_SEEK_CHILD:
01547       if (di->state != di_nextarrayelement)
01548         break;
01549       if ((whence & DI_SEEK_STAY) != 0)
01550         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
01551       di->state = di_entersub;
01552       break;
01553     case DI_SEEK_PARENT:
01554       if (!di->nparents)
01555         {
01556           di->state = di_bye;
01557           break;
01558         }
01559       di->nparents--;
01560       if (di->rootlevel > di->nparents)
01561         di->rootlevel = di->nparents;
01562       di->dp = di->parents[di->nparents].dp;
01563       di->kv = di->parents[di->nparents].kv;
01564       di->keyp = di->parents[di->nparents].keyp;
01565       di->key = di->data->keys + *di->keyp;
01566       di->ddp = (unsigned char *)di->kv.str;
01567       di->keyname = di->keynames[di->nparents - di->rootlevel];
01568       di->state = di_nextarrayelement;
01569       break;
01570     case DI_SEEK_REWIND:
01571       if (!di->nparents)
01572         {
01573           di->state = di_bye;
01574           break;
01575         }
01576       di->dp = (unsigned char *)di->kv.parent->str;
01577       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
01578       di->state = di_enterschema;
01579       break;
01580     default:
01581       break;
01582     }
01583 }
01584 
01585 void
01586 dataiterator_skip_attribute(Dataiterator *di)
01587 {
01588   if (di->state == di_nextsolvableattr)
01589     di->state = di_nextsolvablekey;
01590   else
01591     di->state = di_nextkey;
01592 }
01593 
01594 void
01595 dataiterator_skip_solvable(Dataiterator *di)
01596 {
01597   di->nparents = 0;
01598   di->kv.parent = 0;
01599   di->rootlevel = 0;
01600   di->keyname = di->keynames[0];
01601   di->state = di_nextsolvable;
01602 }
01603 
01604 void
01605 dataiterator_skip_repo(Dataiterator *di)
01606 {
01607   di->nparents = 0;
01608   di->kv.parent = 0;
01609   di->rootlevel = 0;
01610   di->keyname = di->keynames[0];
01611   di->state = di_nextrepo;
01612 }
01613 
01614 void
01615 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
01616 {
01617   di->nparents = 0;
01618   di->kv.parent = 0;
01619   di->rootlevel = 0;
01620   di->keyname = di->keynames[0];
01621   if (solvid == SOLVID_POS)
01622     {
01623       di->repo = di->pool->pos.repo;
01624       if (!di->repo)
01625         {
01626           di->state = di_bye;
01627           return;
01628         }
01629       di->repoid = -1;
01630       di->data = di->repo->repodata + di->pool->pos.repodataid;
01631       di->repodataid = -1;
01632       di->solvid = solvid;
01633       di->state = di_enterrepo;
01634       di->flags |= SEARCH_THISSOLVID;
01635       return;
01636     }
01637   if (solvid > 0)
01638     {
01639       di->repo = di->pool->solvables[solvid].repo;
01640       di->repoid = -1;
01641     }
01642   else if (di->repoid >= 0)
01643     {
01644       if (!di->pool->nrepos)
01645         {
01646           di->state = di_bye;
01647           return;
01648         }
01649       di->repo = di->pool->repos[0];
01650       di->repoid = 0;
01651     }
01652   di->repodataid = 0;
01653   di->solvid = solvid;
01654   if (solvid)
01655     di->flags |= SEARCH_THISSOLVID;
01656   di->state = di_enterrepo;
01657 }
01658 
01659 void
01660 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
01661 {
01662   di->nparents = 0;
01663   di->kv.parent = 0;
01664   di->rootlevel = 0;
01665   di->repo = repo;
01666   di->repoid = -1;
01667   di->repodataid = 0;
01668   di->solvid = 0;
01669   di->flags &= ~SEARCH_THISSOLVID;
01670   di->state = di_enterrepo;
01671 }
01672 
01673 int
01674 dataiterator_match(Dataiterator *di, Datamatcher *ma)
01675 {
01676   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
01677     return 0;
01678   if (!ma)
01679     return 1;
01680   return datamatcher_match(ma, di->kv.str);
01681 }
01682 
01683 /************************************************************************
01684  * data modify functions
01685  */
01686 
01687 /* extend repodata so that it includes solvables p */
01688 void
01689 repodata_extend(Repodata *data, Id p)
01690 {
01691   if (data->start == data->end)
01692     data->start = data->end = p;
01693   if (p >= data->end)
01694     {
01695       int old = data->end - data->start;
01696       int new = p - data->end + 1;
01697       if (data->attrs)
01698         {
01699           data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
01700           memset(data->attrs + old, 0, new * sizeof(Id *));
01701         }
01702       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
01703       memset(data->incoreoffset + old, 0, new * sizeof(Id));
01704       data->end = p + 1;
01705     }
01706   if (p < data->start)
01707     {
01708       int old = data->end - data->start;
01709       int new = data->start - p;
01710       if (data->attrs)
01711         {
01712           data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
01713           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
01714           memset(data->attrs, 0, new * sizeof(Id *));
01715         }
01716       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
01717       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
01718       memset(data->incoreoffset, 0, new * sizeof(Id));
01719       data->start = p;
01720     }
01721 }
01722 
01723 /* shrink end of repodata */
01724 void
01725 repodata_shrink(Repodata *data, int end)
01726 {
01727   int i;
01728 
01729   if (data->end <= end)
01730     return;
01731   if (data->start >= end)
01732     {
01733       if (data->attrs)
01734         {
01735           for (i = 0; i < data->end - data->start; i++)
01736             sat_free(data->attrs[i]);
01737           data->attrs = sat_free(data->attrs);
01738         }
01739       data->incoreoffset = sat_free(data->incoreoffset);
01740       data->start = data->end = 0;
01741       return;
01742     }
01743   if (data->attrs)
01744     {
01745       for (i = end; i < data->end; i++)
01746         sat_free(data->attrs[i - data->start]);
01747       data->attrs = sat_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
01748     }
01749   if (data->incoreoffset)
01750     data->incoreoffset = sat_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
01751   data->end = end;
01752 }
01753 
01754 /* extend repodata so that it includes solvables from start to start + num - 1 */
01755 void
01756 repodata_extend_block(Repodata *data, Id start, Id num)
01757 {
01758   if (!num)
01759     return;
01760   if (!data->incoreoffset)
01761     {
01762       data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
01763       data->start = start;
01764       data->end = start + num;
01765       return;
01766     }
01767   repodata_extend(data, start);
01768   if (num > 1)
01769     repodata_extend(data, start + num - 1);
01770 }
01771 
01772 /**********************************************************************/
01773 
01774 
01775 #define REPODATA_ATTRS_BLOCK 31
01776 #define REPODATA_ATTRDATA_BLOCK 1023
01777 #define REPODATA_ATTRIDDATA_BLOCK 63
01778 
01779 
01780 Id
01781 repodata_new_handle(Repodata *data)
01782 {
01783   if (!data->nxattrs)
01784     {
01785       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
01786       data->nxattrs = 2;
01787     }
01788   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
01789   data->xattrs[data->nxattrs] = 0;
01790   return -(data->nxattrs++);
01791 }
01792 
01793 static inline Id **
01794 repodata_get_attrp(Repodata *data, Id handle)
01795 {
01796   if (handle == SOLVID_META)
01797     {
01798       if (!data->xattrs)
01799         {
01800           data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
01801           data->nxattrs = 2;
01802         }
01803     }
01804   if (handle < 0)
01805     return data->xattrs - handle;
01806   if (handle < data->start || handle >= data->end)
01807     repodata_extend(data, handle);
01808   if (!data->attrs)
01809     data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
01810   return data->attrs + (handle - data->start);
01811 }
01812 
01813 static void
01814 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
01815 {
01816   Id *pp;
01817   Id *ap, **app;
01818   int i;
01819 
01820   app = repodata_get_attrp(data, handle);
01821   ap = *app;
01822   i = 0;
01823   if (ap)
01824     {
01825       /* Determine equality based on the name only, allows us to change
01826          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
01827       for (pp = ap; *pp; pp += 2)
01828         if (data->keys[*pp].name == data->keys[keyid].name)
01829           break;
01830       if (*pp)
01831         {
01832           if (overwrite)
01833             {
01834               pp[0] = keyid;
01835               pp[1] = val;
01836             }
01837           return;
01838         }
01839       i = pp - ap;
01840     }
01841   ap = sat_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
01842   *app = ap;
01843   pp = ap + i;
01844   *pp++ = keyid;
01845   *pp++ = val;
01846   *pp = 0;
01847 }
01848 
01849 
01850 static void
01851 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
01852 {
01853   Id keyid;
01854 
01855   keyid = repodata_key2id(data, key, 1);
01856   repodata_insert_keyid(data, solvid, keyid, val, 1);
01857 }
01858 
01859 void
01860 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
01861 {
01862   Repokey key;
01863   key.name = keyname;
01864   key.type = REPOKEY_TYPE_ID;
01865   key.size = 0;
01866   key.storage = KEY_STORAGE_INCORE;
01867   repodata_set(data, solvid, &key, id);
01868 }
01869 
01870 void
01871 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
01872 {
01873   Repokey key;
01874   key.name = keyname;
01875   key.type = REPOKEY_TYPE_NUM;
01876   key.size = 0;
01877   key.storage = KEY_STORAGE_INCORE;
01878   repodata_set(data, solvid, &key, (Id)num);
01879 }
01880 
01881 void
01882 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
01883 {
01884   Repokey key;
01885   Id id;
01886   if (data->localpool)
01887     id = stringpool_str2id(&data->spool, str, 1);
01888   else
01889     id = str2id(data->repo->pool, str, 1);
01890   key.name = keyname;
01891   key.type = REPOKEY_TYPE_ID;
01892   key.size = 0;
01893   key.storage = KEY_STORAGE_INCORE;
01894   repodata_set(data, solvid, &key, id);
01895 }
01896 
01897 void
01898 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
01899 {
01900   Repokey key;
01901   key.name = keyname;
01902   key.type = REPOKEY_TYPE_CONSTANT;
01903   key.size = constant;
01904   key.storage = KEY_STORAGE_INCORE;
01905   repodata_set(data, solvid, &key, 0);
01906 }
01907 
01908 void
01909 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
01910 {
01911   Repokey key;
01912   key.name = keyname;
01913   key.type = REPOKEY_TYPE_CONSTANTID;
01914   key.size = id;
01915   key.storage = KEY_STORAGE_INCORE;
01916   repodata_set(data, solvid, &key, 0);
01917 }
01918 
01919 void
01920 repodata_set_void(Repodata *data, Id solvid, Id keyname)
01921 {
01922   Repokey key;
01923   key.name = keyname;
01924   key.type = REPOKEY_TYPE_VOID;
01925   key.size = 0;
01926   key.storage = KEY_STORAGE_INCORE;
01927   repodata_set(data, solvid, &key, 0);
01928 }
01929 
01930 void
01931 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
01932 {
01933   Repokey key;
01934   int l;
01935 
01936   l = strlen(str) + 1;
01937   key.name = keyname;
01938   key.type = REPOKEY_TYPE_STR;
01939   key.size = 0;
01940   key.storage = KEY_STORAGE_INCORE;
01941   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
01942   memcpy(data->attrdata + data->attrdatalen, str, l);
01943   repodata_set(data, solvid, &key, data->attrdatalen);
01944   data->attrdatalen += l;
01945 }
01946 
01947 void
01948 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
01949 {
01950   Repokey key;
01951   unsigned char *dp;
01952 
01953   key.name = keyname;
01954   key.type = REPOKEY_TYPE_BINARY;
01955   key.size = 0;
01956   key.storage = KEY_STORAGE_INCORE;
01957   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
01958   dp = data->attrdata + data->attrdatalen;
01959   if (len >= (1 << 14))
01960     {
01961       if (len >= (1 << 28))
01962         *dp++ = (len >> 28) | 128;
01963       if (len >= (1 << 21))
01964         *dp++ = (len >> 21) | 128;
01965       *dp++ = (len >> 14) | 128;
01966     }
01967   if (len >= (1 << 7))
01968     *dp++ = (len >> 7) | 128;
01969   *dp++ = len & 127;
01970   if (len)
01971     memcpy(dp, buf, len);
01972   repodata_set(data, solvid, &key, data->attrdatalen);
01973   data->attrdatalen = dp + len - data->attrdata;
01974 }
01975 
01976 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
01977  * so that the caller can append the new element there */
01978 static void
01979 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
01980 {
01981   int oldsize;
01982   Id *ida, *pp, **ppp;
01983 
01984   /* check if it is the same as last time, this speeds things up a lot */
01985   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
01986     {
01987       /* great! just append the new data */
01988       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
01989       data->attriddatalen--;    /* overwrite terminating 0  */
01990       data->lastdatalen += entrysize;
01991       return;
01992     }
01993 
01994   ppp = repodata_get_attrp(data, handle);
01995   pp = *ppp;
01996   if (pp)
01997     for (; *pp; pp += 2)
01998       if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
01999         break;
02000   if (!pp || !*pp)
02001     {
02002       /* not found. allocate new key */
02003       Repokey key;
02004       key.name = keyname;
02005       key.type = keytype;
02006       key.size = 0;
02007       key.storage = KEY_STORAGE_INCORE;
02008       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02009       repodata_set(data, handle, &key, data->attriddatalen);
02010       data->lasthandle = 0;     /* next time... */
02011       return;
02012     }
02013   oldsize = 0;
02014   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
02015     oldsize += entrysize;
02016   if (ida + 1 == data->attriddata + data->attriddatalen)
02017     {
02018       /* this was the last entry, just append it */
02019       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02020       data->attriddatalen--;    /* overwrite terminating 0  */
02021     }
02022   else
02023     {
02024       /* too bad. move to back. */
02025       data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
02026       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
02027       pp[1] = data->attriddatalen;
02028       data->attriddatalen += oldsize;
02029     }
02030   data->lasthandle = handle;
02031   data->lastkey = *pp;
02032   data->lastdatalen = data->attriddatalen + entrysize + 1;
02033 }
02034 
02035 void
02036 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
02037                       const unsigned char *str)
02038 {
02039   Repokey key;
02040   int l;
02041 
02042   if (!(l = sat_chksum_len(type)))
02043     return;
02044   key.name = keyname;
02045   key.type = type;
02046   key.size = 0;
02047   key.storage = KEY_STORAGE_INCORE;
02048   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
02049   memcpy(data->attrdata + data->attrdatalen, str, l);
02050   repodata_set(data, solvid, &key, data->attrdatalen);
02051   data->attrdatalen += l;
02052 }
02053 
02054 static int
02055 hexstr2bytes(unsigned char *buf, const char *str, int buflen)
02056 {
02057   int i;
02058   for (i = 0; i < buflen; i++)
02059     {
02060 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
02061                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
02062                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
02063                 : -1)
02064       int v = c2h(*str);
02065       str++;
02066       if (v < 0)
02067         return 0;
02068       buf[i] = v;
02069       v = c2h(*str);
02070       str++;
02071       if (v < 0)
02072         return 0;
02073       buf[i] = (buf[i] << 4) | v;
02074 #undef c2h
02075     }
02076   return buflen;
02077 }
02078 
02079 void
02080 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
02081                       const char *str)
02082 {
02083   unsigned char buf[64];
02084   int l;
02085 
02086   if (!(l = sat_chksum_len(type)))
02087     return;
02088   if (hexstr2bytes(buf, str, l) != l)
02089     return;
02090   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
02091 }
02092 
02093 const char *
02094 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
02095 {
02096   int i, l;
02097   char *str, *s;
02098 
02099   if (!(l = sat_chksum_len(type)))
02100     return "";
02101   s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
02102   for (i = 0; i < l; i++)
02103     {
02104       unsigned char v = buf[i];
02105       unsigned char w = v >> 4;
02106       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
02107       w = v & 15;
02108       *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
02109     }
02110   *s = 0;
02111   return str;
02112 }
02113 
02114 /* rpm filenames don't contain the epoch, so strip it */
02115 static inline const char *
02116 evrid2vrstr(Pool *pool, Id evrid)
02117 {
02118   const char *p, *evr = id2str(pool, evrid);
02119   if (!evr)
02120     return evr;
02121   for (p = evr; *p >= '0' && *p <= '9'; p++)
02122     ;
02123   return p != evr && *p == ':' ? p + 1 : evr;
02124 }
02125 
02126 void
02127 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
02128 {
02129   Pool *pool = data->repo->pool;
02130   Solvable *s;
02131   const char *str, *fp;
02132   int l = 0;
02133 
02134   if (medianr)
02135     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
02136   if (!dir)
02137     {
02138       if ((dir = strrchr(file, '/')) != 0)
02139         {
02140           l = dir - file;
02141           dir = file;
02142           file = dir + l + 1;
02143           if (!l)
02144             l++;
02145         }
02146     }
02147   else
02148     l = strlen(dir);
02149   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
02150     {
02151       dir += 2;
02152       l -= 2;
02153     }
02154   if (l == 1 && dir[0] == '.')
02155     l = 0;
02156   s = pool->solvables + solvid;
02157   if (dir && l)
02158     {
02159       str = id2str(pool, s->arch);
02160       if (!strncmp(dir, str, l) && !str[l])
02161         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
02162       else if (!dir[l])
02163         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
02164       else
02165         {
02166           char *dir2 = strdup(dir);
02167           dir2[l] = 0;
02168           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
02169           free(dir2);
02170         }
02171     }
02172   fp = file;
02173   str = id2str(pool, s->name);
02174   l = strlen(str);
02175   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
02176     {
02177       fp += l + 1;
02178       str = evrid2vrstr(pool, s->evr);
02179       l = strlen(str);
02180       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
02181         {
02182           fp += l + 1;
02183           str = id2str(pool, s->arch);
02184           l = strlen(str);
02185           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
02186             {
02187               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
02188               return;
02189             }
02190         }
02191     }
02192   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
02193 }
02194 
02195 void
02196 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
02197 {
02198   assert(dir);
02199 #if 0
02200 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
02201 #endif
02202   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
02203   data->attriddata[data->attriddatalen++] = dir;
02204   data->attriddata[data->attriddatalen++] = num;
02205   data->attriddata[data->attriddatalen++] = num2;
02206   data->attriddata[data->attriddatalen++] = 0;
02207 }
02208 
02209 void
02210 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
02211 {
02212   Id stroff;
02213   int l;
02214 
02215   assert(dir);
02216   l = strlen(str) + 1;
02217   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
02218   memcpy(data->attrdata + data->attrdatalen, str, l);
02219   stroff = data->attrdatalen;
02220   data->attrdatalen += l;
02221 
02222 #if 0
02223 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
02224 #endif
02225   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
02226   data->attriddata[data->attriddatalen++] = dir;
02227   data->attriddata[data->attriddatalen++] = stroff;
02228   data->attriddata[data->attriddatalen++] = 0;
02229 }
02230 
02231 void
02232 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
02233 {
02234 #if 0
02235 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
02236 #endif
02237   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
02238   data->attriddata[data->attriddatalen++] = id;
02239   data->attriddata[data->attriddatalen++] = 0;
02240 }
02241 
02242 void
02243 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
02244                            const char *str)
02245 {
02246   Id id;
02247   if (data->localpool)
02248     id = stringpool_str2id(&data->spool, str, 1);
02249   else
02250     id = str2id(data->repo->pool, str, 1);
02251   repodata_add_idarray(data, solvid, keyname, id);
02252 }
02253 
02254 void
02255 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
02256 {
02257   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
02258   data->attriddata[data->attriddatalen++] = ghandle;
02259   data->attriddata[data->attriddatalen++] = 0;
02260 }
02261 
02262 void
02263 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
02264 {
02265   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
02266   data->attriddata[data->attriddatalen++] = ghandle;
02267   data->attriddata[data->attriddatalen++] = 0;
02268 }
02269 
02270 void
02271 repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
02272 {
02273   Id *pp, *ap, **app;
02274   app = repodata_get_attrp(data, solvid);
02275   ap = *app;
02276   if (!ap)
02277     return;
02278   for (; *ap; ap += 2)
02279     if (data->keys[*ap].name == keyname)
02280       break;
02281   if (!*ap)
02282     return;
02283   pp = ap;
02284   ap += 2;
02285   for (; *ap; ap += 2)
02286     {
02287       if (data->keys[*ap].name == keyname)
02288         continue;
02289       *pp++ = ap[0];
02290       *pp++ = ap[1];
02291     }
02292   *pp = 0;
02293 }
02294 
02295 /* add all attrs from src to dest */
02296 void
02297 repodata_merge_attrs(Repodata *data, Id dest, Id src)
02298 {
02299   Id *keyp;
02300   if (dest == src || !(keyp = data->attrs[src - data->start]))
02301     return;
02302   for (; *keyp; keyp += 2)
02303     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
02304 }
02305 
02306 void
02307 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
02308 {
02309   Id *keyp;
02310   if (dest == src || !(keyp = data->attrs[src - data->start]))
02311     return;
02312   for (; *keyp; keyp += 2)
02313     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
02314       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
02315 }
02316 
02317 
02318 
02319 /**********************************************************************/
02320 
02321 /* TODO: unify with repo_write and repo_solv! */
02322 
02323 #define EXTDATA_BLOCK 1023
02324 
02325 struct extdata {
02326   unsigned char *buf;
02327   int len;
02328 };
02329 
02330 static void
02331 data_addid(struct extdata *xd, Id x)
02332 {
02333   unsigned char *dp;
02334 
02335   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
02336   dp = xd->buf + xd->len;
02337 
02338   if (x >= (1 << 14))
02339     {
02340       if (x >= (1 << 28))
02341         *dp++ = (x >> 28) | 128;
02342       if (x >= (1 << 21))
02343         *dp++ = (x >> 21) | 128;
02344       *dp++ = (x >> 14) | 128;
02345     }
02346   if (x >= (1 << 7))
02347     *dp++ = (x >> 7) | 128;
02348   *dp++ = x & 127;
02349   xd->len = dp - xd->buf;
02350 }
02351 
02352 static void
02353 data_addideof(struct extdata *xd, Id x, int eof)
02354 {
02355   if (x >= 64)
02356     x = (x & 63) | ((x & ~63) << 1);
02357   data_addid(xd, (eof ? x : x | 64));
02358 }
02359 
02360 static void
02361 data_addblob(struct extdata *xd, unsigned char *blob, int len)
02362 {
02363   xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
02364   memcpy(xd->buf + xd->len, blob, len);
02365   xd->len += len;
02366 }
02367 
02368 /*********************************/
02369 
02370 static void
02371 repodata_serialize_key(Repodata *data, struct extdata *newincore,
02372                        struct extdata *newvincore,
02373                        Id *schema,
02374                        Repokey *key, Id val)
02375 {
02376   /* Otherwise we have a new value.  Parse it into the internal
02377      form.  */
02378   Id *ida;
02379   struct extdata *xd;
02380   unsigned int oldvincorelen = 0;
02381   Id schemaid, *sp;
02382 
02383   xd = newincore;
02384   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02385     {
02386       xd = newvincore;
02387       oldvincorelen = xd->len;
02388     }
02389   switch (key->type)
02390     {
02391     case REPOKEY_TYPE_VOID:
02392     case REPOKEY_TYPE_CONSTANT:
02393     case REPOKEY_TYPE_CONSTANTID:
02394       break;
02395     case REPOKEY_TYPE_STR:
02396       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
02397       break;
02398     case REPOKEY_TYPE_MD5:
02399       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
02400       break;
02401     case REPOKEY_TYPE_SHA1:
02402       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
02403       break;
02404     case REPOKEY_TYPE_SHA256:
02405       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
02406       break;
02407     case REPOKEY_TYPE_ID:
02408     case REPOKEY_TYPE_NUM:
02409     case REPOKEY_TYPE_DIR:
02410       data_addid(xd, val);
02411       break;
02412     case REPOKEY_TYPE_BINARY:
02413       {
02414         Id len;
02415         unsigned char *dp = data_read_id(data->attrdata + val, &len);
02416         dp += len;
02417         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
02418       }
02419       break;
02420     case REPOKEY_TYPE_IDARRAY:
02421       for (ida = data->attriddata + val; *ida; ida++)
02422         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
02423       break;
02424     case REPOKEY_TYPE_DIRNUMNUMARRAY:
02425       for (ida = data->attriddata + val; *ida; ida += 3)
02426         {
02427           data_addid(xd, ida[0]);
02428           data_addid(xd, ida[1]);
02429           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
02430         }
02431       break;
02432     case REPOKEY_TYPE_DIRSTRARRAY:
02433       for (ida = data->attriddata + val; *ida; ida += 2)
02434         {
02435           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
02436           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
02437         }
02438       break;
02439     case REPOKEY_TYPE_FIXARRAY:
02440       {
02441         int num = 0;
02442         schemaid = 0;
02443         for (ida = data->attriddata + val; *ida; ida++)
02444           {
02445 #if 0
02446             fprintf(stderr, "serialize struct %d\n", *ida);
02447 #endif
02448             sp = schema;
02449             Id *kp = data->xattrs[-*ida];
02450             if (!kp)
02451               continue;
02452             num++;
02453             for (;*kp; kp += 2)
02454               {
02455 #if 0
02456                 fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
02457 #endif
02458                 *sp++ = *kp;
02459               }
02460             *sp = 0;
02461             if (!schemaid)
02462               schemaid = repodata_schema2id(data, schema, 1);
02463             else if (schemaid != repodata_schema2id(data, schema, 0))
02464               {
02465                 pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
02466                 exit(1);
02467               }
02468 #if 0
02469             fprintf(stderr, "  schema %d\n", schemaid);
02470 #endif
02471           }
02472         if (!num)
02473           break;
02474         data_addid(xd, num);
02475         data_addid(xd, schemaid);
02476         for (ida = data->attriddata + val; *ida; ida++)
02477           {
02478             Id *kp = data->xattrs[-*ida];
02479             if (!kp)
02480               continue;
02481             for (;*kp; kp += 2)
02482               {
02483                 repodata_serialize_key(data, newincore, newvincore,
02484                                        schema, data->keys + *kp, kp[1]);
02485               }
02486           }
02487         break;
02488       }
02489     case REPOKEY_TYPE_FLEXARRAY:
02490       {
02491         int num = 0;
02492         for (ida = data->attriddata + val; *ida; ida++)
02493           num++;
02494         data_addid(xd, num);
02495         for (ida = data->attriddata + val; *ida; ida++)
02496           {
02497             Id *kp = data->xattrs[-*ida];
02498             if (!kp)
02499               {
02500                 data_addid(xd, 0);      /* XXX */
02501                 continue;
02502               }
02503             sp = schema;
02504             for (;*kp; kp += 2)
02505               *sp++ = *kp;
02506             *sp = 0;
02507             schemaid = repodata_schema2id(data, schema, 1);
02508             data_addid(xd, schemaid);
02509             kp = data->xattrs[-*ida];
02510             for (;*kp; kp += 2)
02511               {
02512                 repodata_serialize_key(data, newincore, newvincore,
02513                                        schema, data->keys + *kp, kp[1]);
02514               }
02515           }
02516         break;
02517       }
02518     default:
02519       pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
02520       exit(1);
02521     }
02522   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02523     {
02524       /* put offset/len in incore */
02525       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
02526       oldvincorelen = xd->len - oldvincorelen;
02527       data_addid(newincore, oldvincorelen);
02528     }
02529 }
02530 
02531 void
02532 repodata_internalize(Repodata *data)
02533 {
02534   Repokey *key, solvkey;
02535   Id entry, nentry;
02536   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
02537   unsigned char *dp, *ndp;
02538   int newschema, oldcount;
02539   struct extdata newincore;
02540   struct extdata newvincore;
02541   Id solvkeyid;
02542 
02543   if (!data->attrs && !data->xattrs)
02544     return;
02545 
02546   newvincore.buf = data->vincore;
02547   newvincore.len = data->vincorelen;
02548 
02549   /* find the solvables key, create if needed */
02550   memset(&solvkey, 0, sizeof(solvkey));
02551   solvkey.name = REPOSITORY_SOLVABLES;
02552   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
02553   solvkey.size = 0;
02554   solvkey.storage = KEY_STORAGE_INCORE;
02555   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
02556 
02557   schema = sat_malloc2(data->nkeys, sizeof(Id));
02558   seen = sat_malloc2(data->nkeys, sizeof(Id));
02559 
02560   /* Merge the data already existing (in data->schemata, ->incoredata and
02561      friends) with the new attributes in data->attrs[].  */
02562   nentry = data->end - data->start;
02563   memset(&newincore, 0, sizeof(newincore));
02564   data_addid(&newincore, 0);    /* start data at offset 1 */
02565 
02566   data->mainschema = 0;
02567   data->mainschemaoffsets = sat_free(data->mainschemaoffsets);
02568 
02569   /* join entry data */
02570   /* we start with the meta data, entry -1 */
02571   for (entry = -1; entry < nentry; entry++)
02572     {
02573       memset(seen, 0, data->nkeys * sizeof(Id));
02574       oldschema = 0;
02575       dp = data->incoredata;
02576       if (dp)
02577         {
02578           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
02579           dp = data_read_id(dp, &oldschema);
02580         }
02581 #if 0
02582 fprintf(stderr, "oldschema %d\n", oldschema);
02583 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
02584 fprintf(stderr, "schemadata %p\n", data->schemadata);
02585 #endif
02586       /* seen: -1: old data  0: skipped  >0: id + 1 */
02587       newschema = 0;
02588       oldcount = 0;
02589       sp = schema;
02590       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
02591         {
02592           if (seen[*keyp])
02593             {
02594               pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
02595               exit(1);
02596             }
02597           seen[*keyp] = -1;
02598           *sp++ = *keyp;
02599           oldcount++;
02600         }
02601       if (entry >= 0)
02602         keyp = data->attrs ? data->attrs[entry] : 0;
02603       else
02604         {
02605           /* strip solvables key */
02606           *sp = 0;
02607           for (sp = keyp = schema; *sp; sp++)
02608             if (*sp != solvkeyid)
02609               *keyp++ = *sp;
02610             else
02611               oldcount--;
02612           sp = keyp;
02613           seen[solvkeyid] = 0;
02614           keyp = data->xattrs ? data->xattrs[1] : 0;
02615         }
02616       if (keyp)
02617         for (; *keyp; keyp += 2)
02618           {
02619             if (!seen[*keyp])
02620               {
02621                 newschema = 1;
02622                 *sp++ = *keyp;
02623               }
02624             seen[*keyp] = keyp[1] + 1;
02625           }
02626       if (entry < 0 && data->end != data->start)
02627         {
02628           *sp++ = solvkeyid;
02629           newschema = 1;
02630         }
02631       *sp = 0;
02632       if (newschema)
02633         /* Ideally we'd like to sort the new schema here, to ensure
02634            schema equality independend of the ordering.  We can't do that
02635            yet.  For once see below (old ids need to come before new ids).
02636            An additional difficulty is that we also need to move
02637            the values with the keys.  */
02638         schemaid = repodata_schema2id(data, schema, 1);
02639       else
02640         schemaid = oldschema;
02641 
02642 
02643       /* Now create data blob.  We walk through the (possibly new) schema
02644          and either copy over old data, or insert the new.  */
02645       /* XXX Here we rely on the fact that the (new) schema has the form
02646          o1 o2 o3 o4 ... | n1 n2 n3 ...
02647          (oX being the old keyids (possibly overwritten), and nX being
02648           the new keyids).  This rules out sorting the keyids in order
02649          to ensure a small schema count.  */
02650       if (entry >= 0)
02651         data->incoreoffset[entry] = newincore.len;
02652       data_addid(&newincore, schemaid);
02653       if (entry == -1)
02654         {
02655           data->mainschema = schemaid;
02656           data->mainschemaoffsets = sat_calloc(sp - schema, sizeof(Id));
02657         }
02658       keypstart = data->schemadata + data->schemata[schemaid];
02659       for (keyp = keypstart; *keyp; keyp++)
02660         {
02661           if (entry == -1)
02662             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
02663           if (*keyp == solvkeyid)
02664             {
02665               /* add flexarray entry count */
02666               data_addid(&newincore, data->end - data->start);
02667               break;
02668             }
02669           key = data->keys + *keyp;
02670 #if 0
02671           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
02672 #endif
02673           ndp = dp;
02674           if (oldcount)
02675             {
02676               /* Skip the data associated with this old key.  */
02677               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
02678                 {
02679                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
02680                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
02681                 }
02682               else if (key->storage == KEY_STORAGE_INCORE)
02683                 ndp = data_skip_key(data, dp, key);
02684               oldcount--;
02685             }
02686           if (seen[*keyp] == -1)
02687             {
02688               /* If this key was an old one _and_ was not overwritten with
02689                  a different value copy over the old value (we skipped it
02690                  above).  */
02691               if (dp != ndp)
02692                 data_addblob(&newincore, dp, ndp - dp);
02693               seen[*keyp] = 0;
02694             }
02695           else if (seen[*keyp])
02696             {
02697               /* Otherwise we have a new value.  Parse it into the internal
02698                  form.  */
02699               repodata_serialize_key(data, &newincore, &newvincore,
02700                                      schema, key, seen[*keyp] - 1);
02701             }
02702           dp = ndp;
02703         }
02704       if (entry >= 0 && data->attrs && data->attrs[entry])
02705         data->attrs[entry] = sat_free(data->attrs[entry]);
02706     }
02707   /* free all xattrs */
02708   for (entry = 0; entry < data->nxattrs; entry++)
02709     if (data->xattrs[entry])
02710       sat_free(data->xattrs[entry]);
02711   data->xattrs = sat_free(data->xattrs);
02712   data->nxattrs = 0;
02713 
02714   data->lasthandle = 0;
02715   data->lastkey = 0;
02716   data->lastdatalen = 0;
02717   sat_free(schema);
02718   sat_free(seen);
02719   repodata_free_schemahash(data);
02720 
02721   sat_free(data->incoredata);
02722   data->incoredata = newincore.buf;
02723   data->incoredatalen = newincore.len;
02724   data->incoredatafree = 0;
02725 
02726   sat_free(data->vincore);
02727   data->vincore = newvincore.buf;
02728   data->vincorelen = newvincore.len;
02729 
02730   data->attrs = sat_free(data->attrs);
02731   data->attrdata = sat_free(data->attrdata);
02732   data->attriddata = sat_free(data->attriddata);
02733   data->attrdatalen = 0;
02734   data->attriddatalen = 0;
02735 }
02736 
02737 void
02738 repodata_disable_paging(Repodata *data)
02739 {
02740   if (maybe_load_repodata(data, 0))
02741     repopagestore_disable_paging(&data->store);
02742 }
02743 
02744 static void
02745 repodata_load_stub(Repodata *data)
02746 {
02747   Repo *repo = data->repo;
02748   Pool *pool = repo->pool;
02749   int r;
02750 
02751   if (!pool->loadcallback)
02752     {
02753       data->state = REPODATA_ERROR;
02754       return;
02755     }
02756   data->state = REPODATA_LOADING;
02757   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
02758   if (!r)
02759     data->state = REPODATA_ERROR;
02760 }
02761 
02762 void
02763 repodata_create_stubs(Repodata *data)
02764 {
02765   Repo *repo = data->repo;
02766   Pool *pool = repo->pool;
02767   Repodata *sdata;
02768   int *stubdataids;
02769   Dataiterator di;
02770   Id xkeyname = 0;
02771   int i, cnt = 0;
02772   int repodataid;
02773   int datastart, dataend;
02774 
02775   repodataid = data - repo->repodata;
02776   datastart = data->start;
02777   dataend = data->end;
02778   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
02779   while (dataiterator_step(&di))
02780     {
02781       if (di.data - repo->repodata != repodataid)
02782         continue;
02783       cnt++;
02784     }
02785   dataiterator_free(&di);
02786   if (!cnt)
02787     return;
02788   stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
02789   for (i = 0; i < cnt; i++)
02790     {
02791       sdata = repo_add_repodata(repo, 0);
02792       if (dataend > datastart)
02793         repodata_extend_block(sdata, datastart, dataend - datastart);
02794       stubdataids[i] = sdata - repo->repodata;
02795       sdata->state = REPODATA_STUB;
02796       sdata->loadcallback = repodata_load_stub;
02797     }
02798   i = 0;
02799   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
02800   sdata = 0;
02801   while (dataiterator_step(&di))
02802     {
02803       if (di.data - repo->repodata != repodataid)
02804         continue;
02805       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
02806         {
02807           dataiterator_entersub(&di);
02808           sdata = repo->repodata + stubdataids[i++];
02809           xkeyname = 0;
02810           continue;
02811         }
02812       switch (di.key->type)
02813         {
02814         case REPOKEY_TYPE_ID:
02815           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
02816           break;
02817         case REPOKEY_TYPE_CONSTANTID:
02818           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
02819           break;
02820         case REPOKEY_TYPE_STR:
02821           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
02822           break;
02823         case REPOKEY_TYPE_VOID:
02824           repodata_set_void(sdata, SOLVID_META, di.key->name);
02825           break;
02826         case REPOKEY_TYPE_NUM:
02827           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
02828           break;
02829         case REPOKEY_TYPE_MD5:
02830         case REPOKEY_TYPE_SHA1:
02831         case REPOKEY_TYPE_SHA256:
02832           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
02833           break;
02834         case REPOKEY_TYPE_IDARRAY:
02835           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
02836           if (di.key->name == REPOSITORY_KEYS)
02837             {
02838               Repokey xkey;
02839 
02840               if (!xkeyname)
02841                 {
02842                   if (!di.kv.eof)
02843                     xkeyname = di.kv.id;
02844                   continue;
02845                 }
02846               xkey.name = xkeyname;
02847               xkey.type = di.kv.id;
02848               xkey.storage = KEY_STORAGE_INCORE;
02849               xkey.size = 0; 
02850               repodata_key2id(sdata, &xkey, 1);
02851               xkeyname = 0;
02852             }
02853         }
02854     }
02855   dataiterator_free(&di);
02856   for (i = 0; i < cnt; i++)
02857     repodata_internalize(repo->repodata + stubdataids[i]);
02858   sat_free(stubdataids);
02859 }
02860 
02861 /*
02862 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
02863 */

doxygen