Third-Party: Update inih
@@ -249,7 +249,7 @@ mGBA is Copyright © 2013 – 2020 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
mGBA contains the following third-party libraries: -- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 Ben Hoyt and used under a BSD 3-clause license. +- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 – 2020 Ben Hoyt and used under a BSD 3-clause license. - [blip-buf](https://code.google.com/archive/p/blip-buf), which is copyright © 2003 – 2009 Shay Green and used under a Lesser GNU Public License. - [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain. - [MurmurHash3](https://github.com/aappleby/smhasher) implementation by Austin Appleby, which is public domain.
@@ -1,20 +1,40 @@
+# inih (INI Not Invented Here) + +[![TravisCI Build](https://travis-ci.org/benhoyt/inih.svg)](https://travis-ci.org/benhoyt/inih) + **inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries. To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation. -You can also call `ini_parse_file()` to parse directly from a `FILE*` object, or `ini_parse_stream()` to parse using a custom reader to implement string-based or other custom I/O ([see example code](https://github.com/benhoyt/inih/blob/master/examples/ini_buffer.c)). +You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O. Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros. ## Compile-time options ## +You can control various aspects of inih using preprocessor defines: + +### Syntax options ### + * **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`. * **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`. - * **Stack vs heap:** By default, inih allocates its line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`. + * **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`. + * **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`. + * **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL. + +### Parsing options ### + * **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`. - * **Maximum line length:** The default maximum line length is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. + * **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`. + * **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL. + +### Memory options ### + * **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`. + * **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL). + * **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes. + * **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`. ## Simple example in C ##@@ -102,3 +122,36 @@ Some differences between inih and Python's [ConfigParser](http://docs.python.org/library/configparser.html) standard library module:
* INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error. * Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser). + + +## Platform-specific notes ## + +* Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling. + +## Meson notes ## + +* The `meson.build` file is not required to use or compile inih, its main purpose is for distributions. +* By default Meson only creates a static library for inih, but Meson can be used to configure this behavior: +* with `-Ddefault_library=shared` a shared library is build. +* with `-Ddistro_install=true` the library will be installed with the header and a pkg-config entry, you may want to set `-Ddefault_library=shared` when using this. +* with `-Dwith_INIReader` you can build (and install if selected) the C++ library. +* all compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`. +* If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`. +* In case you use inih as a subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables. + +## Building from vcpkg ## + +You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install inih + +The inih port in vcpkg is kept up to date by microsoft team members and community contributors. +If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Related links ## + +* [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager)
@@ -1,5 +1,9 @@
/* inih -- simple .INI file parser +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info:@@ -7,7 +11,7 @@ https://github.com/benhoyt/inih
*/ -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif@@ -24,6 +28,12 @@
#define MAX_SECTION 128 #define MAX_NAME 128 +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + /* Strip whitespace chars off end of given string, in place. Return s. */ static char* rstrip(char* s) {@@ -41,24 +51,35 @@ s++;
return (char*)s; } -/* Return pointer to first char c or ';' comment in given string, or pointer to - null at end of string if neither found. ';' must be prefixed by a whitespace - character to register as a comment. */ -static char* find_char_or_comment(const char* s, char c) +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to NUL at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) { - int was_whitespace = 0; - while (*s && *s != c && !(was_whitespace && *s == ';')) { - was_whitespace = isspace((unsigned char)(*s)); +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); s++; } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif return (char*)s; } -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +/* Similar to strncpy, but ensures dest (size bytes) is + NUL-terminated, and doesn't pad with NULs. */ static char* strncpy0(char* dest, const char* src, size_t size) { - strncpy(dest, src, size); - dest[size - 1] = '\0'; + /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ + size_t i; + for (i = 0; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + dest[i] = '\0'; return dest; }@@ -69,8 +90,14 @@ {
/* Uses a fair bit of stack (use heap instead if you need to) */ #if INI_USE_STACK char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; #else char* line; + size_t max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC && !INI_USE_STACK + char* new_line; + size_t offset; #endif char section[MAX_SECTION] = ""; char prev_name[MAX_NAME] = "";@@ -83,14 +110,40 @@ int lineno = 0;
int error = 0; #if !INI_USE_STACK - line = (char*)malloc(INI_MAX_LINE); + line = (char*)malloc(INI_INITIAL_ALLOC); if (!line) { return -2; } #endif +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + /* Scan through stream line by line */ - while (reader(line, INI_MAX_LINE, stream) != NULL) { + while (reader(line, (int)max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC && !INI_USE_STACK + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = realloc(line, max_line); + if (!new_line) { + free(line); + return -2; + } + line = new_line; + if (reader(line + offset, (int)(max_line - offset), stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + lineno++; start = line;@@ -103,53 +156,64 @@ }
#endif start = lskip(rstrip(start)); - if (*start == ';' || *start == '#') { - /* Per Python ConfigParser, allow '#' comments at start of line */ + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ } #if INI_ALLOW_MULTILINE else if (*prev_name && *start && start > line) { - /* Non-black line with leading whitespace, treat as continuation - of previous name's value (as per Python ConfigParser). */ - if (!handler(user, section, prev_name, start) && !error) + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) error = lineno; } #endif else if (*start == '[') { /* A "[section]" line */ - end = find_char_or_comment(start + 1, ']'); + end = find_chars_or_comment(start + 1, "]"); if (*end == ']') { *end = '\0'; strncpy0(section, start + 1, sizeof(section)); *prev_name = '\0'; +#if INI_CALL_HANDLER_ON_NEW_SECTION + if (!HANDLER(user, section, NULL, NULL) && !error) + error = lineno; +#endif } else if (!error) { /* No ']' found on section line */ error = lineno; } } - else if (*start && *start != ';') { + else if (*start) { /* Not a comment, must be a name[=:]value pair */ - end = find_char_or_comment(start, '='); - if (*end != '=') { - end = find_char_or_comment(start, ':'); - } + end = find_chars_or_comment(start, "=:"); if (*end == '=' || *end == ':') { *end = '\0'; name = rstrip(start); - value = lskip(end + 1); - end = find_char_or_comment(value, '\0'); - if (*end == ';') + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) *end = '\0'; +#endif + value = lskip(value); rstrip(value); /* Valid name[=:]value pair found, call handler */ strncpy0(prev_name, name, sizeof(prev_name)); - if (!handler(user, section, name, value) && !error) + if (!HANDLER(user, section, name, value) && !error) error = lineno; } else if (!error) { /* No '=' or ':' found on name[=:]value line */ +#if INI_ALLOW_NO_VALUE + *end = '\0'; + name = rstrip(start); + if (!HANDLER(user, section, name, NULL) && !error) + error = lineno; +#else error = lineno; +#endif } }@@ -185,3 +249,40 @@ error = ini_parse_file(file, handler, user);
fclose(file); return error; } + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +}
@@ -1,5 +1,9 @@
/* inih -- simple .INI file parser +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info:@@ -17,9 +21,20 @@ #endif
#include <stdio.h> +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + /* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value); +#endif /* Typedef for prototype of fgets-style reader function. */ typedef char* (*ini_reader)(char* str, int num, void* stream);@@ -27,7 +42,7 @@
/* Parse given INI-style file. May have [section]s, name=value pairs (whitespace stripped), and comments starting with ';' (semicolon). Section is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's ConfigParser. + pairs are also supported as a concession to Python's configparser. For each name=value pair parsed, call handler function with given user pointer as well as section, name, and value (data only valid for duration@@ -44,36 +59,86 @@ close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user); /* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O. */ + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user); +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +int ini_parse_string(const char* string, ini_handler handler, void* user); + /* Nonzero to allow multi-line value parsing, in the style of Python's - ConfigParser. If allowed, ini_parse() will call the handler with the same + configparser. If allowed, ini_parse() will call the handler with the same name for each subsequent line parsed. */ #ifndef INI_ALLOW_MULTILINE #define INI_ALLOW_MULTILINE 1 #endif /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See http://code.google.com/p/inih/issues/detail?id=21 */ + the file. See https://github.com/benhoyt/inih/issues/21 */ #ifndef INI_ALLOW_BOM #define INI_ALLOW_BOM 1 #endif -/* Nonzero to use stack, zero to use heap (malloc/free). */ +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ #ifndef INI_USE_STACK #define INI_USE_STACK 1 #endif +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + /* Stop parsing on first error (default is to keep parsing). */ #ifndef INI_STOP_ON_FIRST_ERROR #define INI_STOP_ON_FIRST_ERROR 0 #endif -/* Maximum line length for any line in INI file. */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 +/* Nonzero to call the handler at the start of each new section (with + name and value NULL). Default is to only call the handler on + each name=value pair. */ +#ifndef INI_CALL_HANDLER_ON_NEW_SECTION +#define INI_CALL_HANDLER_ON_NEW_SECTION 0 +#endif + +/* Nonzero to allow a name without a value (no '=' or ':' on the line) and + call the handler with value NULL in this case. Default is to treat + no-value lines as an error. */ +#ifndef INI_ALLOW_NO_VALUE +#define INI_ALLOW_NO_VALUE 0 #endif #ifdef __cplusplus