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 26static const char dir_sep = '\\'; 27#else 28static const char dir_sep = '/'; 29#endif 30 31 32struct 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'. */ 50static 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 68static 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 87static 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 128static 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 159int 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 242static 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 258int 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 349int 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 381usage: 382 fprintf(stderr, "argc =%d\n", argc); 383 fprintf(stderr, "usage: %s [--strip] [--env] test_dir\n", argv[0]); 384 return 2; 385} 386