* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RCL_H_
-#define RCL_H_
+#ifndef UCL_H_
+#define UCL_H_
-#include <string.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <stdio.h>
#include "config.h"
+#include <stdbool.h>
/**
- * @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
#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
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
* @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;
* @return double value
*/
static inline double
-ucl_obj_todouble (ucl_object_t *obj)
+ucl_object_todouble (ucl_object_t *obj)
{
double result = 0.;
* @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;
* @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;
* @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;
* @return boolean value
*/
static inline bool
-ucl_obj_toboolean (ucl_object_t *obj)
+ucl_object_toboolean (ucl_object_t *obj)
{
bool result = false;
* @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;
* @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;
}
/**
- * 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;
* @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;
* @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
* @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
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
*/
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
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_ */
/**
* @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
* @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);
}
}
* @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;
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);
}
/**
* @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);
}
/**
* @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);
}
/**
* @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;
* @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);
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);
}
}
* @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);
}
/**
* @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);
}
}
* @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);
}
/**
* @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;
* @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);
}
}
* @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);
}
}
* @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);
}
/**
* @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;
* @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;
}