| 562 | |
| 563 | |
| 564 | LPTSTR tcsrstr(LPTSTR aStr, size_t aStr_length, LPCTSTR aPattern, StringCaseSenseType aStringCaseSense, int aOccurrence) |
| 565 | // Returns NULL if not found, otherwise the address of the found string. |
| 566 | // This could probably use a faster algorithm someday. For now it seems adequate because |
| 567 | // scripts rarely use it and when they do, it's usually on short haystack strings (such as |
| 568 | // to find the last period in a filename). |
| 569 | { |
| 570 | if (aOccurrence < 1) |
| 571 | return NULL; |
| 572 | if (!*aPattern) |
| 573 | // The empty string is found in every string, and since we're searching from the right, return |
| 574 | // the position of the zero terminator to indicate the situation: |
| 575 | return aStr + aStr_length; |
| 576 | |
| 577 | size_t aPattern_length = _tcslen(aPattern); |
| 578 | TCHAR aPattern_last_char = aPattern[aPattern_length - 1]; |
| 579 | TCHAR aPattern_last_char_lower = (aStringCaseSense == SCS_INSENSITIVE_LOCALE) |
| 580 | ? (TCHAR)ltolower(aPattern_last_char) |
| 581 | : ctolower(aPattern_last_char); |
| 582 | |
| 583 | int occurrence = 0; |
| 584 | LPCTSTR match_starting_pos = aStr + aStr_length - 1; |
| 585 | |
| 586 | // Keep finding matches from the right until the Nth occurrence (specified by the caller) is found. |
| 587 | for (;;) |
| 588 | { |
| 589 | if (match_starting_pos < aStr) |
| 590 | return NULL; // No further matches are possible. |
| 591 | // Find (from the right) the first occurrence of aPattern's last char: |
| 592 | LPCTSTR last_char_match; |
| 593 | for (last_char_match = match_starting_pos; last_char_match >= aStr; --last_char_match) |
| 594 | { |
| 595 | if (aStringCaseSense == SCS_INSENSITIVE) // The most common mode is listed first for performance. |
| 596 | { |
| 597 | if (ctolower(*last_char_match) == aPattern_last_char_lower) |
| 598 | break; |
| 599 | } |
| 600 | else if (aStringCaseSense == SCS_INSENSITIVE_LOCALE) |
| 601 | { |
| 602 | if (ltolower(*last_char_match) == aPattern_last_char_lower) |
| 603 | break; |
| 604 | } |
| 605 | else // Case sensitive. |
| 606 | { |
| 607 | if (*last_char_match == aPattern_last_char) |
| 608 | break; |
| 609 | } |
| 610 | } |
| 611 | |
| 612 | if (last_char_match < aStr) // No further matches are possible. |
| 613 | return NULL; |
| 614 | |
| 615 | // Now that aPattern's last character has been found in aStr, ensure the rest of aPattern |
| 616 | // exists in aStr to the left of last_char_match: |
| 617 | LPCTSTR full_match, cp; |
| 618 | bool found; |
| 619 | for (found = false, cp = aPattern + aPattern_length - 2, full_match = last_char_match - 1;; --cp, --full_match) |
| 620 | { |
| 621 | if (cp < aPattern) // The complete pattern has been found at the position in full_match + 1. |