diff --git a/src/bindings/cpp/include/key.hpp b/src/bindings/cpp/include/key.hpp index f4a36e28b9c..99ae7c07f43 100644 --- a/src/bindings/cpp/include/key.hpp +++ b/src/bindings/cpp/include/key.hpp @@ -106,6 +106,7 @@ class Key inline std::string getDirName() const; inline void setName (const std::string &newName); + inline void addName (const std::string &addedName); inline void setBaseName (const std::string &baseName); inline void addBaseName (const std::string &baseName); @@ -548,6 +549,14 @@ inline void Key::setName (const std::string &newName) } } +inline void Key::addName (const std::string &addedName) +{ + if (ckdb::keyAddName (getKey(), addedName.c_str()) == -1) + { + throw KeyInvalidName(); + } +} + /**Sets a base name for a key. * * @copydoc keySetBaseName diff --git a/src/include/kdb.h.in b/src/include/kdb.h.in index 4cf36f0ed79..83de25e8c90 100644 --- a/src/include/kdb.h.in +++ b/src/include/kdb.h.in @@ -149,7 +149,9 @@ int keyIsString(const Key *key); const char *keyName(const Key *key); ssize_t keyGetNameSize(const Key *key); ssize_t keyGetName(const Key *key, char *returnedName, size_t maxSize); + ssize_t keySetName(Key *key, const char *newname); +ssize_t keyAddName(Key *key, const char *addName); ssize_t keyGetFullNameSize(const Key *key); ssize_t keyGetFullName(const Key *key, char *returnedName, size_t maxSize); @@ -157,8 +159,9 @@ ssize_t keyGetFullName(const Key *key, char *returnedName, size_t maxSize); const char *keyBaseName(const Key *key); ssize_t keyGetBaseNameSize(const Key *key); ssize_t keyGetBaseName(const Key *key, char *returned, size_t maxSize); -ssize_t keyAddBaseName(Key *key,const char *baseName); + ssize_t keySetBaseName(Key *key,const char *baseName); +ssize_t keyAddBaseName(Key *key,const char *baseName); /* Value Manipulation Methods */ const void *keyValue(const Key *key); diff --git a/src/libelektra/keyname.c b/src/libelektra/keyname.c index 72fdd6366bc..a9103982539 100644 --- a/src/libelektra/keyname.c +++ b/src/libelektra/keyname.c @@ -297,6 +297,28 @@ ssize_t keyGetName(const Key *key, char *returnedName, size_t maxSize) return key->keySize; } +/** + * @internal + * + * @brief Call this function after every key changing operation + * + * @pre key->key and key->keySize are set accordingly and the size of + * allocation is twice as what you actually needed. + * + * @post we get a unsynced key with a correctly terminated + * key name suitable for ordering and the name getter methods + * + * It will duplicate the key length and put a second name afterwards + * that is used for sorting keys. + * + * @param key + */ +static void elektraFinalizeName(Key *key) +{ + key->key[key->keySize - 1] = 0; /* finalize string */ + + key->flags |= KEY_FLAG_SYNC; +} @@ -340,7 +362,6 @@ ssize_t keySetName(Key *key, const char *newName) size_t length; size_t rootLength, userLength, systemLength, ownerLength; char *p=0; - size_t size=0; if (!key) return -1; if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; @@ -407,7 +428,7 @@ ssize_t keySetName(Key *key, const char *newName) key->keySize+=userLength; } - rootLength = userLength; + rootLength = userLength+1; } else if (keyNameIsSystem(newName)) { /* handle "system*" */ if (length > systemLength && *(newName+systemLength)!=KDB_PATH_SEPARATOR) @@ -421,7 +442,7 @@ ssize_t keySetName(Key *key, const char *newName) keySetOwner (key, NULL); - rootLength = systemLength; + rootLength = systemLength+1; } else { /* Given newName is neither "system" or "user" */ /*errno=KDB_ERR_INVALIDKEY;*/ @@ -439,7 +460,7 @@ ssize_t keySetName(Key *key, const char *newName) - key->keySize has number of bytes that will be allocated for key name with already removed owner. - key->owner is already set - - rootLength is sizeof("user")-1 or sizeof("system")-1 + - rootLength is sizeof("user") or sizeof("system") */ /* Allocate memory for key->key */ @@ -453,49 +474,17 @@ ssize_t keySetName(Key *key, const char *newName) /* copy the root of newName to final destination */ strncpy(key->key,newName,rootLength); - - /* skip the root */ - p=(char *)newName; - size=0; - p=keyNameGetOneLevel(p+size,&size); - - /* iterate over each single folder name removing repeated '/' and escaping when needed */ - key->keySize=rootLength; - while (*(p=keyNameGetOneLevel(p+size,&size))) { - /* printf ("level: %s, size: %d\n", p, size); */ - if (size == 1 && strncmp (p, ".",1) == 0) - { - /* printf ("ignore .\n"); */ - continue; /* just ignore current directory */ - } - else if (size == 2 && strncmp (p, "..",2) == 0) /* give away directory */ - { - key->key[key->keySize] = 0; /* initialize first (valgrind) */ - while (key->keySize > rootLength && key->key[key->keySize] != KDB_PATH_SEPARATOR) key->keySize--; - /* printf ("do .. (key->keySize: %d), key->key: %s, rootLength: %d, key->keySize: %d\n", - key->keySize, key->key, rootLength, key->keySize); */ - continue; - } - /* Add a '/' to the end of key name */ - key->key[key->keySize]=KDB_PATH_SEPARATOR; - key->keySize++; - - /* carefully append basenames */ - memcpy(key->key+key->keySize,p,size); - key->keySize+=size; - } - /* remove unescaped trailing slashes */ - while (key->key[key->keySize-1] == KDB_PATH_SEPARATOR && key->key[key->keySize-2] != '\\') key->keySize--; - key->key[key->keySize]=0; /* finalize string */ + /* finish root name for keyAddName() */ + key->keySize=rootLength; + key->key[rootLength] = '\0'; - key->flags |= KEY_FLAG_SYNC; + /* skip namespace we already processed */ + p=keyNameGetOneLevel(newName,&length); - key->keySize ++; /*for \\0 ending*/ - return key->keySize; + return keyAddName(key, p+length); error_mem: - /*errno=KDB_ERR_NOMEM;*/ return -1; } @@ -809,35 +798,94 @@ ssize_t keyGetBaseName(const Key *key, char *returned, size_t maxSize) */ ssize_t keyAddBaseName(Key *key, const char *baseName) { - size_t size=0; - if (!key) return -1; - if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; - if (!baseName) return key->keySize; + // if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; + if (!key->key) return -1; + + size_t size=0; char *escaped = elektraMalloc (strlen (baseName) * 2 + 2); elektraKeyNameEscape (baseName, escaped); - if (key->key) - { - size = strlen (escaped); - key->keySize += size + 1; - key->key = realloc (key->key, key->keySize); + size = strlen (escaped); + key->keySize += size + 1; + key->key = realloc (key->key, key->keySize); - key->key[key->keySize - size - 2] = KDB_PATH_SEPARATOR; - memcpy (key->key + key->keySize - size - 1, escaped, size); + key->key[key->keySize - size - 2] = KDB_PATH_SEPARATOR; + memcpy (key->key + key->keySize - size - 1, escaped, size); - key->key[key->keySize - 1] = 0; /* finalize string */ - elektraFree (escaped); - return key->keySize; - } - else - { - int result = keySetName (key, escaped); - elektraFree (escaped); - return result; - } + elektraFree (escaped); + + elektraFinalizeName(key); + + return key->keySize; } +/** + * @brief Add a already escaped name to the keyname. + * + * The same way as in keySetName() this method finds the canonical pathname. + * Unlike, keySetName() it adds it to an already existing name. + * + * @param key the key where a name should be added + * @param newName the new name to append + * + * @retval -1 if key is a null pointer or did not have a valid name before + * @retval -1 if newName is a null pointer or not a valid name (contains \\ in beginning) + * @retval -1 on allocation errors + * @retval size of the new key + */ +ssize_t keyAddName(Key *key, const char *newName) +{ + if (!key) return -1; + if (!newName) return key->keySize; + // if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; + // if (!elektraValidateKeyNamePart(newName)) return -1; + if (!key->key) return -1; + + size_t const newSize = key->keySize + elektraStrLen(newName); + size_t const rootLength = key->keySize; + elektraRealloc ((void**)&key->key, newSize*2); + if (!key->key) return -1; + + size_t size=0; + const char * p = newName; + + -- key->keySize; // fix keySize for loop below + + /* iterate over each single folder name removing repeated '/', . and .. */ + while (*(p=keyNameGetOneLevel(p+size,&size))) { + // printf ("level: %s, size: %d\n", p, size); + if (size == 1 && strncmp (p, ".",1) == 0) + { + /* printf ("ignore .\n"); */ + continue; /* just ignore current directory */ + } + else if (size == 2 && strncmp (p, "..",2) == 0) /* give away directory */ + { + key->key[key->keySize] = 0; /* initialize first (valgrind) */ + while (key->keySize >= rootLength && key->key[key->keySize] != KDB_PATH_SEPARATOR) key->keySize--; + /* printf ("do .. (key->keySize: %d), key->key: %s, rootLength: %d, key->keySize: %d\n", + key->keySize, key->key, rootLength, key->keySize); */ + continue; + } + /* Add a '/' to the end of key name */ + key->key[key->keySize]=KDB_PATH_SEPARATOR; + key->keySize++; + + /* carefully append basenames */ + memcpy(key->key+key->keySize,p,size); + key->keySize+=size; + } + + /* remove unescaped trailing slashes */ + while (key->key[key->keySize-1] == KDB_PATH_SEPARATOR && key->key[key->keySize-2] != '\\') key->keySize--; + key->keySize ++; /*for \\0 ending*/ + + + elektraFinalizeName(key); + + return key->keySize; +} @@ -880,67 +928,65 @@ ssize_t keySetBaseName(Key *key, const char *baseName) const char *p=0; if (!key) return -1; - if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; + // if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; if (!elektraValidateKeyNamePart(baseName)) return -1; - if (key->key) { + if (!key->key) return -1; - /*Throw away basename of key->key*/ - p=strrchr (key->key, '/'); - if (p == 0) - { - /* we would remove even the namespace */ - if (!strcmp (baseName, "")) return -1; + /*Throw away basename of key->key*/ + p=strrchr (key->key, '/'); + if (p == 0) + { + /* we would remove even the namespace */ + if (!strcmp (baseName, "")) return -1; - if (!strcmp (baseName, ".")) return -1; + if (!strcmp (baseName, ".")) return -1; - return keySetName(key, baseName); - } + return keySetName(key, baseName); + } - /* check if the found / is the only one */ - if (strchr (key->key, '/') == p) - { - /* we would delete the whole keyname */ - if (!strcmp (baseName, "..")) return -1; - } + /* check if the found / is the only one */ + if (strchr (key->key, '/') == p) + { + /* we would delete the whole keyname */ + if (!strcmp (baseName, "..")) return -1; + } + + key->keySize -= (key->key+key->keySize-1)-p; + key->key[key->keySize-1]=0; /* finalize string */ + /* remove yet another part */ + if (!strcmp (baseName, "..")) + { + p=strrchr (key->key, '/'); key->keySize -= (key->key+key->keySize-1)-p; key->key[key->keySize-1]=0; /* finalize string */ + } - /* remove yet another part */ - if (!strcmp (baseName, "..")) - { - p=strrchr (key->key, '/'); - key->keySize -= (key->key+key->keySize-1)-p; - key->key[key->keySize-1]=0; /* finalize string */ - } + /* free the now unused space */ + key->key=realloc(key->key,key->keySize); - /* free the now unused space */ - key->key=realloc(key->key,key->keySize); + /* these cases just delete keyname parts so we are done */ + if (!strcmp (baseName, "..") || + !strcmp (baseName, ".") || + !strcmp (baseName, "")) + { + return key->keySize; + } - /* these cases just delete keyname parts so we are done */ - if (!strcmp (baseName, "..") || - !strcmp (baseName, ".") || - !strcmp (baseName, "")) - { - return key->keySize; - } + /* Now add new baseName */ + size = strlen (baseName); + key->keySize += size + 1; + key->key = realloc (key->key, key->keySize); - /* Now add new baseName */ - size = strlen (baseName); - key->keySize += size + 1; - key->key = realloc (key->key, key->keySize); + key->key[key->keySize - size - 2] = KDB_PATH_SEPARATOR; + memcpy (key->key + key->keySize - size - 1, baseName, size); - key->key[key->keySize - size - 2] = KDB_PATH_SEPARATOR; - memcpy (key->key + key->keySize - size - 1, baseName, size); - /* use keySetRawName() internally and not - * key->key*/ + elektraFinalizeName(key); - key->key[key->keySize-1]=0; /* finalize string */ - return key->keySize; - } else return keySetName(key,baseName); /*that cannot be a good idea*/ + return key->keySize; } diff --git a/src/tools/kdb/mount.cpp b/src/tools/kdb/mount.cpp index 33e96fd2ae6..bc6ba285151 100644 --- a/src/tools/kdb/mount.cpp +++ b/src/tools/kdb/mount.cpp @@ -118,7 +118,8 @@ void MountCommand::buildBackend(Cmdline const& cl) void MountCommand::addConfig (string const& configBasePath, string const& keyName, string const& value) { Key configKey = Key (configBasePath, KEY_END); - configKey.addBaseName (keyName); + std:: cout << "appending " << keyName << " to " << configKey.getName() << std::endl; + configKey.addName (keyName); configKey.setString (value); mountConf.append (configKey); } diff --git a/tests/abi/testabi_key.c b/tests/abi/testabi_key.c index f44bb2bef28..ed5da6ac0c4 100644 --- a/tests/abi/testabi_key.c +++ b/tests/abi/testabi_key.c @@ -824,7 +824,8 @@ static void test_keyName() succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: Can't go higher then user in hierarchy"); succeed_if (keySetName(key, "user///sw/../sw//././MyApp")==sizeof("user/sw/MyApp"), "could not set keySet example"); - succeed_if (strcmp (keyName(key), "user/sw/MyApp") == 0, "Example of keySet does not work"); + // printf("%s %d\n", keyName(key), keyGetNameSize(key)); + succeed_if_same_string (keyName(key), "user/sw/MyApp"); succeed_if (keyGetNameSize(key) == sizeof("user/sw/MyApp"), "incorrect length for keySet example"); printf("Test Mixed Dots and Slashes in Key Name\n"); @@ -2536,6 +2537,25 @@ static void test_keyLock() keyDel (key2); } +static void test_keyAddName() +{ + Key *k = keyNew("user", KEY_END); + keyAddName(k, "something"); + succeed_if_same_string(keyName(k), "user/something"); + keyAddName(k, "with/slash"); + succeed_if_same_string(keyName(k), "user/something/with/slash"); + keyDel(k); + + k = keyNew("system/elektra/mountpoints/_t_error/config", KEY_END); + keyAddName(k, "on_open/error"); + succeed_if_same_string(keyName(k), "system/elektra/mountpoints/_t_error/config/on_open/error"); + keyDel(k); + + k = keyNew("user", KEY_END); + succeed_if (keyAddName(k, "///sw/../sw//././MyApp")==sizeof("user/sw/MyApp"), "could not set keySet example"); + succeed_if_same_string(keyName(k), "user/sw/MyApp"); + keyDel(k); +} int main(int argc, char** argv) { @@ -2567,8 +2587,8 @@ int main(int argc, char** argv) test_keyNameSpecial(); test_keyClear(); test_keyLock(); - test_keyBaseName(); + test_keyAddName(); printf("\ntestabi_key RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError); diff --git a/tests/ctest/test_key.c b/tests/ctest/test_key.c index 1d9a26acc17..d688665d64e 100644 --- a/tests/ctest/test_key.c +++ b/tests/ctest/test_key.c @@ -189,15 +189,15 @@ static void test_keyHelpers() */ k2 = keyNew (KEY_END); - succeed_if (keyAddBaseName (k2, "no") == -1, "Could not add basename"); + succeed_if (keyAddBaseName (k2, "no") == -1, "Could add basename on empty name"); succeed_if (strcmp (keyName(k2), "") == 0, "added basename not correct"); succeed_if (keyGetNameSize(k2) == 1, "Name size not correct"); keyDel (k2); k2 = keyNew (KEY_END); - succeed_if (keyAddBaseName (k2, "user") == 5, "Could not add basename"); - succeed_if (strcmp (keyName(k2), "user") == 0, "added basename not correct"); - succeed_if (keyGetNameSize(k2) == 5, "Name size not correct"); + succeed_if (keyAddBaseName (k2, "user") == -1, "Could add basename on empty name"); + succeed_if (strcmp (keyName(k2), "") == 0, "added basename not correct"); + succeed_if (keyGetNameSize(k2) == 1, "Name size not correct"); keyDel (k2); k2 = keyNew ("user/dir1/dir2/mykey/mykey/a", KEY_END); @@ -301,8 +301,6 @@ static void test_keyPlugin() succeed_if (xlug == (Plugin *) 1222243, "should point to that too"); keyDel (k); - - } int main(int argc, char** argv)