blob: ba985877db4d6a18bebc68d31aef502d8faf184c [file] [log] [blame]
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -04001// 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
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -04009#include <json/json.h>
10#include <algorithm> // sort
11#include <stdio.h>
12
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050013#if defined(_MSC_VER) && _MSC_VER >= 1310
14#pragma warning(disable : 4996) // disable fopen deprecation warning
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040015#endif
16
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050017static 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);
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -040021#else
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -050022 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
45static 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
62static void
63printValueTree(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
129static 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
156static 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
173static 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
183static 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");
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400189#endif
190}
191
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500192static int printUsage(const char* argv[]) {
193 printf("Usage: %s [--strict] input-json-file", argv[0]);
194 return 3;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400195}
196
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500197int 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400206
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500207 int index = 1;
208 if (std::string(argv[1]) == "--json-checker") {
209 features = Json::Features::strictMode();
210 parseOnly = true;
211 ++index;
212 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400213
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500214 if (std::string(argv[1]) == "--json-config") {
215 printConfig();
216 return 3;
217 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400218
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500219 if (index == argc || index + 1 < argc) {
220 return printUsage(argv);
221 }
222
223 path = argv[index];
224 return 0;
225}
226
227int 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());
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400240 return 3;
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500241 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400242
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500243 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 }
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400249
Derek Sollenberger2eb3b4d2016-01-11 14:41:40 -0500250 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;
Leon Scroggins IIIf59fb0e2014-05-28 15:19:42 -0400277}