summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2010-06-10 11:57:25 -0400
committerDmitri Pal <dpal@redhat.com>2010-08-10 12:51:32 -0400
commit85acfe3f0e53db93a741040d12642083260664f6 (patch)
treee1f62378ff562252e9cb5495baed25c45cefaa8b
parent67177f82868a56a255b0eda2003b929d0bdd5f09 (diff)
downloadsssd-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.am22
-rw-r--r--common/ini/ini_valueobj.c901
-rw-r--r--common/ini/ini_valueobj.h130
-rw-r--r--common/ini/ini_valueobj_ut.c538
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 *)(&copy));
+ 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;
+}