diff options
| author | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2021-08-19 01:42:49 +0300 | 
|---|---|---|
| committer | Yaroslav de la Peña Smirnov <yps@yaroslavps.com> | 2021-08-19 01:42:49 +0300 | 
| commit | 6e20bab8f5fcfbe4fe816425704f9edd8ad88444 (patch) | |
| tree | 5f11825e1afc4dbc75ed9778afcdd9e12cddcb05 | |
| parent | 5ed7b33fa8ce0c91af1a58bc17636d6bf0406d24 (diff) | |
| download | parcini-6e20bab8f5fcfbe4fe816425704f9edd8ad88444.tar.gz parcini-6e20bab8f5fcfbe4fe816425704f9edd8ad88444.zip  | |
Example and new helper funcion
* Added an example program an ini file to example/
* Added a new helper function `parcini_value_handle` to handle and copy
  parsed values, depending on the expected value type.
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | README.md | 11 | ||||
| -rw-r--r-- | example/example.c | 99 | ||||
| -rw-r--r-- | example/example.ini | 8 | ||||
| -rw-r--r-- | include/parcini.h | 17 | ||||
| -rw-r--r-- | src/parcini.c | 27 | 
6 files changed, 167 insertions, 3 deletions
@@ -12,16 +12,22 @@ BUILD_DIR:=build  test: CFLAGS := -std=c99 -O0 -g -Wall -DDEBUG  test: $(BUILD_DIR) tests/parcini -tests/%: $(OBJ_DIR)/src/tests/%.o $(OBJ_DIR)/src/%.o $(PARCINI_OBJECTS) +tests/%: $(OBJ_DIR)/src/tests/%.o $(PARCINI_OBJECTS)  	$(CC) $(LDFLAGS) -o $(BUILD_DIR)/$@ $^  	$(BUILD_DIR)/$@ +example: CFLAGS := -std=c99 -O2 -Wall +example: $(OBJ_DIR)/example/example.o $(PARCINI_OBJECTS) +	$(CC) $(LDFLAGS) -o $(BUILD_DIR)/example/$@ $^ +  $(OBJ_DIR)/%.o: %.c  	$(CC) -c $(CFLAGS) $(INC_DIRS) -o $@ $<  $(BUILD_DIR):  	mkdir -p $(BUILD_DIR)/tests +	mkdir -p $(BUILD_DIR)/example  	mkdir -p $(OBJ_DIR)/src/tests +	mkdir -p $(OBJ_DIR)/example  clean:  	$(RM) $(OBJ_DIR) @@ -67,4 +67,13 @@ key="value" # another comment  # Example program -*TODO* +Check out `example/example.c` for an example of a program using parcini. You can +compile it by running `make example`, it will be compiled to +`build/example/example`. + +# Questions + +I don't expect this piece of code to be used by a lot of people (if any), but if +you have any questions, or maybe even patches, send me an email. You can find my +address and my PGP key on my homepage  +[www.yaroslavps.com](http://www.yaroslavps.com). diff --git a/example/example.c b/example/example.c new file mode 100644 index 0000000..1036845 --- /dev/null +++ b/example/example.c @@ -0,0 +1,99 @@ +#define _POSIX_C_SOURCE 200809L +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "parcini.h" + +#define EXAMPLE_INI "example/example.ini" + +struct { +	struct { +		char *name; +		long int age; +	} personal; +	struct { +		char *email; +		bool visible; +	} contact; +} example = { +	.personal.name = NULL, +	.contact.email = NULL, +}; + +static bool +handle_keyvalue(struct parcini_line *parsed) +{ +#define MATCHES(s, k) !strcmp(parsed->section, s) && !strcmp(parsed->key, k) + +	if (MATCHES("personal", "name")) { +		return parcini_value_handle(&parsed->value, PARCINI_VALUE_STRING, +				&example.personal.name); +	} +	if (MATCHES("personal", "age")) { +		return parcini_value_handle(&parsed->value, PARCINI_VALUE_INTEGER, +				&example.personal.age); +	} +	if (MATCHES("contact", "email")) { +		return parcini_value_handle(&parsed->value, PARCINI_VALUE_STRING, +				&example.contact.email); +	} +	if (MATCHES("contact", "visible")) { +		return parcini_value_handle(&parsed->value, PARCINI_VALUE_BOOLEAN,  +				&example.contact.visible); +	} + +	return false; +} + +int +main(void) +{ +	parcini_t *parser = parcini_from_file(EXAMPLE_INI); +	if (parser == NULL) { +		fprintf(stderr, "error opening file: %s\n", strerror(errno)); +		return 1; +	} + +	struct parcini_line parsed; +	enum parcini_result res; +	while ((res = parcini_parse_next_line(parser, &parsed)) != PARCINI_EOF) { +		switch (res) { +		case PARCINI_EMPTY_LINE: +		case PARCINI_SECTION: +			continue; + +		case PARCINI_KEYVALUE: +			if (!handle_keyvalue(&parsed)) { +				fprintf(stderr,  +						"line %ld: error: unexpected section '%s'/key '%s' or " +						"invalid value type\n", +						parsed.lineno, parsed.section, parsed.key); +				goto cleanup; +			} +			continue; + +		case PARCINI_MEMORY_ERROR: +		case PARCINI_STREAM_ERROR: +			fprintf(stderr,  +					"An error occurred while trying to read the next line\n"); +			goto cleanup; + +		default: +			fprintf(stderr, "line %ld: parse error\n", parsed.lineno); +			goto cleanup; +		}; +	} + +	printf("parsed example.ini:\n"); +	printf("name:\t%s\n", example.personal.name); +	printf("age:\t%ld\n", example.personal.age); +	printf("email:\t%s\n", example.contact.email); +	printf("visible:\t%s\n", example.contact.visible ? "true" : "false"); + +cleanup: +	free(example.personal.name); +	free(example.contact.email); +	parcini_destroy(parser); +} diff --git a/example/example.ini b/example/example.ini new file mode 100644 index 0000000..9dd5a0e --- /dev/null +++ b/example/example.ini @@ -0,0 +1,8 @@ +# An example ini file +[personal] +name = "Ivan Ivanovich Ivanov" +age = 69 + +[contact] +email = "ivan@mail.xyz" +visible = yes diff --git a/include/parcini.h b/include/parcini.h index 3bcaf59..d485ffd 100644 --- a/include/parcini.h +++ b/include/parcini.h @@ -94,6 +94,21 @@ struct parcini_line {  };  /* + * 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 @@ -115,7 +130,7 @@ 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. + * said as the source stream. Returns NULL if the file couldn't be opened.   */  parcini_t *parcini_from_file(const char *fpath); diff --git a/src/parcini.c b/src/parcini.c index 6c19d7a..83b5de4 100644 --- a/src/parcini.c +++ b/src/parcini.c @@ -74,6 +74,33 @@ slicecpy(char *start, char *end, char **dst, size_t *dstn)  	return *dst;  } +bool +parcini_value_handle(const struct parcini_value *value, +		const enum parcini_value_type expected, void *dst) +{ + +	if (value->type != expected) { +		return false; +	} +	if(expected == PARCINI_VALUE_STRING) { +		char **string = (char **)dst; +		*string = strdup(value->value.string); +		return true; +	} +	if(expected == PARCINI_VALUE_INTEGER) { +		long int *integer = (long int *)dst; +		*integer = value->value.integer; +		return true; +	} +	if(expected == PARCINI_VALUE_BOOLEAN) { +		bool *boolean = (bool *)dst; +		*boolean = value->value.boolean; +		return true; +	} + +	return false; +} +  enum parcini_result  parcini_parse_next_line(parcini_t *parser, struct parcini_line *parsed)  {  | 
