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 #ifdef HAVE_CONFIG_H
9 #include <jansson_private_config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <jansson.h>
17
18 #ifdef HAVE_LOCALE_H
19 #include <locale.h>
20 #endif
21
22 #if _WIN32
23 #include <io.h> /* for _setmode() */
24 #include <fcntl.h> /* for _O_BINARY */
25
26 static const char dir_sep = '\\';
27 #else
28 static const char dir_sep = '/';
29 #endif
30
31
32 struct config {
33 int indent;
34 int compact;
35 int preserve_order;
36 int ensure_ascii;
37 int sort_keys;
38 int strip;
39 int use_env;
40 int have_hashseed;
41 int hashseed;
42 int precision;
43 } conf;
44
45 #define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t')
46
47 /* Return a pointer to the first non-whitespace character of str.
48 Modifies str so that all trailing whitespace characters are
49 replaced by '\0'. */
strip(char * str)50 static const char *strip(char *str)
51 {
52 size_t length;
53 char *result = str;
54 while (*result && l_isspace(*result))
55 result++;
56
57 length = strlen(result);
58 if (length == 0)
59 return result;
60
61 while (l_isspace(result[length - 1]))
62 result[--length] = '\0';
63
64 return result;
65 }
66
67
loadfile(FILE * file)68 static char *loadfile(FILE *file)
69 {
70 long fsize, ret;
71 char *buf;
72
73 fseek(file, 0, SEEK_END);
74 fsize = ftell(file);
75 fseek(file, 0, SEEK_SET);
76
77 buf = malloc(fsize+1);
78 ret = fread(buf, 1, fsize, file);
79 if (ret != fsize)
80 exit(1);
81 buf[fsize] = '\0';
82
83 return buf;
84 }
85
86
read_conf(FILE * conffile)87 static void read_conf(FILE *conffile)
88 {
89 char *buffer, *line, *val;
90
91 buffer = loadfile(conffile);
92 for (line = strtok(buffer, "\r\n"); line; line = strtok(NULL, "\r\n")) {
93 if (!strncmp(line, "export ", 7))
94 continue;
95 val = strchr(line, '=');
96 if (!val) {
97 printf("invalid configuration line\n");
98 break;
99 }
100 *val++ = '\0';
101
102 if (!strcmp(line, "JSON_INDENT"))
103 conf.indent = atoi(val);
104 if (!strcmp(line, "JSON_COMPACT"))
105 conf.compact = atoi(val);
106 if (!strcmp(line, "JSON_ENSURE_ASCII"))
107 conf.ensure_ascii = atoi(val);
108 if (!strcmp(line, "JSON_PRESERVE_ORDER"))
109 conf.preserve_order = atoi(val);
110 if (!strcmp(line, "JSON_SORT_KEYS"))
111 conf.sort_keys = atoi(val);
112 if (!strcmp(line, "JSON_REAL_PRECISION"))
113 conf.precision = atoi(val);
114 if (!strcmp(line, "STRIP"))
115 conf.strip = atoi(val);
116 if (!strcmp(line, "HASHSEED")) {
117 conf.have_hashseed = 1;
118 conf.hashseed = atoi(val);
119 } else {
120 conf.have_hashseed = 0;
121 }
122 }
123
124 free(buffer);
125 }
126
127
cmpfile(const char * str,const char * path,const char * fname)128 static int cmpfile(const char *str, const char *path, const char *fname)
129 {
130 char filename[1024], *buffer;
131 int ret;
132 FILE *file;
133
134 sprintf(filename, "%s%c%s", path, dir_sep, fname);
135 file = fopen(filename, "rb");
136 if (!file) {
137 if (conf.strip)
138 strcat(filename, ".strip");
139 else
140 strcat(filename, ".normal");
141 file = fopen(filename, "rb");
142 }
143 if (!file) {
144 printf("Error: test result file could not be opened.\n");
145 exit(1);
146 }
147
148 buffer = loadfile(file);
149 if (strcmp(buffer, str) != 0)
150 ret = 1;
151 else
152 ret = 0;
153 free(buffer);
154 fclose(file);
155
156 return ret;
157 }
158
use_conf(char * test_path)159 int use_conf(char *test_path)
160 {
161 int ret;
162 size_t flags = 0;
163 char filename[1024], errstr[1024];
164 char *buffer;
165 FILE *infile, *conffile;
166 json_t *json;
167 json_error_t error;
168
169 sprintf(filename, "%s%cinput", test_path, dir_sep);
170 if (!(infile = fopen(filename, "rb"))) {
171 fprintf(stderr, "Could not open \"%s\"\n", filename);
172 return 2;
173 }
174
175 sprintf(filename, "%s%cenv", test_path, dir_sep);
176 conffile = fopen(filename, "rb");
177 if (conffile) {
178 read_conf(conffile);
179 fclose(conffile);
180 }
181
182 if (conf.indent < 0 || conf.indent > 31) {
183 fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent);
184 fclose(infile);
185 return 2;
186 }
187 if (conf.indent)
188 flags |= JSON_INDENT(conf.indent);
189
190 if (conf.compact)
191 flags |= JSON_COMPACT;
192
193 if (conf.ensure_ascii)
194 flags |= JSON_ENSURE_ASCII;
195
196 if (conf.preserve_order)
197 flags |= JSON_PRESERVE_ORDER;
198
199 if (conf.sort_keys)
200 flags |= JSON_SORT_KEYS;
201
202 if (conf.precision < 0 || conf.precision > 31) {
203 fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
204 conf.precision);
205 fclose(infile);
206 return 2;
207 }
208 if (conf.precision)
209 flags |= JSON_REAL_PRECISION(conf.precision);
210
211 if (conf.have_hashseed)
212 json_object_seed(conf.hashseed);
213
214 if (conf.strip) {
215 /* Load to memory, strip leading and trailing whitespace */
216 buffer = loadfile(infile);
217 json = json_loads(strip(buffer), 0, &error);
218 free(buffer);
219 }
220 else
221 json = json_loadf(infile, 0, &error);
222
223 fclose(infile);
224
225 if (!json) {
226 sprintf(errstr, "%d %d %d\n%s\n",
227 error.line, error.column, error.position,
228 error.text);
229
230 ret = cmpfile(errstr, test_path, "error");
231 return ret;
232 }
233
234 buffer = json_dumps(json, flags);
235 ret = cmpfile(buffer, test_path, "output");
236 free(buffer);
237 json_decref(json);
238
239 return ret;
240 }
241
getenv_int(const char * name)242 static int getenv_int(const char *name)
243 {
244 char *value, *end;
245 long result;
246
247 value = getenv(name);
248 if(!value)
249 return 0;
250
251 result = strtol(value, &end, 10);
252 if(*end != '\0')
253 return 0;
254
255 return (int)result;
256 }
257
use_env()258 int use_env()
259 {
260 int indent, precision;
261 size_t flags = 0;
262 json_t *json;
263 json_error_t error;
264
265 #ifdef _WIN32
266 /* On Windows, set stdout and stderr to binary mode to avoid
267 outputting DOS line terminators */
268 _setmode(_fileno(stdout), _O_BINARY);
269 _setmode(_fileno(stderr), _O_BINARY);
270 #endif
271
272 indent = getenv_int("JSON_INDENT");
273 if(indent < 0 || indent > 31) {
274 fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent);
275 return 2;
276 }
277 if(indent > 0)
278 flags |= JSON_INDENT(indent);
279
280 if(getenv_int("JSON_COMPACT") > 0)
281 flags |= JSON_COMPACT;
282
283 if(getenv_int("JSON_ENSURE_ASCII"))
284 flags |= JSON_ENSURE_ASCII;
285
286 if(getenv_int("JSON_PRESERVE_ORDER"))
287 flags |= JSON_PRESERVE_ORDER;
288
289 if(getenv_int("JSON_SORT_KEYS"))
290 flags |= JSON_SORT_KEYS;
291
292 precision = getenv_int("JSON_REAL_PRECISION");
293 if(precision < 0 || precision > 31) {
294 fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n",
295 precision);
296 return 2;
297 }
298
299 if(getenv("HASHSEED"))
300 json_object_seed(getenv_int("HASHSEED"));
301
302 if(precision > 0)
303 flags |= JSON_REAL_PRECISION(precision);
304
305 if(getenv_int("STRIP")) {
306 /* Load to memory, strip leading and trailing whitespace */
307 size_t size = 0, used = 0;
308 char *buffer = NULL, *buf_ck = NULL;
309
310 while(1) {
311 size_t count;
312
313 size = (size == 0 ? 128 : size * 2);
314 buf_ck = realloc(buffer, size);
315 if(!buf_ck) {
316 fprintf(stderr, "Unable to allocate %d bytes\n", (int)size);
317 free(buffer);
318 return 1;
319 }
320 buffer = buf_ck;
321
322 count = fread(buffer + used, 1, size - used, stdin);
323 if(count < size - used) {
324 buffer[used + count] = '\0';
325 break;
326 }
327 used += count;
328 }
329
330 json = json_loads(strip(buffer), 0, &error);
331 free(buffer);
332 }
333 else
334 json = json_loadf(stdin, 0, &error);
335
336 if(!json) {
337 fprintf(stderr, "%d %d %d\n%s\n",
338 error.line, error.column,
339 error.position, error.text);
340 return 1;
341 }
342
343 json_dumpf(json, stdout, flags);
344 json_decref(json);
345
346 return 0;
347 }
348
main(int argc,char * argv[])349 int main(int argc, char *argv[])
350 {
351 int i;
352 char *test_path = NULL;
353
354 #ifdef HAVE_SETLOCALE
355 setlocale(LC_ALL, "");
356 #endif
357
358 if (argc < 2) {
359 goto usage;
360 }
361
362 for (i = 1; i < argc; i++) {
363 if (!strcmp(argv[i], "--strip"))
364 conf.strip = 1;
365 else if (!strcmp(argv[i], "--env"))
366 conf.use_env = 1;
367 else
368 test_path = argv[i];
369 }
370
371 if (conf.use_env)
372 return use_env();
373 else
374 {
375 if (!test_path)
376 goto usage;
377
378 return use_conf(test_path);
379 }
380
381 usage:
382 fprintf(stderr, "argc =%d\n", argc);
383 fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]);
384 return 2;
385 }
386