]> source.dussan.org Git - rspamd.git/commitdiff
Update from libucl.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 13 Jan 2014 00:44:07 +0000 (00:44 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 13 Jan 2014 00:44:07 +0000 (00:44 +0000)
src/ucl/include/ucl.h
src/ucl/src/ucl_emitter.c
src/ucl/src/ucl_internal.h
src/ucl/src/ucl_util.c

index 632c6e1702f1f8960227b7d7281a4354ffd98b93..d1dd61fdb2797a296b47a38ebb17103d38117919 100644 (file)
  * 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
@@ -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_ */
index d0e62436d72cffd367d910cd7866650499414804..5269acfc2a54cd89d52978bd5d5bc8cafe041149 100644 (file)
 
 /**
  * @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;
 }
index 78b52edbd1d74ebb7e1952b1633959cb0c02fda7..a68403c53a1f3865a1485fee5b39c2de5f6d7c1e 100644 (file)
@@ -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, ...)
index 470d291637aeca6877c12632e9280fdafb25fe6e..817d02ac3ba315de3a911a9f6db1bc646fc0afe5 100644 (file)
@@ -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;