/* * 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 #include #include /* 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; }; /* * Helper function to handle a value that was parsed by parcini. Pass a pointer * of a parcini_value, and the expected type. If the parcini_value typematches * the expected type, then *dst is set to the provided value casting it to the * correct type. * * IMPORTANT: in the case of "string" values, you need to pass a pointer to a * char pointer (i.e. char **), and the memory in the string value is copied * over to the char *. That means, that if some memory was already allocated in * the passed pointer, there could be a memory leak if you don't free that * memory before, unless you have another pointer to that same memory elsewhere. */ bool parcini_value_handle(const struct parcini_value *, const enum parcini_value_type expected, void *dst); /* * 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. Returns NULL if the file couldn't be opened. */ 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