void test_lookup (void) { char **env = NULL; size_t len = 0; char * const *ret; TEST_FUNCTION ("environ_lookup"); len = 0; env = nih_str_array_new (NULL); /* Check that an empty table always returns NULL. */ TEST_FEATURE ("with empty table"); ret = environ_lookup (env, "FOO", 3); TEST_EQ_P (ret, NULL); assert (nih_str_array_add (&env, NULL, &len, "FOOLISH=no")); assert (nih_str_array_add (&env, NULL, &len, "BAR=BAZ")); /* Check that a key that is present is returned. */ TEST_FEATURE ("with key to be found"); ret = environ_lookup (env, "BAR", 3); TEST_EQ_P (ret, &env[1]); /* Check that a key that doesn't exist returns NULL. */ TEST_FEATURE ("with key not found"); ret = environ_lookup (env, "MEEP", 4); TEST_EQ_P (ret, NULL); /* Check that the key is not prefix-matched. */ TEST_FEATURE ("with key that is prefix of another"); ret = environ_lookup (env, "FOO", 3); TEST_EQ_P (ret, NULL); /* Check that the length is honoured. */ TEST_FEATURE ("with longer key"); ret = environ_lookup (env, "FOOLISH", 3); TEST_EQ_P (ret, NULL); nih_free (env); }
/** * Substitute variable references in a value. * * Substitutes variable references in a value (entirely replaces reference * values, and substitutes variables within strings). If the value is a list, * will recurse onto the values contained within the list. If an error occurs * while substituting variables, the error will be raised with config_error() * and the function will return false. For strings/references, if an error * occurs, the value will not be changed. * * @param value Value to substitute in. * @param env Environment to take variables from. * * @return Whether variables were successfully substituted. */ bool value_substitute(value_t *value, environ_t *env) { const value_t *target; char *str; size_t i, start; switch (value->type) { case VALUE_TYPE_REFERENCE: /* Non-string variable reference, replace the whole value. */ target = environ_lookup(env, value->string); if (!target) { config_error("Variable '%s' not found", value->string); return false; } free(value->string); value_copy(target, value); break; case VALUE_TYPE_STRING: /* Search for in-string variable references, which we substitute in the * string for a string representation of the variable. */ str = strdup(value->string); i = 0; start = 0; while (str[i]) { if (start) { if (isalnum(str[i]) || str[i] == '_') { i++; } else if (str[i] == '}') { const char *name; char *subst; size_t prefix_len, var_len, len; str[start - 2] = 0; str[i] = 0; /* We have a whole reference. */ name = &str[start]; target = environ_lookup(env, name); if (!target) { config_error("Variable '%s' not found", name); free(str); return false; } /* Stringify the target into the temporary buffer. */ switch (target->type) { case VALUE_TYPE_INTEGER: snprintf(temp_buf, TEMP_BUF_LEN, "%llu", target->integer); break; case VALUE_TYPE_BOOLEAN: snprintf(temp_buf, TEMP_BUF_LEN, (target->boolean) ? "true" : "false"); break; case VALUE_TYPE_STRING: snprintf(temp_buf, TEMP_BUF_LEN, "%s", target->string); break; default: config_error("Variable '%s' cannot be converted to string", name); free(str); return false; } /* Now allocate a new string. The start and end characters * of the reference have been replaced with null terminators * effectively splitting up the string into 3 parts. */ prefix_len = strlen(str); var_len = strlen(temp_buf); len = prefix_len + var_len + strlen(&str[i + 1]) + 1; subst = malloc(len); snprintf(subst, len, "%s%s%s", str, temp_buf, &str[i + 1]); /* Replace the string and continue after the substituted * portion. */ free(str); str = subst; i = prefix_len + var_len; start = 0; } else { start = 0; i++; } } else { if (str[i] == '$' && str[i + 1] == '{') { i += 2; start = i; } else { i++; } } } free(value->string); value->string = str; break; case VALUE_TYPE_LIST: for (i = 0; i < value->list->count; i++) { if (!value_substitute(&value->list->values[i], env)) return false; } break; default: break; } return true; }