Blame lib/inih/ini.c

Packit Service 4684c1
/* inih -- simple .INI file parser
Packit Service 4684c1
Packit Service 4684c1
inih is released under the New BSD license (see LICENSE.txt). Go to the project
Packit Service 4684c1
home page for more info:
Packit Service 4684c1
Packit Service 4684c1
https://github.com/benhoyt/inih
Packit Service 4684c1
Packit Service 4684c1
*/
Packit Service 4684c1
Packit Service 4684c1
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
Packit Service 4684c1
#define _CRT_SECURE_NO_WARNINGS
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
#include <stdio.h>
Packit Service 4684c1
#include <ctype.h>
Packit Service 4684c1
#include <string.h>
Packit Service 4684c1
Packit Service 4684c1
#include "ini.h"
Packit Service 4684c1
Packit Service 4684c1
#if !INI_USE_STACK
Packit Service 4684c1
#include <stdlib.h>
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
#define MAX_SECTION 50
Packit Service 4684c1
#define MAX_NAME 50
Packit Service 4684c1
Packit Service 4684c1
/* Used by ini_parse_string() to keep track of string parsing state. */
Packit Service 4684c1
typedef struct {
Packit Service 4684c1
    const char* ptr;
Packit Service 4684c1
    size_t num_left;
Packit Service 4684c1
} ini_parse_string_ctx;
Packit Service 4684c1
Packit Service 4684c1
/* Strip whitespace chars off end of given string, in place. Return s. */
Packit Service 4684c1
static char* rstrip(char* s)
Packit Service 4684c1
{
Packit Service 4684c1
    char* p = s + strlen(s);
Packit Service 4684c1
    while (p > s && isspace((unsigned char)(*--p)))
Packit Service 4684c1
        *p = '\0';
Packit Service 4684c1
    return s;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Return pointer to first non-whitespace char in given string. */
Packit Service 4684c1
static char* lskip(const char* s)
Packit Service 4684c1
{
Packit Service 4684c1
    while (*s && isspace((unsigned char)(*s)))
Packit Service 4684c1
        s++;
Packit Service 4684c1
    return (char*)s;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Return pointer to first char (of chars) or inline comment in given string,
Packit Service 4684c1
   or pointer to null at end of string if neither found. Inline comment must
Packit Service 4684c1
   be prefixed by a whitespace character to register as a comment. */
Packit Service 4684c1
static char* find_chars_or_comment(const char* s, const char* chars)
Packit Service 4684c1
{
Packit Service 4684c1
#if INI_ALLOW_INLINE_COMMENTS
Packit Service 4684c1
    int was_space = 0;
Packit Service 4684c1
    while (*s && (!chars || !strchr(chars, *s)) &&
Packit Service 4684c1
           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
Packit Service 4684c1
        was_space = isspace((unsigned char)(*s));
Packit Service 4684c1
        s++;
Packit Service 4684c1
    }
Packit Service 4684c1
#else
Packit Service 4684c1
    while (*s && (!chars || !strchr(chars, *s))) {
Packit Service 4684c1
        s++;
Packit Service 4684c1
    }
Packit Service 4684c1
#endif
Packit Service 4684c1
    return (char*)s;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
Packit Service 4684c1
static char* strncpy0(char* dest, const char* src, size_t size)
Packit Service 4684c1
{
Packit Service 4684c1
    strncpy(dest, src, size - 1);
Packit Service 4684c1
    dest[size - 1] = '\0';
Packit Service 4684c1
    return dest;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* See documentation in header file. */
Packit Service 4684c1
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
Packit Service 4684c1
                     void* user)
Packit Service 4684c1
{
Packit Service 4684c1
    /* Uses a fair bit of stack (use heap instead if you need to) */
Packit Service 4684c1
#if INI_USE_STACK
Packit Service 4684c1
    char line[INI_MAX_LINE];
Packit Service 4684c1
    int max_line = INI_MAX_LINE;
Packit Service 4684c1
#else
Packit Service 4684c1
    char* line;
Packit Service 4684c1
    int max_line = INI_INITIAL_ALLOC;
Packit Service 4684c1
#endif
Packit Service 4684c1
#if INI_ALLOW_REALLOC && !INI_USE_STACK
Packit Service 4684c1
    char* new_line;
Packit Service 4684c1
    int offset;
Packit Service 4684c1
#endif
Packit Service 4684c1
    char section[MAX_SECTION] = "";
Packit Service 4684c1
    char prev_name[MAX_NAME] = "";
Packit Service 4684c1
Packit Service 4684c1
    char* start;
Packit Service 4684c1
    char* end;
Packit Service 4684c1
    char* name;
Packit Service 4684c1
    char* value;
Packit Service 4684c1
    int lineno = 0;
Packit Service 4684c1
    int error = 0;
Packit Service 4684c1
Packit Service 4684c1
#if !INI_USE_STACK
Packit Service 4684c1
    line = (char*)malloc(INI_INITIAL_ALLOC);
Packit Service 4684c1
    if (!line) {
Packit Service 4684c1
        return -2;
Packit Service 4684c1
    }
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
#if INI_HANDLER_LINENO
Packit Service 4684c1
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
Packit Service 4684c1
#else
Packit Service 4684c1
#define HANDLER(u, s, n, v) handler(u, s, n, v)
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
    /* Scan through stream line by line */
Packit Service 4684c1
    while (reader(line, max_line, stream) != NULL) {
Packit Service 4684c1
#if INI_ALLOW_REALLOC && !INI_USE_STACK
Packit Service 4684c1
        offset = strlen(line);
Packit Service 4684c1
        while (offset == max_line - 1 && line[offset - 1] != '\n') {
Packit Service 4684c1
            max_line *= 2;
Packit Service 4684c1
            if (max_line > INI_MAX_LINE)
Packit Service 4684c1
                max_line = INI_MAX_LINE;
Packit Service 4684c1
            new_line = realloc(line, max_line);
Packit Service 4684c1
            if (!new_line) {
Packit Service 4684c1
                free(line);
Packit Service 4684c1
                return -2;
Packit Service 4684c1
            }
Packit Service 4684c1
            line = new_line;
Packit Service 4684c1
            if (reader(line + offset, max_line - offset, stream) == NULL)
Packit Service 4684c1
                break;
Packit Service 4684c1
            if (max_line >= INI_MAX_LINE)
Packit Service 4684c1
                break;
Packit Service 4684c1
            offset += strlen(line + offset);
Packit Service 4684c1
        }
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
        lineno++;
Packit Service 4684c1
Packit Service 4684c1
        start = line;
Packit Service 4684c1
#if INI_ALLOW_BOM
Packit Service 4684c1
        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
Packit Service 4684c1
                           (unsigned char)start[1] == 0xBB &&
Packit Service 4684c1
                           (unsigned char)start[2] == 0xBF) {
Packit Service 4684c1
            start += 3;
Packit Service 4684c1
        }
Packit Service 4684c1
#endif
Packit Service 4684c1
        start = lskip(rstrip(start));
Packit Service 4684c1
Packit Service 4684c1
        if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
Packit Service 4684c1
            /* Start-of-line comment */
Packit Service 4684c1
        }
Packit Service 4684c1
#if INI_ALLOW_MULTILINE
Packit Service 4684c1
        else if (*prev_name && *start && start > line) {
Packit Service 4684c1
            /* Non-blank line with leading whitespace, treat as continuation
Packit Service 4684c1
               of previous name's value (as per Python configparser). */
Packit Service 4684c1
            if (!HANDLER(user, section, prev_name, start) && !error)
Packit Service 4684c1
                error = lineno;
Packit Service 4684c1
        }
Packit Service 4684c1
#endif
Packit Service 4684c1
        else if (*start == '[') {
Packit Service 4684c1
            /* A "[section]" line */
Packit Service 4684c1
            end = find_chars_or_comment(start + 1, "]");
Packit Service 4684c1
            if (*end == ']') {
Packit Service 4684c1
                *end = '\0';
Packit Service 4684c1
                strncpy0(section, start + 1, sizeof(section));
Packit Service 4684c1
                *prev_name = '\0';
Packit Service 4684c1
            }
Packit Service 4684c1
            else if (!error) {
Packit Service 4684c1
                /* No ']' found on section line */
Packit Service 4684c1
                error = lineno;
Packit Service 4684c1
            }
Packit Service 4684c1
        }
Packit Service 4684c1
        else if (*start) {
Packit Service 4684c1
            /* Not a comment, must be a name[=:]value pair */
Packit Service 4684c1
            end = find_chars_or_comment(start, "=:");
Packit Service 4684c1
            if (*end == '=' || *end == ':') {
Packit Service 4684c1
                *end = '\0';
Packit Service 4684c1
                name = rstrip(start);
Packit Service 4684c1
                value = end + 1;
Packit Service 4684c1
#if INI_ALLOW_INLINE_COMMENTS
Packit Service 4684c1
                end = find_chars_or_comment(value, NULL);
Packit Service 4684c1
                if (*end)
Packit Service 4684c1
                    *end = '\0';
Packit Service 4684c1
#endif
Packit Service 4684c1
                value = lskip(value);
Packit Service 4684c1
                rstrip(value);
Packit Service 4684c1
Packit Service 4684c1
                /* Valid name[=:]value pair found, call handler */
Packit Service 4684c1
                strncpy0(prev_name, name, sizeof(prev_name));
Packit Service 4684c1
                if (!HANDLER(user, section, name, value) && !error)
Packit Service 4684c1
                    error = lineno;
Packit Service 4684c1
            }
Packit Service 4684c1
            else if (!error) {
Packit Service 4684c1
                /* No '=' or ':' found on name[=:]value line */
Packit Service 4684c1
                error = lineno;
Packit Service 4684c1
            }
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
#if INI_STOP_ON_FIRST_ERROR
Packit Service 4684c1
        if (error)
Packit Service 4684c1
            break;
Packit Service 4684c1
#endif
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
#if !INI_USE_STACK
Packit Service 4684c1
    free(line);
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
    return error;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* See documentation in header file. */
Packit Service 4684c1
int ini_parse_file(FILE* file, ini_handler handler, void* user)
Packit Service 4684c1
{
Packit Service 4684c1
    return ini_parse_stream((ini_reader)fgets, file, handler, user);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* See documentation in header file. */
Packit Service 4684c1
int ini_parse(const char* filename, ini_handler handler, void* user)
Packit Service 4684c1
{
Packit Service 4684c1
    FILE* file;
Packit Service 4684c1
    int error;
Packit Service 4684c1
Packit Service 4684c1
    file = fopen(filename, "r");
Packit Service 4684c1
    if (!file)
Packit Service 4684c1
        return -1;
Packit Service 4684c1
    error = ini_parse_file(file, handler, user);
Packit Service 4684c1
    fclose(file);
Packit Service 4684c1
    return error;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* An ini_reader function to read the next line from a string buffer. This
Packit Service 4684c1
   is the fgets() equivalent used by ini_parse_string(). */
Packit Service 4684c1
static char* ini_reader_string(char* str, int num, void* stream) {
Packit Service 4684c1
    ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
Packit Service 4684c1
    const char* ctx_ptr = ctx->ptr;
Packit Service 4684c1
    size_t ctx_num_left = ctx->num_left;
Packit Service 4684c1
    char* strp = str;
Packit Service 4684c1
    char c;
Packit Service 4684c1
Packit Service 4684c1
    if (ctx_num_left == 0 || num < 2)
Packit Service 4684c1
        return NULL;
Packit Service 4684c1
Packit Service 4684c1
    while (num > 1 && ctx_num_left != 0) {
Packit Service 4684c1
        c = *ctx_ptr++;
Packit Service 4684c1
        ctx_num_left--;
Packit Service 4684c1
        *strp++ = c;
Packit Service 4684c1
        if (c == '\n')
Packit Service 4684c1
            break;
Packit Service 4684c1
        num--;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
    *strp = '\0';
Packit Service 4684c1
    ctx->ptr = ctx_ptr;
Packit Service 4684c1
    ctx->num_left = ctx_num_left;
Packit Service 4684c1
    return str;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* See documentation in header file. */
Packit Service 4684c1
int ini_parse_string(const char* string, ini_handler handler, void* user) {
Packit Service 4684c1
    ini_parse_string_ctx ctx;
Packit Service 4684c1
Packit Service 4684c1
    ctx.ptr = string;
Packit Service 4684c1
    ctx.num_left = strlen(string);
Packit Service 4684c1
    return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
Packit Service 4684c1
                            user);
Packit Service 4684c1
}