// // Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// #include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif #define STRING_COMPARE_PREAMBLE(CTYPE,LENGTH,S1,CT1,S2,CT2,EQ,LT,GT) \ if ( (nullptr == S1 && 0 != CT1) || (nullptr == S2 && 0 != CT2) ) \ { if ( S1 == S2 ) return EQ; if (nullptr == S1) return LT; if (nullptr == S2) return GT; } \ const CTYPE null_terminator = 0; \ if (CT1 < 0) CT1 = LENGTH(S1); \ if (CT2 < 0) CT2 = LENGTH(S2); \ if (0 == CT1) S1 = &null_terminator; \ if (0 == CT2) S2 = &null_terminator; \ if (S1 == S2 && CT1 == CT2) return EQ #define WIDE_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,0,-1,1) #define CHAR_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,0,-1,1) #define WIDE_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,true,false,false) #define CHAR_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,true,false,false) ON_StringMapOrdinalType ON_StringMapOrdinalTypeFromStringMapType( ON_StringMapType map_type ) { switch (map_type) { case ON_StringMapType::UpperCase: return ON_StringMapOrdinalType::UpperOrdinal; case ON_StringMapType::LowerCase: return ON_StringMapOrdinalType::LowerOrdinal; case ON_StringMapType::Identity: return ON_StringMapOrdinalType::Identity; } return ON_StringMapOrdinalType::Identity; } static ON__UINT32 MapCodePointOrdinal( ON__UINT32 unicode_code_point, unsigned int maximum_singleton_value, ON_StringMapOrdinalType map_type ) { // Converts ordinal "char" and "wchar_t" element values in the // range 0x00 to maximum_singleton_value to "ignore case" ordinal equivalents. // The returned value is always <= input value. // // This is NOT linguistic and NOT culture invariant. // // For UTF-8 strings, maximum_singleton_value = 0x7FU // For UTF-16 strings, maximum_singleton_value = 0xFFFFU // For UTF-32 strings, maximum_singleton_value = 0xFFFFFFFFU // TODO // Should ordinal ignore case compares this modify any values > 0x00FFU? if (unicode_code_point < 0x0041U || unicode_code_point > maximum_singleton_value) return unicode_code_point; if (unicode_code_point <= 0x005AU) { // unicode_code_points U+0041 = capital A to U+005A = capital Z if (ON_StringMapOrdinalType::LowerOrdinal == map_type) { return unicode_code_point + 0x20; } return unicode_code_point; } if (unicode_code_point < 0x0061U) { // unicode_code_points for the the six glyphs // U+005B [ // U+005C \ // U+005D ] // U+005E ^ // U+005F _ // U+0060 ` // These are the ASCII symbols after capital Z and before lower case A (a) // that were inserted so that converting between A...Z and a...z // could be done by setting/clearing a single bit 0x20. // The first edition of the ASCII standard was published in 1963. return unicode_code_point; } if (unicode_code_point <= 0x007AU) { // unicode_code_points u+0061 = lower case A (a) to U+007A = lower case Z (z) if (ON_StringMapOrdinalType::UpperOrdinal == map_type) { return unicode_code_point - 0x20; } if (ON_StringMapOrdinalType::MinimumOrdinal == map_type) { return unicode_code_point - 0x20; } return unicode_code_point; } if ( unicode_code_point < 0x00C0U ) return unicode_code_point; if (unicode_code_point <= 0x00DE) { // 0x00C0: LATIN CAPITAL LETTER A WITH GRAVE // ... // 0x00D7: MULTIPLICATION SIGN // ... // 0x00DE: LATIN CAPITAL LETTER THORN if ( unicode_code_point == 0x00D7 ) return unicode_code_point; // MULTIPLICATION SIGN if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) return unicode_code_point + 0x20; return unicode_code_point; } if (unicode_code_point < 0x00E0U) return unicode_code_point; if (unicode_code_point <= 0x00FEU) { // 0x00E0: LATIN SMALL LETTER A WITH GRAVE // ... // 0x00F7: DIVISION SIGN // ... // 0x00FE: LATIN SMALL LETTER THORN if (0x00F7U == unicode_code_point) return unicode_code_point; // DIVISION SIGN if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) return unicode_code_point - 0x20; return unicode_code_point; } if (unicode_code_point <= 0x00FFU) { // 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS if ( ON_StringMapOrdinalType::UpperOrdinal == map_type && 0x0178U <= maximum_singleton_value ) return 0x0178U; // 0x0178U: LATIN CAPITAL LETTER Y WITH DIAERESIS return unicode_code_point; } if (unicode_code_point <= 0x017FU) { // Latin Extended-A // special cases switch (unicode_code_point) { case 0x0130U: // LATIN CAPITAL LETTER I WITH DOT ABOVE if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type) return 0x0069U; // 0x0069U: LATIN SMALL LETTER i return unicode_code_point; break; case 0x0131U: // LATIN SMALL LETTER DOTLESS I if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) return 0x0049U; // 0x0049U LATIN CAPITAL LETTER I return unicode_code_point; break; case 0x0138U: // LATIN SMALL LETTER KRA // No upper case ordinal return unicode_code_point; break; case 0x0149U: // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) return 0x004EU; // 0x004EU LATIN CAPITAL LETTER N return unicode_code_point; break; case 0x0178U: // LATIN CAPITAL LETTER Y WITH DIAERESIS if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type ) return 0x00FFU; // 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS return unicode_code_point; break; case 0x017FU: // LATIN SMALL LETTER LONG S // No upper case ordinal return unicode_code_point; break; } if (unicode_code_point < 0x0138U || (unicode_code_point >= 0x014AU && unicode_code_point < 0x0178U) ) { // CAPITALS are even and SMALL is CAPITAL+1 if (0 == (unicode_code_point % 2)) { // LATIN CAPITAL LETTER if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return unicode_code_point + 1; } else { // LATIN SMALL LETTER if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) return unicode_code_point - 1; } return unicode_code_point; } else { // CAPITALS are odd and SMALL is CAPITAL+1 if (1 == (unicode_code_point % 2)) { // LATIN CAPITAL LETTER if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return unicode_code_point + 1; } else { // LATIN SMALL LETTER if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) return unicode_code_point - 1; } } return unicode_code_point; } if (unicode_code_point <= 0x01FFU) { // Latin Extended-B // special cases switch (unicode_code_point) { case 0x0180U: // LATIN SMALL LETTER B WITH STROKE if (ON_StringMapOrdinalType::UpperOrdinal == map_type) return 0x0243U; return unicode_code_point; break; case 0x0193U: // LATIN CAPITAL LETTER G WITH HOOK if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return 0x0260U; return unicode_code_point; break; case 0x0194U: // LATIN CAPITAL LETTER GAMMA if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return 0x0263U; return unicode_code_point; break; case 0x0195U: // LATIN SMALL LETTER HV if (ON_StringMapOrdinalType::UpperOrdinal == map_type) return 0x01F6U; return unicode_code_point; break; case 0x0196U: // LATIN CAPITAL LETTER IOTA if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return 0x0269U; return unicode_code_point; break; case 0x0197U: // LATIN CAPITAL LETTER I WITH STROKE if (ON_StringMapOrdinalType::LowerOrdinal == map_type) return 0x0268U; return unicode_code_point; break; } // TODO return unicode_code_point; } if (unicode_code_point < 0x0370) return unicode_code_point; if (unicode_code_point <= 0x03FF) { // Greek and Coptic if (unicode_code_point >= 0x0391 && unicode_code_point <= 0x03A9) { // GREEK CAPITAL LETTER ALPHA ... OMEGA if ( 0x03A2 == unicode_code_point ) return unicode_code_point; // RESERVED if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) return unicode_code_point + 0x20; return unicode_code_point; } if (unicode_code_point >= 0x03B1 && unicode_code_point <= 0x03C9) { // GREEK SMALL LETTER ALPHA ... OMEGA if ( 0x03C2 == unicode_code_point ) return unicode_code_point; // GREEK SMALL LETTER FINAL SIGMA (stigma) if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) return unicode_code_point - 0x20; return unicode_code_point; } if (unicode_code_point <= 0x377U) { if ( 0x0374U ==unicode_code_point || 0x0375U ==unicode_code_point ) return unicode_code_point; if (0 == (unicode_code_point % 2)) { // GREEK CAPITAL LETTER if ( ON_StringMapOrdinalType::LowerOrdinal == map_type) return unicode_code_point+1; } else { // GREEK SMALL LETTER if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) return unicode_code_point-1; } return unicode_code_point; } if (unicode_code_point <= 0x037DU) { if (ON_StringMapOrdinalType::UpperOrdinal == map_type && unicode_code_point >= 0x037BU ) return unicode_code_point + (0x03FDU - 0x037BU); return unicode_code_point; } if (unicode_code_point == 0x037FU || unicode_code_point == 0x03F3U ) { if ( unicode_code_point == 0x037FU && ON_StringMapOrdinalType::LowerOrdinal == map_type ) return 0x03F3U; if ( unicode_code_point == 0x03F3U && ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) ) return 0x03F3U; return unicode_code_point; } return unicode_code_point; } if ( unicode_code_point < 0x0400 ) return unicode_code_point; if ( unicode_code_point <= 0x04FFU ) { // Cyrillic if ( unicode_code_point < 0x0410U ) return unicode_code_point; if (unicode_code_point <= 0x042FU) { // 0x0410: CYRILLIC CAPITAL LETTER A // 0x042F: CYRILLIC CAPITAL LETTER YA if ( ON_StringMapOrdinalType::LowerOrdinal == map_type ) return unicode_code_point + 0x20; return unicode_code_point; } if (unicode_code_point <= 0x044FU) { // 0x0430: CYRILLIC SMALL LETTER A -> CYRILLIC CAPITAL LETTER A // 0x044F: CYRILLIC SMALL LETTER YA -> CYRILLIC CAPITAL LETTER YA if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) return unicode_code_point - 0x20; return unicode_code_point; } return unicode_code_point; } if (unicode_code_point < 0xD800) { // TODO - deal with other codepoint values < 0xD800 // That should be reduced for ordinal ignore case compares return unicode_code_point; } if (unicode_code_point < 0xFF00U) { // surrogate pairs use values from 0xD800U to 0xDFFF // No ordinal ignore conversion applies to surrogate pair values. // // 0xE000 to 0xF8FF = unicode private use area // No ordinal ignore conversion applies to private use area values. } if (unicode_code_point < 0xF900) { // 0xF900 to 0xFAFF = CJK Compatibility Ideographs // 0xFB00... = Alphabetic Presentation Forms // ... // 0xFE00... = Variation Selectors return unicode_code_point; } if (unicode_code_point <= 0xFFEFU) { // Halfwidth and Fullwidth Forms if ( unicode_code_point < 0xFF21 ) return unicode_code_point; if (unicode_code_point <= 0xFF3A) { // 0xFF22: FULLWIDTH LATIN CAPITAL LETTER A // 0xFF3A: FULLWIDTH LATIN CAPITAL LETTER Z if (ON_StringMapOrdinalType::LowerOrdinal == map_type) { return unicode_code_point + 0x20; } return unicode_code_point; } if (unicode_code_point <= 0xFF41) return unicode_code_point; if (unicode_code_point <= 0xFF5A) { if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type) { // 0xFF41: FULLWIDTH LATIN SMALL LETTER A -> FULLWIDTH LATIN CAPITAL LETTER A // 0xFF5A: FULLWIDTH LATIN SMALL LETTER Z -> FULLWIDTH LATIN CAPITAL LETTER Z return unicode_code_point - 0x20; } return unicode_code_point; } return unicode_code_point; } // 0x10000U: Linear B Syllabary // 0x10100U: Aegean Numbers // ... // 0x10FFFFU: maximum valid unicode code point return unicode_code_point; } ON__UINT32 ON_UnicodeMapCodePointOrdinal( ON_StringMapOrdinalType map_type, ON__UINT32 unicode_code_point ) { return MapCodePointOrdinal(unicode_code_point,0x10FFFFU,map_type); } ON__UINT32 ON_UnicodeMapCodePoint( const ON_Locale& locale, ON_StringMapType map_type, ON__UINT32 unicode_code_point ) { if (locale.IsOrdinalOrInvariantCulture()) return ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalTypeFromStringMapType(map_type), unicode_code_point); wchar_t w[7] = { 0 }; wchar_t mapped_w[7] = { 0 }; int w_count = ON_EncodeWideChar(unicode_code_point, 6, w); if (w_count > 0) { int mapped_count = ON_wString::MapString(locale, map_type, w, w_count, mapped_w, sizeof(mapped_w) / sizeof(mapped_w[0]) - 1); if (mapped_count > 0) { ON__UINT32 mapped_unicode_code_point = unicode_code_point; ON_UnicodeErrorParameters e; if ( mapped_count == ON_DecodeWideChar(w,w_count,&e,&mapped_unicode_code_point) ) return mapped_unicode_code_point; } } return unicode_code_point; } static unsigned int OrdinalUnsignedToIgnoreCase( unsigned int c, unsigned int maximum_singleton_value ) { // RH-41224 // map A -> a, ..., Z -> z so underbar is before any "letter". // The preserves the behavior of Rhino component name sorting // that used "ancient" C runtime ASCII sorts. const ON_StringMapOrdinalType map_type = ( c <= 0x7A && c >= 0x41 && maximum_singleton_value >= 0x7A ) ? ON_StringMapOrdinalType::LowerOrdinal : ON_StringMapOrdinalType::MinimumOrdinal; return MapCodePointOrdinal(c, maximum_singleton_value, map_type); } static unsigned int OrdinalUTF8ToIgnoreCase( char c ) { return OrdinalUnsignedToIgnoreCase((unsigned int)c,0x7FU); } static unsigned int OrdinalUTF16ToIgnoreCase( ON__UINT16 c ) { return OrdinalUnsignedToIgnoreCase(c,0xFFFFU); } static unsigned int OrdinalUTF32ToIgnoreCase( ON__UINT32 c ) { return OrdinalUnsignedToIgnoreCase(c,0xFFFFFFFFU); } #if ( 2 == ON_SIZEOF_WCHAR_T ) static unsigned int OrdinalWideCharToIgnoreCase( wchar_t c ) { return OrdinalUTF16ToIgnoreCase((ON__UINT16)c); } #elif ( 4 == ON_SIZEOF_WCHAR_T ) static unsigned int OrdinalWideCharToIgnoreCase( wchar_t c ) { return OrdinalUTF32ToIgnoreCase((ON__UINT32)c); } #endif int ON_StringCompareOrdinalUTF8( const char* string1, int element_count1, const char* string2, int element_count2, bool bOrdinalIgnoreCase ) { STRING_COMPARE_PREAMBLE(char,ON_String::Length,string1,element_count1,string2,element_count2,0,-1,1); unsigned int c1, c2; int i; const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; if (bOrdinalIgnoreCase) { for (i = 0; i < element_count; i++) { c1 = OrdinalUTF8ToIgnoreCase(*string1++); c2 = OrdinalUTF8ToIgnoreCase(*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } else { for (i = 0; i < element_count; i++) { c1 = (unsigned char)(*string1++); c2 = (unsigned char)(*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) return 1; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*string2++)) return -1; } return 0; } int ON_StringCompareOrdinalUTF16( const ON__UINT16* string1, int element_count1, const ON__UINT16* string2, int element_count2, bool bOrdinalIgnoreCase ) { STRING_COMPARE_PREAMBLE(ON__UINT16,ON_StringLengthUTF16,string1,element_count1,string2,element_count2,0,-1,1); unsigned int c1, c2; int i; const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; if (bOrdinalIgnoreCase) { for (i = 0; i < element_count; i++) { c1 = OrdinalUTF16ToIgnoreCase(*string1++); c2 = OrdinalUTF16ToIgnoreCase(*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } else { for (i = 0; i < element_count; i++) { c1 = (*string1++); c2 = (*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) return 1; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*string2++)) return -1; } return 0; } int ON_StringCompareOrdinalUTF32( const ON__UINT32* string1, int element_count1, const ON__UINT32* string2, int element_count2, bool bOrdinalIgnoreCase ) { STRING_COMPARE_PREAMBLE(ON__UINT32,ON_StringLengthUTF32,string1,element_count1,string2,element_count2,0,-1,1); unsigned int c1, c2; int i; const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; if (bOrdinalIgnoreCase) { for (i = 0; i < element_count; i++) { c1 = OrdinalUTF32ToIgnoreCase(*string1++); c2 = OrdinalUTF32ToIgnoreCase(*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } else { for (i = 0; i < element_count; i++) { c1 = (*string1++); c2 = (*string2++); if ( c1 < c2 ) return -1; if ( c1 > c2 ) return 1; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) return 1; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*string2++)) return -1; } return 0; } int ON_StringCompareOrdinalWideChar( const wchar_t* string1, int element_count1, const wchar_t* string2, int element_count2, bool bOrdinalIgnoreCase ) { #if (1 == ON_SIZEOF_WCHAR_T) return ON_StringCompareOrdinalUTF8((const char*)string1, element_count1, (const char*)string2, element_count2, bOrdinalIgnoreCase); #elif (2 == ON_SIZEOF_WCHAR_T) return ON_StringCompareOrdinalUTF16((const ON__UINT16*)string1, element_count1, (const ON__UINT16*)string2, element_count2, bOrdinalIgnoreCase); #elif (4 == ON_SIZEOF_WCHAR_T) return ON_StringCompareOrdinalUTF32((const ON__UINT32*)string1, element_count1, (const ON__UINT32*)string2, element_count2, bOrdinalIgnoreCase); #else #error ON_SIZEOF_WCHAR_T is not defined or has an unexpected value #endif } #define ON_UTF8_PATH_SEPARATOR '/' static char ON_NormalizeUTF8PathSepartor( char c ) { return ((c == '/') || (c == '\\')) ? ON_UTF8_PATH_SEPARATOR : c; } #define ON_WCHAR_PATH_SEPARATOR ((wchar_t)ON_UTF8_PATH_SEPARATOR) static wchar_t ON_NormalizeWideCharPathSepartor( wchar_t c ) { return ((c == ((wchar_t)'/')) || (c == ((wchar_t)'\\'))) ? ON_WCHAR_PATH_SEPARATOR : c; } bool ON_FileSystemPath::PlatformPathIgnoreCase() { // 2015-June-15 Dale Lear // The Windows file system ignores case. // // Default OS X 10.10.3 installs ignore case - even the "UNIX" shell. // User's can customize this setting. // // For other OS's, devs can customize this code as they see fit. return true; } int ON_String::ComparePath( const char* other_path ) const { return ON_String::ComparePath(m_s,-1,other_path,-1); } int ON_String::ComparePath( const char* path1, const char* path2 ) { return ON_String::ComparePath(path1,-1,path2,-1); } int ON_String::ComparePath( const char* path1, int element_count1, const char* path2, int element_count2 ) { CHAR_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2); const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); const char* b1 = path1; const char* b2 = path2; for(;;) { int count1 = 0; int count2 = 0; const char* a1 = b1; const char* a2 = b2; char c1, c2; for (;;) { if ((int)(b1 - path1) < element_count1) { c1 = ON_NormalizeUTF8PathSepartor(*b1++); if (null_terminator == c1 || ON_UTF8_PATH_SEPARATOR == c1) break; } else { c1 = null_terminator; break; } count1++; } for (;;) { if ((int)(b2 - path2) < element_count2) { c2 = ON_NormalizeUTF8PathSepartor(*b2++); if (null_terminator == c2 || ON_UTF8_PATH_SEPARATOR == c2) break; } else { c2 = null_terminator; break; } count2++; } if (count1 > 0 || count2 > 0) { // TODO // At this point, we should apply a compare that uses the same UTF-8 string normalization // as the file system on the current operating system. int rc = ON_StringCompareOrdinalUTF8(a1, count1, a2, count2, bOrdinalIgnoreCase); if (0 != rc) return rc; } if (null_terminator == c1 || null_terminator == c2) { if ( null_terminator != c2 ) return -1; if ( null_terminator != c1 ) return 1; return 0; } } } bool ON_String::EqualPath( const char* other_path ) const { return ON_String::EqualPath(m_s,-1,other_path,-1); } bool ON_String::EqualPath( const char* path1, const char* path2 ) { return ON_String::EqualPath(path1,-1,path2,-1); } bool ON_String::EqualPath( const char* path1, int element_count1, const char* path2, int element_count2 ) { CHAR_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); unsigned int c1=0, c2=0; const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; int i; const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); if (bOrdinalIgnoreCase) { for(i=0; i < element_count; i++) { c1 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path1++)); c2 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path2++)); if ( c1 != c2 ) return false; } } else { for(i=0; i < element_count; i++) { c1 = ON_NormalizeUTF8PathSepartor(*path1++); c2 = ON_NormalizeUTF8PathSepartor(*path2++); if ( c1 != c2 ) return false; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*path1++)) return false; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*path2++)) return false; } return true; } int ON_wString::ComparePath( const wchar_t* other_path ) const { return ON_wString::ComparePath(m_s,-1,other_path,-1); } int ON_wString::ComparePath( const wchar_t* sPath1, const wchar_t* sPath2 ) { return ON_wString::ComparePath(sPath1,-1,sPath2,-1); } int ON_wString::ComparePath( const wchar_t* path1, int element_count1, const wchar_t* path2, int element_count2 ) { WIDE_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2); const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); const wchar_t* b1 = path1; const wchar_t* b2 = path2; for(;;) { int count1 = 0; int count2 = 0; const wchar_t* a1 = b1; const wchar_t* a2 = b2; wchar_t c1, c2; for (;;) { if ((int)(b1 - path1) < element_count1) { c1 = ON_NormalizeWideCharPathSepartor(*b1++); if (null_terminator == c1 || ON_WCHAR_PATH_SEPARATOR == c1) break; } else { c1 = null_terminator; break; } count1++; } for (;;) { if ((int)(b2 - path2) < element_count2) { c2 = ON_NormalizeWideCharPathSepartor(*b2++); if (null_terminator == c2 || ON_WCHAR_PATH_SEPARATOR == c2) break; } else { c2 = null_terminator; break; } count2++; } if (count1 > 0 || count2 > 0) { // TODO // At this point, we should apply a compare that uses the same UTF-8 string normalization // as the file system on the current operating system. int rc = ON_StringCompareOrdinalWideChar(a1, count1, a2, count2, bOrdinalIgnoreCase); if (0 != rc) return rc; if (count1 < count2) return -1; if (count2 < count1) return 1; } if (null_terminator == c1 || null_terminator == c2) { if ( null_terminator != c2 ) return -1; if ( null_terminator != c1 ) return 1; return 0; } } } bool ON_wString::EqualPath( const wchar_t* other_path ) const { return ON_wString::EqualPath(m_s,-1,other_path,-1); } bool ON_wString::EqualPath( const wchar_t* path1, const wchar_t* path2 ) { return ON_wString::EqualPath(path1,-1,path2,-1); } bool ON_wString::EqualPath( const wchar_t* path1, int element_count1, const wchar_t* path2, int element_count2 ) { WIDE_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2); unsigned int c1=0, c2=0; const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; int i; const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase(); if (bOrdinalIgnoreCase) { for(i=0; i < element_count; i++) { c1 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path1++)); c2 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path2++)); if ( c1 != c2 ) return false; } } else { for(i=0; i < element_count; i++) { c1 = ON_NormalizeWideCharPathSepartor(*path1++); c2 = ON_NormalizeWideCharPathSepartor(*path2++); if ( c1 != c2 ) return false; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*path1++)) return false; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*path2++)) return false; } return true; } int ON_String::CompareAttributeName( const char* other_name ) const { const char* name1 = static_cast< const char* >(*this); return ON_String::CompareAttributeName(name1, other_name ); } int ON_String::CompareAttributeName( const char* name1, const char* name2 ) { // TODO - normalize and then use a culture aware ignore case test return ON_String::CompareOrdinal(name1, -1, name2, -1, true); } int ON_wString::CompareAttributeName( const wchar_t* other_name ) const { const wchar_t* name1 = static_cast< const wchar_t* >(*this); return ON_wString::CompareAttributeName(name1, other_name ); } static unsigned int Internal_NameAttributeWideCharRank( wchar_t c, bool bIgnoreCase ) { // Attribute names have ASCII symbols sorted this way // rank symbol // 1 (space) // 2 ! (exclamation point) // 3 " (double quote) // 4 # (hash) // 5 $ (dollar) // 6 % (percent) // 7 & (ampersand) // 8 ( (left paren) // 9 ) (right paren) // 10 * (asterisk) // 11 , (comma) // 12 . (period) // 13 / (slash) // 14 : (colon) // 15 ; (semicolon) // 16 ? (question mark) // 17 @ (at) // 18 [ (left bracket) // 19 \ (back slash) // 20 ] (right bracket) // 21 ^ (carrot) // 22 _ (underscore) // 23 ` (back tick) // 24 { (left brace) // 25 | (pipe) // 26 } (right brace) // 27 ~ (tilde) // 28 + (plus) // 29 < (less than) // 30 = (equal) // 31 > (greater than) // 32 0 // 33 1 // 34 2 // 35 3 // 36 4 // 37 5 // 38 6 // 39 7 // 40 8 // 41 9 // 42 ' (apostrophe) // 43 - (hyphen) // 44 A // 45 B // 46 C // ... // 69 Z // 70 a // ... // 95 z unsigned int code_point = (unsigned int)c; if (0 == code_point) return 0; if (code_point < 0x20 || code_point >= 0x7F) return ON_UNSET_UINT_INDEX; // no rank - sort some other way // 0 to 9 if (code_point >= 0x30 && code_point <= 0x39) return (code_point - 0x30 + 32); // upper case A to Z if (code_point >= 0x41 && code_point <= 0x5A) return (code_point - 0x41 + 44); // lower case a to z if (code_point >= 0x61 && code_point <= 0x7A) return bIgnoreCase ? (code_point - 0x61 + 44) : (code_point - 0x61 + 70); unsigned int rank; switch (code_point) { case 0x20: // 1 (space) rank = 1; break; case 0x21: // 2 ! (exclamation point) rank = 2; break; case 0x22: // 3 " (double quote) rank = 3; break; case 0x23: // 4 # (hash) rank = 4; break; case 0x24: // 5 $ (dollar) rank = 5; break; case 0x25: // 6 % (percent) rank = 6; break; case 0x26: // 7 & (ampersand) rank = 7; break; case 0x28: // 8 ( (left paren) rank = 8; break; case 0x29: // 9 ) (right paren) rank = 9; break; case 0x2A: // 10 * (asterisk) rank = 10; break; case 0x2C: // 11 , (comma) rank = 11; break; case 0x2E: // 12 . (period) rank = 12; break; case 0x2F: // 13 / (slash) rank = 13; break; case 0x3A: // 14 : (colon) rank = 14; break; case 0x3B: // 15 ; (semicolon) rank = 15; break; case 0x3F: // 16 ? (question mark) rank = 16; break; case 0x40: // 17 @ (at) rank = 17; break; case 0x5B: // 18 [ (left bracket) rank = 18; break; case 0x5C: // 19 \ (back slash) rank = 19; break; case 0x5D: // 20 ] (right bracket) rank = 20; break; case 0x5E: // 21 ^ (carrot) rank = 21; break; case 0x5F: // 22 _ (underscore) rank = 22; break; case 0x60: // 23 ` (back tick) rank = 23; break; case 0x7B: // 24 { (left brace) rank = 24; break; case 0x7C: // 25 | (pipe) rank = 25; break; case 0x7D: // 26 } (right brace) rank = 26; break; case 0x7E: // 27 ~ (tilde) rank = 27; break; case 0x2B: // 28 + (plus) rank = 28; break; case 0x3C: // 29 < (less than) rank = 29; break; case 0x3D: // 30 = (equal) rank = 30; break; case 0x3E: // 31 > (greater than) rank = 31; break; // 32 to 41 numerals 0 to 9 handled before this switch case 0x27: // 42 ' (apostrophe) rank = 42; break; case 0x2D: // 43 - (hyphen) rank = 43; break; // 43 to 69 upper case A to Z handled before this switch // 43 to 69 lower case z to z handled before this switch default: // no rank - sort some other way rank = ON_UNSET_UINT_INDEX; break; } return rank; } int ON_wString::CompareAttributeName( const wchar_t* name1, const wchar_t* name2 ) { if (name1 == name2) return 0; const bool bIgnoreCase = true; if ( nullptr != name1 && nullptr != name2 ) { // Sort leading underbar before 'A' // https://mcneel.myjetbrains.com/youtrack/issue/RH-41224 unsigned int rank1 = Internal_NameAttributeWideCharRank(*name1,bIgnoreCase); unsigned int rank2 = Internal_NameAttributeWideCharRank(*name2,bIgnoreCase); while (rank1 == rank2 && ON_UNSET_UINT_INDEX != rank1 && 0 != rank1) { name1++; name2++; rank1 = Internal_NameAttributeWideCharRank(*name1,bIgnoreCase); rank2 = Internal_NameAttributeWideCharRank(*name2,bIgnoreCase); } if (ON_UNSET_UINT_INDEX != rank1 || ON_UNSET_UINT_INDEX != rank2) { if (rank1 < rank2) return -1; if (rank1 > rank2) return 1; if (0 == rank1) return 0; } } // name1 or name2 is nullptr or begins with an unranked code point. return ON_wString::CompareOrdinal(name1, -1, name2, -1, bIgnoreCase); } bool ON_String::EqualAttributeName( const char* other_name ) const { const char* name1 = static_cast< const char* >(*this); return ON_String::EqualAttributeName(name1, other_name ); } bool ON_String::EqualAttributeName( const char* name1, const char* name2 ) { // TODO - normalize and then use a culture aware ignore case test return ON_String::EqualOrdinal(name1, -1, name2, -1, true); } bool ON_wString::EqualAttributeName( const wchar_t* other_name ) const { const wchar_t* name1 = static_cast< const wchar_t* >(*this); return ON_wString::EqualAttributeName(name1, other_name ); } bool ON_wString::EqualAttributeName( const wchar_t* name1, const wchar_t* name2 ) { // TODO - normalize and then use a culture aware ignore case test return ON_wString::EqualOrdinal(name1, -1, name2, -1, true); } int ON_String::Compare( const char* s ) const { return CompareOrdinal(s,false); } int ON_String::Compare( const unsigned char* s) const { return CompareOrdinal((const char*)s,false); } int ON_String::CompareNoCase( const char* s) const { return CompareOrdinal(s,true); } int ON_String::CompareNoCase( const unsigned char* s) const { return CompareOrdinal((const char*)s,true); } bool ON_String::Equal( const ON_String& other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Equal( static_cast(*this), this->Length(), static_cast(other_string), other_string.Length(), locale, bIgnoreCase ); } bool ON_String::Equal( const char* other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Equal( static_cast(*this), this->Length(), other_string, -1, locale, bIgnoreCase ); } bool ON_String::Equal( const char* string1, const char* string2, const class ON_Locale& locale, bool bIgnoreCase ) { return ON_String::Equal( string1, -1, string2, -1, locale, bIgnoreCase ); } bool ON_String::Equal( const char* string1, int element_count1, const char* string2, int element_count2, const class ON_Locale& locale, bool bIgnoreCase ) { if (locale.IsOrdinal()) return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #if defined(ON_RUNTIME_WIN) // TODO - replace with locale aware test return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #elif defined(ON_RUNTIME_APPLE) // TODO - replace with locale aware test return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #else // TODO - replace with locale aware test return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #endif } int ON_String::Compare( const ON_String& other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Compare( static_cast< const char* >(*this), this->Length(), static_cast< const char* >(other_string), other_string.Length(), locale, bIgnoreCase ); } int ON_String::Compare( const char* other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_String::Compare( static_cast< const char* >(*this), this->Length(), other_string, -1, locale, bIgnoreCase ); } int ON_String::Compare( const char* string1, const char* string2, const class ON_Locale& locale, bool bIgnoreCase ) { return ON_String::Compare( string1, -1, string2, -1, locale, bIgnoreCase ); } int ON_String::Compare( const char* string1, int element_count1, const char* string2, int element_count2, const class ON_Locale& locale, bool bIgnoreCase ) { #if defined(ON_RUNTIME_WIN) // TODO // Actually do an invariant culture compare // This involves NFC normalization and then using the correct CE to compare values. // return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; //int rc // = bIgnoreCase // ? _strnicmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) // : _strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); //if (0 == rc) //{ // if ( n < element_count1 ) // rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase); // else if ( n < element_count2 ) // rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase); //} //return rc; #elif defined(ON_RUNTIME_APPLE) // TODO // Actually do an invariant culture compare // This involves NFC normalization and then using the correct CE to compare values. // return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; //int rc // = bIgnoreCase // ? strncasecmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()) // : strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr()); //if (0 == rc) //{ // if ( n < element_count1 ) // rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase); // else if ( n < element_count2 ) // rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase); //} //return rc; #else return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase ); #endif } bool ON_wString::Equal( const ON_wString& other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_wString::Equal( static_cast< const wchar_t* >(*this), this->Length(), static_cast< const wchar_t* >(other_string), other_string.Length(), locale, bIgnoreCase ); } bool ON_wString::Equal( const wchar_t* other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_wString::Equal( static_cast< const wchar_t* >(*this), this->Length(), other_string, -1, locale, bIgnoreCase ); } bool ON_wString::Equal( const wchar_t* string1, const wchar_t* string2, const class ON_Locale& locale, bool bIgnoreCase ) { return ON_wString::Equal(string1,-1,string2,-1,locale,bIgnoreCase); } bool ON_wString::Equal( const wchar_t* string1, int element_count1, const wchar_t* string2, int element_count2, const class ON_Locale& locale, bool bIgnoreCase ) { if ( locale.IsOrdinal() ) return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase ); #if defined(ON_RUNTIME_WIN) return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase )); #elif defined(ON_RUNTIME_APPLE) return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase )); #else return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase ); #endif } int ON_wString::Compare( const ON_wString& other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_wString::Compare( static_cast< const wchar_t* >(*this), this->Length(), static_cast< const wchar_t* >(other_string), other_string.Length(), locale, bIgnoreCase ); } int ON_wString::Compare( const wchar_t* other_string, const class ON_Locale& locale, bool bIgnoreCase ) const { return ON_wString::Compare( static_cast< const wchar_t* >(*this), this->Length(), other_string, -1, locale, bIgnoreCase ); } int ON_wString::Compare( const wchar_t* string1, const wchar_t* string2, const class ON_Locale& locale, bool bIgnoreCase ) { return ON_wString::Compare( string1, -1, string2, -1, locale, bIgnoreCase ); } int ON_wString::Compare( const wchar_t* string1, int element_count1, const wchar_t* string2, int element_count2, const class ON_Locale& locale, bool bIgnoreCase ) { WIDE_STRING_COMPARE_PREAMBLE(string1, element_count1, string2, element_count2); if ( locale.IsOrdinal() ) return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #if defined(ON_RUNTIME_WIN) const bool bIsIsInvariantCulture = locale.IsInvariantCulture(); wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; const wchar_t* locale_name = bIsIsInvariantCulture ? LOCALE_NAME_INVARIANT : locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0])); const DWORD flags = ( bIgnoreCase ) ? ((bIsIsInvariantCulture) ? NORM_IGNORECASE : LINGUISTIC_IGNORECASE) : 0; int rc = ::CompareStringEx( locale_name, flags, string1, element_count1, string2, element_count2, nullptr, nullptr, 0); if (rc == CSTR_LESS_THAN) return -1; if (rc == CSTR_EQUAL) return 0; if (rc == CSTR_GREATER_THAN) return 1; #elif defined(ON_RUNTIME_APPLE) // I need a tool that is similar to // // wcscoll_l(const wchar_t*, const wchar_t*, locale_t ) // // but it needs to take a count (n) and I need one that ignores case. // // It appears wcsncasecmp_l() does use locale to map case, but then does an ordinal compare. // // //int n = (element_count1 <= element_count2) ? element_count1 : element_count2; //int rc // = bIgnoreCase // ? wcsncasecmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr()) // : wcsncmp(string1, string2, (size_t)n); //if (0 == rc) //{ // if ( n < element_count1 ) // rc = ON_wString::CompareOrdinal(string1+n,element_count1-n,L"",0,bIgnoreCase); // else if ( n < element_count2 ) // rc = ON_wString::CompareOrdinal(L"", 0, string2+n, element_count2-n, bIgnoreCase); //} //return rc; return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); #else #endif return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase); } /////////////////////////////////////////////////////////////////////////////// // // ON_String operator ==, !=, <, >, <=, >= // bool operator==( const ON_String& lhs, const ON_String& rhs ) { const int length = lhs.Length(); return (length == rhs.Length()) && ON_String::EqualOrdinal( static_cast(lhs), length, static_cast(rhs), length, false ); } bool operator==( const ON_String& lhs, const char* rhs ) { const int length = lhs.Length(); return (length == ON_String::Length(rhs)) && ON_String::EqualOrdinal( static_cast(lhs), length, rhs, length, false ); } bool operator==( const char* lhs, const ON_String& rhs ) { const int length = rhs.Length(); return (length == ON_String::Length(lhs)) && ON_String::EqualOrdinal( lhs, length, static_cast(rhs), length, false ); } bool operator!=(const ON_String& lhs, const ON_String& rhs) { return !(lhs == rhs); } bool operator!=(const ON_String& lhs, const char* rhs) { return !(lhs == rhs); } bool operator!=(const char* lhs, const ON_String& rhs) { return !(lhs == rhs); } bool operator<( const ON_String& lhs, const ON_String& rhs ) { const int rc = ON_String::CompareOrdinal( static_cast(lhs), lhs.Length(), static_cast(rhs), rhs.Length(), false ); return (rc < 0); } bool operator<( const ON_String& lhs, const char* rhs ) { const int rc = ON_String::CompareOrdinal( static_cast(lhs), lhs.Length(), rhs, -1, false ); return (rc < 0); } bool operator<( const char* lhs, const ON_String& rhs ) { const int rc = ON_String::CompareOrdinal( lhs, -1, static_cast(rhs), rhs.Length(), false ); return (rc < 0); } bool operator>( const ON_String& lhs, const ON_String& rhs ) { return operator<(rhs,lhs); } bool operator>( const ON_String& lhs, const char* rhs ) { return operator<(rhs,lhs); } bool operator>( const char* lhs, const ON_String& rhs ) { return operator<(rhs,lhs); } bool operator<=( const ON_String& lhs, const ON_String& rhs ) { return !operator<(rhs,lhs); } bool operator<=( const ON_String& lhs, const char* rhs ) { return !operator<(rhs,lhs); } bool operator<=( const char* lhs, const ON_String& rhs ) { return !operator<(rhs,lhs); } bool operator>=( const ON_String& lhs, const ON_String& rhs ) { return !operator<(lhs,rhs); } bool operator>=( const ON_String& lhs, const char* rhs ) { return !operator<(lhs,rhs); } bool operator>=( const char* lhs, const ON_String& rhs ) { return !operator<(lhs,rhs); } /////////////////////////////////////////////////////////////////////////////// // // ON_wString operator ==, !=, <, >, <=, >= // bool operator==( const ON_wString& lhs, const ON_wString& rhs ) { const int length = lhs.Length(); return (length == rhs.Length()) && ON_wString::EqualOrdinal( static_cast(lhs), length, static_cast(rhs), length, false ); } bool operator==( const ON_wString& lhs, const wchar_t* rhs ) { const int length = lhs.Length(); return (length == ON_wString::Length(rhs)) && ON_wString::EqualOrdinal( static_cast(lhs), length, rhs, length, false ); } bool operator==( const wchar_t* lhs, const ON_wString& rhs ) { const int length = rhs.Length(); return (length == ON_wString::Length(lhs)) && ON_wString::EqualOrdinal( lhs, length, static_cast(rhs), length, false ); } bool operator!=(const ON_wString& lhs, const ON_wString& rhs) { return !(lhs == rhs); } bool operator!=(const ON_wString& lhs, const wchar_t* rhs) { return !(lhs == rhs); } bool operator!=(const wchar_t* lhs, const ON_wString& rhs) { return !(lhs == rhs); } bool operator<( const ON_wString& lhs, const ON_wString& rhs ) { const int rc = ON_wString::CompareOrdinal( static_cast(lhs), lhs.Length(), static_cast(rhs), rhs.Length(), false ); return (rc < 0); } bool operator<( const ON_wString& lhs, const wchar_t* rhs ) { const int rc = ON_wString::CompareOrdinal( static_cast(lhs), lhs.Length(), rhs, -1, false ); return (rc < 0); } bool operator<( const wchar_t* lhs, const ON_wString& rhs ) { const int rc = ON_wString::CompareOrdinal( lhs, -1, static_cast(rhs), rhs.Length(), false ); return (rc < 0); } bool operator>( const ON_wString& lhs, const ON_wString& rhs ) { return operator<(rhs,lhs); } bool operator>( const ON_wString& lhs, const wchar_t* rhs ) { return operator<(rhs,lhs); } bool operator>( const wchar_t* lhs, const ON_wString& rhs ) { return operator<(rhs,lhs); } bool operator<=( const ON_wString& lhs, const ON_wString& rhs ) { return !operator<(rhs,lhs); } bool operator<=( const ON_wString& lhs, const wchar_t* rhs ) { return !operator<(rhs,lhs); } bool operator<=( const wchar_t* lhs, const ON_wString& rhs ) { return !operator<(rhs,lhs); } bool operator>=( const ON_wString& lhs, const ON_wString& rhs ) { return !operator<(lhs,rhs); } bool operator>=( const ON_wString& lhs, const wchar_t* rhs ) { return !operator<(lhs,rhs); } bool operator>=( const wchar_t* lhs, const ON_wString& rhs ) { return !operator<(lhs,rhs); } /////////////////////////////////////////////////////////////////////////////// // // ON_String::EqualOrdinal // bool ON_String::EqualOrdinal( const ON_String& other_string, bool bOrdinalIgnoreCase ) const { const int length = Length(); return (length == other_string.Length()) && ON_String::EqualOrdinal( static_cast< const char* >(*this), length, static_cast< const char* >(other_string), length, bOrdinalIgnoreCase ); } bool ON_String::EqualOrdinal( const char* other_string, bool bOrdinalIgnoreCase ) const { const int length = Length(); return (length == ON_String::Length(other_string)) && ON_String::EqualOrdinal( static_cast< const char* >(*this), length, other_string, length, bOrdinalIgnoreCase ); } bool ON_String::EqualOrdinal( const char* string1, const char* string2, bool bOrdinalIgnoreCase ) { return ON_String::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase); } bool ON_String::EqualOrdinal( const char* string1, int element_count1, const char* string2, int element_count2, bool bOrdinalIgnoreCase ) { CHAR_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2); const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; int i; if (bOrdinalIgnoreCase) { unsigned int c1, c2; for(i = 0; i < element_count; i++) { c1 = OrdinalUTF8ToIgnoreCase(*string1++); c2 = OrdinalUTF8ToIgnoreCase(*string2++); if ( c1 != c2 ) return false; } } else { for(i = 0; i < element_count; i++) { if ((*string1++) != (*string2++)) return false; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) return false; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*string2++)) return false; } return true; } /////////////////////////////////////////////////////////////////////////////// // // ON_wString::EqualOrdinal // bool ON_wString::EqualOrdinal( const ON_wString& other_string, bool bOrdinalIgnoreCase ) const { const int length = Length(); return (length == other_string.Length()) && ON_wString::EqualOrdinal( static_cast< const wchar_t* >(*this), length, static_cast< const wchar_t* >(other_string), length, bOrdinalIgnoreCase ); } bool ON_wString::EqualOrdinal( const wchar_t* other_string, bool bOrdinalIgnoreCase ) const { const int length = Length(); return (length == ON_wString::Length(other_string)) && ON_wString::EqualOrdinal( static_cast< const wchar_t* >(*this), length, other_string, length, bOrdinalIgnoreCase ); } bool ON_wString::EqualOrdinal( const wchar_t* string1, const wchar_t* string2, bool bOrdinalIgnoreCase ) { return ON_wString::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase); } bool ON_wString::EqualOrdinal( const wchar_t* string1, int element_count1, const wchar_t* string2, int element_count2, bool bOrdinalIgnoreCase ) { WIDE_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2); const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2; int i; if (bOrdinalIgnoreCase) { unsigned int c1, c2; for(i = 0; i < element_count; i++) { c1 = OrdinalWideCharToIgnoreCase(*string1++); c2 = OrdinalWideCharToIgnoreCase(*string2++); if ( c1 != c2 ) return false; } } else { for(i = 0; i < element_count; i++) { if ((*string1++) != (*string2++)) return false; } } for (/*empty init*/; i < element_count1; i++) { if ( 0 != (*string1++)) return false; } for (/*empty init*/; i < element_count2; i++) { if ( 0 != (*string2++)) return false; } return true; } /////////////////////////////////////////////////////////////////////////////// // // ON_String::CompareOrdinal // int ON_String::CompareOrdinal( const ON_String& other_string, bool bOrdinalIgnoreCase ) const { return ON_String::CompareOrdinal( static_cast< const char* >(*this), this->Length(), static_cast< const char* >(other_string), other_string.Length(), bOrdinalIgnoreCase ); } int ON_String::CompareOrdinal( const char* other_string, bool bOrdinalIgnoreCase ) const { return ON_String::CompareOrdinal( static_cast< const char* >(*this), this->Length(), other_string, -1, bOrdinalIgnoreCase ); } int ON_String::CompareOrdinal( const char* string1, const char* string2, bool bOrdinalIgnoreCase ) { return ON_String::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase); } int ON_String::CompareOrdinal( const char* string1, int element_count1, const char* string2, int element_count2, bool bOrdinalIgnoreCase ) { return ON_StringCompareOrdinalUTF8(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase); } /////////////////////////////////////////////////////////////////////////////// // // ON_wString::CompareOrdinal // int ON_wString::CompareOrdinal( const ON_wString& other_string, bool bOrdinalIgnoreCase ) const { return ON_wString::CompareOrdinal( static_cast< const wchar_t* >(*this), this->Length(), static_cast< const wchar_t* >(other_string), other_string.Length(), bOrdinalIgnoreCase ); } int ON_wString::CompareOrdinal( const wchar_t* other_string, bool bOrdinalIgnoreCase ) const { return ON_wString::CompareOrdinal( static_cast< const wchar_t* >(*this), this->Length(), other_string, -1, bOrdinalIgnoreCase ); } int ON_wString::CompareOrdinal( const wchar_t* string1, const wchar_t* string2, bool bOrdinalIgnoreCase ) { return ON_wString::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase); } int ON_wString::CompareOrdinal( const wchar_t* string1, int element_count1, const wchar_t* string2, int element_count2, bool bOrdinalIgnoreCase ) { return ON_StringCompareOrdinalWideChar(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase); } char ON_String::MapCharacterOrdinal( ON_StringMapOrdinalType map_type, char c ) { switch (map_type) { case ON_StringMapOrdinalType::UpperOrdinal: case ON_StringMapOrdinalType::MinimumOrdinal: if (c >= 'a' && c <= 'z') return c - 0x20; break; case ON_StringMapOrdinalType::LowerOrdinal: if (c >= 'A' && c <= 'Z') return c + 0x20; break; default: break; } return c; } ON_String ON_String::MapStringOrdinal( ON_StringMapOrdinalType map_type ) const { ON_String mapped_string(*this); if (mapped_string.IsNotEmpty()) { mapped_string.CopyArray(); const int length = mapped_string.Length(); ON_String::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length); } return mapped_string; } int ON_String::MapStringOrdinal( ON_StringMapOrdinalType map_type, const char* string, int element_count, char* mapped_string, int mapped_string_capacity ) { if (0 != mapped_string_capacity) { if (nullptr == mapped_string || mapped_string_capacity <= 0) return 0; if (string != mapped_string) mapped_string[0] = 0; } if (element_count < 0) { element_count = ON_String::Length(string); if (element_count < 0) return 0; if ( 0 == mapped_string_capacity ) return element_count+1; // +1 for null terminator } else if ( 0 == mapped_string_capacity ) return element_count; // no +1 here if ( element_count > mapped_string_capacity ) return 0; char c; const char* s1 = string + element_count; switch (map_type) { case ON_StringMapOrdinalType::UpperOrdinal: case ON_StringMapOrdinalType::MinimumOrdinal: while (string < s1) { c = *string++; if (c >= 'a' && c <= 'z') c -= 0x20; *mapped_string++ = c; } break; case ON_StringMapOrdinalType::LowerOrdinal: while (string < s1) { c = *string++; if (c >= 'A' && c <= 'Z') c += 0x20; *mapped_string++ = c; } break; default: while (string < s1) *mapped_string++ = *string++; break; } if ( element_count < mapped_string_capacity ) *mapped_string = 0; return element_count; } wchar_t ON_wString::MapCharacterOrdinal( ON_StringMapOrdinalType map_type, wchar_t c ) { return (wchar_t)MapCodePointOrdinal( c, 0xFFFFU, map_type ); } ON_wString ON_wString::MapStringOrdinal( ON_StringMapOrdinalType map_type ) const { ON_wString mapped_string(*this); if (ON_StringMapOrdinalType::Identity != map_type && mapped_string.IsNotEmpty()) { mapped_string.CopyArray(); const int length = mapped_string.Length(); ON_wString::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length); } return mapped_string; } int ON_wString::MapStringOrdinal( ON_StringMapOrdinalType map_type, const wchar_t* string, int element_count, wchar_t* mapped_string, int mapped_string_capacity ) { if (0 != mapped_string_capacity) { if (nullptr == mapped_string || mapped_string_capacity <= 0) return 0; if (string != mapped_string) mapped_string[0] = 0; } if (element_count < 0) { element_count = ON_wString::Length(string); if (element_count < 0) return 0; if ( 0 == mapped_string_capacity ) return element_count+1; // +1 for null terminator } else if ( 0 == mapped_string_capacity ) return element_count; // no +1 here if ( element_count > mapped_string_capacity ) return 0; const wchar_t* s1 = string + element_count; while (string < s1) *mapped_string++ = ON_wString::MapCharacterOrdinal(map_type,*string++); if ( element_count < mapped_string_capacity ) *mapped_string = 0; return element_count; } ON_String ON_String::MapString( const class ON_Locale& locale, ON_StringMapType map_type ) const { return ON_String::MapString( locale, map_type, static_cast< const char* >(*this), Length() ); } ON_wString ON_wString::MapString( const class ON_Locale& locale, ON_StringMapType map_type ) const { return ON_wString::MapString( locale, map_type, static_cast< const wchar_t* >(*this), Length() ); } ON_String ON_String::MapString( const class ON_Locale& locale, ON_StringMapType map_type, const char* string, int element_count ) { bool bMappingNullTerminator; if (element_count < 0) { element_count = ON_String::Length(string); if ( element_count < 0 ) return ON_String::EmptyString; bMappingNullTerminator = false; } else { bMappingNullTerminator = element_count > 0 && nullptr != string && 0 == string[element_count-1] && (1 == element_count || 0 != string[element_count-2]) ; } int mapped_string_capacity = ON_String::MapString(locale,map_type,string,element_count,nullptr,0); if (mapped_string_capacity > 0) { ON_String mapped_string; // reserves mapped_length+1 wchar_t elements in mapped_string.m_s[] // and sets mapped_string.m_s[mapped_length] = 0. mapped_string.ReserveArray(mapped_string_capacity); // Set mapped_string ON_wString header length value to mapped_length. int mapped_string_length = ( bMappingNullTerminator ) ? (mapped_string_capacity-1) : mapped_string_capacity; mapped_string.SetLength(mapped_string_length); int rc = ON_String::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity); if ( rc == mapped_string_capacity ) return mapped_string; } return ON_String::EmptyString; } ON_wString ON_wString::MapString( const class ON_Locale& locale, ON_StringMapType map_type, const wchar_t* string, int element_count ) { bool bMappingNullTerminator; if (element_count < 0) { element_count = ON_wString::Length(string); if ( element_count < 0 ) return ON_wString::EmptyString; bMappingNullTerminator = false; } else { bMappingNullTerminator = element_count > 0 && nullptr != string && 0 == string[element_count-1] && (1 == element_count || 0 != string[element_count-2]) ; } int mapped_string_capacity = ON_wString::MapString(locale,map_type,string,element_count,nullptr,0); if (mapped_string_capacity > 0) { ON_wString mapped_string; // reserves mapped_length+1 wchar_t elements in mapped_string.m_s[] // and sets mapped_string.m_s[mapped_length] = 0. mapped_string.ReserveArray(mapped_string_capacity); // Set mapped_string ON_wString header length value to mapped_length. int mapped_string_length = ( bMappingNullTerminator ) ? (mapped_string_capacity-1) : mapped_string_capacity; mapped_string.SetLength(mapped_string_length); int rc = ON_wString::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity); if ( rc == mapped_string_capacity ) return mapped_string; } return ON_wString::EmptyString; } int ON_String::MapString( const class ON_Locale& locale, ON_StringMapType map_type, const char* string, int element_count, char* mapped_string, int mapped_string_capacity ) { if (mapped_string_capacity < 0) { ON_ERROR("mapped_string_capacity is < 0."); return 0; } if (0 == mapped_string_capacity) mapped_string = nullptr; else if (mapped_string_capacity > 0) { if (nullptr == mapped_string) { ON_ERROR("mapped_string is nullptr."); return 0; } if ( string != mapped_string ) mapped_string[0] = 0; } if (element_count < 0) { element_count = ON_String::Length(string); if (element_count < 0 ) return 0; // string has more than max int elements - probably missing a null terminator. } if ( 0 == element_count) { return 1; } if (nullptr == string) { ON_ERROR("string is nullptr."); return 0; } if (false == locale.IsOrdinal()) { const char* s = string; const char* s1 = s + element_count; while (s < s1) { char c = *s++; if (c >= 0 && c <= 127) continue; // UTF-8 multi char element code point // No robust UTF-8 tools are available from the Windows or Mac API. // TODO - investigate using ICU to avoid UTF-8 -> wide char -> UTF-8 conversion const ON_wString wide_string(string, element_count); const ON_wString wide_mapped_string = wide_string.MapString(locale, map_type); int mapped_element_count = ON_ConvertWideCharToUTF8( false, // bTestByteOrder static_cast(wide_mapped_string), wide_mapped_string.Length(), mapped_string, mapped_string_capacity, nullptr, 0, 0, nullptr ); if (mapped_string_capacity > 0 && wide_mapped_string.Length() > mapped_string_capacity) { ON_ERROR("mapped_string_capacity too small."); return 0; } if (0 == string[element_count - 1] && mapped_element_count < mapped_string_capacity) { if (mapped_element_count < mapped_string_capacity) { mapped_string[mapped_element_count] = 0; mapped_element_count++; // count the null terminator } else { ON_ERROR("mapped_string_capacity too small."); return 0; } } return mapped_element_count; } } if ( 0 == mapped_string_capacity ) return element_count; switch (map_type) { case ON_StringMapType::UpperCase: return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity); break; case ON_StringMapType::LowerCase: return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity); break; default: ON_ERROR("invalid map_type."); break; } return 0; } int ON_wString::MapString( const class ON_Locale& locale, ON_StringMapType map_type, const wchar_t* string, int element_count, wchar_t* mapped_string, int mapped_string_capacity ) { if (mapped_string_capacity < 0) { ON_ERROR("mapped_string_capacity is < 0."); return 0; } if (0 == mapped_string_capacity) mapped_string = nullptr; else if (mapped_string_capacity > 0) { if (nullptr == mapped_string) { ON_ERROR("mapped_string is nullptr."); return 0; } if ( string != mapped_string ) mapped_string[0] = 0; } if (element_count < 0) { element_count = ON_wString::Length(string); if (element_count < 0 ) return 0; // string has more than max int elements - probably missing a null terminator. } if ( 0 == element_count) { return 1; } if (nullptr == string) { ON_ERROR("string is nullptr."); return 0; } while (false == locale.IsOrdinal()) { #if defined(ON_RUNTIME_WIN) wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY]; const wchar_t* locale_name = locale.IsInvariantCulture() ? LOCALE_NAME_INVARIANT : locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0])); DWORD flags = 0; switch (map_type) { case ON_StringMapType::UpperCase: flags = LCMAP_UPPERCASE; break; case ON_StringMapType::LowerCase: flags = LCMAP_LOWERCASE; break; default: ON_ERROR("invalid map_type."); return 0; } const int mapped_length = LCMapStringEx( locale_name, flags, string, element_count, mapped_string, mapped_string_capacity, nullptr, nullptr, 0 ); if ( mapped_length < mapped_string_capacity && mapped_string_capacity > 0 && nullptr != mapped_string ) { if ( mapped_length <= 0 ) mapped_string[0] = 0; else if ( 0 != mapped_string[mapped_length-1] ) mapped_string[mapped_length] = 0; } return (mapped_length < 0) ? 0 : mapped_length; #elif defined(ON_RUNTIME_APPLE) if ( 0 == mapped_string_capacity ) return (element_count > 0 && nullptr != string) ? element_count : 0; if ( mapped_string_capacity < element_count ) return 0; ON_CRT_locale_t loc = locale.StringCollateAndMapLocalePtr(); int mapped_length = 0; switch (map_type) { case ON_StringMapType::UpperCase: while (mapped_length < element_count) { mapped_string[mapped_length] = towupper_l(string[mapped_length], loc); mapped_length++; } break; case ON_StringMapType::LowerCase: while (mapped_length < element_count) { mapped_string[mapped_length] = tolower_l(string[mapped_length], loc); mapped_length++; } break; default: ON_ERROR("invalid map_type."); return 0; } if ( mapped_length < mapped_string_capacity ) mapped_string[mapped_length] = 0; return mapped_length; #else break; #endif } switch (map_type) { case ON_StringMapType::UpperCase: return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity); break; case ON_StringMapType::LowerCase: return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity); break; default: ON_ERROR("invalid map_type."); break; } return 0; } void ON_wString::MakeUpper() { MakeUpperOrdinal(); } void ON_wString::MakeLower() { MakeLowerOrdinal(); } void ON_String::MakeUpper() { MakeUpperOrdinal(); } void ON_String::MakeLower() { MakeLowerOrdinal(); } void ON_wString::MakeUpperOrdinal() { CopyArray(); const int length = Length(); if (length > 0) ON_wString::MapStringOrdinal( ON_StringMapOrdinalType::UpperOrdinal, m_s, length, m_s, length ); } void ON_wString::MakeLowerOrdinal() { CopyArray(); const int length = Length(); if (length > 0) ON_wString::MapStringOrdinal( ON_StringMapOrdinalType::LowerOrdinal, m_s, length, m_s, length ); } void ON_String::MakeUpperOrdinal() { CopyArray(); const int length = Length(); if (length > 0) ON_String::MapStringOrdinal( ON_StringMapOrdinalType::UpperOrdinal, m_s, length, m_s, length ); } void ON_String::MakeLowerOrdinal() { CopyArray(); const int length = Length(); if (length > 0) ON_String::MapStringOrdinal( ON_StringMapOrdinalType::LowerOrdinal, m_s, length, m_s, length ); }