michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/string_util.h" michael@0: michael@0: #include "build/build_config.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "base/logging.h" michael@0: #include "base/singleton.h" michael@0: michael@0: namespace { michael@0: michael@0: // Force the singleton used by Empty[W]String[16] to be a unique type. This michael@0: // prevents other code that might accidentally use Singleton from michael@0: // getting our internal one. michael@0: struct EmptyStrings { michael@0: EmptyStrings() {} michael@0: const std::string s; michael@0: const std::wstring ws; michael@0: const string16 s16; michael@0: }; michael@0: michael@0: // Hack to convert any char-like type to its unsigned counterpart. michael@0: // For example, it will convert char, signed char and unsigned char to unsigned michael@0: // char. michael@0: template michael@0: struct ToUnsigned { michael@0: typedef T Unsigned; michael@0: }; michael@0: michael@0: template<> michael@0: struct ToUnsigned { michael@0: typedef unsigned char Unsigned; michael@0: }; michael@0: template<> michael@0: struct ToUnsigned { michael@0: typedef unsigned char Unsigned; michael@0: }; michael@0: template<> michael@0: struct ToUnsigned { michael@0: #if defined(WCHAR_T_IS_UTF16) michael@0: typedef unsigned short Unsigned; michael@0: #elif defined(WCHAR_T_IS_UTF32) michael@0: typedef uint32_t Unsigned; michael@0: #endif michael@0: }; michael@0: template<> michael@0: struct ToUnsigned { michael@0: typedef unsigned short Unsigned; michael@0: }; michael@0: michael@0: // Generalized string-to-number conversion. michael@0: // michael@0: // StringToNumberTraits should provide: michael@0: // - a typedef for string_type, the STL string type used as input. michael@0: // - a typedef for value_type, the target numeric type. michael@0: // - a static function, convert_func, which dispatches to an appropriate michael@0: // strtol-like function and returns type value_type. michael@0: // - a static function, valid_func, which validates |input| and returns a bool michael@0: // indicating whether it is in proper form. This is used to check for michael@0: // conditions that convert_func tolerates but should result in michael@0: // StringToNumber returning false. For strtol-like funtions, valid_func michael@0: // should check for leading whitespace. michael@0: template michael@0: bool StringToNumber(const typename StringToNumberTraits::string_type& input, michael@0: typename StringToNumberTraits::value_type* output) { michael@0: typedef StringToNumberTraits traits; michael@0: michael@0: errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. michael@0: typename traits::string_type::value_type* endptr = NULL; michael@0: typename traits::value_type value = traits::convert_func(input.c_str(), michael@0: &endptr); michael@0: *output = value; michael@0: michael@0: // Cases to return false: michael@0: // - If errno is ERANGE, there was an overflow or underflow. michael@0: // - If the input string is empty, there was nothing to parse. michael@0: // - If endptr does not point to the end of the string, there are either michael@0: // characters remaining in the string after a parsed number, or the string michael@0: // does not begin with a parseable number. endptr is compared to the michael@0: // expected end given the string's stated length to correctly catch cases michael@0: // where the string contains embedded NUL characters. michael@0: // - valid_func determines that the input is not in preferred form. michael@0: return errno == 0 && michael@0: !input.empty() && michael@0: input.c_str() + input.length() == endptr && michael@0: traits::valid_func(input); michael@0: } michael@0: michael@0: class StringToLongTraits { michael@0: public: michael@0: typedef std::string string_type; michael@0: typedef long value_type; michael@0: static const int kBase = 10; michael@0: static inline value_type convert_func(const string_type::value_type* str, michael@0: string_type::value_type** endptr) { michael@0: return strtol(str, endptr, kBase); michael@0: } michael@0: static inline bool valid_func(const string_type& str) { michael@0: return !str.empty() && !isspace(str[0]); michael@0: } michael@0: }; michael@0: michael@0: class String16ToLongTraits { michael@0: public: michael@0: typedef string16 string_type; michael@0: typedef long value_type; michael@0: static const int kBase = 10; michael@0: static inline value_type convert_func(const string_type::value_type* str, michael@0: string_type::value_type** endptr) { michael@0: #if defined(WCHAR_T_IS_UTF16) michael@0: return wcstol(str, endptr, kBase); michael@0: #elif defined(WCHAR_T_IS_UTF32) michael@0: std::string ascii_string = UTF16ToASCII(string16(str)); michael@0: char* ascii_end = NULL; michael@0: value_type ret = strtol(ascii_string.c_str(), &ascii_end, kBase); michael@0: if (ascii_string.c_str() + ascii_string.length() == ascii_end) { michael@0: *endptr = michael@0: const_cast(str) + ascii_string.length(); michael@0: } michael@0: return ret; michael@0: #endif michael@0: } michael@0: static inline bool valid_func(const string_type& str) { michael@0: return !str.empty() && !iswspace(str[0]); michael@0: } michael@0: }; michael@0: michael@0: class StringToInt64Traits { michael@0: public: michael@0: typedef std::string string_type; michael@0: typedef int64_t value_type; michael@0: static const int kBase = 10; michael@0: static inline value_type convert_func(const string_type::value_type* str, michael@0: string_type::value_type** endptr) { michael@0: #ifdef OS_WIN michael@0: return _strtoi64(str, endptr, kBase); michael@0: #else // assume OS_POSIX michael@0: return strtoll(str, endptr, kBase); michael@0: #endif michael@0: } michael@0: static inline bool valid_func(const string_type& str) { michael@0: return !str.empty() && !isspace(str[0]); michael@0: } michael@0: }; michael@0: michael@0: class String16ToInt64Traits { michael@0: public: michael@0: typedef string16 string_type; michael@0: typedef int64_t value_type; michael@0: static const int kBase = 10; michael@0: static inline value_type convert_func(const string_type::value_type* str, michael@0: string_type::value_type** endptr) { michael@0: #ifdef OS_WIN michael@0: return _wcstoi64(str, endptr, kBase); michael@0: #else // assume OS_POSIX michael@0: std::string ascii_string = UTF16ToASCII(string16(str)); michael@0: char* ascii_end = NULL; michael@0: value_type ret = strtoll(ascii_string.c_str(), &ascii_end, kBase); michael@0: if (ascii_string.c_str() + ascii_string.length() == ascii_end) { michael@0: *endptr = michael@0: const_cast(str) + ascii_string.length(); michael@0: } michael@0: return ret; michael@0: #endif michael@0: } michael@0: static inline bool valid_func(const string_type& str) { michael@0: return !str.empty() && !iswspace(str[0]); michael@0: } michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: michael@0: namespace base { michael@0: michael@0: bool IsWprintfFormatPortable(const wchar_t* format) { michael@0: for (const wchar_t* position = format; *position != '\0'; ++position) { michael@0: michael@0: if (*position == '%') { michael@0: bool in_specification = true; michael@0: bool modifier_l = false; michael@0: while (in_specification) { michael@0: // Eat up characters until reaching a known specifier. michael@0: if (*++position == '\0') { michael@0: // The format string ended in the middle of a specification. Call michael@0: // it portable because no unportable specifications were found. The michael@0: // string is equally broken on all platforms. michael@0: return true; michael@0: } michael@0: michael@0: if (*position == 'l') { michael@0: // 'l' is the only thing that can save the 's' and 'c' specifiers. michael@0: modifier_l = true; michael@0: } else if (((*position == 's' || *position == 'c') && !modifier_l) || michael@0: *position == 'S' || *position == 'C' || *position == 'F' || michael@0: *position == 'D' || *position == 'O' || *position == 'U') { michael@0: // Not portable. michael@0: return false; michael@0: } michael@0: michael@0: if (wcschr(L"diouxXeEfgGaAcspn%", *position)) { michael@0: // Portable, keep scanning the rest of the format string. michael@0: in_specification = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: } // namespace base michael@0: michael@0: static const wchar_t kWhitespaceWide[] = { michael@0: 0x0009, // to michael@0: 0x000A, michael@0: 0x000B, michael@0: 0x000C, michael@0: 0x000D, michael@0: 0x0020, // Space michael@0: 0x0085, // michael@0: 0x00A0, // No-Break Space michael@0: 0x1680, // Ogham Space Mark michael@0: 0x180E, // Mongolian Vowel Separator michael@0: 0x2000, // En Quad to Hair Space michael@0: 0x2001, michael@0: 0x2002, michael@0: 0x2003, michael@0: 0x2004, michael@0: 0x2005, michael@0: 0x2006, michael@0: 0x2007, michael@0: 0x2008, michael@0: 0x2009, michael@0: 0x200A, michael@0: 0x200C, // Zero Width Non-Joiner michael@0: 0x2028, // Line Separator michael@0: 0x2029, // Paragraph Separator michael@0: 0x202F, // Narrow No-Break Space michael@0: 0x205F, // Medium Mathematical Space michael@0: 0x3000, // Ideographic Space michael@0: 0 michael@0: }; michael@0: static const char kWhitespaceASCII[] = { michael@0: 0x09, // to michael@0: 0x0A, michael@0: 0x0B, michael@0: 0x0C, michael@0: 0x0D, michael@0: 0x20, // Space michael@0: 0 michael@0: }; michael@0: michael@0: template michael@0: TrimPositions TrimStringT(const STR& input, michael@0: const typename STR::value_type trim_chars[], michael@0: TrimPositions positions, michael@0: STR* output) { michael@0: // Find the edges of leading/trailing whitespace as desired. michael@0: const typename STR::size_type last_char = input.length() - 1; michael@0: const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ? michael@0: input.find_first_not_of(trim_chars) : 0; michael@0: const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ? michael@0: input.find_last_not_of(trim_chars) : last_char; michael@0: michael@0: // When the string was all whitespace, report that we stripped off whitespace michael@0: // from whichever position the caller was interested in. For empty input, we michael@0: // stripped no whitespace, but we still need to clear |output|. michael@0: if (input.empty() || michael@0: (first_good_char == STR::npos) || (last_good_char == STR::npos)) { michael@0: bool input_was_empty = input.empty(); // in case output == &input michael@0: output->clear(); michael@0: return input_was_empty ? TRIM_NONE : positions; michael@0: } michael@0: michael@0: // Trim the whitespace. michael@0: *output = michael@0: input.substr(first_good_char, last_good_char - first_good_char + 1); michael@0: michael@0: // Return where we trimmed from. michael@0: return static_cast( michael@0: ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | michael@0: ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); michael@0: } michael@0: michael@0: TrimPositions TrimWhitespace(const std::wstring& input, michael@0: TrimPositions positions, michael@0: std::wstring* output) { michael@0: return TrimStringT(input, kWhitespaceWide, positions, output); michael@0: } michael@0: michael@0: TrimPositions TrimWhitespaceASCII(const std::string& input, michael@0: TrimPositions positions, michael@0: std::string* output) { michael@0: return TrimStringT(input, kWhitespaceASCII, positions, output); michael@0: } michael@0: michael@0: // This function is only for backward-compatibility. michael@0: // To be removed when all callers are updated. michael@0: TrimPositions TrimWhitespace(const std::string& input, michael@0: TrimPositions positions, michael@0: std::string* output) { michael@0: return TrimWhitespaceASCII(input, positions, output); michael@0: } michael@0: michael@0: std::string WideToASCII(const std::wstring& wide) { michael@0: DCHECK(IsStringASCII(wide)); michael@0: return std::string(wide.begin(), wide.end()); michael@0: } michael@0: michael@0: std::wstring ASCIIToWide(const std::string& ascii) { michael@0: DCHECK(IsStringASCII(ascii)); michael@0: return std::wstring(ascii.begin(), ascii.end()); michael@0: } michael@0: michael@0: std::string UTF16ToASCII(const string16& utf16) { michael@0: DCHECK(IsStringASCII(utf16)); michael@0: return std::string(utf16.begin(), utf16.end()); michael@0: } michael@0: michael@0: string16 ASCIIToUTF16(const std::string& ascii) { michael@0: DCHECK(IsStringASCII(ascii)); michael@0: return string16(ascii.begin(), ascii.end()); michael@0: } michael@0: michael@0: template michael@0: static bool DoIsStringASCII(const STR& str) { michael@0: for (size_t i = 0; i < str.length(); i++) { michael@0: typename ToUnsigned::Unsigned c = str[i]; michael@0: if (c > 0x7F) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool IsStringASCII(const std::wstring& str) { michael@0: return DoIsStringASCII(str); michael@0: } michael@0: michael@0: #if !defined(WCHAR_T_IS_UTF16) michael@0: bool IsStringASCII(const string16& str) { michael@0: return DoIsStringASCII(str); michael@0: } michael@0: #endif michael@0: michael@0: bool IsStringASCII(const std::string& str) { michael@0: return DoIsStringASCII(str); michael@0: } michael@0: michael@0: // Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter michael@0: // is the size of the buffer. These return the number of characters in the michael@0: // formatted string excluding the NUL terminator. If the buffer is not michael@0: // large enough to accommodate the formatted string without truncation, they michael@0: // return the number of characters that would be in the fully-formatted string michael@0: // (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). michael@0: inline int vsnprintfT(char* buffer, michael@0: size_t buf_size, michael@0: const char* format, michael@0: va_list argptr) { michael@0: return base::vsnprintf(buffer, buf_size, format, argptr); michael@0: } michael@0: michael@0: inline int vsnprintfT(wchar_t* buffer, michael@0: size_t buf_size, michael@0: const wchar_t* format, michael@0: va_list argptr) { michael@0: return base::vswprintf(buffer, buf_size, format, argptr); michael@0: } michael@0: michael@0: // Templatized backend for StringPrintF/StringAppendF. This does not finalize michael@0: // the va_list, the caller is expected to do that. michael@0: template michael@0: static void StringAppendVT(StringType* dst, michael@0: const typename StringType::value_type* format, michael@0: va_list ap) { michael@0: // First try with a small fixed size buffer. michael@0: // This buffer size should be kept in sync with StringUtilTest.GrowBoundary michael@0: // and StringUtilTest.StringPrintfBounds. michael@0: typename StringType::value_type stack_buf[1024]; michael@0: michael@0: va_list backup_ap; michael@0: base_va_copy(backup_ap, ap); michael@0: michael@0: #if !defined(OS_WIN) michael@0: errno = 0; michael@0: #endif michael@0: int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, backup_ap); michael@0: va_end(backup_ap); michael@0: michael@0: if (result >= 0 && result < static_cast(arraysize(stack_buf))) { michael@0: // It fit. michael@0: dst->append(stack_buf, result); michael@0: return; michael@0: } michael@0: michael@0: // Repeatedly increase buffer size until it fits. michael@0: int mem_length = arraysize(stack_buf); michael@0: while (true) { michael@0: if (result < 0) { michael@0: #if !defined(OS_WIN) michael@0: // On Windows, vsnprintfT always returns the number of characters in a michael@0: // fully-formatted string, so if we reach this point, something else is michael@0: // wrong and no amount of buffer-doubling is going to fix it. michael@0: if (errno != 0 && errno != EOVERFLOW) michael@0: #endif michael@0: { michael@0: // If an error other than overflow occurred, it's never going to work. michael@0: DLOG(WARNING) << "Unable to printf the requested string due to error."; michael@0: return; michael@0: } michael@0: // Try doubling the buffer size. michael@0: mem_length *= 2; michael@0: } else { michael@0: // We need exactly "result + 1" characters. michael@0: mem_length = result + 1; michael@0: } michael@0: michael@0: if (mem_length > 32 * 1024 * 1024) { michael@0: // That should be plenty, don't try anything larger. This protects michael@0: // against huge allocations when using vsnprintfT implementations that michael@0: // return -1 for reasons other than overflow without setting errno. michael@0: DLOG(WARNING) << "Unable to printf the requested string due to size."; michael@0: return; michael@0: } michael@0: michael@0: std::vector mem_buf(mem_length); michael@0: michael@0: // Restore the va_list before we use it again. michael@0: base_va_copy(backup_ap, ap); michael@0: michael@0: result = vsnprintfT(&mem_buf[0], mem_length, format, ap); michael@0: va_end(backup_ap); michael@0: michael@0: if ((result >= 0) && (result < mem_length)) { michael@0: // It fit. michael@0: dst->append(&mem_buf[0], result); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: template michael@0: struct IntToStringT { michael@0: michael@0: // This is to avoid a compiler warning about unary minus on unsigned type. michael@0: // For example, say you had the following code: michael@0: // template michael@0: // INT abs(INT value) { return value < 0 ? -value : value; } michael@0: // Even though if INT is unsigned, it's impossible for value < 0, so the michael@0: // unary minus will never be taken, the compiler will still generate a michael@0: // warning. We do a little specialization dance... michael@0: template michael@0: struct ToUnsignedT { }; michael@0: michael@0: template michael@0: struct ToUnsignedT { michael@0: static UINT2 ToUnsigned(INT2 value) { michael@0: return static_cast(value); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct ToUnsignedT { michael@0: static UINT2 ToUnsigned(INT2 value) { michael@0: return static_cast(value < 0 ? -value : value); michael@0: } michael@0: }; michael@0: michael@0: // This set of templates is very similar to the above templates, but michael@0: // for testing whether an integer is negative. michael@0: template michael@0: struct TestNegT {}; michael@0: template michael@0: struct TestNegT { michael@0: static bool TestNeg(INT2 value) { michael@0: // value is unsigned, and can never be negative. michael@0: return false; michael@0: } michael@0: }; michael@0: template michael@0: struct TestNegT { michael@0: static bool TestNeg(INT2 value) { michael@0: return value < 0; michael@0: } michael@0: }; michael@0: michael@0: static STR IntToString(INT value) { michael@0: // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. michael@0: // So round up to allocate 3 output characters per byte, plus 1 for '-'. michael@0: const int kOutputBufSize = 3 * sizeof(INT) + 1; michael@0: michael@0: // Allocate the whole string right away, we will right back to front, and michael@0: // then return the substr of what we ended up using. michael@0: STR outbuf(kOutputBufSize, 0); michael@0: michael@0: bool is_neg = TestNegT::TestNeg(value); michael@0: // Even though is_neg will never be true when INT is parameterized as michael@0: // unsigned, even the presence of the unary operation causes a warning. michael@0: UINT res = ToUnsignedT::ToUnsigned(value); michael@0: michael@0: for (typename STR::iterator it = outbuf.end();;) { michael@0: --it; michael@0: DCHECK(it != outbuf.begin()); michael@0: *it = static_cast((res % 10) + '0'); michael@0: res /= 10; michael@0: michael@0: // We're done.. michael@0: if (res == 0) { michael@0: if (is_neg) { michael@0: --it; michael@0: DCHECK(it != outbuf.begin()); michael@0: *it = static_cast('-'); michael@0: } michael@0: return STR(it, outbuf.end()); michael@0: } michael@0: } michael@0: NOTREACHED(); michael@0: return STR(); michael@0: } michael@0: }; michael@0: michael@0: } michael@0: michael@0: std::string IntToString(int value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::wstring IntToWString(int value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::string UintToString(unsigned int value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::wstring UintToWString(unsigned int value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::string Int64ToString(int64_t value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::wstring Int64ToWString(int64_t value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::string Uint64ToString(uint64_t value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: std::wstring Uint64ToWString(uint64_t value) { michael@0: return IntToStringT:: michael@0: IntToString(value); michael@0: } michael@0: michael@0: // Lower-level routine that takes a va_list and appends to a specified michael@0: // string. All other routines are just convenience wrappers around it. michael@0: static void StringAppendV(std::string* dst, const char* format, va_list ap) { michael@0: StringAppendVT(dst, format, ap); michael@0: } michael@0: michael@0: static void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { michael@0: StringAppendVT(dst, format, ap); michael@0: } michael@0: michael@0: std::string StringPrintf(const char* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: std::string result; michael@0: StringAppendV(&result, format, ap); michael@0: va_end(ap); michael@0: return result; michael@0: } michael@0: michael@0: std::wstring StringPrintf(const wchar_t* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: std::wstring result; michael@0: StringAppendV(&result, format, ap); michael@0: va_end(ap); michael@0: return result; michael@0: } michael@0: michael@0: const std::string& SStringPrintf(std::string* dst, const char* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: dst->clear(); michael@0: StringAppendV(dst, format, ap); michael@0: va_end(ap); michael@0: return *dst; michael@0: } michael@0: michael@0: const std::wstring& SStringPrintf(std::wstring* dst, michael@0: const wchar_t* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: dst->clear(); michael@0: StringAppendV(dst, format, ap); michael@0: va_end(ap); michael@0: return *dst; michael@0: } michael@0: michael@0: void StringAppendF(std::string* dst, const char* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: StringAppendV(dst, format, ap); michael@0: va_end(ap); michael@0: } michael@0: michael@0: void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: StringAppendV(dst, format, ap); michael@0: va_end(ap); michael@0: } michael@0: michael@0: template michael@0: static void SplitStringT(const STR& str, michael@0: const typename STR::value_type s, michael@0: bool trim_whitespace, michael@0: std::vector* r) { michael@0: size_t last = 0; michael@0: size_t i; michael@0: size_t c = str.size(); michael@0: for (i = 0; i <= c; ++i) { michael@0: if (i == c || str[i] == s) { michael@0: size_t len = i - last; michael@0: STR tmp = str.substr(last, len); michael@0: if (trim_whitespace) { michael@0: STR t_tmp; michael@0: TrimWhitespace(tmp, TRIM_ALL, &t_tmp); michael@0: r->push_back(t_tmp); michael@0: } else { michael@0: r->push_back(tmp); michael@0: } michael@0: last = i + 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SplitString(const std::wstring& str, michael@0: wchar_t s, michael@0: std::vector* r) { michael@0: SplitStringT(str, s, true, r); michael@0: } michael@0: michael@0: void SplitString(const std::string& str, michael@0: char s, michael@0: std::vector* r) { michael@0: SplitStringT(str, s, true, r); michael@0: } michael@0: michael@0: // For the various *ToInt conversions, there are no *ToIntTraits classes to use michael@0: // because there's no such thing as strtoi. Use *ToLongTraits through a cast michael@0: // instead, requiring that long and int are compatible and equal-width. They michael@0: // are on our target platforms. michael@0: michael@0: // XXX Sigh. michael@0: michael@0: #if !defined(ARCH_CPU_64_BITS) michael@0: bool StringToInt(const std::string& input, int* output) { michael@0: COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); michael@0: return StringToNumber(input, michael@0: reinterpret_cast(output)); michael@0: } michael@0: michael@0: bool StringToInt(const string16& input, int* output) { michael@0: COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); michael@0: return StringToNumber(input, michael@0: reinterpret_cast(output)); michael@0: } michael@0: michael@0: #else michael@0: bool StringToInt(const std::string& input, int* output) { michael@0: long tmp; michael@0: bool ok = StringToNumber(input, &tmp); michael@0: if (!ok || tmp > kint32max) { michael@0: return false; michael@0: } michael@0: *output = static_cast(tmp); michael@0: return true; michael@0: } michael@0: michael@0: bool StringToInt(const string16& input, int* output) { michael@0: long tmp; michael@0: bool ok = StringToNumber(input, &tmp); michael@0: if (!ok || tmp > kint32max) { michael@0: return false; michael@0: } michael@0: *output = static_cast(tmp); michael@0: return true; michael@0: } michael@0: #endif // !defined(ARCH_CPU_64_BITS) michael@0: michael@0: bool StringToInt64(const std::string& input, int64_t* output) { michael@0: return StringToNumber(input, output); michael@0: } michael@0: michael@0: bool StringToInt64(const string16& input, int64_t* output) { michael@0: return StringToNumber(input, output); michael@0: } michael@0: michael@0: int StringToInt(const std::string& value) { michael@0: int result; michael@0: StringToInt(value, &result); michael@0: return result; michael@0: } michael@0: michael@0: int StringToInt(const string16& value) { michael@0: int result; michael@0: StringToInt(value, &result); michael@0: return result; michael@0: } michael@0: michael@0: int64_t StringToInt64(const std::string& value) { michael@0: int64_t result; michael@0: StringToInt64(value, &result); michael@0: return result; michael@0: } michael@0: michael@0: int64_t StringToInt64(const string16& value) { michael@0: int64_t result; michael@0: StringToInt64(value, &result); michael@0: return result; michael@0: } michael@0: michael@0: // The following code is compatible with the OpenBSD lcpy interface. See: michael@0: // http://www.gratisoft.us/todd/papers/strlcpy.html michael@0: // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c michael@0: michael@0: namespace { michael@0: michael@0: template michael@0: size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { michael@0: for (size_t i = 0; i < dst_size; ++i) { michael@0: if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. michael@0: return i; michael@0: } michael@0: michael@0: // We were left off at dst_size. We over copied 1 byte. Null terminate. michael@0: if (dst_size != 0) michael@0: dst[dst_size - 1] = 0; michael@0: michael@0: // Count the rest of the |src|, and return it's length in characters. michael@0: while (src[dst_size]) ++dst_size; michael@0: return dst_size; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { michael@0: return lcpyT(dst, src, dst_size); michael@0: } michael@0: size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { michael@0: return lcpyT(dst, src, dst_size); michael@0: }