| // Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gestures/include/string_util.h" |
| |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <strings.h> |
| |
| #include "gestures/include/macros.h" |
| |
| namespace gestures { |
| |
| namespace { |
| |
| const char kWhitespaceASCII[] = { |
| 0x09, // CHARACTER TABULATION |
| 0x0A, // LINE FEED (LF) |
| 0x0B, // LINE TABULATION |
| 0x0C, // FORM FEED (FF) |
| 0x0D, // CARRIAGE RETURN (CR) |
| 0x20, // SPACE |
| 0 |
| }; |
| |
| // Backend for StringPrintF/StringAppendF. This does not finalize |
| // the va_list, the caller is expected to do that. |
| PRINTF_FORMAT(2, 0) |
| static void StringAppendVT(std::string* dst, |
| const char* format, |
| va_list ap) { |
| // First try with a small fixed size buffer. |
| // This buffer size should be kept in sync with StringUtilTest.GrowBoundary |
| // and StringUtilTest.StringPrintfBounds. |
| char stack_buf[1024]; |
| |
| va_list ap_copy; |
| va_copy(ap_copy, ap); |
| |
| errno = 0; |
| int result = vsnprintf(stack_buf, arraysize(stack_buf), format, ap_copy); |
| va_end(ap_copy); |
| |
| if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { |
| // It fit. |
| dst->append(stack_buf, result); |
| return; |
| } |
| |
| // Repeatedly increase buffer size until it fits. |
| int mem_length = arraysize(stack_buf); |
| while (true) { |
| if (result < 0) { |
| if (errno != 0 && errno != EOVERFLOW) |
| return; |
| // Try doubling the buffer size. |
| mem_length *= 2; |
| } else { |
| // We need exactly "result + 1" characters. |
| mem_length = result + 1; |
| } |
| |
| if (mem_length > 32 * 1024 * 1024) { |
| // That should be plenty, don't try anything larger. This protects |
| // against huge allocations when using vsnprintfT implementations that |
| // return -1 for reasons other than overflow without setting errno. |
| return; |
| } |
| |
| std::vector<char> mem_buf(mem_length); |
| |
| // NOTE: You can only use a va_list once. Since we're in a while loop, we |
| // need to make a new copy each time so we don't use up the original. |
| va_copy(ap_copy, ap); |
| result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy); |
| va_end(ap_copy); |
| |
| if ((result >= 0) && (result < mem_length)) { |
| // It fit. |
| dst->append(&mem_buf[0], result); |
| return; |
| } |
| } |
| } |
| |
| template <typename STR> |
| void SplitStringT(const STR& str, |
| const typename STR::value_type s, |
| bool trim_whitespace, |
| std::vector<STR>* r) { |
| r->clear(); |
| size_t last = 0; |
| size_t c = str.size(); |
| for (size_t i = 0; i <= c; ++i) { |
| if (i == c || str[i] == s) { |
| STR tmp(str, last, i - last); |
| if (trim_whitespace) |
| TrimWhitespaceASCII(tmp, TRIM_ALL, &tmp); |
| // Avoid converting an empty or all-whitespace source string into a vector |
| // of one empty string. |
| if (i != c || !r->empty() || !tmp.empty()) |
| r->push_back(tmp); |
| last = i + 1; |
| } |
| } |
| } |
| |
| template<typename STR> |
| TrimPositions TrimStringT(const STR& input, |
| const typename STR::value_type trim_chars[], |
| TrimPositions positions, |
| STR* output) { |
| // Find the edges of leading/trailing whitespace as desired. |
| const typename STR::size_type last_char = input.length() - 1; |
| const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ? |
| input.find_first_not_of(trim_chars) : 0; |
| const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ? |
| input.find_last_not_of(trim_chars) : last_char; |
| |
| // When the string was all whitespace, report that we stripped off whitespace |
| // from whichever position the caller was interested in. For empty input, we |
| // stripped no whitespace, but we still need to clear |output|. |
| if (input.empty() || |
| (first_good_char == STR::npos) || (last_good_char == STR::npos)) { |
| bool input_was_empty = input.empty(); // in case output == &input |
| output->clear(); |
| return input_was_empty ? TRIM_NONE : positions; |
| } |
| |
| // Trim the whitespace. |
| *output = |
| input.substr(first_good_char, last_good_char - first_good_char + 1); |
| |
| // Return where we trimmed from. |
| return static_cast<TrimPositions>( |
| ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | |
| ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); |
| } |
| |
| } // namespace |
| |
| void StringAppendV(std::string* dst, const char* format, va_list ap) { |
| StringAppendVT(dst, format, ap); |
| } |
| |
| std::string StringPrintf(const char* format, ...) { |
| va_list ap; |
| va_start(ap, format); |
| std::string result; |
| StringAppendV(&result, format, ap); |
| va_end(ap); |
| return result; |
| } |
| |
| bool StartsWithASCII(const std::string& str, |
| const std::string& search, |
| bool case_sensitive) { |
| if (case_sensitive) |
| return str.compare(0, search.length(), search) == 0; |
| else |
| return strncasecmp(str.c_str(), search.c_str(), search.length()) == 0; |
| } |
| |
| void SplitString(const std::string& str, |
| char c, |
| std::vector<std::string>* r) { |
| SplitStringT(str, c, true, r); |
| } |
| |
| TrimPositions TrimWhitespaceASCII(const std::string& input, |
| TrimPositions positions, |
| std::string* output) { |
| return TrimStringT(input, kWhitespaceASCII, positions, output); |
| } |
| |
| } // namespace gestures |