Upgrade jsoncpp to 1.9.4

Bug: 170642246
Change-Id: Id1fae5a1b6421117f923c616718ee4b3571231e0
diff --git a/src/jsontestrunner/main.cpp b/src/jsontestrunner/main.cpp
index ba98587..3452c59 100644
--- a/src/jsontestrunner/main.cpp
+++ b/src/jsontestrunner/main.cpp
@@ -1,39 +1,49 @@
-// Copyright 2007-2010 Baptiste Lepilleur
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
 // Distributed under MIT license, or public domain if desired and
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning(disable : 4996)
+#endif
+
 /* This executable is used for testing parser/writer using real JSON files.
  */
 
-#include <json/json.h>
 #include <algorithm> // sort
-#include <stdio.h>
+#include <cstdio>
+#include <iostream>
+#include <json/json.h>
+#include <memory>
+#include <sstream>
 
-#if defined(_MSC_VER) && _MSC_VER >= 1310
-#pragma warning(disable : 4996) // disable fopen deprecation warning
-#endif
+struct Options {
+  Json::String path;
+  Json::Features features;
+  bool parseOnly;
+  using writeFuncType = Json::String (*)(Json::Value const&);
+  writeFuncType write;
+};
 
-static std::string normalizeFloatingPointStr(double value) {
+static Json::String normalizeFloatingPointStr(double value) {
   char buffer[32];
-#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
-  sprintf_s(buffer, sizeof(buffer), "%.16g", value);
-#else
-  snprintf(buffer, sizeof(buffer), "%.16g", value);
-#endif
+  jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value);
   buffer[sizeof(buffer) - 1] = 0;
-  std::string s(buffer);
-  std::string::size_type index = s.find_last_of("eE");
-  if (index != std::string::npos) {
-    std::string::size_type hasSign =
+  Json::String s(buffer);
+  Json::String::size_type index = s.find_last_of("eE");
+  if (index != Json::String::npos) {
+    Json::String::size_type hasSign =
         (s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
-    std::string::size_type exponentStartIndex = index + 1 + hasSign;
-    std::string normalized = s.substr(0, exponentStartIndex);
-    std::string::size_type indexDigit =
+    Json::String::size_type exponentStartIndex = index + 1 + hasSign;
+    Json::String normalized = s.substr(0, exponentStartIndex);
+    Json::String::size_type indexDigit =
         s.find_first_not_of('0', exponentStartIndex);
-    std::string exponent = "0";
-    if (indexDigit !=
-        std::string::npos) // There is an exponent different from 0
+    Json::String exponent = "0";
+    if (indexDigit != Json::String::npos) // There is an exponent different
+                                          // from 0
     {
       exponent = s.substr(indexDigit);
     }
@@ -42,25 +52,26 @@
   return s;
 }
 
-static std::string readInputTestFile(const char* path) {
+static Json::String readInputTestFile(const char* path) {
   FILE* file = fopen(path, "rb");
   if (!file)
-    return std::string("");
+    return "";
   fseek(file, 0, SEEK_END);
-  long size = ftell(file);
+  auto const size = ftell(file);
+  auto const usize = static_cast<size_t>(size);
   fseek(file, 0, SEEK_SET);
-  std::string text;
-  char* buffer = new char[size + 1];
+  auto buffer = new char[size + 1];
   buffer[size] = 0;
-  if (fread(buffer, 1, size, file) == (unsigned long)size)
+  Json::String text;
+  if (fread(buffer, 1, usize, file) == usize)
     text = buffer;
   fclose(file);
   delete[] buffer;
   return text;
 }
 
-static void
-printValueTree(FILE* fout, Json::Value& value, const std::string& path = ".") {
+static void printValueTree(FILE* fout, Json::Value& value,
+                           const Json::String& path = ".") {
   if (value.hasComment(Json::commentBefore)) {
     fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
   }
@@ -69,21 +80,15 @@
     fprintf(fout, "%s=null\n", path.c_str());
     break;
   case Json::intValue:
-    fprintf(fout,
-            "%s=%s\n",
-            path.c_str(),
+    fprintf(fout, "%s=%s\n", path.c_str(),
             Json::valueToString(value.asLargestInt()).c_str());
     break;
   case Json::uintValue:
-    fprintf(fout,
-            "%s=%s\n",
-            path.c_str(),
+    fprintf(fout, "%s=%s\n", path.c_str(),
             Json::valueToString(value.asLargestUInt()).c_str());
     break;
   case Json::realValue:
-    fprintf(fout,
-            "%s=%s\n",
-            path.c_str(),
+    fprintf(fout, "%s=%s\n", path.c_str(),
             normalizeFloatingPointStr(value.asDouble()).c_str());
     break;
   case Json::stringValue:
@@ -94,14 +99,10 @@
     break;
   case Json::arrayValue: {
     fprintf(fout, "%s=[]\n", path.c_str());
-    int size = value.size();
-    for (int index = 0; index < size; ++index) {
+    Json::ArrayIndex size = value.size();
+    for (Json::ArrayIndex index = 0; index < size; ++index) {
       static char buffer[16];
-#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
-      sprintf_s(buffer, sizeof(buffer), "[%d]", index);
-#else
-      snprintf(buffer, sizeof(buffer), "[%d]", index);
-#endif
+      jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index);
       printValueTree(fout, value[index], path + buffer);
     }
   } break;
@@ -109,11 +110,8 @@
     fprintf(fout, "%s={}\n", path.c_str());
     Json::Value::Members members(value.getMemberNames());
     std::sort(members.begin(), members.end());
-    std::string suffix = *(path.end() - 1) == '.' ? "" : ".";
-    for (Json::Value::Members::iterator it = members.begin();
-         it != members.end();
-         ++it) {
-      const std::string& name = *it;
+    Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
+    for (const auto& name : members) {
       printValueTree(fout, value[name], path + suffix + name);
     }
   } break;
@@ -126,152 +124,219 @@
   }
 }
 
-static int parseAndSaveValueTree(const std::string& input,
-                                 const std::string& actual,
-                                 const std::string& kind,
-                                 Json::Value& root,
-                                 const Json::Features& features,
-                                 bool parseOnly) {
-  Json::Reader reader(features);
-  bool parsingSuccessful = reader.parse(input, root);
-  if (!parsingSuccessful) {
-    printf("Failed to parse %s file: \n%s\n",
-           kind.c_str(),
-           reader.getFormattedErrorMessages().c_str());
-    return 1;
+static int parseAndSaveValueTree(const Json::String& input,
+                                 const Json::String& actual,
+                                 const Json::String& kind,
+                                 const Json::Features& features, bool parseOnly,
+                                 Json::Value* root, bool use_legacy) {
+  if (!use_legacy) {
+    Json::CharReaderBuilder builder;
+
+    builder.settings_["allowComments"] = features.allowComments_;
+    builder.settings_["strictRoot"] = features.strictRoot_;
+    builder.settings_["allowDroppedNullPlaceholders"] =
+        features.allowDroppedNullPlaceholders_;
+    builder.settings_["allowNumericKeys"] = features.allowNumericKeys_;
+
+    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+    Json::String errors;
+    const bool parsingSuccessful =
+        reader->parse(input.data(), input.data() + input.size(), root, &errors);
+
+    if (!parsingSuccessful) {
+      std::cerr << "Failed to parse " << kind << " file: " << std::endl
+                << errors << std::endl;
+      return 1;
+    }
+
+    // We may instead check the legacy implementation (to ensure it doesn't
+    // randomly get broken).
+  } else {
+    Json::Reader reader(features);
+    const bool parsingSuccessful =
+        reader.parse(input.data(), input.data() + input.size(), *root);
+    if (!parsingSuccessful) {
+      std::cerr << "Failed to parse " << kind << " file: " << std::endl
+                << reader.getFormatedErrorMessages() << std::endl;
+      return 1;
+    }
   }
 
   if (!parseOnly) {
     FILE* factual = fopen(actual.c_str(), "wt");
     if (!factual) {
-      printf("Failed to create %s actual file.\n", kind.c_str());
+      std::cerr << "Failed to create '" << kind << "' actual file."
+                << std::endl;
       return 2;
     }
-    printValueTree(factual, root);
+    printValueTree(factual, *root);
     fclose(factual);
   }
   return 0;
 }
-
-static int rewriteValueTree(const std::string& rewritePath,
-                            const Json::Value& root,
-                            std::string& rewrite) {
-  // Json::FastWriter writer;
-  // writer.enableYAMLCompatibility();
+// static Json::String useFastWriter(Json::Value const& root) {
+//   Json::FastWriter writer;
+//   writer.enableYAMLCompatibility();
+//   return writer.write(root);
+// }
+static Json::String useStyledWriter(Json::Value const& root) {
   Json::StyledWriter writer;
-  rewrite = writer.write(root);
+  return writer.write(root);
+}
+static Json::String useStyledStreamWriter(Json::Value const& root) {
+  Json::StyledStreamWriter writer;
+  Json::OStringStream sout;
+  writer.write(sout, root);
+  return sout.str();
+}
+static Json::String useBuiltStyledStreamWriter(Json::Value const& root) {
+  Json::StreamWriterBuilder builder;
+  return Json::writeString(builder, root);
+}
+static int rewriteValueTree(const Json::String& rewritePath,
+                            const Json::Value& root,
+                            Options::writeFuncType write,
+                            Json::String* rewrite) {
+  *rewrite = write(root);
   FILE* fout = fopen(rewritePath.c_str(), "wt");
   if (!fout) {
-    printf("Failed to create rewrite file: %s\n", rewritePath.c_str());
+    std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
     return 2;
   }
-  fprintf(fout, "%s\n", rewrite.c_str());
+  fprintf(fout, "%s\n", rewrite->c_str());
   fclose(fout);
   return 0;
 }
 
-static std::string removeSuffix(const std::string& path,
-                                const std::string& extension) {
+static Json::String removeSuffix(const Json::String& path,
+                                 const Json::String& extension) {
   if (extension.length() >= path.length())
-    return std::string("");
-  std::string suffix = path.substr(path.length() - extension.length());
+    return Json::String("");
+  Json::String suffix = path.substr(path.length() - extension.length());
   if (suffix != extension)
-    return std::string("");
+    return Json::String("");
   return path.substr(0, path.length() - extension.length());
 }
 
 static void printConfig() {
 // Print the configuration used to compile JsonCpp
 #if defined(JSON_NO_INT64)
-  printf("JSON_NO_INT64=1\n");
+  std::cout << "JSON_NO_INT64=1" << std::endl;
 #else
-  printf("JSON_NO_INT64=0\n");
+  std::cout << "JSON_NO_INT64=0" << std::endl;
 #endif
 }
 
 static int printUsage(const char* argv[]) {
-  printf("Usage: %s [--strict] input-json-file", argv[0]);
+  std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
+            << std::endl;
   return 3;
 }
 
-int parseCommandLine(int argc,
-                     const char* argv[],
-                     Json::Features& features,
-                     std::string& path,
-                     bool& parseOnly) {
-  parseOnly = false;
+static int parseCommandLine(int argc, const char* argv[], Options* opts) {
+  opts->parseOnly = false;
+  opts->write = &useStyledWriter;
   if (argc < 2) {
     return printUsage(argv);
   }
-
   int index = 1;
-  if (std::string(argv[1]) == "--json-checker") {
-    features = Json::Features::strictMode();
-    parseOnly = true;
+  if (Json::String(argv[index]) == "--json-checker") {
+    opts->features = Json::Features::strictMode();
+    opts->parseOnly = true;
     ++index;
   }
-
-  if (std::string(argv[1]) == "--json-config") {
+  if (Json::String(argv[index]) == "--json-config") {
     printConfig();
     return 3;
   }
-
+  if (Json::String(argv[index]) == "--json-writer") {
+    ++index;
+    Json::String const writerName(argv[index++]);
+    if (writerName == "StyledWriter") {
+      opts->write = &useStyledWriter;
+    } else if (writerName == "StyledStreamWriter") {
+      opts->write = &useStyledStreamWriter;
+    } else if (writerName == "BuiltStyledStreamWriter") {
+      opts->write = &useBuiltStyledStreamWriter;
+    } else {
+      std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
+      return 4;
+    }
+  }
   if (index == argc || index + 1 < argc) {
     return printUsage(argv);
   }
-
-  path = argv[index];
+  opts->path = argv[index];
   return 0;
 }
 
-int main(int argc, const char* argv[]) {
-  std::string path;
-  Json::Features features;
-  bool parseOnly;
-  int exitCode = parseCommandLine(argc, argv, features, path, parseOnly);
-  if (exitCode != 0) {
+static int runTest(Options const& opts, bool use_legacy) {
+  int exitCode = 0;
+
+  Json::String input = readInputTestFile(opts.path.c_str());
+  if (input.empty()) {
+    std::cerr << "Invalid input file: " << opts.path << std::endl;
+    return 3;
+  }
+
+  Json::String basePath = removeSuffix(opts.path, ".json");
+  if (!opts.parseOnly && basePath.empty()) {
+    std::cerr << "Bad input path '" << opts.path
+              << "'. Must end with '.expected'" << std::endl;
+    return 3;
+  }
+
+  Json::String const actualPath = basePath + ".actual";
+  Json::String const rewritePath = basePath + ".rewrite";
+  Json::String const rewriteActualPath = basePath + ".actual-rewrite";
+
+  Json::Value root;
+  exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
+                                   opts.parseOnly, &root, use_legacy);
+  if (exitCode || opts.parseOnly) {
     return exitCode;
   }
 
-  try {
-    std::string input = readInputTestFile(path.c_str());
-    if (input.empty()) {
-      printf("Failed to read input or empty input: %s\n", path.c_str());
-      return 3;
-    }
-
-    std::string basePath = removeSuffix(argv[1], ".json");
-    if (!parseOnly && basePath.empty()) {
-      printf("Bad input path. Path does not end with '.expected':\n%s\n",
-             path.c_str());
-      return 3;
-    }
-
-    std::string actualPath = basePath + ".actual";
-    std::string rewritePath = basePath + ".rewrite";
-    std::string rewriteActualPath = basePath + ".actual-rewrite";
-
-    Json::Value root;
-    exitCode = parseAndSaveValueTree(
-        input, actualPath, "input", root, features, parseOnly);
-    if (exitCode == 0 && !parseOnly) {
-      std::string rewrite;
-      exitCode = rewriteValueTree(rewritePath, root, rewrite);
-      if (exitCode == 0) {
-        Json::Value rewriteRoot;
-        exitCode = parseAndSaveValueTree(rewrite,
-                                         rewriteActualPath,
-                                         "rewrite",
-                                         rewriteRoot,
-                                         features,
-                                         parseOnly);
-      }
-    }
+  Json::String rewrite;
+  exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
+  if (exitCode) {
+    return exitCode;
   }
-  catch (const std::exception& e) {
-    printf("Unhandled exception:\n%s\n", e.what());
-    exitCode = 1;
-  }
+
+  Json::Value rewriteRoot;
+  exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
+                                   opts.features, opts.parseOnly, &rewriteRoot,
+                                   use_legacy);
 
   return exitCode;
 }
+
+int main(int argc, const char* argv[]) {
+  Options opts;
+  try {
+    int exitCode = parseCommandLine(argc, argv, &opts);
+    if (exitCode != 0) {
+      std::cerr << "Failed to parse command-line." << std::endl;
+      return exitCode;
+    }
+
+    const int modern_return_code = runTest(opts, false);
+    if (modern_return_code) {
+      return modern_return_code;
+    }
+
+    const std::string filename =
+        opts.path.substr(opts.path.find_last_of("\\/") + 1);
+    const bool should_run_legacy = (filename.rfind("legacy_", 0) == 0);
+    if (should_run_legacy) {
+      return runTest(opts, true);
+    }
+  } catch (const std::exception& e) {
+    std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
+    return 1;
+  }
+}
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif