1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 The code included in this file is provided under the terms of the ISC license 11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission 12 To use, copy, modify, and/or distribute this software for any purpose with or 13 without fee is hereby granted provided that the above copyright notice and 14 this permission notice appear in all copies. 15 16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 18 DISCLAIMED. 19 20 ============================================================================== 21 */ 22 23 namespace juce 24 { 25 26 //============================================================================== 27 #if JUCE_WINDOWS && ! DOXYGEN 28 #define JUCE_NATIVE_WCHAR_IS_UTF8 0 29 #define JUCE_NATIVE_WCHAR_IS_UTF16 1 30 #define JUCE_NATIVE_WCHAR_IS_UTF32 0 31 #else 32 /** This macro will be set to 1 if the compiler's native wchar_t is an 8-bit type. */ 33 #define JUCE_NATIVE_WCHAR_IS_UTF8 0 34 /** This macro will be set to 1 if the compiler's native wchar_t is a 16-bit type. */ 35 #define JUCE_NATIVE_WCHAR_IS_UTF16 0 36 /** This macro will be set to 1 if the compiler's native wchar_t is a 32-bit type. */ 37 #define JUCE_NATIVE_WCHAR_IS_UTF32 1 38 #endif 39 40 #if JUCE_NATIVE_WCHAR_IS_UTF32 || DOXYGEN 41 /** A platform-independent 32-bit unicode character type. */ 42 using juce_wchar = wchar_t; 43 #else 44 using juce_wchar = uint32; 45 #endif 46 47 #ifndef DOXYGEN 48 /** This macro is deprecated, but preserved for compatibility with old code. */ 49 #define JUCE_T(stringLiteral) (L##stringLiteral) 50 #endif 51 52 #if JUCE_DEFINE_T_MACRO 53 /** The 'T' macro is an alternative for using the "L" prefix in front of a string literal. 54 55 This macro is deprecated, but available for compatibility with old code if you set 56 JUCE_DEFINE_T_MACRO = 1. The fastest, most portable and best way to write your string 57 literals is as standard char strings, using escaped utf-8 character sequences for extended 58 characters, rather than trying to store them as wide-char strings. 59 */ 60 #define T(stringLiteral) JUCE_T(stringLiteral) 61 #endif 62 63 #if ! DOXYGEN 64 65 //============================================================================== 66 // GNU libstdc++ does not have std::make_unsigned 67 namespace internal 68 { 69 template <typename Type> struct make_unsigned { using type = Type; }; 70 template <> struct make_unsigned<signed char> { using type = unsigned char; }; 71 template <> struct make_unsigned<char> { using type = unsigned char; }; 72 template <> struct make_unsigned<short> { using type = unsigned short; }; 73 template <> struct make_unsigned<int> { using type = unsigned int; }; 74 template <> struct make_unsigned<long> { using type = unsigned long; }; 75 template <> struct make_unsigned<long long> { using type = unsigned long long; }; 76 } 77 78 #endif 79 80 //============================================================================== 81 /** 82 A collection of functions for manipulating characters and character strings. 83 84 Most of these methods are designed for internal use by the String and CharPointer 85 classes, but some of them may be useful to call directly. 86 87 @see String, CharPointer_UTF8, CharPointer_UTF16, CharPointer_UTF32 88 89 @tags{Core} 90 */ 91 class JUCE_API CharacterFunctions 92 { 93 public: 94 //============================================================================== 95 /** Converts a character to upper-case. */ 96 static juce_wchar toUpperCase (juce_wchar character) noexcept; 97 /** Converts a character to lower-case. */ 98 static juce_wchar toLowerCase (juce_wchar character) noexcept; 99 100 /** Checks whether a unicode character is upper-case. */ 101 static bool isUpperCase (juce_wchar character) noexcept; 102 /** Checks whether a unicode character is lower-case. */ 103 static bool isLowerCase (juce_wchar character) noexcept; 104 105 /** Checks whether a character is whitespace. */ 106 static bool isWhitespace (char character) noexcept; 107 /** Checks whether a character is whitespace. */ 108 static bool isWhitespace (juce_wchar character) noexcept; 109 110 /** Checks whether a character is a digit. */ 111 static bool isDigit (char character) noexcept; 112 /** Checks whether a character is a digit. */ 113 static bool isDigit (juce_wchar character) noexcept; 114 115 /** Checks whether a character is alphabetic. */ 116 static bool isLetter (char character) noexcept; 117 /** Checks whether a character is alphabetic. */ 118 static bool isLetter (juce_wchar character) noexcept; 119 120 /** Checks whether a character is alphabetic or numeric. */ 121 static bool isLetterOrDigit (char character) noexcept; 122 /** Checks whether a character is alphabetic or numeric. */ 123 static bool isLetterOrDigit (juce_wchar character) noexcept; 124 125 /** Checks whether a character is a printable character, i.e. alphabetic, numeric, 126 a punctuation character or a space. 127 */ 128 static bool isPrintable (char character) noexcept; 129 130 /** Checks whether a character is a printable character, i.e. alphabetic, numeric, 131 a punctuation character or a space. 132 */ 133 static bool isPrintable (juce_wchar character) noexcept; 134 135 /** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */ 136 static int getHexDigitValue (juce_wchar digit) noexcept; 137 138 /** Converts a byte of Windows 1252 codepage to unicode. */ 139 static juce_wchar getUnicodeCharFromWindows1252Codepage (uint8 windows1252Char) noexcept; 140 141 //============================================================================== 142 /** Parses a character string to read a floating-point number. 143 Note that this will advance the pointer that is passed in, leaving it at 144 the end of the number. 145 */ 146 template <typename CharPointerType> 147 static double readDoubleValue (CharPointerType& text) noexcept 148 { 149 #if JUCE_MINGW 150 bool isNegative = false; 151 #else 152 constexpr const int maxSignificantDigits = 17 + 1; // An additional digit for rounding 153 constexpr const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator 154 char buffer[(size_t) bufferSize] = {}; 155 char* currentCharacter = &(buffer[0]); 156 #endif 157 158 text = text.findEndOfWhitespace(); 159 auto c = *text; 160 161 switch (c) 162 { 163 case '-': 164 #if JUCE_MINGW 165 isNegative = true; 166 #else 167 *currentCharacter++ = '-'; 168 #endif 169 JUCE_FALLTHROUGH 170 case '+': 171 c = *++text; 172 break; 173 default: 174 break; 175 } 176 177 switch (c) 178 { 179 case 'n': 180 case 'N': 181 if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N')) 182 return std::numeric_limits<double>::quiet_NaN(); 183 break; 184 185 case 'i': 186 case 'I': 187 if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F')) 188 return std::numeric_limits<double>::infinity(); 189 break; 190 191 default: 192 break; 193 } 194 195 #if JUCE_MINGW 196 // MinGW does not have access to the locale functions required for strtold, so we parse the doubles 197 // ourselves. There are some edge cases where the least significant digit will be wrong! 198 double result[3] = { 0 }, accumulator[2] = { 0 }; 199 int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 }; 200 int exponent = 0, decPointIndex = 0, digit = 0; 201 int lastDigit = 0, numSignificantDigits = 0; 202 bool digitsFound = false; 203 constexpr const int maxSignificantDigits = 17 + 1; 204 205 for (;;) 206 { 207 if (text.isDigit()) 208 { 209 lastDigit = digit; 210 digit = (int) text.getAndAdvance() - '0'; 211 digitsFound = true; 212 213 if (decPointIndex != 0) 214 exponentAdjustment[1]++; 215 216 if (numSignificantDigits == 0 && digit == 0) 217 continue; 218 219 if (++numSignificantDigits > maxSignificantDigits) 220 { 221 if (digit > 5) 222 ++accumulator [decPointIndex]; 223 else if (digit == 5 && (lastDigit & 1) != 0) 224 ++accumulator [decPointIndex]; 225 226 if (decPointIndex > 0) 227 exponentAdjustment[1]--; 228 else 229 exponentAdjustment[0]++; 230 231 while (text.isDigit()) 232 { 233 ++text; 234 if (decPointIndex == 0) 235 exponentAdjustment[0]++; 236 } 237 } 238 else 239 { 240 const auto maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10); 241 if (accumulator [decPointIndex] > maxAccumulatorValue) 242 { 243 result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex]) 244 + accumulator [decPointIndex]; 245 accumulator [decPointIndex] = 0; 246 exponentAccumulator [decPointIndex] = 0; 247 } 248 249 accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit; 250 exponentAccumulator [decPointIndex]++; 251 } 252 } 253 else if (decPointIndex == 0 && *text == '.') 254 { 255 ++text; 256 decPointIndex = 1; 257 258 if (numSignificantDigits > maxSignificantDigits) 259 { 260 while (text.isDigit()) 261 ++text; 262 break; 263 } 264 } 265 else 266 { 267 break; 268 } 269 } 270 271 result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0]; 272 273 if (decPointIndex != 0) 274 result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1]; 275 276 c = *text; 277 if ((c == 'e' || c == 'E') && digitsFound) 278 { 279 auto negativeExponent = false; 280 281 switch (*++text) 282 { 283 case '-': negativeExponent = true; JUCE_FALLTHROUGH 284 case '+': ++text; 285 } 286 287 while (text.isDigit()) 288 exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0'); 289 290 if (negativeExponent) 291 exponent = -exponent; 292 } 293 294 auto r = mulexp10 (result[0], exponent + exponentAdjustment[0]); 295 if (decPointIndex != 0) 296 r += mulexp10 (result[1], exponent - exponentAdjustment[1]); 297 298 return isNegative ? -r : r; 299 300 #else // ! JUCE_MINGW 301 302 int numSigFigs = 0; 303 bool decimalPointFound = false; 304 int extraExponent = 0; 305 306 for (;;) 307 { 308 if (text.isDigit()) 309 { 310 auto digit = (int) text.getAndAdvance() - '0'; 311 312 if (decimalPointFound) 313 { 314 if (numSigFigs >= maxSignificantDigits) 315 continue; 316 } 317 else 318 { 319 if (numSigFigs >= maxSignificantDigits) 320 { 321 ++extraExponent; 322 continue; 323 } 324 325 if (numSigFigs == 0 && digit == 0) 326 continue; 327 } 328 329 *currentCharacter++ = (char) ('0' + (char) digit); 330 numSigFigs++; 331 } 332 else if ((! decimalPointFound) && *text == '.') 333 { 334 ++text; 335 *currentCharacter++ = '.'; 336 decimalPointFound = true; 337 } 338 else 339 { 340 break; 341 } 342 } 343 344 c = *text; 345 346 auto writeExponentDigits = [] (int exponent, char* destination) 347 { 348 auto exponentDivisor = 100; 349 350 while (exponentDivisor > 1) 351 { 352 auto digit = exponent / exponentDivisor; 353 *destination++ = (char) ('0' + (char) digit); 354 exponent -= digit * exponentDivisor; 355 exponentDivisor /= 10; 356 } 357 358 *destination++ = (char) ('0' + (char) exponent); 359 }; 360 361 if ((c == 'e' || c == 'E') && numSigFigs > 0) 362 { 363 *currentCharacter++ = 'e'; 364 bool parsedExponentIsPositive = true; 365 366 switch (*++text) 367 { 368 case '-': parsedExponentIsPositive = false; JUCE_FALLTHROUGH 369 case '+': ++text; break; 370 default: break; 371 } 372 373 int exponent = 0; 374 375 while (text.isDigit()) 376 { 377 auto digit = (int) text.getAndAdvance() - '0'; 378 379 if (digit != 0 || exponent != 0) 380 exponent = (exponent * 10) + digit; 381 } 382 383 exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent); 384 385 if (exponent < 0) 386 *currentCharacter++ = '-'; 387 388 exponent = std::abs (exponent); 389 390 if (exponent > std::numeric_limits<double>::max_exponent10) 391 return std::numeric_limits<double>::quiet_NaN(); 392 393 writeExponentDigits (exponent, currentCharacter); 394 } 395 else if (extraExponent > 0) 396 { 397 *currentCharacter++ = 'e'; 398 writeExponentDigits (extraExponent, currentCharacter); 399 } 400 401 #if JUCE_WINDOWS 402 static _locale_t locale = _create_locale (LC_ALL, "C"); 403 return _strtod_l (&buffer[0], nullptr, locale); 404 #else 405 static locale_t locale = newlocale (LC_ALL_MASK, "C", nullptr); 406 #if JUCE_ANDROID 407 return (double) strtold_l (&buffer[0], nullptr, locale); 408 #else 409 return strtod_l (&buffer[0], nullptr, locale); 410 #endif 411 #endif 412 413 #endif // JUCE_MINGW 414 } 415 416 /** Parses a character string, to read a floating-point value. */ 417 template <typename CharPointerType> 418 static double getDoubleValue (CharPointerType text) noexcept 419 { 420 return readDoubleValue (text); 421 } 422 423 //============================================================================== 424 /** Parses a character string, to read an integer value. */ 425 template <typename IntType, typename CharPointerType> 426 static IntType getIntValue (const CharPointerType text) noexcept 427 { 428 using UIntType = typename internal::make_unsigned<IntType>::type; 429 430 UIntType v = 0; 431 auto s = text.findEndOfWhitespace(); 432 const bool isNeg = *s == '-'; 433 434 if (isNeg) 435 ++s; 436 437 for (;;) 438 { 439 auto c = s.getAndAdvance(); 440 441 if (c >= '0' && c <= '9') 442 v = v * 10 + (UIntType) (c - '0'); 443 else 444 break; 445 } 446 447 return isNeg ? - (IntType) v : (IntType) v; 448 } 449 450 /** Parses a character string, to read a hexadecimal value. */ 451 template <typename ResultType> 452 struct HexParser 453 { 454 template <typename CharPointerType> 455 static ResultType parse (CharPointerType t) noexcept 456 { 457 ResultType result = 0; 458 459 while (! t.isEmpty()) 460 { 461 auto hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); 462 463 if (hexValue >= 0) 464 result = (result << 4) | hexValue; 465 } 466 467 return result; 468 } 469 }; 470 471 //============================================================================== 472 /** Counts the number of characters in a given string, stopping if the count exceeds 473 a specified limit. */ 474 template <typename CharPointerType> 475 static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) noexcept 476 { 477 size_t len = 0; 478 479 while (len < maxCharsToCount && text.getAndAdvance() != 0) 480 ++len; 481 482 return len; 483 } 484 485 /** Counts the number of characters in a given string, stopping if the count exceeds 486 a specified end-pointer. */ 487 template <typename CharPointerType> 488 static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept 489 { 490 size_t len = 0; 491 492 while (start < end && start.getAndAdvance() != 0) 493 ++len; 494 495 return len; 496 } 497 498 /** Copies null-terminated characters from one string to another. */ 499 template <typename DestCharPointerType, typename SrcCharPointerType> 500 static void copyAll (DestCharPointerType& dest, SrcCharPointerType src) noexcept 501 { 502 while (auto c = src.getAndAdvance()) 503 dest.write (c); 504 505 dest.writeNull(); 506 } 507 508 /** Copies characters from one string to another, up to a null terminator 509 or a given byte size limit. */ 510 template <typename DestCharPointerType, typename SrcCharPointerType> 511 static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept 512 { 513 auto startAddress = dest.getAddress(); 514 auto maxBytes = (ssize_t) maxBytesToWrite; 515 maxBytes -= (ssize_t) sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) 516 517 for (;;) 518 { 519 auto c = src.getAndAdvance(); 520 auto bytesNeeded = (ssize_t) DestCharPointerType::getBytesRequiredFor (c); 521 maxBytes -= bytesNeeded; 522 523 if (c == 0 || maxBytes < 0) 524 break; 525 526 dest.write (c); 527 } 528 529 dest.writeNull(); 530 531 return (size_t) getAddressDifference (dest.getAddress(), startAddress) 532 + sizeof (typename DestCharPointerType::CharType); 533 } 534 535 /** Copies characters from one string to another, up to a null terminator 536 or a given maximum number of characters. */ 537 template <typename DestCharPointerType, typename SrcCharPointerType> 538 static void copyWithCharLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxChars) noexcept 539 { 540 while (--maxChars > 0) 541 { 542 auto c = src.getAndAdvance(); 543 544 if (c == 0) 545 break; 546 547 dest.write (c); 548 } 549 550 dest.writeNull(); 551 } 552 553 /** Compares two characters. */ 554 static int compare (juce_wchar char1, juce_wchar char2) noexcept 555 { 556 if (auto diff = static_cast<int> (char1) - static_cast<int> (char2)) 557 return diff < 0 ? -1 : 1; 558 559 return 0; 560 } 561 562 /** Compares two null-terminated character strings. */ 563 template <typename CharPointerType1, typename CharPointerType2> 564 static int compare (CharPointerType1 s1, CharPointerType2 s2) noexcept 565 { 566 for (;;) 567 { 568 auto c1 = s1.getAndAdvance(); 569 570 if (auto diff = compare (c1, s2.getAndAdvance())) 571 return diff; 572 573 if (c1 == 0) 574 break; 575 } 576 577 return 0; 578 } 579 580 /** Compares two null-terminated character strings, up to a given number of characters. */ 581 template <typename CharPointerType1, typename CharPointerType2> 582 static int compareUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept 583 { 584 while (--maxChars >= 0) 585 { 586 auto c1 = s1.getAndAdvance(); 587 588 if (auto diff = compare (c1, s2.getAndAdvance())) 589 return diff; 590 591 if (c1 == 0) 592 break; 593 } 594 595 return 0; 596 } 597 598 /** Compares two characters, using a case-independant match. */ 599 static int compareIgnoreCase (juce_wchar char1, juce_wchar char2) noexcept 600 { 601 return char1 != char2 ? compare (toUpperCase (char1), toUpperCase (char2)) : 0; 602 } 603 604 /** Compares two null-terminated character strings, using a case-independant match. */ 605 template <typename CharPointerType1, typename CharPointerType2> 606 static int compareIgnoreCase (CharPointerType1 s1, CharPointerType2 s2) noexcept 607 { 608 for (;;) 609 { 610 auto c1 = s1.getAndAdvance(); 611 612 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance())) 613 return diff; 614 615 if (c1 == 0) 616 break; 617 } 618 619 return 0; 620 } 621 622 /** Compares two null-terminated character strings, using a case-independent match. */ 623 template <typename CharPointerType1, typename CharPointerType2> 624 static int compareIgnoreCaseUpTo (CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept 625 { 626 while (--maxChars >= 0) 627 { 628 auto c1 = s1.getAndAdvance(); 629 630 if (auto diff = compareIgnoreCase (c1, s2.getAndAdvance())) 631 return diff; 632 633 if (c1 == 0) 634 break; 635 } 636 637 return 0; 638 } 639 640 /** Finds the character index of a given substring in another string. 641 Returns -1 if the substring is not found. 642 */ 643 template <typename CharPointerType1, typename CharPointerType2> 644 static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept 645 { 646 int index = 0; 647 auto substringLength = (int) substringToLookFor.length(); 648 649 for (;;) 650 { 651 if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0) 652 return index; 653 654 if (textToSearch.getAndAdvance() == 0) 655 return -1; 656 657 ++index; 658 } 659 } 660 661 /** Returns a pointer to the first occurrence of a substring in a string. 662 If the substring is not found, this will return a pointer to the string's 663 null terminator. 664 */ 665 template <typename CharPointerType1, typename CharPointerType2> 666 static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept 667 { 668 auto substringLength = (int) substringToLookFor.length(); 669 670 while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0 671 && ! textToSearch.isEmpty()) 672 ++textToSearch; 673 674 return textToSearch; 675 } 676 677 /** Returns a pointer to the first occurrence of a substring in a string. 678 If the substring is not found, this will return a pointer to the string's 679 null terminator. 680 */ 681 template <typename CharPointerType> 682 static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept 683 { 684 for (;; ++textToSearch) 685 { 686 auto c = *textToSearch; 687 688 if (c == charToLookFor || c == 0) 689 break; 690 } 691 692 return textToSearch; 693 } 694 695 /** Finds the character index of a given substring in another string, using 696 a case-independent match. 697 Returns -1 if the substring is not found. 698 */ 699 template <typename CharPointerType1, typename CharPointerType2> 700 static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept 701 { 702 int index = 0; 703 auto needleLength = (int) needle.length(); 704 705 for (;;) 706 { 707 if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0) 708 return index; 709 710 if (haystack.getAndAdvance() == 0) 711 return -1; 712 713 ++index; 714 } 715 } 716 717 /** Finds the character index of a given character in another string. 718 Returns -1 if the character is not found. 719 */ 720 template <typename Type> 721 static int indexOfChar (Type text, const juce_wchar charToFind) noexcept 722 { 723 int i = 0; 724 725 while (! text.isEmpty()) 726 { 727 if (text.getAndAdvance() == charToFind) 728 return i; 729 730 ++i; 731 } 732 733 return -1; 734 } 735 736 /** Finds the character index of a given character in another string, using 737 a case-independent match. 738 Returns -1 if the character is not found. 739 */ 740 template <typename Type> 741 static int indexOfCharIgnoreCase (Type text, juce_wchar charToFind) noexcept 742 { 743 charToFind = CharacterFunctions::toLowerCase (charToFind); 744 int i = 0; 745 746 while (! text.isEmpty()) 747 { 748 if (text.toLowerCase() == charToFind) 749 return i; 750 751 ++text; 752 ++i; 753 } 754 755 return -1; 756 } 757 758 /** Returns a pointer to the first non-whitespace character in a string. 759 If the string contains only whitespace, this will return a pointer 760 to its null terminator. 761 */ 762 template <typename Type> 763 static Type findEndOfWhitespace (Type text) noexcept 764 { 765 while (text.isWhitespace()) 766 ++text; 767 768 return text; 769 } 770 771 /** Returns a pointer to the first character in the string which is found in 772 the breakCharacters string. 773 */ 774 template <typename Type, typename BreakType> 775 static Type findEndOfToken (Type text, BreakType breakCharacters, Type quoteCharacters) 776 { 777 juce_wchar currentQuoteChar = 0; 778 779 while (! text.isEmpty()) 780 { 781 auto c = text.getAndAdvance(); 782 783 if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0) 784 { 785 --text; 786 break; 787 } 788 789 if (quoteCharacters.indexOf (c) >= 0) 790 { 791 if (currentQuoteChar == 0) 792 currentQuoteChar = c; 793 else if (currentQuoteChar == c) 794 currentQuoteChar = 0; 795 } 796 } 797 798 return text; 799 } 800 801 private: 802 static double mulexp10 (double value, int exponent) noexcept; 803 }; 804 805 } // namespace juce 806