float parsing must be locale independent
[platform/upstream/json-c.git] / json_util.c
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 HAVE_LOCALE_H
40 #include <locale.h>
41 #endif /* HAVE_LOCALE_H */
42
43 #ifdef WIN32
44 # define WIN32_LEAN_AND_MEAN
45 # include <windows.h>
46 # include <io.h>
47 #endif /* defined(WIN32) */
48
49 #if !defined(HAVE_OPEN) && defined(WIN32)
50 # define open _open
51 #endif
52
53 #if !defined(HAVE_SNPRINTF) && defined(_MSC_VER)
54   /* MSC has the version as _snprintf */
55 # define snprintf _snprintf
56 #elif !defined(HAVE_SNPRINTF)
57 # error You do not have snprintf on your system.
58 #endif /* HAVE_SNPRINTF */
59
60 #include "bits.h"
61 #include "debug.h"
62 #include "printbuf.h"
63 #include "json_inttypes.h"
64 #include "json_object.h"
65 #include "json_tokener.h"
66 #include "json_util.h"
67
68 struct 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 reading 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_ABORT("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
102 int json_object_to_file_ext(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
144 int json_object_to_file(char *filename, struct json_object *obj)
145 {
146   return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN);
147 }
148
149 int json_parse_double(const char *buf, double *retval)
150 {
151         int ret;
152 #ifdef HAVE_SETLOCALE
153         char *old=NULL, *tmp;
154
155         tmp = setlocale(LC_NUMERIC, NULL);
156         if (tmp) old = strdup(tmp);
157         setlocale(LC_NUMERIC, "C");
158 #endif
159
160         ret = sscanf(buf, "%lf", retval);
161
162 #ifdef HAVE_SETLOCALE
163         setlocale(LC_NUMERIC, old);
164         if (old) free(old);
165 #endif
166
167         return (ret==1 ? 0 : 1);
168 }
169
170 int json_parse_int64(const char *buf, int64_t *retval)
171 {
172         int64_t num64;
173         const char *buf_skip_space;
174         int orig_has_neg;
175         int _errno;
176         errno = 0; // sscanf won't always set errno, so initialize
177         if (sscanf(buf, "%" SCNd64, &num64) != 1)
178         {
179                 MC_DEBUG("Failed to parse, sscanf != 1\n");
180                 return 1;
181         }
182         _errno = errno;
183         buf_skip_space = buf;
184         orig_has_neg = 0;
185         // Skip leading spaces
186         while (isspace((int)*buf_skip_space) && *buf_skip_space)
187                 buf_skip_space++;
188         if (*buf_skip_space == '-')
189         {
190                 buf_skip_space++;
191                 orig_has_neg = 1;
192         }
193         // Skip leading zeros, but keep at least one digit
194         while (buf_skip_space[0] == '0' && buf_skip_space[1] != '\0')
195                 buf_skip_space++;
196         if (buf_skip_space[0] == '0' && buf_skip_space[1] == '\0')
197                 orig_has_neg = 0; // "-0" is the same as just plain "0"
198         
199         if (_errno != ERANGE)
200         {
201                 char buf_cmp[100];
202                 char *buf_cmp_start = buf_cmp;
203                 int recheck_has_neg = 0;
204                 int buf_cmp_len;
205                 snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64);
206                 if (*buf_cmp_start == '-')
207                 {
208                         recheck_has_neg = 1;
209                         buf_cmp_start++;
210                 }
211                 // No need to skip leading spaces or zeros here.
212
213                 buf_cmp_len = strlen(buf_cmp_start);
214                 /**
215                  * If the sign is different, or
216                  * some of the digits are different, or
217                  * there is another digit present in the original string
218                  * then we NOT successfully parsed the value.
219                  */
220                 if (orig_has_neg != recheck_has_neg ||
221                     strncmp(buf_skip_space, buf_cmp_start, strlen(buf_cmp_start)) != 0 ||
222                         (strlen(buf_skip_space) != buf_cmp_len &&
223                          isdigit((int)buf_skip_space[buf_cmp_len])
224                     )
225                    )
226                 {
227                         _errno = ERANGE;
228                 }
229         }
230         if (_errno == ERANGE)
231         {
232                 if (orig_has_neg)
233                         num64 = INT64_MIN;
234                 else
235                         num64 = INT64_MAX;
236         }
237         *retval = num64;
238         return 0;
239 }
240
241 #ifndef HAVE_REALLOC
242 void* rpl_realloc(void* p, size_t n)
243 {
244         if (n == 0)
245                 n = 1;
246         if (p == 0)
247                 return malloc(n);
248         return realloc(p, n);
249 }
250 #endif
251
252 #define NELEM(a)        (sizeof(a) / sizeof(a[0]))
253 static const char* json_type_name[] = {
254   /* If you change this, be sure to update the enum json_type definition too */
255   "null",
256   "boolean",
257   "double",
258   "int",
259   "object",
260   "array",
261   "string",
262 };
263
264 const char *json_type_to_name(enum json_type o_type)
265 {
266         if (o_type < 0 || o_type >= NELEM(json_type_name))
267         {
268                 MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name));
269                 return NULL;
270         }
271         return json_type_name[o_type];
272 }
273