diff options
Diffstat (limited to 'src/ini.c')
| -rw-r--r-- | src/ini.c | 409 | 
1 files changed, 409 insertions, 0 deletions
| diff --git a/src/ini.c b/src/ini.c new file mode 100644 index 0000000..b2df150 --- /dev/null +++ b/src/ini.c @@ -0,0 +1,409 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "ohmycal.h" +#include "ini.h" +/* +char *strip(char *s) { +    size_t len = strlen(s) + 1; +    while (--len) { +        if (isalnum(s[len])) { +            break; +        } +        if (isblank(s[len])) { +            s[len] = '\0'; +        } +    } +    return s; +} + */ + +/* +char *lstrip(char *s) { +    size_t i = 0; +    char *end = NULL; +    do { +        end = &s[i]; +        if (!isblank(*end)) { +            break; +        } +        i++; +    } while (1); +    if (i) { +        size_t len = strlen(end); +        memmove(s, end, len); +        if (strlen(s)) { +            s[len] = '\0'; +        } +    } +    return s; +} + */ + +/* +int startswith(const char *s1, char *s2) { +    size_t i; +    for (i = 0; i < strlen(s2); i++) { +        if (s1[i] != s2[i]) { +            return 0; +        } +    } +    return 1; +} +*/ + +/* +int endswith(const char *s1, char *s2) { +    size_t s2x, s1x; +    for (s2x = strlen(s2), s1x = strlen(s1); s2x >= 0; s2x--, s1x--) { +        char *s1p = &s1[s1x]; +        char *s2p = &s2[s2x]; +        if (s1[s1x] != s2[s2x]) { +            return 0; +        } +        if (s2x == 0) { +            break; +        } +    } +    return 1; +} + */ + +struct INIFILE *ini_init() { +    struct INIFILE *ini; +    ini = calloc(1, sizeof(*ini)); +    ini->section_count = 0; +    return ini; +} + +void ini_section_init(struct INIFILE **ini) { +    (*ini)->section = calloc((*ini)->section_count + 1, sizeof(**(*ini)->section)); +} + +struct INISection *ini_section_search(struct INIFILE **ini, char *value) { +    struct INISection *result = NULL; +    for (size_t i = 0; i < (*ini)->section_count; i++) { +        if ((*ini)->section[i]->key != NULL) { +            if (!strcmp((*ini)->section[i]->key, value)) { +                result = (*ini)->section[i]; +            } +        } +    } +    return result; +} + +int ini_data_init(struct INIFILE **ini, char *section_name) { +    struct INISection *section = ini_section_search(ini, section_name); +    if (section == NULL) { +        return 1; +    } +    section->data = calloc(section->data_count + 1, sizeof(**section->data)); +    return 0; +} + +struct INIData *ini_data_get(struct INIFILE *ini, char *section_name, char *key) { +    struct INISection *section = NULL; +    section = ini_section_search(&ini, section_name); +    for (size_t i = 0; i < section->data_count; i++) { +        if (section->data[i]->key != NULL) { +            if (!strcmp(section->data[i]->key, key)) { +                return section->data[i]; +            } +        } +    } +    return NULL; +} + +struct INIData *ini_getall(struct INIFILE *ini, char *section_name) { +    struct INISection *section = NULL; +    struct INIData *result = NULL; +    static size_t i = 0; + +    section = ini_section_search(&ini, section_name); +    if (section->data[i]) { +        result = section->data[i]; +        i++; +    } else { +        result = NULL; +        i = 0; +    } + +    return result; +} + +int ini_getval(struct INIFILE *ini, char *section_name, char *key, int type, union INIVal *result) { +    char *token = NULL; +    char tbuf[BUFSIZ]; +    char *tbufp = tbuf; +    struct INIData *data; +    data = ini_data_get(ini, section_name, key); +    if (!data) { +        result->as_char_p = NULL; +        return -1; +    } +    switch (type) { +        case INIVAL_TYPE_INT: +            result->as_int = (int) strtol(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_UINT: +            result->as_uint = (unsigned int) strtoul(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_LONG: +            result->as_long = (long) strtol(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_ULONG: +            result->as_ulong = (unsigned long) strtoul(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_LLONG: +            result->as_llong = (long long) strtoll(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_ULLONG: +            result->as_ullong = (unsigned long long) strtoull(data->value, NULL, 10); +            break; +        case INIVAL_TYPE_DOUBLE: +            result->as_double = (double) strtod(data->value, NULL); +            break; +        case INIVAL_TYPE_FLOAT: +            result->as_float = (float) strtod(data->value, NULL); +            break; +        case INIVAL_TYPE_STR: +            result->as_char_p = lstrip(data->value); +            break; +        case INIVAL_TYPE_STR_ARRAY: +            strcpy(tbufp, data->value); +            *data->value = '\0'; +            for (size_t i = 0; (token = strsep(&tbufp, "\n")) != NULL; i++) { +                lstrip(token); +                strcat(data->value, token); +                strcat(data->value, "\n"); +            } +            result->as_char_p = data->value; +            break; +        case INIVAL_TYPE_BOOL: +            result->as_bool = false; +            if ((!strcmp(data->value, "true") || !strcmp(data->value, "True")) || +                    (!strcmp(data->value, "yes") || !strcmp(data->value, "Yes")) || +                    strtol(data->value, NULL, 10)) { +                result->as_bool = true; +            } +            break; +        default: +            memset(result, 0, sizeof(*result)); +            break; +    } +    return 0; +} + +int ini_data_record(struct INIFILE **ini, char *section_name, char *key, char *value) { +    struct INISection *section = ini_section_search(ini, section_name); +    if (section == NULL) { +        return 1; +    } + +    struct INIData **tmp = realloc(section->data, (section->data_count + 1) * sizeof(**section->data)); +    if (!tmp) { +        perror(__FUNCTION__); +        exit(1); +    } +    section->data = tmp; +    if (!ini_data_get((*ini), section_name, key)) { +        section->data[section->data_count] = calloc(1, sizeof(*section->data[0])); +        section->data[section->data_count]->key = key; //strdup(key); +        section->data[section->data_count]->value = value; //strdup(value); +        section->data_count++; +    } else { +        struct INIData *data = ini_data_get(*ini, section_name, key); +        size_t value_len_old = strlen(data->value); +        size_t value_len = strlen(value); +        size_t value_len_new = value_len_old + value_len; +        /* +        char *value_tmp = NULL; +        value_tmp = realloc(data->value, value_len_new + 2); +        if (!value_tmp) { +            perror(__FUNCTION__ ); +            exit(1); +        } +        data->value = value_tmp; +         */ +        //strcat(data->value, " "); +        strcat(data->value, value); +    } +    return 0; +} + +void ini_section_record(struct INIFILE **ini, char *key) { +    struct INISection **tmp = realloc((*ini)->section, ((*ini)->section_count + 1) * sizeof((*ini)->section)); +    if (!tmp) { +        perror(__FUNCTION__); +        exit(1); +    } +    (*ini)->section = tmp; +    (*ini)->section[(*ini)->section_count] = calloc(1, sizeof(*(*ini)->section[0])); +    (*ini)->section[(*ini)->section_count]->key = strdup(key); +    (*ini)->section_count++; +} + +void ini_show(struct INIFILE *ini) { +    for (size_t x = 0; x < ini->section_count; x++) { +        printf("[%s]\n", ini->section[x]->key); +        for (size_t y = 0; y < ini->section[x]->data_count; y++) { +            printf("%s='%s'\n", ini->section[x]->data[y]->key, ini->section[x]->data[y]->value); +        } +        printf("\n"); +    } +} + +char *unquote(char *s) { +    int found = 0; +    if (startswith(s, "'") && endswith(s, "'")) { +        found = 1; +    } else if (startswith(s, "\"") && endswith(s, "\"")) { +        found = 1; +    } + +    if (found) { +        memmove(s, s + 1, strlen(s)); +        s[strlen(s) - 1] = '\0'; +    } +    return s; +} + +char *collapse_whitespace(char **s) { +    size_t len = strlen(*s); +    size_t i; +    for (i = 0; isblank((int)*s[i]); i++); +    memmove(*s, *s + i, strlen(*s)); +    if (i) { +        *s[len - i] = '\0'; +    } +    return *s; +} + +void ini_free(struct INIFILE **ini) { +    for (size_t section = 0; section < (*ini)->section_count; section++) { +        for (size_t data = 0; data < (*ini)->section[section]->data_count; data++) { +            if ((*ini)->section[section]->data[data]) { +                free((*ini)->section[section]->data[data]->key); +                free((*ini)->section[section]->data[data]->value); +                free((*ini)->section[section]->data[data]); +            } +        } +        free((*ini)->section[section]->data); +        free((*ini)->section[section]->key); +        free((*ini)->section[section]); +    } +    free((*ini)->section); +    free((*ini)); +} + +struct INIFILE *ini_open(const char *filename) { +    FILE *fp; +    char line[BUFSIZ] = {0}; +    char current_section[BUFSIZ] = {0}; +    char *key_last = NULL; +    struct INIFILE *ini = ini_init(); + +    ini_section_init(&ini); + +    // Create an implicit section. [default] does not need to be present in the INI config +    ini_section_record(&ini, "default"); +    strcpy(current_section, "default"); +    //ini_data_init(&ini, "default"); + +    // Open the configuration file for reading +    fp = fopen(filename, "r"); +    if (!fp) { +        perror(filename); +        exit(1); +    } + +    // Read file +    for (size_t i = 0; fgets(line, sizeof(line), fp) != NULL; i++) { +        // Find pointer to first comment character +        char *comment = strpbrk(line, ";#"); +        if (comment) { +            // Remove comment from line (standalone and inline comments) +            if (!(comment - line > 0 && (*(comment - 1) == '\\') || (*comment - 1) == '#')) { +                *comment = '\0'; +            } else { +                // Handle escaped comment characters. Remove the escape character '\' +                memmove(comment - 1, comment, strlen(comment)); +                comment[strlen(comment) - 1] = '\0'; +            } +        } + +        // Removing comments could have reduced the line's length, so calculate it now +        size_t len = strlen(line); + +        // Ignore empty lines +        if (!len || line[0] == '\n') { +            continue; +        } + +        // Test for section header: [string] +        if (startswith(line, "[")) { +            // Ignore default section because we already have an implicit one +            if (!strncmp(&line[1], "default", strlen("default"))) { +                continue; +            } + +            // Remove section ending: ']' +            line[strlen(line) - 2] = '\0'; + +            // Create new named section +            ini_section_record(&ini, &line[1]); +            //ini_data_init(&ini, &line[1]); + +            // Record the name of the section. This is used until another section is found. +            strcpy(current_section, &line[1]); +            continue; +        } + +        char *key = NULL; +        char *value = malloc(BUFSIZ); +        char *operator = strchr(line, '='); + +        // continuation line +        if (startswith(line, " ") || startswith(line, "\t")) { +            operator = NULL; +        } + +        if (operator) { +            size_t key_len = operator - line; +            key = strndup(line, key_len); +            key_last = key; +            strcpy(value, &operator[1]); +            value[strlen(value) - 1] = '\0'; +        } else if (!key && !strlen(value) && ! (startswith(line, " ") || startswith(line, "\t"))) { +            fprintf(stderr, "NO OPERATOR OR INDENT: %zu:'%s'\n", i, line); +            struct INISection *section = ini_section_search(&ini, current_section); +            struct INIData *data = NULL; +            //key = key_last; +            free(value); +            value = NULL; +        } else { +            struct INISection *section = ini_section_search(&ini, current_section); +            struct INIData *data = section->data[section->data_count - 1]; +            if (strlen(data->value)) { +                data->value[strlen(data->value) - 1] = '\n'; +            } +            key = key_last; +            strcpy(value, line); +            if (endswith(value, "\n")) { +                value[strlen(value) - 1] = '\n'; +            } +        } + +        // Store key value pair in section's data array +        if (key) { +            lstrip(key); +            strip(key); +            unquote(value); +            lstrip(value); +            ini_data_record(&ini, current_section, key, value); +        } +    } + +    return ini; +}
\ No newline at end of file | 
