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@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 "bits.h" 57#include "debug.h" 58#include "printbuf.h" 59#include "json_inttypes.h" 60#include "json_object.h" 61#include "json_tokener.h" 62#include "json_util.h" 63 64static int sscanf_is_broken = 0; 65static int sscanf_is_broken_testdone = 0; 66static void sscanf_is_broken_test(void); 67 68struct json_object* json_object_from_file(const char *filename) 69{ 70 struct printbuf *pb; 71 struct json_object *obj; 72 char buf[JSON_FILE_BUF_SIZE]; 73 int fd, ret; 74 75 if((fd = open(filename, O_RDONLY)) < 0) { 76 MC_ERROR("json_object_from_file: error opening file %s: %s\n", 77 filename, strerror(errno)); 78 return NULL; 79 } 80 if(!(pb = printbuf_new())) { 81 close(fd); 82 MC_ERROR("json_object_from_file: printbuf_new failed\n"); 83 return NULL; 84 } 85 while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { 86 printbuf_memappend(pb, buf, ret); 87 } 88 close(fd); 89 if(ret < 0) { 90 MC_ERROR("json_object_from_file: error reading file %s: %s\n", 91 filename, strerror(errno)); 92 printbuf_free(pb); 93 return NULL; 94 } 95 obj = json_tokener_parse(pb->buf); 96 printbuf_free(pb); 97 return obj; 98} 99 100/* extended "format and write to file" function */ 101 102int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) 103{ 104 const char *json_str; 105 int fd, ret; 106 unsigned int wpos, wsize; 107 108 if(!obj) { 109 MC_ERROR("json_object_to_file: object is null\n"); 110 return -1; 111 } 112 113 if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { 114 MC_ERROR("json_object_to_file: error opening file %s: %s\n", 115 filename, strerror(errno)); 116 return -1; 117 } 118 119 if(!(json_str = json_object_to_json_string_ext(obj,flags))) { 120 close(fd); 121 return -1; 122 } 123 124 wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ 125 wpos = 0; 126 while(wpos < wsize) { 127 if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { 128 close(fd); 129 MC_ERROR("json_object_to_file: error writing file %s: %s\n", 130 filename, strerror(errno)); 131 return -1; 132 } 133 134 /* because of the above check for ret < 0, we can safely cast and add */ 135 wpos += (unsigned int)ret; 136 } 137 138 close(fd); 139 return 0; 140} 141 142// backwards compatible "format and write to file" function 143 144int json_object_to_file(const char *filename, struct json_object *obj) 145{ 146 return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); 147} 148 149int json_parse_double(const char *buf, double *retval) 150{ 151 return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); 152} 153 154/* 155 * Not all implementations of sscanf actually work properly. 156 * Check whether the one we're currently using does, and if 157 * it's broken, enable the workaround code. 158 */ 159static void sscanf_is_broken_test() 160{ 161 int64_t num64; 162 int ret_errno, is_int64_min, ret_errno2, is_int64_max; 163 164 (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); 165 ret_errno = errno; 166 is_int64_min = (num64 == INT64_MIN); 167 168 (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); 169 ret_errno2 = errno; 170 is_int64_max = (num64 == INT64_MAX); 171 172 if (ret_errno != ERANGE || !is_int64_min || 173 ret_errno2 != ERANGE || !is_int64_max) 174 { 175 MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); 176 sscanf_is_broken = 1; 177 } 178} 179 180int json_parse_int64(const char *buf, int64_t *retval) 181{ 182 int64_t num64; 183 const char *buf_sig_digits; 184 int orig_has_neg; 185 int saved_errno; 186 187 if (!sscanf_is_broken_testdone) 188 { 189 sscanf_is_broken_test(); 190 sscanf_is_broken_testdone = 1; 191 } 192 193 // Skip leading spaces 194 while (isspace((int)*buf) && *buf) 195 buf++; 196 197 errno = 0; // sscanf won't always set errno, so initialize 198 if (sscanf(buf, "%" SCNd64, &num64) != 1) 199 { 200 MC_DEBUG("Failed to parse, sscanf != 1\n"); 201 return 1; 202 } 203 204 saved_errno = errno; 205 buf_sig_digits = buf; 206 orig_has_neg = 0; 207 if (*buf_sig_digits == '-') 208 { 209 buf_sig_digits++; 210 orig_has_neg = 1; 211 } 212 213 // Not all sscanf implementations actually work 214 if (sscanf_is_broken && saved_errno != ERANGE) 215 { 216 char buf_cmp[100]; 217 char *buf_cmp_start = buf_cmp; 218 int recheck_has_neg = 0; 219 int buf_cmp_len; 220 221 // Skip leading zeros, but keep at least one digit 222 while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') 223 buf_sig_digits++; 224 if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 225 orig_has_neg = 0; // "-0" is the same as just plain "0" 226 227 snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); 228 if (*buf_cmp_start == '-') 229 { 230 recheck_has_neg = 1; 231 buf_cmp_start++; 232 } 233 // No need to skip leading spaces or zeros here. 234 235 buf_cmp_len = strlen(buf_cmp_start); 236 /** 237 * If the sign is different, or 238 * some of the digits are different, or 239 * there is another digit present in the original string 240 * then we have NOT successfully parsed the value. 241 */ 242 if (orig_has_neg != recheck_has_neg || 243 strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || 244 ((int)strlen(buf_sig_digits) != buf_cmp_len && 245 isdigit((int)buf_sig_digits[buf_cmp_len]) 246 ) 247 ) 248 { 249 saved_errno = ERANGE; 250 } 251 } 252 253 // Not all sscanf impl's set the value properly when out of range. 254 // Always do this, even for properly functioning implementations, 255 // since it shouldn't slow things down much. 256 if (saved_errno == ERANGE) 257 { 258 if (orig_has_neg) 259 num64 = INT64_MIN; 260 else 261 num64 = INT64_MAX; 262 } 263 *retval = num64; 264 return 0; 265} 266 267#ifndef HAVE_REALLOC 268void* rpl_realloc(void* p, size_t n) 269{ 270 if (n == 0) 271 n = 1; 272 if (p == 0) 273 return malloc(n); 274 return realloc(p, n); 275} 276#endif 277 278#define NELEM(a) (sizeof(a) / sizeof(a[0])) 279static const char* json_type_name[] = { 280 /* If you change this, be sure to update the enum json_type definition too */ 281 "null", 282 "boolean", 283 "double", 284 "int", 285 "object", 286 "array", 287 "string", 288}; 289 290const char *json_type_to_name(enum json_type o_type) 291{ 292 int o_type_int = (int)o_type; 293 if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) 294 { 295 MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); 296 return NULL; 297 } 298 return json_type_name[o_type]; 299} 300 301