diff options
author | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2021-08-18 04:54:45 +0300 |
---|---|---|
committer | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2021-08-18 04:54:45 +0300 |
commit | 544100da719843438372c6e30739bf9b5d3ebb9c (patch) | |
tree | 00894be08c33d97dd0873052d01609f5a78c2a54 /include | |
download | parcini-544100da719843438372c6e30739bf9b5d3ebb9c.tar.gz parcini-544100da719843438372c6e30739bf9b5d3ebb9c.zip |
Init
Basically functional.
Diffstat (limited to 'include')
-rw-r--r-- | include/parcini.h | 136 | ||||
-rw-r--r-- | include/tests/tests.h | 45 |
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 |