Blame src/ini.cpp

Packit Service 21b5d1
// Read an INI file into easy-to-access name/value pairs.
Packit Service 21b5d1
Packit Service 21b5d1
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
Packit Service 21b5d1
// Go to the project home page for more info:
Packit Service 21b5d1
//
Packit Service 21b5d1
// https://github.com/benhoyt/inih
Packit Service 21b5d1
Packit Service 21b5d1
#include "config.h"
Packit Service 21b5d1
#include "ini.hpp"
Packit Service 21b5d1
Packit Service 21b5d1
#include <algorithm>
Packit Service 21b5d1
#include <cctype>
Packit Service 21b5d1
#include <cstdlib>
Packit Service 21b5d1
Packit Service 21b5d1
using std::string;
Packit Service 21b5d1
using namespace Exiv2;
Packit Service 21b5d1
Packit Service 21b5d1
/* inih -- simple .INI file parser
Packit Service 21b5d1
Packit Service 21b5d1
inih is released under the New BSD license (see LICENSE.txt). Go to the project
Packit Service 21b5d1
home page for more info:
Packit Service 21b5d1
Packit Service 21b5d1
https://github.com/benhoyt/inih
Packit Service 21b5d1
Packit Service 21b5d1
*/
Packit Service 21b5d1
Packit Service 21b5d1
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
Packit Service 21b5d1
#define _CRT_SECURE_NO_WARNINGS
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#include <stdio.h>
Packit Service 21b5d1
#include <ctype.h>
Packit Service 21b5d1
#include <string.h>
Packit Service 21b5d1
Packit Service 21b5d1
#if !INI_USE_STACK
Packit Service 21b5d1
#include <cstdlib>
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
#define MAX_SECTION 50
Packit Service 21b5d1
#define MAX_NAME 50
Packit Service 21b5d1
Packit Service 21b5d1
/* Strip whitespace chars off end of given string, in place. Return s. */
Packit Service 21b5d1
static char* rstrip(char* s)
Packit Service 21b5d1
{
Packit Service 21b5d1
    char* p = s + strlen(s);
Packit Service 21b5d1
    while (p > s && isspace((unsigned char)(*--p)))
Packit Service 21b5d1
        *p = '\0';
Packit Service 21b5d1
    return s;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* Return pointer to first non-whitespace char in given string. */
Packit Service 21b5d1
static char* lskip(const char* s)
Packit Service 21b5d1
{
Packit Service 21b5d1
    while (*s && isspace((unsigned char)(*s)))
Packit Service 21b5d1
        s++;
Packit Service 21b5d1
    return (char*)s;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* Return pointer to first char (of chars) or inline comment in given string,
Packit Service 21b5d1
   or pointer to null at end of string if neither found. Inline comment must
Packit Service 21b5d1
   be prefixed by a whitespace character to register as a comment. */
Packit Service 21b5d1
static char* find_chars_or_comment(const char* s, const char* chars)
Packit Service 21b5d1
{
Packit Service 21b5d1
#if INI_ALLOW_INLINE_COMMENTS
Packit Service 21b5d1
    int was_space = 0;
Packit Service 21b5d1
    while (*s && (!chars || !strchr(chars, *s)) &&
Packit Service 21b5d1
           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
Packit Service 21b5d1
        was_space = isspace((unsigned char)(*s));
Packit Service 21b5d1
        s++;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#else
Packit Service 21b5d1
    while (*s && (!chars || !strchr(chars, *s))) {
Packit Service 21b5d1
        s++;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    return (char*)s;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
Packit Service 21b5d1
static char* strncpy0(char* dest, const char* src, size_t size)
Packit Service 21b5d1
{
Packit Service 21b5d1
    strncpy(dest, src, size);
Packit Service 21b5d1
    dest[size - 1] = '\0';
Packit Service 21b5d1
    return dest;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* See documentation in header file. */
Packit Service 21b5d1
int Exiv2::ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
Packit Service 21b5d1
                     void* user)
Packit Service 21b5d1
{
Packit Service 21b5d1
    /* Uses a fair bit of stack (use heap instead if you need to) */
Packit Service 21b5d1
#if INI_USE_STACK
Packit Service 21b5d1
    char line[INI_MAX_LINE];
Packit Service 21b5d1
#else
Packit Service 21b5d1
    char* line;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    char section[MAX_SECTION] = "";
Packit Service 21b5d1
    char prev_name[MAX_NAME] = "";
Packit Service 21b5d1
Packit Service 21b5d1
    char* start;
Packit Service 21b5d1
    char* end;
Packit Service 21b5d1
    char* name;
Packit Service 21b5d1
    char* value;
Packit Service 21b5d1
    int lineno = 0;
Packit Service 21b5d1
    int error = 0;
Packit Service 21b5d1
Packit Service 21b5d1
#if !INI_USE_STACK
Packit Service 21b5d1
    line = (char*)malloc(INI_MAX_LINE);
Packit Service 21b5d1
    if (!line) {
Packit Service 21b5d1
        return -2;
Packit Service 21b5d1
    }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    /* Scan through stream line by line */
Packit Service 21b5d1
    while (reader(line, INI_MAX_LINE, stream) != NULL) {
Packit Service 21b5d1
        lineno++;
Packit Service 21b5d1
Packit Service 21b5d1
        start = line;
Packit Service 21b5d1
#if INI_ALLOW_BOM
Packit Service 21b5d1
        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
Packit Service 21b5d1
                           (unsigned char)start[1] == 0xBB &&
Packit Service 21b5d1
                           (unsigned char)start[2] == 0xBF) {
Packit Service 21b5d1
            start += 3;
Packit Service 21b5d1
        }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        start = lskip(rstrip(start));
Packit Service 21b5d1
Packit Service 21b5d1
        if (*start == ';' || *start == '#') {
Packit Service 21b5d1
            /* Per Python configparser, allow both ; and # comments at the
Packit Service 21b5d1
               start of a line */
Packit Service 21b5d1
        }
Packit Service 21b5d1
#if INI_ALLOW_MULTILINE
Packit Service 21b5d1
        else if (*prev_name && *start && start > line) {
Packit Service 21b5d1
            /* Non-blank line with leading whitespace, treat as continuation
Packit Service 21b5d1
               of previous name's value (as per Python configparser). */
Packit Service 21b5d1
            if (!handler(user, section, prev_name, start) && !error)
Packit Service 21b5d1
                error = lineno;
Packit Service 21b5d1
        }
Packit Service 21b5d1
#endif
Packit Service 21b5d1
        else if (*start == '[') {
Packit Service 21b5d1
            /* A "[section]" line */
Packit Service 21b5d1
            end = find_chars_or_comment(start + 1, "]");
Packit Service 21b5d1
            if (*end == ']') {
Packit Service 21b5d1
                *end = '\0';
Packit Service 21b5d1
                strncpy0(section, start + 1, sizeof(section));
Packit Service 21b5d1
                *prev_name = '\0';
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else if (!error) {
Packit Service 21b5d1
                /* No ']' found on section line */
Packit Service 21b5d1
                error = lineno;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
        else if (*start) {
Packit Service 21b5d1
            /* Not a comment, must be a name[=:]value pair */
Packit Service 21b5d1
            end = find_chars_or_comment(start, "=:");
Packit Service 21b5d1
            if (*end == '=' || *end == ':') {
Packit Service 21b5d1
                *end = '\0';
Packit Service 21b5d1
                name = rstrip(start);
Packit Service 21b5d1
                value = lskip(end + 1);
Packit Service 21b5d1
#if INI_ALLOW_INLINE_COMMENTS
Packit Service 21b5d1
                end = find_chars_or_comment(value, NULL);
Packit Service 21b5d1
                if (*end)
Packit Service 21b5d1
                    *end = '\0';
Packit Service 21b5d1
#endif
Packit Service 21b5d1
                rstrip(value);
Packit Service 21b5d1
Packit Service 21b5d1
                /* Valid name[=:]value pair found, call handler */
Packit Service 21b5d1
                strncpy0(prev_name, name, sizeof(prev_name));
Packit Service 21b5d1
                if (!handler(user, section, name, value) && !error)
Packit Service 21b5d1
                    error = lineno;
Packit Service 21b5d1
            }
Packit Service 21b5d1
            else if (!error) {
Packit Service 21b5d1
                /* No '=' or ':' found on name[=:]value line */
Packit Service 21b5d1
                error = lineno;
Packit Service 21b5d1
            }
Packit Service 21b5d1
        }
Packit Service 21b5d1
Packit Service 21b5d1
#if INI_STOP_ON_FIRST_ERROR
Packit Service 21b5d1
        if (error)
Packit Service 21b5d1
            break;
Packit Service 21b5d1
#endif
Packit Service 21b5d1
    }
Packit Service 21b5d1
Packit Service 21b5d1
#if !INI_USE_STACK
Packit Service 21b5d1
    free(line);
Packit Service 21b5d1
#endif
Packit Service 21b5d1
Packit Service 21b5d1
    return error;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* See documentation in header file. */
Packit Service 21b5d1
int Exiv2::ini_parse_file(FILE* file, ini_handler handler, void* user)
Packit Service 21b5d1
{
Packit Service 21b5d1
    return Exiv2::ini_parse_stream((ini_reader)fgets, file, handler, user);
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
/* See documentation in header file. */
Packit Service 21b5d1
int Exiv2::ini_parse(const char* filename, ini_handler handler, void* user)
Packit Service 21b5d1
{
Packit Service 21b5d1
    FILE* file;
Packit Service 21b5d1
    int error;
Packit Service 21b5d1
Packit Service 21b5d1
    file = fopen(filename, "r");
Packit Service 21b5d1
    if (!file)
Packit Service 21b5d1
        return -1;
Packit Service 21b5d1
    error = Exiv2::ini_parse_file(file, handler, user);
Packit Service 21b5d1
    fclose(file);
Packit Service 21b5d1
    return error;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
INIReader::INIReader(const std::string &filename)
Packit Service 21b5d1
{
Packit Service 21b5d1
    _error = ini_parse(filename.c_str(), ValueHandler, this);
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
int INIReader::ParseError()
Packit Service 21b5d1
{
Packit Service 21b5d1
    return _error;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
string INIReader::Get(string section, string name, string default_value)
Packit Service 21b5d1
{
Packit Service 21b5d1
    string key = MakeKey(section, name);
Packit Service 21b5d1
    return _values.count(key) ? _values[key] : default_value;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
long INIReader::GetInteger(string section, string name, long default_value)
Packit Service 21b5d1
{
Packit Service 21b5d1
    string valstr = Get(section, name, "");
Packit Service 21b5d1
    const char* value = valstr.c_str();
Packit Service 21b5d1
    char* end;
Packit Service 21b5d1
    // This parses "1234" (decimal) and also "0x4D2" (hex)
Packit Service 21b5d1
    long n = strtol(value, &end, 0);
Packit Service 21b5d1
    return end > value ? n : default_value;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
double INIReader::GetReal(string section, string name, double default_value)
Packit Service 21b5d1
{
Packit Service 21b5d1
    string valstr = Get(section, name, "");
Packit Service 21b5d1
    const char* value = valstr.c_str();
Packit Service 21b5d1
    char* end;
Packit Service 21b5d1
    double n = strtod(value, &end;;
Packit Service 21b5d1
    return end > value ? n : default_value;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
bool INIReader::GetBoolean(string section, string name, bool default_value)
Packit Service 21b5d1
{
Packit Service 21b5d1
    string valstr = Get(section, name, "");
Packit Service 21b5d1
    // Convert to lower case to make string comparisons case-insensitive
Packit Service 21b5d1
    std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
Packit Service 21b5d1
    if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
Packit Service 21b5d1
        return true;
Packit Service 21b5d1
    else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
Packit Service 21b5d1
        return false;
Packit Service 21b5d1
    else
Packit Service 21b5d1
        return default_value;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
string INIReader::MakeKey(string section, string name)
Packit Service 21b5d1
{
Packit Service 21b5d1
    string key = section + "=" + name;
Packit Service 21b5d1
    // Convert to lower case to make section/name lookups case-insensitive
Packit Service 21b5d1
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
Packit Service 21b5d1
    return key;
Packit Service 21b5d1
}
Packit Service 21b5d1
Packit Service 21b5d1
int INIReader::ValueHandler(void* user, const char* section, const char* name,
Packit Service 21b5d1
                            const char* value)
Packit Service 21b5d1
{
Packit Service 21b5d1
    INIReader* reader = (INIReader*)user;
Packit Service 21b5d1
    string key = MakeKey(section, name);
Packit Service 21b5d1
    if (reader->_values[key].size() > 0)
Packit Service 21b5d1
        reader->_values[key] += "\n";
Packit Service 21b5d1
    reader->_values[key] += value;
Packit Service 21b5d1
    return 1;
Packit Service 21b5d1
}
Packit Service 21b5d1