aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2021-08-18 04:54:45 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2021-08-18 04:54:45 +0300
commit544100da719843438372c6e30739bf9b5d3ebb9c (patch)
tree00894be08c33d97dd0873052d01609f5a78c2a54 /include
downloadparcini-544100da719843438372c6e30739bf9b5d3ebb9c.tar.gz
parcini-544100da719843438372c6e30739bf9b5d3ebb9c.zip
Init
Basically functional.
Diffstat (limited to 'include')
-rw-r--r--include/parcini.h136
-rw-r--r--include/tests/tests.h45
2 files changed, 181 insertions, 0 deletions
diff --git a/include/parcini.h b/include/parcini.h
new file mode 100644
index 0000000..3bcaf59
--- /dev/null
+++ b/include/parcini.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2021 Yaroslav de la Peña Smirnov
+ *
+ * Released under the GNU Lesser General Public License, version 2.1 only.
+ * For more information see `LICENSE' or visit
+ * https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
+ */
+#ifndef PARCINI_H
+#define PARCINI_H
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* You can choose to use a different character for comments (e.g. ';') at
+ * compile time */
+#ifndef PARCINI_COMMENT_CHAR
+#define PARCINI_COMMENT_CHAR '#'
+#endif
+
+/*
+ * An opaque structure that represents an instance of a "parcini" ini parser,
+ * used to parse a stream.
+ */
+typedef struct parcini parcini_t;
+
+/*
+ * All possible values to be returned by parcini_parse_next_line.
+ */
+enum parcini_result {
+ /* A line with only whitespace and/or a comment */
+ PARCINI_EMPTY_LINE,
+ /* There are no more lines to parse */
+ PARCINI_EOF,
+ /* A section. parcini_line.key shall be NULL and parcini_line.value.type
+ * shalle be PARCINI_VALUE_NONE */
+ PARCINI_SECTION,
+ /* Key/value. parcini_line.key contains the name of the key, and
+ * parcini_line.value contains the type and actual value. */
+ PARCINI_KEYVALUE,
+ /* A call to malloc/realloc failed. errno might be set */
+ PARCINI_MEMORY_ERROR,
+ /* An error other than EOF occured when attempting to read the stream. errno
+ * might be set */
+ PARCINI_STREAM_ERROR,
+ /* What seemed to start as a key is incorrectly formated, and thus couldn't
+ * be parsed correctly. */
+ PARCINI_KEY_PARSE_ERROR,
+ /* Where a value should have followed a key, there is none, or it is
+ * incorrectly formated, and thus couldn't be parsed correctly. */
+ PARCINI_VALUE_PARSE_ERROR,
+ /* The value is an integer number that couldn't be parsed correctly due to
+ * it being out of the range of a long int */
+ PARCINI_VALUE_RANGE_ERROR,
+ /* What seemed to be a line containing a section name, wasn't formatted
+ * correctly and thus couldn't be parsed. */
+ PARCINI_SECTION_PARSE_ERROR,
+};
+
+enum parcini_value_type {
+ /* There is no value */
+ PARCINI_VALUE_NONE,
+ /* The value is long int */
+ PARCINI_VALUE_INTEGER,
+ /* The value is bool */
+ PARCINI_VALUE_BOOLEAN,
+ /* The value is pointer to char */
+ PARCINI_VALUE_STRING,
+};
+
+/*
+ * A tagged union-like structure representing a parsed value. Accessing a .value
+ * member for which the type is not set is undefined behaviour (beware of the
+ * char *, it could segfault your program).
+ */
+struct parcini_value {
+ enum parcini_value_type type;
+ union {
+ long int integer;
+ bool boolean;
+ char *string;
+ } value;
+};
+
+/*
+ * This structure should be passed as a pointer to parcini_parse_next_line so
+ * that it is filled with the parsed information. The pointers here MUST NOT be
+ * manually freed. See function parcini_parse_next_line for more details.
+ */
+struct parcini_line {
+ size_t lineno;
+ char *section;
+ char *key;
+ struct parcini_value value;
+};
+
+/*
+ * Parse the next line in the stream returning the kind of result/error of
+ * parsing and filling the parcini_line structure with the information that was
+ * extracted from the line, as well as the current section (including after
+ * parsing a key/value line) and line number.
+ *
+ * If you need the information returned to last after the next call to this
+ * function, or after the destruction of the parcini_t parser instance, then you
+ * should copy it to a different variable/pointer before parsing the next
+ * line/freeing your parser instance. This data also MUST NOT be manually freed
+ * by the caller.
+ */
+enum parcini_result parcini_parse_next_line(parcini_t *, struct parcini_line *);
+
+/*
+ * Main function through which you initialize a parcini_t parser object, before
+ * you can parse the stream line by line.
+ */
+parcini_t *parcini_init(FILE *stream);
+
+/*
+ * Helper function to open a file and then initialize a parcini_t object with
+ * said as the source stream.
+ */
+parcini_t *parcini_from_file(const char *fpath);
+
+/*
+ * Helper function to initialize a parcini_t object with said string of said
+ * size open as a stream.
+ */
+parcini_t *parcini_from_string(const char *, const size_t);
+
+/*
+ * Free all memory related to the parcini_t instance. IMPORTANT: this will also
+ * close the stream passed to parcini_init, so if you manually opened a file and
+ * then passed it to parcini_init, it WILL NOT be valid after a call to
+ * parcini_destroy.
+ */
+void parcini_destroy(parcini_t *);
+
+#endif
diff --git a/include/tests/tests.h b/include/tests/tests.h
new file mode 100644
index 0000000..8c89fcc
--- /dev/null
+++ b/include/tests/tests.h
@@ -0,0 +1,45 @@
+#ifndef TESTS_H
+#define TESTS_H
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef NOCOLOR
+#define TBLD "\033[1m"
+#define TRED "\033[31m"
+#define TGRN "\033[32m"
+#define TBLU "\033[34m"
+#define TRST "\033[0m"
+#else
+#define TBLD ""
+#define TRED ""
+#define TGRN ""
+#define TBLU ""
+#define TRST ""
+#endif
+
+#define RUN_TEST(test_func) \
+ printf("%s:\t", #test_func); \
+ fflush(stdout); \
+ test_func(); \
+ printf(TGRN "OK!\n" TRST)
+
+#define INIT_TESTS() \
+ printf(TBLD "running %s tests\n" TRST, __FILE__)
+
+#define FAIL_TEST(reason) \
+ printf(TBLD TRED "FAIL!\n" TRST); \
+ printf("%s:%d: %s: ", __FILE__, __LINE__, __func__); \
+ printf(reason); \
+ abort()
+
+#define asserteq(a, b) \
+ if (a != b) { \
+ FAIL_TEST("assertion " TBLD TBLU #a " == " #b TRST " failed\n"); \
+ }
+
+#define assertneq(a, b) \
+ if (a == b) { \
+ FAIL_TEST("assertion " TBLD TBLU #a " != " #b TRST " failed\n"); \
+ }
+
+#endif