failIfExtra

1. failing regression tests, from #164 and #107
2. implemented; tests pass
3. allow trailing comments
diff --git a/include/json/reader.h b/include/json/reader.h
index 255ff8e..b59e183 100644
--- a/include/json/reader.h
+++ b/include/json/reader.h
@@ -315,6 +315,9 @@
         cause an exception.
       - This is a security issue (seg-faults caused by deeply nested JSON),
         so the default is low.
+    - `"failIfExtra": false or true`
+      - If true, `parse()` returns false when extra non-whitespace trails
+        the JSON value in the input string.
 
     You can examine 'settings_` yourself
     to see the defaults. You can also write and read them just like any
diff --git a/src/lib_json/json_reader.cpp b/src/lib_json/json_reader.cpp
index 103bf67..2e78ff0 100644
--- a/src/lib_json/json_reader.cpp
+++ b/src/lib_json/json_reader.cpp
@@ -914,6 +914,7 @@
   bool strictRoot_;
   bool allowDroppedNullPlaceholders_;
   bool allowNumericKeys_;
+  bool failIfExtra_;
   int stackLimit_;
 };  // OurFeatures
 
@@ -1083,6 +1084,12 @@
   bool successful = readValue();
   Token token;
   skipCommentTokens(token);
+  if (features_.failIfExtra_) {
+    if (token.type_ != tokenError && token.type_ != tokenEndOfStream) {
+      addError("Extra non-whitespace after JSON value.", token);
+      return false;
+    }
+  }
   if (collectComments_ && !commentsBefore_.empty())
     root.setComment(commentsBefore_, commentAfter);
   if (features_.strictRoot_) {
@@ -1870,6 +1877,7 @@
   features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
   features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
   features.stackLimit_ = settings_["stackLimit"].asInt();
+  features.failIfExtra_ = settings_["failIfExtra"].asBool();
   return new OurCharReader(collectComments, features);
 }
 static void getValidReaderKeys(std::set<std::string>* valid_keys)
@@ -1881,6 +1889,7 @@
   valid_keys->insert("allowDroppedNullPlaceholders");
   valid_keys->insert("allowNumericKeys");
   valid_keys->insert("stackLimit");
+  valid_keys->insert("failIfExtra");
 }
 bool CharReaderBuilder::validate(Json::Value* invalid) const
 {
@@ -1908,6 +1917,7 @@
   (*settings)["strictRoot"] = true;
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
+  (*settings)["failIfExtra"] = true;
 //! [CharReaderBuilderStrictMode]
 }
 // static
@@ -1920,6 +1930,7 @@
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
   (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = false;
 //! [CharReaderBuilderDefaults]
 }
 
diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp
index 30c80e2..bc8b9ae 100644
--- a/src/test_lib_json/main.cpp
+++ b/src/test_lib_json/main.cpp
@@ -1741,6 +1741,126 @@
   }
 }
 
+struct CharReaderFailIfExtraTest : JsonTest::TestCase {};
+
+JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue164) {
+  // This is interpretted as a string value followed by a colon.
+  Json::CharReaderBuilder b;
+  Json::Value root;
+  char const doc[] =
+      " \"property\" : \"value\" }";
+  {
+  b.settings_["failIfExtra"] = false;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(ok);
+  JSONTEST_ASSERT(errs == "");
+  JSONTEST_ASSERT_EQUAL("property", root);
+  delete reader;
+  }
+  {
+  b.settings_["failIfExtra"] = true;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(!ok);
+  JSONTEST_ASSERT_STRING_EQUAL(errs,
+      "* Line 1, Column 13\n"
+      "  Extra non-whitespace after JSON value.\n");
+  JSONTEST_ASSERT_EQUAL("property", root);
+  delete reader;
+  }
+  {
+  b.settings_["failIfExtra"] = false;
+  b.strictMode(&b.settings_);
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(!ok);
+  JSONTEST_ASSERT_STRING_EQUAL(errs,
+      "* Line 1, Column 13\n"
+      "  Extra non-whitespace after JSON value.\n");
+  JSONTEST_ASSERT_EQUAL("property", root);
+  delete reader;
+  }
+}
+JSONTEST_FIXTURE(CharReaderFailIfExtraTest, issue107) {
+  // This is interpretted as an int value followed by a colon.
+  Json::CharReaderBuilder b;
+  Json::Value root;
+  char const doc[] =
+      "1:2:3";
+  b.settings_["failIfExtra"] = true;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(!ok);
+  JSONTEST_ASSERT_STRING_EQUAL(
+      "* Line 1, Column 2\n"
+      "  Extra non-whitespace after JSON value.\n",
+      errs);
+  JSONTEST_ASSERT_EQUAL(1, root.asInt());
+  delete reader;
+}
+JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterObject) {
+  Json::CharReaderBuilder b;
+  Json::Value root;
+  {
+  char const doc[] =
+      "{ \"property\" : \"value\" } //trailing\n//comment\n";
+  b.settings_["failIfExtra"] = true;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(ok);
+  JSONTEST_ASSERT_STRING_EQUAL("", errs);
+  JSONTEST_ASSERT_EQUAL("value", root["property"]);
+  delete reader;
+  }
+}
+JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterArray) {
+  Json::CharReaderBuilder b;
+  Json::Value root;
+  char const doc[] =
+      "[ \"property\" , \"value\" ] //trailing\n//comment\n";
+  b.settings_["failIfExtra"] = true;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(ok);
+  JSONTEST_ASSERT_STRING_EQUAL("", errs);
+  JSONTEST_ASSERT_EQUAL("value", root[1u]);
+  delete reader;
+}
+JSONTEST_FIXTURE(CharReaderFailIfExtraTest, commentAfterBool) {
+  Json::CharReaderBuilder b;
+  Json::Value root;
+  char const doc[] =
+      " true /*trailing\ncomment*/";
+  b.settings_["failIfExtra"] = true;
+  Json::CharReader* reader(b.newCharReader());
+  std::string errs;
+  bool ok = reader->parse(
+      doc, doc + std::strlen(doc),
+      &root, &errs);
+  JSONTEST_ASSERT(ok);
+  JSONTEST_ASSERT_STRING_EQUAL("", errs);
+  JSONTEST_ASSERT_EQUAL(true, root.asBool());
+  delete reader;
+}
 int main(int argc, const char* argv[]) {
   JsonTest::Runner runner;
   JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
@@ -1779,6 +1899,12 @@
   JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
   JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit);
 
+  JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue164);
+  JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, issue107);
+  JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterObject);
+  JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterArray);
+  JSONTEST_REGISTER_FIXTURE(runner, CharReaderFailIfExtraTest, commentAfterBool);
+
   JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
   JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);