diff options
Diffstat (limited to 'module/conf.c')
-rw-r--r-- | module/conf.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/module/conf.c b/module/conf.c new file mode 100644 index 0000000..a0a3917 --- /dev/null +++ b/module/conf.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2005, Stefan Walter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * + * CONTRIBUTORS + * Stef Walter <stef@memberwebs.com> + */ + +#include "config.h" + +#include "conf.h" + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void +errmsg (const char *filename, const char* msg, ...) +{ + #define MAX_MSGLEN 1024 + char buf[MAX_MSGLEN]; + va_list ap; + + va_start (ap, msg); + vsnprintf (buf, MAX_MSGLEN, msg, ap); + buf[MAX_MSGLEN - 1] = 0; + conf_error (filename, buf); + va_end (ap); +} + +static void +strcln (char* data, char ch) +{ + char* p; + for (p = data; *data; data++, p++) { + while (*data == ch) + data++; + *p = *data; + } + + /* Renull terminate */ + *p = 0; +} + +static char* +strbtrim (const char* data) +{ + while (*data && isspace (*data)) + ++data; + return (char*)data; +} + +static void +stretrim (char* data) +{ + char* t = data + strlen (data); + while (t > data && isspace (*(t - 1))) { + t--; + *t = 0; + } +} + +static char* +strtrim (char* data) +{ + data = (char*)strbtrim (data); + stretrim (data); + return data; +} + +/* ----------------------------------------------------------------------------- + * CONFIG PARSER + */ + +static char* +read_config_file (const char* filename, int flags) +{ + char* config = NULL; + FILE* f = NULL; + long len; + + assert (filename); + + f = fopen (filename, "r"); + if (f == NULL) { + if ((flags & CONF_IGNORE_MISSING) && + (errno == ENOENT || errno == ENOTDIR)) { + config = strdup ("\n"); + if (!config) + errno = ENOMEM; + return config; + } + errmsg (filename, "couldn't open config file: %s", filename); + return NULL; + } + + /* Figure out size */ + if (fseek (f, 0, SEEK_END) == -1 || + (len = ftell (f)) == -1 || + fseek (f, 0, SEEK_SET) == -1) { + errmsg (filename, "couldn't seek config file: %s", filename); + return NULL; + } + + if ((config = (char*)malloc (len + 2)) == NULL) { + errmsg (filename, "out of memory"); + errno = ENOMEM; + return NULL; + } + + /* And read in one block */ + if (fread (config, 1, len, f) != len) { + errmsg (filename, "couldn't read config file: %s", filename); + return NULL; + } + + fclose (f); + + /* Null terminate the data */ + config[len] = '\n'; + config[len + 1] = 0; + + /* Remove nasty dos line endings */ + strcln (config, '\r'); + + return config; +} + +hash_t* +conf_parse_file (const char* filename, int flags) +{ + char *name; + char *value; + hash_t *ht = NULL; + char *config; + char *next; + char *end; + + assert (filename); + + /* Adds an extra newline to end of file */ + config = read_config_file (filename, flags); + if (!config) + return NULL; + + ht = hash_create (hash_string_hash, hash_string_equal, free, free); + next = config; + + /* Go through lines and process them */ + while ((end = strchr (next, '\n')) != NULL) { + *end = 0; + name = strbtrim (next); + next = end + 1; + + /* Empty lines / comments at start */ + if (!*name || *name == '#') + continue; + + /* Look for the break between name = value on the same line */ + value = name + strcspn (name, ":="); + if (!*value) { + errmsg (filename, "%s: invalid config line: %s", filename, name); + errno = EINVAL; + break; + } + + /* Null terminate and split value part */ + *value = 0; + value++; + + name = strtrim (name); + value = strtrim (value); + + name = strdup (name); + if (!name) { + errno = ENOMEM; + break; + } + value = strdup (value); + if (!value) { + free (name); + errno = ENOMEM; + break; + } + if (!hash_set (ht, name, value)) { + free (name); + free (value); + errno = ENOMEM; + break; + } + } + + /* Unsuccessful? */ + if (end != NULL) { + hash_free (ht); + ht = NULL; + } + + free (config); + return ht; +} |