diff options
Diffstat (limited to '')
-rw-r--r-- | lib/util/tiniparser.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/lib/util/tiniparser.c b/lib/util/tiniparser.c new file mode 100644 index 0000000..428ccff --- /dev/null +++ b/lib/util/tiniparser.c @@ -0,0 +1,390 @@ +/* + * Trivial smb.conf parsing code + * iniparser compatibility layer. + * + * Copyright Jeremy Allison <jra@samba.org> 2014 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License Version 3 or later, in which case the provisions + * of the GPL are required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `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 AUTHOR 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <stddef.h> +#include "tini.h" +#include "tiniparser.h" + +struct tiniparser_entry { + struct tiniparser_entry *next_entry; + char *key; + char *value; +}; + +struct tiniparser_section { + struct tiniparser_section *next_section; + struct tiniparser_entry *entry_list; + char section_name[]; +}; + +struct tiniparser_dictionary { + struct tiniparser_section *section_list; +}; + +/* + * Find a section from a given key. + * Also return start of subkey. + * Return NULL if section name can't be found, + * if no section name given, or no subkey given. + */ + +static struct tiniparser_section *find_section(struct tiniparser_dictionary *d, + const char *key, + const char **subkey) +{ + struct tiniparser_section *curr_section; + const char *p; + size_t section_len; + + if (key == NULL) { + return NULL; + } + p = strchr(key, ':'); + if (p == NULL) { + /* No section. */ + return NULL; + } + + section_len = p - key; + /* Ensure we have at least one character of section name. */ + if (section_len == 0) { + return NULL; + } + /* Ensure we have at least one character of subkey. */ + if (p[1] == '\0') { + return NULL; + } + + for (curr_section = d->section_list; + curr_section; + curr_section = curr_section->next_section) { + /* + * Check if the key section matches the + * section name *exactly* (with terminating + * null after section_len characters. + */ + if ((strncasecmp(key, curr_section->section_name, section_len) == 0) && + (curr_section->section_name[section_len] == '\0')) { + *subkey = p+1; + return curr_section; + } + } + return NULL; +} + +static struct tiniparser_entry *find_entry(struct tiniparser_section *section, + const char *key) +{ + struct tiniparser_entry *curr_entry; + + for (curr_entry = section->entry_list; + curr_entry; + curr_entry = curr_entry->next_entry) { + if (strcasecmp(key, + curr_entry->key) == 0) { + return curr_entry; + } + } + return NULL; +} + +const char *tiniparser_getstring(struct tiniparser_dictionary *d, + const char *key, + const char *default_value) +{ + struct tiniparser_section *section; + struct tiniparser_entry *entry; + const char *subkey; + + section = find_section(d, key, &subkey); + if (section == NULL) { + return default_value; + } + + entry = find_entry(section, subkey); + if (entry == NULL) { + return default_value; + } + + return entry->value; +} + + +bool tiniparser_getboolean(struct tiniparser_dictionary *d, + const char *key, + bool default_value) +{ + const char *value = tiniparser_getstring(d, key, NULL); + + if (value == NULL) { + return default_value; + } + + switch(value[0]) { + case '1': + case 'T': + case 't': + case 'y': + case 'Y': + return true; + case '0': + case 'F': + case 'f': + case 'n': + case 'N': + return false; + default: + break; + } + + return default_value; +} + +int tiniparser_getint(struct tiniparser_dictionary *d, + const char *key, + int default_value) +{ + const char *value = tiniparser_getstring(d, key, NULL); + + if (value == NULL) { + return default_value; + } + + return (int)strtol(value, NULL, 0); +} + +static bool value_parser(const char *key, + const char *value, + void *private_data) +{ + struct tiniparser_dictionary *d = + (struct tiniparser_dictionary *)private_data; + struct tiniparser_section *section = d->section_list; + struct tiniparser_entry *entry = NULL; + size_t val_len; + size_t key_len; + + if (section == NULL) { + return false; + } + if (key == NULL) { + return false; + } + if (value == NULL) { + return false; + } + + key_len = strlen(key) + 1; + val_len = strlen(value) + 1; + + entry = find_entry(section, key); + if (entry) { + /* Replace current value. */ + char *new_val = malloc(val_len); + if (new_val == NULL) { + return false; + } + memcpy(new_val, value, val_len); + free(entry->value); + entry->value = new_val; + return true; + } + + /* Create a new entry. */ + entry = malloc(sizeof(struct tiniparser_entry)); + if (entry == NULL) { + return false; + } + entry->key = malloc(key_len); + if (entry->key == NULL) { + free(entry); + return false; + } + memcpy(entry->key, key, key_len); + + entry->value = malloc(val_len); + if (entry->value == NULL) { + free(entry->key); + free(entry); + return false; + } + memcpy(entry->value, value, val_len); + + entry->next_entry = section->entry_list; + section->entry_list = entry; + return true; +} + +static bool section_parser(const char *section_name, + void *private_data) +{ + struct tiniparser_section **pp_section; + struct tiniparser_section *new_section; + struct tiniparser_dictionary *d = + (struct tiniparser_dictionary *)private_data; + size_t section_name_len; + + if (section_name == NULL) { + return false; + } + + /* Section names can't contain ':' */ + if (strchr(section_name, ':') != NULL) { + return false; + } + + /* Do we already have this section ? */ + for (pp_section = &d->section_list; + *pp_section; + pp_section = &(*pp_section)->next_section) { + if (strcasecmp(section_name, + (*pp_section)->section_name) == 0) { + /* + * Move to the front of the list for + * value_parser() to find it. + */ + + /* First save current entry. */ + struct tiniparser_section *curr_section = *pp_section; + + /* Now unlink current entry from list. */ + *pp_section = curr_section->next_section; + + /* Make current entry next point to whole list. */ + curr_section->next_section = d->section_list; + + /* And replace list with current entry at start. */ + d->section_list = curr_section; + + return true; + } + } + + section_name_len = strlen(section_name) + 1; + + /* Create new section. */ + new_section = malloc( + offsetof(struct tiniparser_section, section_name) + + section_name_len); + if (new_section == NULL) { + return false; + } + + memcpy(new_section->section_name, section_name, section_name_len); + + new_section->entry_list = NULL; + + /* Add it to the head of the singly linked list. */ + new_section->next_section = d->section_list; + d->section_list = new_section; + return true; +} + +struct tiniparser_dictionary *tiniparser_load_stream(FILE *fp) +{ + bool ret; + struct tiniparser_dictionary *d = NULL; + + d = malloc(sizeof(struct tiniparser_dictionary)); + if (d == NULL) { + return NULL; + } + d->section_list = NULL; + + ret = tini_parse(fp, + false, + section_parser, + value_parser, + d); + if (ret == false) { + tiniparser_freedict(d); + d = NULL; + } + return d; +} + +struct tiniparser_dictionary *tiniparser_load(const char *filename) +{ + struct tiniparser_dictionary *d; + FILE *fp = fopen(filename, "r"); + + if (fp == NULL) { + return NULL; + } + + d = tiniparser_load_stream(fp); + + fclose(fp); + + return d; +} + +void tiniparser_freedict(struct tiniparser_dictionary *d) +{ + struct tiniparser_section *curr_section, *next_section; + + if (d == NULL) { + return; + } + + for (curr_section = d->section_list; + curr_section; + curr_section = next_section) { + struct tiniparser_entry *curr_entry, *next_entry; + + next_section = curr_section->next_section; + + for (curr_entry = curr_section->entry_list; + curr_entry; + curr_entry = next_entry) { + next_entry = curr_entry->next_entry; + + free(curr_entry->key); + free(curr_entry->value); + free(curr_entry); + } + free(curr_section); + } + free(d); +} |