aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--any_ini.h373
-rw-r--r--test/ini.c93
-rw-r--r--test/test.ini30
3 files changed, 363 insertions, 133 deletions
diff --git a/any_ini.h b/any_ini.h
index 3d80b85..aecdcd1 100644
--- a/any_ini.h
+++ b/any_ini.h
@@ -1,3 +1,22 @@
+// any_ini
+//
+// A single-file library that provides a simple and somewhat opinionated
+// interface for parsing ini files, either from a buffer or stream.
+//
+// To use this library you should choose a suitable file to put the
+// implementation and define ANY_INI_IMPLEMENT. For example
+//
+// #define ANY_INI_IMPLEMENT
+// #include "any_ini.h"
+//
+// Additionally, you can customize the library behavior by defining certain
+// macros in the file where you put the implementation. You can see which are
+// supported by reading the code guarded by ANY_INI_IMPLEMENT.
+//
+// This library is licensed under the terms of the MIT license.
+// A copy of the license is included at the end of this file.
+//
+
#ifndef ANY_INI_INCLUDE
#define ANY_INI_INCLUDE
@@ -10,10 +29,10 @@
#include <stdbool.h>
typedef struct {
- const char *source;
- size_t length;
- size_t cursor;
- size_t line;
+ const char *source;
+ size_t length;
+ size_t cursor;
+ size_t line;
} any_ini_t;
void any_ini_init(any_ini_t *ini, const char *source, size_t length);
@@ -26,11 +45,36 @@ char *any_ini_next_key(any_ini_t *ini);
char *any_ini_next_value(any_ini_t *ini);
-//typedef struct {
-// FILE *stream;
-//} any_ini_stream_t;
-//
-//void any_ini_stream_init(any_ini_stream_t *ini, const char *source, size_t length);
+#ifndef ANY_INI_NO_STREAM
+
+#ifndef ANY_INI_BUFFER_SIZE
+#define ANY_INI_BUFFER_SIZE 4096
+#endif
+
+typedef char *(*any_ini_stream_read_t)(char *string, int size, void *stream);
+
+typedef struct {
+ char buffer[ANY_INI_BUFFER_SIZE];
+ size_t cursor;
+ size_t line;
+ any_ini_stream_read_t read;
+ void *stream;
+ bool eof;
+} any_ini_stream_t;
+
+void any_ini_stream_init(any_ini_stream_t *ini, any_ini_stream_read_t read, void *stream);
+
+void any_ini_file_init(any_ini_stream_t *ini, FILE *file);
+
+bool any_ini_stream_eof(any_ini_stream_t *ini);
+
+char *any_ini_stream_next_section(any_ini_stream_t *ini);
+
+char *any_ini_stream_next_key(any_ini_stream_t *ini);
+
+char *any_ini_stream_next_value(any_ini_stream_t *ini);
+
+#endif
#endif
@@ -56,150 +100,269 @@ char *any_ini_next_value(any_ini_t *ini);
#define ANY_INI_SECTION_END ']'
#endif
-static inline bool any_ini_special(char c)
-{
- return c == ANY_INI_DELIM_PAIR
- || c == ANY_INI_SECTION_START
- || c == ANY_INI_SECTION_END;
-}
-
-static size_t any_ini_trim(any_ini_t *ini, size_t start, size_t end)
+static size_t any_ini_trim(const char *source, size_t start, size_t end)
{
- while (isspace(ini->source[end - 1]))
- end--;
+ while (isspace(source[end - 1]))
+ end--;
- return end - start;
+ return end - start;
}
static char *any_ini_copy(const char *start, size_t length)
{
- char *string = ANY_INI_MALLOC(length + 1);
- if (string) {
- memcpy(string, start, length);
- string[length] = '\0';
- }
- return string;
+ char *string = ANY_INI_MALLOC(length + 1);
+ if (string) {
+ memcpy(string, start, length);
+ string[length] = '\0';
+ }
+ return string;
}
static void any_ini_skip(any_ini_t *ini)
{
- while (!any_ini_eof(ini)) {
- switch (ini->source[ini->cursor]) {
- case ' ':
- case '\t':
- case '\v':
- case '\r':
- break;
+ while (!any_ini_eof(ini)) {
+ switch (ini->source[ini->cursor]) {
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\r':
+ break;
+
+ case '\n':
+ ini->line++;
+ break;
+
+ case ANY_INI_DELIM_COMMENT:
+#ifdef ANY_INI_DELIM_COMMENT2
+ case ANY_INI_DELIM_COMMENT2:
+#endif
+ while (!any_ini_eof(ini) && ini->source[ini->cursor] != '\n')
+ ini->cursor++;
+ continue;
+
+ default:
+ return;
+ }
+ ini->cursor++;
+ }
+}
- case '\n':
- ini->line++;
- break;
+static bool any_ini_skip_pair(const char *source, size_t cursor, bool key)
+{
+ switch (source[cursor]) {
+ case '\n':
+ return false;
- case ANY_INI_DELIM_COMMENT:
+#ifndef ANY_INI_NO_INLINE_COMMENT
+ case ANY_INI_DELIM_COMMENT:
#ifdef ANY_INI_DELIM_COMMENT2
- case ANY_INI_DELIM_COMMENT2:
+ case ANY_INI_DELIM_COMMENT2:
+#endif
+ if (isspace(source[cursor - 1]))
+ return false;
+ // fallthrough
#endif
- while (!any_ini_eof(ini) && ini->source[ini->cursor] != '\n')
- ini->cursor++;
- continue;
- default:
- return;
- }
- ini->cursor++;
- }
+ default:
+ return !key || source[cursor] != ANY_INI_DELIM_PAIR;
+ }
}
-
void any_ini_init(any_ini_t *ini, const char *source, size_t length)
{
- ini->source = source;
- ini->length = length;
- ini->cursor = 0;
- ini->line = 1;
+ ini->source = source;
+ ini->length = length;
+ ini->cursor = 0;
+ ini->line = 1;
}
bool any_ini_eof(any_ini_t *ini)
{
- return ini->cursor >= ini->length;
+ return ini->cursor >= ini->length;
}
char *any_ini_next_section(any_ini_t *ini)
{
- any_ini_skip(ini);
+ any_ini_skip(ini);
- if (any_ini_eof(ini) || ini->source[ini->cursor] != ANY_INI_SECTION_START)
- return NULL;
+ if (any_ini_eof(ini) || ini->source[ini->cursor] != ANY_INI_SECTION_START)
+ return NULL;
- size_t start = ++ini->cursor;
- while (!any_ini_eof(ini) && ini->source[ini->cursor] != '\n')
- ini->cursor++;
+ size_t start = ++ini->cursor;
+ while (!any_ini_eof(ini) && ini->source[ini->cursor] != '\n') {
+ if (isspace(ini->source[ini->cursor])) ++start;
+ ini->cursor++;
+ }
- // NOTE: Does not handle the case where ANY_INI_SECTION_END is not found
- size_t end = ini->cursor;
- while (end > start && ini->source[end] != ANY_INI_SECTION_END)
- end--;
+ // NOTE: Does not handle the case where ANY_INI_SECTION_END is not found
+ size_t end = ini->cursor;
+ while (end > start && ini->source[end] != ANY_INI_SECTION_END)
+ end--;
- size_t length = any_ini_trim(ini, start, end);
- return any_ini_copy(ini->source + start, length);
+ size_t length = any_ini_trim(ini->source, start, end);
+ return any_ini_copy(ini->source + start, length);
}
char *any_ini_next_key(any_ini_t *ini)
{
- any_ini_skip(ini);
+ any_ini_skip(ini);
- if (any_ini_eof(ini) || any_ini_special(ini->source[ini->cursor]))
- return NULL;
+ if (any_ini_eof(ini) || ini->source[ini->cursor] == ANY_INI_SECTION_START)
+ return NULL;
- size_t start = ini->cursor;
- while (!any_ini_eof(ini)) {
- switch (ini->source[ini->cursor]) {
- case '\n':
- case ANY_INI_DELIM_COMMENT:
-#ifdef ANY_INI_DELIM_COMMENT2
- case ANY_INI_DELIM_COMMENT2:
-#endif
- case ANY_INI_DELIM_PAIR:
- break;
+ size_t start = ini->cursor;
+ while (!any_ini_eof(ini) && any_ini_skip_pair(ini->source, ini->cursor, true))
+ ini->cursor++;
- default:
- ini->cursor++;
- continue;
- }
- break;
- }
-
- size_t length = any_ini_trim(ini, start, ini->cursor);
- return any_ini_copy(ini->source + start, length);
+ size_t length = any_ini_trim(ini->source, start, ini->cursor);
+ return any_ini_copy(ini->source + start, length);
}
char *any_ini_next_value(any_ini_t *ini)
{
- if (any_ini_eof(ini) || ini->source[ini->cursor] != ANY_INI_DELIM_PAIR)
- return NULL;
+ if (any_ini_eof(ini) || ini->source[ini->cursor] != ANY_INI_DELIM_PAIR)
+ return NULL;
+
+ ++ini->cursor;
+ any_ini_skip(ini);
+
+ size_t start = ini->cursor;
+ while (!any_ini_eof(ini) && any_ini_skip_pair(ini->source, ini->cursor, false))
+ ini->cursor++;
+
+ size_t length = any_ini_trim(ini->source, start, ini->cursor);
+ return any_ini_copy(ini->source + start, length);
+}
+
+#ifndef ANY_INI_NO_STREAM
- ++ini->cursor;
- any_ini_skip(ini);
+static void any_ini_stream_read(any_ini_stream_t *ini)
+{
+ ini->eof = ini->read(ini->buffer, ANY_INI_BUFFER_SIZE, ini->stream) == NULL;
+ ini->cursor = 0;
+}
- size_t start = ini->cursor;
- while (!any_ini_eof(ini)) {
- switch (ini->source[ini->cursor]) {
- case '\n':
- case ANY_INI_DELIM_COMMENT:
+static void any_ini_stream_skip(any_ini_stream_t *ini)
+{
+ while (!ini->eof) {
+ switch (ini->buffer[ini->cursor]) {
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\r':
+ break;
+
+ // Discard the current line
+ case '\n':
+ case ANY_INI_DELIM_COMMENT:
#ifdef ANY_INI_DELIM_COMMENT2
- case ANY_INI_DELIM_COMMENT2:
+ case ANY_INI_DELIM_COMMENT2:
#endif
- break;
+ ini->line++;
+ any_ini_stream_read(ini);
+ continue;
+
+ default:
+ return;
+ }
+ ini->cursor++;
+ }
+}
+
+void any_ini_stream_init(any_ini_stream_t *ini, any_ini_stream_read_t read, void *stream)
+{
+ ini->cursor = 0;
+ ini->line = 1;
+ ini->read = read;
+ ini->stream = stream;
+ ini->eof = read(ini->buffer, ANY_INI_BUFFER_SIZE, stream) == NULL;
+}
+
+void any_ini_file_init(any_ini_stream_t *ini, FILE *file)
+{
+ any_ini_stream_init(ini, (any_ini_stream_read_t)fgets, file);
+}
+
+bool any_ini_stream_eof(any_ini_stream_t *ini)
+{
+ return ini->eof;
+}
+
+char *any_ini_stream_next_section(any_ini_stream_t *ini)
+{
+ any_ini_stream_skip(ini);
+
+ if (ini->eof || ini->buffer[ini->cursor] != ANY_INI_SECTION_START)
+ return NULL;
+
+ size_t start = ++ini->cursor;
+ while (isspace(ini->buffer[ini->cursor])) ++start;
+
+ while (ini->buffer[ini->cursor] != '\n') ini->cursor++;
+
+ // NOTE: Does not handle the case where ANY_INI_SECTION_END is not found
+ size_t end = ini->cursor;
+ while (end > start && ini->buffer[end] != ANY_INI_SECTION_END)
+ end--;
- default:
- ini->cursor++;
- continue;
- }
- break;
- }
+ size_t length = any_ini_trim(ini->buffer, start, end);
+ return any_ini_copy(ini->buffer + start, length);
+}
+
+char *any_ini_stream_next_key(any_ini_stream_t *ini)
+{
+ any_ini_stream_skip(ini);
+
+ if (ini->eof || ini->buffer[ini->cursor] == ANY_INI_SECTION_START)
+ return NULL;
+
+ size_t start = ini->cursor;
+ while (any_ini_skip_pair(ini->buffer, ini->cursor, true))
+ ini->cursor++;
- size_t length = any_ini_trim(ini, start, ini->cursor);
- return any_ini_copy(ini->source + start, length);
+ size_t length = any_ini_trim(ini->buffer, start, ini->cursor);
+ return any_ini_copy(ini->buffer + start, length);
}
+char *any_ini_stream_next_value(any_ini_stream_t *ini)
+{
+ if (ini->eof || ini->buffer[ini->cursor] != ANY_INI_DELIM_PAIR)
+ return NULL;
+
+ ++ini->cursor;
+ any_ini_stream_skip(ini);
+
+ size_t start = ini->cursor;
+ while (any_ini_skip_pair(ini->buffer, ini->cursor, false))
+ ini->cursor++;
+
+ size_t length = any_ini_trim(ini->buffer, start, ini->cursor);
+ return any_ini_copy(ini->buffer + start, length);
+}
+
+#endif
+
#endif
+
+// MIT License
+//
+// Copyright (c) 2024 Federico Angelilli
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
diff --git a/test/ini.c b/test/ini.c
index 49b39cc..ebd41a6 100644
--- a/test/ini.c
+++ b/test/ini.c
@@ -5,34 +5,71 @@
#define ANY_INI_DELIM_COMMENT2 '#'
#include "any_ini.h"
+void test_ini()
+{
+ const char *src =
+ /* 1*/ "ciao = 10\n"
+ /* 2*/ "global = yes\n"
+ /* 3*/ " complex name with space = value with space \n\n"
+ /* 5*/ "[sus]\n"
+ /* 6*/ "]nice = 1\n"
+ /* 7*/ ";comment\n\n"
+ /* 9*/ "another=10;x\n"
+ /*10*/ "true=1 ;xx\n"
+ /*11*/ " # comment 2 ;\n\n"
+ /*13*/ "try = catch 123 bool\n"
+ /*14*/ " k e y = value pair! ; comment\n"
+ /*15*/ " su;s = [x] \n"
+ /*16*/ " sus \n";
+
+ any_ini_t ini;
+ any_ini_init(&ini, src, strlen(src));
+
+ char *section = "";
+ do {
+ printf("%ld: SECTION \"%s\"\n", ini.line, section);
+
+ char *key, *value;
+ while ((key = any_ini_next_key(&ini)) != NULL) {
+ value = any_ini_next_value(&ini);
+ printf("%ld: \"%s\" = \"%s\"\n", ini.line, key, value);
+ }
+ } while ((section = any_ini_next_section(&ini)) != NULL);
+}
+
+void test_ini_stream()
+{
+ FILE *file = fopen("test/test.ini", "rb");
+
+ if (file == NULL) {
+ perror("test_ini_stream");
+ return;
+ }
+
+ any_ini_stream_t ini;
+ any_ini_stream_init(&ini, (any_ini_stream_read_t)fgets, file);
+
+ char *section = "";
+ do {
+ printf("%ld: SECTION \"%s\"\n", ini.line, section);
+
+ char *key, *value;
+ while ((key = any_ini_stream_next_key(&ini)) != NULL) {
+ value = any_ini_stream_next_value(&ini);
+ printf("%ld: \"%s\" = \"%s\"\n", ini.line, key, value);
+ }
+ } while ((section = any_ini_stream_next_section(&ini)) != NULL);
+
+ fclose(file);
+}
+
int main()
{
- const char *src =
- /* 1*/ "ciao = 10\n"
- /* 2*/ "global = yes\n"
- /* 3*/ " complex name with space = value with space \n\n"
- /* 5*/ "[sus]\n"
- /* 6*/ "nice = 1\n"
- /* 7*/ ";comment\n\n"
- /* 9*/ "another=10;x\n"
- /*10*/ "true=1 ;xx\n"
- /*11*/ " # comment 2 ;\n\n"
- /*13*/ "try = catch 123 bool\n"
- /*14*/ " k e y = value pair! ; comment\n";
-
- any_ini_t ini;
- any_ini_init(&ini, src, strlen(src));
-
- char *section = "";
- do {
- printf("%ld: SECTION \"%s\"\n", ini.line, section);
-
- char *key, *value;
- while ((key = any_ini_next_key(&ini)) != NULL) {
- value = any_ini_next_value(&ini);
- printf("%ld: \"%s\" = \"%s\"\n", ini.line, key, value);
- }
- } while ((section = any_ini_next_section(&ini)) != NULL);
-
- return 0;
+ printf("INI STRING TEST\n");
+ test_ini();
+
+ printf("\nINI STREAM TEST\n");
+ test_ini_stream();
+
+ return 0;
}
diff --git a/test/test.ini b/test/test.ini
new file mode 100644
index 0000000..c09d7e6
--- /dev/null
+++ b/test/test.ini
@@ -0,0 +1,30 @@
+ciao=sus
+
+[hello]
+world=true
+
+this=is sparta
+
+my name is = slim shady
+
+123 = 456
+
+; comment
+ ; another
+crazy = stuff ; here
+ ; x = 1
+ ;
+ xx = 123
+
+[another]
+
+ ciao = s
+
+]err = sus[
+
+ [empty]
+
+; yes
+;
+; indeed
+