From 0008faacbd1d583767001358565589442425a1ec Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 13 Jan 2014 00:44:07 +0000 Subject: [PATCH] Update from libucl. --- src/ucl/include/ucl.h | 417 ++++++++++++++++++------------ src/ucl/src/ucl_emitter.c | 516 ++++++++++++++++++++++--------------- src/ucl/src/ucl_internal.h | 3 - src/ucl/src/ucl_util.c | 74 +++--- 4 files changed, 608 insertions(+), 402 deletions(-) diff --git a/src/ucl/include/ucl.h b/src/ucl/include/ucl.h index 632c6e170..d1dd61fdb 100644 --- a/src/ucl/include/ucl.h +++ b/src/ucl/include/ucl.h @@ -21,47 +21,36 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef RCL_H_ -#define RCL_H_ +#ifndef UCL_H_ +#define UCL_H_ -#include -#include -#include -#include -#include -#include -#include #include "config.h" +#include /** - * @file rcl.h - * RCL is an rspamd configuration language, which is a form of - * JSON with less strict rules that make it more comfortable for - * using as a configuration language + * @mainpage + * This is a reference manual for UCL API. You may find the description of UCL format by following this + * [github repository](https://github.com/vstakhov/libucl). + * + * This manual has several main sections: + * - @ref structures + * - @ref utils + * - @ref parser + * - @ref emitter */ /** - * XXX: Poorly named API functions, need to replace them with the appropriate - * named function. All API functions *must* use naming ucl_object_*. Usage of - * ucl_obj* should be avoided. + * @file ucl.h + * @brief UCL parsing and emitting functions + * + * UCL is universal configuration language, which is a form of + * JSON with less strict rules that make it more comfortable for + * using as a configuration language */ -#define ucl_object_todouble_safe ucl_obj_todouble_safe -#define ucl_object_todouble ucl_obj_todouble -#define ucl_object_tostring ucl_obj_tostring -#define ucl_object_tostring_safe ucl_obj_tostring_safe -#define ucl_object_tolstring ucl_obj_tolstring -#define ucl_object_tolstring_safe ucl_obj_tolstring_safe -#define ucl_object_toint ucl_obj_toint -#define ucl_object_toint_safe ucl_obj_toint_safe -#define ucl_object_toboolean ucl_obj_toboolean -#define ucl_object_toboolean_safe ucl_obj_toboolean_safe -#define ucl_object_find_key ucl_obj_get_key -#define ucl_object_find_keyl ucl_obj_get_keyl -#define ucl_object_unref ucl_obj_unref -#define ucl_object_ref ucl_obj_ref -#define ucl_object_free ucl_obj_free - -/** +#ifdef __cplusplus +extern "C" { +#endif +/* * Memory allocation utilities * UCL_ALLOC(size) - allocate memory for UCL * UCL_FREE(size, ptr) - free memory of specified size at ptr @@ -81,100 +70,126 @@ #define UCL_WARN_UNUSED_RESULT #endif -enum ucl_error { - UCL_EOK = 0, //!< UCL_EOK - UCL_ESYNTAX, //!< UCL_ESYNTAX - UCL_EIO, //!< UCL_EIO - UCL_ESTATE, //!< UCL_ESTATE - UCL_ENESTED, //!< UCL_ENESTED - UCL_EMACRO, //!< UCL_EMACRO - UCL_ERECURSION,//!< UCL_ERECURSION - UCL_EINTERNAL, //!< UCL_EINTERNAL - UCL_ESSL //!< UCL_ESSL -}; +/** + * @defgroup structures Structures and types + * UCL defines several enumeration types used for error reporting or specifying flags and attributes. + * + * @{ + */ /** - * Object types + * The common error codes returned by ucl parser */ -enum ucl_type { - UCL_OBJECT = 0,//!< UCL_OBJECT - UCL_ARRAY, //!< UCL_ARRAY - UCL_INT, //!< UCL_INT - UCL_FLOAT, //!< UCL_FLOAT - UCL_STRING, //!< UCL_STRING - UCL_BOOLEAN, //!< UCL_BOOLEAN - UCL_TIME, //!< UCL_TIME - UCL_USERDATA, //!< UCL_USERDATA - UCL_NULL //!< UCL_NULL -}; +typedef enum ucl_error { + UCL_EOK = 0, /**< No error */ + UCL_ESYNTAX, /**< Syntax error occurred during parsing */ + UCL_EIO, /**< IO error occurred during parsing */ + UCL_ESTATE, /**< Invalid state machine state */ + UCL_ENESTED, /**< Input has too many recursion levels */ + UCL_EMACRO, /**< Error processing a macro */ + UCL_EINTERNAL, /**< Internal unclassified error */ + UCL_ESSL /**< SSL error */ +} ucl_error_t; /** - * Emitting types + * #ucl_object_t may have one of specified types, some types are compatible with each other and some are not. + * For example, you can always convert #UCL_TIME to #UCL_FLOAT. Also you can convert #UCL_FLOAT to #UCL_INTEGER + * by loosing floating point. Every object may be converted to a string by #ucl_object_tostring_forced() function. + * */ -enum ucl_emitter { - UCL_EMIT_JSON = 0, //!< UCL_EMIT_JSON - UCL_EMIT_JSON_COMPACT,//!< UCL_EMIT_JSON_COMPACT - UCL_EMIT_CONFIG, //!< UCL_EMIT_CONFIG - UCL_EMIT_YAML //!< UCL_EMIT_YAML -}; +typedef enum ucl_type { + UCL_OBJECT = 0, /**< UCL object - key/value pairs */ + UCL_ARRAY, /**< UCL array */ + UCL_INT, /**< Integer number */ + UCL_FLOAT, /**< Floating point number */ + UCL_STRING, /**< Null terminated string */ + UCL_BOOLEAN, /**< Boolean value */ + UCL_TIME, /**< Time value (floating point number of seconds) */ + UCL_USERDATA, /**< Opaque userdata pointer (may be used in macros) */ + UCL_NULL /**< Null value */ +} ucl_type_t; /** - * Parsing flags + * You can use one of these types to serialise #ucl_object_t by using ucl_object_emit(). */ -enum ucl_parser_flags { - UCL_PARSER_KEY_LOWERCASE = 0x1,//!< UCL_FLAG_KEY_LOWERCASE - UCL_PARSER_ZEROCOPY = 0x2 //!< UCL_FLAG_ZEROCOPY -}; +typedef enum ucl_emitter { + UCL_EMIT_JSON = 0, /**< Emit fine formatted JSON */ + UCL_EMIT_JSON_COMPACT, /**< Emit compacted JSON */ + UCL_EMIT_CONFIG, /**< Emit human readable config format */ + UCL_EMIT_YAML /**< Emit embedded YAML format */ +} ucl_emitter_t; /** - * String conversion flags + * These flags defines parser behaviour. If you specify #UCL_PARSER_ZEROCOPY you must ensure + * that the input memory is not freed if an object is in use. Moreover, if you want to use + * zero-terminated keys and string values then you should not use zero-copy mode, as in this case + * UCL still has to perform copying implicitly. */ -enum ucl_string_flags { - UCL_STRING_ESCAPE = 0x1, /**< UCL_STRING_ESCAPE perform JSON escape */ - UCL_STRING_TRIM = 0x2, /**< UCL_STRING_TRIM trim leading and trailing whitespaces */ - UCL_STRING_PARSE_BOOLEAN = 0x4, /**< UCL_STRING_PARSE_BOOLEAN parse passed string and detect boolean */ - UCL_STRING_PARSE_INT = 0x8, /**< UCL_STRING_PARSE_INT parse passed string and detect integer number */ - UCL_STRING_PARSE_DOUBLE = 0x10, /**< UCL_STRING_PARSE_DOUBLE parse passed string and detect integer or float number */ +typedef enum ucl_parser_flags { + UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */ + UCL_PARSER_ZEROCOPY = 0x2 /**< Parse input in zero-copy mode if possible */ +} ucl_parser_flags_t; + +/** + * String conversion flags, that are used in #ucl_object_fromstring_common function. + */ +typedef enum ucl_string_flags { + UCL_STRING_ESCAPE = 0x1, /**< Perform JSON escape */ + UCL_STRING_TRIM = 0x2, /**< Trim leading and trailing whitespaces */ + UCL_STRING_PARSE_BOOLEAN = 0x4, /**< Parse passed string and detect boolean */ + UCL_STRING_PARSE_INT = 0x8, /**< Parse passed string and detect integer number */ + UCL_STRING_PARSE_DOUBLE = 0x10, /**< Parse passed string and detect integer or float number */ UCL_STRING_PARSE_NUMBER = UCL_STRING_PARSE_INT|UCL_STRING_PARSE_DOUBLE , /**< - UCL_STRING_PARSE_NUMBER parse passed string and detect number */ + Parse passed string and detect number */ UCL_STRING_PARSE = UCL_STRING_PARSE_BOOLEAN|UCL_STRING_PARSE_NUMBER, /**< - UCL_STRING_PARSE parse passed string (and detect booleans and numbers) */ + Parse passed string (and detect booleans and numbers) */ UCL_STRING_PARSE_BYTES = 0x20 /**< Treat numbers as bytes */ -}; +} ucl_string_flags_t; /** * Basic flags for an object */ -enum ucl_object_flags { - UCL_OBJECT_ALLOCATED_KEY = 1, //!< UCL_OBJECT_ALLOCATED_KEY - UCL_OBJECT_ALLOCATED_VALUE = 2, //!< UCL_OBJECT_ALLOCATED_VALUE - UCL_OBJECT_NEED_KEY_ESCAPE = 4 //!< UCL_OBJECT_NEED_KEY_ESCAPE -}; +typedef enum ucl_object_flags { + UCL_OBJECT_ALLOCATED_KEY = 1, /**< An object has key allocated internally */ + UCL_OBJECT_ALLOCATED_VALUE = 2, /**< An object has a string value allocated internally */ + UCL_OBJECT_NEED_KEY_ESCAPE = 4 /**< The key of an object need to be escaped on output */ +} ucl_object_flags_t; /** - * UCL object + * UCL object structure. Please mention that the most of fields should not be touched by + * UCL users. In future, this structure may be converted to private one. */ typedef struct ucl_object_s { + /** + * Variant value type + */ union { - int64_t iv; /**< int value of an object */ - const char *sv; /**< string value of an object */ - double dv; /**< double value of an object */ - struct ucl_object_s *av; /**< array */ - void *ov; /**< object */ - void* ud; /**< opaque user data */ + int64_t iv; /**< Int value of an object */ + const char *sv; /**< String value of an object */ + double dv; /**< Double value of an object */ + struct ucl_object_s *av; /**< Array */ + void *ov; /**< Object */ + void* ud; /**< Opaque user data */ } value; - const char *key; /**< key of an object */ - struct ucl_object_s *next; /**< array handle */ - struct ucl_object_s *prev; /**< array handle */ - unsigned char* trash_stack[2]; /**< pointer to allocated chunks */ - unsigned keylen; /**< lenght of a key */ - unsigned len; /**< size of an object */ - enum ucl_type type; /**< real type */ - uint16_t ref; /**< reference count */ - uint16_t flags; /**< object flags */ + const char *key; /**< Key of an object */ + struct ucl_object_s *next; /**< Array handle */ + struct ucl_object_s *prev; /**< Array handle */ + unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */ + unsigned keylen; /**< Lenght of a key */ + unsigned len; /**< Size of an object */ + enum ucl_type type; /**< Real type */ + uint16_t ref; /**< Reference count */ + uint16_t flags; /**< Object flags */ } ucl_object_t; +/** @} */ +/** + * @defgroup utils Utility functions + * A number of utility functions simplify handling of UCL objects + * + * @{ + */ /** * Copy and return a key of an object, returned key is zero-terminated * @param obj CL object @@ -328,6 +343,19 @@ ucl_object_frombool (bool bv) ucl_object_t* ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT; +/** + * Replace a object 'elt' to the hash 'top' and associate it with key 'key', old object will be unrefed, + * if no object has been found this function works like ucl_object_insert_key() + * @param top destination object (will be created automatically if top is NULL) + * @param elt element to insert (must NOT be NULL) + * @param key key to associate with this object (either const or preallocated) + * @param keylen length of the key (or 0 for NULL terminated keys) + * @param copy_key make an internal copy of key + * @return new value of top object + */ +ucl_object_t* ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT; + /** * Insert a object 'elt' to the hash 'top' and associate it with key 'key', if the specified key exist, * try to merge its content @@ -410,7 +438,7 @@ ucl_elt_append (ucl_object_t *head, ucl_object_t *elt) * @return true if conversion was successful */ static inline bool -ucl_obj_todouble_safe (ucl_object_t *obj, double *target) +ucl_object_todouble_safe (ucl_object_t *obj, double *target) { if (obj == NULL) { return false; @@ -436,7 +464,7 @@ ucl_obj_todouble_safe (ucl_object_t *obj, double *target) * @return double value */ static inline double -ucl_obj_todouble (ucl_object_t *obj) +ucl_object_todouble (ucl_object_t *obj) { double result = 0.; @@ -451,7 +479,7 @@ ucl_obj_todouble (ucl_object_t *obj) * @return true if conversion was successful */ static inline bool -ucl_obj_toint_safe (ucl_object_t *obj, int64_t *target) +ucl_object_toint_safe (ucl_object_t *obj, int64_t *target) { if (obj == NULL) { return false; @@ -477,7 +505,7 @@ ucl_obj_toint_safe (ucl_object_t *obj, int64_t *target) * @return int value */ static inline int64_t -ucl_obj_toint (ucl_object_t *obj) +ucl_object_toint (ucl_object_t *obj) { int64_t result = 0; @@ -492,7 +520,7 @@ ucl_obj_toint (ucl_object_t *obj) * @return true if conversion was successful */ static inline bool -ucl_obj_toboolean_safe (ucl_object_t *obj, bool *target) +ucl_object_toboolean_safe (ucl_object_t *obj, bool *target) { if (obj == NULL) { return false; @@ -514,7 +542,7 @@ ucl_obj_toboolean_safe (ucl_object_t *obj, bool *target) * @return boolean value */ static inline bool -ucl_obj_toboolean (ucl_object_t *obj) +ucl_object_toboolean (ucl_object_t *obj) { bool result = false; @@ -529,7 +557,7 @@ ucl_obj_toboolean (ucl_object_t *obj) * @return true if conversion was successful */ static inline bool -ucl_obj_tostring_safe (ucl_object_t *obj, const char **target) +ucl_object_tostring_safe (ucl_object_t *obj, const char **target) { if (obj == NULL) { return false; @@ -552,7 +580,7 @@ ucl_obj_tostring_safe (ucl_object_t *obj, const char **target) * @return string value */ static inline const char * -ucl_obj_tostring (ucl_object_t *obj) +ucl_object_tostring (ucl_object_t *obj) { const char *result = NULL; @@ -572,15 +600,15 @@ ucl_object_tostring_forced (ucl_object_t *obj) } /** - * Return string as char * and len, string may be not zero terminated, more efficient that tostring as it - * allows zero-copy + * Return string as char * and len, string may be not zero terminated, more efficient that \ref ucl_obj_tostring as it + * allows zero-copy (if #UCL_PARSER_ZEROCOPY has been used during parsing) * @param obj CL object * @param target target string variable, no need to free value * @param tlen target length * @return true if conversion was successful */ static inline bool -ucl_obj_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen) +ucl_object_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen) { if (obj == NULL) { return false; @@ -603,7 +631,7 @@ ucl_obj_tolstring_safe (ucl_object_t *obj, const char **target, size_t *tlen) * @return string value */ static inline const char * -ucl_obj_tolstring (ucl_object_t *obj, size_t *tlen) +ucl_object_tolstring (ucl_object_t *obj, size_t *tlen) { const char *result = NULL; @@ -617,7 +645,7 @@ ucl_obj_tolstring (ucl_object_t *obj, size_t *tlen) * @param key key to search * @return object matched the specified key or NULL if key is not found */ -ucl_object_t * ucl_obj_get_key (ucl_object_t *obj, const char *key); +ucl_object_t * ucl_object_find_key (ucl_object_t *obj, const char *key); /** * Return object identified by a fixed size key in the specified object @@ -626,7 +654,7 @@ ucl_object_t * ucl_obj_get_key (ucl_object_t *obj, const char *key); * @param klen length of a key * @return object matched the specified key or NULL if key is not found */ -ucl_object_t *ucl_obj_get_keyl (ucl_object_t *obj, const char *key, size_t klen); +ucl_object_t *ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen); /** * Returns a key of an object as a NULL terminated string @@ -652,6 +680,56 @@ ucl_object_keyl (ucl_object_t *obj, size_t *len) return obj->key; } +/** + * Free ucl object + * @param obj ucl object to free + */ +void ucl_object_free (ucl_object_t *obj); + +/** + * Increase reference count for an object + * @param obj object to ref + */ +static inline ucl_object_t * +ucl_object_ref (ucl_object_t *obj) { + obj->ref ++; + return obj; +} + +/** + * Decrease reference count for an object + * @param obj object to unref + */ +static inline void +ucl_object_unref (ucl_object_t *obj) { + if (--obj->ref <= 0) { + ucl_object_free (obj); + } +} +/** + * Opaque iterator object + */ +typedef void* ucl_object_iter_t; + +/** + * Get next key from an object + * @param obj object to iterate + * @param iter opaque iterator, must be set to NULL on the first call: + * ucl_object_iter_t it = NULL; + * while ((cur = ucl_iterate_object (obj, &it)) != NULL) ... + * @return the next object or NULL + */ +ucl_object_t* ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values); +/** @} */ + + +/** + * @defgroup parser Parsing functions + * These functions are used to parse UCL objects + * + * @{ + */ + /** * Macro handler for a parser * @param data the content of macro @@ -724,47 +802,11 @@ ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser); */ const char *ucl_parser_get_error(struct ucl_parser *parser); /** - * Free cl parser object + * Free ucl parser object * @param parser parser object */ void ucl_parser_free (struct ucl_parser *parser); -/** - * Free cl object - * @param obj cl object to free - */ -void ucl_obj_free (ucl_object_t *obj); - -/** - * Icrease reference count for an object - * @param obj object to ref - */ -static inline ucl_object_t * -ucl_obj_ref (ucl_object_t *obj) { - obj->ref ++; - return obj; -} - -/** - * Decrease reference count for an object - * @param obj object to unref - */ -static inline void -ucl_obj_unref (ucl_object_t *obj) { - if (--obj->ref <= 0) { - ucl_obj_free (obj); - } -} - -/** - * Emit object to a string - * @param obj object - * @param emit_type if type is UCL_EMIT_JSON then emit json, if type is - * UCL_EMIT_CONFIG then emit config like object - * @return dump of an object (must be freed after using) or NULL in case of error - */ -unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type); - /** * Add new public key to parser for signatures check * @param parser parser object @@ -785,16 +827,73 @@ bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand); -typedef void* ucl_object_iter_t; +/** @} */ /** - * Get next key from an object - * @param obj object to iterate - * @param iter opaque iterator, must be set to NULL on the first call: - * ucl_object_iter_t it = NULL; - * while ((cur = ucl_iterate_object (obj, &it)) != NULL) ... - * @return the next object or NULL + * @defgroup emitter Emitting functions + * These functions are used to serialise UCL objects to some string representation. + * + * @{ */ -ucl_object_t* ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values); -#endif /* RCL_H_ */ +/** + * Structure using for emitter callbacks + */ +struct ucl_emitter_functions { + /** Append a single character */ + int (*ucl_emitter_append_character) (unsigned char c, size_t nchars, void *ud); + /** Append a string of a specified length */ + int (*ucl_emitter_append_len) (unsigned const char *str, size_t len, void *ud); + /** Append a 64 bit integer */ + int (*ucl_emitter_append_int) (int64_t elt, void *ud); + /** Append floating point element */ + int (*ucl_emitter_append_double) (double elt, void *ud); + /** Opaque userdata pointer */ + void *ud; +}; + +/** + * Emit object to a string + * @param obj object + * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is + * #UCL_EMIT_CONFIG then emit config like object + * @return dump of an object (must be freed after using) or NULL in case of error + */ +unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type); + +/** + * Emit object to a string + * @param obj object + * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is + * #UCL_EMIT_CONFIG then emit config like object + * @return dump of an object (must be freed after using) or NULL in case of error + */ +bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter); +/** @} */ + +#ifdef __cplusplus +} +#endif +/* + * XXX: Poorly named API functions, need to replace them with the appropriate + * named function. All API functions *must* use naming ucl_object_*. Usage of + * ucl_obj* should be avoided. + */ +#define ucl_obj_todouble_safe ucl_object_todouble_safe +#define ucl_obj_todouble ucl_object_todouble +#define ucl_obj_tostring ucl_object_tostring +#define ucl_obj_tostring_safe ucl_object_tostring_safe +#define ucl_obj_tolstring ucl_object_tolstring +#define ucl_obj_tolstring_safe ucl_object_tolstring_safe +#define ucl_obj_toint ucl_object_toint +#define ucl_obj_toint_safe ucl_object_toint_safe +#define ucl_obj_toboolean ucl_object_toboolean +#define ucl_obj_toboolean_safe ucl_object_toboolean_safe +#define ucl_obj_get_key ucl_object_find_key +#define ucl_obj_get_keyl ucl_object_find_keyl +#define ucl_obj_unref ucl_object_unref +#define ucl_obj_ref ucl_object_ref +#define ucl_obj_free ucl_object_free + +#endif /* UCL_H_ */ diff --git a/src/ucl/src/ucl_emitter.c b/src/ucl/src/ucl_emitter.c index d0e62436d..5269acfc2 100644 --- a/src/ucl/src/ucl_emitter.c +++ b/src/ucl/src/ucl_emitter.c @@ -29,16 +29,37 @@ /** * @file rcl_emitter.c - * Serialise RCL object to the RCL format + * Serialise UCL object to various of output formats */ -static void ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact); -static void ucl_elt_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, - bool start_tabs, bool is_top, bool expand_array); -static void ucl_elt_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, - bool start_tabs, bool compact, bool expand_array); -static void ucl_elt_array_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top); +static void ucl_obj_write_json (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact); +static void ucl_elt_write_json (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact); +static void ucl_elt_write_config (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool is_top, + bool expand_array); +static void ucl_elt_write_yaml (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool compact, + bool expand_array); +static void ucl_elt_array_write_yaml (ucl_object_t *obj, + struct ucl_emitter_functions *func, + unsigned int tabs, + bool start_tabs, + bool is_top); /** * Add tabulation to the output buffer @@ -46,21 +67,10 @@ static void ucl_elt_array_write_yaml (ucl_object_t *obj, UT_string *buf, unsigne * @param tabs number of tabs to add */ static inline void -ucl_add_tabs (UT_string *buf, unsigned int tabs, bool compact) +ucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact) { - char *p; - unsigned int i; - if (!compact) { - while (buf->n - buf->i <= tabs * 4) { - utstring_reserve (buf, buf->n * 2); - } - p = &buf->d[buf->i]; - for (i = 0; i < tabs; i ++) { - memset (&p[i * 4], ' ', 4); - } - buf->i += i * 4; - buf->d[buf->i] = '\0'; + func->ucl_emitter_append_character (' ', tabs * 4, func->ud); } } @@ -70,38 +80,39 @@ ucl_add_tabs (UT_string *buf, unsigned int tabs, bool compact) * @param buf target buffer */ static void -ucl_elt_string_write_json (const char *str, size_t size, UT_string *buf) +ucl_elt_string_write_json (const char *str, size_t size, + struct ucl_emitter_functions *func) { const char *p = str, *c = str; size_t len = 0; - utstring_append_c (buf, '"'); + func->ucl_emitter_append_character ('"', 1, func->ud); while (size) { if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { if (len > 0) { - utstring_append_len (buf, c, len); + func->ucl_emitter_append_len (c, len, func->ud); } switch (*p) { case '\n': - utstring_append_len (buf, "\\n", 2); + func->ucl_emitter_append_len ("\\n", 2, func->ud); break; case '\r': - utstring_append_len (buf, "\\r", 2); + func->ucl_emitter_append_len ("\\r", 2, func->ud); break; case '\b': - utstring_append_len (buf, "\\b", 2); + func->ucl_emitter_append_len ("\\b", 2, func->ud); break; case '\t': - utstring_append_len (buf, "\\t", 2); + func->ucl_emitter_append_len ("\\t", 2, func->ud); break; case '\f': - utstring_append_len (buf, "\\f", 2); + func->ucl_emitter_append_len ("\\f", 2, func->ud); break; case '\\': - utstring_append_len (buf, "\\\\", 2); + func->ucl_emitter_append_len ("\\\\", 2, func->ud); break; case '"': - utstring_append_len (buf, "\\\"", 2); + func->ucl_emitter_append_len ("\\\"", 2, func->ud); break; } len = 0; @@ -114,24 +125,9 @@ ucl_elt_string_write_json (const char *str, size_t size, UT_string *buf) size --; } if (len > 0) { - utstring_append_len (buf, c, len); - } - utstring_append_c (buf, '"'); -} - -static inline void -ucl_print_float (UT_string *buf, double val) -{ - if (val == (double)(int)val) { - utstring_printf (buf, "%.1lf", val); - } - else if (fabs (val - (double)(int)val) < 0.0000001) { - /* Write at maximum precision */ - utstring_printf (buf, "%.*lg", DBL_DIG, val); - } - else { - utstring_printf (buf, "%lf", val); + func->ucl_emitter_append_len (c, len, func->ud); } + func->ucl_emitter_append_character ('"', 1, func->ud); } /** @@ -140,49 +136,50 @@ ucl_print_float (UT_string *buf, double val) * @param buf target buffer */ static void -ucl_elt_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact) +ucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) { ucl_object_t *cur; ucl_hash_iter_t it = NULL; if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } if (compact) { - utstring_append_c (buf, '{'); + func->ucl_emitter_append_character ('{', 1, func->ud); } else { - utstring_append_len (buf, "{\n", 2); + func->ucl_emitter_append_len ("{\n", 2, func->ud); } while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { - ucl_add_tabs (buf, tabs + 1, compact); + ucl_add_tabs (func, tabs + 1, compact); if (cur->keylen > 0) { - ucl_elt_string_write_json (cur->key, cur->keylen, buf); + ucl_elt_string_write_json (cur->key, cur->keylen, func); } else { - utstring_append_len (buf, "null", 4); + func->ucl_emitter_append_len ("null", 4, func->ud); } if (compact) { - utstring_append_c (buf, ':'); + func->ucl_emitter_append_character (':', 1, func->ud); } else { - utstring_append_len (buf, ": ", 2); + func->ucl_emitter_append_len (": ", 2, func->ud); } - ucl_obj_write_json (cur, buf, tabs + 1, false, compact); + ucl_obj_write_json (cur, func, tabs + 1, false, compact); if (ucl_hash_iter_has_next (it)) { if (compact) { - utstring_append_c (buf, ','); + func->ucl_emitter_append_character (',', 1, func->ud); } else { - utstring_append_len (buf, ",\n", 2); + func->ucl_emitter_append_len (",\n", 2, func->ud); } } else if (!compact) { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } } - ucl_add_tabs (buf, tabs, compact); - utstring_append_c (buf, '}'); + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character ('}', 1, func->ud); } /** @@ -191,36 +188,37 @@ ucl_elt_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bo * @param buf target buffer */ static void -ucl_elt_array_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact) +ucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) { ucl_object_t *cur = obj; if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } if (compact) { - utstring_append_c (buf, '['); + func->ucl_emitter_append_character ('[', 1, func->ud); } else { - utstring_append_len (buf, "[\n", 2); + func->ucl_emitter_append_len ("[\n", 2, func->ud); } while (cur) { - ucl_elt_write_json (cur, buf, tabs + 1, true, compact); + ucl_elt_write_json (cur, func, tabs + 1, true, compact); if (cur->next != NULL) { if (compact) { - utstring_append_c (buf, ','); + func->ucl_emitter_append_character (',', 1, func->ud); } else { - utstring_append_len (buf, ",\n", 2); + func->ucl_emitter_append_len (",\n", 2, func->ud); } } else if (!compact) { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } cur = cur->next; } - ucl_add_tabs (buf, tabs, compact); - utstring_append_c (buf, ']'); + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character (']', 1, func->ud); } /** @@ -228,46 +226,55 @@ ucl_elt_array_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, * @param obj object * @param buf buffer */ -void -ucl_elt_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact) +static void +ucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) { + bool flag; + switch (obj->type) { case UCL_INT: if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } - utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj)); + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); break; case UCL_FLOAT: case UCL_TIME: if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } - ucl_print_float (buf, ucl_object_todouble (obj)); + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); break; case UCL_BOOLEAN: if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); } - utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false"); break; case UCL_STRING: if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } - ucl_elt_string_write_json (obj->value.sv, obj->len, buf); + ucl_elt_string_write_json (obj->value.sv, obj->len, func); break; case UCL_NULL: if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } - utstring_printf (buf, "null"); + func->ucl_emitter_append_len ("null", 4, func->ud); break; case UCL_OBJECT: - ucl_elt_obj_write_json (obj, buf, tabs, start_tabs, compact); + ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact); break; case UCL_ARRAY: - ucl_elt_array_write_json (obj->value.av, buf, tabs, start_tabs, compact); + ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact); break; case UCL_USERDATA: break; @@ -280,7 +287,8 @@ ucl_elt_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool s * @param buf target buffer */ static void -ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool compact) +ucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool compact) { ucl_object_t *cur; bool is_array = (obj->next != NULL); @@ -288,31 +296,31 @@ ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool s if (is_array) { /* This is an array actually */ if (start_tabs) { - ucl_add_tabs (buf, tabs, compact); + ucl_add_tabs (func, tabs, compact); } if (compact) { - utstring_append_c (buf, '['); + func->ucl_emitter_append_character ('[', 1, func->ud); } else { - utstring_append_len (buf, "[\n", 2); + func->ucl_emitter_append_len ("[\n", 2, func->ud); } cur = obj; while (cur != NULL) { - ucl_elt_write_json (cur, buf, tabs + 1, true, compact); + ucl_elt_write_json (cur, func, tabs + 1, true, compact); if (cur->next) { - utstring_append_c (buf, ','); + func->ucl_emitter_append_character (',', 1, func->ud); } if (!compact) { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } cur = cur->next; } - ucl_add_tabs (buf, tabs, compact); - utstring_append_c (buf, ']'); + ucl_add_tabs (func, tabs, compact); + func->ucl_emitter_append_character (']', 1, func->ud); } else { - ucl_elt_write_json (obj, buf, tabs, start_tabs, compact); + ucl_elt_write_json (obj, func, tabs, start_tabs, compact); } } @@ -322,17 +330,10 @@ ucl_obj_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool s * @param obj object * @return json output (should be freed after using) */ -static UT_string * -ucl_object_emit_json (ucl_object_t *obj, bool compact) +static void +ucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func) { - UT_string *buf; - - /* Allocate large enough buffer */ - utstring_new (buf); - - ucl_obj_write_json (obj, buf, 0, false, compact); - - return buf; + ucl_obj_write_json (obj, func, 0, false, compact); } /** @@ -341,46 +342,49 @@ ucl_object_emit_json (ucl_object_t *obj, bool compact) * @param buf target buffer */ static void -ucl_elt_obj_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top) +ucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) { ucl_object_t *cur, *cur_obj; ucl_hash_iter_t it = NULL; if (start_tabs) { - ucl_add_tabs (buf, tabs, is_top); + ucl_add_tabs (func, tabs, is_top); } if (!is_top) { - utstring_append_len (buf, "{\n", 2); + func->ucl_emitter_append_len ("{\n", 2, func->ud); } while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { LL_FOREACH (cur, cur_obj) { - ucl_add_tabs (buf, tabs + 1, is_top); + ucl_add_tabs (func, tabs + 1, is_top); if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) { - ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, buf); + ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func); } else { - utstring_append_len (buf, cur_obj->key, cur_obj->keylen); + func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud); } if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { - utstring_append_len (buf, " = ", 3); + func->ucl_emitter_append_len (" = ", 3, func->ud); } else { - utstring_append_c (buf, ' '); + func->ucl_emitter_append_character (' ', 1, func->ud); } - ucl_elt_write_rcl (cur_obj, buf, is_top ? tabs : tabs + 1, false, false, false); + ucl_elt_write_config (cur_obj, func, + is_top ? tabs : tabs + 1, + false, false, false); if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { - utstring_append_len (buf, ";\n", 2); + func->ucl_emitter_append_len (";\n", 2, func->ud); } else { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } } } - ucl_add_tabs (buf, tabs, is_top); + ucl_add_tabs (func, tabs, is_top); if (!is_top) { - utstring_append_c (buf, '}'); + func->ucl_emitter_append_character ('}', 1, func->ud); } } @@ -390,22 +394,23 @@ ucl_elt_obj_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, boo * @param buf target buffer */ static void -ucl_elt_array_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top) +ucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) { ucl_object_t *cur = obj; if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_append_len (buf, "[\n", 2); + func->ucl_emitter_append_len ("[\n", 2, func->ud); while (cur) { - ucl_elt_write_rcl (cur, buf, tabs + 1, true, false, false); - utstring_append_len (buf, ",\n", 2); + ucl_elt_write_config (cur, func, tabs + 1, true, false, false); + func->ucl_emitter_append_len (",\n", 2, func->ud); cur = cur->next; } - ucl_add_tabs (buf, tabs, false); - utstring_append_c (buf, ']'); + ucl_add_tabs (func, tabs, false); + func->ucl_emitter_append_character (']', 1, func->ud); } /** @@ -414,50 +419,58 @@ ucl_elt_array_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, b * @param buf buffer */ static void -ucl_elt_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, - bool start_tabs, bool is_top, bool expand_array) +ucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) { + bool flag; + if (expand_array && obj->next != NULL) { - ucl_elt_array_write_rcl (obj, buf, tabs, start_tabs, is_top); + ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top); } else { switch (obj->type) { case UCL_INT: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj)); + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); break; case UCL_FLOAT: case UCL_TIME: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - ucl_print_float (buf, ucl_object_todouble (obj)); + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); break; case UCL_BOOLEAN: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); } - utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false"); break; case UCL_STRING: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - ucl_elt_string_write_json (obj->value.sv, obj->len, buf); + ucl_elt_string_write_json (obj->value.sv, obj->len, func); break; case UCL_NULL: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_printf (buf, "null"); + func->ucl_emitter_append_len ("null", 4, func->ud); break; case UCL_OBJECT: - ucl_elt_obj_write_rcl (obj, buf, tabs, start_tabs, is_top); + ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top); break; case UCL_ARRAY: - ucl_elt_array_write_rcl (obj->value.av, buf, tabs, start_tabs, is_top); + ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top); break; case UCL_USERDATA: break; @@ -470,30 +483,24 @@ ucl_elt_write_rcl (ucl_object_t *obj, UT_string *buf, unsigned int tabs, * @param obj object * @return rcl output (should be freed after using) */ -static UT_string * -ucl_object_emit_rcl (ucl_object_t *obj) +static void +ucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func) { - UT_string *buf; - - /* Allocate large enough buffer */ - utstring_new (buf); - - ucl_elt_write_rcl (obj, buf, 0, false, true, true); - - return buf; + ucl_elt_write_config (obj, func, 0, false, true, true); } static void -ucl_obj_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs) +ucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs) { bool is_array = (obj->next != NULL); if (is_array) { - ucl_elt_array_write_yaml (obj, buf, tabs, start_tabs, false); + ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false); } else { - ucl_elt_write_yaml(obj, buf, tabs, start_tabs, false, true); + ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true); } } @@ -503,44 +510,45 @@ ucl_obj_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool s * @param buf target buffer */ static void -ucl_elt_obj_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top) +ucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) { ucl_object_t *cur; ucl_hash_iter_t it = NULL; if (start_tabs) { - ucl_add_tabs (buf, tabs, is_top); + ucl_add_tabs (func, tabs, is_top); } if (!is_top) { - utstring_append_len (buf, "{\n", 2); + func->ucl_emitter_append_len ("{\n", 2, func->ud); } while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { - ucl_add_tabs (buf, tabs + 1, is_top); + ucl_add_tabs (func, tabs + 1, is_top); if (cur->keylen > 0) { - ucl_elt_string_write_json (cur->key, cur->keylen, buf); + ucl_elt_string_write_json (cur->key, cur->keylen, func); } else { - utstring_append_len (buf, "null", 4); + func->ucl_emitter_append_len ("null", 4, func->ud); } - utstring_append_len(buf, ": ", 2); - ucl_obj_write_yaml (cur, buf, is_top ? tabs : tabs + 1, false); + func->ucl_emitter_append_len (": ", 2, func->ud); + ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false); if (ucl_hash_iter_has_next(it)) { if (!is_top) { - utstring_append_len (buf, ",\n", 2); + func->ucl_emitter_append_len (",\n", 2, func->ud); } else { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } } else { - utstring_append_c (buf, '\n'); + func->ucl_emitter_append_character ('\n', 1, func->ud); } } - ucl_add_tabs (buf, tabs, is_top); + ucl_add_tabs (func, tabs, is_top); if (!is_top) { - utstring_append_c (buf, '}'); + func->ucl_emitter_append_character ('}', 1, func->ud); } } @@ -550,22 +558,23 @@ ucl_elt_obj_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bo * @param buf target buffer */ static void -ucl_elt_array_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, bool start_tabs, bool is_top) +ucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top) { ucl_object_t *cur = obj; if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_append_len (buf, "[\n", 2); + func->ucl_emitter_append_len ("[\n", 2, func->ud); while (cur) { - ucl_elt_write_yaml (cur, buf, tabs + 1, true, false, false); - utstring_append_len (buf, ",\n", 2); + ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false); + func->ucl_emitter_append_len (",\n", 2, func->ud); cur = cur->next; } - ucl_add_tabs (buf, tabs, false); - utstring_append_c (buf, ']'); + ucl_add_tabs (func, tabs, false); + func->ucl_emitter_append_character (']', 1, func->ud); } /** @@ -574,50 +583,58 @@ ucl_elt_array_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, * @param buf buffer */ static void -ucl_elt_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, - bool start_tabs, bool is_top, bool expand_array) +ucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, + unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) { - if (expand_array && obj->next != NULL) { - ucl_elt_array_write_yaml (obj, buf, tabs, start_tabs, is_top); - } + bool flag; + + if (expand_array && obj->next != NULL ) { + ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top); + } else { switch (obj->type) { case UCL_INT: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_printf (buf, "%jd", (intmax_t)ucl_object_toint (obj)); + func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); break; case UCL_FLOAT: case UCL_TIME: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - ucl_print_float (buf, ucl_object_todouble (obj)); + func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); break; case UCL_BOOLEAN: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); + } + flag = ucl_object_toboolean (obj); + if (flag) { + func->ucl_emitter_append_len ("true", 4, func->ud); + } + else { + func->ucl_emitter_append_len ("false", 5, func->ud); } - utstring_printf (buf, "%s", ucl_object_toboolean (obj) ? "true" : "false"); break; case UCL_STRING: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - ucl_elt_string_write_json (obj->value.sv, obj->len, buf); + ucl_elt_string_write_json (obj->value.sv, obj->len, func); break; case UCL_NULL: if (start_tabs) { - ucl_add_tabs (buf, tabs, false); + ucl_add_tabs (func, tabs, false); } - utstring_printf (buf, "null"); + func->ucl_emitter_append_len ("null", 4, func->ud); break; case UCL_OBJECT: - ucl_elt_obj_write_yaml (obj, buf, tabs, start_tabs, is_top); + ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top); break; case UCL_ARRAY: - ucl_elt_array_write_yaml (obj->value.av, buf, tabs, start_tabs, is_top); + ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top); break; case UCL_USERDATA: break; @@ -630,46 +647,129 @@ ucl_elt_write_yaml (ucl_object_t *obj, UT_string *buf, unsigned int tabs, * @param obj object * @return rcl output (should be freed after using) */ -static UT_string * -ucl_object_emit_yaml (ucl_object_t *obj) +static void +ucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func) { - UT_string *buf; + ucl_elt_write_yaml (obj, func, 0, false, true, true); +} - /* Allocate large enough buffer */ - utstring_new (buf); +/* + * Generic utstring output + */ +static int +ucl_utstring_append_character (unsigned char c, size_t len, void *ud) +{ + UT_string *buf = ud; - ucl_elt_write_yaml (obj, buf, 0, false, true, true); + if (len == 1) { + utstring_append_c (buf, c); + } + else { + utstring_reserve (buf, len); + memset (&buf->d[buf->i], c, len); + buf->i += len; + buf->d[buf->i] = '\0'; + } + + return 0; +} + +static int +ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud) +{ + UT_string *buf = ud; + + utstring_append_len (buf, str, len); + + return 0; +} + +static int +ucl_utstring_append_int (int64_t val, void *ud) +{ + UT_string *buf = ud; + + utstring_printf (buf, "%jd", (intmax_t)val); + return 0; +} + +static int +ucl_utstring_append_double (double val, void *ud) +{ + UT_string *buf = ud; + const double delta = 0.0000001; + + if (val == (double)(int)val) { + utstring_printf (buf, "%.1lf", val); + } + else if (fabs (val - (double)(int)val) < delta) { + /* Write at maximum precision */ + utstring_printf (buf, "%.*lg", DBL_DIG, val); + } + else { + utstring_printf (buf, "%lf", val); + } - return buf; + return 0; } + unsigned char * ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type) { UT_string *buf = NULL; unsigned char *res = NULL; + struct ucl_emitter_functions func = { + .ucl_emitter_append_character = ucl_utstring_append_character, + .ucl_emitter_append_len = ucl_utstring_append_len, + .ucl_emitter_append_int = ucl_utstring_append_int, + .ucl_emitter_append_double = ucl_utstring_append_double + }; if (obj == NULL) { return NULL; } + utstring_new (buf); + func.ud = buf; + + if (buf != NULL) { + if (emit_type == UCL_EMIT_JSON) { + ucl_object_emit_json (obj, false, &func); + } + else if (emit_type == UCL_EMIT_JSON_COMPACT) { + ucl_object_emit_json (obj, true, &func); + } + else if (emit_type == UCL_EMIT_YAML) { + ucl_object_emit_yaml (obj, &func); + } + else { + ucl_object_emit_config (obj, &func); + } + + res = utstring_body (buf); + free (buf); + } + + return res; +} + +bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, + struct ucl_emitter_functions *emitter) +{ if (emit_type == UCL_EMIT_JSON) { - buf = ucl_object_emit_json (obj, false); + ucl_object_emit_json (obj, false, emitter); } else if (emit_type == UCL_EMIT_JSON_COMPACT) { - buf = ucl_object_emit_json (obj, true); + ucl_object_emit_json (obj, true, emitter); } else if (emit_type == UCL_EMIT_YAML) { - buf = ucl_object_emit_yaml (obj); + ucl_object_emit_yaml (obj, emitter); } else { - buf = ucl_object_emit_rcl (obj); + ucl_object_emit_config (obj, emitter); } - if (buf != NULL) { - res = utstring_body (buf); - free (buf); - } - - return res; + /* XXX: need some error checks here */ + return true; } diff --git a/src/ucl/src/ucl_internal.h b/src/ucl/src/ucl_internal.h index 78b52edbd..a68403c53 100644 --- a/src/ucl/src/ucl_internal.h +++ b/src/ucl/src/ucl_internal.h @@ -174,9 +174,6 @@ size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz); size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz); -void ucl_elt_write_json (ucl_object_t *obj, UT_string *buf, unsigned int tabs, - bool start_tabs, bool compact); - #ifdef __GNUC__ static inline void ucl_create_err (UT_string **err, const char *fmt, ...) diff --git a/src/ucl/src/ucl_util.c b/src/ucl/src/ucl_util.c index 470d29163..817d02ac3 100644 --- a/src/ucl/src/ucl_util.c +++ b/src/ucl/src/ucl_util.c @@ -78,7 +78,7 @@ ucl_object_free_internal (ucl_object_t *obj, bool allow_rec) } void -ucl_obj_free (ucl_object_t *obj) +ucl_object_free (ucl_object_t *obj) { ucl_object_free_internal (obj, true); } @@ -196,7 +196,6 @@ ucl_copy_key_trash (ucl_object_t *obj) char * ucl_copy_value_trash (ucl_object_t *obj) { - UT_string *emitted; if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) { if (obj->type == UCL_STRING) { /* Special case for strings */ @@ -209,14 +208,9 @@ ucl_copy_value_trash (ucl_object_t *obj) } else { /* Just emit value in json notation */ - utstring_new (emitted); - - if (emitted != NULL) { - ucl_elt_write_json (obj, emitted, 0, 0, true); - obj->trash_stack[UCL_TRASH_VALUE] = emitted->d; - obj->len = emitted->i; - free (emitted); - } + obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit (obj, + UCL_EMIT_JSON_COMPACT); + obj->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]); } obj->flags |= UCL_OBJECT_ALLOCATED_VALUE; } @@ -943,7 +937,7 @@ ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags static ucl_object_t * ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, - const char *key, size_t keylen, bool copy_key, bool merge) + const char *key, size_t keylen, bool copy_key, bool merge, bool replace) { ucl_object_t *found, *cur; ucl_object_iter_t it = NULL; @@ -997,30 +991,39 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, top->value.ov = ucl_hash_insert_object (top->value.ov, elt); DL_APPEND (found, elt); } - else if (!merge) { - DL_APPEND (found, elt); - } else { - if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) { - /* Insert old elt to new one */ - elt = ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false); + if (replace) { ucl_hash_delete (top->value.ov, found); + ucl_object_unref (found); top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + found = NULL; + DL_APPEND (found, elt); } - else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { - /* Insert new to old */ - found = ucl_object_insert_key_common (found, elt, elt->key, elt->keylen, copy_key, false); - } - else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) { - /* Mix two hashes */ - while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) { - ucl_object_ref (cur); - found = ucl_object_insert_key_common (found, cur, cur->key, cur->keylen, copy_key, false); + else if (merge) { + if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Insert old elt to new one */ + elt = ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false, false); + ucl_hash_delete (top->value.ov, found); + top->value.ov = ucl_hash_insert_object (top->value.ov, elt); + } + else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { + /* Insert new to old */ + found = ucl_object_insert_key_common (found, elt, elt->key, elt->keylen, copy_key, false, false); + } + else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) { + /* Mix two hashes */ + while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) { + ucl_object_ref (cur); + found = ucl_object_insert_key_common (found, cur, cur->key, cur->keylen, copy_key, false, false); + } + ucl_object_unref (elt); + } + else { + /* Just make a list of scalars */ + DL_APPEND (found, elt); } - ucl_object_unref (elt); } else { - /* Just make a list of scalars */ DL_APPEND (found, elt); } } @@ -1032,18 +1035,25 @@ ucl_object_t * ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, const char *key, size_t keylen, bool copy_key) { - return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false); + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false); } ucl_object_t * ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, const char *key, size_t keylen, bool copy_key) { - return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true); + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false); +} + +ucl_object_t * +ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, + const char *key, size_t keylen, bool copy_key) +{ + return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true); } ucl_object_t * -ucl_obj_get_keyl (ucl_object_t *obj, const char *key, size_t klen) +ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen) { ucl_object_t *ret, srch; @@ -1059,7 +1069,7 @@ ucl_obj_get_keyl (ucl_object_t *obj, const char *key, size_t klen) } ucl_object_t * -ucl_obj_get_key (ucl_object_t *obj, const char *key) +ucl_object_find_key (ucl_object_t *obj, const char *key) { size_t klen; ucl_object_t *ret, srch; -- 2.39.5