diff options
author | Dmitri Pal <dpal@redhat.com> | 2010-06-10 11:57:25 -0400 |
---|---|---|
committer | Dmitri Pal <dpal@redhat.com> | 2010-08-10 12:51:32 -0400 |
commit | 85acfe3f0e53db93a741040d12642083260664f6 (patch) | |
tree | e1f62378ff562252e9cb5495baed25c45cefaa8b | |
parent | 67177f82868a56a255b0eda2003b929d0bdd5f09 (diff) | |
download | sssd-85acfe3f0e53db93a741040d12642083260664f6.tar.gz sssd-85acfe3f0e53db93a741040d12642083260664f6.tar.xz sssd-85acfe3f0e53db93a741040d12642083260664f6.zip |
[INI] Introducing Value object
Value object is an object that combines
a potentially multiline value
comment and some statistical information
regarding a configuration value.
Patch includes:
Source
Header
Unit test
Makefile changes
-rw-r--r-- | common/ini/Makefile.am | 22 | ||||
-rw-r--r-- | common/ini/ini_valueobj.c | 901 | ||||
-rw-r--r-- | common/ini/ini_valueobj.h | 130 | ||||
-rw-r--r-- | common/ini/ini_valueobj_ut.c | 538 |
4 files changed, 1585 insertions, 6 deletions
diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am index ee6c6de..eca3bb0 100644 --- a/common/ini/Makefile.am +++ b/common/ini/Makefile.am @@ -14,7 +14,8 @@ if HAVE_GCC endif AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection \ - -I$(topdir)/path_utils -I$(topdir)/refarray $(TRACE_LEVEL) + -I$(topdir)/path_utils -I$(topdir)/refarray \ + -I$(topdir)/basicobjects $(TRACE_LEVEL) ACLOCAL_AMFLAGS = -I m4 @@ -46,13 +47,16 @@ libini_config_la_SOURCES = \ ini_metadata.h \ ini_defines.h \ ini_comment.c \ - ini_comment.h + ini_comment.h \ + ini_valueobj.c \ + ini_valueobj.h libini_config_la_LIBADD = \ -L$(topbuilddir)/collection \ -L$(topbuilddir)/path_utils \ -L$(topbuilddir)/refarray \ + -L$(topbuilddir)/basicobjects \ -lcollection \ -lref_array \ -lpath_utils @@ -60,11 +64,17 @@ libini_config_la_LDFLAGS = \ -version-info 2:0:0 # Build unit test -check_PROGRAMS = ini_config_ut ini_comment_ut +check_PROGRAMS = ini_config_ut ini_comment_ut ini_valueobj_ut ini_config_ut_SOURCES = ini_config_ut.c -ini_config_ut_LDADD = libini_config.la -L$(topbuilddir)/collection -lcollection +ini_config_ut_LDADD = libini_config.la \ + -L$(topbuilddir)/collection -lcollection \ + -L$(topbuilddir)/basicobjects -lbasicobjects ini_comment_ut_SOURCES = ini_comment_ut.c -ini_comment_ut_LDADD = libini_config.la +ini_comment_ut_LDADD = libini_config.la \ + -L$(topbuilddir)/basicobjects -lbasicobjects +ini_valueobj_ut_SOURCES = ini_valueobj_ut.c +ini_valueobj_ut_LDADD = libini_config.la \ + -L$(topbuilddir)/basicobjects -lbasicobjects if HAVE_DOXYGEN docs: @@ -78,7 +88,7 @@ docs: @exit 1 endif -TESTS = ini_config_ut ini_comment_ut +TESTS = ini_config_ut ini_comment_ut ini_valueobj_ut tests: all $(check_PROGRAMS) diff --git a/common/ini/ini_valueobj.c b/common/ini/ini_valueobj.c new file mode 100644 index 0000000..e21e9e6 --- /dev/null +++ b/common/ini/ini_valueobj.c @@ -0,0 +1,901 @@ +/* + INI LIBRARY + + Module represents interface to the value object. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 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 <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include "config.h" +#include "simplebuffer.h" +#include "ref_array.h" +#include "ini_comment.h" +#include "trace.h" + +struct value_obj { + struct ref_array *raw_lines; + struct ref_array *raw_lengths; + struct simplebuffer *unfolded; + uint32_t origin; + uint32_t line; + uint32_t keylen; + uint32_t boundary; + struct ini_comment *ic; +}; + +/* Size of the block for a value */ +#define INI_VALUE_BLOCK 100 + +/* The length of " =" which is 3 */ +#define INI_FOLDING_OVERHEAD 3 + +/* Array growth */ +#define INI_ARRAY_GROW 2 + +/* Equal sign */ +#define INI_EQUAL_SIGN " = " + + +/* Unfold the value represented by the array */ +static int value_unfold(struct ref_array *raw_lines, + struct ref_array *raw_lengths, + struct simplebuffer **unfolded) +{ + int error; + struct simplebuffer *oneline = NULL; + uint32_t len = 0; + char *ptr = NULL; + uint32_t i = 0; + char *part = NULL; + + TRACE_FLOW_ENTRY(); + + error = simplebuffer_alloc(&oneline); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + for (;;) { + /* Get line */ + ptr = ref_array_get(raw_lines, i, NULL); + if (ptr) { + /* Get its length */ + ref_array_get(raw_lengths, i, (void *)&len); + + part = *((char **)(ptr)); + + TRACE_INFO_STRING("Value:", part); + TRACE_INFO_NUMBER("Lenght:", len); + + error = simplebuffer_add_raw(oneline, + part, + len, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add string", error); + simplebuffer_free(oneline); + return error; + } + + i++; + } + else break; + } + + *unfolded = oneline; + + TRACE_FLOW_EXIT(); + return error; +} + + +static int save_portion(struct ref_array *raw_lines, + struct ref_array *raw_lengths, + const char* buf, + uint32_t len) +{ + int error = EOK; + char *copy = NULL; + uint32_t adj = 0; + + TRACE_FLOW_ENTRY(); + + /* Add leading space only if there is + * a) no space + * b) it is not an empty line + * c) it is now a first line + */ + + if ((buf[0] != ' ') && + (buf[0] != '\t') && + (len != 0) && + (ref_array_len(raw_lines) != 0)) adj = 1; + + copy = malloc(len + adj + 1); + if (!copy) { + TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM); + return ENOMEM; + } + + memcpy(copy + adj, buf, len); + len += adj; + copy[len] = 0; + + /* If the section being saved is not starting + * with space add a space. + */ + if (adj) copy[0] = ' '; + + error = ref_array_append(raw_lines, (void *)(©)); + if (error) { + TRACE_ERROR_NUMBER("Failed to append line", + error); + free(copy); + return error; + } + + error = ref_array_append(raw_lengths, (void *)(&len)); + if (error) { + TRACE_ERROR_NUMBER("Failed to append length", + error); + return error; + } + + TRACE_INFO_STRING("Added string:", (char *)copy); + TRACE_INFO_NUMBER("Added number:", len); + + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Function to create a folded value out of the unfolded string */ +static int value_fold(struct simplebuffer *unfolded, + uint32_t key_len, + uint32_t fold_bound, + struct ref_array *raw_lines, + struct ref_array *raw_lengths) +{ + int error; + const char *buf; + uint32_t len = 0; /* Full length of the buffer */ + uint32_t fold_place = 0; /* Potential folding place */ + uint32_t best_place = 0; /* Dynamic folding boundary */ + uint32_t next_place = 0; /* Position of the found space */ + uint32_t fold_len = 0; /* Determined length of the substring */ + uint32_t idx = 0; /* Counter of lines */ + uint32_t i = 0; /* Internal counter */ + uint32_t resume_place = 0; /* Place we resume parsing */ + uint32_t start_place = 0; /* Start of the string */ + int done = 0; /* Are we done? */ + + TRACE_FLOW_ENTRY(); + + /* Reset arrays */ + ref_array_reset(raw_lines); + ref_array_reset(raw_lengths); + + /* Get the buffer info */ + len = simplebuffer_get_len(unfolded); + buf = (const char *)simplebuffer_get_buf(unfolded); + + /* Make sure that we have at least one character to fold */ + if (fold_bound == 0) fold_bound++; + + while (!done) { + /* Determine the max length of the line */ + if (idx == 0) { + if (fold_bound > (key_len + INI_FOLDING_OVERHEAD)) { + best_place = fold_bound - key_len - INI_FOLDING_OVERHEAD; + } + else best_place = 0; + } + else { + best_place = fold_bound; + } + + TRACE_INFO_NUMBER("Best place", best_place); + + fold_place = start_place; + next_place = start_place; + best_place += start_place; + + /* Parse the buffer from the right place */ + for (i = resume_place; i <= len; i++) { + + /* Check for folding opportunity */ + if (i == len) { + next_place = i; + done = 1; + } + /* + * Fold if we found the separator or the first line + * is too long right away + */ + else if (((buf[i] == ' ') || (buf[i] == '\t')) || + ((best_place == 0) && (i == 0))) { + next_place = i; + TRACE_INFO_NUMBER("Next place:", next_place); + } + else continue; + + if ((next_place > best_place) || (next_place == 0)) { + if ((fold_place == start_place) && + (next_place != 0)) { + /* Our first found folding place + * is already after the preferred + * folding place. Time to fold then... + */ + fold_len = next_place - start_place; + + } + else { + /* We will use the previous + * folding place. + */ + fold_len = fold_place - start_place; + + } + + TRACE_INFO_NUMBER("Fold len:", fold_len); + + error = save_portion(raw_lines, + raw_lengths, + buf + start_place, + fold_len); + if (error) { + TRACE_ERROR_NUMBER("Failed to save", error); + return error; + } + + start_place += fold_len; + /* + * This will force the re-processing + * of the same space but it is + * helpful in case the middle portion + * of the value is beyond our folding limit. + */ + resume_place = next_place; + idx++; + break; + } + else { /* Case when next_place <= best_place */ + fold_place = next_place; + } + } + + /* Save last portion */ + if (done) { + error = save_portion(raw_lines, + raw_lengths, + buf + start_place, + next_place - start_place); + if (error) { + TRACE_ERROR_NUMBER("Failed to save last chunk", error); + return error; + } + idx++; + } + } + + TRACE_FLOW_EXIT(); + return error; +} + + +/* Create value from a referenced array */ +int value_create_from_refarray(struct ref_array *raw_lines, + struct ref_array *raw_lengths, + uint32_t line, + uint32_t origin, + uint32_t key_len, + uint32_t boundary, + struct ini_comment *ic, + struct value_obj **vo) +{ + int error = EOK; + struct value_obj *new_vo = NULL; + + TRACE_FLOW_ENTRY(); + + if ((!raw_lines) || (!raw_lengths) || (!vo)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + + } + + new_vo = malloc(sizeof(struct value_obj)); + if (!new_vo) { + TRACE_ERROR_NUMBER("No memory", ENOMEM); + return ENOMEM; + + } + + /* We are not using references here since + * it will be inconsistent with the way + * how comment is handled. + * We could have added references here and make + * comment keep references but it seems to be + * and overhead in this case. + */ + new_vo->raw_lines = raw_lines; + new_vo->raw_lengths = raw_lengths; + new_vo->origin = origin; + new_vo->line = line; + new_vo->keylen = key_len; + new_vo->boundary = boundary; + new_vo->ic = ic; + + error = value_unfold(new_vo->raw_lines, + new_vo->raw_lengths, + &(new_vo->unfolded)); + if (error) { + TRACE_ERROR_NUMBER("Failed to unfold", error); + return error; + } + + TRACE_INFO_STRING("Unfolded:", + (const char *)simplebuffer_get_buf(new_vo->unfolded)); + *vo = new_vo; + + TRACE_FLOW_EXIT(); + + return error; +} + +/* Cleanup callback for lines array */ +void value_lines_cleanup_cb(void *elem, + ref_array_del_enum type, + void *data) +{ + char *part; + + TRACE_FLOW_ENTRY(); + + part = *((char **)(elem)); + + TRACE_INFO_STRING("Freeing:", part); + + free(part); + + TRACE_FLOW_EXIT(); +} + +/* Create a pair of arrays */ +int value_create_arrays(struct ref_array **raw_lines, + struct ref_array **raw_lengths) +{ + int error = EOK; + struct ref_array *new_lines = NULL; + struct ref_array *new_lengths = NULL; + + TRACE_FLOW_ENTRY(); + + error = ref_array_create(&new_lines, + sizeof(char *), + INI_ARRAY_GROW, + value_lines_cleanup_cb, + NULL); + if (error) { + TRACE_ERROR_NUMBER("Failed to create lines array", error); + return error; + + } + + error = ref_array_create(&new_lengths, + sizeof(uint32_t), + INI_ARRAY_GROW, + NULL, + NULL); + if (error) { + TRACE_ERROR_NUMBER("Failed to create lengths array", error); + ref_array_destroy(new_lines); + return error; + + } + + *raw_lines = new_lines; + *raw_lengths = new_lengths; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Add a raw string to the arrays */ +int value_add_to_arrays(const char *strvalue, + uint32_t len, + struct ref_array *raw_lines, + struct ref_array *raw_lengths) +{ + int error = EOK; + + TRACE_FLOW_ENTRY(); + + error = ref_array_append(raw_lines, (void *)(&strvalue)); + if (error) { + TRACE_ERROR_NUMBER("Failed to add to lines array", error); + return error; + + } + + error = ref_array_append(raw_lengths, (void *)(&len)); + if (error) { + TRACE_ERROR_NUMBER("Failed to add to lengths array", error); + return error; + + } + + TRACE_FLOW_EXIT(); + return error; +} + + +/* Destroy arrays */ +void value_destroy_arrays(struct ref_array *raw_lines, + struct ref_array *raw_lengths) +{ + TRACE_FLOW_ENTRY(); + + /* Function checks validity inside */ + ref_array_destroy(raw_lines); + /* Function checks validity inside */ + ref_array_destroy(raw_lengths); + + TRACE_FLOW_EXIT(); + +} + +/* Destroy a value object */ +void value_destroy(struct value_obj *vo) +{ + TRACE_FLOW_ENTRY(); + + if (vo) { + /* Free arrays if any */ + value_destroy_arrays(vo->raw_lines, + vo->raw_lengths); + /* Free the simple buffer if any */ + simplebuffer_free(vo->unfolded); + /* Function checks validity inside */ + ini_comment_destroy(vo->ic); + free(vo); + } + + TRACE_FLOW_EXIT(); +} + +/* Create value object from string buffer */ +int value_create_new(const char *strvalue, + uint32_t length, + uint32_t origin, + uint32_t key_len, + uint32_t boundary, + struct ini_comment *ic, + struct value_obj **vo) +{ + int error = EOK; + struct value_obj *new_vo = NULL; + struct simplebuffer *oneline = NULL; + void *val; + + TRACE_FLOW_ENTRY(); + + if ((!strvalue) || (!vo)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + /* Create buffer to hold the value */ + error = simplebuffer_alloc(&oneline); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + /* Deal with the type casting */ + memcpy(&val, &strvalue, sizeof(char *)); + /* Put value into the buffer */ + error = simplebuffer_add_raw(oneline, + val, + length, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add string", error); + simplebuffer_free(oneline); + return error; + } + + /* Acllocate new INI value structure */ + new_vo = malloc(sizeof(struct value_obj)); + if (!new_vo) { + TRACE_ERROR_NUMBER("No memory", ENOMEM); + simplebuffer_free(oneline); + return ENOMEM; + } + + new_vo->origin = origin; + /* Line is not known in this case */ + new_vo->line = 0; + new_vo->ic = ic; + new_vo->unfolded = oneline; + new_vo->keylen = key_len; + new_vo->boundary = boundary; + new_vo->raw_lines = NULL; + new_vo->raw_lengths = NULL; + + error = value_create_arrays(&(new_vo->raw_lines), + &(new_vo->raw_lengths)); + + if (error) { + TRACE_ERROR_NUMBER("Failed to fold", error); + value_destroy(new_vo); + return error; + } + + /* Create arrays by folding the value */ + error = value_fold(new_vo->unfolded, + new_vo->keylen, + new_vo->boundary, + new_vo->raw_lines, + new_vo->raw_lengths); + if (error) { + TRACE_ERROR_NUMBER("Failed to fold", error); + value_destroy(new_vo); + return error; + } + + *vo = new_vo; + + TRACE_FLOW_EXIT(); + + return error; +} + +/* Get concatenated value */ +int value_get_concatenated(struct value_obj *vo, + const char **fullstr) +{ + TRACE_FLOW_ENTRY(); + + if (!vo) { + TRACE_ERROR_NUMBER("Invalid object", EINVAL); + return EINVAL; + } + + *fullstr = (const char *)simplebuffer_get_buf(vo->unfolded); + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Get value's origin */ +int value_get_origin(struct value_obj *vo, uint32_t *origin) +{ + TRACE_FLOW_ENTRY(); + + if (!vo) { + TRACE_ERROR_NUMBER("Invalid object", EINVAL); + return EINVAL; + } + + *origin = vo->origin; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Get value's line */ +int value_get_line(struct value_obj *vo, uint32_t *line) +{ + TRACE_FLOW_ENTRY(); + + if (!vo) { + TRACE_ERROR_NUMBER("Invalid object", EINVAL); + return EINVAL; + } + + *line = vo->line; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Update key length */ +int value_set_keylen(struct value_obj *vo, uint32_t key_len) +{ + int error = EOK; + TRACE_FLOW_ENTRY(); + + if (!vo) { + TRACE_ERROR_NUMBER("Invalid object", EINVAL); + return EINVAL; + } + + vo->keylen = key_len; + + /* Fold in new value */ + error = value_fold(vo->unfolded, + vo->keylen, + vo->boundary, + vo->raw_lines, + vo->raw_lengths); + if (error) { + TRACE_ERROR_NUMBER("Failed to fold", error); + /* In this case nothing to free here but + * the object might be unsiable */ + return error; + } + + TRACE_FLOW_EXIT(); + return EOK; +} + + +/* Update value */ +int value_update(struct value_obj *vo, + const char *value, + uint32_t length, + uint32_t origin, + uint32_t boundary) +{ + int error = EOK; + struct simplebuffer *oneline = NULL; + void *val; + + if ((!value) || (!vo)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + /* Create buffer to hold the value */ + error = simplebuffer_alloc(&oneline); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + /* Deal with the type cast */ + memcpy(&val, &value, sizeof(char *)); + /* Put value into the buffer */ + error = simplebuffer_add_raw(oneline, + val, + length, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add string", error); + simplebuffer_free(oneline); + return error; + } + + simplebuffer_free(vo->unfolded); + + vo->origin = origin; + vo->unfolded = oneline; + vo->boundary = boundary; + + /* Fold in new value */ + error = value_fold(vo->unfolded, + vo->keylen, + vo->boundary, + vo->raw_lines, + vo->raw_lengths); + if (error) { + TRACE_ERROR_NUMBER("Failed to fold", error); + /* In this case nothing to free here but + * the object might be unsiable */ + return error; + } + + TRACE_FLOW_EXIT(); + + return error; + +} + +/* Get comment from the value */ +int value_extract_comment(struct value_obj *vo, struct ini_comment **ic) +{ + int error = EOK; + + TRACE_FLOW_ENTRY(); + + if ((!vo) || (!ic)) { + TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL); + return EINVAL; + } + + *ic = vo->ic; + vo->ic = NULL; + + TRACE_FLOW_EXIT(); + return error; + +} + +/* Set comment into the value */ +int value_put_comment(struct value_obj *vo, struct ini_comment *ic) +{ + int error = EOK; + + TRACE_FLOW_ENTRY(); + + if ((!vo) || (!ic)) { + TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL); + return EINVAL; + } + + if (vo->ic != ic) { + /* Remove existing comment if any */ + ini_comment_destroy(vo->ic); + } + + vo->ic = ic; + + TRACE_FLOW_EXIT(); + return error; + +} + +/* Serialize value */ +int value_serialize(struct value_obj *vo, + const char *key, + struct simplebuffer **sbobj) +{ + int error = EOK; + uint32_t num = 0; + uint32_t i = 0; + uint32_t len = 0; + char *commentline = NULL; + char *ptr = NULL; + struct simplebuffer *new_sbobj = NULL; + void *val = NULL; + const char *eq = INI_EQUAL_SIGN; + char *part = NULL; + + TRACE_FLOW_ENTRY(); + + if (!vo) { + TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL); + return EINVAL; + } + + error = simplebuffer_alloc(&new_sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + /* Put comment first */ + if (vo->ic) { + + /* Get number of lines in the comment */ + error = ini_comment_get_numlines(vo->ic, &num); + if (error) { + TRACE_ERROR_NUMBER("Failed to get number of lines", errno); + simplebuffer_free(new_sbobj); + return error; + } + + for (i = 0; i < num; i++) { + + len = 0; + commentline = NULL; + + error = ini_comment_get_line(vo->ic, i, &commentline, &len); + if (error) { + TRACE_ERROR_NUMBER("Failed to get number of lines", errno); + simplebuffer_free(new_sbobj); + return error; + } + + error = simplebuffer_add_raw(new_sbobj, + commentline, + len, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add comment", error); + simplebuffer_free(new_sbobj); + return error; + } + + error = simplebuffer_add_cr(new_sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to add CR", error); + simplebuffer_free(new_sbobj); + return error; + } + } + } + + /* Deal with the type cast */ + memcpy(&val, &key, sizeof(char *)); + + error = simplebuffer_add_raw(new_sbobj, + val, + vo->keylen, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add key", error); + simplebuffer_free(new_sbobj); + return error; + } + + /* Deal with the type cast */ + memcpy(&val, &eq, sizeof(char *)); + + error = simplebuffer_add_raw(new_sbobj, + val, + sizeof(INI_EQUAL_SIGN) - 1, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add equal sign", error); + simplebuffer_free(new_sbobj); + return error; + } + + + if (vo->raw_lines) { + i = 0; + for (;;) { + /* Get line */ + ptr = ref_array_get(vo->raw_lines, i, NULL); + if (ptr) { + /* Get its length */ + ref_array_get(vo->raw_lengths, i, (void *)&len); + if (error) { + TRACE_ERROR_NUMBER("Failed to add string", error); + simplebuffer_free(new_sbobj); + return error; + } + + part = *((char **)(ptr)); + + TRACE_INFO_STRING("Value:", part); + TRACE_INFO_NUMBER("Lenght:", len); + + error = simplebuffer_add_raw(new_sbobj, + part, + len, + INI_VALUE_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Failed to add value", error); + simplebuffer_free(new_sbobj); + return error; + } + + error = simplebuffer_add_cr(new_sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to add CR", error); + simplebuffer_free(new_sbobj); + return error; + } + i++; + } + else break; + } + } + + *sbobj = new_sbobj; + + TRACE_INFO_STRING("Buffer:", (const char *)simplebuffer_get_buf(*sbobj)); + TRACE_FLOW_EXIT(); + return error; +} diff --git a/common/ini/ini_valueobj.h b/common/ini/ini_valueobj.h new file mode 100644 index 0000000..d8e10de --- /dev/null +++ b/common/ini/ini_valueobj.h @@ -0,0 +1,130 @@ +/* + INI LIBRARY + + Header file for the value object. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 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 <http://www.gnu.org/licenses/>. +*/ + + +#ifndef INI_VALUEOBJ_H +#define INI_VALUEOBJ_H + +#include "ref_array.h" +#include "simplebuffer.h" +#include "ini_comment.h" + +struct value_obj; + +#define INI_VALUE_READ 0 /* Value is read from the file */ +#define INI_VALUE_CREATED 1 /* Value is created in memory */ + +/* + * Create value from a referenced array. + * + * NOTE: arrays and comment are NOT treated as + * objects that keep reference count. + * They are created externally and passed in + * as separate parts that are glued together + * by the value object. + * The caller should not free it himself + * (only in case of failure) since + * after the call the arrays and comment + * are owned by the value object and will + * be freed when it is destroyed. + */ +int value_create_from_refarray(struct ref_array *raw_lines, + struct ref_array *raw_lengths, + uint32_t line, + uint32_t origin, + uint32_t key_len, + uint32_t boundary, + struct ini_comment *ic, + struct value_obj **vo); + +/* Cleanup callback for lines array */ +void value_lines_cleanup_cb(void *elem, + ref_array_del_enum type, + void *data); + +/* Create a pair of arrays */ +int value_create_arrays(struct ref_array **raw_lines, + struct ref_array **raw_lengths); + +/* Add a raw read line to the arrays */ +int value_add_to_arrays(const char *strvalue, + uint32_t len, + struct ref_array *raw_lines, + struct ref_array *raw_lengths); + +/* Create a pair of arrays */ +void value_destroy_arrays(struct ref_array *raw_lines, + struct ref_array *raw_lengths); + +/* Create value object from string buffer. + * NOTE: see note above + * in the description of the + * value_create_from_refarray function. + */ +int value_create_new(const char *strvalue, + uint32_t length, + uint32_t origin, + uint32_t key_len, + uint32_t boundary, + struct ini_comment *ic, + struct value_obj **vo); + +/* Destroy a value object */ +void value_destroy(struct value_obj *vo); + +/* Get concatenated value */ +int value_get_concatenated(struct value_obj *vo, + const char **fullstr); + +/* Get value's origin */ +int value_get_origin(struct value_obj *vo, + uint32_t *origin); + +/* Get value's line */ +int value_get_line(struct value_obj *vo, + uint32_t *line); + +/* Update key length */ +int value_set_keylen(struct value_obj *vo, + uint32_t key_len); + +/* Update value */ +int value_update(struct value_obj *vo, + const char *value, + uint32_t length, + uint32_t origin, + uint32_t boundary); + +/* Get comment from the value */ +int value_extract_comment(struct value_obj *vo, + struct ini_comment **ic); + +/* Set comment into the value */ +int value_put_comment(struct value_obj *vo, + struct ini_comment *ic); + +/* Serialize value */ +int value_serialize(struct value_obj *vo, + const char *key, + struct simplebuffer **sbobj); + + +#endif diff --git a/common/ini/ini_valueobj_ut.c b/common/ini/ini_valueobj_ut.c new file mode 100644 index 0000000..7736663 --- /dev/null +++ b/common/ini/ini_valueobj_ut.c @@ -0,0 +1,538 @@ +/* + INI LIBRARY + + Unit test for the value object. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program 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 General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include <errno.h> /* for errors */ +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include "ini_valueobj.h" +#include "ini_defines.h" +#include "config.h" +#define TRACE_HOME +#include "trace.h" + +int verbose = 0; + +#define VOOUT(foo) \ + do { \ + if (verbose) foo; \ + } while(0) + + +typedef int (*test_fn)(void); + + +extern void ref_array_debug(struct ref_array *ra); + +static int create_comment(int i, struct ini_comment **ic) +{ + int error = EOK; + const char *template = ";Line 0 of the value %d"; + char comment[80]; + struct ini_comment *new_ic = NULL; + + TRACE_FLOW_ENTRY(); + + sprintf(comment, template, i); + + + if ((error = ini_comment_create(&new_ic)) || + (error = ini_comment_build(new_ic, comment)) || + (error = ini_comment_build(new_ic, NULL)) || + (error = ini_comment_build(new_ic, "#This is the second line")) || + (error = ini_comment_build(new_ic, ";This is the third line")) || + (error = ini_comment_build(new_ic, ""))) { + printf("Failed to create comment object\n"); + ini_comment_destroy(new_ic); + return -1; + } + + *ic = new_ic; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Save value to the file */ +/* NOTE: might be moved into the API in future */ +int save_value(FILE *ff, const char *key, struct value_obj *vo) +{ + + int error = EOK; + struct simplebuffer *sbobj = NULL; + uint32_t left = 0; + + TRACE_FLOW_ENTRY(); + + + /* Serialize */ + error = value_serialize(vo, key, &sbobj); + if (error) { + printf("Failed to serialize a value object %d.\n", error); + return error; + } + + /* Add CR */ + error = simplebuffer_add_cr(sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to add CR", error); + simplebuffer_free(sbobj); + return error; + } + + /* Save */ + left = simplebuffer_get_len(sbobj); + while (left > 0) { + error = simplebuffer_write(fileno(ff), sbobj, &left); + if (error) { + printf("Failed to write value object %d.\n", error); + simplebuffer_free(sbobj); + return error; + } + } + + simplebuffer_free(sbobj); + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Test to create value object using arrays */ +int other_create_test(FILE *ff, struct value_obj **vo) +{ + int error = EOK; + struct value_obj *new_vo = NULL; + struct ref_array *raw_lines; + struct ref_array *raw_lengths; + struct ini_comment *ic = NULL; + struct ini_comment *ic2 = NULL; + char *val; + const char *vallines[] = { "Domain1,", + " Domain2 ,", + " Domain3" }; + const char *fullstr; + const char *expected = "Domain1, Domain2 , Domain3"; + int i; + uint32_t origin = 0; + uint32_t line = 0; + + + TRACE_FLOW_ENTRY(); + + /* Create a pair of arrays */ + error = value_create_arrays(&raw_lines, + &raw_lengths); + if (error) { + printf("Failed to create arrays %d.\n", error); + return error; + } + + for (i=0; i< 3; i++) { + errno = 0; + val = strdup(vallines[i]); + if (val == NULL) { + error = errno; + printf("Failed to dup memory %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + /* Add line to the arrays */ + error = value_add_to_arrays(val, + strlen(val), + raw_lines, + raw_lengths); + if (error) { + printf("Failed to add to arrays %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + } + + /* Create a comment */ + error = create_comment(1000, &ic); + if (error) { + printf("Failed to create comment %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + return error; + } + + /* Create object */ + error = value_create_from_refarray(raw_lines, + raw_lengths, + 1, + INI_VALUE_READ, + 3, + 70, + ic, + &new_vo); + + if (error) { + printf("Failed to create comment %d.\n", error); + value_destroy_arrays(raw_lines, + raw_lengths); + ini_comment_destroy(ic); + return error; + } + + /* Save value to the file */ + error = save_value(stdout, "bar", new_vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(new_vo); + return error; + } + + /* Now do assertions and modifications to the object */ + + /* NOTE: Below this line do need to free arrays or comment + * they became internal parts of the value object + * and will be freed as a part of it. + */ + + /* Get concatenated value */ + error = value_get_concatenated(new_vo, + &fullstr); + + if (error) { + printf("Failed to get the string %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (strncmp(fullstr, expected, strlen(expected) + 1) != 0) { + printf("The expected value is different.\n%s\n", fullstr); + value_destroy(new_vo); + return error; + } + + /* Get value's origin */ + error = value_get_origin(new_vo, &origin); + if (error) { + printf("Failed to get origin %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (origin != INI_VALUE_READ) { + printf("The expected origin is different.\n%d\n", origin); + value_destroy(new_vo); + return error; + } + + /* Get value's line */ + error = value_get_line(new_vo, &line); + if (error) { + printf("Failed to get origin %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (line != 1) { + printf("The expected line is different.\n%d\n", origin); + value_destroy(new_vo); + return error; + } + + /* Get comment from the value */ + ic = NULL; + error = value_extract_comment(new_vo, &ic); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(new_vo); + return error; + } + + if (ic == NULL) { + printf("The expected comment to be there.\n"); + value_destroy(new_vo); + return error; + } + + VOOUT(ini_comment_print(ic, stdout)); + + /* Get comment again */ + ic2 = NULL; + error = value_extract_comment(new_vo, &ic2); + if (error) { + printf("Failed to extract comment %d.\n", error); + value_destroy(new_vo); + ini_comment_destroy(ic); + return error; + } + + if (ic2 != NULL) { + printf("The expected NO comment to be there.\n"); + value_destroy(new_vo); + ini_comment_destroy(ic); + /* No free for ic2 since it is the same object */ + + /* But this should not happen anyways - + * it will be coding error. + */ + return error; + } + + /* Put comment back */ + error = value_put_comment(new_vo, ic); + if (error) { + printf("Failed to put comment back %d.\n", error); + value_destroy(new_vo); + ini_comment_destroy(ic); + return error; + } + + /* Save value to the file */ + error = save_value(ff, "bar", new_vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(new_vo); + return error; + } + + *vo = new_vo; + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Modify the value object */ +int modify_test(FILE *ff, struct value_obj *vo) +{ + int error = EOK; + const char *strval = "Domain100, Domain200, Domain300"; + + TRACE_FLOW_ENTRY(); + + + /* Update key length */ + error = value_set_keylen(vo, strlen("foobar")); + if (error) { + printf("Failed to change key length %d.\n", error); + return error; + } + + /* Update value */ + error = value_update(vo, + strval, + strlen(strval), + INI_VALUE_CREATED, + 10); + + + /* Save value to the file */ + error = save_value(ff, "foobar", vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + return error; + } + + TRACE_FLOW_EXIT(); + return EOK; +} + + +int vo_basic_test(void) +{ + int error = EOK; + const char *strvalue = "Test multi_word_value_that_will_" + "be_split_between_several_lines_!"; + + /* Other testing can be done with the following string: + * const char *strvalue = "Test multi word value that " + * "will be split between several lines"; + */ + + struct value_obj *vo = NULL; + uint32_t wrap = 0; + struct ini_comment *ic = NULL; + FILE *ff = NULL; + + TRACE_FLOW_ENTRY(); + + errno = 0; + ff = fopen("test.ini","wt"); + if (ff == NULL) { + error = errno; + printf("Failed to open file. Error %d.\n", error); + return error; + } + + + for (wrap = 0; wrap < 80; wrap++) { + + ic = NULL; + error = create_comment(wrap, &ic); + if (error) { + printf("Failed to create a new value object %d.\n", error); + fclose(ff); + return error; + } + + error = value_create_new(strvalue, + strlen(strvalue), + INI_VALUE_CREATED, + 3, + wrap, + ic, + &vo); + if (error) { + printf("Failed to create a new value object %d.\n", error); + ini_comment_destroy(ic); + fclose(ff); + return error; + } + + error = save_value(ff, "key", vo); + if (error) { + printf("Failed to save value to file %d.\n", error); + value_destroy(vo); + return error; + } + + value_destroy(vo); + } + + /* Run other create test here */ + error = other_create_test(ff, &vo); + if (error) { + printf("Create test failed %d.\n", error); + fclose(ff); + return error; + } + + /* Run modify test here */ + error = modify_test(ff, vo); + if (error) { + printf("Modify test failed %d.\n", error); + fclose(ff); + value_destroy(vo); + return error; + } + + value_destroy(vo); + + + ic = NULL; + error = create_comment(100, &ic); + if (error) { + printf("Failed to create a new value object %d.\n", error); + fclose(ff); + return error; + } + + ini_comment_print(ic, ff); + + ini_comment_destroy(ic); + + fclose(ff); + + TRACE_FLOW_EXIT(); + return EOK; +} + + +int vo_file_test(void) +{ + int error = EOK; + FILE *ff = NULL; + uint32_t line = 0; + char *oneline = NULL; + char buffer[BUFFER_SIZE + 1]; + + TRACE_FLOW_ENTRY(); + + errno = 0; + ff = fopen("test.ini","r"); + if (ff == NULL) { + error = errno; + printf("Failed to open file. Error %d.\n", error); + return error; + } + + while (1) { + + oneline = NULL; + errno = 0; + line++; + + oneline = fgets(buffer, BUFFER_SIZE, ff); + if (oneline == NULL) { + error = errno; + if (feof(ff)) break; + printf("Failed to read file %d\n", error); + fclose(ff); + return error; + } + + VOOUT(printf("Line %04d: %s", line, oneline)); + + } + + fclose(ff); + + /* THIS TEST IS INCOMPLETE. + * BUT I NEED TO ADD PARSING + * TO COMPLETE IT. + * Will be delivered later. + */ + + TRACE_FLOW_EXIT(); + return EOK; +} + + +/* Main function of the unit test */ +int main(int argc, char *argv[]) +{ + int error = 0; + test_fn tests[] = { vo_basic_test, + vo_file_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + VOOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + VOOUT(printf("Failed with error %d!\n", error)); + return error; + } + } + + VOOUT(printf("Success!\n")); + return 0; +} |