1 /*
2  * Copyright 2010, Lloyd Hilaiel.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  1. Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  *
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in
13  *     the documentation and/or other materials provided with the
14  *     distribution.
15  *
16  *  3. Neither the name of Lloyd Hilaiel nor the names of its
17  *     contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "yajl_gen.h"
34 #include "yajl_buf.h"
35 #include "yajl_encode.h"
36 
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <math.h>
41 
42 typedef enum {
43     yajl_gen_start,
44     yajl_gen_map_start,
45     yajl_gen_map_key,
46     yajl_gen_map_val,
47     yajl_gen_array_start,
48     yajl_gen_in_array,
49     yajl_gen_complete,
50     yajl_gen_error
51 } yajl_gen_state;
52 
53 struct yajl_gen_t
54 {
55     unsigned int depth;
56     unsigned int pretty;
57     const char * indentString;
58     yajl_gen_state state[YAJL_MAX_DEPTH];
59     yajl_print_t print;
60     void * ctx; /* yajl_buf */
61     /* memory allocation routines */
62     yajl_alloc_funcs alloc;
63 };
64 
65 yajl_gen
yajl_gen_alloc(const yajl_gen_config * config, const yajl_alloc_funcs * afs)66 yajl_gen_alloc(const yajl_gen_config * config,
67                const yajl_alloc_funcs * afs)
68 {
69     return yajl_gen_alloc2(NULL, config, afs, NULL);
70 }
71 
72 yajl_gen
yajl_gen_alloc2(const yajl_print_t callback, const yajl_gen_config * config, const yajl_alloc_funcs * afs, void * ctx)73 yajl_gen_alloc2(const yajl_print_t callback,
74                 const yajl_gen_config * config,
75                 const yajl_alloc_funcs * afs,
76                 void * ctx)
77 {
78     yajl_gen g = NULL;
79     yajl_alloc_funcs afsBuffer;
80 
81     /* first order of business is to set up memory allocation routines */
82     if (afs != NULL) {
83         if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
84         {
85             return NULL;
86         }
87     } else {
88         yajl_set_default_alloc_funcs(&afsBuffer);
89         afs = &afsBuffer;
90     }
91 
92     g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
93     memset((void *) g, 0, sizeof(struct yajl_gen_t));
94     /* copy in pointers to allocation routines */
95     memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
96 
97     if (config) {
98         g->pretty = config->beautify;
99         g->indentString = config->indentString ? config->indentString : "  ";
100     }
101 
102     if (callback) {
103         g->print = callback;
104         g->ctx = ctx;
105     } else {
106         g->print = (yajl_print_t)&yajl_buf_append;
107         g->ctx = yajl_buf_alloc(&(g->alloc));
108     }
109 
110     return g;
111 }
112 
113 void
yajl_gen_free(yajl_gen g)114 yajl_gen_free(yajl_gen g)
115 {
116     if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx);
117     YA_FREE(&(g->alloc), g);
118 }
119 
120 #define INSERT_SEP \
121     if (g->state[g->depth] == yajl_gen_map_key ||               \
122         g->state[g->depth] == yajl_gen_in_array) {              \
123         g->print(g->ctx, ",", 1);                               \
124         if (g->pretty) g->print(g->ctx, "\n", 1);               \
125     } else if (g->state[g->depth] == yajl_gen_map_val) {        \
126         g->print(g->ctx, ":", 1);                               \
127         if (g->pretty) g->print(g->ctx, " ", 1);                \
128    }
129 
130 #define INSERT_WHITESPACE                                               \
131     if (g->pretty) {                                                    \
132         if (g->state[g->depth] != yajl_gen_map_val) {                   \
133             unsigned int _i;                                            \
134             for (_i=0;_i<g->depth;_i++)                                 \
135                 g->print(g->ctx, g->indentString,                       \
136                          strlen(g->indentString));                      \
137         }                                                               \
138     }
139 
140 #define ENSURE_NOT_KEY \
141     if (g->state[g->depth] == yajl_gen_map_key) {   \
142         return yajl_gen_keys_must_be_strings;       \
143     }                                               \
144 
145 /* check that we're not complete, or in error state.  in a valid state
146  * to be generating */
147 #define ENSURE_VALID_STATE \
148     if (g->state[g->depth] == yajl_gen_error) {   \
149         return yajl_gen_in_error_state;\
150     } else if (g->state[g->depth] == yajl_gen_complete) {   \
151         return yajl_gen_generation_complete;                \
152     }
153 
154 #define INCREMENT_DEPTH \
155     if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
156 
157 #define APPENDED_ATOM \
158     switch (g->state[g->depth]) {                   \
159         case yajl_gen_start:                        \
160             g->state[g->depth] = yajl_gen_complete; \
161             break;                                  \
162         case yajl_gen_map_start:                    \
163         case yajl_gen_map_key:                      \
164             g->state[g->depth] = yajl_gen_map_val;  \
165             break;                                  \
166         case yajl_gen_array_start:                  \
167             g->state[g->depth] = yajl_gen_in_array; \
168             break;                                  \
169         case yajl_gen_map_val:                      \
170             g->state[g->depth] = yajl_gen_map_key;  \
171             break;                                  \
172         default:                                    \
173             break;                                  \
174     }                                               \
175 
176 #define FINAL_NEWLINE                                        \
177     if (g->pretty && g->state[g->depth] == yajl_gen_complete) \
178         g->print(g->ctx, "\n", 1);
179 
180 yajl_gen_status
yajl_gen_integer(yajl_gen g, long int number)181 yajl_gen_integer(yajl_gen g, long int number)
182 {
183     char i[32];
184     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
185     sprintf(i, "%ld", number);
186     g->print(g->ctx, i, strlen(i));
187     APPENDED_ATOM;
188     FINAL_NEWLINE;
189     return yajl_gen_status_ok;
190 }
191 
192 #ifdef WIN32
193 #include <float.h>
194 #define isnan _isnan
195 #define isinf !_finite
196 #endif
197 
198 yajl_gen_status
yajl_gen_double(yajl_gen g, double number)199 yajl_gen_double(yajl_gen g, double number)
200 {
201     char i[32];
202     ENSURE_VALID_STATE; ENSURE_NOT_KEY;
203     if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
204     INSERT_SEP; INSERT_WHITESPACE;
205     sprintf(i, "%g", number);
206     g->print(g->ctx, i, strlen(i));
207     APPENDED_ATOM;
208     FINAL_NEWLINE;
209     return yajl_gen_status_ok;
210 }
211 
212 yajl_gen_status
yajl_gen_number(yajl_gen g, const char * s, unsigned int l)213 yajl_gen_number(yajl_gen g, const char * s, unsigned int l)
214 {
215     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
216     g->print(g->ctx, s, l);
217     APPENDED_ATOM;
218     FINAL_NEWLINE;
219     return yajl_gen_status_ok;
220 }
221 
222 yajl_gen_status
yajl_gen_string(yajl_gen g, const unsigned char * str, unsigned int len)223 yajl_gen_string(yajl_gen g, const unsigned char * str,
224                 unsigned int len)
225 {
226     ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
227     g->print(g->ctx, "\"", 1);
228     yajl_string_encode2(g->print, g->ctx, str, len);
229     g->print(g->ctx, "\"", 1);
230     APPENDED_ATOM;
231     FINAL_NEWLINE;
232     return yajl_gen_status_ok;
233 }
234 
235 yajl_gen_status
yajl_gen_null(yajl_gen g)236 yajl_gen_null(yajl_gen g)
237 {
238     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
239     g->print(g->ctx, "null", strlen("null"));
240     APPENDED_ATOM;
241     FINAL_NEWLINE;
242     return yajl_gen_status_ok;
243 }
244 
245 yajl_gen_status
yajl_gen_bool(yajl_gen g, int boolean)246 yajl_gen_bool(yajl_gen g, int boolean)
247 {
248     const char * val = boolean ? "true" : "false";
249 
250 	ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
251     g->print(g->ctx, val, strlen(val));
252     APPENDED_ATOM;
253     FINAL_NEWLINE;
254     return yajl_gen_status_ok;
255 }
256 
257 yajl_gen_status
yajl_gen_map_open(yajl_gen g)258 yajl_gen_map_open(yajl_gen g)
259 {
260     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
261     INCREMENT_DEPTH;
262 
263     g->state[g->depth] = yajl_gen_map_start;
264     g->print(g->ctx, "{", 1);
265     if (g->pretty) g->print(g->ctx, "\n", 1);
266     FINAL_NEWLINE;
267     return yajl_gen_status_ok;
268 }
269 
270 yajl_gen_status
yajl_gen_map_close(yajl_gen g)271 yajl_gen_map_close(yajl_gen g)
272 {
273     ENSURE_VALID_STATE;
274     (g->depth)--;
275     if (g->pretty) g->print(g->ctx, "\n", 1);
276     APPENDED_ATOM;
277     INSERT_WHITESPACE;
278     g->print(g->ctx, "}", 1);
279     FINAL_NEWLINE;
280     return yajl_gen_status_ok;
281 }
282 
283 yajl_gen_status
yajl_gen_array_open(yajl_gen g)284 yajl_gen_array_open(yajl_gen g)
285 {
286     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
287     INCREMENT_DEPTH;
288     g->state[g->depth] = yajl_gen_array_start;
289     g->print(g->ctx, "[", 1);
290     if (g->pretty) g->print(g->ctx, "\n", 1);
291     FINAL_NEWLINE;
292     return yajl_gen_status_ok;
293 }
294 
295 yajl_gen_status
yajl_gen_array_close(yajl_gen g)296 yajl_gen_array_close(yajl_gen g)
297 {
298     ENSURE_VALID_STATE;
299     if (g->pretty) g->print(g->ctx, "\n", 1);
300     (g->depth)--;
301     APPENDED_ATOM;
302     INSERT_WHITESPACE;
303     g->print(g->ctx, "]", 1);
304     FINAL_NEWLINE;
305     return yajl_gen_status_ok;
306 }
307 
308 yajl_gen_status
yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, unsigned int * len)309 yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
310                  unsigned int * len)
311 {
312     if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
313     *buf = yajl_buf_data((yajl_buf)g->ctx);
314     *len = yajl_buf_len((yajl_buf)g->ctx);
315     return yajl_gen_status_ok;
316 }
317 
318 void
yajl_gen_clear(yajl_gen g)319 yajl_gen_clear(yajl_gen g)
320 {
321     if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx);
322 }
323