static KeySet * prepareGlobalKS (KeySet * ks, Key * parentKey) { ksRewind (ks); Key * cutKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END); keyAddName (cutKey, strchr (keyName (parentKey), '/')); KeySet * cutKS = ksCut (ks, cutKey); Key * specCutKey = keyNew ("spec", KEY_END); KeySet * specCut = ksCut (cutKS, specCutKey); ksRewind (specCut); Key * cur; while ((cur = ksNext (specCut)) != NULL) { if (keyGetNamespace (cur) == KEY_NS_CASCADING) { ksAppendKey (cutKS, cur); keyDel (ksLookup (specCut, cur, KDB_O_POP)); } } ksAppend (ks, specCut); ksDel (specCut); keyDel (specCutKey); keyDel (cutKey); ksRewind (cutKS); return cutKS; }
static void validateWildcardSubs (KeySet * ks, Key * key, Key * specKey) { const Key * requiredMeta = keyGetMeta (specKey, "required"); if (!requiredMeta) return; Key * tmpParent = keyDup (key); keySetBaseName (tmpParent, 0); Key * parent = ksLookup (ks, tmpParent, KDB_O_NONE); keyDel (tmpParent); if (parent == NULL) return; KeySet * ksCopy = ksDup (ks); KeySet * subKeys = ksCut (ksCopy, parent); Key * cur; long subCount = 0; while ((cur = ksNext (subKeys)) != NULL) { if (keyIsDirectBelow (parent, cur)) ++subCount; } long required = atol (keyString (requiredMeta)); if (required != subCount) { char buffer[MAX_CHARS_IN_LONG + 1]; snprintf (buffer, sizeof (buffer), "%ld", subCount); keySetMeta (parent, "conflict/invalid/subcount", buffer); } ksDel (subKeys); ksDel (ksCopy); }
/** * @internal * Recursively move all keys in keyset below source to dest. * * Modifies the keyset. * * Example: * ``` * moveKeysRecursive("user/plugins/#0", "user/plugins/#1", config); * ``` * * @param source Root part to replace * @param dest Destination for keys * @param keyset keyset */ static void moveKeysRecursive (const char * source, const char * dest, KeySet * keyset) { Key * sourceBaseKey = keyNew (source, KEY_END); KeySet * newKeys = ksNew (0, KS_END); // Rename keys in keyset Key * sourceKey; ksRewind (keyset); while ((sourceKey = ksNext (keyset)) != NULL) { // Rename all keys below sourceKey if (!keyIsBelowOrSame (sourceBaseKey, sourceKey)) continue; Key * destKey = renameKey (source, dest, sourceKey); ksAppendKey (newKeys, destKey); } // Remove source keys from keyset KeySet * cut = ksCut (keyset, sourceBaseKey); ksDel (cut); ksAppend (keyset, newKeys); ksDel (newKeys); keyDel (sourceBaseKey); }
static int csvWrite(KeySet *returned, Key *parentKey, char delim, short useHeader) { FILE *fp; fp = fopen(keyString(parentKey), "w"); if(!fp) { ELEKTRA_SET_ERROR_SET(parentKey); return -1; } keyDel(ksLookup(returned, parentKey, KDB_O_POP)); unsigned long colCounter = 0; unsigned long columns = 0; unsigned long lineCounter = 0; Key *cur; KeySet *toWriteKS; Key *toWrite; while((cur = ksNext(returned)) != NULL) { if(keyRel(parentKey, cur) != 1) continue; if(useHeader) { useHeader = 0; continue; } toWriteKS = ksCut(returned, cur); colCounter = 0; while(1) { toWrite = getKeyByOrderNr(toWriteKS, colCounter); if(!toWrite) break; if(colCounter) fprintf(fp, "%c", delim); ++colCounter; fprintf(fp, "%s", keyString(toWrite)); } ksDel(toWriteKS); fprintf(fp, "\n"); if(columns == 0) { columns = colCounter; } if(colCounter != columns) { ELEKTRA_SET_ERRORF(117, parentKey, "illegal number of columns in line %lu\n", lineCounter); fclose(fp); return -1; } ++lineCounter; } fclose(fp); return 1; }
/** * @internal * Remove plugin at all placements from list plugin configuration and apply it. * * @param list List plugin * @param plugin Plugin to remove * @retval 0 on error * @retval 1 on success */ static int listRemovePlugin (Plugin * list, Plugin * plugin) { ELEKTRA_NOT_NULL (list); ELEKTRA_NOT_NULL (plugin); KeySet * newConfig = ksDup (list->config); Key * configBase = keyNew ("user/plugins", KEY_END); KeySet * array = elektraArrayGet (configBase, newConfig); // Find the plugin with our handle Key * current; ksRewind (array); while ((current = ksNext (array)) != NULL) { Key * handleLookup = keyDup (current); keyAddBaseName (handleLookup, "handle"); Key * handle = ksLookup (newConfig, handleLookup, 0); keyDel (handleLookup); if (handle) { Plugin * handleValue = (*(Plugin **) keyValue (handle)); if (handleValue == plugin) { // Remove plugin configuration KeySet * cut = ksCut (newConfig, current); ksDel (cut); } } } ksDel (array); // Renumber array items KeySet * sourceArray = elektraArrayGet (configBase, newConfig); Key * renumberBase = keyNew ("user/plugins/#", KEY_END); ksRewind (sourceArray); while ((current = ksNext (sourceArray)) != NULL) { // Create new array item base name e.g. "user/plugins/#0" elektraArrayIncName (renumberBase); moveKeysRecursive (keyName (current), keyName (renumberBase), newConfig); } keyDel (configBase); keyDel (renumberBase); ksDel (sourceArray); ksDel (list->config); // Apply new configuration list->config = newConfig; list->kdbOpen (list, NULL); return 1; }
/** * @brief Bootstrap, first phase with fallback * @internal * * @param handle already allocated, but without defaultBackend * @param [out] keys for bootstrapping * @param errorKey key to add errors too * * @retval -1 failure: cannot initialize defaultBackend * @retval 0 warning: could not get initial config * @retval 1 success * @retval 2 success in fallback mode */ int elektraOpenBootstrap (KDB * handle, KeySet * keys, Key * errorKey) { handle->defaultBackend = backendOpenDefault (handle->modules, KDB_DB_INIT, errorKey); if (!handle->defaultBackend) return -1; handle->split = splitNew (); splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2); keySetName (errorKey, KDB_SYSTEM_ELEKTRA); keySetString (errorKey, "kdbOpen(): get"); int funret = 1; int ret = kdbGet (handle, keys, errorKey); int fallbackret = 0; if (ret == 0 || ret == -1) { // could not get KDB_DB_INIT, try KDB_DB_FILE // first cleanup: ksClear (keys); backendClose (handle->defaultBackend, errorKey); splitDel (handle->split); // then create new setup: handle->defaultBackend = backendOpenDefault (handle->modules, KDB_DB_FILE, errorKey); if (!handle->defaultBackend) { elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet() return -1; } handle->split = splitNew (); splitAppend (handle->split, handle->defaultBackend, keyNew (KDB_SYSTEM_ELEKTRA, KEY_END), 2); keySetName (errorKey, KDB_SYSTEM_ELEKTRA); keySetString (errorKey, "kdbOpen(): get fallback"); fallbackret = kdbGet (handle, keys, errorKey); keySetName (errorKey, "system/elektra/mountpoints"); KeySet * cutKeys = ksCut (keys, errorKey); if (fallbackret == 1 && ksGetSize (cutKeys) != 0) { funret = 2; } ksAppend (keys, cutKeys); ksDel (cutKeys); } if (ret == -1 && fallbackret == -1) { funret = 0; } elektraRemoveMetaData (errorKey, "error"); // fix errors from kdbGet() return funret; }
static void validateArray (KeySet * ks, Key * arrayKey, Key * specKey) { Key * tmpArrayParent = keyDup (arrayKey); keySetBaseName (tmpArrayParent, 0); Key * arrayParent = ksLookup (ks, tmpArrayParent, KDB_O_NONE); keyDel (tmpArrayParent); if (arrayParent == NULL) return; KeySet * ksCopy = ksDup (ks); KeySet * subKeys = ksCut (ksCopy, arrayParent); Key * cur; long validCount = 0; while ((cur = ksNext (subKeys)) != NULL) { if (!keyIsDirectBelow (arrayParent, cur)) continue; if (keyBaseName (cur)[0] == '#') { if (elektraArrayValidateName (cur) == 1) { ++validCount; keySetMeta (cur, "spec/internal/valid", ""); } else { KeySet * invalidCutKS = ksCut (subKeys, cur); Key * toMark; while ((toMark = ksNext (invalidCutKS)) != NULL) { if (strcmp (keyName (cur), keyName (toMark))) keySetMeta (toMark, "conflict/invalid", ""); elektraMetaArrayAdd (arrayParent, "conflict/invalid/hasmember", keyName (toMark)); } ksDel (invalidCutKS); } } } ksDel (subKeys); ksDel (ksCopy); validateArrayRange (arrayParent, validCount, specKey); }
int main () { // clang-format off //! [cut] Key * parentKey = keyNew ("system/mountpoint/interest", KEY_END); KDB * kdb = kdbOpen (parentKey); KeySet * ks = ksNew (0, KS_END); kdbGet (kdb, ks, parentKey); KeySet * returned = ksCut (ks, parentKey); kdbSet (kdb, ks, parentKey); // all keys below cutpoint are now removed kdbClose (kdb, parentKey); //! [cut] outputKeySet (returned); outputKeySet (ks); }
static void test_ksCut (const size_t storagePlugin, const char * tmpFile) { Key * parentKey = keyNew (TEST_ROOT_KEY, KEY_VALUE, tmpFile, KEY_END); open_storage_plugin (storagePlugin); Plugin * plugin = plugins[storagePlugin]; // create keyset with some folder 'other' that we will then cut KeySet * ks = simpleTestKeySet (); KeySet * other = ksNew (10, keyNew ("user/tests/storage/other", KEY_VALUE, "other key", KEY_END), keyNew ("user/tests/storage/other/a", KEY_VALUE, "other a value", KEY_END), keyNew ("user/tests/storage/other/b", KEY_VALUE, "other b value", KEY_END), KS_END); if (ksAppend (ks, other) == -1) { yield_error ("ksAppend failed"); } succeed_if (plugin->kdbSet (plugin, ks, parentKey) == 1, "kdbSet was not successful"); succeed_if (plugin->kdbGet (plugin, ks, parentKey) == 1, "kdbGet was not successful"); // now cut the 'other' folder Key * cutKey = keyNew ("user/tests/storage/other", KEY_END); KeySet * returned = ksCut (ks, cutKey); succeed_if (returned, "keyset is empty (does not contain the cut keyset)"); KeySet * simple = simpleTestKeySet (); compare_keyset (simple, ks); compare_keyset (other, returned); ksDel (other); ksDel (returned); ksDel (simple); keyDel (cutKey); keyDel (parentKey); ksDel (ks); closeStoragePlugin (storagePlugin); }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; Key * root; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { backend->mountpoint = keyNew("", KEY_VALUE, keyBaseName(root), KEY_END); elektraKeySetName(backend->mountpoint, keyString(cur), KEY_CASCADING_NAME | KEY_EMPTY_NAME); if (!backend->mountpoint) { if (!failure) ELEKTRA_ADD_WARNINGF(14, errorKey, "Could not create mountpoint with name %s and value %s", keyString(cur), keyBaseName(root)); failure = 1; } keyIncRef(backend->mountpoint); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
int elektraCsvstorageGet(Plugin *handle, KeySet *returned, Key *parentKey) { if (!strcmp(keyName(parentKey), "system/elektra/modules/csvstorage")) { KeySet *contract = ksNew (30, keyNew ("system/elektra/modules/csvstorage", KEY_VALUE, "csvstorage plugin waits for your orders", KEY_END), keyNew ("system/elektra/modules/csvstorage/exports", KEY_END), keyNew ("system/elektra/modules/csvstorage/exports/get", KEY_FUNC, elektraCsvstorageGet, KEY_END), keyNew ("system/elektra/modules/csvstorage/exports/set", KEY_FUNC, elektraCsvstorageSet, KEY_END), #include ELEKTRA_README(csvstorage) keyNew ("system/elektra/modules/csvstorage/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, contract); ksDel (contract); return 1; /* success */ } KeySet *config = elektraPluginGetConfig(handle); Key *delimKey = ksLookupByName(config, "/delimiter", 0); char delim = ';'; if(delimKey) { const char *delimString = keyString(delimKey); delim = delimString[0]; } Key *readHeaderKey = ksLookupByName(config, "/header", 0); short useHeader = 0; if(readHeaderKey) { const char *printHeaderString = keyString(readHeaderKey); if(!strcmp(printHeaderString, "colname")) { useHeader = 1; } else if(!(strcmp(printHeaderString, "skip"))) { useHeader = -1; } else if(!(strcmp(printHeaderString, "record"))) { useHeader = 0; } else { useHeader = 0; } } unsigned long fixColumnCount = 0; Key *fixColumnCountKey = ksLookupByName(config, "/columns", 0); if(fixColumnCountKey) { if(keyString(fixColumnCountKey)) { fixColumnCount = atol(keyString(fixColumnCountKey)); } } Key *setNamesKey = ksLookupByName(config, "/columns/names", 0); char *colNames = NULL; if(setNamesKey) { if(fixColumnCountKey) { KeySet *namesKS = ksCut(config, setNamesKey); unsigned long nrNames = (unsigned long)ksGetSize(namesKS)-1; if(nrNames == fixColumnCount) { colNames = (char *)elektraMalloc(nrNames*sizeof(char *)); Key *cur; char **ptr = (char **)colNames; while((cur = ksNext(namesKS)) != NULL) { if(!strcmp(keyName(cur), keyName(setNamesKey))) continue; if(!strcmp(keyString(cur), "")) *ptr = NULL; else *ptr = (char *)keyString(cur); ++ptr; } } ksAppend(config, namesKS); ksDel(namesKS); } } int nr_keys; nr_keys = csvRead(returned, parentKey, delim, useHeader, fixColumnCount, (const char **)colNames); if(colNames) elektraFree(colNames); if (nr_keys == -1) return -1; return 1; }
/**Builds a backend out of the configuration supplied * from: * @verbatim system/elektra/mountpoints/<name> @endverbatim * * The root key must be like the above example. You do * not need to rewind the keyset. But every key must be * below the root key. * * The internal consistency will be checked in this * function. If necessary parts are missing, like * no plugins, they cant be loaded or similar 0 * will be returned. * * ksCut() is perfectly suitable for cutting out the * configuration like needed. * * @note The given KeySet will be deleted within the function, * don't use it afterwards. * * @param elektraConfig the configuration to work with. * It is used to build up this backend. * @param modules used to load new modules or get references * to existing one * @param errorKey the key where an error and warnings are added * * @return a pointer to a freshly allocated backend * this could be the requested backend or a so called * "missing backend". * @retval 0 if out of memory * @ingroup backend */ Backend* elektraBackendOpen(KeySet *elektraConfig, KeySet *modules, Key *errorKey) { Key * cur; KeySet *referencePlugins = 0; KeySet *systemConfig = 0; int failure = 0; referencePlugins = ksNew(0, KS_END); ksRewind(elektraConfig); Key * root = ksNext (elektraConfig); Backend *backend = elektraBackendAllocate(); if (elektraBackendSetMountpoint(backend, elektraConfig, errorKey) == -1) { // warning already set failure = 1; } while ((cur = ksNext(elektraConfig)) != 0) { if (keyRel (root, cur) == 1) { // direct below root key KeySet *cut = ksCut (elektraConfig, cur); if (!strcmp(keyBaseName(cur), "config")) { systemConfig = elektraRenameKeys(cut, "system"); ksDel (cut); } else if (!strcmp(keyBaseName(cur), "errorplugins")) { if (elektraProcessPlugins(backend->errorplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for error failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "getplugins")) { if (elektraProcessPlugins(backend->getplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(13, errorKey, "elektraProcessPlugins for get failed"); failure = 1; } } else if (!strcmp(keyBaseName(cur), "mountpoint")) { ksDel (cut); // already handled by elektraBackendSetMountpoint continue; } else if (!strcmp(keyBaseName(cur), "setplugins")) { if (elektraProcessPlugins(backend->setplugins, modules, referencePlugins, cut, systemConfig, errorKey) == -1) { if (!failure) ELEKTRA_ADD_WARNING(15, errorKey, "elektraProcessPlugins for set failed"); failure = 1; } } else { // no one cares about that config if (!failure) ELEKTRA_ADD_WARNING(16, errorKey, keyBaseName(cur)); ksDel (cut); } } } if (failure) { Backend *tmpBackend = elektraBackendOpenMissing(backend->mountpoint); elektraBackendClose(backend, errorKey); backend = tmpBackend; } ksDel (systemConfig); ksDel (elektraConfig); ksDel (referencePlugins); return backend; }
/** * Load a plugin. * * The array of plugins must be set to 0. * Its length is NR_OF_PLUGINS. * * systemConfig will only be used, not deleted. * * @param config the config with the information how the * plugins should be put together * @param systemConfig the shared (system) config for the plugins. * Every plugin additional get this config. * * @retval -1 on failure */ int elektraProcessPlugins (Plugin ** plugins, KeySet * modules, KeySet * referencePlugins, KeySet * config, KeySet * systemConfig, Key * errorKey) { Key * root; Key * cur; ksRewind (config); root = ksNext (config); while ((cur = ksNext (config)) != 0) { if (keyRel (root, cur) == 1) { char * pluginName = 0; char * referenceName = 0; int pluginNumber = 0; Key * key; if (elektraProcessPlugin (cur, &pluginNumber, &pluginName, &referenceName, errorKey) == -1) { elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } if (pluginName) { key = keyDup (cur); keyAddBaseName (key, "config"); KeySet * cutConfig = ksCut (config, key); keyDel (key); KeySet * pluginConfig = elektraRenameKeys (cutConfig, "user"); ksDel (cutConfig); if (!pluginConfig) return -1; ksAppend (pluginConfig, systemConfig); ksRewind (pluginConfig); /* TODO: bug ksAppend invalidates cursor */ /* case 1, we create a new plugin, note that errorKey is not passed here, because it would set error information but we only want a warning instead. */ plugins[pluginNumber] = elektraPluginOpen (pluginName, modules, pluginConfig, errorKey); if (!plugins[pluginNumber]) { ELEKTRA_ADD_WARNING (64, errorKey, pluginName); /* Loading plugin did not work */ elektraFree (pluginName); elektraFree (referenceName); ksDel (config); return -1; } /* case 2, we label it for later use */ if (referenceName) ksAppendKey (referencePlugins, keyNew (referenceName, KEY_BINARY, KEY_SIZE, sizeof (plugins[pluginNumber]), KEY_VALUE, &plugins[pluginNumber], KEY_END)); } else { /* case 3, we use an existing plugin */ Key * lookup = ksLookup (referencePlugins, keyNew (referenceName, KEY_END), KDB_O_DEL); if (!lookup) { ELEKTRA_ADD_WARNING (65, errorKey, referenceName); /* Getting a reference plugin at a previous stage did not work. Note that this check is necessary, because loading the plugin could fail for example at errorplugins and at a later point, for example at setplugins it is tried to refer to that.*/ elektraFree (referenceName); ksDel (config); return -1; } plugins[pluginNumber] = *(Plugin **)keyValue (lookup); ++plugins[pluginNumber]->refcounter; } elektraFree (pluginName); elektraFree (referenceName); } else { ELEKTRA_ADD_WARNING (21, errorKey, keyString (cur)); } } ksDel (config); return 0; }
static int listParseConfiguration (Placements * placements, KeySet * config) { Key * cur; Key * key = ksLookupByName (config, "/plugins", 0); KeySet * cutKS = ksCut (config, key); ksRewind (cutKS); if (ksGetSize (cutKS) < 2) return 0; int rc = 0; while ((cur = ksNext (cutKS)) != NULL) { if (keyRel (key, cur) != 1) { continue; } if (keyBaseName (cur)[0] == '#') { if (strcmp (lastIndex, keyBaseName (cur)) < 0) { snprintf (lastIndex, ELEKTRA_MAX_ARRAY_SIZE, "%s", keyBaseName (cur)); } } Key * sub; Key * lookup = keyDup (cur); keyAddBaseName (lookup, "placements"); keyAddBaseName (lookup, "set"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * setString = keyString (sub); const char * setStrings[] = { "presetstorage", "presetcleanup", "precommit", "postcommit" }; SetPlacements setPlacement = preSetStorage; while (setPlacement != setEnd) { if (strstr (setString, setStrings[setPlacement])) { rc = 1; ksAppendKey (placements->setKS[setPlacement], keyDup (cur)); } ++setPlacement; } } keySetBaseName (lookup, "get"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * getString = keyString (sub); const char * getStrings[] = { "pregetstorage", "postgetstorage", "postgetcleanup" }; GetPlacements getPlacement = preGetStorage; while (getPlacement != getEnd) { if (strstr (getString, getStrings[getPlacement])) { rc = 1; ksAppendKey (placements->getKS[getPlacement], keyDup (cur)); } ++getPlacement; } } keySetBaseName (lookup, "error"); sub = ksLookup (cutKS, lookup, 0); if (sub) { const char * errString = keyString (sub); const char * errStrings[] = { "prerollback", "postrollback" }; ErrPlacements errPlacement = preRollback; while (errPlacement != errEnd) { if (strstr (errString, errStrings[errPlacement])) { rc = 1; ksAppendKey (placements->errKS[errPlacement], keyDup (cur)); } ++errPlacement; } } keyDel (lookup); } ksDel (cutKS); return rc; }