ucl_emitter.c revision 262398
1189251Ssam/* Copyright (c) 2013, Vsevolod Stakhov 2189251Ssam * All rights reserved. 3252726Srpaulo * 4189251Ssam * Redistribution and use in source and binary forms, with or without 5252726Srpaulo * modification, are permitted provided that the following conditions are met: 6252726Srpaulo * * Redistributions of source code must retain the above copyright 7189251Ssam * notice, this list of conditions and the following disclaimer. 8189251Ssam * * Redistributions in binary form must reproduce the above copyright 9189251Ssam * notice, this list of conditions and the following disclaimer in the 10189251Ssam * documentation and/or other materials provided with the distribution. 11189251Ssam * 12189251Ssam * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY 13189251Ssam * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14252726Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15214734Srpaulo * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 16214734Srpaulo * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17214734Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18214734Srpaulo * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19214734Srpaulo * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20189251Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21214734Srpaulo * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22252726Srpaulo */ 23214734Srpaulo 24252726Srpaulo#include <float.h> 25214734Srpaulo#include <math.h> 26214734Srpaulo#include "ucl.h" 27214734Srpaulo#include "ucl_internal.h" 28214734Srpaulo#include "ucl_chartable.h" 29189251Ssam 30214734Srpaulo/** 31214734Srpaulo * @file rcl_emitter.c 32252726Srpaulo * Serialise UCL object to various of output formats 33252726Srpaulo */ 34252726Srpaulo 35189251Ssam 36189251Ssamstatic void ucl_obj_write_json (ucl_object_t *obj, 37209158Srpaulo struct ucl_emitter_functions *func, 38252726Srpaulo unsigned int tabs, 39189251Ssam bool start_tabs, 40252726Srpaulo bool compact); 41189251Ssamstatic void ucl_elt_write_json (ucl_object_t *obj, 42189251Ssam struct ucl_emitter_functions *func, 43189251Ssam unsigned int tabs, 44189251Ssam bool start_tabs, 45189251Ssam bool compact); 46252726Srpaulostatic void ucl_elt_write_config (ucl_object_t *obj, 47252726Srpaulo struct ucl_emitter_functions *func, 48252726Srpaulo unsigned int tabs, 49252726Srpaulo bool start_tabs, 50252726Srpaulo bool is_top, 51252726Srpaulo bool expand_array); 52252726Srpaulostatic void ucl_elt_write_yaml (ucl_object_t *obj, 53252726Srpaulo struct ucl_emitter_functions *func, 54252726Srpaulo unsigned int tabs, 55189251Ssam bool start_tabs, 56189251Ssam bool compact, 57189251Ssam bool expand_array); 58189251Ssamstatic void ucl_elt_array_write_yaml (ucl_object_t *obj, 59189251Ssam struct ucl_emitter_functions *func, 60189251Ssam unsigned int tabs, 61189251Ssam bool start_tabs, 62189251Ssam bool is_top); 63189251Ssam 64189251Ssam/** 65189251Ssam * Add tabulation to the output buffer 66189251Ssam * @param buf target buffer 67189251Ssam * @param tabs number of tabs to add 68189251Ssam */ 69189251Ssamstatic inline void 70189251Ssamucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact) 71189251Ssam{ 72189251Ssam if (!compact) { 73189251Ssam func->ucl_emitter_append_character (' ', tabs * 4, func->ud); 74189251Ssam } 75189251Ssam} 76189251Ssam 77189251Ssam/** 78252726Srpaulo * Serialise string 79189251Ssam * @param str string to emit 80252726Srpaulo * @param buf target buffer 81252726Srpaulo */ 82189251Ssamstatic void 83189251Ssamucl_elt_string_write_json (const char *str, size_t size, 84189251Ssam struct ucl_emitter_functions *func) 85252726Srpaulo{ 86252726Srpaulo const char *p = str, *c = str; 87189251Ssam size_t len = 0; 88252726Srpaulo 89252726Srpaulo func->ucl_emitter_append_character ('"', 1, func->ud); 90189251Ssam while (size) { 91189251Ssam if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { 92252726Srpaulo if (len > 0) { 93252726Srpaulo func->ucl_emitter_append_len (c, len, func->ud); 94252726Srpaulo } 95252726Srpaulo switch (*p) { 96252726Srpaulo case '\n': 97214734Srpaulo func->ucl_emitter_append_len ("\\n", 2, func->ud); 98252726Srpaulo break; 99252726Srpaulo case '\r': 100189251Ssam func->ucl_emitter_append_len ("\\r", 2, func->ud); 101189251Ssam break; 102189251Ssam case '\b': 103189251Ssam func->ucl_emitter_append_len ("\\b", 2, func->ud); 104189251Ssam break; 105189251Ssam case '\t': 106189251Ssam func->ucl_emitter_append_len ("\\t", 2, func->ud); 107189251Ssam break; 108189251Ssam case '\f': 109189251Ssam func->ucl_emitter_append_len ("\\f", 2, func->ud); 110189251Ssam break; 111189251Ssam case '\\': 112189251Ssam func->ucl_emitter_append_len ("\\\\", 2, func->ud); 113189251Ssam break; 114189251Ssam case '"': 115189251Ssam func->ucl_emitter_append_len ("\\\"", 2, func->ud); 116189251Ssam break; 117189251Ssam } 118209158Srpaulo len = 0; 119209158Srpaulo c = ++p; 120209158Srpaulo } 121209158Srpaulo else { 122209158Srpaulo p ++; 123214734Srpaulo len ++; 124209158Srpaulo } 125209158Srpaulo size --; 126209158Srpaulo } 127209158Srpaulo if (len > 0) { 128209158Srpaulo func->ucl_emitter_append_len (c, len, func->ud); 129209158Srpaulo } 130209158Srpaulo func->ucl_emitter_append_character ('"', 1, func->ud); 131209158Srpaulo} 132209158Srpaulo 133209158Srpaulo/** 134209158Srpaulo * Write a single object to the buffer 135209158Srpaulo * @param obj object to write 136209158Srpaulo * @param buf target buffer 137209158Srpaulo */ 138209158Srpaulostatic void 139252726Srpauloucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, 140252726Srpaulo unsigned int tabs, bool start_tabs, bool compact) 141214734Srpaulo{ 142214734Srpaulo ucl_object_t *cur; 143214734Srpaulo ucl_hash_iter_t it = NULL; 144214734Srpaulo 145209158Srpaulo if (start_tabs) { 146209158Srpaulo ucl_add_tabs (func, tabs, compact); 147209158Srpaulo } 148214734Srpaulo if (compact) { 149214734Srpaulo func->ucl_emitter_append_character ('{', 1, func->ud); 150214734Srpaulo } 151209158Srpaulo else { 152209158Srpaulo func->ucl_emitter_append_len ("{\n", 2, func->ud); 153209158Srpaulo } 154209158Srpaulo while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { 155209158Srpaulo ucl_add_tabs (func, tabs + 1, compact); 156214734Srpaulo if (cur->keylen > 0) { 157209158Srpaulo ucl_elt_string_write_json (cur->key, cur->keylen, func); 158209158Srpaulo } 159209158Srpaulo else { 160209158Srpaulo func->ucl_emitter_append_len ("null", 4, func->ud); 161209158Srpaulo } 162209158Srpaulo if (compact) { 163209158Srpaulo func->ucl_emitter_append_character (':', 1, func->ud); 164209158Srpaulo } 165209158Srpaulo else { 166209158Srpaulo func->ucl_emitter_append_len (": ", 2, func->ud); 167209158Srpaulo } 168209158Srpaulo ucl_obj_write_json (cur, func, tabs + 1, false, compact); 169209158Srpaulo if (ucl_hash_iter_has_next (it)) { 170209158Srpaulo if (compact) { 171209158Srpaulo func->ucl_emitter_append_character (',', 1, func->ud); 172209158Srpaulo } 173209158Srpaulo else { 174209158Srpaulo func->ucl_emitter_append_len (",\n", 2, func->ud); 175209158Srpaulo } 176209158Srpaulo } 177209158Srpaulo else if (!compact) { 178209158Srpaulo func->ucl_emitter_append_character ('\n', 1, func->ud); 179209158Srpaulo } 180209158Srpaulo } 181209158Srpaulo ucl_add_tabs (func, tabs, compact); 182209158Srpaulo func->ucl_emitter_append_character ('}', 1, func->ud); 183209158Srpaulo} 184209158Srpaulo 185209158Srpaulo/** 186209158Srpaulo * Write a single array to the buffer 187209158Srpaulo * @param obj array to write 188209158Srpaulo * @param buf target buffer 189209158Srpaulo */ 190209158Srpaulostatic void 191209158Srpauloucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, 192209158Srpaulo unsigned int tabs, bool start_tabs, bool compact) 193209158Srpaulo{ 194209158Srpaulo ucl_object_t *cur = obj; 195209158Srpaulo 196209158Srpaulo if (start_tabs) { 197209158Srpaulo ucl_add_tabs (func, tabs, compact); 198209158Srpaulo } 199189251Ssam if (compact) { 200189251Ssam func->ucl_emitter_append_character ('[', 1, func->ud); 201189251Ssam } 202189251Ssam else { 203189251Ssam func->ucl_emitter_append_len ("[\n", 2, func->ud); 204209158Srpaulo } 205209158Srpaulo while (cur) { 206252726Srpaulo ucl_elt_write_json (cur, func, tabs + 1, true, compact); 207252726Srpaulo if (cur->next != NULL) { 208252726Srpaulo if (compact) { 209189251Ssam func->ucl_emitter_append_character (',', 1, func->ud); 210189251Ssam } 211189251Ssam else { 212189251Ssam func->ucl_emitter_append_len (",\n", 2, func->ud); 213189251Ssam } 214189251Ssam } 215189251Ssam else if (!compact) { 216189251Ssam func->ucl_emitter_append_character ('\n', 1, func->ud); 217189251Ssam } 218189251Ssam cur = cur->next; 219189251Ssam } 220189251Ssam ucl_add_tabs (func, tabs, compact); 221214734Srpaulo func->ucl_emitter_append_character (']', 1, func->ud); 222214734Srpaulo} 223189251Ssam 224189251Ssam/** 225189251Ssam * Emit a single element 226189251Ssam * @param obj object 227189251Ssam * @param buf buffer 228189251Ssam */ 229189251Ssamstatic void 230189251Ssamucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, 231189251Ssam unsigned int tabs, bool start_tabs, bool compact) 232209158Srpaulo{ 233209158Srpaulo bool flag; 234209158Srpaulo 235209158Srpaulo switch (obj->type) { 236209158Srpaulo case UCL_INT: 237209158Srpaulo if (start_tabs) { 238209158Srpaulo ucl_add_tabs (func, tabs, compact); 239209158Srpaulo } 240209158Srpaulo func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); 241209158Srpaulo break; 242209158Srpaulo case UCL_FLOAT: 243209158Srpaulo case UCL_TIME: 244209158Srpaulo if (start_tabs) { 245209158Srpaulo ucl_add_tabs (func, tabs, compact); 246209158Srpaulo } 247209158Srpaulo func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); 248209158Srpaulo break; 249209158Srpaulo case UCL_BOOLEAN: 250209158Srpaulo if (start_tabs) { 251209158Srpaulo ucl_add_tabs (func, tabs, compact); 252209158Srpaulo } 253189251Ssam flag = ucl_object_toboolean (obj); 254209158Srpaulo if (flag) { 255209158Srpaulo func->ucl_emitter_append_len ("true", 4, func->ud); 256189251Ssam } 257189251Ssam else { 258189251Ssam func->ucl_emitter_append_len ("false", 5, func->ud); 259252726Srpaulo } 260252726Srpaulo break; 261252726Srpaulo case UCL_STRING: 262252726Srpaulo if (start_tabs) { 263252726Srpaulo ucl_add_tabs (func, tabs, compact); 264252726Srpaulo } 265252726Srpaulo ucl_elt_string_write_json (obj->value.sv, obj->len, func); 266252726Srpaulo break; 267252726Srpaulo case UCL_NULL: 268189251Ssam if (start_tabs) { 269189251Ssam ucl_add_tabs (func, tabs, compact); 270189251Ssam } 271252726Srpaulo func->ucl_emitter_append_len ("null", 4, func->ud); 272252726Srpaulo break; 273252726Srpaulo case UCL_OBJECT: 274252726Srpaulo ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact); 275252726Srpaulo break; 276252726Srpaulo case UCL_ARRAY: 277252726Srpaulo ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact); 278189251Ssam break; 279189251Ssam case UCL_USERDATA: 280189251Ssam break; 281189251Ssam } 282189251Ssam} 283189251Ssam 284189251Ssam/** 285252726Srpaulo * Write a single object to the buffer 286252726Srpaulo * @param obj object 287252726Srpaulo * @param buf target buffer 288252726Srpaulo */ 289252726Srpaulostatic void 290252726Srpauloucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func, 291252726Srpaulo unsigned int tabs, bool start_tabs, bool compact) 292189251Ssam{ 293189251Ssam ucl_object_t *cur; 294189251Ssam bool is_array = (obj->next != NULL); 295189251Ssam 296189251Ssam if (is_array) { 297189251Ssam /* This is an array actually */ 298214734Srpaulo if (start_tabs) { 299189251Ssam ucl_add_tabs (func, tabs, compact); 300189251Ssam } 301189251Ssam 302189251Ssam if (compact) { 303189251Ssam func->ucl_emitter_append_character ('[', 1, func->ud); 304189251Ssam } 305189251Ssam else { 306189251Ssam func->ucl_emitter_append_len ("[\n", 2, func->ud); 307189251Ssam } 308189251Ssam cur = obj; 309189251Ssam while (cur != NULL) { 310189251Ssam ucl_elt_write_json (cur, func, tabs + 1, true, compact); 311189251Ssam if (cur->next) { 312189251Ssam func->ucl_emitter_append_character (',', 1, func->ud); 313189251Ssam } 314209158Srpaulo if (!compact) { 315209158Srpaulo func->ucl_emitter_append_character ('\n', 1, func->ud); 316209158Srpaulo } 317209158Srpaulo cur = cur->next; 318209158Srpaulo } 319209158Srpaulo ucl_add_tabs (func, tabs, compact); 320209158Srpaulo func->ucl_emitter_append_character (']', 1, func->ud); 321209158Srpaulo } 322209158Srpaulo else { 323209158Srpaulo ucl_elt_write_json (obj, func, tabs, start_tabs, compact); 324209158Srpaulo } 325209158Srpaulo 326209158Srpaulo} 327209158Srpaulo 328209158Srpaulo/** 329209158Srpaulo * Emit an object to json 330209158Srpaulo * @param obj object 331209158Srpaulo * @return json output (should be freed after using) 332209158Srpaulo */ 333209158Srpaulostatic void 334209158Srpauloucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func) 335209158Srpaulo{ 336209158Srpaulo ucl_obj_write_json (obj, func, 0, false, compact); 337209158Srpaulo} 338209158Srpaulo 339209158Srpaulo/** 340189251Ssam * Write a single object to the buffer 341209158Srpaulo * @param obj object to write 342189251Ssam * @param buf target buffer 343209158Srpaulo */ 344189251Ssamstatic void 345189251Ssamucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, 346189251Ssam unsigned int tabs, bool start_tabs, bool is_top) 347189251Ssam{ 348189251Ssam ucl_object_t *cur, *cur_obj; 349189251Ssam ucl_hash_iter_t it = NULL; 350189251Ssam 351189251Ssam if (start_tabs) { 352189251Ssam ucl_add_tabs (func, tabs, is_top); 353209158Srpaulo } 354189251Ssam if (!is_top) { 355189251Ssam func->ucl_emitter_append_len ("{\n", 2, func->ud); 356189251Ssam } 357189251Ssam 358252726Srpaulo while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { 359252726Srpaulo LL_FOREACH (cur, cur_obj) { 360252726Srpaulo ucl_add_tabs (func, tabs + 1, is_top); 361252726Srpaulo if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) { 362252726Srpaulo ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func); 363252726Srpaulo } 364252726Srpaulo else { 365252726Srpaulo func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud); 366252726Srpaulo } 367252726Srpaulo if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { 368189251Ssam func->ucl_emitter_append_len (" = ", 3, func->ud); 369189251Ssam } 370189251Ssam else { 371189251Ssam func->ucl_emitter_append_character (' ', 1, func->ud); 372189251Ssam } 373189251Ssam ucl_elt_write_config (cur_obj, func, 374189251Ssam is_top ? tabs : tabs + 1, 375189251Ssam false, false, false); 376189251Ssam if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) { 377189251Ssam func->ucl_emitter_append_len (";\n", 2, func->ud); 378189251Ssam } 379189251Ssam else { 380189251Ssam func->ucl_emitter_append_character ('\n', 1, func->ud); 381189251Ssam } 382189251Ssam } 383189251Ssam } 384189251Ssam 385189251Ssam ucl_add_tabs (func, tabs, is_top); 386189251Ssam if (!is_top) { 387189251Ssam func->ucl_emitter_append_character ('}', 1, func->ud); 388189251Ssam } 389189251Ssam} 390189251Ssam 391189251Ssam/** 392189251Ssam * Write a single array to the buffer 393189251Ssam * @param obj array to write 394189251Ssam * @param buf target buffer 395252726Srpaulo */ 396189251Ssamstatic void 397189251Ssamucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, 398189251Ssam unsigned int tabs, bool start_tabs, bool is_top) 399189251Ssam{ 400189251Ssam ucl_object_t *cur = obj; 401189251Ssam 402189251Ssam if (start_tabs) { 403189251Ssam ucl_add_tabs (func, tabs, false); 404252726Srpaulo } 405189251Ssam 406189251Ssam func->ucl_emitter_append_len ("[\n", 2, func->ud); 407189251Ssam while (cur) { 408189251Ssam ucl_elt_write_config (cur, func, tabs + 1, true, false, false); 409189251Ssam func->ucl_emitter_append_len (",\n", 2, func->ud); 410189251Ssam cur = cur->next; 411189251Ssam } 412189251Ssam ucl_add_tabs (func, tabs, false); 413209158Srpaulo func->ucl_emitter_append_character (']', 1, func->ud); 414209158Srpaulo} 415252726Srpaulo 416252726Srpaulo/** 417252726Srpaulo * Emit a single element 418189251Ssam * @param obj object 419189251Ssam * @param buf buffer 420189251Ssam */ 421189251Ssamstatic void 422189251Ssamucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func, 423189251Ssam unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) 424189251Ssam{ 425189251Ssam bool flag; 426252726Srpaulo 427252726Srpaulo if (expand_array && obj->next != NULL) { 428252726Srpaulo ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top); 429252726Srpaulo } 430252726Srpaulo else { 431252726Srpaulo switch (obj->type) { 432252726Srpaulo case UCL_INT: 433189251Ssam if (start_tabs) { 434189251Ssam ucl_add_tabs (func, tabs, false); 435189251Ssam } 436189251Ssam func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); 437252726Srpaulo break; 438252726Srpaulo case UCL_FLOAT: 439252726Srpaulo case UCL_TIME: 440252726Srpaulo if (start_tabs) { 441252726Srpaulo ucl_add_tabs (func, tabs, false); 442252726Srpaulo } 443252726Srpaulo func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); 444252726Srpaulo break; 445252726Srpaulo case UCL_BOOLEAN: 446189251Ssam if (start_tabs) { 447189251Ssam ucl_add_tabs (func, tabs, false); 448189251Ssam } 449189251Ssam flag = ucl_object_toboolean (obj); 450189251Ssam if (flag) { 451189251Ssam func->ucl_emitter_append_len ("true", 4, func->ud); 452214734Srpaulo } 453252726Srpaulo else { 454252726Srpaulo func->ucl_emitter_append_len ("false", 5, func->ud); 455252726Srpaulo } 456252726Srpaulo break; 457252726Srpaulo case UCL_STRING: 458252726Srpaulo if (start_tabs) { 459252726Srpaulo ucl_add_tabs (func, tabs, false); 460252726Srpaulo } 461252726Srpaulo ucl_elt_string_write_json (obj->value.sv, obj->len, func); 462252726Srpaulo break; 463252726Srpaulo case UCL_NULL: 464252726Srpaulo if (start_tabs) { 465252726Srpaulo ucl_add_tabs (func, tabs, false); 466252726Srpaulo } 467252726Srpaulo func->ucl_emitter_append_len ("null", 4, func->ud); 468189251Ssam break; 469189251Ssam case UCL_OBJECT: 470189251Ssam ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top); 471252726Srpaulo break; 472252726Srpaulo case UCL_ARRAY: 473252726Srpaulo ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top); 474252726Srpaulo break; 475252726Srpaulo case UCL_USERDATA: 476252726Srpaulo break; 477189251Ssam } 478189251Ssam } 479189251Ssam} 480252726Srpaulo 481252726Srpaulo/** 482252726Srpaulo * Emit an object to rcl 483252726Srpaulo * @param obj object 484252726Srpaulo * @return rcl output (should be freed after using) 485252726Srpaulo */ 486252726Srpaulostatic void 487252726Srpauloucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func) 488252726Srpaulo{ 489252726Srpaulo ucl_elt_write_config (obj, func, 0, false, true, true); 490252726Srpaulo} 491252726Srpaulo 492252726Srpaulo 493252726Srpaulostatic void 494252726Srpauloucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, 495252726Srpaulo unsigned int tabs, bool start_tabs) 496252726Srpaulo{ 497252726Srpaulo bool is_array = (obj->next != NULL); 498252726Srpaulo 499252726Srpaulo if (is_array) { 500252726Srpaulo ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false); 501189251Ssam } 502214734Srpaulo else { 503252726Srpaulo ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true); 504252726Srpaulo } 505252726Srpaulo} 506189251Ssam 507189251Ssam/** 508189251Ssam * Write a single object to the buffer 509252726Srpaulo * @param obj object to write 510252726Srpaulo * @param buf target buffer 511252726Srpaulo */ 512252726Srpaulostatic void 513252726Srpauloucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, 514252726Srpaulo unsigned int tabs, bool start_tabs, bool is_top) 515252726Srpaulo{ 516252726Srpaulo ucl_object_t *cur; 517252726Srpaulo ucl_hash_iter_t it = NULL; 518252726Srpaulo 519252726Srpaulo if (start_tabs) { 520252726Srpaulo ucl_add_tabs (func, tabs, is_top); 521252726Srpaulo } 522252726Srpaulo if (!is_top) { 523252726Srpaulo func->ucl_emitter_append_len ("{\n", 2, func->ud); 524252726Srpaulo } 525252726Srpaulo 526252726Srpaulo while ((cur = ucl_hash_iterate (obj->value.ov, &it))) { 527252726Srpaulo ucl_add_tabs (func, tabs + 1, is_top); 528252726Srpaulo if (cur->keylen > 0) { 529252726Srpaulo ucl_elt_string_write_json (cur->key, cur->keylen, func); 530252726Srpaulo } 531252726Srpaulo else { 532252726Srpaulo func->ucl_emitter_append_len ("null", 4, func->ud); 533252726Srpaulo } 534252726Srpaulo func->ucl_emitter_append_len (": ", 2, func->ud); 535252726Srpaulo ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false); 536252726Srpaulo if (ucl_hash_iter_has_next(it)) { 537252726Srpaulo if (!is_top) { 538252726Srpaulo func->ucl_emitter_append_len (",\n", 2, func->ud); 539252726Srpaulo } 540252726Srpaulo else { 541252726Srpaulo func->ucl_emitter_append_character ('\n', 1, func->ud); 542252726Srpaulo } 543252726Srpaulo } 544252726Srpaulo else { 545252726Srpaulo func->ucl_emitter_append_character ('\n', 1, func->ud); 546252726Srpaulo } 547189251Ssam } 548189251Ssam 549189251Ssam ucl_add_tabs (func, tabs, is_top); 550189251Ssam if (!is_top) { 551214734Srpaulo func->ucl_emitter_append_character ('}', 1, func->ud); 552252726Srpaulo } 553252726Srpaulo} 554252726Srpaulo 555252726Srpaulo/** 556252726Srpaulo * Write a single array to the buffer 557252726Srpaulo * @param obj array to write 558252726Srpaulo * @param buf target buffer 559252726Srpaulo */ 560252726Srpaulostatic void 561252726Srpauloucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, 562252726Srpaulo unsigned int tabs, bool start_tabs, bool is_top) 563252726Srpaulo{ 564189251Ssam ucl_object_t *cur = obj; 565189251Ssam 566189251Ssam if (start_tabs) { 567214734Srpaulo ucl_add_tabs (func, tabs, false); 568214734Srpaulo } 569214734Srpaulo 570214734Srpaulo func->ucl_emitter_append_len ("[\n", 2, func->ud); 571214734Srpaulo while (cur) { 572214734Srpaulo ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false); 573214734Srpaulo func->ucl_emitter_append_len (",\n", 2, func->ud); 574214734Srpaulo cur = cur->next; 575214734Srpaulo } 576214734Srpaulo ucl_add_tabs (func, tabs, false); 577214734Srpaulo func->ucl_emitter_append_character (']', 1, func->ud); 578214734Srpaulo} 579214734Srpaulo 580214734Srpaulo/** 581214734Srpaulo * Emit a single element 582214734Srpaulo * @param obj object 583214734Srpaulo * @param buf buffer 584214734Srpaulo */ 585214734Srpaulostatic void 586214734Srpauloucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func, 587214734Srpaulo unsigned int tabs, bool start_tabs, bool is_top, bool expand_array) 588214734Srpaulo{ 589214734Srpaulo bool flag; 590214734Srpaulo 591214734Srpaulo if (expand_array && obj->next != NULL ) { 592214734Srpaulo ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top); 593214734Srpaulo } 594214734Srpaulo else { 595214734Srpaulo switch (obj->type) { 596214734Srpaulo case UCL_INT: 597214734Srpaulo if (start_tabs) { 598214734Srpaulo ucl_add_tabs (func, tabs, false); 599214734Srpaulo } 600214734Srpaulo func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud); 601214734Srpaulo break; 602214734Srpaulo case UCL_FLOAT: 603214734Srpaulo case UCL_TIME: 604214734Srpaulo if (start_tabs) { 605214734Srpaulo ucl_add_tabs (func, tabs, false); 606214734Srpaulo } 607214734Srpaulo func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud); 608214734Srpaulo break; 609214734Srpaulo case UCL_BOOLEAN: 610214734Srpaulo if (start_tabs) { 611214734Srpaulo ucl_add_tabs (func, tabs, false); 612214734Srpaulo } 613214734Srpaulo flag = ucl_object_toboolean (obj); 614214734Srpaulo if (flag) { 615214734Srpaulo func->ucl_emitter_append_len ("true", 4, func->ud); 616214734Srpaulo } 617214734Srpaulo else { 618214734Srpaulo func->ucl_emitter_append_len ("false", 5, func->ud); 619214734Srpaulo } 620214734Srpaulo break; 621214734Srpaulo case UCL_STRING: 622214734Srpaulo if (start_tabs) { 623214734Srpaulo ucl_add_tabs (func, tabs, false); 624214734Srpaulo } 625214734Srpaulo ucl_elt_string_write_json (obj->value.sv, obj->len, func); 626214734Srpaulo break; 627214734Srpaulo case UCL_NULL: 628214734Srpaulo if (start_tabs) { 629214734Srpaulo ucl_add_tabs (func, tabs, false); 630214734Srpaulo } 631214734Srpaulo func->ucl_emitter_append_len ("null", 4, func->ud); 632214734Srpaulo break; 633214734Srpaulo case UCL_OBJECT: 634214734Srpaulo ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top); 635214734Srpaulo break; 636214734Srpaulo case UCL_ARRAY: 637252726Srpaulo ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top); 638252726Srpaulo break; 639252726Srpaulo case UCL_USERDATA: 640252726Srpaulo break; 641252726Srpaulo } 642252726Srpaulo } 643252726Srpaulo} 644252726Srpaulo 645252726Srpaulo/** 646252726Srpaulo * Emit an object to rcl 647252726Srpaulo * @param obj object 648252726Srpaulo * @return rcl output (should be freed after using) 649252726Srpaulo */ 650252726Srpaulostatic void 651252726Srpauloucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func) 652252726Srpaulo{ 653252726Srpaulo ucl_elt_write_yaml (obj, func, 0, false, true, true); 654252726Srpaulo} 655252726Srpaulo 656252726Srpaulo/* 657252726Srpaulo * Generic utstring output 658252726Srpaulo */ 659252726Srpaulostatic int 660252726Srpauloucl_utstring_append_character (unsigned char c, size_t len, void *ud) 661252726Srpaulo{ 662252726Srpaulo UT_string *buf = ud; 663252726Srpaulo 664252726Srpaulo if (len == 1) { 665252726Srpaulo utstring_append_c (buf, c); 666252726Srpaulo } 667252726Srpaulo else { 668252726Srpaulo utstring_reserve (buf, len); 669252726Srpaulo memset (&buf->d[buf->i], c, len); 670252726Srpaulo buf->i += len; 671252726Srpaulo buf->d[buf->i] = '\0'; 672252726Srpaulo } 673252726Srpaulo 674252726Srpaulo return 0; 675252726Srpaulo} 676252726Srpaulo 677252726Srpaulostatic int 678252726Srpauloucl_utstring_append_len (const unsigned char *str, size_t len, void *ud) 679252726Srpaulo{ 680252726Srpaulo UT_string *buf = ud; 681252726Srpaulo 682252726Srpaulo utstring_append_len (buf, str, len); 683252726Srpaulo 684252726Srpaulo return 0; 685252726Srpaulo} 686252726Srpaulo 687252726Srpaulostatic int 688252726Srpauloucl_utstring_append_int (int64_t val, void *ud) 689252726Srpaulo{ 690189251Ssam UT_string *buf = ud; 691189251Ssam 692189251Ssam utstring_printf (buf, "%jd", (intmax_t)val); 693189251Ssam return 0; 694189251Ssam} 695189251Ssam 696189251Ssamstatic int 697189251Ssamucl_utstring_append_double (double val, void *ud) 698189251Ssam{ 699189251Ssam UT_string *buf = ud; 700189251Ssam const double delta = 0.0000001; 701189251Ssam 702189251Ssam if (val == (double)(int)val) { 703189251Ssam utstring_printf (buf, "%.1lf", val); 704189251Ssam } 705252726Srpaulo else if (fabs (val - (double)(int)val) < delta) { 706252726Srpaulo /* Write at maximum precision */ 707252726Srpaulo utstring_printf (buf, "%.*lg", DBL_DIG, val); 708252726Srpaulo } 709189251Ssam else { 710209158Srpaulo utstring_printf (buf, "%lf", val); 711209158Srpaulo } 712209158Srpaulo 713209158Srpaulo return 0; 714214734Srpaulo} 715214734Srpaulo 716214734Srpaulo 717214734Srpaulounsigned char * 718214734Srpauloucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type) 719214734Srpaulo{ 720214734Srpaulo UT_string *buf = NULL; 721214734Srpaulo unsigned char *res = NULL; 722214734Srpaulo struct ucl_emitter_functions func = { 723214734Srpaulo .ucl_emitter_append_character = ucl_utstring_append_character, 724214734Srpaulo .ucl_emitter_append_len = ucl_utstring_append_len, 725214734Srpaulo .ucl_emitter_append_int = ucl_utstring_append_int, 726214734Srpaulo .ucl_emitter_append_double = ucl_utstring_append_double 727214734Srpaulo }; 728252726Srpaulo 729252726Srpaulo if (obj == NULL) { 730252726Srpaulo return NULL; 731252726Srpaulo } 732252726Srpaulo 733252726Srpaulo utstring_new (buf); 734252726Srpaulo func.ud = buf; 735252726Srpaulo 736252726Srpaulo if (buf != NULL) { 737252726Srpaulo if (emit_type == UCL_EMIT_JSON) { 738189251Ssam ucl_object_emit_json (obj, false, &func); 739189251Ssam } 740189251Ssam else if (emit_type == UCL_EMIT_JSON_COMPACT) { 741189251Ssam ucl_object_emit_json (obj, true, &func); 742189251Ssam } 743189251Ssam else if (emit_type == UCL_EMIT_YAML) { 744189251Ssam ucl_object_emit_yaml (obj, &func); 745189251Ssam } 746189251Ssam else { 747189251Ssam ucl_object_emit_config (obj, &func); 748189251Ssam } 749189251Ssam 750189251Ssam res = utstring_body (buf); 751189251Ssam free (buf); 752189251Ssam } 753189251Ssam 754189251Ssam return res; 755252726Srpaulo} 756189251Ssam 757252726Srpaulobool 758252726Srpauloucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type, 759252726Srpaulo struct ucl_emitter_functions *emitter) 760252726Srpaulo{ 761252726Srpaulo if (emit_type == UCL_EMIT_JSON) { 762189251Ssam ucl_object_emit_json (obj, false, emitter); 763189251Ssam } 764189251Ssam else if (emit_type == UCL_EMIT_JSON_COMPACT) { 765189251Ssam ucl_object_emit_json (obj, true, emitter); 766189251Ssam } 767189251Ssam else if (emit_type == UCL_EMIT_YAML) { 768214734Srpaulo ucl_object_emit_yaml (obj, emitter); 769189251Ssam } 770214734Srpaulo else { 771214734Srpaulo ucl_object_emit_config (obj, emitter); 772214734Srpaulo } 773189251Ssam 774214734Srpaulo /* XXX: need some error checks here */ 775189251Ssam return true; 776189251Ssam} 777189251Ssam 778214734Srpaulo 779252726Srpaulounsigned char * 780252726Srpauloucl_object_emit_single_json (ucl_object_t *obj) 781252726Srpaulo{ 782252726Srpaulo UT_string *buf = NULL; 783252726Srpaulo unsigned char *res = NULL; 784214734Srpaulo 785189251Ssam if (obj == NULL) { 786214734Srpaulo return NULL; 787189251Ssam } 788252726Srpaulo 789252726Srpaulo utstring_new (buf); 790189251Ssam 791189251Ssam if (buf != NULL) { 792189251Ssam switch (obj->type) { 793189251Ssam case UCL_OBJECT: 794189251Ssam ucl_utstring_append_len ("object", 6, buf); 795189251Ssam break; 796252726Srpaulo case UCL_ARRAY: 797252726Srpaulo ucl_utstring_append_len ("array", 5, buf); 798189251Ssam break; 799189251Ssam case UCL_INT: 800189251Ssam ucl_utstring_append_int (obj->value.iv, buf); 801189251Ssam break; 802189251Ssam case UCL_FLOAT: 803189251Ssam case UCL_TIME: 804189251Ssam ucl_utstring_append_double (obj->value.dv, buf); 805189251Ssam break; 806189251Ssam case UCL_NULL: 807189251Ssam ucl_utstring_append_len ("null", 4, buf); 808189251Ssam break; 809189251Ssam case UCL_BOOLEAN: 810214734Srpaulo if (obj->value.iv) { 811189251Ssam ucl_utstring_append_len ("true", 4, buf); 812252726Srpaulo } 813189251Ssam else { 814189251Ssam ucl_utstring_append_len ("false", 5, buf); 815189251Ssam } 816189251Ssam break; 817189251Ssam case UCL_STRING: 818214734Srpaulo ucl_utstring_append_len (obj->value.sv, obj->len, buf); 819189251Ssam break; 820189251Ssam case UCL_USERDATA: 821189251Ssam ucl_utstring_append_len ("userdata", 8, buf); 822189251Ssam break; 823189251Ssam } 824252726Srpaulo res = utstring_body (buf); 825214734Srpaulo free (buf); 826209158Srpaulo } 827252726Srpaulo 828189251Ssam return res; 829189251Ssam} 830189251Ssam