| /* | |
| * Copyright 2016 Google Inc. All rights reserved. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // clang-format off | |
| // Dont't remove `format off`, it prevent reordering of win-includes. | |
| #include <cstring> | |
| #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) || \ | |
| defined(__QNXNTO__) | |
| # define _POSIX_C_SOURCE 200809L | |
| # define _XOPEN_SOURCE 700L | |
| #endif | |
| #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) | |
| # ifndef WIN32_LEAN_AND_MEAN | |
| # define WIN32_LEAN_AND_MEAN | |
| # endif | |
| # ifndef NOMINMAX | |
| # define NOMINMAX | |
| # endif | |
| # ifdef _MSC_VER | |
| # include <crtdbg.h> | |
| # endif | |
| # include <windows.h> // Must be included before <direct.h> | |
| # ifndef __CYGWIN__ | |
| # include <direct.h> | |
| # endif | |
| # include <winbase.h> | |
| # undef interface // This is also important because of reasons | |
| #endif | |
| // clang-format on | |
| #include "flatbuffers/util.h" | |
| #include <sys/stat.h> | |
| #include <clocale> | |
| #include <cstdlib> | |
| #include <fstream> | |
| #include <functional> | |
| #include "flatbuffers/base.h" | |
| namespace flatbuffers { | |
| namespace { | |
| static bool FileExistsRaw(const char *name) { | |
| std::ifstream ifs(name); | |
| return ifs.good(); | |
| } | |
| static bool LoadFileRaw(const char *name, bool binary, std::string *buf) { | |
| if (DirExists(name)) return false; | |
| std::ifstream ifs(name, binary ? std::ifstream::binary : std::ifstream::in); | |
| if (!ifs.is_open()) return false; | |
| if (binary) { | |
| // The fastest way to read a file into a string. | |
| ifs.seekg(0, std::ios::end); | |
| auto size = ifs.tellg(); | |
| (*buf).resize(static_cast<size_t>(size)); | |
| ifs.seekg(0, std::ios::beg); | |
| ifs.read(&(*buf)[0], (*buf).size()); | |
| } else { | |
| // This is slower, but works correctly on all platforms for text files. | |
| std::ostringstream oss; | |
| oss << ifs.rdbuf(); | |
| *buf = oss.str(); | |
| } | |
| return !ifs.bad(); | |
| } | |
| LoadFileFunction g_load_file_function = LoadFileRaw; | |
| FileExistsFunction g_file_exists_function = FileExistsRaw; | |
| static std::string ToCamelCase(const std::string &input, bool first) { | |
| std::string s; | |
| for (size_t i = 0; i < input.length(); i++) { | |
| if (!i && first) | |
| s += CharToUpper(input[i]); | |
| else if (input[i] == '_' && i + 1 < input.length()) | |
| s += CharToUpper(input[++i]); | |
| else | |
| s += input[i]; | |
| } | |
| return s; | |
| } | |
| static std::string ToSnakeCase(const std::string &input, bool screaming) { | |
| std::string s; | |
| for (size_t i = 0; i < input.length(); i++) { | |
| if (i == 0) { | |
| s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]); | |
| } else if (input[i] == '_') { | |
| s += '_'; | |
| } else if (!islower(input[i])) { | |
| // Prevent duplicate underscores for Upper_Snake_Case strings | |
| // and UPPERCASE strings. | |
| if (islower(input[i - 1]) || (isdigit(input[i-1]) && !isdigit(input[i]))) { s += '_'; } | |
| s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]); | |
| } else { | |
| s += screaming ? CharToUpper(input[i]) : input[i]; | |
| } | |
| } | |
| return s; | |
| } | |
| std::string ToAll(const std::string &input, | |
| std::function<char(const char)> transform) { | |
| std::string s; | |
| for (size_t i = 0; i < input.length(); i++) { s += transform(input[i]); } | |
| return s; | |
| } | |
| std::string CamelToSnake(const std::string &input) { | |
| std::string s; | |
| for (size_t i = 0; i < input.length(); i++) { | |
| if (i == 0) { | |
| s += CharToLower(input[i]); | |
| } else if (input[i] == '_') { | |
| s += '_'; | |
| } else if (!islower(input[i])) { | |
| // Prevent duplicate underscores for Upper_Snake_Case strings | |
| // and UPPERCASE strings. | |
| if (islower(input[i - 1]) || (isdigit(input[i-1]) && !isdigit(input[i]))) { s += '_'; } | |
| s += CharToLower(input[i]); | |
| } else { | |
| s += input[i]; | |
| } | |
| } | |
| return s; | |
| } | |
| std::string DasherToSnake(const std::string &input) { | |
| std::string s; | |
| for (size_t i = 0; i < input.length(); i++) { | |
| if (input[i] == '-') { | |
| s += "_"; | |
| } else { | |
| s += input[i]; | |
| } | |
| } | |
| return s; | |
| } | |
| std::string ToDasher(const std::string &input) { | |
| std::string s; | |
| char p = 0; | |
| for (size_t i = 0; i < input.length(); i++) { | |
| char const &c = input[i]; | |
| if (c == '_') { | |
| if (i > 0 && p != kPathSeparator && | |
| // The following is a special case to ignore digits after a _. This is | |
| // because ThisExample3 would be converted to this_example_3 in the | |
| // CamelToSnake conversion, and then dasher would do this-example-3, | |
| // but it expects this-example3. | |
| !(i + 1 < input.length() && isdigit(input[i + 1]))) | |
| s += "-"; | |
| } else { | |
| s += c; | |
| } | |
| p = c; | |
| } | |
| return s; | |
| } | |
| // Converts foo_bar_123baz_456 to foo_bar123_baz456 | |
| std::string SnakeToSnake2(const std::string &s) { | |
| if (s.length() <= 1) return s; | |
| std::string result; | |
| result.reserve(s.size()); | |
| for (size_t i = 0; i < s.length() - 1; i++) { | |
| if (s[i] == '_' && isdigit(s[i + 1])) { | |
| continue; // Move the `_` until after the digits. | |
| } | |
| result.push_back(s[i]); | |
| if (isdigit(s[i]) && isalpha(s[i + 1]) && islower(s[i + 1])) { | |
| result.push_back('_'); | |
| } | |
| } | |
| result.push_back(s.back()); | |
| return result; | |
| } | |
| } // namespace | |
| bool LoadFile(const char *name, bool binary, std::string *buf) { | |
| FLATBUFFERS_ASSERT(g_load_file_function); | |
| return g_load_file_function(name, binary, buf); | |
| } | |
| bool FileExists(const char *name) { | |
| FLATBUFFERS_ASSERT(g_file_exists_function); | |
| return g_file_exists_function(name); | |
| } | |
| bool DirExists(const char *name) { | |
| // clang-format off | |
| #ifdef _WIN32 | |
| #define flatbuffers_stat _stat | |
| #define FLATBUFFERS_S_IFDIR _S_IFDIR | |
| #else | |
| #define flatbuffers_stat stat | |
| #define FLATBUFFERS_S_IFDIR S_IFDIR | |
| #endif | |
| // clang-format on | |
| struct flatbuffers_stat file_info; | |
| if (flatbuffers_stat(name, &file_info) != 0) return false; | |
| return (file_info.st_mode & FLATBUFFERS_S_IFDIR) != 0; | |
| } | |
| LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function) { | |
| LoadFileFunction previous_function = g_load_file_function; | |
| g_load_file_function = load_file_function ? load_file_function : LoadFileRaw; | |
| return previous_function; | |
| } | |
| FileExistsFunction SetFileExistsFunction( | |
| FileExistsFunction file_exists_function) { | |
| FileExistsFunction previous_function = g_file_exists_function; | |
| g_file_exists_function = | |
| file_exists_function ? file_exists_function : FileExistsRaw; | |
| return previous_function; | |
| } | |
| bool SaveFile(const char *name, const char *buf, size_t len, bool binary) { | |
| std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out); | |
| if (!ofs.is_open()) return false; | |
| ofs.write(buf, len); | |
| return !ofs.bad(); | |
| } | |
| // We internally store paths in posix format ('/'). Paths supplied | |
| // by the user should go through PosixPath to ensure correct behavior | |
| // on Windows when paths are string-compared. | |
| static const char kPathSeparatorWindows = '\\'; | |
| static const char *PathSeparatorSet = "\\/"; // Intentionally no ':' | |
| std::string StripExtension(const std::string &filepath) { | |
| size_t i = filepath.find_last_of('.'); | |
| return i != std::string::npos ? filepath.substr(0, i) : filepath; | |
| } | |
| std::string GetExtension(const std::string &filepath) { | |
| size_t i = filepath.find_last_of('.'); | |
| return i != std::string::npos ? filepath.substr(i + 1) : ""; | |
| } | |
| std::string StripPath(const std::string &filepath) { | |
| size_t i = filepath.find_last_of(PathSeparatorSet); | |
| return i != std::string::npos ? filepath.substr(i + 1) : filepath; | |
| } | |
| std::string StripFileName(const std::string &filepath) { | |
| size_t i = filepath.find_last_of(PathSeparatorSet); | |
| return i != std::string::npos ? filepath.substr(0, i) : ""; | |
| } | |
| std::string StripPrefix(const std::string &filepath, | |
| const std::string &prefix_to_remove) { | |
| if (!strncmp(filepath.c_str(), prefix_to_remove.c_str(), | |
| prefix_to_remove.size())) { | |
| return filepath.substr(prefix_to_remove.size()); | |
| } | |
| return filepath; | |
| } | |
| std::string ConCatPathFileName(const std::string &path, | |
| const std::string &filename) { | |
| std::string filepath = path; | |
| if (filepath.length()) { | |
| char &filepath_last_character = filepath.back(); | |
| if (filepath_last_character == kPathSeparatorWindows) { | |
| filepath_last_character = kPathSeparator; | |
| } else if (filepath_last_character != kPathSeparator) { | |
| filepath += kPathSeparator; | |
| } | |
| } | |
| filepath += filename; | |
| // Ignore './' at the start of filepath. | |
| if (filepath[0] == '.' && filepath[1] == kPathSeparator) { | |
| filepath.erase(0, 2); | |
| } | |
| return filepath; | |
| } | |
| std::string PosixPath(const char *path) { | |
| std::string p = path; | |
| std::replace(p.begin(), p.end(), '\\', '/'); | |
| return p; | |
| } | |
| std::string PosixPath(const std::string &path) { | |
| return PosixPath(path.c_str()); | |
| } | |
| void EnsureDirExists(const std::string &filepath) { | |
| auto parent = StripFileName(filepath); | |
| if (parent.length()) EnsureDirExists(parent); | |
| // clang-format off | |
| #ifdef _WIN32 | |
| (void)_mkdir(filepath.c_str()); | |
| #else | |
| mkdir(filepath.c_str(), S_IRWXU|S_IRGRP|S_IXGRP); | |
| #endif | |
| // clang-format on | |
| } | |
| std::string AbsolutePath(const std::string &filepath) { | |
| // clang-format off | |
| #ifdef FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION | |
| return filepath; | |
| #else | |
| #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) | |
| char abs_path[MAX_PATH]; | |
| return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr) | |
| #else | |
| char *abs_path_temp = realpath(filepath.c_str(), nullptr); | |
| bool success = abs_path_temp != nullptr; | |
| std::string abs_path; | |
| if(success) { | |
| abs_path = abs_path_temp; | |
| free(abs_path_temp); | |
| } | |
| return success | |
| #endif | |
| ? abs_path | |
| : filepath; | |
| #endif // FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION | |
| // clang-format on | |
| } | |
| std::string RelativeToRootPath(const std::string &project, | |
| const std::string &filepath) { | |
| std::string absolute_project = PosixPath(AbsolutePath(project)); | |
| if (absolute_project.back() != '/') absolute_project += "/"; | |
| std::string absolute_filepath = PosixPath(AbsolutePath(filepath)); | |
| // Find the first character where they disagree. | |
| // The previous directory is the lowest common ancestor; | |
| const char *a = absolute_project.c_str(); | |
| const char *b = absolute_filepath.c_str(); | |
| size_t common_prefix_len = 0; | |
| while (*a != '\0' && *b != '\0' && *a == *b) { | |
| if (*a == '/') common_prefix_len = a - absolute_project.c_str(); | |
| a++; | |
| b++; | |
| } | |
| // the number of ../ to prepend to b depends on the number of remaining | |
| // directories in A. | |
| const char *suffix = absolute_project.c_str() + common_prefix_len; | |
| size_t num_up = 0; | |
| while (*suffix != '\0') | |
| if (*suffix++ == '/') num_up++; | |
| num_up--; // last one is known to be '/'. | |
| std::string result = "//"; | |
| for (size_t i = 0; i < num_up; i++) result += "../"; | |
| result += absolute_filepath.substr(common_prefix_len + 1); | |
| return result; | |
| } | |
| // Locale-independent code. | |
| #if defined(FLATBUFFERS_LOCALE_INDEPENDENT) && \ | |
| (FLATBUFFERS_LOCALE_INDEPENDENT > 0) | |
| // clang-format off | |
| // Allocate locale instance at startup of application. | |
| ClassicLocale ClassicLocale::instance_; | |
| #ifdef _MSC_VER | |
| ClassicLocale::ClassicLocale() | |
| : locale_(_create_locale(LC_ALL, "C")) {} | |
| ClassicLocale::~ClassicLocale() { _free_locale(locale_); } | |
| #else | |
| ClassicLocale::ClassicLocale() | |
| : locale_(newlocale(LC_ALL, "C", nullptr)) {} | |
| ClassicLocale::~ClassicLocale() { freelocale(locale_); } | |
| #endif | |
| // clang-format on | |
| #endif // !FLATBUFFERS_LOCALE_INDEPENDENT | |
| std::string RemoveStringQuotes(const std::string &s) { | |
| auto ch = *s.c_str(); | |
| return ((s.size() >= 2) && (ch == '\"' || ch == '\'') && (ch == s.back())) | |
| ? s.substr(1, s.length() - 2) | |
| : s; | |
| } | |
| bool SetGlobalTestLocale(const char *locale_name, std::string *_value) { | |
| const auto the_locale = setlocale(LC_ALL, locale_name); | |
| if (!the_locale) return false; | |
| if (_value) *_value = std::string(the_locale); | |
| return true; | |
| } | |
| bool ReadEnvironmentVariable(const char *var_name, std::string *_value) { | |
| #ifdef _MSC_VER | |
| __pragma(warning(disable : 4996)); // _CRT_SECURE_NO_WARNINGS | |
| #endif | |
| auto env_str = std::getenv(var_name); | |
| if (!env_str) return false; | |
| if (_value) *_value = std::string(env_str); | |
| return true; | |
| } | |
| std::string ConvertCase(const std::string &input, Case output_case, | |
| Case input_case) { | |
| if (output_case == Case::kKeep) return input; | |
| // The output cases expect snake_case inputs, so if we don't have that input | |
| // format, try to convert to snake_case. | |
| switch (input_case) { | |
| case Case::kLowerCamel: | |
| case Case::kUpperCamel: | |
| return ConvertCase(CamelToSnake(input), output_case); | |
| case Case::kDasher: return ConvertCase(DasherToSnake(input), output_case); | |
| case Case::kKeep: printf("WARNING: Converting from kKeep case.\n"); break; | |
| default: | |
| case Case::kSnake: | |
| case Case::kScreamingSnake: | |
| case Case::kAllLower: | |
| case Case::kAllUpper: break; | |
| } | |
| switch (output_case) { | |
| case Case::kUpperCamel: return ToCamelCase(input, true); | |
| case Case::kLowerCamel: return ToCamelCase(input, false); | |
| case Case::kSnake: return input; | |
| case Case::kScreamingSnake: return ToSnakeCase(input, true); | |
| case Case::kAllUpper: return ToAll(input, CharToUpper); | |
| case Case::kAllLower: return ToAll(input, CharToLower); | |
| case Case::kDasher: return ToDasher(input); | |
| case Case::kSnake2: return SnakeToSnake2(input); | |
| default: | |
| case Case::kUnknown: return input; | |
| } | |
| } | |
| } // namespace flatbuffers |