blob: 398f369b279ed78a588267ec7e7fff6baa09652e [file] [log] [blame]
// 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