From 97840a33d5e8ae1c03eaa4f4454b87bfb86a4ba5 Mon Sep 17 00:00:00 2001 From: Dmitri Pal Date: Wed, 21 Apr 2010 21:16:18 -0400 Subject: Introducing a comment object Comment object will store the comments found in the INI file. It is based on the ref_array interface. Fixing review comments for comment obj. --- ini/Makefile.am | 15 +- ini/ini_comment.c | 509 +++++++++++++++++++++++++++++++++++++++++++++++++++ ini/ini_comment.h | 106 +++++++++++ ini/ini_comment_ut.c | 176 ++++++++++++++++++ ini/ini_config_ut.c | 4 +- 5 files changed, 804 insertions(+), 6 deletions(-) create mode 100644 ini/ini_comment.c create mode 100644 ini/ini_comment.h create mode 100644 ini/ini_comment_ut.c diff --git a/ini/Makefile.am b/ini/Makefile.am index 1dc55d9..ee6c6de 100644 --- a/ini/Makefile.am +++ b/ini/Makefile.am @@ -13,7 +13,8 @@ if HAVE_GCC -Wcast-align -Wwrite-strings endif -AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection -I$(topdir)/path_utils $(TRACE_LEVEL) +AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection \ + -I$(topdir)/path_utils -I$(topdir)/refarray $(TRACE_LEVEL) ACLOCAL_AMFLAGS = -I m4 @@ -43,21 +44,27 @@ libini_config_la_SOURCES = \ ini_parse.h \ ini_metadata.c \ ini_metadata.h \ - ini_defines.h + ini_defines.h \ + ini_comment.c \ + ini_comment.h libini_config_la_LIBADD = \ -L$(topbuilddir)/collection \ -L$(topbuilddir)/path_utils \ + -L$(topbuilddir)/refarray \ -lcollection \ + -lref_array \ -lpath_utils libini_config_la_LDFLAGS = \ -version-info 2:0:0 # Build unit test -check_PROGRAMS = ini_config_ut +check_PROGRAMS = ini_config_ut ini_comment_ut ini_config_ut_SOURCES = ini_config_ut.c ini_config_ut_LDADD = libini_config.la -L$(topbuilddir)/collection -lcollection +ini_comment_ut_SOURCES = ini_comment_ut.c +ini_comment_ut_LDADD = libini_config.la if HAVE_DOXYGEN docs: @@ -71,7 +78,7 @@ docs: @exit 1 endif -TESTS = ini_config_ut +TESTS = ini_config_ut ini_comment_ut tests: all $(check_PROGRAMS) diff --git a/ini/ini_comment.c b/ini/ini_comment.c new file mode 100644 index 0000000..30d21b2 --- /dev/null +++ b/ini/ini_comment.c @@ -0,0 +1,509 @@ +/* + INI LIBRARY + + Object to handle comments + + Copyright (C) Dmitri Pal 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include "config.h" +#include "trace.h" +#include "ref_array.h" +#include "ini_comment.h" + +/* The lines will increment in this number */ +#define INI_COMMENT_BLOCK 10 + +/***************************/ +/* Internal comment states */ +/***************************/ +/* Empty - initial */ +#define INI_COMMENT_EMPTY 0 +/* Read - read from file */ +#define INI_COMMENT_READ 1 +/* Comment was altered */ +#define INI_COMMENT_CHANGED 2 + + +/*********************************/ +/* Modes to wrap ref array calls */ +/*********************************/ +#define INI_COMMENT_MODE_BUILD 1 +#define INI_COMMENT_MODE_APPEND 2 +#define INI_COMMENT_MODE_INSERT 3 +#define INI_COMMENT_MODE_REPLACE 4 +#define INI_COMMENT_MODE_REMOVE 5 +#define INI_COMMENT_MODE_CLEAR 6 + +/****************************************/ +/* Internal structure to hold a comment */ +/****************************************/ +struct ini_comment { + struct ref_array *ra; + uint32_t state; +}; + + +/****************************************/ + +/* Destroy the comment object */ +void ini_comment_destroy(struct ini_comment *ic) +{ + + TRACE_FLOW_STRING("ini_comment_destroy", "Entry"); + if (ic) { + /* Function will check for NULL */ + ref_array_destroy(ic->ra); + free(ic); + } + TRACE_FLOW_STRING("ini_comment_destroy", "Exit"); +} + + +/* Cleanup callback */ +void ini_comment_cb(void *elem, + ref_array_del_enum type, + void *data) +{ + + TRACE_FLOW_STRING("ini_comment_cb", "Entry"); + free(*((char **)elem)); + TRACE_FLOW_STRING("ini_comment_cb", "Exit"); +} + + +/* Create a comment object */ +int ini_comment_create(struct ini_comment **ic) +{ + int error = EOK; + struct ref_array *ra = NULL; + struct ini_comment *ic_new = NULL; + + TRACE_FLOW_STRING("ini_comment_create", "Entry"); + + error = ref_array_create(&ra, + sizeof(char *), + INI_COMMENT_BLOCK, + ini_comment_cb, + NULL); + if (error) { + TRACE_ERROR_NUMBER("Error creating ref array", error); + return error; + } + + ic_new = malloc(sizeof(struct ini_comment)); + if (!ic_new) { + TRACE_ERROR_NUMBER("Memory allocation error", ENOMEM); + ref_array_destroy(ic_new->ra); + return ENOMEM; + } + + /* Initialize members here */ + ic_new->ra = ra; + ic_new->state = INI_COMMENT_EMPTY; + + *ic = ic_new; + + TRACE_FLOW_STRING("ini_comment_create", "Exit"); + return error; +} + + +/* Is the comment valid? */ +static int ini_comment_is_valid(const char *line) +{ + int i; + + TRACE_FLOW_STRING("ini_comment_is_valid", "Entry"); + + /* Null is ok */ + if (!line) { + TRACE_FLOW_STRING("ini_comment_is_valid", "Exit - NULL str"); + return 1; + } + + /* Empty is Ok or starts with a special symbol */ + if ((line[0] == '\0') || + (line[0] == ';') || + (line[0] == '#')) { + TRACE_FLOW_STRING("ini_comment_is_valid", "Exit - empty or comment"); + return 1; + } + + /* All spaces is Ok too */ + TRACE_INFO_STRING("Line to eval", line); + + i = 0; + while (line[i] != '\0') { + if (!isspace(line[i])) { + TRACE_ERROR_STRING("ini_comment_is_valid", "Invalid comment"); + return 0; + } + i++; + } + + TRACE_FLOW_STRING("ini_comment_is_valid", "Exit - empty str"); + return 1; + +} + + +/* + * Modify the comment object + */ +static int ini_comment_modify(struct ini_comment *ic, + int mode, + uint32_t idx, + const char *line) +{ + int error = EOK; + char *elem = NULL; + char *input = NULL; + char *empty = NULL; + uint32_t i, len = 0; + + TRACE_FLOW_STRING("ini_comment_modify", "Entry"); + + if (!ic) { + TRACE_ERROR_NUMBER("Invalid comment object", EINVAL); + return EINVAL; + } + + + if (mode == INI_COMMENT_MODE_BUILD) { + /* + * Can use this function only if object is empty or + * reading from the file. + */ + if ((ic->state != INI_COMMENT_EMPTY) && + (ic->state != INI_COMMENT_READ)) { + TRACE_ERROR_NUMBER("Invalid use of the function", EINVAL); + return EINVAL; + } + } + + /* Make sure that we ignore "line" in reset case */ + if (mode != INI_COMMENT_MODE_CLEAR) + memcpy(&input, &line, sizeof(char *)); + + if (mode != INI_COMMENT_MODE_REMOVE) { + /* + * Check that provided line is a comment or an empty line. + * Can be NULL too. + */ + if (!ini_comment_is_valid(input)) { + TRACE_ERROR_NUMBER("Invalid comment", EINVAL); + return EINVAL; + } + + /* Dup it */ + if (input) elem = strdup(input); + else elem = strdup(""); + + if (!elem) { + TRACE_ERROR_NUMBER("Memory allocation error", ENOMEM); + return ENOMEM; + } + } + + /* Do action depending on mode */ + switch (mode) { + case INI_COMMENT_MODE_BUILD: + + TRACE_INFO_STRING("BUILD mode", ""); + error = ref_array_append(ic->ra, &elem); + break; + + case INI_COMMENT_MODE_APPEND: + + TRACE_INFO_STRING("Append mode", ""); + error = ref_array_append(ic->ra, &elem); + break; + + case INI_COMMENT_MODE_INSERT: + + TRACE_INFO_STRING("Insert mode", ""); + len = ref_array_len(ic->ra); + if (idx > len) { + /* Fill in empty lines */ + for (i = 0; i < (idx-len); i++) { + empty = strdup(""); + if (empty) { + TRACE_ERROR_NUMBER("Memory problem", ENOMEM); + return ENOMEM; + } + error = ref_array_append(ic->ra, &empty); + if (error) { + TRACE_ERROR_NUMBER("Append problem", error); + free(empty); + return error; + } + } + /* Append last line */ + error = ref_array_append(ic->ra, &elem); + } + else { + /* Insert inside the array */ + error = ref_array_insert(ic->ra, idx, &elem); + } + break; + + + case INI_COMMENT_MODE_REPLACE: + + TRACE_INFO_STRING("Replace mode", ""); + error = ref_array_replace(ic->ra, idx, &elem); + break; + + case INI_COMMENT_MODE_REMOVE: + + TRACE_INFO_STRING("Remove mode", ""); + error = ref_array_remove(ic->ra, idx); + break; + + case INI_COMMENT_MODE_CLEAR: + + TRACE_INFO_STRING("Clear mode", ""); + error = ref_array_replace(ic->ra, idx, &elem); + break; + + default : + + TRACE_ERROR_STRING("Coding error", ""); + error = EINVAL; + + } + + if (error) { + TRACE_ERROR_NUMBER("Failed to append line to an array", error); + free(elem); + return error; + } + + /* Change state */ + if (INI_COMMENT_MODE_BUILD) ic->state = INI_COMMENT_READ; + else ic->state = INI_COMMENT_CHANGED; + + + TRACE_FLOW_STRING("ini_comment_modify", "Exit"); + return error; +} + +/* + * Build up a comment object - use this when reading + * comments from a file. + */ +int ini_comment_build(struct ini_comment *ic, const char *line) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_build", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_BUILD, 0, line); + + TRACE_FLOW_NUMBER("ini_comment_build - Returning", error); + return error; +} + +/* + * Modify comment by instering a line. + */ +int ini_comment_insert(struct ini_comment *ic, + uint32_t idx, + const char *line) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_insert", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_INSERT, idx, line); + + TRACE_FLOW_NUMBER("ini_comment_insert - Returning", error); + return error; +} + +/* Modify comment by appending a line. */ +int ini_comment_append(struct ini_comment *ic, const char *line) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_append", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_APPEND, 0, line); + + TRACE_FLOW_NUMBER("ini_comment_append - Returning", error); + return error; +} + +/* Remove line from the comment.*/ +int ini_comment_remove(struct ini_comment *ic, uint32_t idx) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_remove", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_REMOVE, idx, NULL); + + TRACE_FLOW_NUMBER("ini_comment_remove - Returning", error); + return error; +} + +/* Clear line in the comment. Line is replaced with an empty line */ +int ini_comment_clear(struct ini_comment *ic, uint32_t idx) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_clear", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_CLEAR, idx, NULL); + + TRACE_FLOW_NUMBER("ini_comment_clear - Returning", error); + return error; + +} + +/* Replace a line in the comment */ +int ini_comment_replace(struct ini_comment *ic, + uint32_t idx, + const char *line) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_replace", "Entry"); + + error = ini_comment_modify(ic, INI_COMMENT_MODE_REPLACE, idx, line); + + TRACE_FLOW_NUMBER("ini_comment_replace - Returning", error); + return error; +} + + +/* Reset the comment - clean all lines.*/ +int ini_comment_reset(struct ini_comment *ic) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_reset", "Entry"); + + if (!ic) { + TRACE_ERROR_NUMBER("Invalid comment object", EINVAL); + return EINVAL; + } + + /* Reset comment if it is not empty */ + if (ic->state != INI_COMMENT_EMPTY) { + ref_array_reset(ic->ra); + ic->state = INI_COMMENT_CHANGED; + } + + TRACE_FLOW_STRING("ini_comment_reset", "Exit"); + return error; +} + +/* Get number of lines */ +int ini_comment_get_numlines(struct ini_comment *ic, uint32_t *num) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_get_numlines", "Entry"); + + if ((!ic) || (!num)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + error = ref_array_getlen(ic->ra, num); + + TRACE_FLOW_NUMBER("ini_comment_get_numlines - Returning", error); + return error; + +} + +/* Get line */ +int ini_comment_get_line(struct ini_comment *ic, uint32_t idx, char **line) +{ + int error = EOK; + void *res = NULL; + + TRACE_FLOW_STRING("ini_comment_get_line", "Entry"); + + if ((!ic) || (!line)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + res = ref_array_get(ic->ra, idx, (void *)line); + if (!res) { + error = EINVAL; + *line = NULL; + } + TRACE_FLOW_NUMBER("ini_comment_get_line - Returning", error); + return error; +} + +/* Swap lines */ +int ini_comment_swap(struct ini_comment *ic, + uint32_t idx1, + uint32_t idx2) +{ + int error = EOK; + + TRACE_FLOW_STRING("ini_comment_swap", "Entry"); + + if (!ic) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + error = ref_array_swap(ic->ra, idx1, idx2); + if ((!error) && (idx1 != idx2)) { + ic->state = INI_COMMENT_CHANGED; + } + + TRACE_FLOW_NUMBER("ini_comment_swap - Returning", error); + return error; +} + + +/* Internal function to print comment */ +void ini_comment_print(struct ini_comment *ic, FILE *file) +{ + int len; + int i; + char *ret = NULL; + + TRACE_FLOW_STRING("ini_comment_print", "Entry"); + + if (!file) { + TRACE_ERROR_NUMBER("Invalid file argument", EINVAL); + return; + } + + if (ic) { + len = ref_array_len(ic->ra); + for (i = 0; i < len; i++) { + ref_array_get(ic->ra, i, &ret); + fprintf(file, "%s\n", ret); + } + } + + TRACE_FLOW_STRING("ini_comment_print", "Exit"); + +} diff --git a/ini/ini_comment.h b/ini/ini_comment.h new file mode 100644 index 0000000..05c6a72 --- /dev/null +++ b/ini/ini_comment.h @@ -0,0 +1,106 @@ +/* + INI LIBRARY + + Header file for comment object. + + Copyright (C) Dmitri Pal 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#ifndef INI_COMMENT_H +#define INI_COMMENT_H + +#include +#include + +#ifndef EOK +#define EOK 0 +#endif + +struct ini_comment; + +/** + * Create a comment object + */ +int ini_comment_create(struct ini_comment **ic); + +/** + * Destroy the comment object + */ +void ini_comment_destroy(struct ini_comment *ic); + +/** + * Build up a comment object - use this when reading + * comments from a file. + */ +int ini_comment_build(struct ini_comment *ic, + const char *line); + +/** + * Modify comment by instering a line. + * + * idx can be: + * 0 - as first + * 1 - after first + * 2 - after second + * ... + * If greater than number of lines + * missing lines are added automatically + * as empty lines + */ +int ini_comment_insert(struct ini_comment *ic, + uint32_t idx, + const char *line); + +/* Modify comment by appending a line. */ +int ini_comment_append(struct ini_comment *ic, + const char *line); + +/* Remove line from the comment.*/ +int ini_comment_remove(struct ini_comment *ic, + uint32_t idx); + +/* Clear line in the comment. Line is replaced with an empty line */ +int ini_comment_clear(struct ini_comment *ic, + uint32_t idx); + +/* Replace a line in the comment */ +int ini_comment_replace(struct ini_comment *ic, + uint32_t idx, + const char *line); + +/* Reset the comment - clean all lines.*/ +int ini_comment_reset(struct ini_comment *ic); + +/* Get number of lines */ +int ini_comment_get_numlines(struct ini_comment *ic, + uint32_t *num); + +/* Get line */ +int ini_comment_get_line(struct ini_comment *ic, + uint32_t idx, + char **line); + +/* Swap lines */ +int ini_comment_swap(struct ini_comment *ic, + uint32_t idx1, + uint32_t idx2); + + +/* Internal function to print comment */ +void ini_comment_print(struct ini_comment *ic, FILE *file); + + +#endif diff --git a/ini/ini_comment_ut.c b/ini/ini_comment_ut.c new file mode 100644 index 0000000..40c15b8 --- /dev/null +++ b/ini/ini_comment_ut.c @@ -0,0 +1,176 @@ +/* + INI LIBRARY + + Unit test for the comment object. + + Copyright (C) Dmitri Pal 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see . +*/ + +#include +#include +#include +#define TRACE_HOME +#include "trace.h" +#include "ini_comment.h" + +int verbose = 0; + +#define INIOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + +int file_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, ";Line 1")) || + (error = ini_comment_build(ic, ";Line 2"))) { + printf("Failed to create comment object %d\n", + error); + ini_comment_destroy(ic); + return -1; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + ini_comment_destroy(ic); + + return error; +} + + +int alter_test(void) +{ + int error = EOK; + struct ini_comment *ic = NULL; + uint32_t i, num = 0; + char *line = NULL; + const char *expected[] = { ";Line 0 inserted", + ";Line 1 inserted", + "", + "", + ";Line 3 replaced", + "", + ";Line 4" }; + + + if ((error = ini_comment_create(&ic)) || + (error = ini_comment_build(ic, ";Line 0")) || + (error = ini_comment_build(ic, NULL)) || + (error = ini_comment_build(ic, ";Line 2")) || + (error = ini_comment_build(ic, ";Line 3")) || + (error = ini_comment_build(ic, ""))) { + printf("Failed to create comment object\n"); + ini_comment_destroy(ic); + return -1; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + if ((error = ini_comment_append(ic, ";Line 4")) || + (error = ini_comment_clear(ic, 2)) || + (error = ini_comment_replace(ic, 3, ";Line 3 replaced")) || + (error = ini_comment_remove(ic, 0)) || + (error = ini_comment_insert(ic, 0, ";Line 0 inserted")) || + (error = ini_comment_insert(ic, 1, ";Line 1 inserted"))) { + printf("Failed to create comment object\n"); + ini_comment_destroy(ic); + return -1; + } + + INIOUT(printf("<==== Comment ====>\n")); + INIOUT(ini_comment_print(ic, stdout)); + INIOUT(printf("<=================>\n")); + + error = ini_comment_get_numlines(ic, &num); + if (error) { + printf("Failed to get number of lines.\n"); + ini_comment_destroy(ic); + return -1; + } + + for (i = 0; i < num; i++) { + error = ini_comment_get_line(ic, i, &line); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + return -1; + } + if (strcmp(line, expected[i]) != 0) { + printf("Lines do not match.\n"); + printf("GOT: %s\n", line); + printf("EXP: %s\n", expected[i]); + ini_comment_destroy(ic); + return -1; + } + } + + INIOUT(printf("\n\nSwap test\n\n")); + + if ((error = ini_comment_swap(ic, 0 , 6)) || + (error = ini_comment_swap(ic, 1 , 5)) || + (error = ini_comment_swap(ic, 2 , 4))) { + printf("Failed to swap lines.\n"); + ini_comment_destroy(ic); + return -1; + } + + for (i = 0; i < num; i++) { + error = ini_comment_get_line(ic, i, &line); + if (error) { + printf("Failed to get line.\n"); + ini_comment_destroy(ic); + return -1; + } + if (strcmp(line, expected[6 - i]) != 0) { + printf("Lines do not match.\n"); + printf("GOT: %s\n", line); + printf("EXP: %s\n", expected[6 - i]); + ini_comment_destroy(ic); + return -1; + } + } + + ini_comment_destroy(ic); + return error; +} + + +int main(int argc, char *argv[]) +{ + int error = EOK; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + + INIOUT(printf("Start\n")); + + if ((error = file_test()) || + (error = alter_test())) { + printf("Test failed! Error %d.\n", error); + return -1; + } + + INIOUT(printf("Success!\n")); + return 0; +} diff --git a/ini/ini_config_ut.c b/ini/ini_config_ut.c index 55a8b33..c9b13fd 100644 --- a/ini/ini_config_ut.c +++ b/ini/ini_config_ut.c @@ -1319,7 +1319,7 @@ int main(int argc, char *argv[]) if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; - printf("Start\n"); + COLOUT(printf("Start\n")); srcdir = getenv("srcdir"); if(srcdir) { @@ -1343,6 +1343,6 @@ int main(int argc, char *argv[]) return -1; } - printf("Success!\n"); + COLOUT(printf("Success!\n")); return 0; } -- cgit