1 // Copyright 2007-2010 Baptiste Lepilleur
2 // Distributed under MIT license, or public domain if desired and
3 // recognized in your jurisdiction.
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
5
6 /* This executable is used for testing parser/writer using real JSON files.
7 */
8
9 #include <json/json.h>
10 #include <algorithm> // sort
11 #include <stdio.h>
12
13 #if defined(_MSC_VER) && _MSC_VER >= 1310
14 #pragma warning(disable : 4996) // disable fopen deprecation warning
15 #endif
16
normalizeFloatingPointStr(double value)17 static std::string normalizeFloatingPointStr(double value) {
18 char buffer[32];
19 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
20 sprintf_s(buffer, sizeof(buffer), "%.16g", value);
21 #else
22 snprintf(buffer, sizeof(buffer), "%.16g", value);
23 #endif
24 buffer[sizeof(buffer) - 1] = 0;
25 std::string s(buffer);
26 std::string::size_type index = s.find_last_of("eE");
27 if (index != std::string::npos) {
28 std::string::size_type hasSign =
29 (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
30 std::string::size_type exponentStartIndex = index + 1 + hasSign;
31 std::string normalized = s.substr(0, exponentStartIndex);
32 std::string::size_type indexDigit =
33 s.find_first_not_of('0', exponentStartIndex);
34 std::string exponent = "0";
35 if (indexDigit !=
36 std::string::npos) // There is an exponent different from 0
37 {
38 exponent = s.substr(indexDigit);
39 }
40 return normalized + exponent;
41 }
42 return s;
43 }
44
readInputTestFile(const char * path)45 static std::string readInputTestFile(const char* path) {
46 FILE* file = fopen(path, "rb");
47 if (!file)
48 return std::string("");
49 fseek(file, 0, SEEK_END);
50 long size = ftell(file);
51 fseek(file, 0, SEEK_SET);
52 std::string text;
53 char* buffer = new char[size + 1];
54 buffer[size] = 0;
55 if (fread(buffer, 1, size, file) == (unsigned long)size)
56 text = buffer;
57 fclose(file);
58 delete[] buffer;
59 return text;
60 }
61
62 static void
printValueTree(FILE * fout,Json::Value & value,const std::string & path=".")63 printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
64 if (value.hasComment(Json::commentBefore)) {
65 fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
66 }
67 switch (value.type()) {
68 case Json::nullValue:
69 fprintf(fout, "%s=null\n", path.c_str());
70 break;
71 case Json::intValue:
72 fprintf(fout,
73 "%s=%s\n",
74 path.c_str(),
75 Json::valueToString(value.asLargestInt()).c_str());
76 break;
77 case Json::uintValue:
78 fprintf(fout,
79 "%s=%s\n",
80 path.c_str(),
81 Json::valueToString(value.asLargestUInt()).c_str());
82 break;
83 case Json::realValue:
84 fprintf(fout,
85 "%s=%s\n",
86 path.c_str(),
87 normalizeFloatingPointStr(value.asDouble()).c_str());
88 break;
89 case Json::stringValue:
90 fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
91 break;
92 case Json::booleanValue:
93 fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
94 break;
95 case Json::arrayValue: {
96 fprintf(fout, "%s=[]\n", path.c_str());
97 int size = value.size();
98 for (int index = 0; index < size; ++index) {
99 static char buffer[16];
100 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
101 sprintf_s(buffer, sizeof(buffer), "[%d]", index);
102 #else
103 snprintf(buffer, sizeof(buffer), "[%d]", index);
104 #endif
105 printValueTree(fout, value[index], path + buffer);
106 }
107 } break;
108 case Json::objectValue: {
109 fprintf(fout, "%s={}\n", path.c_str());
110 Json::Value::Members members(value.getMemberNames());
111 std::sort(members.begin(), members.end());
112 std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
113 for (Json::Value::Members::iterator it = members.begin();
114 it != members.end();
115 ++it) {
116 const std::string& name = *it;
117 printValueTree(fout, value[name], path + suffix + name);
118 }
119 } break;
120 default:
121 break;
122 }
123
124 if (value.hasComment(Json::commentAfter)) {
125 fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
126 }
127 }
128
parseAndSaveValueTree(const std::string & input,const std::string & actual,const std::string & kind,Json::Value & root,const Json::Features & features,bool parseOnly)129 static int parseAndSaveValueTree(const std::string& input,
130 const std::string& actual,
131 const std::string& kind,
132 Json::Value& root,
133 const Json::Features& features,
134 bool parseOnly) {
135 Json::Reader reader(features);
136 bool parsingSuccessful = reader.parse(input, root);
137 if (!parsingSuccessful) {
138 printf("Failed to parse %s file: \n%s\n",
139 kind.c_str(),
140 reader.getFormattedErrorMessages().c_str());
141 return 1;
142 }
143
144 if (!parseOnly) {
145 FILE* factual = fopen(actual.c_str(), "wt");
146 if (!factual) {
147 printf("Failed to create %s actual file.\n", kind.c_str());
148 return 2;
149 }
150 printValueTree(factual, root);
151 fclose(factual);
152 }
153 return 0;
154 }
155
rewriteValueTree(const std::string & rewritePath,const Json::Value & root,std::string & rewrite)156 static int rewriteValueTree(const std::string& rewritePath,
157 const Json::Value& root,
158 std::string& rewrite) {
159 // Json::FastWriter writer;
160 // writer.enableYAMLCompatibility();
161 Json::StyledWriter writer;
162 rewrite = writer.write(root);
163 FILE* fout = fopen(rewritePath.c_str(), "wt");
164 if (!fout) {
165 printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
166 return 2;
167 }
168 fprintf(fout, "%s\n", rewrite.c_str());
169 fclose(fout);
170 return 0;
171 }
172
removeSuffix(const std::string & path,const std::string & extension)173 static std::string removeSuffix(const std::string& path,
174 const std::string& extension) {
175 if (extension.length() >= path.length())
176 return std::string("");
177 std::string suffix = path.substr(path.length() - extension.length());
178 if (suffix != extension)
179 return std::string("");
180 return path.substr(0, path.length() - extension.length());
181 }
182
printConfig()183 static void printConfig() {
184 // Print the configuration used to compile JsonCpp
185 #if defined(JSON_NO_INT64)
186 printf("JSON_NO_INT64=1\n");
187 #else
188 printf("JSON_NO_INT64=0\n");
189 #endif
190 }
191
printUsage(const char * argv[])192 static int printUsage(const char* argv[]) {
193 printf("Usage: %s [--strict] input-json-file", argv[0]);
194 return 3;
195 }
196
parseCommandLine(int argc,const char * argv[],Json::Features & features,std::string & path,bool & parseOnly)197 int parseCommandLine(int argc,
198 const char* argv[],
199 Json::Features& features,
200 std::string& path,
201 bool& parseOnly) {
202 parseOnly = false;
203 if (argc < 2) {
204 return printUsage(argv);
205 }
206
207 int index = 1;
208 if (std::string(argv[1]) == "--json-checker") {
209 features = Json::Features::strictMode();
210 parseOnly = true;
211 ++index;
212 }
213
214 if (std::string(argv[1]) == "--json-config") {
215 printConfig();
216 return 3;
217 }
218
219 if (index == argc || index + 1 < argc) {
220 return printUsage(argv);
221 }
222
223 path = argv[index];
224 return 0;
225 }
226
main(int argc,const char * argv[])227 int main(int argc, const char* argv[]) {
228 std::string path;
229 Json::Features features;
230 bool parseOnly;
231 int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
232 if (exitCode != 0) {
233 return exitCode;
234 }
235
236 try {
237 std::string input = readInputTestFile(path.c_str());
238 if (input.empty()) {
239 printf("Failed to read input or empty input: %s\n", path.c_str());
240 return 3;
241 }
242
243 std::string basePath = removeSuffix(argv[1], ".json");
244 if (!parseOnly && basePath.empty()) {
245 printf("Bad input path. Path does not end with '.expected':\n%s\n",
246 path.c_str());
247 return 3;
248 }
249
250 std::string actualPath = basePath + ".actual";
251 std::string rewritePath = basePath + ".rewrite";
252 std::string rewriteActualPath = basePath + ".actual-rewrite";
253
254 Json::Value root;
255 exitCode = parseAndSaveValueTree(
256 input, actualPath, "input", root, features, parseOnly);
257 if (exitCode == 0 && !parseOnly) {
258 std::string rewrite;
259 exitCode = rewriteValueTree(rewritePath, root, rewrite);
260 if (exitCode == 0) {
261 Json::Value rewriteRoot;
262 exitCode = parseAndSaveValueTree(rewrite,
263 rewriteActualPath,
264 "rewrite",
265 rewriteRoot,
266 features,
267 parseOnly);
268 }
269 }
270 }
271 catch (const std::exception& e) {
272 printf("Unhandled exception:\n%s\n", e.what());
273 exitCode = 1;
274 }
275
276 return exitCode;
277 }
278