diff --git a/.github/workflows/stable-compilation.yml b/.github/workflows/stable-compilation.yml index cb7393878..eb8293ef1 100644 --- a/.github/workflows/stable-compilation.yml +++ b/.github/workflows/stable-compilation.yml @@ -36,7 +36,7 @@ jobs: apt-get update apt-get install -y --no-install-recommends --no-install-suggests \ ca-certificates build-essential cmake ninja-build git \ - libicu-dev libexpat1-dev + libicu-dev libexpat1-dev libinih-dev - name: Clone Repository uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index a639c80da..fc831d1d7 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,11 @@ build/ # IntelliJ / CLion .idea/ +# VS Code +.vscode/ +.cache/ +compile_commands.json + # doxygen generated files /doc/ @@ -72,3 +77,4 @@ test_runner* # distribution archives liblcf-*.tar.* + diff --git a/CMakeLists.txt b/CMakeLists.txt index 79df46b5e..60096a8d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ set(LCF_SOURCES src/dbarray.cpp src/dbstring_struct.cpp src/encoder.cpp - src/ini.cpp src/inireader.cpp src/ldb_equipment.cpp src/ldb_eventcommand.cpp @@ -201,7 +200,6 @@ set(LCF_HEADERS src/lcf/encoder.h src/lcf/enum_tags.h src/lcf/flag_set.h - src/lcf/ini.h src/lcf/inireader.h src/lcf/ldb/reader.h src/lcf/lmt/reader.h @@ -343,6 +341,10 @@ set_property(TARGET lcf PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) # Name of the exported library set_property(TARGET lcf PROPERTY EXPORT_NAME liblcf) +# inih +find_package(inih REQUIRED) +target_link_libraries(lcf inih::inih) + # icu set(LCF_SUPPORT_ICU 0) if(LIBLCF_WITH_ICU) @@ -380,7 +382,6 @@ set_property(TARGET lcf PROPERTY SOVERSION 0) # installation if(LIBLCF_ENABLE_INSTALL) - # pkg-config file generation set(LCF_LIBDIR ${CMAKE_INSTALL_LIBDIR}) if(IS_ABSOLUTE ${LCF_LIBDIR}) @@ -441,6 +442,7 @@ if(LIBLCF_ENABLE_INSTALL) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/liblcf-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/liblcf-config-version.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/builds/cmake/Modules/Findinih.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liblcf ) diff --git a/Makefile.am b/Makefile.am index fe10c1e74..d7fc97df5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,9 +34,11 @@ liblcf_la_CXXFLAGS = \ -std=gnu++17 \ -fno-math-errno \ $(AM_CXXFLAGS) \ + $(INIH_CFLAGS) \ $(EXPAT_CFLAGS) \ $(ICU_CFLAGS) liblcf_la_LIBADD = \ + $(INIH_LIBS) \ $(EXPAT_LIBS) \ $(ICU_LIBS) liblcf_la_LDFLAGS = \ @@ -46,7 +48,6 @@ liblcf_la_SOURCES = \ src/dbarray.cpp \ src/dbstring_struct.cpp \ src/encoder.cpp \ - src/ini.cpp \ src/inireader.cpp \ src/ldb_equipment.cpp \ src/ldb_eventcommand.cpp \ @@ -219,7 +220,6 @@ lcfinclude_HEADERS = \ src/lcf/encoder.h \ src/lcf/enum_tags.h \ src/lcf/flag_set.h \ - src/lcf/ini.h \ src/lcf/inireader.h \ src/lcf/log_handler.h \ src/lcf/reader_lcf.h \ @@ -347,10 +347,12 @@ test_runner_CPPFLAGS = \ test_runner_CXXFLAGS = \ -std=gnu++17 \ -DDOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING=1 \ + $(INIH_CXXFLAGS) \ $(EXPAT_CXXFLAGS) \ $(ICU_CXXFLAGS) test_runner_LDADD = \ liblcf.la \ + $(INIH_LIBS) \ $(EXPAT_LIBS) \ $(ICU_LIBS) test_runner_LDFLAGS = -no-install diff --git a/README.md b/README.md index 41ada9367..bc0359680 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Documentation is available at the documentation wiki: https://wiki.easyrpg.org ## Requirements +- [inih] for INI file reading. (required) - [Expat] for XML reading support. - [ICU] for character encoding detection and conversion (recommended). diff --git a/builds/cmake/Modules/Findinih.cmake b/builds/cmake/Modules/Findinih.cmake new file mode 100644 index 000000000..b919f9202 --- /dev/null +++ b/builds/cmake/Modules/Findinih.cmake @@ -0,0 +1,63 @@ +#.rst: +# Findinih +# -------- +# +# Find the inih Library +# +# Imported Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following :prop_tgt:`IMPORTED` targets: +# +# ``inih::inih`` +# The ``inih`` library, if found. +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# ``INIH_INCLUDE_DIRS`` +# where to find inih headers. +# ``INIH_LIBRARIES`` +# the libraries to link against to use inih. +# ``INIH_FOUND`` +# true if the inih headers and libraries were found. + +find_package(PkgConfig QUIET) + +pkg_check_modules(PC_INIH QUIET libinih) + +# Look for the header file. +find_path(INIH_INCLUDE_DIR + NAMES ini.h + HINTS ${PC_INIH_INCLUDE_DIRS}) + +# Look for the library. +# Allow INIH_LIBRARY to be set manually, as the location of the inih library +if(NOT INIH_LIBRARY) + find_library(INIH_LIBRARY + NAMES libinih inih + HINTS ${PC_INIH_LIBRARY_DIRS}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(inih + REQUIRED_VARS INIH_LIBRARY INIH_INCLUDE_DIR) + +if(INIH_FOUND) + set(INIH_INCLUDE_DIRS ${INIH_INCLUDE_DIR}) + + if(NOT INIH_LIBRARIES) + set(INIH_LIBRARIES ${INIH_LIBRARIES}) + endif() + + if(NOT TARGET inih::inih) + add_library(inih::inih UNKNOWN IMPORTED) + set_target_properties(inih::inih PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${INIH_INCLUDE_DIRS}" + IMPORTED_LOCATION "${INIH_LIBRARY}") + endif() +endif() + +mark_as_advanced(INIH_INCLUDE_DIR INIH_LIBRARY) diff --git a/builds/cmake/liblcf-config.cmake.in b/builds/cmake/liblcf-config.cmake.in index c892c0a56..b595bc1c1 100644 --- a/builds/cmake/liblcf-config.cmake.in +++ b/builds/cmake/liblcf-config.cmake.in @@ -2,6 +2,11 @@ include(CMakeFindDependencyMacro) +# Required to find our installed Findinih.cmake +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +find_dependency(inih REQUIRED) + if(@LCF_SUPPORT_ICU@) find_dependency(ICU COMPONENTS i18n uc data REQUIRED) endif() diff --git a/configure.ac b/configure.ac index fae5ccb94..ed25ed4bc 100644 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,8 @@ LT_INIT([win32-dll]) AM_CONDITIONAL(CROSS_COMPILING,[test "x$cross_compiling" = "xyes"]) # Checks for libraries. +AX_PKG_CHECK_MODULES([INIH],[],[inih],[]) + AC_SUBST([LCF_SUPPORT_ICU],[0]) AC_ARG_ENABLE([icu],[AS_HELP_STRING([--disable-icu],[Disable ICU encoding handling (only windows-1252 supported) [default=no]])]) AS_IF([test "x$enable_icu" != "xno"],[ diff --git a/src/ini.cpp b/src/ini.cpp deleted file mode 100644 index 68caa3eab..000000000 --- a/src/ini.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* inih -- simple .INI file parser - -The "inih" library is distributed under the New BSD license: - -Copyright (c) 2009, Ben Hoyt -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * 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. - * Neither the name of Ben Hoyt nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY BEN HOYT ''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 BEN HOYT 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. - -Go to the project home page for more info: https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include - -#include "lcf/ini.h" - -#if !INI_USE_STACK -#include -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* 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) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to null 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) -{ -#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. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* 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; - int max_line = INI_INITIAL_ALLOC; -#endif -#if INI_ALLOW_REALLOC && !INI_USE_STACK - char* new_line; - int offset; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - 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, 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, max_line - offset, stream) == NULL) - break; - if (max_line >= INI_MAX_LINE) - break; - offset += strlen(line + offset); - } -#endif - - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (strchr(INI_START_COMMENT_PREFIXES, *start)) { - /* Start-of-line comment */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* 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_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - 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) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - 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); -} diff --git a/src/inireader.cpp b/src/inireader.cpp index 57f2fbb99..6222ae29d 100644 --- a/src/inireader.cpp +++ b/src/inireader.cpp @@ -34,7 +34,7 @@ #include #include #include -#include "lcf/ini.h" +#include #include "lcf/inireader.h" namespace lcf { diff --git a/src/lcf/ini.h b/src/lcf/ini.h deleted file mode 100644 index 32d50449b..000000000 --- a/src/lcf/ini.h +++ /dev/null @@ -1,154 +0,0 @@ -/* inih -- simple .INI file parser - -The "inih" library is distributed under the New BSD license: - -Copyright (c) 2009, Ben Hoyt -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * 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. - * Neither the name of Ben Hoyt nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY BEN HOYT ''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 BEN HOYT 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. - -Go to the project home page for more info: https://github.com/benhoyt/inih - -*/ - -#ifndef LCF_INI_H -#define LCF_INI_H - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* 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); - -/* 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. - - 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 - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - 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 (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 - 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 https://github.com/benhoyt/inih/issues/21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* 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 - -#ifdef __cplusplus -} -#endif - -#endif