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 | 
