...

Text file src/github.com/google/flatbuffers/src/util.cpp

Documentation: github.com/google/flatbuffers/src

     1/*
     2 * Copyright 2016 Google Inc. All rights reserved.
     3 *
     4 * Licensed under the Apache License, Version 2.0 (the "License");
     5 * you may not use this file except in compliance with the License.
     6 * You may obtain a copy of the License at
     7 *
     8 *     http://www.apache.org/licenses/LICENSE-2.0
     9 *
    10 * Unless required by applicable law or agreed to in writing, software
    11 * distributed under the License is distributed on an "AS IS" BASIS,
    12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13 * See the License for the specific language governing permissions and
    14 * limitations under the License.
    15 */
    16
    17// clang-format off
    18// Dont't remove `format off`, it prevent reordering of win-includes.
    19
    20#include <cstring>
    21#if defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) || \
    22    defined(__QNXNTO__)
    23#  define _POSIX_C_SOURCE 200809L
    24#  define _XOPEN_SOURCE 700L
    25#endif
    26
    27#if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
    28#  ifndef WIN32_LEAN_AND_MEAN
    29#    define WIN32_LEAN_AND_MEAN
    30#  endif
    31#  ifndef NOMINMAX
    32#    define NOMINMAX
    33#  endif
    34#  ifdef _MSC_VER
    35#    include <crtdbg.h>
    36#  endif
    37#  include <windows.h>  // Must be included before <direct.h>
    38#  ifndef __CYGWIN__
    39#    include <direct.h>
    40#  endif
    41#  include <winbase.h>
    42#  undef interface  // This is also important because of reasons
    43#endif
    44// clang-format on
    45
    46#include "flatbuffers/util.h"
    47
    48#include <sys/stat.h>
    49
    50#include <clocale>
    51#include <cstdlib>
    52#include <fstream>
    53#include <functional>
    54
    55#include "flatbuffers/base.h"
    56
    57namespace flatbuffers {
    58
    59namespace {
    60
    61static bool FileExistsRaw(const char *name) {
    62  std::ifstream ifs(name);
    63  return ifs.good();
    64}
    65
    66static bool LoadFileRaw(const char *name, bool binary, std::string *buf) {
    67  if (DirExists(name)) return false;
    68  std::ifstream ifs(name, binary ? std::ifstream::binary : std::ifstream::in);
    69  if (!ifs.is_open()) return false;
    70  if (binary) {
    71    // The fastest way to read a file into a string.
    72    ifs.seekg(0, std::ios::end);
    73    auto size = ifs.tellg();
    74    (*buf).resize(static_cast<size_t>(size));
    75    ifs.seekg(0, std::ios::beg);
    76    ifs.read(&(*buf)[0], (*buf).size());
    77  } else {
    78    // This is slower, but works correctly on all platforms for text files.
    79    std::ostringstream oss;
    80    oss << ifs.rdbuf();
    81    *buf = oss.str();
    82  }
    83  return !ifs.bad();
    84}
    85
    86LoadFileFunction g_load_file_function = LoadFileRaw;
    87FileExistsFunction g_file_exists_function = FileExistsRaw;
    88
    89static std::string ToCamelCase(const std::string &input, bool is_upper) {
    90  std::string s;
    91  for (size_t i = 0; i < input.length(); i++) {
    92    if (!i && input[i] == '_') {
    93      s += input[i];
    94      // we ignore leading underscore but make following
    95      // alphabet char upper.
    96      if (i + 1 < input.length() && is_alpha(input[i + 1]))
    97        s += CharToUpper(input[++i]);
    98    } else if (!i)
    99      s += is_upper ? CharToUpper(input[i]) : CharToLower(input[i]);
   100    else if (input[i] == '_' && i + 1 < input.length())
   101      s += CharToUpper(input[++i]);
   102    else
   103      s += input[i];
   104  }
   105  return s;
   106}
   107
   108static std::string ToSnakeCase(const std::string &input, bool screaming) {
   109  std::string s;
   110  for (size_t i = 0; i < input.length(); i++) {
   111    if (i == 0) {
   112      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
   113    } else if (input[i] == '_') {
   114      s += '_';
   115    } else if (!islower(input[i])) {
   116      // Prevent duplicate underscores for Upper_Snake_Case strings
   117      // and UPPERCASE strings.
   118      if (islower(input[i - 1]) ||
   119          (isdigit(input[i - 1]) && !isdigit(input[i]))) {
   120        s += '_';
   121      }
   122      s += screaming ? CharToUpper(input[i]) : CharToLower(input[i]);
   123    } else {
   124      s += screaming ? CharToUpper(input[i]) : input[i];
   125    }
   126  }
   127  return s;
   128}
   129
   130std::string ToAll(const std::string &input,
   131                  std::function<char(const char)> transform) {
   132  std::string s;
   133  for (size_t i = 0; i < input.length(); i++) { s += transform(input[i]); }
   134  return s;
   135}
   136
   137std::string CamelToSnake(const std::string &input) {
   138  std::string s;
   139  for (size_t i = 0; i < input.length(); i++) {
   140    if (i == 0) {
   141      s += CharToLower(input[i]);
   142    } else if (input[i] == '_') {
   143      s += '_';
   144    } else if (!islower(input[i])) {
   145      // Prevent duplicate underscores for Upper_Snake_Case strings
   146      // and UPPERCASE strings.
   147      if (islower(input[i - 1]) ||
   148          (isdigit(input[i - 1]) && !isdigit(input[i]))) {
   149        s += '_';
   150      }
   151      s += CharToLower(input[i]);
   152    } else {
   153      s += input[i];
   154    }
   155  }
   156  return s;
   157}
   158
   159std::string DasherToSnake(const std::string &input) {
   160  std::string s;
   161  for (size_t i = 0; i < input.length(); i++) {
   162    if (input[i] == '-') {
   163      s += "_";
   164    } else {
   165      s += input[i];
   166    }
   167  }
   168  return s;
   169}
   170
   171std::string ToDasher(const std::string &input) {
   172  std::string s;
   173  char p = 0;
   174  for (size_t i = 0; i < input.length(); i++) {
   175    char const &c = input[i];
   176    if (c == '_') {
   177      if (i > 0 && p != kPathSeparator &&
   178          // The following is a special case to ignore digits after a _. This is
   179          // because ThisExample3 would be converted to this_example_3 in the
   180          // CamelToSnake conversion, and then dasher would do this-example-3,
   181          // but it expects this-example3.
   182          !(i + 1 < input.length() && isdigit(input[i + 1])))
   183        s += "-";
   184    } else {
   185      s += c;
   186    }
   187    p = c;
   188  }
   189  return s;
   190}
   191
   192// Converts foo_bar_123baz_456 to foo_bar123_baz456
   193std::string SnakeToSnake2(const std::string &s) {
   194  if (s.length() <= 1) return s;
   195  std::string result;
   196  result.reserve(s.size());
   197  for (size_t i = 0; i < s.length() - 1; i++) {
   198    if (s[i] == '_' && isdigit(s[i + 1])) {
   199      continue;  // Move the `_` until after the digits.
   200    }
   201
   202    result.push_back(s[i]);
   203
   204    if (isdigit(s[i]) && isalpha(s[i + 1]) && islower(s[i + 1])) {
   205      result.push_back('_');
   206    }
   207  }
   208  result.push_back(s.back());
   209
   210  return result;
   211}
   212
   213}  // namespace
   214
   215bool LoadFile(const char *name, bool binary, std::string *buf) {
   216  FLATBUFFERS_ASSERT(g_load_file_function);
   217  return g_load_file_function(name, binary, buf);
   218}
   219
   220bool FileExists(const char *name) {
   221  FLATBUFFERS_ASSERT(g_file_exists_function);
   222  return g_file_exists_function(name);
   223}
   224
   225bool DirExists(const char *name) {
   226  // clang-format off
   227
   228  #ifdef _WIN32
   229    #define flatbuffers_stat _stat
   230    #define FLATBUFFERS_S_IFDIR _S_IFDIR
   231  #else
   232    #define flatbuffers_stat stat
   233    #define FLATBUFFERS_S_IFDIR S_IFDIR
   234  #endif
   235  // clang-format on
   236  struct flatbuffers_stat file_info;
   237  if (flatbuffers_stat(name, &file_info) != 0) return false;
   238  return (file_info.st_mode & FLATBUFFERS_S_IFDIR) != 0;
   239}
   240
   241LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function) {
   242  LoadFileFunction previous_function = g_load_file_function;
   243  g_load_file_function = load_file_function ? load_file_function : LoadFileRaw;
   244  return previous_function;
   245}
   246
   247FileExistsFunction SetFileExistsFunction(
   248    FileExistsFunction file_exists_function) {
   249  FileExistsFunction previous_function = g_file_exists_function;
   250  g_file_exists_function =
   251      file_exists_function ? file_exists_function : FileExistsRaw;
   252  return previous_function;
   253}
   254
   255bool SaveFile(const char *name, const char *buf, size_t len, bool binary) {
   256  std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out);
   257  if (!ofs.is_open()) return false;
   258  ofs.write(buf, len);
   259  return !ofs.bad();
   260}
   261
   262// We internally store paths in posix format ('/'). Paths supplied
   263// by the user should go through PosixPath to ensure correct behavior
   264// on Windows when paths are string-compared.
   265
   266static const char kPathSeparatorWindows = '\\';
   267static const char *PathSeparatorSet = "\\/";  // Intentionally no ':'
   268
   269std::string StripExtension(const std::string &filepath) {
   270  size_t i = filepath.find_last_of('.');
   271  return i != std::string::npos ? filepath.substr(0, i) : filepath;
   272}
   273
   274std::string GetExtension(const std::string &filepath) {
   275  size_t i = filepath.find_last_of('.');
   276  return i != std::string::npos ? filepath.substr(i + 1) : "";
   277}
   278
   279std::string StripPath(const std::string &filepath) {
   280  size_t i = filepath.find_last_of(PathSeparatorSet);
   281  return i != std::string::npos ? filepath.substr(i + 1) : filepath;
   282}
   283
   284std::string StripFileName(const std::string &filepath) {
   285  size_t i = filepath.find_last_of(PathSeparatorSet);
   286  return i != std::string::npos ? filepath.substr(0, i) : "";
   287}
   288
   289std::string StripPrefix(const std::string &filepath,
   290                        const std::string &prefix_to_remove) {
   291  if (!strncmp(filepath.c_str(), prefix_to_remove.c_str(),
   292               prefix_to_remove.size())) {
   293    return filepath.substr(prefix_to_remove.size());
   294  }
   295  return filepath;
   296}
   297
   298std::string ConCatPathFileName(const std::string &path,
   299                               const std::string &filename) {
   300  std::string filepath = path;
   301  if (filepath.length()) {
   302    char &filepath_last_character = filepath.back();
   303    if (filepath_last_character == kPathSeparatorWindows) {
   304      filepath_last_character = kPathSeparator;
   305    } else if (filepath_last_character != kPathSeparator) {
   306      filepath += kPathSeparator;
   307    }
   308  }
   309  filepath += filename;
   310  // Ignore './' at the start of filepath.
   311  if (filepath[0] == '.' && filepath[1] == kPathSeparator) {
   312    filepath.erase(0, 2);
   313  }
   314  return filepath;
   315}
   316
   317std::string PosixPath(const char *path) {
   318  std::string p = path;
   319  std::replace(p.begin(), p.end(), '\\', '/');
   320  return p;
   321}
   322std::string PosixPath(const std::string &path) {
   323  return PosixPath(path.c_str());
   324}
   325
   326void EnsureDirExists(const std::string &filepath) {
   327  auto parent = StripFileName(filepath);
   328  if (parent.length()) EnsureDirExists(parent);
   329    // clang-format off
   330
   331  #ifdef _WIN32
   332    (void)_mkdir(filepath.c_str());
   333  #else
   334    mkdir(filepath.c_str(), S_IRWXU|S_IRGRP|S_IXGRP);
   335  #endif
   336  // clang-format on
   337}
   338
   339std::string AbsolutePath(const std::string &filepath) {
   340  // clang-format off
   341
   342  #ifdef FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION
   343    return filepath;
   344  #else
   345    #if defined(_WIN32) || defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__)
   346      char abs_path[MAX_PATH];
   347      return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr)
   348    #else
   349      char *abs_path_temp = realpath(filepath.c_str(), nullptr);
   350      bool success = abs_path_temp != nullptr;
   351      std::string abs_path;
   352      if(success) {
   353        abs_path = abs_path_temp;
   354        free(abs_path_temp);
   355      }
   356      return success
   357    #endif
   358      ? abs_path
   359      : filepath;
   360  #endif // FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION
   361  // clang-format on
   362}
   363
   364std::string RelativeToRootPath(const std::string &project,
   365                               const std::string &filepath) {
   366  std::string absolute_project = PosixPath(AbsolutePath(project));
   367  if (absolute_project.back() != '/') absolute_project += "/";
   368  std::string absolute_filepath = PosixPath(AbsolutePath(filepath));
   369
   370  // Find the first character where they disagree.
   371  // The previous directory is the lowest common ancestor;
   372  const char *a = absolute_project.c_str();
   373  const char *b = absolute_filepath.c_str();
   374  size_t common_prefix_len = 0;
   375  while (*a != '\0' && *b != '\0' && *a == *b) {
   376    if (*a == '/') common_prefix_len = a - absolute_project.c_str();
   377    a++;
   378    b++;
   379  }
   380  // the number of ../ to prepend to b depends on the number of remaining
   381  // directories in A.
   382  const char *suffix = absolute_project.c_str() + common_prefix_len;
   383  size_t num_up = 0;
   384  while (*suffix != '\0')
   385    if (*suffix++ == '/') num_up++;
   386  num_up--;  // last one is known to be '/'.
   387  std::string result = "//";
   388  for (size_t i = 0; i < num_up; i++) result += "../";
   389  result += absolute_filepath.substr(common_prefix_len + 1);
   390
   391  return result;
   392}
   393
   394// Locale-independent code.
   395#if defined(FLATBUFFERS_LOCALE_INDEPENDENT) && \
   396    (FLATBUFFERS_LOCALE_INDEPENDENT > 0)
   397
   398// clang-format off
   399// Allocate locale instance at startup of application.
   400ClassicLocale ClassicLocale::instance_;
   401
   402#ifdef _MSC_VER
   403  ClassicLocale::ClassicLocale()
   404    : locale_(_create_locale(LC_ALL, "C")) {}
   405  ClassicLocale::~ClassicLocale() { _free_locale(locale_); }
   406#else
   407  ClassicLocale::ClassicLocale()
   408    : locale_(newlocale(LC_ALL, "C", nullptr)) {}
   409  ClassicLocale::~ClassicLocale() { freelocale(locale_); }
   410#endif
   411// clang-format on
   412
   413#endif  // !FLATBUFFERS_LOCALE_INDEPENDENT
   414
   415std::string RemoveStringQuotes(const std::string &s) {
   416  auto ch = *s.c_str();
   417  return ((s.size() >= 2) && (ch == '\"' || ch == '\'') && (ch == s.back()))
   418             ? s.substr(1, s.length() - 2)
   419             : s;
   420}
   421
   422bool SetGlobalTestLocale(const char *locale_name, std::string *_value) {
   423  const auto the_locale = setlocale(LC_ALL, locale_name);
   424  if (!the_locale) return false;
   425  if (_value) *_value = std::string(the_locale);
   426  return true;
   427}
   428
   429bool ReadEnvironmentVariable(const char *var_name, std::string *_value) {
   430#ifdef _MSC_VER
   431  __pragma(warning(disable : 4996));  // _CRT_SECURE_NO_WARNINGS
   432#endif
   433  auto env_str = std::getenv(var_name);
   434  if (!env_str) return false;
   435  if (_value) *_value = std::string(env_str);
   436  return true;
   437}
   438
   439std::string ConvertCase(const std::string &input, Case output_case,
   440                        Case input_case) {
   441  if (output_case == Case::kKeep) return input;
   442  // The output cases expect snake_case inputs, so if we don't have that input
   443  // format, try to convert to snake_case.
   444  switch (input_case) {
   445    case Case::kLowerCamel:
   446    case Case::kUpperCamel:
   447      return ConvertCase(CamelToSnake(input), output_case);
   448    case Case::kDasher: return ConvertCase(DasherToSnake(input), output_case);
   449    case Case::kKeep: printf("WARNING: Converting from kKeep case.\n"); break;
   450    default:
   451    case Case::kSnake:
   452    case Case::kScreamingSnake:
   453    case Case::kAllLower:
   454    case Case::kAllUpper: break;
   455  }
   456
   457  switch (output_case) {
   458    case Case::kUpperCamel: return ToCamelCase(input, true);
   459    case Case::kLowerCamel: return ToCamelCase(input, false);
   460    case Case::kSnake: return input;
   461    case Case::kScreamingSnake: return ToSnakeCase(input, true);
   462    case Case::kAllUpper: return ToAll(input, CharToUpper);
   463    case Case::kAllLower: return ToAll(input, CharToLower);
   464    case Case::kDasher: return ToDasher(input);
   465    case Case::kSnake2: return SnakeToSnake2(input);
   466    default:
   467    case Case::kUnknown: return input;
   468  }
   469}
   470
   471}  // namespace flatbuffers

View as plain text