aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar gingerBill 2015-09-27 10:20:44 +0100
committerGravatar gingerBill 2015-09-27 10:20:44 +0100
commit0da2fecb4113187cc1671b2e5effcdcfc105d72f (patch)
tree9e6dcc8902c79def786c04eea35d8b5d96511a2e
parentUpdate README.md (diff)
Add gb_ini.h
-rw-r--r--gb_ini.h451
1 files changed, 451 insertions, 0 deletions
diff --git a/gb_ini.h b/gb_ini.h
new file mode 100644
index 0000000..9233c85
--- /dev/null
+++ b/gb_ini.h
@@ -0,0 +1,451 @@
+// gb_ini.h - v0.90 - public domain ini file loader library - no warranty implied; use at your own risk
+// A Simple Ini File Loader Library for C and C++
+//
+// Version History:
+// 0.90 - Initial Version
+//
+// LICENSE
+//
+// This software is in the public domain. Where that dedication is not
+// recognized, you are granted a perpetual, irrevocable license to copy,
+// distribute, and modify this file as you see fit.
+//
+// How to use:
+//
+// Do this:
+// #define GB_INI_IMPLEMENTATION
+// before you include this file in *one* C++ file to create the implementation
+//
+// i.e. it should look like this:
+// #include ...
+// #include ...
+// #include ...
+// #define GB_INI_IMPLEMENTATION
+// #include "gb_ini.h"
+////
+// If you prefer to use C++, you can use all the same functions in a
+// namespace instead, do this:
+// #define GB_INI_CPP
+// before you include the header file
+//
+// i.e it should look like this:
+// #define GB_INI_CPP
+// #include "gb_ini.h"
+//
+// Examples:
+//
+// test.ini contents
+#if 0
+; This is a Comment
+# This is also
+
+name=gb_ini.h
+version = 090
+
+[license]
+name = Public Domain
+
+[author]
+name = Ginger Bill
+#endif
+// C example
+#if 0
+#define GB_INI_IMPLEMENTATION
+#include "gb_ini.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+struct Library
+{
+ const char* name;
+ int version;
+ const char* license;
+ const char* author;
+};
+
+// The below macro expands to this:
+// static test_ini_handler(void* data, const char* section, const char* name, const char* value)
+static GB_INI_HANDLER(test_ini_handler)
+{
+ struct Library* lib = (struct Library*)data;
+
+ #define TEST(s, n) (strcmp(section, s) == 0 && strcmp(name, n) == 0)
+
+ if (TEST("", "name"))
+ lib->name = strdup(value);
+ else if (TEST("", "version"))
+ lib->version = atoi(value);
+ else if (TEST("license", "name"))
+ lib->license = strdup(value);
+ else if (TEST("author", "name"))
+ lib->author = strdup(value);
+ else
+ return 0;
+
+ #undef TEST
+
+ return 1;
+}
+
+int main(int argc, char** argv)
+{
+ struct Library lib = {0};
+ struct gb_Ini_Error err = gb_ini_parse("test.ini", &test_ini_handler, &lib);
+ if (err.type != GB_INI_ERROR_NONE)
+ {
+ if (err.line_num > 0)
+ printf("Line (%d): ", err.line_num);
+ printf("%s\n", gb_ini_error_string(err));
+
+ return 1;
+ }
+
+ printf("Name : %s\n", lib.name); // Name : gb_init.h
+ printf("Version : %d\n", lib.version); // Version : 90
+ printf("License : %s\n", lib.license); // License : Public Domain
+ printf("Author : %s\n", lib.author); // Author : Ginger Bill
+
+ return 0;
+}
+#endif
+//
+// C++ example
+#if 0
+#define GB_INI_CPP
+#define GB_INI_IMPLEMENTATION
+#include "gb_ini.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct Library
+{
+ const char* name;
+ int version;
+ const char* license;
+ const char* author;
+};
+
+// The below macro expands to this:
+// static test_ini_handler(void* data, const char* section, const char* name, const char* value)
+static GB_INI_HANDLER(test_ini_handler)
+{
+ Library* lib = (Library*)data;
+
+ #define TEST(s, n) (strcmp(section, s) == 0 && strcmp(name, n) == 0)
+
+ if (TEST("", "name"))
+ lib->name = strdup(value);
+ else if (TEST("", "version"))
+ lib->version = atoi(value);
+ else if (TEST("license", "name"))
+ lib->license = strdup(value);
+ else if (TEST("author", "name"))
+ lib->author = strdup(value);
+ else
+ return 0;
+
+ #undef TEST
+
+ return 1;
+}
+
+int main(int argc, char** argv)
+{
+ Library lib = {};
+
+ using namespace gb;
+
+ Ini_Error err = ini_parse("test.ini", &test_ini_handler, &lib);
+ if (err.type != INI_ERROR_NONE)
+ {
+ if (err.line_num > 0)
+ printf("Line (%d): ", err.line_num);
+ printf("%s\n", ini_error_string(err));
+
+ return 1;
+ }
+
+ printf("Name : %s\n", lib.name); // Name : gb_init.h
+ printf("Version : %d\n", lib.version); // Version : 90
+ printf("License : %s\n", lib.license); // License : Public Domain
+ printf("Author : %s\n", lib.author); // Author : Ginger Bill
+
+ return 0;
+}
+#endif
+
+#ifndef GB_INI_INCLUDE_GB_INI_H
+#define GB_INI_INCLUDE_GB_INI_H
+
+#ifndef GB_INI_MAX_LINE_LENGTH
+#define GB_INI_MAX_LINE_LENGTH 128
+#endif
+
+#ifndef GB_INI_MAX_SECTION_LENGTH
+#define GB_INI_MAX_SECTION_LENGTH 100
+#endif
+
+#ifndef GB_INI_MAX_NAME_LENGTH
+#define GB_INI_MAX_NAME_LENGTH 100
+#endif
+
+#ifndef GB_INI_CHECK_FOR_UTF8_BOM
+#define GB_INI_CHECK_FOR_UTF8_BOM 1
+#endif
+
+
+#ifndef _MSC_VER
+ #ifdef __cplusplus
+ #define gb_inline inline
+ #else
+ #define gb_inline
+ #endif
+#else
+#define gb_inline __forceinline
+#endif
+
+#include <stdio.h>
+
+enum
+{
+ GB_INI_ERROR_NONE = 0,
+
+ GB_INI_ERROR_FILE_ERROR,
+ GB_INI_ERROR_MISSING_SECTION_BRACKET,
+ GB_INI_ERROR_ASSIGNMENT_MISSING,
+ GB_INI_ERROR_HANDLER_ERROR,
+
+ GB_INI_ERROR_COUNT,
+};
+
+struct gb_Ini_Error
+{
+ int type;
+ size_t line_num;
+};
+
+#ifndef GB_INI_HANDLER_RETURN_TYPE
+#define GB_INI_HANDLER_RETURN_TYPE
+typedef int gb_Ini_HRT;
+#endif
+
+// This macro can be use to declare this type of function without
+// needing to define all the of the parameters
+#define GB_INI_HANDLER(func_name) gb_Ini_HRT func_name(void* data, const char* section, const char* name, const char* value)
+typedef GB_INI_HANDLER(gb_Ini_Handler);
+
+extern const char* GB_ERROR_STRINGS[GB_INI_ERROR_COUNT];
+
+struct gb_Ini_Error gb_ini_parse(const char* filename, gb_Ini_Handler* handler_func, void* data);
+struct gb_Ini_Error gb_ini_parse_file(FILE* file, gb_Ini_Handler* handler_func, void* data);
+
+gb_inline const char*
+gb_ini_error_string(const struct gb_Ini_Error err)
+{
+ return GB_ERROR_STRINGS[err.type];
+}
+
+#ifdef GB_INI_IMPLEMENTATION
+#include <ctype.h>
+#include <string.h>
+
+const char* GB_ERROR_STRINGS[GB_INI_ERROR_COUNT] = {
+ "",
+
+ "Error in opening file",
+ "Missing closing section bracket ']'",
+ "Missing assignment operator '='",
+ "Error in handler function",
+};
+
+static gb_inline char*
+gb__left_whitespace_skip(const char* str)
+{
+ while (*str && isspace((unsigned char)(*str)))
+ str++;
+ return (char*)str;
+}
+
+static gb_inline char*
+gb__right_whitespace_strip(char* str)
+{
+ char* end = str + strlen(str);
+ while (end > str && isspace((unsigned char)(*--end)))
+ *end = '\0';
+ return str;
+}
+
+static gb_inline char*
+gb__find_char_or_comment_in_string(const char* str, char c)
+{
+ int was_whitespace = 0;
+ while (*str && *str != c &&
+ !(was_whitespace && *str == ';'))
+ {
+ was_whitespace = isspace((unsigned char)(*str));
+ str++;
+ }
+
+ return (char*)str;
+}
+
+static gb_inline char*
+gb__string_copy(char* dst, const char* src, size_t size)
+{
+ strncpy(dst, src, size);
+ dst[size - 1] = '\0';
+ return dst;
+}
+
+
+struct gb_Ini_Error
+gb_ini_parse(const char* filename, gb_Ini_Handler* handler_func, void* data)
+{
+ struct gb_Ini_Error err = {GB_INI_ERROR_NONE, 0};
+
+ FILE* file = fopen(filename, "r");
+ if (!file)
+ return err;
+
+ err = gb_ini_parse_file(file, handler_func, data);
+ fclose(file);
+ return err;
+}
+
+struct gb_Ini_Error
+gb_ini_parse_file(FILE* file, gb_Ini_Handler* handler_func, void* data)
+{
+ char line[GB_INI_MAX_LINE_LENGTH] = "";
+ size_t line_num = 0;
+
+ char section[GB_INI_MAX_SECTION_LENGTH] = "";
+ char prev_name[GB_INI_MAX_NAME_LENGTH] = "";
+
+ char* start;
+ char* end;
+
+ struct gb_Ini_Error err = {GB_INI_ERROR_NONE, 0};
+
+ while (fgets(line, GB_INI_MAX_LINE_LENGTH, file) != 0)
+ {
+ line_num++;
+
+ start = line;
+
+#if GB_INI_CHECK_FOR_UTF8_BOM
+ // Check for UTF-8 Byte Order Mark
+ if (line_num == 1 &&
+ (unsigned char)start[0] == 0xef &&
+ (unsigned char)start[1] == 0xbb &&
+ (unsigned char)start[2] == 0xbf)
+ {
+ start += 3;
+ }
+#endif
+
+ start = gb__left_whitespace_skip(gb__right_whitespace_strip(start));
+
+ if (start[0] == ';' || start[0] == '#')
+ continue; // Allow '#' and ';' comments at start of line
+
+ if (start[0] == '[') // [section]
+ {
+ end = gb__find_char_or_comment_in_string(start+1, ']');
+ if (*end == ']')
+ {
+ *end = '\0';
+ char* sect = start + 1;
+ sect = gb__left_whitespace_skip(gb__right_whitespace_strip(sect));
+
+ gb__string_copy(section, sect, sizeof(section));
+ *prev_name = '\0';
+ }
+ else if (!err.type)
+ {
+ err.type = GB_INI_ERROR_MISSING_SECTION_BRACKET;
+ err.line_num = line_num;
+ }
+ }
+ else if (start[0] && start[0] != ';')
+ {
+ end = gb__find_char_or_comment_in_string(start, '=');
+ if (*end == '=')
+ {
+ *end = '\0';
+ char* name = gb__right_whitespace_strip(start);
+ char* value = gb__left_whitespace_skip(end + 1);
+ end = gb__find_char_or_comment_in_string(value, '\0');
+ if (*end == ';')
+ *end = '\0';
+ gb__right_whitespace_strip(value);
+
+ gb__string_copy(prev_name, name, sizeof(prev_name));
+ if (!handler_func(data, section, name, value) && err.type)
+ {
+ err.type = GB_INI_ERROR_HANDLER_ERROR;
+ err.line_num = line_num;
+ }
+ }
+ else if (!err.type)
+ {
+ // No '=' found on name=value line
+ err.type = GB_INI_ERROR_ASSIGNMENT_MISSING;
+ err.line_num = line_num;
+ continue;
+ }
+ }
+
+ if (err.type)
+ break;
+ }
+
+ return err;
+}
+
+#endif // GB_INI_IMPLEMENTATION
+
+#ifdef GB_INI_CPP
+#if !defined(__cplusplus)
+#error You need to compile as C++ for the C++ version of gb_ini.h to work
+#endif
+
+namespace gb
+{
+typedef gb_Ini_Error Ini_Error;
+typedef gb_Ini_Handler Ini_Handler;
+
+// Just a copy but with the GB_ prefix stripped
+enum
+{
+ INI_ERROR_NONE = 0,
+
+ INI_ERROR_FILE_ERROR,
+ INI_ERROR_MISSING_SECTION_BRACKET,
+ INI_ERROR_ASSIGNMENT_MISSING,
+ INI_ERROR_HANDLER_ERROR,
+
+ // No need for enum count
+};
+
+Ini_Error
+ini_parse(const char* filename, Ini_Handler* handler_func, void* data)
+{
+ return gb_ini_parse(filename, handler_func, data);
+}
+
+Ini_Error
+ini_parse(FILE* file, Ini_Handler* handler_func, void* data)
+{
+ return gb_ini_parse_file(file, handler_func, data);
+}
+
+const char*
+ini_error_string(const Ini_Error err)
+{
+ return GB_ERROR_STRINGS[err.type];
+}
+
+} // namespace gb
+#endif // GB_INI_CPP
+#endif // GB_INI_INCLUDE_GB_INI_H