string_util.cc (23266B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/string_util.h" 8 9 #include <ctype.h> 10 #include <errno.h> 11 #include <math.h> 12 #include <stdarg.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <time.h> 17 #include <wchar.h> 18 #include <wctype.h> 19 20 #include <vector> 21 22 #include "base/basictypes.h" 23 #include "base/logging.h" 24 25 namespace { 26 27 // Hack to convert any char-like type to its unsigned counterpart. 28 // For example, it will convert char, signed char and unsigned char to unsigned 29 // char. 30 template <typename T> 31 struct ToUnsigned { 32 typedef T Unsigned; 33 }; 34 35 template <> 36 struct ToUnsigned<char> { 37 typedef unsigned char Unsigned; 38 }; 39 template <> 40 struct ToUnsigned<signed char> { 41 typedef unsigned char Unsigned; 42 }; 43 template <> 44 struct ToUnsigned<wchar_t> { 45 #if defined(XP_WIN) 46 typedef unsigned short Unsigned; 47 #else 48 typedef uint32_t Unsigned; 49 #endif 50 }; 51 template <> 52 struct ToUnsigned<short> { 53 typedef unsigned short Unsigned; 54 }; 55 56 // Generalized string-to-number conversion. 57 // 58 // StringToNumberTraits should provide: 59 // - a typedef for string_type, the STL string type used as input. 60 // - a typedef for value_type, the target numeric type. 61 // - a static function, convert_func, which dispatches to an appropriate 62 // strtol-like function and returns type value_type. 63 // - a static function, valid_func, which validates |input| and returns a bool 64 // indicating whether it is in proper form. This is used to check for 65 // conditions that convert_func tolerates but should result in 66 // StringToNumber returning false. For strtol-like funtions, valid_func 67 // should check for leading whitespace. 68 template <typename StringToNumberTraits> 69 bool StringToNumber(const typename StringToNumberTraits::string_type& input, 70 typename StringToNumberTraits::value_type* output) { 71 typedef StringToNumberTraits traits; 72 73 errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. 74 typename traits::string_type::value_type* endptr = NULL; 75 typename traits::value_type value = 76 traits::convert_func(input.c_str(), &endptr); 77 *output = value; 78 79 // Cases to return false: 80 // - If errno is ERANGE, there was an overflow or underflow. 81 // - If the input string is empty, there was nothing to parse. 82 // - If endptr does not point to the end of the string, there are either 83 // characters remaining in the string after a parsed number, or the string 84 // does not begin with a parseable number. endptr is compared to the 85 // expected end given the string's stated length to correctly catch cases 86 // where the string contains embedded NUL characters. 87 // - valid_func determines that the input is not in preferred form. 88 return errno == 0 && !input.empty() && 89 input.c_str() + input.length() == endptr && traits::valid_func(input); 90 } 91 92 class StringToLongTraits { 93 public: 94 typedef std::string string_type; 95 typedef long value_type; 96 static const int kBase = 10; 97 static inline value_type convert_func(const string_type::value_type* str, 98 string_type::value_type** endptr) { 99 return strtol(str, endptr, kBase); 100 } 101 static inline bool valid_func(const string_type& str) { 102 return !str.empty() && !isspace(str[0]); 103 } 104 }; 105 106 class String16ToLongTraits { 107 public: 108 typedef string16 string_type; 109 typedef long value_type; 110 static const int kBase = 10; 111 static inline value_type convert_func(const string_type::value_type* str, 112 string_type::value_type** endptr) { 113 #if defined(XP_WIN) 114 return wcstol(str, endptr, kBase); 115 #else 116 std::string ascii_string = UTF16ToASCII(string16(str)); 117 char* ascii_end = NULL; 118 value_type ret = strtol(ascii_string.c_str(), &ascii_end, kBase); 119 if (ascii_string.c_str() + ascii_string.length() == ascii_end) { 120 *endptr = 121 const_cast<string_type::value_type*>(str) + ascii_string.length(); 122 } 123 return ret; 124 #endif 125 } 126 static inline bool valid_func(const string_type& str) { 127 return !str.empty() && !iswspace(str[0]); 128 } 129 }; 130 131 class StringToInt64Traits { 132 public: 133 typedef std::string string_type; 134 typedef int64_t value_type; 135 static const int kBase = 10; 136 static inline value_type convert_func(const string_type::value_type* str, 137 string_type::value_type** endptr) { 138 #ifdef XP_WIN 139 return _strtoi64(str, endptr, kBase); 140 #else // assume XP_UNIX 141 return strtoll(str, endptr, kBase); 142 #endif 143 } 144 static inline bool valid_func(const string_type& str) { 145 return !str.empty() && !isspace(str[0]); 146 } 147 }; 148 149 class String16ToInt64Traits { 150 public: 151 typedef string16 string_type; 152 typedef int64_t value_type; 153 static const int kBase = 10; 154 static inline value_type convert_func(const string_type::value_type* str, 155 string_type::value_type** endptr) { 156 #ifdef XP_WIN 157 return _wcstoi64(str, endptr, kBase); 158 #else // assume XP_UNIX 159 std::string ascii_string = UTF16ToASCII(string16(str)); 160 char* ascii_end = NULL; 161 value_type ret = strtoll(ascii_string.c_str(), &ascii_end, kBase); 162 if (ascii_string.c_str() + ascii_string.length() == ascii_end) { 163 *endptr = 164 const_cast<string_type::value_type*>(str) + ascii_string.length(); 165 } 166 return ret; 167 #endif 168 } 169 static inline bool valid_func(const string_type& str) { 170 return !str.empty() && !iswspace(str[0]); 171 } 172 }; 173 174 } // namespace 175 176 namespace base { 177 178 bool IsWprintfFormatPortable(const wchar_t* format) { 179 for (const wchar_t* position = format; *position != '\0'; ++position) { 180 if (*position == '%') { 181 bool in_specification = true; 182 bool modifier_l = false; 183 while (in_specification) { 184 // Eat up characters until reaching a known specifier. 185 if (*++position == '\0') { 186 // The format string ended in the middle of a specification. Call 187 // it portable because no unportable specifications were found. The 188 // string is equally broken on all platforms. 189 return true; 190 } 191 192 if (*position == 'l') { 193 // 'l' is the only thing that can save the 's' and 'c' specifiers. 194 modifier_l = true; 195 } else if (((*position == 's' || *position == 'c') && !modifier_l) || 196 *position == 'S' || *position == 'C' || *position == 'F' || 197 *position == 'D' || *position == 'O' || *position == 'U') { 198 // Not portable. 199 return false; 200 } 201 202 if (wcschr(L"diouxXeEfgGaAcspn%", *position)) { 203 // Portable, keep scanning the rest of the format string. 204 in_specification = false; 205 } 206 } 207 } 208 } 209 210 return true; 211 } 212 213 } // namespace base 214 215 static const wchar_t kWhitespaceWide[] = { 216 0x0009, // <control-0009> to <control-000D> 217 0x000A, 0x000B, 0x000C, 0x000D, 218 0x0020, // Space 219 0x0085, // <control-0085> 220 0x00A0, // No-Break Space 221 0x1680, // Ogham Space Mark 222 0x180E, // Mongolian Vowel Separator 223 0x2000, // En Quad to Hair Space 224 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 225 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 226 0x200C, // Zero Width Non-Joiner 227 0x2028, // Line Separator 228 0x2029, // Paragraph Separator 229 0x202F, // Narrow No-Break Space 230 0x205F, // Medium Mathematical Space 231 0x3000, // Ideographic Space 232 0}; 233 static const char kWhitespaceASCII[] = { 234 0x09, // <control-0009> to <control-000D> 235 0x0A, 0x0B, 0x0C, 0x0D, 236 0x20, // Space 237 0}; 238 239 template <typename STR> 240 TrimPositions TrimStringT(const STR& input, 241 const typename STR::value_type trim_chars[], 242 TrimPositions positions, STR* output) { 243 // Find the edges of leading/trailing whitespace as desired. 244 const typename STR::size_type last_char = input.length() - 1; 245 const typename STR::size_type first_good_char = 246 (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; 247 const typename STR::size_type last_good_char = 248 (positions & TRIM_TRAILING) ? input.find_last_not_of(trim_chars) 249 : last_char; 250 251 // When the string was all whitespace, report that we stripped off whitespace 252 // from whichever position the caller was interested in. For empty input, we 253 // stripped no whitespace, but we still need to clear |output|. 254 if (input.empty() || (first_good_char == STR::npos) || 255 (last_good_char == STR::npos)) { 256 bool input_was_empty = input.empty(); // in case output == &input 257 output->clear(); 258 return input_was_empty ? TRIM_NONE : positions; 259 } 260 261 // Trim the whitespace. 262 *output = input.substr(first_good_char, last_good_char - first_good_char + 1); 263 264 // Return where we trimmed from. 265 return static_cast<TrimPositions>( 266 ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | 267 ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); 268 } 269 270 TrimPositions TrimWhitespace(const std::wstring& input, TrimPositions positions, 271 std::wstring* output) { 272 return TrimStringT(input, kWhitespaceWide, positions, output); 273 } 274 275 TrimPositions TrimWhitespaceASCII(const std::string& input, 276 TrimPositions positions, 277 std::string* output) { 278 return TrimStringT(input, kWhitespaceASCII, positions, output); 279 } 280 281 // This function is only for backward-compatibility. 282 // To be removed when all callers are updated. 283 TrimPositions TrimWhitespace(const std::string& input, TrimPositions positions, 284 std::string* output) { 285 return TrimWhitespaceASCII(input, positions, output); 286 } 287 288 std::string WideToASCII(const std::wstring& wide) { 289 DCHECK(IsStringASCII(wide)); 290 return std::string(wide.begin(), wide.end()); 291 } 292 293 std::wstring ASCIIToWide(const std::string& ascii) { 294 DCHECK(IsStringASCII(ascii)); 295 return std::wstring(ascii.begin(), ascii.end()); 296 } 297 298 std::string UTF16ToASCII(const string16& utf16) { 299 DCHECK(IsStringASCII(utf16)); 300 return std::string(utf16.begin(), utf16.end()); 301 } 302 303 string16 ASCIIToUTF16(const std::string& ascii) { 304 DCHECK(IsStringASCII(ascii)); 305 return string16(ascii.begin(), ascii.end()); 306 } 307 308 template <class STR> 309 static bool DoIsStringASCII(const STR& str) { 310 for (size_t i = 0; i < str.length(); i++) { 311 typename ToUnsigned<typename STR::value_type>::Unsigned c = str[i]; 312 if (c > 0x7F) return false; 313 } 314 return true; 315 } 316 317 bool IsStringASCII(const std::wstring& str) { return DoIsStringASCII(str); } 318 319 #if !defined(XP_WIN) 320 bool IsStringASCII(const string16& str) { return DoIsStringASCII(str); } 321 #endif 322 323 bool IsStringASCII(const std::string& str) { return DoIsStringASCII(str); } 324 325 // Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter 326 // is the size of the buffer. These return the number of characters in the 327 // formatted string excluding the NUL terminator. If the buffer is not 328 // large enough to accommodate the formatted string without truncation, they 329 // return the number of characters that would be in the fully-formatted string 330 // (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). 331 inline int vsnprintfT(char* buffer, size_t buf_size, const char* format, 332 va_list argptr) { 333 return base::vsnprintf(buffer, buf_size, format, argptr); 334 } 335 336 inline int vsnprintfT(wchar_t* buffer, size_t buf_size, const wchar_t* format, 337 va_list argptr) { 338 return base::vswprintf(buffer, buf_size, format, argptr); 339 } 340 341 // Templatized backend for StringPrintF/StringAppendF. This does not finalize 342 // the va_list, the caller is expected to do that. 343 template <class StringType> 344 static void StringAppendVT(StringType* dst, 345 const typename StringType::value_type* format, 346 va_list ap) { 347 // First try with a small fixed size buffer. 348 // This buffer size should be kept in sync with StringUtilTest.GrowBoundary 349 // and StringUtilTest.StringPrintfBounds. 350 typename StringType::value_type stack_buf[1024]; 351 352 va_list backup_ap; 353 base_va_copy(backup_ap, ap); 354 355 #if !defined(XP_WIN) 356 errno = 0; 357 #endif 358 int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, backup_ap); 359 va_end(backup_ap); 360 361 if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { 362 // It fit. 363 dst->append(stack_buf, result); 364 return; 365 } 366 367 // Repeatedly increase buffer size until it fits. 368 int mem_length = arraysize(stack_buf); 369 while (true) { 370 if (result < 0) { 371 #if !defined(XP_WIN) 372 // On Windows, vsnprintfT always returns the number of characters in a 373 // fully-formatted string, so if we reach this point, something else is 374 // wrong and no amount of buffer-doubling is going to fix it. 375 if (errno != 0 && errno != EOVERFLOW) 376 #endif 377 { 378 // If an error other than overflow occurred, it's never going to work. 379 DLOG(WARNING) << "Unable to printf the requested string due to error."; 380 return; 381 } 382 // Try doubling the buffer size. 383 mem_length *= 2; 384 } else { 385 // We need exactly "result + 1" characters. 386 mem_length = result + 1; 387 } 388 389 if (mem_length > 32 * 1024 * 1024) { 390 // That should be plenty, don't try anything larger. This protects 391 // against huge allocations when using vsnprintfT implementations that 392 // return -1 for reasons other than overflow without setting errno. 393 DLOG(WARNING) << "Unable to printf the requested string due to size."; 394 return; 395 } 396 397 std::vector<typename StringType::value_type> mem_buf(mem_length); 398 399 // Restore the va_list before we use it again. 400 base_va_copy(backup_ap, ap); 401 402 result = vsnprintfT(&mem_buf[0], mem_length, format, ap); 403 va_end(backup_ap); 404 405 if ((result >= 0) && (result < mem_length)) { 406 // It fit. 407 dst->append(&mem_buf[0], result); 408 return; 409 } 410 } 411 } 412 413 namespace { 414 415 template <typename STR, typename INT, typename UINT, bool NEG> 416 struct IntToStringT { 417 // This is to avoid a compiler warning about unary minus on unsigned type. 418 // For example, say you had the following code: 419 // template <typename INT> 420 // INT abs(INT value) { return value < 0 ? -value : value; } 421 // Even though if INT is unsigned, it's impossible for value < 0, so the 422 // unary minus will never be taken, the compiler will still generate a 423 // warning. We do a little specialization dance... 424 template <typename INT2, typename UINT2, bool NEG2> 425 struct ToUnsignedT {}; 426 427 template <typename INT2, typename UINT2> 428 struct ToUnsignedT<INT2, UINT2, false> { 429 static UINT2 ToUnsigned(INT2 value) { return static_cast<UINT2>(value); } 430 }; 431 432 template <typename INT2, typename UINT2> 433 struct ToUnsignedT<INT2, UINT2, true> { 434 static UINT2 ToUnsigned(INT2 value) { 435 return static_cast<UINT2>(value < 0 ? -value : value); 436 } 437 }; 438 439 // This set of templates is very similar to the above templates, but 440 // for testing whether an integer is negative. 441 template <typename INT2, bool NEG2> 442 struct TestNegT {}; 443 template <typename INT2> 444 struct TestNegT<INT2, false> { 445 static bool TestNeg(INT2 value) { 446 // value is unsigned, and can never be negative. 447 return false; 448 } 449 }; 450 template <typename INT2> 451 struct TestNegT<INT2, true> { 452 static bool TestNeg(INT2 value) { return value < 0; } 453 }; 454 455 static STR IntToString(INT value) { 456 // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. 457 // So round up to allocate 3 output characters per byte, plus 1 for '-'. 458 const int kOutputBufSize = 3 * sizeof(INT) + 1; 459 460 // Allocate the whole string right away, we will right back to front, and 461 // then return the substr of what we ended up using. 462 STR outbuf(kOutputBufSize, 0); 463 464 bool is_neg = TestNegT<INT, NEG>::TestNeg(value); 465 // Even though is_neg will never be true when INT is parameterized as 466 // unsigned, even the presence of the unary operation causes a warning. 467 UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value); 468 469 for (typename STR::iterator it = outbuf.end();;) { 470 --it; 471 DCHECK(it != outbuf.begin()); 472 *it = static_cast<typename STR::value_type>((res % 10) + '0'); 473 res /= 10; 474 475 // We're done.. 476 if (res == 0) { 477 if (is_neg) { 478 --it; 479 DCHECK(it != outbuf.begin()); 480 *it = static_cast<typename STR::value_type>('-'); 481 } 482 return STR(it, outbuf.end()); 483 } 484 } 485 NOTREACHED(); 486 return STR(); 487 } 488 }; 489 490 } // namespace 491 492 std::string IntToString(int value) { 493 return IntToStringT<std::string, int, unsigned int, true>::IntToString(value); 494 } 495 std::wstring IntToWString(int value) { 496 return IntToStringT<std::wstring, int, unsigned int, true>::IntToString( 497 value); 498 } 499 std::string UintToString(unsigned int value) { 500 return IntToStringT<std::string, unsigned int, unsigned int, 501 false>::IntToString(value); 502 } 503 std::wstring UintToWString(unsigned int value) { 504 return IntToStringT<std::wstring, unsigned int, unsigned int, 505 false>::IntToString(value); 506 } 507 std::string Int64ToString(int64_t value) { 508 return IntToStringT<std::string, int64_t, uint64_t, true>::IntToString(value); 509 } 510 std::wstring Int64ToWString(int64_t value) { 511 return IntToStringT<std::wstring, int64_t, uint64_t, true>::IntToString( 512 value); 513 } 514 std::string Uint64ToString(uint64_t value) { 515 return IntToStringT<std::string, uint64_t, uint64_t, false>::IntToString( 516 value); 517 } 518 std::wstring Uint64ToWString(uint64_t value) { 519 return IntToStringT<std::wstring, uint64_t, uint64_t, false>::IntToString( 520 value); 521 } 522 523 // Lower-level routine that takes a va_list and appends to a specified 524 // string. All other routines are just convenience wrappers around it. 525 static void StringAppendV(std::string* dst, const char* format, va_list ap) { 526 StringAppendVT(dst, format, ap); 527 } 528 529 static void StringAppendV(std::wstring* dst, const wchar_t* format, 530 va_list ap) { 531 StringAppendVT(dst, format, ap); 532 } 533 534 std::string StringPrintf(const char* format, ...) { 535 va_list ap; 536 va_start(ap, format); 537 std::string result; 538 StringAppendV(&result, format, ap); 539 va_end(ap); 540 return result; 541 } 542 543 std::wstring StringPrintf(const wchar_t* format, ...) { 544 va_list ap; 545 va_start(ap, format); 546 std::wstring result; 547 StringAppendV(&result, format, ap); 548 va_end(ap); 549 return result; 550 } 551 552 const std::string& SStringPrintf(std::string* dst, const char* format, ...) { 553 va_list ap; 554 va_start(ap, format); 555 dst->clear(); 556 StringAppendV(dst, format, ap); 557 va_end(ap); 558 return *dst; 559 } 560 561 const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, 562 ...) { 563 va_list ap; 564 va_start(ap, format); 565 dst->clear(); 566 StringAppendV(dst, format, ap); 567 va_end(ap); 568 return *dst; 569 } 570 571 void StringAppendF(std::string* dst, const char* format, ...) { 572 va_list ap; 573 va_start(ap, format); 574 StringAppendV(dst, format, ap); 575 va_end(ap); 576 } 577 578 void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { 579 va_list ap; 580 va_start(ap, format); 581 StringAppendV(dst, format, ap); 582 va_end(ap); 583 } 584 585 template <typename STR> 586 static void SplitStringT(const STR& str, const typename STR::value_type s, 587 bool trim_whitespace, std::vector<STR>* r) { 588 size_t last = 0; 589 size_t i; 590 size_t c = str.size(); 591 for (i = 0; i <= c; ++i) { 592 if (i == c || str[i] == s) { 593 size_t len = i - last; 594 STR tmp = str.substr(last, len); 595 if (trim_whitespace) { 596 STR t_tmp; 597 TrimWhitespace(tmp, TRIM_ALL, &t_tmp); 598 r->push_back(t_tmp); 599 } else { 600 r->push_back(tmp); 601 } 602 last = i + 1; 603 } 604 } 605 } 606 607 void SplitString(const std::wstring& str, wchar_t s, 608 std::vector<std::wstring>* r) { 609 SplitStringT(str, s, true, r); 610 } 611 612 void SplitString(const std::string& str, char s, std::vector<std::string>* r) { 613 SplitStringT(str, s, true, r); 614 } 615 616 // For the various *ToInt conversions, there are no *ToIntTraits classes to use 617 // because there's no such thing as strtoi. Use *ToLongTraits through a cast 618 // instead, requiring that long and int are compatible and equal-width. They 619 // are on our target platforms. 620 621 // XXX Sigh. 622 623 #if !defined(HAVE_64BIT_BUILD) 624 bool StringToInt(const std::string& input, int* output) { 625 COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); 626 return StringToNumber<StringToLongTraits>(input, 627 reinterpret_cast<long*>(output)); 628 } 629 630 bool StringToInt(const string16& input, int* output) { 631 COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); 632 return StringToNumber<String16ToLongTraits>(input, 633 reinterpret_cast<long*>(output)); 634 } 635 636 #else 637 bool StringToInt(const std::string& input, int* output) { 638 long tmp; 639 bool ok = StringToNumber<StringToLongTraits>(input, &tmp); 640 if (!ok || tmp > kint32max) { 641 return false; 642 } 643 *output = static_cast<int>(tmp); 644 return true; 645 } 646 647 bool StringToInt(const string16& input, int* output) { 648 long tmp; 649 bool ok = StringToNumber<String16ToLongTraits>(input, &tmp); 650 if (!ok || tmp > kint32max) { 651 return false; 652 } 653 *output = static_cast<int>(tmp); 654 return true; 655 } 656 #endif // !defined(HAVE_64BIT_BUILD) 657 658 bool StringToInt64(const std::string& input, int64_t* output) { 659 return StringToNumber<StringToInt64Traits>(input, output); 660 } 661 662 bool StringToInt64(const string16& input, int64_t* output) { 663 return StringToNumber<String16ToInt64Traits>(input, output); 664 } 665 666 int StringToInt(const std::string& value) { 667 int result; 668 StringToInt(value, &result); 669 return result; 670 } 671 672 int StringToInt(const string16& value) { 673 int result; 674 StringToInt(value, &result); 675 return result; 676 } 677 678 int64_t StringToInt64(const std::string& value) { 679 int64_t result; 680 StringToInt64(value, &result); 681 return result; 682 } 683 684 int64_t StringToInt64(const string16& value) { 685 int64_t result; 686 StringToInt64(value, &result); 687 return result; 688 } 689 690 // The following code is compatible with the OpenBSD lcpy interface. See: 691 // http://www.gratisoft.us/todd/papers/strlcpy.html 692 // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c 693 694 namespace { 695 696 template <typename CHAR> 697 size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { 698 for (size_t i = 0; i < dst_size; ++i) { 699 if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. 700 return i; 701 } 702 703 // We were left off at dst_size. We over copied 1 byte. Null terminate. 704 if (dst_size != 0) dst[dst_size - 1] = 0; 705 706 // Count the rest of the |src|, and return it's length in characters. 707 while (src[dst_size]) ++dst_size; 708 return dst_size; 709 } 710 711 } // namespace 712 713 size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { 714 return lcpyT<char>(dst, src, dst_size); 715 } 716 size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { 717 return lcpyT<wchar_t>(dst, src, dst_size); 718 }