1 /* 2 * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ 3 * 4 * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. 5 * Michael Clark <michael (at) metaparadigm.com> 6 * 7 * This library is free software; you can redistribute it and/or modify 8 * it under the terms of the MIT license. See COPYING for details. 9 * 10 */ 11 12 #include "config.h" 13 #undef realloc 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <stddef.h> 18 #include <limits.h> 19 #include <string.h> 20 #include <errno.h> 21 #include <ctype.h> 22 23 #ifdef HAVE_SYS_TYPES_H 24 #include <sys/types.h> 25 #endif /* HAVE_SYS_TYPES_H */ 26 27 #ifdef HAVE_SYS_STAT_H 28 #include <sys/stat.h> 29 #endif /* HAVE_SYS_STAT_H */ 30 31 #ifdef HAVE_FCNTL_H 32 #include <fcntl.h> 33 #endif /* HAVE_FCNTL_H */ 34 35 #ifdef HAVE_UNISTD_H 36 # include <unistd.h> 37 #endif /* HAVE_UNISTD_H */ 38 39 #ifdef WIN32 40 # define WIN32_LEAN_AND_MEAN 41 # include <windows.h> 42 # include <io.h> 43 #endif /* defined(WIN32) */ 44 45 #if !defined(HAVE_OPEN) && defined(WIN32) 46 # define open _open 47 #endif 48 49 #if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) 50 /* MSC has the version as _snprintf */ 51 # define snprintf _snprintf 52 #elif !defined(HAVE_SNPRINTF) 53 # error You do not have snprintf on your system. 54 #endif /* HAVE_SNPRINTF */ 55 56 #include "debug.h" 57 #include "printbuf.h" 58 #include "json_inttypes.h" 59 #include "json_object.h" 60 #include "json_tokener.h" 61 #include "json_util.h" 62 63 static int sscanf_is_broken = 0; 64 static int sscanf_is_broken_testdone = 0; 65 static void sscanf_is_broken_test(void); 66 67 struct json_object* json_object_from_file(const char *filename) 68 { 69 struct printbuf *pb; 70 struct json_object *obj; 71 char buf[JSON_FILE_BUF_SIZE]; 72 int fd, ret; 73 74 if((fd = open(filename, O_RDONLY)) < 0) { 75 MC_ERROR("json_object_from_file: error opening file %s: %s\n", 76 filename, strerror(errno)); 77 return NULL; 78 } 79 if(!(pb = printbuf_new())) { 80 close(fd); 81 MC_ERROR("json_object_from_file: printbuf_new failed\n"); 82 return NULL; 83 } 84 while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { 85 printbuf_memappend(pb, buf, ret); 86 } 87 close(fd); 88 if(ret < 0) { 89 MC_ERROR("json_object_from_file: error reading file %s: %s\n", 90 filename, strerror(errno)); 91 printbuf_free(pb); 92 return NULL; 93 } 94 obj = json_tokener_parse(pb->buf); 95 printbuf_free(pb); 96 return obj; 97 } 98 99 /* extended "format and write to file" function */ 100 101 int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) 102 { 103 const char *json_str; 104 int fd, ret; 105 unsigned int wpos, wsize; 106 107 if(!obj) { 108 MC_ERROR("json_object_to_file: object is null\n"); 109 return -1; 110 } 111 112 if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { 113 MC_ERROR("json_object_to_file: error opening file %s: %s\n", 114 filename, strerror(errno)); 115 return -1; 116 } 117 118 if(!(json_str = json_object_to_json_string_ext(obj,flags))) { 119 close(fd); 120 return -1; 121 } 122 123 wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ 124 wpos = 0; 125 while(wpos < wsize) { 126 if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { 127 close(fd); 128 MC_ERROR("json_object_to_file: error writing file %s: %s\n", 129 filename, strerror(errno)); 130 return -1; 131 } 132 133 /* because of the above check for ret < 0, we can safely cast and add */ 134 wpos += (unsigned int)ret; 135 } 136 137 close(fd); 138 return 0; 139 } 140 141 // backwards compatible "format and write to file" function 142 143 int json_object_to_file(const char *filename, struct json_object *obj) 144 { 145 return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); 146 } 147 148 int json_parse_double(const char *buf, double *retval) 149 { 150 return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); 151 } 152 153 /* 154 * Not all implementations of sscanf actually work properly. 155 * Check whether the one we're currently using does, and if 156 * it's broken, enable the workaround code. 157 */ 158 static void sscanf_is_broken_test() 159 { 160 int64_t num64; 161 int ret_errno, is_int64_min, ret_errno2, is_int64_max; 162 163 (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); 164 ret_errno = errno; 165 is_int64_min = (num64 == INT64_MIN); 166 167 (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); 168 ret_errno2 = errno; 169 is_int64_max = (num64 == INT64_MAX); 170 171 if (ret_errno != ERANGE || !is_int64_min || 172 ret_errno2 != ERANGE || !is_int64_max) 173 { 174 MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); 175 sscanf_is_broken = 1; 176 } 177 } 178 179 int json_parse_int64(const char *buf, int64_t *retval) 180 { 181 int64_t num64; 182 const char *buf_sig_digits; 183 int orig_has_neg; 184 int saved_errno; 185 186 if (!sscanf_is_broken_testdone) 187 { 188 sscanf_is_broken_test(); 189 sscanf_is_broken_testdone = 1; 190 } 191 192 // Skip leading spaces 193 while (isspace((int)*buf) && *buf) 194 buf++; 195 196 errno = 0; // sscanf won't always set errno, so initialize 197 if (sscanf(buf, "%" SCNd64, &num64) != 1) 198 { 199 MC_DEBUG("Failed to parse, sscanf != 1\n"); 200 return 1; 201 } 202 203 saved_errno = errno; 204 buf_sig_digits = buf; 205 orig_has_neg = 0; 206 if (*buf_sig_digits == '-') 207 { 208 buf_sig_digits++; 209 orig_has_neg = 1; 210 } 211 212 // Not all sscanf implementations actually work 213 if (sscanf_is_broken && saved_errno != ERANGE) 214 { 215 char buf_cmp[100]; 216 char *buf_cmp_start = buf_cmp; 217 int recheck_has_neg = 0; 218 int buf_cmp_len; 219 220 // Skip leading zeros, but keep at least one digit 221 while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') 222 buf_sig_digits++; 223 if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 224 orig_has_neg = 0; // "-0" is the same as just plain "0" 225 226 snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); 227 if (*buf_cmp_start == '-') 228 { 229 recheck_has_neg = 1; 230 buf_cmp_start++; 231 } 232 // No need to skip leading spaces or zeros here. 233 234 buf_cmp_len = strlen(buf_cmp_start); 235 /** 236 * If the sign is different, or 237 * some of the digits are different, or 238 * there is another digit present in the original string 239 * then we have NOT successfully parsed the value. 240 */ 241 if (orig_has_neg != recheck_has_neg || 242 strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || 243 ((int)strlen(buf_sig_digits) != buf_cmp_len && 244 isdigit((int)buf_sig_digits[buf_cmp_len]) 245 ) 246 ) 247 { 248 saved_errno = ERANGE; 249 } 250 } 251 252 // Not all sscanf impl's set the value properly when out of range. 253 // Always do this, even for properly functioning implementations, 254 // since it shouldn't slow things down much. 255 if (saved_errno == ERANGE) 256 { 257 if (orig_has_neg) 258 num64 = INT64_MIN; 259 else 260 num64 = INT64_MAX; 261 } 262 *retval = num64; 263 return 0; 264 } 265 266 #ifndef HAVE_REALLOC 267 void* rpl_realloc(void* p, size_t n) 268 { 269 if (n == 0) 270 n = 1; 271 if (p == 0) 272 return malloc(n); 273 return realloc(p, n); 274 } 275 #endif 276 277 #define NELEM(a) (sizeof(a) / sizeof(a[0])) 278 static const char* json_type_name[] = { 279 /* If you change this, be sure to update the enum json_type definition too */ 280 "null", 281 "boolean", 282 "double", 283 "int", 284 "object", 285 "array", 286 "string", 287 }; 288 289 const char *json_type_to_name(enum json_type o_type) 290 { 291 int o_type_int = (int)o_type; 292 if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) 293 { 294 MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); 295 return NULL; 296 } 297 return json_type_name[o_type]; 298 } 299 300