#############################################################################*/
#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
+#include <unistd.h>
+#include "address.h"
#include "config.h"
#include "logging.h"
#include "string.h"
char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
};
+struct nw_config_option {
+ STAILQ_ENTRY(nw_config_option) nodes;
+
+ const char* key;
+ void* value;
+ size_t length;
+
+ // Callbacks
+ nw_config_option_read_callback_t read_callback;
+ nw_config_option_write_callback_t write_callback;
+ void* data;
+};
+
struct nw_config {
int nrefs;
- STAILQ_HEAD(entries, nw_config_entry) entries;
+ STAILQ_HEAD(config_entries, nw_config_entry) entries;
+
+ // Options
+ STAILQ_HEAD(parser_entries, nw_config_option) options;
};
static void nw_config_entry_free(struct nw_config_entry* entry) {
free(entry);
}
+static void nw_config_option_free(struct nw_config_option* option) {
+ free(option);
+}
+
static struct nw_config_entry* nw_config_entry_create(
- struct nw_config* config, const char* key) {
+ nw_config* config, const char* key) {
int r;
// Check input value
// Append the new entry
STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
+ return entry;
+
ERROR:
nw_config_entry_free(entry);
return NULL;
}
-static void nw_config_free(struct nw_config* config) {
+static void nw_config_free(nw_config* config) {
+ struct nw_config_option* option = NULL;
+
// Flush all entries
nw_config_flush(config);
+ // Free all options
+ while (!STAILQ_EMPTY(&config->options)) {
+ option = STAILQ_FIRST(&config->options);
+ STAILQ_REMOVE_HEAD(&config->options, nodes);
+
+ // Free the options
+ nw_config_option_free(option);
+ }
+
free(config);
}
-int nw_config_create(struct nw_config** config) {
- struct nw_config* c = calloc(1, sizeof(*c));
+int nw_config_create(nw_config** config, FILE* f) {
+ int r;
+
+ nw_config* c = calloc(1, sizeof(*c));
if (!c)
return 1;
// Initialise entries
STAILQ_INIT(&c->entries);
+ // Initialise options
+ STAILQ_INIT(&c->options);
+
+ // Read configuration
+ if (f) {
+ r = nw_config_read(c, f);
+ if (r < 0)
+ goto ERROR;
+ }
+
*config = c;
return 0;
+
+ERROR:
+ nw_config_free(c);
+
+ return r;
+}
+
+int nw_config_open(nw_config** config, const char* path) {
+ FILE* f = NULL;
+ int r;
+
+ // Open path
+ f = fopen(path, "r");
+ if (!f)
+ return -errno;
+
+ // Create a new configuration
+ r = nw_config_create(config, f);
+
+ERROR:
+ if (f)
+ fclose(f);
+
+ return r;
}
-struct nw_config* nw_config_ref(struct nw_config* config) {
+nw_config* nw_config_ref(nw_config* config) {
config->nrefs++;
return config;
}
-struct nw_config* nw_config_unref(struct nw_config* config) {
+nw_config* nw_config_unref(nw_config* config) {
if (--config->nrefs > 0)
return config;
return NULL;
}
-int nw_config_flush(struct nw_config* config) {
+int nw_config_copy(nw_config* config, nw_config** copy) {
+ struct nw_config_entry* entry = NULL;
+ nw_config* c = NULL;
+ int r;
+
+ // Create a new configuration
+ r = nw_config_create(&c, NULL);
+ if (r)
+ return r;
+
+ // Copy everything
+ STAILQ_FOREACH(entry, &config->entries, nodes) {
+ r = nw_config_set(c, entry->key, entry->value);
+ if (r)
+ goto ERROR;
+ }
+
+ *copy = c;
+ return 0;
+
+ERROR:
+ if (c)
+ nw_config_unref(c);
+
+ return r;
+}
+
+int nw_config_flush(nw_config* config) {
struct nw_config_entry* entry = NULL;
while (!STAILQ_EMPTY(&config->entries)) {
return 0;
}
-int nw_config_readf(struct nw_config* config, FILE* f) {
- // XXX TODO
+int nw_config_read(nw_config* config, FILE* f) {
+ char* line = NULL;
+ size_t length = 0;
+ int r;
+
+ ssize_t bytes_read = 0;
- return 0;
-}
+ char* key = NULL;
+ char* val = NULL;
-int nw_config_read(struct nw_config* config, const char* path) {
- FILE* f = NULL;
- int r;
+ for (;;) {
+ // Read the next line
+ bytes_read = getline(&line, &length, f);
+ if (bytes_read < 0)
+ break;
- // Open the file
- f = fopen(path, "r");
- if (!f) {
- ERROR("Could not read configuration file %s: %m\n", path);
- r = 1;
- goto ERROR;
- }
+ // Key starts at the beginning of the line
+ key = line;
- // Read from file
- r = nw_config_readf(config, f);
+ // Value starts after '='
+ val = strchr(line, '=');
-ERROR:
- if (f)
- fclose(f);
+ // Invalid line without a '=' character
+ if (!val)
+ continue;
+
+ // Split the string
+ *val++ = '\0';
+
+ // Strip any whitespace from value
+ r = nw_string_strip(val);
+ if (r)
+ break;
+
+ // Store the setting
+ r = nw_config_set(config, key, val);
+ if (r)
+ break;
+ }
+
+ if (line)
+ free(line);
return r;
}
-int nw_config_writef(struct nw_config* config, FILE* f) {
+int nw_config_write(nw_config* config, FILE* f) {
struct nw_config_entry* entry = NULL;
int r;
continue;
// Write the entry
- r = fprintf(f, "%s=\"%s\"\n", entry->key, entry->value);
+ r = fprintf(f, "%s=%s\n", entry->key, entry->value);
if (r < 0) {
ERROR("Failed to write configuration: %m\n");
return r;
return 0;
}
-int nw_config_write(struct nw_config* config, const char* path) {
- int r;
-
- FILE* f = fopen(path, "w");
- if (!f) {
- ERROR("Failed to open %s for writing: %m\n", path);
- r = 1;
- goto ERROR;
- }
-
- // Write configuration
- r = nw_config_writef(config, f);
-
-ERROR:
- if (f)
- fclose(f);
-
- return r;
-}
-
-static struct nw_config_entry* nw_config_find(struct nw_config* config, const char* key) {
+static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
struct nw_config_entry* entry = NULL;
STAILQ_FOREACH(entry, &config->entries, nodes) {
return NULL;
}
-int nw_config_del(struct nw_config* config, const char* key) {
+int nw_config_del(nw_config* config, const char* key) {
struct nw_config_entry* entry = NULL;
// Find an entry matching the key
return 0;
}
-int nw_config_set(struct nw_config* config, const char* key, const char* value) {
+const char* nw_config_get(nw_config* config, const char* key) {
+ struct nw_config_entry* entry = nw_config_find(config, key);
+
+ // Return the value if found and set
+ if (entry && *entry->value)
+ return entry->value;
+
+ // Otherwise return NULL
+ return NULL;
+}
+
+int nw_config_set(nw_config* config, const char* key, const char* value) {
struct nw_config_entry* entry = NULL;
+ // Log the change
+ DEBUG("%p: Setting %s = %s\n", config, key, value);
+
// Delete the entry if val is NULL
if (!value)
return nw_config_del(config, key);
return nw_string_set(entry->value, value);
}
-const char* nw_config_get(struct nw_config* config, const char* key) {
- struct nw_config_entry* entry = nw_config_find(config, key);
+int nw_config_get_int(nw_config* config, const char* key, const int __default) {
+ char* p = NULL;
+ int r;
- // Return the value if found and set
- if (entry && *entry->value)
- return entry->value;
+ const char* value = nw_config_get(config, key);
- // Otherwise return NULL
- return NULL;
+ // Return zero if not set
+ if (!value)
+ return __default;
+
+ // Parse the input
+ r = strtoul(value, &p, 10);
+
+ // If we have characters following the input, we throw it away
+ if (p)
+ return __default;
+
+ return r;
}
-unsigned int nw_config_get_unsigned_int(struct nw_config* config, const char* key) {
+int nw_config_set_int(nw_config* config, const char* key, const int value) {
+ char __value[1024];
+ int r;
+
+ // Format the value as string
+ r = nw_string_format(__value, "%d", value);
+ if (r)
+ return r;
+
+ return nw_config_set(config, key, __value);
+}
+
+static const char* nw_config_true[] = {
+ "true",
+ "yes",
+ "1",
+ NULL,
+};
+
+int nw_config_get_bool(nw_config* config, const char* key) {
const char* value = nw_config_get(config, key);
- // Return zero if not set
+ // No value indicates false
if (!value)
return 0;
- return strtoul(value, NULL, 10);
+ // Check if we match any known true words
+ for (const char** s = nw_config_true; *s; s++) {
+ if (strcasecmp(value, *s) == 0)
+ return 1;
+ }
+
+ // No match means false
+ return 0;
+}
+
+int nw_config_set_bool(nw_config* config, const char* key, const int value) {
+ return nw_config_set(config, key, value ? "true" : "false");
+}
+
+/*
+ Options
+*/
+
+int nw_config_options_read(nw_config* config) {
+ struct nw_config_option* option = NULL;
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+ r = option->read_callback(config,
+ option->key, option->value, option->length, option->data);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int nw_config_options_write(nw_config* config) {
+ struct nw_config_option* option = NULL;
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+ r = option->write_callback(config,
+ option->key, option->value, option->length, option->data);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int nw_config_option_add(nw_config* config,
+ const char* key, void* value, const size_t length,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback, void* data) {
+ // Check input
+ if (!key || !value || !read_callback || !write_callback)
+ return -EINVAL;
+
+ // Allocate a new option
+ struct nw_config_option* option = calloc(1, sizeof(*option));
+ if (!option)
+ return -errno;
+
+ // Set key
+ option->key = key;
+
+ // Set value
+ option->value = value;
+ option->length = length;
+
+ // Set callbacks
+ option->read_callback = read_callback;
+ option->write_callback = write_callback;
+ option->data = data;
+
+ // Append the new option
+ STAILQ_INSERT_TAIL(&config->options, option, nodes);
+
+ return 0;
+}
+
+int nw_config_read_int(nw_config* config,
+ const char* key, void* value, const size_t length, void* data) {
+ // Fetch the value
+ *(int*)value = nw_config_get_int(config, key, -1);
+
+ return 0;
+}
+
+int nw_config_write_int(nw_config* config,
+ const char* key, const void* value, const size_t length, void* data) {
+ return 0;
+}
+
+// String
+
+int nw_config_read_string(nw_config* config,
+ const char* key, void* value, const size_t length, void* data) {
+ // Fetch the value
+ const char* p = nw_config_get(config, key);
+ if (p)
+ *(const char**)value = p;
+
+ return 0;
+}
+
+int nw_config_write_string(nw_config* config,
+ const char* key, const void* value, const size_t length, void* data) {
+ return nw_config_set(config, key, *(const char**)value);
+}
+
+// String Buffer
+
+int nw_config_read_string_buffer(nw_config* config,
+ const char* key, void* value, const size_t length, void* data) {
+ char* string = (char*)value;
+
+ // Fetch the value
+ const char* p = nw_config_get(config, key);
+ if (p)
+ return __nw_string_set(string, length, p);
+
+ return 0;
+}
+
+// String Table
+
+int nw_config_read_string_table(nw_config* config,
+ const char* key, void* value, const size_t length, void* data) {
+ const char* s = NULL;
+ int* v = (int*)value;
+
+ const nw_string_table_t* table = (nw_string_table_t*)data;
+
+ // Fetch the string
+ s = nw_config_get(config, key);
+ if (!s)
+ return -errno;
+
+ // Lookup the string in the table
+ *v = nw_string_table_lookup_id(table, s);
+
+ // If the result is negative, nothing was found
+ if (*v < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int nw_config_write_string_table(nw_config* config,
+ const char* key, const void* value, const size_t length, void* data) {
+ int* v = (int*)value;
+
+ const nw_string_table_t* table = (nw_string_table_t*)data;
+
+ // Lookup the string
+ const char* s = nw_string_table_lookup_string(table, *v);
+ if (!s)
+ return -errno;
+
+ return nw_config_set(config, key, s);
+}
+
+// Address
+
+int nw_config_read_address(nw_config* config,
+ const char* key, void* value, const size_t length, void* data) {
+ nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+ // Fetch the value
+ const char* p = nw_config_get(config, key);
+ if (!p)
+ return -EINVAL;
+
+ r = nw_address_from_string(address, p);
+ if (r < 0)
+ ERROR("Could not parse address: %s\n", p);
+
+ return r;
+}
+
+int nw_config_write_address(nw_config* config,
+ const char* key, const void* value, const size_t length, void* data) {
+ const nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+ // Format the address to string
+ char* p = nw_address_to_string(address);
+ if (!p)
+ return -errno;
+
+ // Store the value
+ r = nw_config_set(config, key, p);
+ if (r < 0)
+ goto ERROR;
+
+ // Success
+ r = 0;
+
+ERROR:
+ if (p)
+ free(p);
+
+ return r;
}