diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | settings.c | 174 | ||||
-rw-r--r-- | settings.h | 52 |
3 files changed, 227 insertions, 1 deletions
@@ -5,7 +5,7 @@ LIBMINOR = 0 LIBNAME = libdnsldap.so.$(LIBMAJOR).$(LIBMINOR).0 LIBSONAME = libdnsldap.so.$(LIBMAJOR) -OBJS = ldap_driver.o semaphore.o log.o str.o +OBJS = ldap_driver.o semaphore.o log.o settings.o str.o CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -fPIC $(CFLAGS) diff --git a/settings.c b/settings.c new file mode 100644 index 0000000..6f1aa45 --- /dev/null +++ b/settings.c @@ -0,0 +1,174 @@ +#include <isc/util.h> +#include <isc/mem.h> +#include <isc/result.h> + +#include <ctype.h> +#include <stdlib.h> + +#include "log.h" +#include "settings.h" +#include "str.h" +#include "util.h" + +/* Forward declarations. */ +static int args_are_equal(const char *setting_argument, + const char *argv_argument); +static isc_result_t set_value(isc_mem_t *mctx, setting_t *setting, void *target, + const char *arg); +static isc_result_t set_default_value(isc_mem_t *mctx, setting_t *setting, + void *target); +static const char * get_value_str(const char *arg); + +isc_result_t +set_settings(isc_mem_t *mctx, void *target, setting_t settings[], + const char * const* argv) +{ + isc_result_t result; + int i, j; + const char *value_ptr; + void *target_member; + + for (i = 0; argv[i] != NULL; i++) { + for (j = 0; settings[j].name != NULL; j++) { + if (!args_are_equal(settings[j].name, argv[i])) + continue; + + target_member = (char *)target + settings[j].offset; + value_ptr = get_value_str(argv[i]); + CHECK(set_value(mctx, &settings[j], target, value_ptr)); + } + } + + /* When all is done, check that all the required settings are set. */ + for (j = 0; settings[j].name != NULL; j++) { + if (settings[j].set != 0) + continue; + if (settings[j].type == ST_NO_DEFAULT) { + log_error("argument %s must be set"); + result = ISC_R_FAILURE; + goto cleanup; + } + target_member = (char *)target + settings[j].offset; + CHECK(set_default_value(mctx, &settings[j], target_member)); + } + + return ISC_R_SUCCESS; + +cleanup: + /* TODO: Free memory in case of error. */ + return result; +} + +/* + * Return 1 if the argument names are equal. The argv_argument also needs to + * contain an additional space at the end. + */ +static int +args_are_equal(const char *setting_argument, const char *argv_argument) +{ + if (setting_argument == argv_argument) + return 1; + if (setting_argument == NULL || argv_argument == NULL) + return 0; + + while (1) { + if (*argv_argument == '\0') + return 0; + if (*setting_argument == '\0') + break; + if (*setting_argument != *argv_argument) + return 0; + setting_argument++; + argv_argument++; + } + + /* Now make sure we also found a space at the end of argv_argument. */ + if (*argv_argument != ' ') + return 0; + + return 1; +} + +static isc_result_t +set_value(isc_mem_t *mctx, setting_t *setting, void *target, const char *arg) +{ + isc_result_t result; + int numeric_value; + const char *value; + ld_string_t **ld_string_ptr; + + REQUIRE(target != NULL); + + value = get_value_str(arg); + + if (setting->type == ST_LD_STRING) { + ld_string_ptr = (ld_string_t **)target; + + if (setting->set) + str_destroy(ld_string_ptr); + + CHECK(str_new(mctx, ld_string_ptr)); + CHECK(str_init_char(*ld_string_ptr, value)); + } else if (setting->type == ST_SIGNED_INTEGER || + setting->type == ST_UNSIGNED_INTEGER) { + if (*value == '\0') { + result = ISC_R_FAILURE; + goto cleanup; + } + /* TODO: better type checking. */ + numeric_value = atoi(value); + if (setting->type == ST_SIGNED_INTEGER) { + (*(signed int *)target) = (signed int)numeric_value; + } else { + if (numeric_value < 0) { + log_error("argument %s must be an unsigned integer", setting->name); + result = ISC_R_FAILURE; + goto cleanup; + } + (*(unsigned int *)target) = (unsigned int)numeric_value; + } + } else { + fatal_error("unknown type in function set_value()"); + result = ISC_R_FAILURE; + goto cleanup; + } + + setting->set = 1; + + return ISC_R_SUCCESS; + +cleanup: + return result; +} + +static isc_result_t +set_default_value(isc_mem_t *mctx, setting_t *setting, void *target) +{ + switch (setting->type) { + case ST_LD_STRING: + return set_value(mctx, setting, target, setting->value_char); + break; + case ST_SIGNED_INTEGER: + (*(signed int *)target) = (signed int)setting->value_sint; + break; + case ST_UNSIGNED_INTEGER: + (*(unsigned int *)target) = (unsigned int)setting->value_uint; + break; + default: + fatal_error("unknown type in function set_default_value()"); + return ISC_R_FAILURE; + } + + return ISC_R_SUCCESS; +} + +static const char * +get_value_str(const char *arg) +{ + while (*arg != '\0' && !isblank(*arg)) + arg++; + while (*arg != '\0' && isblank(*arg)) + arg++; + + return arg; +} diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..9819cd3 --- /dev/null +++ b/settings.h @@ -0,0 +1,52 @@ +#ifndef _LD_SETTINGS_H_ +#define _LD_SETTINGS_H_ + +typedef struct setting setting_t; + +typedef enum { + ST_LD_STRING, + ST_SIGNED_INTEGER, + ST_UNSIGNED_INTEGER, + ST_NO_DEFAULT, +} setting_type_t; + +struct setting { + const char *name; + int set; + setting_type_t type; + union { + const char *value_char; + signed int value_sint; + unsigned int value_uint; + } default_value; + size_t offset; +}; + +/* + * These defines are used as initializers for setting_t, for example: + * + * const setting_t my_setting = { + * "name", default_string("this is the default"), + * offsetof(some_struct, some_member + * } + */ +#define default_string(value) 0, ST_LD_STRING, { .value_char = (value) } +#define default_sint(value) 0, ST_SIGNED_INTEGER, { .value_sint = (value) } +#define default_uint(value) 0, ST_UNSIGNED_INTEGER, { .value_uint = (value) } +#define default_nothing() 0, ST_NO_DEFAULT, { .value_uint = 0 } + +/* This is used in the end of setting_t arrays. */ +#define end_of_settings { NULL, default_sint(0), 0 } + +#define value_char default_value.value_char +#define value_sint default_value.value_sint +#define value_uint default_value.value_uint + +/* + * Prototypes. + */ +isc_result_t +set_settings(isc_mem_t *mctx, void *target, setting_t settings[], + const char * const* argv); + +#endif /* !_LD_SETTINGS_H_ */ |