diff --git a/Makefile b/Makefile index c275363..0413132 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ EXE=pb_gnu # -Wno-comment // or /* within a comment -CFLAGS=-c -Wall -std=c++11 -Wno-comment -g +CFLAGS=-c -Wall -std=c++11 -Wno-comment -Os -D__NO_INLINE_HYPOTF__ #for optimization add -Os -D__NO_INLINE_HYPOTF__ #optimization gave a speed bonus from 40 to 65fps at 2x20.000 ships LDFLAGS=-lglfw3 -lenet @@ -27,8 +27,8 @@ endif all: $(EXE) # executable -$(EXE): main.o server.o client.o inc.o draw.o game.o net.o makefile - $(CC) main.o server.o client.o inc.o draw.o game.o net.o -o $(EXE) $(LDFLAGS) +$(EXE): main.o server.o client.o inc.o draw.o game.o net.o configuration.o inih.o inihreader.o makefile + $(CC) main.o server.o client.o inc.o draw.o game.o net.o configuration.o inih.o inihreader.o -o $(EXE) $(LDFLAGS) main.o: main.cpp src/server.hpp src/client.hpp makefile $(CC) $(CFLAGS) $(LDFLAGS) main.cpp @@ -51,6 +51,14 @@ inc.o: src/inc.cpp src/inc.hpp makefile net.o: src/net.cpp src/net.hpp makefile $(CC) $(CFLAGS) $(LDFLAGS) src/net.cpp +configuration.o: src/configuration.cpp src/configuration.hpp src/include/inih/INIReader.hpp makefile + $(CC) $(CFLAGS) $(LDFLAGS) src/configuration.cpp + +inih.o: src/include/inih/ini.c src/include/inih/ini.h makefile + $(CC) $(CFLAGS) $(LDFLAGS) src/include/inih/ini.c -o inih.o +inihreader.o: src/include/inih/ini.c src/include/inih/INIReader.hpp src/include/inih/INIReader.cpp makefile + $(CC) $(CFLAGS) $(LDFLAGS) src/include/inih/INIReader.cpp -o inihreader.o + clean: $(RM) *.o $(RM) $(EXE) diff --git a/PlanetBattle Multiplayer.vcxproj b/PlanetBattle Multiplayer.vcxproj index 63093e6..d4f338d 100644 --- a/PlanetBattle Multiplayer.vcxproj +++ b/PlanetBattle Multiplayer.vcxproj @@ -21,9 +21,12 @@ + + + @@ -31,11 +34,19 @@ + + + + + + + + 16.0 {4A0F77B9-ADCF-4B3B-8ADD-2C0322F84308} @@ -109,6 +120,7 @@ Disabled true true + stdcpp17 Console @@ -124,6 +136,7 @@ Disabled true true + stdcpp17 Console @@ -137,6 +150,7 @@ true true true + stdcpp17 Console @@ -153,6 +167,7 @@ true true true + stdcpp17 Console diff --git a/PlanetBattle Multiplayer.vcxproj.filters b/PlanetBattle Multiplayer.vcxproj.filters index 9ccb6ee..8a05e4e 100644 --- a/PlanetBattle Multiplayer.vcxproj.filters +++ b/PlanetBattle Multiplayer.vcxproj.filters @@ -36,6 +36,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -59,5 +68,19 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 3f1c0a6..600b92d 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ A [Planet Battle 3](http://forums.purebasic.com/german/viewtopic.php?f=12&t=1810 Requires: [glfw3](http://www.glfw.org/download.html "glfw"), [ENet](http://enet.bespin.org/ "ENet") > Note: I tweaked `math.h` to only not only `hypotf` inlined when using `-D__NO_INLINE_HYPOTF__`. If you get compilerproblems regarding `hypotf`, try `-D__NO_INLINE__` instead. -Run `make` to compile and run `./server` or `./client` to play! -It is important that the game will not start until there are two clients connected to the server, so make sure you start the `client` twice or have a friend that connects, too. +Run `make` to compile and run `./game` to play! +It is important that the game will not start until there are two clients connected to the server, so make sure you start the game as client twice or have a friend that connects, too. -Both, client and server, can take a console-parameter. For the client it is the server-ip, and for the server it's the maximum amount of ships per player. +To start the game as a client or as a sever you can use the console paramters `-c []` and `-s []` respectively or just run it up and enter the configuration in the console. +The settings.ini file is not yet functional (see issue #15). # ToDo - [ ] [another todolist](https://github.com/nitzel/pb_mp/blob/master/notes/todo%2Bideas.md "more extensive todo list") @@ -35,4 +36,6 @@ Both, client and server, can take a console-parameter. For the client it is the - [glfw3](http://www.glfw.org/download.html "glfw") awesome cross os lib for GL setup and window creation - [ENet](http://enet.bespin.org/ "ENet") networking library. (un-)reliable packets via UDP. - + + - [inih](https://github.com/benhoyt/inih "INI Not Invented Here by Ben Hoyt") by Ben Hoyt to read ini files. + \ No newline at end of file diff --git a/main.cpp b/main.cpp index 99b5f89..d1f7941 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ #include "src/server.hpp" #include "src/client.hpp" +#include "src/configuration.hpp" #include #include // std::strcmp @@ -10,6 +11,11 @@ int main(int argc, char** argv) { std::cout << i << ' ' << argv[i] << std::endl; } std::cout << std::endl; + + // todo 20190419nitzel Forward to and use the settings in the game etc, to replace the #defines in + CConfiguration config("settings.ini"); + std::cout << "Config=" << config << std::endl; + if (argc == 1) { // ask user diff --git a/settings.ini b/settings.ini new file mode 100644 index 0000000..222b670 --- /dev/null +++ b/settings.ini @@ -0,0 +1,10 @@ +[Graphics] +width = 800 +height = 600 +[Server] +name = "Spiel1" +port = 12345 +[Client] +name = "nitzel" +port = 12345 +host = "127!!" \ No newline at end of file diff --git a/src/configuration.cpp b/src/configuration.cpp new file mode 100644 index 0000000..75a5e7a --- /dev/null +++ b/src/configuration.cpp @@ -0,0 +1,28 @@ +#include "configuration.hpp" + +const std::string CConfiguration::CServer::section = "Server"; +const std::string CConfiguration::CClient::section = "Client"; + +CConfiguration::CServer::CServer(INIReader& iniReader) : + port(iniReader.GetInteger(CServer::section, "port", port)), + name(iniReader.GetString(CServer::section, "name", name)) { +} + +CConfiguration::CServer::CServer() { +} + +CConfiguration::CClient::CClient(INIReader& iniReader) : + port(iniReader.GetInteger(CClient::section, "port", port)), + name(iniReader.GetString(CClient::section, "name", name)), + host(iniReader.GetString(CClient::section, "host", host)) { +} + +CConfiguration::CClient::CClient() { +} + + +CConfiguration::CConfiguration(std::string iniFilename) { + INIReader iniReader(iniFilename); + server = CConfiguration::CServer(iniReader); + client = CConfiguration::CClient(iniReader); +} diff --git a/src/configuration.hpp b/src/configuration.hpp new file mode 100644 index 0000000..3fe9a98 --- /dev/null +++ b/src/configuration.hpp @@ -0,0 +1,56 @@ +#ifndef __CONFIGURATION__ +#define __CONFIGURATION__ + +#include +#include +#include "include/inih/INIReader.hpp" + +#define DEFAULT_PORT 12345 + +/// Reads the configuration from an ini file. +/// Has default values for missing entries. +class CConfiguration { +public: + /// Server-section of ini file. + class CServer { + private: + static const std::string section; + public: + unsigned int port = DEFAULT_PORT; + std::string name = "Planet Battle"; + + CServer(); + CServer(INIReader& iniReader); + friend std::ostream& operator<< (std::ostream& os, CConfiguration::CServer& server) { + return os << "[name=" << server.name << "; port=" << server.port << ";]"; + } + }; + + /// Client-section of ini file. + class CClient { + private: + static const std::string section; + public: + unsigned int port = DEFAULT_PORT; + std::string name = "unknown"; + std::string host = "localhost"; + + CClient(); + CClient(INIReader& iniReader); + friend std::ostream& operator<< (std::ostream& os, CConfiguration::CClient& client) { + return os << "[name=" << client.name << "; port=" << client.port << "; host=" << client.host << ";]"; + } + }; + +private: +public: + CServer server; + CClient client; + //CConfiguration() = delete; + CConfiguration(std::string iniFilename = "config.ini"); + friend std::ostream& operator<< (std::ostream& os, CConfiguration& configuration) { + return os << "[Server=" << configuration.server << "; Client=" << configuration.client << ";]"; + } +}; + +#endif \ No newline at end of file diff --git a/src/include/inih/INIReader.cpp b/src/include/inih/INIReader.cpp new file mode 100644 index 0000000..b9f1961 --- /dev/null +++ b/src/include/inih/INIReader.cpp @@ -0,0 +1,94 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih + +#include +#include +#include +#include "ini.h" +#include "INIReader.hpp" + +using std::string; + +INIReader::INIReader(const string& filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +int INIReader::ParseError() const +{ + return _error; +} + +string INIReader::Get(const string& section, const string& name, const string& default_value) const +{ + string key = MakeKey(section, name); + // Use _values.find() here instead of _values.at() to support pre C++11 compilers + return _values.count(key) ? _values.find(key)->second : default_value; +} + +string INIReader::GetString(const string& section, const string& name, const string& default_value) const +{ + const string str = Get(section, name, ""); + return str.empty() ? default_value : str; +} + +long INIReader::GetInteger(const string& section, const string& name, long default_value) const +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +double INIReader::GetReal(const string& section, const string& name, double default_value) const +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +bool INIReader::GetBoolean(const string& section, const string& name, bool default_value) const +{ + string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +bool INIReader::HasValue(const std::string& section, const std::string& name) const +{ + string key = MakeKey(section, name); + return _values.count(key); +} + +string INIReader::MakeKey(const string& section, const string& name) +{ + string key = section + "=" + name; + // Convert to lower case to make section/name lookups case-insensitive + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + return key; +} + +int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + INIReader* reader = static_cast(user); + string key = MakeKey(section, name); + if (reader->_values[key].size() > 0) + reader->_values[key] += "\n"; + reader->_values[key] += value; + return 1; +} diff --git a/src/include/inih/INIReader.hpp b/src/include/inih/INIReader.hpp new file mode 100644 index 0000000..19538fb --- /dev/null +++ b/src/include/inih/INIReader.hpp @@ -0,0 +1,61 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih + +#ifndef __INIREADER_H__ +#define __INIREADER_H__ + +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + explicit INIReader(const std::string& filename); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError() const; + + // Get a string value from INI file, returning default_value if not found. + std::string Get(const std::string& section, const std::string& name, + const std::string& default_value) const; + + // Get a string value from INI file, returning default_value if not found, + // empty, or contains only whitespace. + std::string GetString(const std::string& section, const std::string& name, + const std::string& default_value) const; + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(const std::string& section, const std::string& name, long default_value) const; + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(const std::string& section, const std::string& name, double default_value) const; + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; + + // Return true if a value exists with the given section and field names. + bool HasValue(const std::string& section, const std::string& name) const; + +private: + int _error; + std::map _values; + static std::string MakeKey(const std::string& section, const std::string& name); + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // __INIREADER_H__ diff --git a/src/include/inih/LICENSE.txt b/src/include/inih/LICENSE.txt new file mode 100644 index 0000000..cb7ee2d --- /dev/null +++ b/src/include/inih/LICENSE.txt @@ -0,0 +1,27 @@ + +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. diff --git a/src/include/inih/ini.c b/src/include/inih/ini.c new file mode 100644 index 0000000..df13939 --- /dev/null +++ b/src/include/inih/ini.c @@ -0,0 +1,273 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). 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 "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'; +#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) { + /* 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/include/inih/ini.h b/src/include/inih/ini.h new file mode 100644 index 0000000..4db7d77 --- /dev/null +++ b/src/include/inih/ini.h @@ -0,0 +1,130 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __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 /* __INI_H__ */