| // Copyright 2014 The Bazel Authors. 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. | 
 |  | 
 | #if defined(_WIN32) || defined(__CYGWIN__) | 
 | #ifndef WIN32_LEAN_AND_MEAN | 
 | #define WIN32_LEAN_AND_MEAN | 
 | #endif | 
 | #endif  // defined(_WIN32) || defined(__CYGWIN__) | 
 |  | 
 | #include "src/main/cpp/util/strings.h" | 
 |  | 
 | #if defined(_WIN32) || defined(__CYGWIN__) | 
 | #include <windows.h> | 
 | #endif  // defined(_WIN32) || defined(__CYGWIN__) | 
 |  | 
 | #include <stdarg.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #include <cassert> | 
 | #include <memory> | 
 |  | 
 | #include "src/main/cpp/util/exit_code.h" | 
 |  | 
 | namespace blaze_util { | 
 |  | 
 | using std::string; | 
 | using std::unique_ptr; | 
 | using std::vector; | 
 | using std::wstring; | 
 |  | 
 | static const char kSeparator[] = " \n\t\r"; | 
 |  | 
 | bool starts_with(const string &haystack, const string &needle) { | 
 |   return (haystack.length() >= needle.length()) && | 
 |          (memcmp(haystack.c_str(), needle.c_str(), needle.length()) == 0); | 
 | } | 
 |  | 
 | template <typename char_type> | 
 | static bool ends_with_impl(const std::basic_string<char_type> &haystack, | 
 |                            const std::basic_string<char_type> &needle) { | 
 |   return (haystack.length() >= needle.length()) && | 
 |          std::equal(haystack.cend() - needle.length(), haystack.cend(), | 
 |                     needle.cbegin()); | 
 | } | 
 |  | 
 | bool ends_with(const string &haystack, const string &needle) { | 
 |   return ends_with_impl(haystack, needle); | 
 | } | 
 |  | 
 | bool ends_with(const wstring &haystack, const wstring &needle) { | 
 |   return ends_with_impl(haystack, needle); | 
 | } | 
 |  | 
 | void JoinStrings(const vector<string> &pieces, const char delimeter, | 
 |                  string *output) { | 
 |   bool first = true; | 
 |   for (const auto &piece : pieces) { | 
 |     if (first) { | 
 |       *output = piece; | 
 |       first = false; | 
 |     } else { | 
 |       *output += delimeter + piece; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | vector<string> Split(const string &contents, const char delimeter) { | 
 |   vector<string> result; | 
 |   SplitStringUsing(contents, delimeter, &result); | 
 |   return result; | 
 | } | 
 |  | 
 | void SplitStringUsing(const string &contents, const char delimeter, | 
 |                       vector<string> *result) { | 
 |   assert(result); | 
 |  | 
 |   size_t start = 0; | 
 |   while (start < contents.length() && contents[start] == delimeter) { | 
 |     ++start; | 
 |   } | 
 |  | 
 |   size_t newline = contents.find(delimeter, start); | 
 |   while (newline != string::npos) { | 
 |     result->push_back(string(contents, start, newline - start)); | 
 |     start = newline; | 
 |     while (start < contents.length() && contents[start] == delimeter) { | 
 |       ++start; | 
 |     } | 
 |     newline = contents.find(delimeter, start); | 
 |   } | 
 |  | 
 |   // If there is a trailing line, add that. | 
 |   if (start != newline && start != contents.size()) { | 
 |     result->push_back(string(contents, start)); | 
 |   } | 
 | } | 
 |  | 
 | size_t SplitQuotedStringUsing(const string &contents, const char delimeter, | 
 |                               std::vector<string> *output) { | 
 |   size_t len = contents.length(); | 
 |   size_t start = 0; | 
 |   size_t quote = string::npos;  // quote position | 
 |   size_t num_segments = 0; | 
 |  | 
 |   for (size_t pos = 0; pos < len; ++pos) { | 
 |     if (start == pos && contents[start] == delimeter) { | 
 |       ++start; | 
 |     } else if (contents[pos] == '\\') { | 
 |       ++pos; | 
 |     } else if (quote != string::npos && contents[pos] == contents[quote]) { | 
 |       quote = string::npos; | 
 |     } else if (quote == string::npos && | 
 |                (contents[pos] == '"' || contents[pos] == '\'')) { | 
 |       quote = pos; | 
 |     } else if (quote == string::npos && contents[pos] == delimeter) { | 
 |       output->push_back(string(contents, start, pos - start)); | 
 |       start = pos + 1; | 
 |       num_segments++; | 
 |     } | 
 |   } | 
 |  | 
 |   // A trailing element | 
 |   if (start < len) { | 
 |     output->push_back(string(contents, start)); | 
 |     num_segments++; | 
 |   } | 
 |   return num_segments; | 
 | } | 
 |  | 
 | void Replace(const string &oldsub, const string &newsub, string *str) { | 
 |   size_t start = 0; | 
 |   // This is O(n^2) (the complexity of erase() is actually unspecified, but | 
 |   // usually linear). | 
 |   while ((start = str->find(oldsub, start)) != string::npos) { | 
 |     str->erase(start, oldsub.length()); | 
 |     str->insert(start, newsub); | 
 |     start += newsub.length(); | 
 |   } | 
 | } | 
 |  | 
 | void StripWhitespace(string *str) { | 
 |   int str_length = str->length(); | 
 |  | 
 |   // Strip off leading whitespace. | 
 |   int first = 0; | 
 |   while (first < str_length && ascii_isspace(str->at(first))) { | 
 |     ++first; | 
 |   } | 
 |   // If entire string is white space. | 
 |   if (first == str_length) { | 
 |     str->clear(); | 
 |     return; | 
 |   } | 
 |   if (first > 0) { | 
 |     str->erase(0, first); | 
 |     str_length -= first; | 
 |   } | 
 |  | 
 |   // Strip off trailing whitespace. | 
 |   int last = str_length - 1; | 
 |   while (last >= 0 && ascii_isspace(str->at(last))) { | 
 |     --last; | 
 |   } | 
 |   if (last != (str_length - 1) && last >= 0) { | 
 |     str->erase(last + 1, string::npos); | 
 |   } | 
 | } | 
 |  | 
 | static void GetNextToken(const string &str, const char &comment, | 
 |                          string::const_iterator *iter, vector<string> *words) { | 
 |   string output; | 
 |   auto last = *iter; | 
 |   char quote = '\0'; | 
 |   // While not a delimiter. | 
 |   while (last != str.end() && (quote || strchr(kSeparator, *last) == nullptr)) { | 
 |     // Absorb escapes. | 
 |     if (*last == '\\') { | 
 |       ++last; | 
 |       if (last == str.end()) { | 
 |         break; | 
 |       } | 
 |       output += *last++; | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (quote) { | 
 |       if (*last == quote) { | 
 |         // Absorb closing quote. | 
 |         quote = '\0'; | 
 |         ++last; | 
 |       } else { | 
 |         output += *last++; | 
 |       } | 
 |     } else { | 
 |       if (*last == comment) { | 
 |         last = str.end(); | 
 |         break; | 
 |       } | 
 |       if (*last == '\'' || *last == '"') { | 
 |         // Absorb opening quote. | 
 |         quote = *last++; | 
 |       } else { | 
 |         output += *last++; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (!output.empty()) { | 
 |     words->push_back(output); | 
 |   } | 
 |  | 
 |   *iter = last; | 
 | } | 
 |  | 
 | void Tokenize(const string &str, const char &comment, vector<string> *words) { | 
 |   assert(words); | 
 |   words->clear(); | 
 |  | 
 |   string::const_iterator i = str.begin(); | 
 |   while (i != str.end()) { | 
 |     // Skip whitespace. | 
 |     while (i != str.end() && strchr(kSeparator, *i) != nullptr) { | 
 |       i++; | 
 |     } | 
 |     if (i != str.end() && *i == comment) { | 
 |       break; | 
 |     } | 
 |     GetNextToken(str, comment, &i, words); | 
 |   } | 
 | } | 
 |  | 
 | // Evaluate a format string and store the result in 'str'. | 
 | void StringPrintf(string *str, const char *format, ...) { | 
 |   assert(str); | 
 |  | 
 |   // Determine the required buffer size. vsnpritnf won't account for the | 
 |   // terminating '\0'. | 
 |   va_list args; | 
 |   va_start(args, format); | 
 |   int output_size = vsnprintf(nullptr, 0, format, args); | 
 |   if (output_size < 0) { | 
 |     fprintf(stderr, "Fatal error formatting string: %d", output_size); | 
 |     exit(blaze_exit_code::INTERNAL_ERROR); | 
 |   } | 
 |   va_end(args); | 
 |  | 
 |   // Allocate a buffer and format the input. | 
 |   int buffer_size = output_size + sizeof '\0'; | 
 |   char *buf = new char[buffer_size]; | 
 |   va_start(args, format); | 
 |   int print_result = vsnprintf(buf, buffer_size, format, args); | 
 |   if (print_result < 0) { | 
 |     fprintf(stderr, "Fatal error formatting string: %d", print_result); | 
 |     exit(blaze_exit_code::INTERNAL_ERROR); | 
 |   } | 
 |   va_end(args); | 
 |  | 
 |   *str = buf; | 
 |   delete[] buf; | 
 | } | 
 |  | 
 | string ToLower(const string &str) { | 
 |   if (str.empty()) { | 
 |     return ""; | 
 |   } | 
 |   unique_ptr<char[]> result(new char[str.size() + 1]); | 
 |   char *result_ptr = result.get(); | 
 |   for (const auto &ch : str) { | 
 |     *result_ptr++ = tolower(ch); | 
 |   } | 
 |   result.get()[str.size()] = 0; | 
 |   return string(result.get()); | 
 | } | 
 |  | 
 | #if defined(_WIN32) || defined(__CYGWIN__) | 
 |  | 
 | template <typename U, typename V> | 
 | static bool UStrToVStr(const std::basic_string<U> &input, | 
 |                        std::basic_string<V> *output, const bool use_utf8, | 
 |                        int (*Convert)(const bool _utf8, | 
 |                                       const std::basic_string<U> &_in, V *_out, | 
 |                                       const size_t _size), | 
 |                        uint32_t *win32_error) { | 
 |   int buf_size = input.size() + 1; | 
 |   std::unique_ptr<V[]> buf(new V[buf_size]); | 
 |   // Attempt to convert, optimistically using the estimated output buffer size. | 
 |   int res = Convert(use_utf8, input, buf.get(), buf_size); | 
 |   if (res > 0) { | 
 |     *output = buf.get(); | 
 |     return true; | 
 |   } | 
 |  | 
 |   DWORD err = GetLastError(); | 
 |   if (err != ERROR_INSUFFICIENT_BUFFER) { | 
 |     if (win32_error) { | 
 |       *win32_error = static_cast<uint32_t>(err); | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   // The output buffer was too small. Get required buffer size. | 
 |   res = Convert(use_utf8, input, nullptr, 0); | 
 |   if (res > 0) { | 
 |     buf_size = res; | 
 |     buf.reset(new V[buf_size]); | 
 |     res = Convert(use_utf8, input, buf.get(), buf_size); | 
 |     if (res > 0) { | 
 |       *output = buf.get(); | 
 |       return true; | 
 |     } | 
 |   } | 
 |   if (win32_error) { | 
 |     *win32_error = static_cast<uint32_t>(GetLastError()); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static int ConvertWcsToMbs(const bool use_utf8, const std::wstring &input, | 
 |                            char *output, const size_t output_size) { | 
 |   return WideCharToMultiByte(use_utf8 ? CP_UTF8 : CP_ACP, 0, input.c_str(), -1, | 
 |                              output, output_size, nullptr, nullptr); | 
 | } | 
 |  | 
 | static int ConvertMbsToWcs(const bool /* unused */, const std::string &input, | 
 |                            wchar_t *output, const size_t output_size) { | 
 |   return MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, output, | 
 |                              output_size); | 
 | } | 
 |  | 
 | bool WcsToAcp(const std::wstring &input, std::string *output, | 
 |               uint32_t *win32_error) { | 
 |   return UStrToVStr(input, output, false, ConvertWcsToMbs, win32_error); | 
 | } | 
 |  | 
 | bool WcsToUtf8(const std::wstring &input, std::string *output, | 
 |                uint32_t *win32_error) { | 
 |   return UStrToVStr(input, output, true, ConvertWcsToMbs, win32_error); | 
 | } | 
 |  | 
 | bool Utf8ToWcs(const std::string &input, std::wstring *output, | 
 |                uint32_t *win32_error) { | 
 |   return UStrToVStr(input, output, /* unused */ true, ConvertMbsToWcs, | 
 |                     win32_error); | 
 | } | 
 |  | 
 | std::string WstringToCstring(const std::wstring &input) { | 
 |   std::string result; | 
 |   uint32_t err; | 
 |   if (!WcsToUtf8(input, &result, &err)) { | 
 |     fprintf(stderr, | 
 |             "WstringToCstring: failed with error %d (0x%08x), " | 
 |             "invalid input \"%ls\"\n", | 
 |             err, err, input.c_str()); | 
 |     exit(blaze_exit_code::INTERNAL_ERROR); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | std::wstring CstringToWstring(const std::string &input) { | 
 |   std::wstring result; | 
 |   uint32_t err; | 
 |   if (!Utf8ToWcs(input, &result, &err)) { | 
 |     fprintf(stderr, | 
 |             "CstringToWstring: failed with error %d (0x%08x), " | 
 |             "invalid input \"%s\"\n", | 
 |             err, err, input.c_str()); | 
 |     exit(blaze_exit_code::INTERNAL_ERROR); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | char** WArgsToCArgs(int argc, wchar_t **wargv) { | 
 |   char **argv = new char*[argc]; | 
 |   for (int i = 0; i < argc; i++) { | 
 |     std::string arg = WstringToCstring(std::wstring(wargv[i])); | 
 |     argv[i] = new char[arg.length()+1]; | 
 |     for (int j = 0; j < arg.length(); j++) { | 
 |       argv[i][j] = arg[j]; | 
 |     } | 
 |     argv[i][arg.length()] = 0; | 
 |   } | 
 |   return argv; | 
 | } | 
 | #endif  // defined(_WIN32) || defined(__CYGWIN__) | 
 |  | 
 | }  // namespace blaze_util |