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

Generated on Mon Dec 15 17:56:24 2014 for satsolver by  doxygen 1.5.6