1/* 2 * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> 3 * 4 * Jansson is free software; you can redistribute it and/or modify 5 * it under the terms of the MIT license. See LICENSE for details. 6 */ 7 8#ifndef _GNU_SOURCE 9#define _GNU_SOURCE 10#endif 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <assert.h> 16 17#include "jansson.h" 18#include "jansson_private.h" 19#include "strbuffer.h" 20#include "utf.h" 21 22#define MAX_INTEGER_STR_LENGTH 100 23#define MAX_REAL_STR_LENGTH 100 24 25#define FLAGS_TO_INDENT(f) ((f) & 0x1F) 26#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) 27 28struct object_key { 29 size_t serial; 30 const char *key; 31}; 32 33static int dump_to_strbuffer(const char *buffer, size_t size, void *data) 34{ 35 return strbuffer_append_bytes((strbuffer_t *)data, buffer, size); 36} 37 38static int dump_to_file(const char *buffer, size_t size, void *data) 39{ 40 FILE *dest = (FILE *)data; 41 if(fwrite(buffer, size, 1, dest) != 1) 42 return -1; 43 return 0; 44} 45 46/* 32 spaces (the maximum indentation size) */ 47static const char whitespace[] = " "; 48 49static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) 50{ 51 if(FLAGS_TO_INDENT(flags) > 0) 52 { 53 int i, ws_count = FLAGS_TO_INDENT(flags); 54 55 if(dump("\n", 1, data)) 56 return -1; 57 58 for(i = 0; i < depth; i++) 59 { 60 if(dump(whitespace, ws_count, data)) 61 return -1; 62 } 63 } 64 else if(space && !(flags & JSON_COMPACT)) 65 { 66 return dump(" ", 1, data); 67 } 68 return 0; 69} 70 71static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags) 72{ 73 const char *pos, *end, *lim; 74 int32_t codepoint; 75 76 if(dump("\"", 1, data)) 77 return -1; 78 79 end = pos = str; 80 lim = str + len; 81 while(1) 82 { 83 const char *text; 84 char seq[13]; 85 int length; 86 87 while(end < lim) 88 { 89 end = utf8_iterate(pos, lim - pos, &codepoint); 90 if(!end) 91 return -1; 92 93 /* mandatory escape or control char */ 94 if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20) 95 break; 96 97 /* slash */ 98 if((flags & JSON_ESCAPE_SLASH) && codepoint == '/') 99 break; 100 101 /* non-ASCII */ 102 if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F) 103 break; 104 105 pos = end; 106 } 107 108 if(pos != str) { 109 if(dump(str, pos - str, data)) 110 return -1; 111 } 112 113 if(end == pos) 114 break; 115 116 /* handle \, /, ", and control codes */ 117 length = 2; 118 switch(codepoint) 119 { 120 case '\\': text = "\\\\"; break; 121 case '\"': text = "\\\""; break; 122 case '\b': text = "\\b"; break; 123 case '\f': text = "\\f"; break; 124 case '\n': text = "\\n"; break; 125 case '\r': text = "\\r"; break; 126 case '\t': text = "\\t"; break; 127 case '/': text = "\\/"; break; 128 default: 129 { 130 /* codepoint is in BMP */ 131 if(codepoint < 0x10000) 132 { 133 sprintf(seq, "\\u%04X", codepoint); 134 length = 6; 135 } 136 137 /* not in BMP -> construct a UTF-16 surrogate pair */ 138 else 139 { 140 int32_t first, last; 141 142 codepoint -= 0x10000; 143 first = 0xD800 | ((codepoint & 0xffc00) >> 10); 144 last = 0xDC00 | (codepoint & 0x003ff); 145 146 sprintf(seq, "\\u%04X\\u%04X", first, last); 147 length = 12; 148 } 149 150 text = seq; 151 break; 152 } 153 } 154 155 if(dump(text, length, data)) 156 return -1; 157 158 str = pos = end; 159 } 160 161 return dump("\"", 1, data); 162} 163 164static int object_key_compare_keys(const void *key1, const void *key2) 165{ 166 return strcmp(((const struct object_key *)key1)->key, 167 ((const struct object_key *)key2)->key); 168} 169 170static int object_key_compare_serials(const void *key1, const void *key2) 171{ 172 size_t a = ((const struct object_key *)key1)->serial; 173 size_t b = ((const struct object_key *)key2)->serial; 174 175 return a < b ? -1 : a == b ? 0 : 1; 176} 177 178static int dump_integer(const json_t *json, json_dump_callback_t dump, const char *fmt, 179 void *data) { 180 char buffer[MAX_INTEGER_STR_LENGTH]; 181 int size; 182 183 size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, 184 fmt, json_integer_value(json)); 185 if(size < 0 || size >= MAX_INTEGER_STR_LENGTH) 186 return -1; 187 188 return dump(buffer, size, data); 189} 190 191static int do_dump(const json_t *json, size_t flags, int depth, 192 json_dump_callback_t dump, void *data) 193{ 194 if(!json) 195 return -1; 196 197 switch(json_typeof(json)) { 198 case JSON_NULL: 199 return dump("null", 4, data); 200 201 case JSON_TRUE: 202 return dump("true", 4, data); 203 204 case JSON_FALSE: 205 return dump("false", 5, data); 206 207 case JSON_INTEGER: 208 return dump_integer(json, dump, "%" JSON_INTEGER_FORMAT, data); 209 210 case JSON_NATURAL: 211 return dump_integer(json, dump, "%" JSON_NATURAL_FORMAT, data); 212 213 case JSON_REAL: 214 { 215 char buffer[MAX_REAL_STR_LENGTH]; 216 int size; 217 double value = json_real_value(json); 218 219 size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, 220 FLAGS_TO_PRECISION(flags)); 221 if(size < 0) 222 return -1; 223 224 return dump(buffer, size, data); 225 } 226 227 case JSON_STRING: 228 return dump_string(json_string_value(json), json_string_length(json), dump, data, flags); 229 230 case JSON_ARRAY: 231 { 232 int i; 233 int n; 234 json_array_t *array; 235 236 /* detect circular references */ 237 array = json_to_array(json); 238 if(array->visited) 239 goto array_error; 240 array->visited = 1; 241 242 n = json_array_size(json); 243 244 if(dump("[", 1, data)) 245 goto array_error; 246 if(n == 0) { 247 array->visited = 0; 248 return dump("]", 1, data); 249 } 250 if(dump_indent(flags, depth + 1, 0, dump, data)) 251 goto array_error; 252 253 for(i = 0; i < n; ++i) { 254 if(do_dump(json_array_get(json, i), flags, depth + 1, 255 dump, data)) 256 goto array_error; 257 258 if(i < n - 1) 259 { 260 if(dump(",", 1, data) || 261 dump_indent(flags, depth + 1, 1, dump, data)) 262 goto array_error; 263 } 264 else 265 { 266 if(dump_indent(flags, depth, 0, dump, data)) 267 goto array_error; 268 } 269 } 270 271 array->visited = 0; 272 return dump("]", 1, data); 273 274 array_error: 275 array->visited = 0; 276 return -1; 277 } 278 279 case JSON_OBJECT: 280 { 281 json_object_t *object; 282 void *iter; 283 const char *separator; 284 int separator_length; 285 286 if(flags & JSON_COMPACT) { 287 separator = ":"; 288 separator_length = 1; 289 } 290 else { 291 separator = ": "; 292 separator_length = 2; 293 } 294 295 /* detect circular references */ 296 object = json_to_object(json); 297 if(object->visited) 298 goto object_error; 299 object->visited = 1; 300 301 iter = json_object_iter((json_t *)json); 302 303 if(dump("{", 1, data)) 304 goto object_error; 305 if(!iter) { 306 object->visited = 0; 307 return dump("}", 1, data); 308 } 309 if(dump_indent(flags, depth + 1, 0, dump, data)) 310 goto object_error; 311 312 if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER) 313 { 314 struct object_key *keys; 315 size_t size, i; 316 int (*cmp_func)(const void *, const void *); 317 318 size = json_object_size(json); 319 keys = jsonp_malloc(size * sizeof(struct object_key)); 320 if(!keys) 321 goto object_error; 322 323 i = 0; 324 while(iter) 325 { 326 keys[i].serial = hashtable_iter_serial(iter); 327 keys[i].key = json_object_iter_key(iter); 328 iter = json_object_iter_next((json_t *)json, iter); 329 i++; 330 } 331 assert(i == size); 332 333 if(flags & JSON_SORT_KEYS) 334 cmp_func = object_key_compare_keys; 335 else 336 cmp_func = object_key_compare_serials; 337 338 qsort(keys, size, sizeof(struct object_key), cmp_func); 339 340 for(i = 0; i < size; i++) 341 { 342 const char *key; 343 json_t *value; 344 345 key = keys[i].key; 346 value = json_object_get(json, key); 347 assert(value); 348 349 dump_string(key, strlen(key), dump, data, flags); 350 if(dump(separator, separator_length, data) || 351 do_dump(value, flags, depth + 1, dump, data)) 352 { 353 jsonp_free(keys); 354 goto object_error; 355 } 356 357 if(i < size - 1) 358 { 359 if(dump(",", 1, data) || 360 dump_indent(flags, depth + 1, 1, dump, data)) 361 { 362 jsonp_free(keys); 363 goto object_error; 364 } 365 } 366 else 367 { 368 if(dump_indent(flags, depth, 0, dump, data)) 369 { 370 jsonp_free(keys); 371 goto object_error; 372 } 373 } 374 } 375 376 jsonp_free(keys); 377 } 378 else 379 { 380 /* Don't sort keys */ 381 382 while(iter) 383 { 384 void *next = json_object_iter_next((json_t *)json, iter); 385 const char *key = json_object_iter_key(iter); 386 387 dump_string(key, strlen(key), dump, data, flags); 388 if(dump(separator, separator_length, data) || 389 do_dump(json_object_iter_value(iter), flags, depth + 1, 390 dump, data)) 391 goto object_error; 392 393 if(next) 394 { 395 if(dump(",", 1, data) || 396 dump_indent(flags, depth + 1, 1, dump, data)) 397 goto object_error; 398 } 399 else 400 { 401 if(dump_indent(flags, depth, 0, dump, data)) 402 goto object_error; 403 } 404 405 iter = next; 406 } 407 } 408 409 object->visited = 0; 410 return dump("}", 1, data); 411 412 object_error: 413 object->visited = 0; 414 return -1; 415 } 416 417 default: 418 /* not reached */ 419 return -1; 420 } 421} 422 423char *json_dumps(const json_t *json, size_t flags) 424{ 425 strbuffer_t strbuff; 426 char *result; 427 428 if(strbuffer_init(&strbuff)) 429 return NULL; 430 431 if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags)) 432 result = NULL; 433 else 434 result = jsonp_strdup(strbuffer_value(&strbuff)); 435 436 strbuffer_close(&strbuff); 437 return result; 438} 439 440int json_dumpf(const json_t *json, FILE *output, size_t flags) 441{ 442 return json_dump_callback(json, dump_to_file, (void *)output, flags); 443} 444 445int json_dump_file(const json_t *json, const char *path, size_t flags) 446{ 447 int result; 448 449 FILE *output = fopen(path, "w"); 450 if(!output) 451 return -1; 452 453 result = json_dumpf(json, output, flags); 454 455 fclose(output); 456 return result; 457} 458 459int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) 460{ 461 if(!(flags & JSON_ENCODE_ANY)) { 462 if(!json_is_array(json) && !json_is_object(json)) 463 return -1; 464 } 465 466 return do_dump(json, flags, 0, callback, data); 467} 468