2017-07-21 18:44:36 +08:00
|
|
|
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
2010-04-21 05:35:19 +08:00
|
|
|
// 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
|
|
|
|
|
2017-11-10 17:58:43 +08:00
|
|
|
#if defined(__GNUC__)
|
2017-08-28 21:38:29 +08:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
2017-11-10 17:58:43 +08:00
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
#pragma warning(disable : 4996)
|
|
|
|
#endif
|
2017-08-28 21:38:29 +08:00
|
|
|
|
2010-12-24 20:47:14 +08:00
|
|
|
/* This executable is used for testing parser/writer using real JSON files.
|
|
|
|
*/
|
|
|
|
|
2007-06-15 05:01:26 +08:00
|
|
|
#include <algorithm> // sort
|
2019-01-18 00:07:53 +08:00
|
|
|
#include <cstdio>
|
2019-11-09 11:49:16 +08:00
|
|
|
#include <iostream>
|
2018-05-21 04:55:27 +08:00
|
|
|
#include <json/json.h>
|
2019-11-09 11:49:16 +08:00
|
|
|
#include <memory>
|
2015-01-23 23:02:44 +08:00
|
|
|
#include <sstream>
|
2007-06-15 05:01:26 +08:00
|
|
|
|
2018-05-21 04:55:27 +08:00
|
|
|
struct Options {
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String path;
|
2015-01-24 01:46:05 +08:00
|
|
|
Json::Features features;
|
|
|
|
bool parseOnly;
|
2019-01-18 16:46:57 +08:00
|
|
|
using writeFuncType = Json::String (*)(Json::Value const&);
|
2015-01-24 01:46:05 +08:00
|
|
|
writeFuncType write;
|
|
|
|
};
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
static Json::String normalizeFloatingPointStr(double value) {
|
2014-07-01 06:48:54 +08:00
|
|
|
char buffer[32];
|
2018-12-13 03:31:55 +08:00
|
|
|
jsoncpp_snprintf(buffer, sizeof(buffer), "%.16g", value);
|
2014-07-01 06:48:54 +08:00
|
|
|
buffer[sizeof(buffer) - 1] = 0;
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String s(buffer);
|
|
|
|
Json::String::size_type index = s.find_last_of("eE");
|
|
|
|
if (index != Json::String::npos) {
|
|
|
|
Json::String::size_type hasSign =
|
2014-07-01 06:48:54 +08:00
|
|
|
(s[index + 1] == '+' || s[index + 1] == '-') ? 1 : 0;
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String::size_type exponentStartIndex = index + 1 + hasSign;
|
|
|
|
Json::String normalized = s.substr(0, exponentStartIndex);
|
2019-01-18 16:46:57 +08:00
|
|
|
Json::String::size_type indexDigit =
|
|
|
|
s.find_first_not_of('0', exponentStartIndex);
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String exponent = "0";
|
|
|
|
if (indexDigit != Json::String::npos) // There is an exponent different
|
2019-01-18 16:46:57 +08:00
|
|
|
// from 0
|
2011-05-27 04:14:32 +08:00
|
|
|
{
|
2014-07-01 06:48:54 +08:00
|
|
|
exponent = s.substr(indexDigit);
|
2011-05-27 04:14:32 +08:00
|
|
|
}
|
2014-07-01 06:48:54 +08:00
|
|
|
return normalized + exponent;
|
|
|
|
}
|
|
|
|
return s;
|
2011-05-27 04:14:32 +08:00
|
|
|
}
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
static Json::String readInputTestFile(const char* path) {
|
2014-09-15 08:15:29 +08:00
|
|
|
FILE* file = fopen(path, "rb");
|
2014-07-01 06:48:54 +08:00
|
|
|
if (!file)
|
2019-01-18 05:35:29 +08:00
|
|
|
return "";
|
2014-07-01 06:48:54 +08:00
|
|
|
fseek(file, 0, SEEK_END);
|
2020-02-03 12:03:45 +08:00
|
|
|
auto const size = ftell(file);
|
|
|
|
auto const usize = static_cast<size_t>(size);
|
2014-07-01 06:48:54 +08:00
|
|
|
fseek(file, 0, SEEK_SET);
|
2020-02-03 12:03:45 +08:00
|
|
|
auto buffer = new char[size + 1];
|
2014-07-01 06:48:54 +08:00
|
|
|
buffer[size] = 0;
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String text;
|
2016-02-06 23:25:20 +08:00
|
|
|
if (fread(buffer, 1, usize, file) == usize)
|
2014-07-01 06:48:54 +08:00
|
|
|
text = buffer;
|
|
|
|
fclose(file);
|
|
|
|
delete[] buffer;
|
|
|
|
return text;
|
2007-06-15 05:01:26 +08:00
|
|
|
}
|
|
|
|
|
2019-10-12 02:19:00 +08:00
|
|
|
static void printValueTree(FILE* fout, Json::Value& value,
|
|
|
|
const Json::String& path = ".") {
|
2014-10-10 04:29:47 +08:00
|
|
|
if (value.hasComment(Json::commentBefore)) {
|
|
|
|
fprintf(fout, "%s\n", value.getComment(Json::commentBefore).c_str());
|
|
|
|
}
|
2014-07-01 06:48:54 +08:00
|
|
|
switch (value.type()) {
|
|
|
|
case Json::nullValue:
|
|
|
|
fprintf(fout, "%s=null\n", path.c_str());
|
|
|
|
break;
|
|
|
|
case Json::intValue:
|
2018-05-21 04:55:27 +08:00
|
|
|
fprintf(fout, "%s=%s\n", path.c_str(),
|
2014-07-01 06:48:54 +08:00
|
|
|
Json::valueToString(value.asLargestInt()).c_str());
|
|
|
|
break;
|
|
|
|
case Json::uintValue:
|
2018-05-21 04:55:27 +08:00
|
|
|
fprintf(fout, "%s=%s\n", path.c_str(),
|
2014-07-01 06:48:54 +08:00
|
|
|
Json::valueToString(value.asLargestUInt()).c_str());
|
|
|
|
break;
|
|
|
|
case Json::realValue:
|
2018-05-21 04:55:27 +08:00
|
|
|
fprintf(fout, "%s=%s\n", path.c_str(),
|
2014-07-01 06:48:54 +08:00
|
|
|
normalizeFloatingPointStr(value.asDouble()).c_str());
|
|
|
|
break;
|
|
|
|
case Json::stringValue:
|
|
|
|
fprintf(fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str());
|
|
|
|
break;
|
|
|
|
case Json::booleanValue:
|
|
|
|
fprintf(fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false");
|
|
|
|
break;
|
|
|
|
case Json::arrayValue: {
|
|
|
|
fprintf(fout, "%s=[]\n", path.c_str());
|
2016-02-06 23:25:20 +08:00
|
|
|
Json::ArrayIndex size = value.size();
|
|
|
|
for (Json::ArrayIndex index = 0; index < size; ++index) {
|
2014-07-01 06:48:54 +08:00
|
|
|
static char buffer[16];
|
2018-12-13 03:31:55 +08:00
|
|
|
jsoncpp_snprintf(buffer, sizeof(buffer), "[%u]", index);
|
2014-07-01 06:48:54 +08:00
|
|
|
printValueTree(fout, value[index], path + buffer);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case Json::objectValue: {
|
|
|
|
fprintf(fout, "%s={}\n", path.c_str());
|
|
|
|
Json::Value::Members members(value.getMemberNames());
|
|
|
|
std::sort(members.begin(), members.end());
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String suffix = *(path.end() - 1) == '.' ? "" : ".";
|
2020-04-12 13:26:04 +08:00
|
|
|
for (const auto& name : members) {
|
2014-07-01 06:48:54 +08:00
|
|
|
printValueTree(fout, value[name], path + suffix + name);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2014-10-10 04:29:47 +08:00
|
|
|
|
|
|
|
if (value.hasComment(Json::commentAfter)) {
|
|
|
|
fprintf(fout, "%s\n", value.getComment(Json::commentAfter).c_str());
|
|
|
|
}
|
2007-06-15 05:01:26 +08:00
|
|
|
}
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
static int parseAndSaveValueTree(const Json::String& input,
|
|
|
|
const Json::String& actual,
|
|
|
|
const Json::String& kind,
|
2019-10-12 02:19:00 +08:00
|
|
|
const Json::Features& features, bool parseOnly,
|
2019-11-09 11:49:16 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-07-01 06:48:54 +08:00
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
2014-07-01 06:48:54 +08:00
|
|
|
if (!parseOnly) {
|
2014-09-15 08:15:29 +08:00
|
|
|
FILE* factual = fopen(actual.c_str(), "wt");
|
2014-07-01 06:48:54 +08:00
|
|
|
if (!factual) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Failed to create '" << kind << "' actual file."
|
|
|
|
<< std::endl;
|
2007-06-15 05:01:26 +08:00
|
|
|
return 2;
|
2014-07-01 06:48:54 +08:00
|
|
|
}
|
2015-01-24 01:09:04 +08:00
|
|
|
printValueTree(factual, *root);
|
2014-07-01 06:48:54 +08:00
|
|
|
fclose(factual);
|
|
|
|
}
|
|
|
|
return 0;
|
2007-06-15 05:01:26 +08:00
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
// static Json::String useFastWriter(Json::Value const& root) {
|
2015-01-24 01:27:19 +08:00
|
|
|
// Json::FastWriter writer;
|
|
|
|
// writer.enableYAMLCompatibility();
|
|
|
|
// return writer.write(root);
|
|
|
|
// }
|
2019-01-18 05:35:29 +08:00
|
|
|
static Json::String useStyledWriter(Json::Value const& root) {
|
2015-01-24 01:27:19 +08:00
|
|
|
Json::StyledWriter writer;
|
|
|
|
return writer.write(root);
|
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
static Json::String useStyledStreamWriter(Json::Value const& root) {
|
2015-01-23 23:02:44 +08:00
|
|
|
Json::StyledStreamWriter writer;
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::OStringStream sout;
|
2015-01-23 23:02:44 +08:00
|
|
|
writer.write(sout, root);
|
2015-01-24 01:27:19 +08:00
|
|
|
return sout.str();
|
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
static Json::String useBuiltStyledStreamWriter(Json::Value const& root) {
|
2015-01-27 01:01:15 +08:00
|
|
|
Json::StreamWriterBuilder builder;
|
2015-02-10 05:25:57 +08:00
|
|
|
return Json::writeString(builder, root);
|
2015-01-23 22:38:32 +08:00
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
static int rewriteValueTree(const Json::String& rewritePath,
|
2018-05-21 04:55:27 +08:00
|
|
|
const Json::Value& root,
|
|
|
|
Options::writeFuncType write,
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String* rewrite) {
|
2015-01-24 01:46:05 +08:00
|
|
|
*rewrite = write(root);
|
2014-09-15 08:15:29 +08:00
|
|
|
FILE* fout = fopen(rewritePath.c_str(), "wt");
|
2014-07-01 06:48:54 +08:00
|
|
|
if (!fout) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Failed to create rewrite file: " << rewritePath << std::endl;
|
2014-07-01 06:48:54 +08:00
|
|
|
return 2;
|
|
|
|
}
|
2015-01-24 01:09:04 +08:00
|
|
|
fprintf(fout, "%s\n", rewrite->c_str());
|
2014-07-01 06:48:54 +08:00
|
|
|
fclose(fout);
|
|
|
|
return 0;
|
2007-06-15 05:01:26 +08:00
|
|
|
}
|
|
|
|
|
2019-01-18 16:46:57 +08:00
|
|
|
static Json::String removeSuffix(const Json::String& path,
|
|
|
|
const Json::String& extension) {
|
2014-07-01 06:48:54 +08:00
|
|
|
if (extension.length() >= path.length())
|
2019-01-18 05:35:29 +08:00
|
|
|
return Json::String("");
|
|
|
|
Json::String suffix = path.substr(path.length() - extension.length());
|
2014-07-01 06:48:54 +08:00
|
|
|
if (suffix != extension)
|
2019-01-18 05:35:29 +08:00
|
|
|
return Json::String("");
|
2014-07-01 06:48:54 +08:00
|
|
|
return path.substr(0, path.length() - extension.length());
|
|
|
|
}
|
2010-04-19 15:37:41 +08:00
|
|
|
|
2014-07-01 06:48:54 +08:00
|
|
|
static void printConfig() {
|
|
|
|
// Print the configuration used to compile JsonCpp
|
2010-04-19 15:37:41 +08:00
|
|
|
#if defined(JSON_NO_INT64)
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cout << "JSON_NO_INT64=1" << std::endl;
|
2010-04-19 15:37:41 +08:00
|
|
|
#else
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cout << "JSON_NO_INT64=0" << std::endl;
|
2010-04-19 15:37:41 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-09-15 08:15:29 +08:00
|
|
|
static int printUsage(const char* argv[]) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cout << "Usage: " << argv[0] << " [--strict] input-json-file"
|
|
|
|
<< std::endl;
|
2014-07-01 06:48:54 +08:00
|
|
|
return 3;
|
2009-11-19 05:38:54 +08:00
|
|
|
}
|
|
|
|
|
2018-05-21 04:55:27 +08:00
|
|
|
static int parseCommandLine(int argc, const char* argv[], Options* opts) {
|
2015-01-24 01:27:19 +08:00
|
|
|
opts->parseOnly = false;
|
2015-01-24 01:46:05 +08:00
|
|
|
opts->write = &useStyledWriter;
|
2014-07-01 06:48:54 +08:00
|
|
|
if (argc < 2) {
|
|
|
|
return printUsage(argv);
|
|
|
|
}
|
|
|
|
int index = 1;
|
2024-09-10 08:30:16 +08:00
|
|
|
if (Json::String(argv[index]) == "--parse-only") {
|
2015-01-24 01:27:19 +08:00
|
|
|
opts->parseOnly = true;
|
2014-07-01 06:48:54 +08:00
|
|
|
++index;
|
|
|
|
}
|
2024-09-10 08:30:16 +08:00
|
|
|
if (Json::String(argv[index]) == "--strict") {
|
|
|
|
opts->features = Json::Features::strictMode();
|
|
|
|
++index;
|
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
if (Json::String(argv[index]) == "--json-config") {
|
2014-07-01 06:48:54 +08:00
|
|
|
printConfig();
|
|
|
|
return 3;
|
|
|
|
}
|
2019-01-18 05:35:29 +08:00
|
|
|
if (Json::String(argv[index]) == "--json-writer") {
|
2015-01-24 01:46:05 +08:00
|
|
|
++index;
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String const writerName(argv[index++]);
|
2015-01-24 01:46:05 +08:00
|
|
|
if (writerName == "StyledWriter") {
|
|
|
|
opts->write = &useStyledWriter;
|
|
|
|
} else if (writerName == "StyledStreamWriter") {
|
|
|
|
opts->write = &useStyledStreamWriter;
|
2015-01-23 22:38:32 +08:00
|
|
|
} else if (writerName == "BuiltStyledStreamWriter") {
|
|
|
|
opts->write = &useBuiltStyledStreamWriter;
|
2015-01-24 01:46:05 +08:00
|
|
|
} else {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Unknown '--json-writer' " << writerName << std::endl;
|
2015-01-24 01:46:05 +08:00
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
}
|
2014-07-01 06:48:54 +08:00
|
|
|
if (index == argc || index + 1 < argc) {
|
|
|
|
return printUsage(argv);
|
|
|
|
}
|
2015-01-24 01:27:19 +08:00
|
|
|
opts->path = argv[index];
|
2014-07-01 06:48:54 +08:00
|
|
|
return 0;
|
2009-11-19 05:38:54 +08:00
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
|
|
|
static int runTest(Options const& opts, bool use_legacy) {
|
2015-01-24 01:36:55 +08:00
|
|
|
int exitCode = 0;
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String input = readInputTestFile(opts.path.c_str());
|
2015-01-24 01:36:55 +08:00
|
|
|
if (input.empty()) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Invalid input file: " << opts.path << std::endl;
|
2015-01-24 01:36:55 +08:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String basePath = removeSuffix(opts.path, ".json");
|
2015-01-24 01:36:55 +08:00
|
|
|
if (!opts.parseOnly && basePath.empty()) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Bad input path '" << opts.path
|
|
|
|
<< "'. Must end with '.expected'" << std::endl;
|
2015-01-24 01:36:55 +08:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String const actualPath = basePath + ".actual";
|
|
|
|
Json::String const rewritePath = basePath + ".rewrite";
|
|
|
|
Json::String const rewriteActualPath = basePath + ".actual-rewrite";
|
2015-01-24 01:36:55 +08:00
|
|
|
|
|
|
|
Json::Value root;
|
2018-05-21 04:55:27 +08:00
|
|
|
exitCode = parseAndSaveValueTree(input, actualPath, "input", opts.features,
|
2019-11-09 11:49:16 +08:00
|
|
|
opts.parseOnly, &root, use_legacy);
|
2015-01-24 01:36:55 +08:00
|
|
|
if (exitCode || opts.parseOnly) {
|
|
|
|
return exitCode;
|
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
2019-01-18 05:35:29 +08:00
|
|
|
Json::String rewrite;
|
2015-01-24 01:46:05 +08:00
|
|
|
exitCode = rewriteValueTree(rewritePath, root, opts.write, &rewrite);
|
2015-01-24 01:36:55 +08:00
|
|
|
if (exitCode) {
|
|
|
|
return exitCode;
|
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
2015-01-24 01:36:55 +08:00
|
|
|
Json::Value rewriteRoot;
|
2018-05-21 04:55:27 +08:00
|
|
|
exitCode = parseAndSaveValueTree(rewrite, rewriteActualPath, "rewrite",
|
2019-11-09 11:49:16 +08:00
|
|
|
opts.features, opts.parseOnly, &rewriteRoot,
|
|
|
|
use_legacy);
|
|
|
|
|
|
|
|
return exitCode;
|
2015-01-24 01:27:19 +08:00
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
2014-09-15 08:15:29 +08:00
|
|
|
int main(int argc, const char* argv[]) {
|
2015-01-24 01:27:19 +08:00
|
|
|
Options opts;
|
2015-09-22 16:23:19 +08:00
|
|
|
try {
|
2018-05-21 04:55:27 +08:00
|
|
|
int exitCode = parseCommandLine(argc, argv, &opts);
|
|
|
|
if (exitCode != 0) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Failed to parse command-line." << std::endl;
|
2018-05-21 04:55:27 +08:00
|
|
|
return exitCode;
|
|
|
|
}
|
2019-11-09 11:49:16 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2018-05-21 04:55:27 +08:00
|
|
|
} catch (const std::exception& e) {
|
2019-11-09 11:49:16 +08:00
|
|
|
std::cerr << "Unhandled exception:" << std::endl << e.what() << std::endl;
|
2015-01-24 01:36:55 +08:00
|
|
|
return 1;
|
2014-07-01 06:48:54 +08:00
|
|
|
}
|
2021-01-15 18:19:59 +08:00
|
|
|
return 0;
|
2007-06-15 05:01:26 +08:00
|
|
|
}
|
2017-08-28 21:38:29 +08:00
|
|
|
|
2017-11-10 17:58:43 +08:00
|
|
|
#if defined(__GNUC__)
|
2017-08-28 21:38:29 +08:00
|
|
|
#pragma GCC diagnostic pop
|
2017-11-10 17:58:43 +08:00
|
|
|
#endif
|