/* * Copyright 2007-2009, Lloyd Hilaiel. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. Neither the name of Lloyd Hilaiel nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "yajl_gen.h" #include "yajl_buf.h" #include "yajl_encode.h" #include #include #include typedef enum { yajl_gen_start, yajl_gen_map_start, yajl_gen_map_key, yajl_gen_map_val, yajl_gen_array_start, yajl_gen_in_array, yajl_gen_complete, yajl_gen_error } yajl_gen_state; struct yajl_gen_t { unsigned int depth; unsigned int pretty; const char * indentString; yajl_gen_state state[YAJL_MAX_DEPTH]; yajl_buf buf; unsigned char *pendingComment; unsigned int pendingLen; /* Length of pending comment */ int pendingCpp; /* NZ if comment is C++ style, Z if C */ /* memory allocation routines */ yajl_alloc_funcs alloc; }; yajl_gen yajl_gen_alloc(const yajl_gen_config * config, const yajl_alloc_funcs * afs) { yajl_gen g = NULL; yajl_alloc_funcs afsBuffer; /* first order of business is to set up memory allocation routines */ if (afs != NULL) { if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) { return NULL; } } else { yajl_set_default_alloc_funcs(&afsBuffer); afs = &afsBuffer; } g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); memset((void *) g, 0, sizeof(struct yajl_gen_t)); /* copy in pointers to allocation routines */ memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); if (config) { g->pretty = config->beautify; g->indentString = config->indentString ? config->indentString : " "; } g->buf = yajl_buf_alloc(&(g->alloc)); return g; } void yajl_gen_free(yajl_gen g) { yajl_buf_free(g->buf); YA_FREE(&(g->alloc), g); } #define INSERT_EOL \ if (g->pretty || g->pendingComment != NULL) \ yajl_insert_eol(g); #define INSERT_SEP \ if (g->state[g->depth] == yajl_gen_map_key || \ g->state[g->depth] == yajl_gen_in_array) { \ yajl_buf_append(g->buf, ",", 1); \ INSERT_EOL; \ } else if (g->state[g->depth] == yajl_gen_map_val) { \ yajl_buf_append(g->buf, ":", 1); \ if (g->pretty) yajl_buf_append(g->buf, " ", 1); \ } #define INSERT_WHITESPACE \ if (g->pretty) { \ if (g->state[g->depth] != yajl_gen_map_val) { \ unsigned int _i; \ for (_i=0;_idepth;_i++) \ yajl_buf_append(g->buf, g->indentString, \ strlen(g->indentString)); \ } \ } #define INSERT_SOME_WHITESPACE \ if (g->pretty) { \ if (g->state[g->depth] != yajl_gen_map_val) { \ yajl_buf_append(g->buf, g->indentString, \ strlen(g->indentString)); \ } \ } #define ENSURE_NOT_KEY \ if (g->state[g->depth] == yajl_gen_map_key) { \ return yajl_gen_keys_must_be_strings; \ } \ /* check that we're not complete, or in error state. in a valid state * to be generating */ #define ENSURE_VALID_STATE \ if (g->state[g->depth] == yajl_gen_error) { \ return yajl_gen_in_error_state;\ } else if (g->state[g->depth] == yajl_gen_complete) { \ return yajl_gen_generation_complete; \ } #define INCREMENT_DEPTH \ if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; #define APPENDED_ATOM \ switch (g->state[g->depth]) { \ case yajl_gen_start: \ g->state[g->depth] = yajl_gen_complete; \ break; \ case yajl_gen_map_start: \ case yajl_gen_map_key: \ g->state[g->depth] = yajl_gen_map_val; \ break; \ case yajl_gen_array_start: \ g->state[g->depth] = yajl_gen_in_array; \ break; \ case yajl_gen_map_val: \ g->state[g->depth] = yajl_gen_map_key; \ break; \ default: \ break; \ } \ #define FINAL_NEWLINE \ if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ INSERT_EOL /* Insert an end of line, and take care of any */ /* pending comments */ static void yajl_insert_eol(yajl_gen g) { if (g->pendingComment != NULL) { INSERT_SOME_WHITESPACE; if (g->pendingCpp) yajl_buf_append(g->buf, "//", 2); else yajl_buf_append(g->buf, "/*", 2); yajl_string_encode(g->buf, g->pendingComment, g->pendingLen); if (!g->pendingCpp) yajl_buf_append(g->buf, "*/", 2); free(g->pendingComment); g->pendingComment = NULL; g->pendingLen = 0; g->pendingCpp = 0; } yajl_buf_append(g->buf, "\n", 1); } /* Insert a comment at the end of the line. Append if there is already */ /* one pending. */ static void yajl_insert_pending_comment( yajl_gen g, const unsigned char * str, unsigned int len, int cpp) { if (g->pendingComment != NULL) { unsigned int tlen = g->pendingLen + 0 + len; unsigned char *pendingComment; pendingComment = (unsigned char *) realloc(g->pendingComment, sizeof(char) * tlen); memcpy(pendingComment + g->pendingLen + 0, str, len); g->pendingComment = pendingComment; g->pendingLen = tlen; } else { g->pendingComment = (unsigned char *) malloc(sizeof(char) * len); memcpy(g->pendingComment, str, len); g->pendingLen = len; } g->pendingCpp = cpp; } yajl_gen_status yajl_gen_integer(yajl_gen g, long int number) { char i[32]; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%ld", number); yajl_buf_append(g->buf, i, strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_double(yajl_gen g, double number) { char i[32]; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%g", number); yajl_buf_append(g->buf, i, strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_number(yajl_gen g, const char * s, unsigned int l) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; yajl_buf_append(g->buf, s, l); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_string(yajl_gen g, const unsigned char * str, unsigned int len) { ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; yajl_buf_append(g->buf, "\"", 1); yajl_string_encode(g->buf, str, len); yajl_buf_append(g->buf, "\"", 1); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_null(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; yajl_buf_append(g->buf, "null", strlen("null")); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_bool(yajl_gen g, int boolean) { const char * val = boolean ? "true" : "false"; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; yajl_buf_append(g->buf, val, strlen(val)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_map_open(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_map_start; yajl_buf_append(g->buf, "{", 1); INSERT_EOL; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_map_close(yajl_gen g) { ENSURE_VALID_STATE; (g->depth)--; INSERT_EOL; APPENDED_ATOM; INSERT_WHITESPACE; yajl_buf_append(g->buf, "}", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_array_open(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_array_start; yajl_buf_append(g->buf, "[", 1); INSERT_EOL; FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_array_close(yajl_gen g) { ENSURE_VALID_STATE; INSERT_EOL; (g->depth)--; APPENDED_ATOM; INSERT_WHITESPACE; yajl_buf_append(g->buf, "]", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_c_comment(yajl_gen g, const unsigned char * str, unsigned int len, int dlytoeol) { ENSURE_VALID_STATE; if (dlytoeol) { yajl_insert_pending_comment(g, str, len, 0); } else { if (g->pretty) yajl_buf_append(g->buf, " /*", 3); else yajl_buf_append(g->buf, "/*", 2); yajl_string_encode(g->buf, str, len); if (g->pretty) yajl_buf_append(g->buf, "*/ ", 3); else yajl_buf_append(g->buf, "*/", 2); } FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_cpp_comment(yajl_gen g, const unsigned char * str, unsigned int len) { ENSURE_VALID_STATE; yajl_insert_pending_comment(g, str, len, 1); FINAL_NEWLINE; return yajl_gen_status_ok; } yajl_gen_status yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, unsigned int * len) { *buf = yajl_buf_data(g->buf); *len = yajl_buf_len(g->buf); return yajl_gen_status_ok; } void yajl_gen_clear(yajl_gen g) { yajl_buf_clear(g->buf); }