/** * @brief Close all levels in cur not needed in next * * Closing is much simpler then opening because no names need to be * yield. * * @pre keys are not allowed to be below, * except: last run where everything below root/parent key is * closed * * Then all levels are reverse iterated until the level before the equal * level. * @see elektraGenCloseIterate * * In the level before the equal level there is some special handling in * regards to the next level. * @see elektraGenCloseFirst * * @example * * cur: user/sw/org/deeper * next: user/sw/org/other/deeper/below * -> nothing will be done ("deeper" is value) * [eq: 3, cur: 4, next: 6, gen: 0] * * cur: user/sw/org/other/deeper/below * next: user/no * -> "deeper", "other", "org" and "sw" maps will be closed ("below" is value) * [eq: 1, cur: 6, next: 2, gen: 4] * * cur: user/no * next: user/oops/it/is/below * -> nothing will be done ("no" is value) * [eq: 1, cur: 2, next: 5, gen: 0] * * cur: user/oops/it/is/below * next: user/x/t/s/x * -> close "is", "it", "oops" * [eq: 1, cur: 5, next: 5, gen: 3] * * last iteration (e.g. close down to root) * cur: user/x/t/s/x * next: user * -> close "s", "t" and "x" maps * [eq: 1, cur: 5, next: 1, gen: 3] * * cur: user/#0/1/1/1 * next: user/#1/1/1/1 * -> close "1", "1", "1", but not array * [eq: 1, cur: 5, next: 5, gen: 3] * * @param g * @param cur * @param next */ void elektraGenClose (yajl_gen g, const Key * cur, const Key * next) { int curLevels = elektraKeyCountLevel (cur); #ifdef ELEKTRA_YAJL_VERBOSE int nextLevels = elektraKeyCountLevel (next); #endif int equalLevels = elektraKeyCountEqualLevel (cur, next); // 1 for last level not to iterate, 1 before 1 after equal int levels = curLevels - equalLevels - 2; const char * pcur = keyName (cur); size_t csize = 0; const char * pnext = keyName (next); size_t nsize = 0; for (int i = 0; i < equalLevels + 1; ++i) { pcur = keyNameGetOneLevel (pcur + csize, &csize); pnext = keyNameGetOneLevel (pnext + nsize, &nsize); } #ifdef ELEKTRA_YAJL_VERBOSE printf ("elektraGenClose, eq: %d, cur: %s %d, next: %s %d, " "levels: %d\n", equalLevels, pcur, curLevels, pnext, nextLevels, levels); #endif if (levels > 0) { elektraGenCloseLast (g, cur); } elektraGenCloseIterate (g, cur, levels); elektraGenCloseFirst (g, pcur, csize, pnext, levels); }
/** * @brief Close the last element * * Needs less special handling because cur is fully below next. * * Will fully iterate over all elements. * * @param g handle to yield close events * @param cur current key * @param next the last key (the parentKey) */ void elektraGenCloseFinally(yajl_gen g, const Key *cur, const Key *next) { int curLevels = elektraKeyCountLevel(cur); #ifdef ELEKTRA_YAJL_VERBOSE int nextLevels = elektraKeyCountLevel(next); #endif int equalLevels = elektraKeyCountEqualLevel(cur, next); // 1 for last level not to iterate, 1 after equal int levels = curLevels - equalLevels - 1; const char *pcur = keyName(cur); size_t csize = 0; const char *pnext = keyName(next); size_t nsize = 0; for (int i=0; i < equalLevels+1; ++i) { pcur=keyNameGetOneLevel(pcur+csize,&csize); pnext=keyNameGetOneLevel(pnext+nsize, &nsize); } #ifdef ELEKTRA_YAJL_VERBOSE printf ("elektraGenFinally, eq: %d, cur: %s %d, next: %s %d, " "levels: %d\n", equalLevels, pcur, curLevels, pnext, nextLevels, levels); #endif // fixes elektraGenCloseIterate for the special handling of // arrays finally elektraGenCloseLast(g, cur); // now we iterate over the middle part elektraGenCloseIterate(g, cur, levels); // now we look at the first unequal element // this is the very last element we are about to close if (pcur && *pcur == '#') { #ifdef ELEKTRA_YAJL_VERBOSE printf ("array close FINAL\n"); #endif } else { #ifdef ELEKTRA_YAJL_VERBOSE printf ("GEN map close FINAL\n"); #endif yajl_gen_map_close(g); } }
/** * @brief Add an already escaped name to the keyname. * * The same way as in keySetName() this method finds the canonical pathname: * - it will ignore /./ * - it will remove a level when /../ is used * - it will remove multiple slashes //// * * For example: * @snippet keyName.c add name * * Unlike keySetName() it adds relative to the previous name and * cannot change the namespace of a key. * For example: * @snippet keyName.c namespace * * The passed name needs to be valid according the @link keyname key name rules @endlink. * It is not allowed to: * - be empty * - end with unequal number of \\ * * @param key the key where a name should be added * @param newName the new name to append * * @since 0.8.11 * * @retval size of the new key * @retval -1 if key is a null pointer or did not have a valid name before * @retval -1 if newName is not a valid escaped name * @retval -1 on allocation errors * @retval -1 if key was inserted to a keyset before * @retval 0 if nothing was done because newName had only slashes, is too short, is empty or is null * @ingroup keyname */ ssize_t keyAddName (Key * key, const char * newName) { if (!key) return -1; if (test_bit (key->flags, KEY_FLAG_RO_NAME)) return -1; if (!key->key) return -1; if (!strcmp (key->key, "")) return -1; if (!newName) return 0; size_t const nameSize = elektraStrLen (newName); if (nameSize < 2) return 0; if (!elektraValidateKeyName (newName, nameSize)) return -1; const size_t origSize = key->keySize; const size_t newSize = origSize + nameSize; elektraRealloc ((void **)&key->key, newSize * 2); if (!key->key) return -1; size_t size = 0; const char * p = newName; int avoidSlash = 0; if (*key->key == '/') avoidSlash = key->keySize == 2; --key->keySize; // loop assumes that key->key[key->keySize] is last character and not NULL /* iterate over each single folder name removing repeated '/', . and .. */ while (*(p = keyNameGetOneLevel (p + size, &size))) { if (size == 1 && strncmp (p, ".", 1) == 0) { continue; /* just ignore current directory */ } else if (size == 2 && strncmp (p, "..", 2) == 0) /* give away one level*/ { elektraRemoveOneLevel (key, &avoidSlash); continue; } if (!avoidSlash) { /* Add a '/' to the end of key name */ key->key[key->keySize] = KDB_PATH_SEPARATOR; key->keySize++; } else { avoidSlash = 0; } /* carefully append basenames */ char * d = key->key + key->keySize; memcpy (d, p, size); key->keySize += size; } ++key->keySize; /*for \\0 ending*/ elektraFinalizeName (key); return origSize == key->keySize ? 0 : key->keySize; }
/** * @internal * * @brief Used by keyAddName * * Will remove one level of key, even if key->key is not null terminated * also handles cascading keys and sets avoidSlash properly. * * @param key to remove one level * @param [out] avoidSlash set to 1 if / is already present (cascading) */ static void elektraRemoveOneLevel (Key * key, int * avoidSlash) { int levels = 0; char * x = key->key; size_t xsize = 0; size_t sizeOfLastLevel = 0; char * const last = &key->key[key->keySize]; const char save = *last; *last = 0; while (*(x = keyNameGetOneLevel (x + xsize, &xsize))) { sizeOfLastLevel = xsize; levels++; } if (levels > 1) { key->keySize -= sizeOfLastLevel + 1; key->key[key->keySize] = 0; } else if (*key->key == '/') // cascading key { // strip down to root key->keySize = 1; *avoidSlash = 1; } *last = save; }
/** * @internal * * @brief Unescapes a key name. * * Writes a null terminated sequence of key name parts to dest. * * May only need half the storage than the source string. * It is not safe to use the same string for source and dest. **/ size_t elektraUnescapeKeyName (const char * source, char * dest) { const char * sp = source; char * dp = dest; size_t size = 0; ELEKTRA_ASSERT (sp != NULL && dp != NULL, "Got null pointer sp: %p dp: %p", (void *) sp, (void *) dp); if (*sp == '/') { // handling for cascading names *dp = 0; ++dp; } while (*(sp = keyNameGetOneLevel (sp + size, &size))) { if (!elektraUnescapeKeyNamePartBegin (sp, size, &dp)) { dp = elektraUnescapeKeyNamePart (sp, size, dp); } *dp = 0; ++dp; } return dp - dest; }
static void test_keyNameGetOneLevel (void) { printf ("test keyNameGetOneLevel\n"); size_t size = 0; char buffer[] = "a\\/\\/def"; char * p = keyNameGetOneLevel (buffer, &size); succeed_if (p == buffer, "p not at start of buffer"); succeed_if (size == sizeof (buffer) - 1, "size not set correctly"); }
/** * Sets @c baseName as the new basename for @c key. * * Only the baseName will be affected and no other part of the key. * * All text after the last @c '/' in the @p key keyname is erased and * @p baseName is appended. * * So let us suppose @p key has name @c "system/dir1/dir2/key1". If @p baseName * is @c "key2", the resulting key name will be @c "system/dir1/dir2/key2". * If @p baseName is empty or NULL, the resulting key name will * be @c "system/dir1/dir2". * * This function does proper escaping on the supplied name argument. * * You can use all names to set as basename (e.g. . (dot), .. * (dot-dot), % and "" (empty)). They will be properly escaped. * * A simple example is: * @snippet basename.c set base basic * * If you want to add and not change the basename, use keyAddBaseName() * instead. If you do not want escaping, use keyAddName() instead. * * To add an inactive key name, use: * @snippet testabi_key.c base1 * * When you want to add an array item, use: * @snippet testabi_key.c base2 * * @see keyname for more details on special names * * @param key the key object to work with * @param baseName the string used to overwrite the basename of the key * @return the size in bytes of the new key name * @retval -1 on NULL pointers * @retval -1 if key was inserted to a keyset before * @see keyAddBaseName() * @see keySetName() to set a new name * @ingroup keyname */ ssize_t keySetBaseName(Key *key, const char *baseName) { if (!key) return -1; if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; if (!key->key) return -1; size_t size=0; char *searchBaseName=0; size_t searchBaseSize=0; char *p = key->key; while (*(p=keyNameGetOneLevel(p+size,&size))) { searchBaseName=p; searchBaseSize=size+1; } if (!searchBaseName || searchBaseName==key->key) { return -1; } // truncate the key key->keySize -= searchBaseSize; if (!baseName) { // just remove base name, so we are finished elektraFinalizeName(key); return key->keySize; } char *escaped = elektraMalloc (strlen (baseName) * 2 + 2); elektraEscapeKeyNamePart(baseName, escaped); size_t sizeEscaped = elektraStrLen (escaped); elektraRealloc((void**)&key->key, (key->keySize+sizeEscaped)*2); if (!key->key) { elektraFree (escaped); return -1; } key->key [key->keySize - 1] = KDB_PATH_SEPARATOR; memcpy (key->key + key->keySize, escaped, sizeEscaped); elektraFree (escaped); key->keySize += sizeEscaped; elektraFinalizeName(key); return key->keySize; }
ssize_t elektraKeySetName(Key *key, const char *newName, option_t options) { if (!key) return -1; if (test_bit(key->flags, KEY_FLAG_RO_NAME)) return -1; elektraRemoveKeyName(key); if (!(options & KEY_META_NAME)) keySetOwner (key, NULL); switch (keyGetNameNamespace(newName)) { case KEY_NS_NONE: ELEKTRA_ASSERT(0); case KEY_NS_EMPTY: elektraFinalizeEmptyName(key); return 0; // as documented case KEY_NS_CASCADING: key->keyUSize=1;key->keySize=sizeof("/"); break; case KEY_NS_SPEC: key->keyUSize=key->keySize=sizeof("spec"); break; case KEY_NS_PROC: key->keyUSize=key->keySize=sizeof("proc"); break; case KEY_NS_DIR: key->keyUSize=key->keySize=sizeof("dir"); break; case KEY_NS_USER: elektraHandleUserName(key, newName); break; case KEY_NS_SYSTEM: key->keyUSize=key->keySize=sizeof("system"); break; case KEY_NS_META: if (!(options & KEY_META_NAME)) return -1; keyNameGetOneLevel(newName,&key->keySize); key->keyUSize = ++ key->keySize; // for null break; } // Note that we abused keyUSize for cascading and user:owner const size_t length = elektraStrLen(newName); key->key=elektraMalloc(key->keySize*2); memcpy(key->key, newName, key->keySize); if (length == key->keyUSize || length == key->keySize) { // use || because full length is keyUSize in user, but keySize for / // newName consisted of root only elektraFinalizeName(key); return key->keyUSize; } if (elektraOnlySlashes(newName+key->keyUSize-1)) { elektraFinalizeName(key); return key->keySize; } key->key[key->keySize-1] = '\0'; const ssize_t ret = keyAddName(key, newName+key->keyUSize); if (ret == -1) elektraRemoveKeyName(key); else return key->keySize; return ret; }
static void elektraHandleUserName (Key * key, const char * newName) { const size_t userLength = sizeof ("user"); key->keyUSize = key->keySize = userLength; const char delim = newName[userLength - 1]; // no owner, we are finished if (delim == '/' || delim == '\0') return; ELEKTRA_ASSERT (delim == ':'); // handle owner (compatibility, to be removed) keyNameGetOneLevel (newName, &key->keyUSize); const size_t ownerLength = key->keyUSize - userLength; ++key->keyUSize; char * owner = elektraMalloc (ownerLength + 1); if (!owner) return; // out of memory, ok for owner strncpy (owner, newName + userLength, ownerLength); owner[ownerLength] = 0; keySetOwner (key, owner); elektraFree (owner); }
/** * Check whether a key is inactive. * * In Elektra terminology a hierarchy of keys is inactive if * the rootkey's basename starts with '.'. So a key is * also inactive if it is below an inactive key. * For example, user/key/.hidden is inactive and so * is user/.hidden/below. * * Inactive keys should not have any meaning to applications, * they are only a convention reserved for users and * administrators. To automatically remove all inactive keys * for an application, consider to use the hidden plugin. * * @param key the key object to work with * @retval 1 if the key is inactive * @retval 0 if the key is active * @retval -1 on NULL pointer or when key has no name * @ingroup keytest * */ int keyIsInactive (const Key * key) { if (!key) return -1; const char * p = keyName (key); if (!p) return -1; if (p[0] == '\0') return -1; size_t size = 0; while (*(p = keyNameGetOneLevel (p + size, &size))) { if (size > 0) { if (p[0] == '.') { return 1; } } } return 0; }
int keyIsBelow (const Key * key, const Key * check) { const char * keyname = 0; const char * checkname = 0; const char * ukeyname = 0; const char * ucheckname = 0; ssize_t keysize = 0; ssize_t checksize = 0; ssize_t ukeysize = 0; ssize_t uchecksize = 0; if (!key || !check) return -1; keyname = keyName (key); checkname = keyName (check); ukeyname = keyUnescapedName (key); ucheckname = keyUnescapedName (check); keysize = keyGetNameSize (key); checksize = keyGetNameSize (check); ukeysize = keyGetUnescapedNameSize (key); uchecksize = keyGetUnescapedNameSize (check); if (!strcmp (checkname, "/")) return 0; if (!strcmp (keyname, "/")) { if (checkname[0] == '/') return 1; if (strchr (checkname, '/')) return 1; } else if (checkname[0] == '/') { if (keyname[0] == '/') { if (!strncmp (keyname, checkname, keysize - 1)) { if (ucheckname[ukeysize - 1] == '\0' && uchecksize > ukeysize) { return 1; } } } else { size_t size = 0; char * ptr = (char *)keyname; ptr = keyNameGetOneLevel (ptr, &size); if (size == (size_t)keysize) { return 1; } keyname += size; keysize = elektraStrLen (keyname); ptr = strrchr (ukeyname, '\0'); ukeysize -= (ptr - ukeyname); if (!strncmp (keyname, checkname, keysize - 1)) { if (ucheckname[ukeysize - 1] == '\0' && uchecksize > ukeysize) { return 1; } } } } else if (keyname[0] == '/') { size_t size = 0; char * ptr = (char *)checkname; ptr = keyNameGetOneLevel (ptr, &size); if (size == (size_t)checksize) { return 0; } checkname += size; checksize = elektraStrLen (checkname); ptr = strrchr (ucheckname, '\0'); uchecksize -= (ptr - ucheckname); ucheckname = ptr; if (!strncmp (keyname, checkname, keysize - 1)) { if (ucheckname[ukeysize - 1] == '\0' && uchecksize > ukeysize) { return 1; } } } else if (!strncmp (keyname, checkname, keysize - 1)) { if ((ucheckname[ukeysize - 1] == '\0') && (uchecksize > ukeysize)) return 1; } return 0; }