From 36f2332baaf211a231a152ae54c66f0a3a3940eb Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Tue, 21 Jan 2014 14:24:54 -0500 Subject: [PATCH] Add a config_writer API based on libxml2 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau Signed-off-by: David Goulet --- src/common/config/Makefile.am | 2 + src/common/config/config-internal.h | 23 +++ src/common/config/config.c | 250 +++++++++++++++++++++++++++- src/common/config/config.h | 115 +++++++++++++ 4 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 src/common/config/config-internal.h diff --git a/src/common/config/Makefile.am b/src/common/config/Makefile.am index 057c13712..0bde30e4d 100644 --- a/src/common/config/Makefile.am +++ b/src/common/config/Makefile.am @@ -3,3 +3,5 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src noinst_LTLIBRARIES = libconfig.la libconfig_la_SOURCES = ini.c ini.h config.c config.h +libconfig_la_CPPFLAGS = $(XML_CPPFLAGS) +libconfig_la_LIBADD = $(XML_LIBS) diff --git a/src/common/config/config-internal.h b/src/common/config/config-internal.h new file mode 100644 index 000000000..ff3e6293e --- /dev/null +++ b/src/common/config/config-internal.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2013 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +struct config_writer { + xmlTextWriterPtr writer; +}; diff --git a/src/common/config/config.c b/src/common/config/config.c index e945a19fb..18baf6bd5 100644 --- a/src/common/config/config.c +++ b/src/common/config/config.c @@ -17,12 +17,11 @@ #define _GNU_SOURCE #include -#include #include -#include #include #include #include +#include #include #include @@ -30,6 +29,7 @@ #include #include "config.h" +#include "config-internal.h" struct handler_filter_args { const char* section; @@ -43,6 +43,12 @@ const char * const config_str_on = "on"; const char * const config_str_no = "no"; const char * const config_str_false = "false"; const char * const config_str_off = "off"; +const char * const config_xml_encoding = "UTF-8"; +/* Size of the encoding's largest character */ +const size_t config_xml_encoding_bytes_per_char = 2; +const char * const config_xml_indent_string = "\t"; +const char * const config_xml_true = "true"; +const char * const config_xml_false = "false"; static int config_entry_handler_filter(struct handler_filter_args *args, const char *section, const char *name, const char *value) @@ -171,3 +177,243 @@ int config_parse_value(const char *value) end: return ret; } + +/* + * Returns a xmlChar string which must be released using xmlFree(). + */ +static xmlChar *encode_string(const char *in_str) +{ + xmlChar *out_str = NULL; + xmlCharEncodingHandlerPtr handler; + int out_len, ret, in_len; + + assert(in_str); + + handler = xmlFindCharEncodingHandler(config_xml_encoding); + if (!handler) { + ERR("xmlFindCharEncodingHandler return NULL!. Configure issue!"); + goto end; + } + + in_len = strlen(in_str); + /* + * Add 1 byte for the NULL terminted character. The factor 2 here is + * because UTF-8 can be on two bytes so this fits the worst case for each + * bytes. + */ + out_len = (in_len * 2) + 1; + out_str = xmlMalloc(out_len); + if (!out_str) { + goto end; + } + + ret = handler->input(out_str, &out_len, (const xmlChar *) in_str, &in_len); + if (ret < 0) { + xmlFree(out_str); + out_str = NULL; + goto end; + } + + /* out_len is now the size of out_str */ + out_str[out_len] = '\0'; +end: + return out_str; +} + +LTTNG_HIDDEN +struct config_writer *config_writer_create(int fd_output) +{ + int ret; + struct config_writer *writer; + xmlOutputBufferPtr buffer; + + writer = zmalloc(sizeof(struct config_writer)); + if (!writer) { + PERROR("zmalloc config_writer_create"); + goto end; + } + + buffer = xmlOutputBufferCreateFd(fd_output, NULL); + if (!buffer) { + goto error_destroy; + } + + writer->writer = xmlNewTextWriter(buffer); + ret = xmlTextWriterStartDocument(writer->writer, NULL, + config_xml_encoding, NULL); + if (ret < 0) { + goto error_destroy; + } + + ret = xmlTextWriterSetIndentString(writer->writer, + BAD_CAST config_xml_indent_string); + if (ret) { + goto error_destroy; + } + + ret = xmlTextWriterSetIndent(writer->writer, 1); + if (ret) { + goto error_destroy; + } + +end: + return writer; +error_destroy: + config_writer_destroy(writer); + return NULL; +} + +LTTNG_HIDDEN +int config_writer_destroy(struct config_writer *writer) +{ + int ret = 0; + + if (!writer) { + ret = -EINVAL; + goto end; + } + + if (xmlTextWriterEndDocument(writer->writer) < 0) { + WARN("Could not close XML document"); + ret = -EIO; + } + + if (writer->writer) { + xmlFreeTextWriter(writer->writer); + } + + free(writer); +end: + return ret; +} + +LTTNG_HIDDEN +int config_writer_open_element(struct config_writer *writer, + const char *element_name) +{ + int ret; + xmlChar *encoded_element_name; + + if (!writer || !writer->writer || !element_name || !element_name[0]) { + ret = -1; + goto end; + } + + encoded_element_name = encode_string(element_name); + if (!encoded_element_name) { + ret = -1; + goto end; + } + + ret = xmlTextWriterStartElement(writer->writer, encoded_element_name); + xmlFree(encoded_element_name); +end: + return ret > 0 ? 0 : ret; +} + +LTTNG_HIDDEN +int config_writer_close_element(struct config_writer *writer) +{ + int ret; + + if (!writer || !writer->writer) { + ret = -1; + goto end; + } + + ret = xmlTextWriterEndElement(writer->writer); +end: + return ret > 0 ? 0 : ret; +} + +LTTNG_HIDDEN +int config_writer_write_element_unsigned_int(struct config_writer *writer, + const char *element_name, uint64_t value) +{ + int ret; + xmlChar *encoded_element_name; + + if (!writer || !writer->writer || !element_name || !element_name[0]) { + ret = -1; + goto end; + } + + encoded_element_name = encode_string(element_name); + if (!encoded_element_name) { + ret = -1; + goto end; + } + + ret = xmlTextWriterWriteFormatElement(writer->writer, + encoded_element_name, "%" PRIu64, value); + xmlFree(encoded_element_name); +end: + return ret > 0 ? 0 : ret; +} + +LTTNG_HIDDEN +int config_writer_write_element_signed_int(struct config_writer *writer, + const char *element_name, int64_t value) +{ + int ret; + xmlChar *encoded_element_name; + + if (!writer || !writer->writer || !element_name || !element_name[0]) { + ret = -1; + goto end; + } + + encoded_element_name = encode_string(element_name); + if (!encoded_element_name) { + ret = -1; + goto end; + } + + ret = xmlTextWriterWriteFormatElement(writer->writer, + encoded_element_name, "%" PRIi64, value); + xmlFree(encoded_element_name); +end: + return ret > 0 ? 0 : ret; +} + +LTTNG_HIDDEN +int config_writer_write_element_bool(struct config_writer *writer, + const char *element_name, int value) +{ + return config_writer_write_element_string(writer, element_name, + value ? config_xml_true : config_xml_false); +} + +LTTNG_HIDDEN +int config_writer_write_element_string(struct config_writer *writer, + const char *element_name, const char *value) +{ + int ret; + xmlChar *encoded_element_name = NULL; + xmlChar *encoded_value = NULL; + + if (!writer || !writer->writer || !element_name || !element_name[0] || + !value) { + ret = -1; + goto end; + } + + encoded_element_name = encode_string(element_name); + if (!encoded_element_name) { + ret = -1; + goto end; + } + + encoded_value = encode_string(value); + if (!encoded_value) { + ret = -1; + goto end; + } + + ret = xmlTextWriterWriteElement(writer->writer, encoded_element_name, + encoded_value); +end: + xmlFree(encoded_element_name); + xmlFree(encoded_value); + return ret > 0 ? 0 : ret; +} diff --git a/src/common/config/config.h b/src/common/config/config.h index 73e39fc32..f5ca42485 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -20,6 +20,7 @@ #include #include +#include struct config_entry { /* section is NULL if the entry is not in a section */ @@ -28,6 +29,9 @@ struct config_entry { const char *value; }; +/* Instance of a configuration writer. */ +struct config_writer; + /* * A config_entry_handler_cb receives config_entry structures belonging to the * sections the handler has been registered to. @@ -70,4 +74,115 @@ int config_get_section_entries(const char *path, const char *section, LTTNG_HIDDEN int config_parse_value(const char *value); +/* + * Create an instance of a configuration writer. + * + * fd_output File to which the XML content must be written. The file will be + * closed once the config_writer has been destroyed. + * + * Returns an instance of a configuration writer on success, NULL on + * error. + */ +LTTNG_HIDDEN +struct config_writer *config_writer_create(int fd_output); + +/* + * Destroy an instance of a configuration writer. + * + * writer An instance of a configuration writer. + * + * Returns zero if the XML document could be closed cleanly. Negative values + * indicate an error. + */ +LTTNG_HIDDEN +int config_writer_destroy(struct config_writer *writer); + +/* + * Open an element tag. + * + * writer An instance of a configuration writer. + * + * element_name Element tag name. + * + * Returns zero if the XML document could be closed cleanly. + * Negative values indicate an error. + */ +LTTNG_HIDDEN +int config_writer_open_element(struct config_writer *writer, + const char *element_name); + +/* + * Close the current element tag. + * + * writer An instance of a configuration writer. + * + * Returns zero if the XML document could be closed cleanly. + * Negative values indicate an error. + */ +LTTNG_HIDDEN +int config_writer_close_element(struct config_writer *writer); + +/* + * Write an element of type unsigned int. + * + * writer An instance of a configuration writer. + * + * element_name Element name. + * + * value Unsigned int value of the element + * + * Returns zero if the element's value could be written. + * Negative values indicate an error. + */ +LTTNG_HIDDEN +int config_writer_write_element_unsigned_int(struct config_writer *writer, + const char *element_name, uint64_t value); + +/* + * Write an element of type signed int. + * + * writer An instance of a configuration writer. + * + * element_name Element name. + * + * value Signed int value of the element + * + * Returns zero if the element's value could be written. + * Negative values indicate an error. + */LTTNG_HIDDEN +int config_writer_write_element_signed_int(struct config_writer *writer, + const char *element_name, int64_t value); + +/* + * Write an element of type boolean. + * + * writer An instance of a configuration writer. + * + * element_name Element name. + * + * value Boolean value of the element + * + * Returns zero if the element's value could be written. + * Negative values indicate an error. + */ +LTTNG_HIDDEN +int config_writer_write_element_bool(struct config_writer *writer, + const char *element_name, int value); + +/* + * Write an element of type string. + * + * writer An instance of a configuration writer. + * + * element_name Element name. + * + * value String value of the element + * + * Returns zero if the element's value could be written. + * Negative values indicate an error. + */ +LTTNG_HIDDEN +int config_writer_write_element_string(struct config_writer *writer, + const char *element_name, const char *value); + #endif /* _CONFIG_H */ -- 2.34.1