| @@ -1,521 +1,521 @@ | | | @@ -1,521 +1,521 @@ |
1 | /*- | | 1 | /*- |
2 | * Copyright (c) 2010 Alistair Crooks <agc@NetBSD.org> | | 2 | * Copyright (c) 2010 Alistair Crooks <agc@NetBSD.org> |
3 | * All rights reserved. | | 3 | * All rights reserved. |
4 | * | | 4 | * |
5 | * Redistribution and use in source and binary forms, with or without | | 5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | | 6 | * modification, are permitted provided that the following conditions |
7 | * are met: | | 7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright | | 8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. | | 9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright | | 10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the | | 11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. | | 12 | * documentation and/or other materials provided with the distribution. |
13 | * | | 13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | | 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | | 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ | | 24 | */ |
25 | #include <sys/types.h> | | 25 | #include <sys/types.h> |
26 | | | 26 | |
27 | #include <inttypes.h> | | 27 | #include <inttypes.h> |
28 | #include <regex.h> | | 28 | #include <regex.h> |
29 | #include <stdarg.h> | | 29 | #include <stdarg.h> |
30 | #include <stdio.h> | | 30 | #include <stdio.h> |
31 | #include <stdlib.h> | | 31 | #include <stdlib.h> |
32 | #include <string.h> | | 32 | #include <string.h> |
33 | #include <unistd.h> | | 33 | #include <unistd.h> |
34 | | | 34 | |
35 | #include "mj.h" | | 35 | #include "mj.h" |
36 | #include "defs.h" | | 36 | #include "defs.h" |
37 | | | 37 | |
38 | /* save 'n' chars of 's' in malloc'd memory */ | | 38 | /* save 'n' chars of 's' in malloc'd memory */ |
39 | static char * | | 39 | static char * |
40 | strnsave(const char *s, int n, unsigned esc) | | 40 | strnsave(const char *s, int n, unsigned esc) |
41 | { | | 41 | { |
42 | char *newc; | | 42 | char *newc; |
43 | char *cp; | | 43 | char *cp; |
44 | int i; | | 44 | int i; |
45 | | | 45 | |
46 | if (n < 0) { | | 46 | if (n < 0) { |
47 | n = strlen(s); | | 47 | n = (int)strlen(s); |
48 | } | | 48 | } |
49 | NEWARRAY(char, cp, (n * 2) + 1, "strnsave", return NULL); | | 49 | NEWARRAY(char, cp, (n * 2) + 1, "strnsave", return NULL); |
50 | if (esc) { | | 50 | if (esc) { |
51 | newc = cp; | | 51 | newc = cp; |
52 | for (i = 0 ; i < n ; i++) { | | 52 | for (i = 0 ; i < n ; i++) { |
53 | if (*s == '\\') { | | 53 | if (*s == '\\') { |
54 | *newc++ = *s++; | | 54 | *newc++ = *s++; |
55 | } else if (*s == '"') { | | 55 | } else if (*s == '"') { |
56 | *newc++ = '\\'; | | 56 | *newc++ = '\\'; |
57 | } | | 57 | } |
58 | *newc++ = *s++; | | 58 | *newc++ = *s++; |
59 | } | | 59 | } |
60 | *newc = 0x0; | | 60 | *newc = 0x0; |
61 | } else { | | 61 | } else { |
62 | (void) memcpy(cp, s, (unsigned)n); | | 62 | (void) memcpy(cp, s, (unsigned)n); |
63 | cp[n] = 0x0; | | 63 | cp[n] = 0x0; |
64 | } | | 64 | } |
65 | return cp; | | 65 | return cp; |
66 | } | | 66 | } |
67 | | | 67 | |
68 | /* look in an object for the item */ | | 68 | /* look in an object for the item */ |
69 | static int | | 69 | static int |
70 | findentry(mj_t *atom, const char *name, const unsigned from, const unsigned incr) | | 70 | findentry(mj_t *atom, const char *name, const unsigned from, const unsigned incr) |
71 | { | | 71 | { |
72 | unsigned i; | | 72 | unsigned i; |
73 | | | 73 | |
74 | for (i = from ; i < atom->c ; i += incr) { | | 74 | for (i = from ; i < atom->c ; i += incr) { |
75 | if (strcmp(name, atom->value.v[i].value.s) == 0) { | | 75 | if (strcmp(name, atom->value.v[i].value.s) == 0) { |
76 | return i; | | 76 | return i; |
77 | } | | 77 | } |
78 | } | | 78 | } |
79 | return -1; | | 79 | return -1; |
80 | } | | 80 | } |
81 | | | 81 | |
82 | /* create a real number */ | | 82 | /* create a real number */ |
83 | static void | | 83 | static void |
84 | create_number(mj_t *atom, double d) | | 84 | create_number(mj_t *atom, double d) |
85 | { | | 85 | { |
86 | char number[128]; | | 86 | char number[128]; |
87 | | | 87 | |
88 | atom->type = MJ_NUMBER; | | 88 | atom->type = MJ_NUMBER; |
89 | atom->c = snprintf(number, sizeof(number), "%g", d); | | 89 | atom->c = snprintf(number, sizeof(number), "%g", d); |
90 | atom->value.s = strnsave(number, (int)atom->c, 0); | | 90 | atom->value.s = strnsave(number, (int)atom->c, 0); |
91 | } | | 91 | } |
92 | | | 92 | |
93 | /* create an integer */ | | 93 | /* create an integer */ |
94 | static void | | 94 | static void |
95 | create_integer(mj_t *atom, int64_t i) | | 95 | create_integer(mj_t *atom, int64_t i) |
96 | { | | 96 | { |
97 | char number[128]; | | 97 | char number[128]; |
98 | | | 98 | |
99 | atom->type = MJ_NUMBER; | | 99 | atom->type = MJ_NUMBER; |
100 | atom->c = snprintf(number, sizeof(number), "%" PRIi64, i); | | 100 | atom->c = snprintf(number, sizeof(number), "%" PRIi64, i); |
101 | atom->value.s = strnsave(number, (int)atom->c, 0); | | 101 | atom->value.s = strnsave(number, (int)atom->c, 0); |
102 | } | | 102 | } |
103 | | | 103 | |
104 | /* create a string */ | | 104 | /* create a string */ |
105 | static void | | 105 | static void |
106 | create_string(mj_t *atom, const char *s) | | 106 | create_string(mj_t *atom, const char *s) |
107 | { | | 107 | { |
108 | atom->type = MJ_STRING; | | 108 | atom->type = MJ_STRING; |
109 | atom->value.s = strnsave(s, -1, 1); | | 109 | atom->value.s = strnsave(s, -1, 1); |
110 | atom->c = strlen(atom->value.s); | | 110 | atom->c = (unsigned)strlen(atom->value.s); |
111 | } | | 111 | } |
112 | | | 112 | |
113 | #define MJ_OPEN_BRACKET (MJ_OBJECT + 1) /* 8 */ | | 113 | #define MJ_OPEN_BRACKET (MJ_OBJECT + 1) /* 8 */ |
114 | #define MJ_CLOSE_BRACKET (MJ_OPEN_BRACKET + 1) /* 9 */ | | 114 | #define MJ_CLOSE_BRACKET (MJ_OPEN_BRACKET + 1) /* 9 */ |
115 | #define MJ_OPEN_BRACE (MJ_CLOSE_BRACKET + 1) /* 10 */ | | 115 | #define MJ_OPEN_BRACE (MJ_CLOSE_BRACKET + 1) /* 10 */ |
116 | #define MJ_CLOSE_BRACE (MJ_OPEN_BRACE + 1) /* 11 */ | | 116 | #define MJ_CLOSE_BRACE (MJ_OPEN_BRACE + 1) /* 11 */ |
117 | #define MJ_COLON (MJ_CLOSE_BRACE + 1) /* 12 */ | | 117 | #define MJ_COLON (MJ_CLOSE_BRACE + 1) /* 12 */ |
118 | #define MJ_COMMA (MJ_COLON + 1) /* 13 */ | | 118 | #define MJ_COMMA (MJ_COLON + 1) /* 13 */ |
119 | | | 119 | |
120 | /* return the token type, and start and finish locations in string */ | | 120 | /* return the token type, and start and finish locations in string */ |
121 | static int | | 121 | static int |
122 | gettok(const char *s, int *from, int *to, int *tok) | | 122 | gettok(const char *s, int *from, int *to, int *tok) |
123 | { | | 123 | { |
124 | static regex_t tokregex; | | 124 | static regex_t tokregex; |
125 | regmatch_t matches[15]; | | 125 | regmatch_t matches[15]; |
126 | static int compiled; | | 126 | static int compiled; |
127 | | | 127 | |
128 | if (!compiled) { | | 128 | if (!compiled) { |
129 | compiled = 1; | | 129 | compiled = 1; |
130 | (void) regcomp(&tokregex, | | 130 | (void) regcomp(&tokregex, |
131 | "[ \t\r\n]*(([+-]?[0-9]{1,21}(\\.[0-9]*)?([eE][-+][0-9]+)?)|" | | 131 | "[ \t\r\n]*(([+-]?[0-9]{1,21}(\\.[0-9]*)?([eE][-+][0-9]+)?)|" |
132 | "(\"([^\"]|\\\\.)*\")|(null)|(false)|(true)|([][{}:,]))", | | 132 | "(\"([^\"]|\\\\.)*\")|(null)|(false)|(true)|([][{}:,]))", |
133 | REG_EXTENDED); | | 133 | REG_EXTENDED); |
134 | } | | 134 | } |
135 | if (regexec(&tokregex, &s[*from = *to], 15, matches, 0) != 0) { | | 135 | if (regexec(&tokregex, &s[*from = *to], 15, matches, 0) != 0) { |
136 | return *tok = -1; | | 136 | return *tok = -1; |
137 | } | | 137 | } |
138 | *to = *from + (int)(matches[1].rm_eo); | | 138 | *to = *from + (int)(matches[1].rm_eo); |
139 | *tok = (matches[2].rm_so >= 0) ? MJ_NUMBER : | | 139 | *tok = (matches[2].rm_so >= 0) ? MJ_NUMBER : |
140 | (matches[5].rm_so >= 0) ? MJ_STRING : | | 140 | (matches[5].rm_so >= 0) ? MJ_STRING : |
141 | (matches[7].rm_so >= 0) ? MJ_NULL : | | 141 | (matches[7].rm_so >= 0) ? MJ_NULL : |
142 | (matches[8].rm_so >= 0) ? MJ_FALSE : | | 142 | (matches[8].rm_so >= 0) ? MJ_FALSE : |
143 | (matches[9].rm_so >= 0) ? MJ_TRUE : | | 143 | (matches[9].rm_so >= 0) ? MJ_TRUE : |
144 | (matches[10].rm_so < 0) ? -1 : | | 144 | (matches[10].rm_so < 0) ? -1 : |
145 | (s[*from + (int)(matches[10].rm_so)] == '[') ? MJ_OPEN_BRACKET : | | 145 | (s[*from + (int)(matches[10].rm_so)] == '[') ? MJ_OPEN_BRACKET : |
146 | (s[*from + (int)(matches[10].rm_so)] == ']') ? MJ_CLOSE_BRACKET : | | 146 | (s[*from + (int)(matches[10].rm_so)] == ']') ? MJ_CLOSE_BRACKET : |
147 | (s[*from + (int)(matches[10].rm_so)] == '{') ? MJ_OPEN_BRACE : | | 147 | (s[*from + (int)(matches[10].rm_so)] == '{') ? MJ_OPEN_BRACE : |
148 | (s[*from + (int)(matches[10].rm_so)] == '}') ? MJ_CLOSE_BRACE : | | 148 | (s[*from + (int)(matches[10].rm_so)] == '}') ? MJ_CLOSE_BRACE : |
149 | (s[*from + (int)(matches[10].rm_so)] == ':') ? MJ_COLON : | | 149 | (s[*from + (int)(matches[10].rm_so)] == ':') ? MJ_COLON : |
150 | MJ_COMMA; | | 150 | MJ_COMMA; |
151 | *from += (int)(matches[1].rm_so); | | 151 | *from += (int)(matches[1].rm_so); |
152 | return *tok; | | 152 | return *tok; |
153 | } | | 153 | } |
154 | | | 154 | |
155 | /***************************************************************************/ | | 155 | /***************************************************************************/ |
156 | | | 156 | |
157 | /* return the number of entries in the array */ | | 157 | /* return the number of entries in the array */ |
158 | int | | 158 | int |
159 | mj_arraycount(mj_t *atom) | | 159 | mj_arraycount(mj_t *atom) |
160 | { | | 160 | { |
161 | return atom->c; | | 161 | return atom->c; |
162 | } | | 162 | } |
163 | | | 163 | |
164 | /* create a new JSON node */ | | 164 | /* create a new JSON node */ |
165 | int | | 165 | int |
166 | mj_create(mj_t *atom, const char *type, ...) | | 166 | mj_create(mj_t *atom, const char *type, ...) |
167 | { | | 167 | { |
168 | va_list args; | | 168 | va_list args; |
169 | | | 169 | |
170 | if (strcmp(type, "false") == 0) { | | 170 | if (strcmp(type, "false") == 0) { |
171 | atom->type = MJ_FALSE; | | 171 | atom->type = MJ_FALSE; |
172 | atom->c = 0; | | 172 | atom->c = 0; |
173 | } else if (strcmp(type, "true") == 0) { | | 173 | } else if (strcmp(type, "true") == 0) { |
174 | atom->type = MJ_TRUE; | | 174 | atom->type = MJ_TRUE; |
175 | atom->c = 1; | | 175 | atom->c = 1; |
176 | } else if (strcmp(type, "null") == 0) { | | 176 | } else if (strcmp(type, "null") == 0) { |
177 | atom->type = MJ_NULL; | | 177 | atom->type = MJ_NULL; |
178 | } else if (strcmp(type, "number") == 0) { | | 178 | } else if (strcmp(type, "number") == 0) { |
179 | va_start(args, type); | | 179 | va_start(args, type); |
180 | create_number(atom, (double)va_arg(args, double)); | | 180 | create_number(atom, (double)va_arg(args, double)); |
181 | va_end(args); | | 181 | va_end(args); |
182 | } else if (strcmp(type, "integer") == 0) { | | 182 | } else if (strcmp(type, "integer") == 0) { |
183 | va_start(args, type); | | 183 | va_start(args, type); |
184 | create_integer(atom, (int64_t)va_arg(args, int64_t)); | | 184 | create_integer(atom, (int64_t)va_arg(args, int64_t)); |
185 | va_end(args); | | 185 | va_end(args); |
186 | } else if (strcmp(type, "string") == 0) { | | 186 | } else if (strcmp(type, "string") == 0) { |
187 | va_start(args, type); | | 187 | va_start(args, type); |
188 | create_string(atom, (char *)va_arg(args, char *)); | | 188 | create_string(atom, (char *)va_arg(args, char *)); |
189 | va_end(args); | | 189 | va_end(args); |
190 | } else if (strcmp(type, "array") == 0) { | | 190 | } else if (strcmp(type, "array") == 0) { |
191 | atom->type = MJ_ARRAY; | | 191 | atom->type = MJ_ARRAY; |
192 | } else if (strcmp(type, "object") == 0) { | | 192 | } else if (strcmp(type, "object") == 0) { |
193 | atom->type = MJ_OBJECT; | | 193 | atom->type = MJ_OBJECT; |
194 | } else { | | 194 | } else { |
195 | (void) fprintf(stderr, "weird type '%s'\n", type); | | 195 | (void) fprintf(stderr, "weird type '%s'\n", type); |
196 | return 0; | | 196 | return 0; |
197 | } | | 197 | } |
198 | return 1; | | 198 | return 1; |
199 | } | | 199 | } |
200 | | | 200 | |
201 | /* put a JSON tree into a text string */ | | 201 | /* put a JSON tree into a text string */ |
202 | int | | 202 | int |
203 | mj_snprint(char *buf, size_t size, mj_t *atom) | | 203 | mj_snprint(char *buf, size_t size, mj_t *atom) |
204 | { | | 204 | { |
205 | unsigned i; | | 205 | unsigned i; |
206 | int cc; | | 206 | int cc; |
207 | | | 207 | |
208 | switch(atom->type) { | | 208 | switch(atom->type) { |
209 | case MJ_NULL: | | 209 | case MJ_NULL: |
210 | return snprintf(buf, size, "null"); | | 210 | return snprintf(buf, size, "null"); |
211 | case MJ_FALSE: | | 211 | case MJ_FALSE: |
212 | return snprintf(buf, size, "false"); | | 212 | return snprintf(buf, size, "false"); |
213 | case MJ_TRUE: | | 213 | case MJ_TRUE: |
214 | return snprintf(buf, size, "true"); | | 214 | return snprintf(buf, size, "true"); |
215 | case MJ_NUMBER: | | 215 | case MJ_NUMBER: |
216 | return snprintf(buf, size, "%s", atom->value.s); | | 216 | return snprintf(buf, size, "%s", atom->value.s); |
217 | case MJ_STRING: | | 217 | case MJ_STRING: |
218 | return snprintf(buf, size, "\"%s\"", atom->value.s); | | 218 | return snprintf(buf, size, "\"%s\"", atom->value.s); |
219 | case MJ_ARRAY: | | 219 | case MJ_ARRAY: |
220 | cc = snprintf(buf, size, "[ "); | | 220 | cc = snprintf(buf, size, "[ "); |
221 | for (i = 0 ; i < atom->c ; i++) { | | 221 | for (i = 0 ; i < atom->c ; i++) { |
222 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i]); | | 222 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i]); |
223 | if (i < atom->c - 1) { | | 223 | if (i < atom->c - 1) { |
224 | cc += snprintf(&buf[cc], size - cc, ", "); | | 224 | cc += snprintf(&buf[cc], size - cc, ", "); |
225 | } | | 225 | } |
226 | } | | 226 | } |
227 | return cc + snprintf(&buf[cc], size - cc, "]\n"); | | 227 | return cc + snprintf(&buf[cc], size - cc, "]\n"); |
228 | case MJ_OBJECT: | | 228 | case MJ_OBJECT: |
229 | cc = snprintf(buf, size, "{ "); | | 229 | cc = snprintf(buf, size, "{ "); |
230 | for (i = 0 ; i < atom->c ; i += 2) { | | 230 | for (i = 0 ; i < atom->c ; i += 2) { |
231 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i]); | | 231 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i]); |
232 | cc += snprintf(&buf[cc], size - cc, ":"); | | 232 | cc += snprintf(&buf[cc], size - cc, ":"); |
233 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i + 1]); | | 233 | cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i + 1]); |
234 | if (i + 1 < atom->c - 1) { | | 234 | if (i + 1 < atom->c - 1) { |
235 | cc += snprintf(&buf[cc], size - cc, ", "); | | 235 | cc += snprintf(&buf[cc], size - cc, ", "); |
236 | } | | 236 | } |
237 | } | | 237 | } |
238 | return cc + snprintf(&buf[cc], size - cc, "}\n"); | | 238 | return cc + snprintf(&buf[cc], size - cc, "}\n"); |
239 | default: | | 239 | default: |
240 | (void) fprintf(stderr, "mj_snprint: weird type %d\n", atom->type); | | 240 | (void) fprintf(stderr, "mj_snprint: weird type %d\n", atom->type); |
241 | return 0; | | 241 | return 0; |
242 | } | | 242 | } |
243 | } | | 243 | } |
244 | | | 244 | |
245 | /* allocate and print the atom */ | | 245 | /* allocate and print the atom */ |
246 | int | | 246 | int |
247 | mj_asprint(char **buf, mj_t *atom) | | 247 | mj_asprint(char **buf, mj_t *atom) |
248 | { | | 248 | { |
249 | int size; | | 249 | int size; |
250 | | | 250 | |
251 | size = mj_string_size(atom); | | 251 | size = mj_string_size(atom); |
252 | if ((*buf = calloc(1, (unsigned)(size + 1))) == NULL) { | | 252 | if ((*buf = calloc(1, (unsigned)(size + 1))) == NULL) { |
253 | return -1; | | 253 | return -1; |
254 | } | | 254 | } |
255 | (void) mj_snprint(*buf, (unsigned)(size + 1), atom); | | 255 | (void) mj_snprint(*buf, (unsigned)(size + 1), atom); |
256 | return size + 1; | | 256 | return size + 1; |
257 | } | | 257 | } |
258 | | | 258 | |
259 | /* read into a JSON tree from a string */ | | 259 | /* read into a JSON tree from a string */ |
260 | int | | 260 | int |
261 | mj_parse(mj_t *atom, const char *s, int *from, int *to, int *tok) | | 261 | mj_parse(mj_t *atom, const char *s, int *from, int *to, int *tok) |
262 | { | | 262 | { |
263 | int i; | | 263 | int i; |
264 | | | 264 | |
265 | switch(atom->type = *tok = gettok(s, from, to, tok)) { | | 265 | switch(atom->type = *tok = gettok(s, from, to, tok)) { |
266 | case MJ_NUMBER: | | 266 | case MJ_NUMBER: |
267 | atom->value.s = strnsave(&s[*from], *to - *from, 1); | | 267 | atom->value.s = strnsave(&s[*from], *to - *from, 1); |
268 | atom->c = atom->size = strlen(atom->value.s); | | 268 | atom->c = atom->size = (unsigned)strlen(atom->value.s); |
269 | return gettok(s, from, to, tok); | | 269 | return gettok(s, from, to, tok); |
270 | case MJ_STRING: | | 270 | case MJ_STRING: |
271 | atom->value.s = strnsave(&s[*from + 1], *to - *from - 2, 1); | | 271 | atom->value.s = strnsave(&s[*from + 1], *to - *from - 2, 1); |
272 | atom->c = atom->size = strlen(atom->value.s); | | 272 | atom->c = atom->size = (unsigned)strlen(atom->value.s); |
273 | return gettok(s, from, to, tok); | | 273 | return gettok(s, from, to, tok); |
274 | case MJ_NULL: | | 274 | case MJ_NULL: |
275 | case MJ_FALSE: | | 275 | case MJ_FALSE: |
276 | case MJ_TRUE: | | 276 | case MJ_TRUE: |
277 | atom->c = (unsigned)*to; | | 277 | atom->c = (unsigned)*to; |
278 | return gettok(s, from, to, tok); | | 278 | return gettok(s, from, to, tok); |
279 | case MJ_OPEN_BRACKET: | | 279 | case MJ_OPEN_BRACKET: |
280 | mj_create(atom, "array"); | | 280 | mj_create(atom, "array"); |
281 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); | | 281 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); |
282 | while (mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACKET) { | | 282 | while (mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACKET) { |
283 | if (*tok != MJ_COMMA) { | | 283 | if (*tok != MJ_COMMA) { |
284 | (void) fprintf(stderr, "1. expected comma (got %d) at '%s'\n", *tok, &s[*from]); | | 284 | (void) fprintf(stderr, "1. expected comma (got %d) at '%s'\n", *tok, &s[*from]); |
285 | break; | | 285 | break; |
286 | } | | 286 | } |
287 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); | | 287 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); |
288 | } | | 288 | } |
289 | return gettok(s, from, to, tok); | | 289 | return gettok(s, from, to, tok); |
290 | case MJ_OPEN_BRACE: | | 290 | case MJ_OPEN_BRACE: |
291 | mj_create(atom, "object"); | | 291 | mj_create(atom, "object"); |
292 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); | | 292 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); |
293 | for (i = 0 ; mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACE ; i++) { | | 293 | for (i = 0 ; mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACE ; i++) { |
294 | if (((i % 2) == 0 && *tok != MJ_COLON) || ((i % 2) == 1 && *tok != MJ_COMMA)) { | | 294 | if (((i % 2) == 0 && *tok != MJ_COLON) || ((i % 2) == 1 && *tok != MJ_COMMA)) { |
295 | (void) fprintf(stderr, "2. expected comma (got %d) at '%s'\n", *tok, &s[*from]); | | 295 | (void) fprintf(stderr, "2. expected comma (got %d) at '%s'\n", *tok, &s[*from]); |
296 | break; | | 296 | break; |
297 | } | | 297 | } |
298 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); | | 298 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0); |
299 | } | | 299 | } |
300 | return gettok(s, from, to, tok); | | 300 | return gettok(s, from, to, tok); |
301 | default: | | 301 | default: |
302 | return *tok; | | 302 | return *tok; |
303 | } | | 303 | } |
304 | } | | 304 | } |
305 | | | 305 | |
306 | /* return the index of the item which corresponds to the name in the array */ | | 306 | /* return the index of the item which corresponds to the name in the array */ |
307 | int | | 307 | int |
308 | mj_object_find(mj_t *atom, const char *name, const unsigned from, const unsigned incr) | | 308 | mj_object_find(mj_t *atom, const char *name, const unsigned from, const unsigned incr) |
309 | { | | 309 | { |
310 | return findentry(atom, name, from, incr); | | 310 | return findentry(atom, name, from, incr); |
311 | } | | 311 | } |
312 | | | 312 | |
313 | /* find an atom in a composite mj JSON node */ | | 313 | /* find an atom in a composite mj JSON node */ |
314 | mj_t * | | 314 | mj_t * |
315 | mj_get_atom(mj_t *atom, ...) | | 315 | mj_get_atom(mj_t *atom, ...) |
316 | { | | 316 | { |
317 | unsigned i; | | 317 | unsigned i; |
318 | va_list args; | | 318 | va_list args; |
319 | char *name; | | 319 | char *name; |
320 | int n; | | 320 | int n; |
321 | | | 321 | |
322 | switch(atom->type) { | | 322 | switch(atom->type) { |
323 | case MJ_ARRAY: | | 323 | case MJ_ARRAY: |
324 | va_start(args, atom); | | 324 | va_start(args, atom); |
325 | i = va_arg(args, int); | | 325 | i = va_arg(args, int); |
326 | va_end(args); | | 326 | va_end(args); |
327 | return (i < atom->c) ? &atom->value.v[i] : NULL; | | 327 | return (i < atom->c) ? &atom->value.v[i] : NULL; |
328 | case MJ_OBJECT: | | 328 | case MJ_OBJECT: |
329 | va_start(args, atom); | | 329 | va_start(args, atom); |
330 | name = va_arg(args, char *); | | 330 | name = va_arg(args, char *); |
331 | va_end(args); | | 331 | va_end(args); |
332 | return ((n = findentry(atom, name, 0, 2)) >= 0) ? &atom->value.v[n + 1] : NULL; | | 332 | return ((n = findentry(atom, name, 0, 2)) >= 0) ? &atom->value.v[n + 1] : NULL; |
333 | default: | | 333 | default: |
334 | return NULL; | | 334 | return NULL; |
335 | } | | 335 | } |
336 | } | | 336 | } |
337 | | | 337 | |
338 | /* perform a deep copy on an mj JSON atom */ | | 338 | /* perform a deep copy on an mj JSON atom */ |
339 | int | | 339 | int |
340 | mj_deepcopy(mj_t *dst, mj_t *src) | | 340 | mj_deepcopy(mj_t *dst, mj_t *src) |
341 | { | | 341 | { |
342 | unsigned i; | | 342 | unsigned i; |
343 | | | 343 | |
344 | switch(src->type) { | | 344 | switch(src->type) { |
345 | case MJ_FALSE: | | 345 | case MJ_FALSE: |
346 | case MJ_TRUE: | | 346 | case MJ_TRUE: |
347 | case MJ_NULL: | | 347 | case MJ_NULL: |
348 | (void) memcpy(dst, src, sizeof(*dst)); | | 348 | (void) memcpy(dst, src, sizeof(*dst)); |
349 | return 1; | | 349 | return 1; |
350 | case MJ_STRING: | | 350 | case MJ_STRING: |
351 | case MJ_NUMBER: | | 351 | case MJ_NUMBER: |
352 | (void) memcpy(dst, src, sizeof(*dst)); | | 352 | (void) memcpy(dst, src, sizeof(*dst)); |
353 | dst->value.s = strnsave(src->value.s, -1, 0); | | 353 | dst->value.s = strnsave(src->value.s, -1, 0); |
354 | dst->c = dst->size = strlen(dst->value.s); | | 354 | dst->c = dst->size = (unsigned)strlen(dst->value.s); |
355 | return 1; | | 355 | return 1; |
356 | case MJ_ARRAY: | | 356 | case MJ_ARRAY: |
357 | case MJ_OBJECT: | | 357 | case MJ_OBJECT: |
358 | (void) memcpy(dst, src, sizeof(*dst)); | | 358 | (void) memcpy(dst, src, sizeof(*dst)); |
359 | NEWARRAY(mj_t, dst->value.v, dst->size, "mj_deepcopy()", return 0); | | 359 | NEWARRAY(mj_t, dst->value.v, dst->size, "mj_deepcopy()", return 0); |
360 | for (i = 0 ; i < src->c ; i++) { | | 360 | for (i = 0 ; i < src->c ; i++) { |
361 | if (!mj_deepcopy(&dst->value.v[i], &src->value.v[i])) { | | 361 | if (!mj_deepcopy(&dst->value.v[i], &src->value.v[i])) { |
362 | return 0; | | 362 | return 0; |
363 | } | | 363 | } |
364 | } | | 364 | } |
365 | return 1; | | 365 | return 1; |
366 | default: | | 366 | default: |
367 | (void) fprintf(stderr, "weird type '%d'\n", src->type); | | 367 | (void) fprintf(stderr, "weird type '%d'\n", src->type); |
368 | return 0; | | 368 | return 0; |
369 | } | | 369 | } |
370 | } | | 370 | } |
371 | | | 371 | |
372 | /* do a deep delete on the object */ | | 372 | /* do a deep delete on the object */ |
373 | void | | 373 | void |
374 | mj_delete(mj_t *atom) | | 374 | mj_delete(mj_t *atom) |
375 | { | | 375 | { |
376 | unsigned i; | | 376 | unsigned i; |
377 | | | 377 | |
378 | switch(atom->type) { | | 378 | switch(atom->type) { |
379 | case MJ_STRING: | | 379 | case MJ_STRING: |
380 | case MJ_NUMBER: | | 380 | case MJ_NUMBER: |
381 | free(atom->value.s); | | 381 | free(atom->value.s); |
382 | break; | | 382 | break; |
383 | case MJ_ARRAY: | | 383 | case MJ_ARRAY: |
384 | case MJ_OBJECT: | | 384 | case MJ_OBJECT: |
385 | for (i = 0 ; i < atom->c ; i++) { | | 385 | for (i = 0 ; i < atom->c ; i++) { |
386 | mj_delete(&atom->value.v[i]); | | 386 | mj_delete(&atom->value.v[i]); |
387 | } | | 387 | } |
388 | break; | | 388 | break; |
389 | default: | | 389 | default: |
390 | break; | | 390 | break; |
391 | } | | 391 | } |
392 | } | | 392 | } |
393 | | | 393 | |
394 | /* return the string size needed for the textual output of the JSON node */ | | 394 | /* return the string size needed for the textual output of the JSON node */ |
395 | int | | 395 | int |
396 | mj_string_size(mj_t *atom) | | 396 | mj_string_size(mj_t *atom) |
397 | { | | 397 | { |
398 | unsigned i; | | 398 | unsigned i; |
399 | int cc; | | 399 | int cc; |
400 | | | 400 | |
401 | switch(atom->type) { | | 401 | switch(atom->type) { |
402 | case MJ_NULL: | | 402 | case MJ_NULL: |
403 | case MJ_TRUE: | | 403 | case MJ_TRUE: |
404 | return 4; | | 404 | return 4; |
405 | case MJ_FALSE: | | 405 | case MJ_FALSE: |
406 | return 5; | | 406 | return 5; |
407 | case MJ_NUMBER: | | 407 | case MJ_NUMBER: |
408 | return atom->c; | | 408 | return atom->c; |
409 | case MJ_STRING: | | 409 | case MJ_STRING: |
410 | return atom->c + 2; | | 410 | return atom->c + 2; |
411 | case MJ_ARRAY: | | 411 | case MJ_ARRAY: |
412 | for (cc = 2, i = 0 ; i < atom->c ; i++) { | | 412 | for (cc = 2, i = 0 ; i < atom->c ; i++) { |
413 | cc += mj_string_size(&atom->value.v[i]); | | 413 | cc += mj_string_size(&atom->value.v[i]); |
414 | if (i < atom->c - 1) { | | 414 | if (i < atom->c - 1) { |
415 | cc += 2; | | 415 | cc += 2; |
416 | } | | 416 | } |
417 | } | | 417 | } |
418 | return cc + 1 + 1; | | 418 | return cc + 1 + 1; |
419 | case MJ_OBJECT: | | 419 | case MJ_OBJECT: |
420 | for (cc = 2, i = 0 ; i < atom->c ; i += 2) { | | 420 | for (cc = 2, i = 0 ; i < atom->c ; i += 2) { |
421 | cc += mj_string_size(&atom->value.v[i]) + 1 + mj_string_size(&atom->value.v[i + 1]); | | 421 | cc += mj_string_size(&atom->value.v[i]) + 1 + mj_string_size(&atom->value.v[i + 1]); |
422 | if (i + 1 < atom->c - 1) { | | 422 | if (i + 1 < atom->c - 1) { |
423 | cc += 2; | | 423 | cc += 2; |
424 | } | | 424 | } |
425 | } | | 425 | } |
426 | return cc + 1 + 1; | | 426 | return cc + 1 + 1; |
427 | default: | | 427 | default: |
428 | (void) fprintf(stderr, "mj_string_size: weird type %d\n", atom->type); | | 428 | (void) fprintf(stderr, "mj_string_size: weird type %d\n", atom->type); |
429 | return 0; | | 429 | return 0; |
430 | } | | 430 | } |
431 | } | | 431 | } |
432 | | | 432 | |
433 | /* create a new atom, and append it to the array or object */ | | 433 | /* create a new atom, and append it to the array or object */ |
434 | int | | 434 | int |
435 | mj_append(mj_t *atom, const char *type, ...) | | 435 | mj_append(mj_t *atom, const char *type, ...) |
436 | { | | 436 | { |
437 | va_list args; | | 437 | va_list args; |
438 | | | 438 | |
439 | if (atom->type != MJ_ARRAY && atom->type != MJ_OBJECT) { | | 439 | if (atom->type != MJ_ARRAY && atom->type != MJ_OBJECT) { |
440 | return 0; | | 440 | return 0; |
441 | } | | 441 | } |
442 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append()", return 0); | | 442 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append()", return 0); |
443 | va_start(args, type); | | 443 | va_start(args, type); |
444 | if (strcmp(type, "string") == 0) { | | 444 | if (strcmp(type, "string") == 0) { |
445 | create_string(&atom->value.v[atom->c++], (char *)va_arg(args, char *)); | | 445 | create_string(&atom->value.v[atom->c++], (char *)va_arg(args, char *)); |
446 | } else if (strcmp(type, "integer") == 0) { | | 446 | } else if (strcmp(type, "integer") == 0) { |
447 | create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t)); | | 447 | create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t)); |
448 | } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) { | | 448 | } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) { |
449 | mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *)); | | 449 | mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *)); |
450 | } else { | | 450 | } else { |
451 | (void) fprintf(stderr, "mj_append: weird type '%s'\n", type); | | 451 | (void) fprintf(stderr, "mj_append: weird type '%s'\n", type); |
452 | } | | 452 | } |
453 | va_end(args); | | 453 | va_end(args); |
454 | return 1; | | 454 | return 1; |
455 | } | | 455 | } |
456 | | | 456 | |
457 | /* append a field to an object */ | | 457 | /* append a field to an object */ |
458 | int | | 458 | int |
459 | mj_append_field(mj_t *atom, const char *name, const char *type, ...) | | 459 | mj_append_field(mj_t *atom, const char *name, const char *type, ...) |
460 | { | | 460 | { |
461 | va_list args; | | 461 | va_list args; |
462 | | | 462 | |
463 | if (atom->type != MJ_OBJECT) { | | 463 | if (atom->type != MJ_OBJECT) { |
464 | return 0; | | 464 | return 0; |
465 | } | | 465 | } |
466 | mj_append(atom, "string", name); | | 466 | mj_append(atom, "string", name); |
467 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append_field()", return 0); | | 467 | ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append_field()", return 0); |
468 | va_start(args, type); | | 468 | va_start(args, type); |
469 | if (strcmp(type, "string") == 0) { | | 469 | if (strcmp(type, "string") == 0) { |
470 | create_string(&atom->value.v[atom->c++], (char *)va_arg(args, char *)); | | 470 | create_string(&atom->value.v[atom->c++], (char *)va_arg(args, char *)); |
471 | } else if (strcmp(type, "integer") == 0) { | | 471 | } else if (strcmp(type, "integer") == 0) { |
472 | create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t)); | | 472 | create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t)); |
473 | } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) { | | 473 | } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) { |
474 | mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *)); | | 474 | mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *)); |
475 | } else { | | 475 | } else { |
476 | (void) fprintf(stderr, "mj_append_field: weird type '%s'\n", type); | | 476 | (void) fprintf(stderr, "mj_append_field: weird type '%s'\n", type); |
477 | } | | 477 | } |
478 | va_end(args); | | 478 | va_end(args); |
479 | return 1; | | 479 | return 1; |
480 | } | | 480 | } |
481 | | | 481 | |
482 | int | | 482 | int |
483 | mj_lint(mj_t *obj) | | 483 | mj_lint(mj_t *obj) |
484 | { | | 484 | { |
485 | unsigned i; | | 485 | unsigned i; |
486 | int ret; | | 486 | int ret; |
487 | | | 487 | |
488 | switch(obj->type) { | | 488 | switch(obj->type) { |
489 | case MJ_NULL: | | 489 | case MJ_NULL: |
490 | case MJ_FALSE: | | 490 | case MJ_FALSE: |
491 | case MJ_TRUE: | | 491 | case MJ_TRUE: |
492 | if (obj->value.s != NULL) { | | 492 | if (obj->value.s != NULL) { |
493 | (void) fprintf(stderr, "null/false/true: non zero string\n"); | | 493 | (void) fprintf(stderr, "null/false/true: non zero string\n"); |
494 | return 0; | | 494 | return 0; |
495 | } | | 495 | } |
496 | return 1; | | 496 | return 1; |
497 | case MJ_NUMBER: | | 497 | case MJ_NUMBER: |
498 | case MJ_STRING: | | 498 | case MJ_STRING: |
499 | if (obj->c > obj->size) { | | 499 | if (obj->c > obj->size) { |
500 | (void) fprintf(stderr, "string/number lint c (%u) > size (%u)\n", obj->c, obj->size); | | 500 | (void) fprintf(stderr, "string/number lint c (%u) > size (%u)\n", obj->c, obj->size); |
501 | return 0; | | 501 | return 0; |
502 | } | | 502 | } |
503 | return 1; | | 503 | return 1; |
504 | case MJ_ARRAY: | | 504 | case MJ_ARRAY: |
505 | case MJ_OBJECT: | | 505 | case MJ_OBJECT: |
506 | if (obj->c > obj->size) { | | 506 | if (obj->c > obj->size) { |
507 | (void) fprintf(stderr, "array/object lint c (%u) > size (%u)\n", obj->c, obj->size); | | 507 | (void) fprintf(stderr, "array/object lint c (%u) > size (%u)\n", obj->c, obj->size); |
508 | return 0; | | 508 | return 0; |
509 | } | | 509 | } |
510 | for (ret = 1, i = 0 ; i < obj->c ; i++) { | | 510 | for (ret = 1, i = 0 ; i < obj->c ; i++) { |
511 | if (!mj_lint(&obj->value.v[i])) { | | 511 | if (!mj_lint(&obj->value.v[i])) { |
512 | (void) fprintf(stderr, "array/object lint found at %d of %p\n", i, obj); | | 512 | (void) fprintf(stderr, "array/object lint found at %d of %p\n", i, obj); |
513 | ret = 0; | | 513 | ret = 0; |
514 | } | | 514 | } |
515 | } | | 515 | } |
516 | return ret; | | 516 | return ret; |
517 | default: | | 517 | default: |
518 | (void) fprintf(stderr, "problem type %d in %p\n", obj->type, obj); | | 518 | (void) fprintf(stderr, "problem type %d in %p\n", obj->type, obj); |
519 | return 0; | | 519 | return 0; |
520 | } | | 520 | } |
521 | } | | 521 | } |