/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include #include #if __PLATFORM_WINDOWS__ #include #elif __PLATFORM_NINTENDO_WII__ #include #elif __PLATFORM_POSIX__ #include #include #endif #include "nstring/nstring.h" #include "container/nlist.h" #include "container/narray.h" #include "platform_config.h" #include "alloc/ialloc.h" using namespace GS; //------------------------------------------------------------------------------ static char __lowercase(char c) { if ((c >= 'A') && (c <= 'Z')) c = c - 'A' + 'a'; return c; } static bool __isValidSymbolCharacter(char c) { return ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (c == '_') ); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ #if (__PLATFORM_LINUX__ || __PLATFORM_OSX__ || __PLATFORM_IOS__ || __PLATFORM_ANDROID_NDK__) void strupr(char *s) { for (; s[0]; ++s) if ((s[0] >= 'a') || (s[0] <= 'z')) s[0] = toupper(s[0]); } void strlwr(char *s) { for (; s[0]; ++s) if ((s[0] >= 0x41) || (s[0] <= 0x5A)) s[0] = tolower(s[0]); } #endif //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void String::NormalizeEOL(EOLConvention to_eol) { switch (to_eol) { case EOLUnix: { uint invalid_eol_count = 0; for (char *p = _p.c_ptr(); p[0] && p[1]; ++p) if ((p[0] == '\r') && (p[1] == '\n')) { ++invalid_eol_count; ++p; } Array _o(Size() - invalid_eol_count + 1); // remove 1 byte per invalid EOL char *o = _o.c_ptr(), *p = _p.c_ptr(); for ( ; p[0] && p[1]; ++p) if ((p[0] == '\r') && (p[1] == '\n')) { *o++ = '\n'; ++p; } else *o++ = p[0]; if (p[0] != 0) *o++ = *p++; *o = 0; _p.Transfer(_o); } break; case EOLWindows: { uint invalid_eol_count = 0; char *p = _p.c_ptr(); for (int n = 0; p[n]; ++n) if ((p[n] == '\n') && ((n > 0) && (p[n - 1] != '\r'))) ++invalid_eol_count; Array _o(Size() + invalid_eol_count + 1); // add 1 byte per invalid EOL char *o = _o.c_ptr(); p = _p.c_ptr(); for (int n = 0; p[n]; ++n) if ((p[n] == '\n') && ((n > 0) && (p[n - 1] != '\r'))) { *o++ = '\r'; *o++ = '\n'; } else *o++ = p[n]; *o = 0; _p.Transfer(_o); } break; } Touch(); } String String::NormalizedEOL(EOLConvention to_eol) const { String o(*this); o.NormalizeEOL(to_eol); return o; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint String::Len() const { if (len == -1) (const_cast (len)) = _p.IsValid() ? strlen(_p) : 0; return len; } size_t String::Size() const { size_t size = 0; for (char *p = _p; p[0]; ++p) ++size; return size; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String String::RewritePatternAll(const char *p, const char *pattern, RewriteFunc &rewrite_func) { String out; forever { const char *s, *e; s = String::LocatePattern(p, pattern, &e); if (!s) { // append reminder out += String(p, p + String::strlen(p)); break; } // append skipped segment out += String(p, s); // extract args... List args; String source(s, e); if (!source.Extract(pattern, args)) break; // ...do rewrite. out += rewrite_func(source, args); p = e; } return out; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::Extract(const char *format, List &arg) { arg.Clear(); const char *o = format; for (const char *i = c_str(); i[0] && o[0]; ) { if (o[0] == '%') { ++o; // get arg value const char *e = i; for (; (e[0] != o[0]) && e[0]; ++e); arg.Add(String(i, e)); i = e; } else { if (i[0] != o[0]) { // ignore character mismatch on space if (i[0] == ' ') { ++i; continue; } // source format mismatch return false; } ++i; ++o; } } return true; } const char *String::LocatePattern(const char *s, const char *format, const char **e) { for (; s[0]; ++s) { const char *o = format; if (s[0] != o[0]) continue; const char *i = s; while (i[0] && o[0]) { if (o[0] == '%') { ++o; for (; (i[0] != o[0]) && i[0]; ++i); } else { if (i[0] != o[0]) { // ignore character mismatch on space if (i[0] == ' ') { ++i; continue; } // source format mismatch o = NULL; break; } ++i; ++o; } } if (o == NULL) continue; if (e) *e = i; return s; } return NULL; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::Compare(const char *s, CaseSensitivity cs) const { char *p = _p.c_ptr(); if (!s) return true; if (!p) return false; if (cs == CaseSensitive) { while (*p && *s) if (*p++ != *s++) return false; } else { while (*p && *s) if (__lowercase(*p++) != __lowercase(*s++)) return false; } return p[0] == s[0]; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::StartsWith(const char *b, CaseSensitivity cs) const { uint b_len = strlen(b); if (b_len > Len()) return false; return c_str() ? Left(b_len).Compare(b, cs) : false; } bool String::EndsWith(const char *b, CaseSensitivity cs) const { uint b_len = strlen(b); if (b_len > Len()) return false; return c_str() ? Right(b_len).Compare(b, cs) : false; } bool String::Contains(const char *b, CaseSensitivity cs) const { return c_str() ? asbool(FindString(b, cs)) : false; } bool String::Replace(const char *what, const char *by, CaseSensitivity cs) { char *sp = _p.c_ptr() ? FindString(what, 0, cs) : NULL; if (!sp) return false; uint what_len = strlen(what), by_len = strlen(by); uint final_len = Len() - what_len + by_len; Array np(final_len + 1, Alloc::StringBuffer); char *wp = np; if (np.IsNull()) return false; memcpy(wp, _p.c_ptr(), sp - _p.c_ptr()); wp += sp - _p; memcpy(wp, by, by_len); wp += by_len; memcpy(wp, sp + what_len, (_p + Len()) - (sp + what_len) + 1); Clear(); _p = np; return true; } bool String::ReplaceAll(const char **what, const char **by, bool match_whole_word) { if (!_p) return false; // no replace uint final_len = Len(); Array p_out(Alloc::StringBuffer); char *_p_out = NULL; for (uint p = 0; p < 2; ++p) { bool has_replacement = false; for (char *c_p = _p; *c_p; ) { // Search for a match. uint n; for (n = 0; what[n] && by[n]; ++n) if (!strccmp(c_p, what[n])) break; if (what[n] && by[n]) { // Got a match. size_t what_len = strlen(what[n]), by_len = strlen(by[n]); // Check whole word constraint. bool do_replace = true; if (match_whole_word) { if ((c_p > _p) && __isValidSymbolCharacter(c_p[-1])) do_replace = false; else if (c_p[what_len] && __isValidSymbolCharacter(c_p[what_len])) do_replace = false; } if (do_replace) { if (_p_out) { strncpy(_p_out, by[n], by_len); _p_out += by_len; } else final_len += by_len - what_len; has_replacement = true; } else if (_p_out) { strncpy(_p_out, c_p, what_len); _p_out += what_len; } c_p += what_len; } else { if (_p_out) *_p_out++ = *c_p; ++c_p; } } if (!has_replacement) return false; // no replace if (!_p_out) { p_out.Allocate(final_len + 1); _p_out = p_out; } } // Commit modified buffer to string. Clear(); p_out[final_len] = 0; _p = p_out; len = final_len; hash = 0; return true; } bool String::ReplaceAll(const char *what, const char *by, bool match_whole_word) { const char *_what[] = { what, NULL }, *_by[] = { by, NULL }; return ReplaceAll(_what, _by, match_whole_word); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ size_t String::Utf8toUtf32(const uchar *utf8, uint *utf32) { __ASSERT__(utf8); __ASSERT__(utf32); const uchar *p = utf8; int length = GetUtf8CharSize(utf8); switch (length) { case 4: *utf32 = (*p ^ 0xf0); break; case 3: *utf32 = (*p ^ 0xe0); break; case 2: *utf32 = (*p ^ 0xc0); break; case 1: *utf32 = *p; break; default: *utf32 = 0; break; } for (int n = length; n > 1; --n) { ++p; *utf32 <<= 6; *utf32 |= (*p ^ 0x80); } return length; } size_t String::GetUtf8CharSize(const uchar *utf8) { __ASSERT__(utf8); uchar c = utf8[0] >> 3; // 6 => 0x7e // 5 => 0x3e if (c == 0x1e) return 4; c >>= 1; if (c == 0xe) return 3; c >>= 1; if (c == 0x6) return 2; return 1; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String String::FromUcs2(const ushort *ucs2) { Array out(256); size_t out_count = 0; for (uint n = 0; ucs2[n]; ++n) { wchar_t w = ucs2[n]; if (out_count > (out.GetCount() - 4)) out.Reallocate(out.GetCount() * 2); if (w <= 0x7f) out[(int)out_count++] = (char)w; else if (w <= 0x7ff) { out[(int)out_count++] = 0xc0 | ((w >> 6) & 0x1f); out[(int)out_count++] = 0x80 | (w & 0x3f); } else /* if (w <= 0xffff) */ { out[(int)out_count++] = 0xe0 | ((w >> 12) & 0x0f); out[(int)out_count++] = 0x80 | ((w >> 6) & 0x3f); out[(int)out_count++] = 0x80 | (w & 0x3f); } /* else if (w <= 0x10ffff) { out[out_count++] = 0xf0 | ((w >> 18) & 0x07); out[out_count++] = 0x80 | ((w >> 12) & 0x3f); out[out_count++] = 0x80 | ((w >> 6) & 0x3f); out[out_count++] = 0x80 | (w & 0x3f); } else out[out_count++] = '?'; */ } return String(out.c_ptr(), out.c_ptr() + out_count); } Array String::toUcs2() const { if (!_p) return Array (Alloc::StringBuffer); // Get utf-8 character count. int ccount = 0; char *p = _p; for (; p[0]; ++ccount) p += GetUtf8CharSize((const uchar *)p); // Allocate array. Array ucs2(ccount + 1, Alloc::StringBuffer); p = _p; for (ccount = 0; p[0]; ++ccount) { uint utf32; p += Utf8toUtf32((const uchar *)p, &utf32); ucs2[ccount] = utf32 & 0xffff; } ucs2[ccount] = 0; return ucs2; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::IsAbsolutePath() const { return asbool((Len() > 2) && (c_str()[1] == ':')); } String String::BeautifyFileName(bool allow_spaces) const { if (!_p) return String(); Array p(Len() + 1, Alloc::StringBuffer); strncpy(p.c_ptr(), c_str(), Len() + 1); for (uint n = 0; n < Len(); ++n) { if (p[n] == ' ') { if (!allow_spaces) p[n] = '_'; } else { if (p[n] == '_') p[n] = ' '; else { if (n > 0) { if ((p[n - 1] == ' ') && (p[n] >= 'a') && (p[n] <= 'z')) p[n] += 'A' - 'a'; } else if ((p[n] >= 'a') && (p[n] <= 'z')) p[n] += 'A' - 'a'; } } } return String(&p[0]); } String String::CutFilePath() const { String b = *this; b.FileCutPath(); return b; } String String::CutFileExtension() const { String b = *this; b.FileCutExtension(); return b; } String String::CutFileName() const { String b = *this; b.FileCutName(); return b; } String String::CleanFilePath() const { String b = *this; b.FileCleanName(); return b; } String String::GetFileName() const { String b = *this; b.FileCutPathAndExtension(); return b; } String String::GetFileNameAndExtension() const { String b = *this; b.FileCutPath(); return b; } String String::GetFilePath() const { String b = *this; b.FileCutName(); return b; } String String::GetFileExtension() const { return FileGetExtension(c_str()); } String String::GetSwappedExtension(const char *ext) const { String b = *this; b.FileSwapExtension(ext); return b; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ int String::Compare(const String &c, const String &n) { const char *a = c.c_str(), *b = n.c_str(); if (!a || !b) return a == b ? 0 : 1; while (a[0] || b[0]) { if (!b[0]) return -1; if (!a[0]) return 1; if (b[0] < a[0]) return -1; if (b[0] > a[0]) return 1; a++; b++; } return 0; } String String::Upper() const { String s(_p); if (!s.IsEmpty()) strupr(s._p); s.Touch(); return s; } String String::Lower() const { String s(_p); if (!s.IsEmpty()) strlwr(s._p); s.Touch(); return s; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String String::Slice(uint s, uint e) const { if (s > Len()) s = Len(); if (e > Len()) e = Len(); return String(_p + s, _p + e); } String String::TrimChar(char c) { int occurence = 0; for (char *p = _p; p[0]; p++) if (p[0] == c) occurence++; if (!occurence) return *this; // Trim character occurrences. Array new_string(Len() - occurence + 1, Alloc::StringBuffer); char *pnew_string = &new_string[0]; for (char *p = _p; p[0]; p++) if (p[0] != c) *pnew_string++ = p[0]; pnew_string[0] = 0; return String(&new_string[0]); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ size_t String::IndexOf(char c, uint p) const { for (size_t n = p; n < Len(); ++n) if (_p[(int)n] == c) return n; return (size_t)-1; } char *String::FindString(const char *c, uint p, CaseSensitivity cs) const { size_t len_c = strlen(c); if (len_c <= Len()) for (size_t n = p; n <= (Len() - len_c); ++n) { size_t i = 0; switch (cs) { case CaseSensitive: for (; i < len_c; ++i) if (_p[(int)(n + i)] != c[i]) break; break; case CaseInsensitive: for (; i < len_c; ++i) if (__lowercase(_p[(int)(n + i)]) != __lowercase(c[i])) break; break; } if (i == len_c) return &_p[(int)n]; // match } return NULL; } String String::Left(uint n) const { return String(_p, _p + Types::Min(n, Len())); } String String::Right(uint n) const { const uint l = Len(); return String(_p + Types::Min(n, l), _p + l); } String String::Mid(uint p, int n) const { uint l = Len(); p = Types::Min(p, l); int char_left = int(l) - int(p); n = (n < 0) || (n > char_left) ? char_left : n; return String(_p + p, _p + p + n); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String String::Format(const char *format, ...) { char static_buffer[4096]; va_list varg; va_start(varg, format); vsnprintf(static_buffer, 4095, format, varg); va_end(varg); return String(static_buffer); } String String::operator + (const char *_ptr) const { String _s = *this; _s += _ptr; return _s; } String String::operator + (const String &s) const { String _s = *this; _s += s.c_str(); return _s; } void String::operator += (const char *_ptr) { if (!_ptr) return; if (_p.IsNull()) *this = _ptr; else { size_t in_len = strlen(_ptr), total_len = Len() + in_len; if ((total_len + 1) <= _p.GetCount()) { memcpy(&_p[Len()], _ptr, in_len); _p[(int)total_len] = 0; Touch(); } else { Array n_p(total_len + 1, Alloc::StringBuffer); if (n_p.IsValid()) { memcpy(&n_p[0], c_str(), Len()); memcpy(&n_p[Len()], _ptr, in_len); n_p[(int)total_len] = 0; _p.Transfer(n_p); Touch(); } else Clear(); } } } void String::operator += (float v) { char tmp[32]; _snprintf(tmp, 31, "%.4f", v); *this += tmp; } void String::operator += (double v) { char tmp[32]; _snprintf(tmp, 31, "%.4f", v); *this += tmp; } void String::operator += (int v) { char tmp[32]; _snprintf(tmp, 31, "%d", v); *this += tmp; } void String::operator += (uint v) { char tmp[32]; _snprintf(tmp, 31, "%d", (int)v); *this += tmp; } void String::operator += (bool v) { *this += v ? "true" : "false"; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String &String::operator = (const String &b) { if (b.IsEmpty()) Clear(); else { Set(b.c_str(), b.c_str() + b.Len()); hash = b.hash; len = b.len; } return *this; } String &String::operator = (const char *s) { Set(s); return *this; } void String::Set(const char *s, const char *e) { // TODO properly handle copying one part of a string to itself. if (s == _p) return; size_t new_len = s ? (e ? e - s : strlen(s)) : 0; if (new_len > 0) { if ((new_len + 1) > _p.GetCount()) { Clear(); _p.Allocate(new_len + 8); // Note: +8 to help reduce allocator pressure on string concatenation. } if (_p.IsValid()) { memcpy(_p, s, new_len); _p[(int)new_len] = 0; Touch(); } else Clear(); } else Clear(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ String &String::operator << (const String &v) { *this += v; return *this; } String &String::operator << (const char *v) { *this += v; return *this; } String &String::operator << (float v) { *this += v; return *this; } String &String::operator << (double v) { *this += v; return *this; } String &String::operator << (int v) { *this += v; return *this; } String &String::operator << (uint v) { *this += v; return *this; } String &String::operator << (bool v) { *this += v ? "true" : "false"; return *this; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ int String::atoh(const char *_sr) { ushort p = 0, s = 0; int v = 0; char str[32]; strncpy(str, _sr, 8); str[8] = 0; strlwr(str); while (str[p]) { if ((str[p] == 'x') || (str[p] == 'h')) { s = (ushort)(p + 1); break; } p++; } while (str[s]) { v *= 16; if ((str[s] >= '0') && (str[s] <= '9')) v += str[s] - '0'; else if ((str[s] >= 'a') && (str[s] <= 'f')) v += str[s] - 'a' + 10; s++; } return v; } int String::atoi(const char *p) { char sign = 1; if (p[0] == '-') { sign = -1; p++; } int integer = 0; while ((p[0] >= '0') && (p[0] <= '9')) { integer *= 10; integer += *p++ - '0'; } return (integer * sign); } float String::atof(const char *s, const char *e, bool support_comma) { if (!s) return 0.f; if (!e) e = s + strlen(s); float sign = 1; if (s == e) return 0.f; if (s[0] == '-') { sign = -1; s++; } if (s == e) return -0.f; float tt = 1.f; float real = 0.f; float integer = 0.f; while ((s < e) && (s[0] != '.') && ((s[0] != ',') || !support_comma)) { if (s[0] == 'e') goto exponent; if ((s[0] < '0') || (s[0] > '9')) return (float)(integer * sign); integer *= 10.f; integer += *s++ - '0'; } if (s == e) return (float)(integer * sign); s++; while ((s < e) && (s[0] != 'e')) { if ((s[0] != '-') && ((s[0] < '0') || (s[0] > '9'))) return sign * (integer + (float)real / tt); real *= 10.f; real += *s++ - '0'; tt *= 10.f; } if (s == e) return sign * (integer + real / tt); exponent:; s++; if (s == e) return (float)(integer * sign); int expsign = (*s++ == '-') ? -1 : 1; if (s == e) return (float)(integer * sign); uint exp = 0; float mulexp = 1; while ((s < e) && (s[0] >= '0') && (s[0] <= '9')) { exp *= 10; exp += *s++ - '0'; } for (uint expc = 0; expc < exp; expc++) mulexp *= 10.f; if (expsign == -1) return (float)(sign * (integer + (float)real / tt)) / mulexp; return (float)(sign * (integer + (float)real / tt)) * mulexp; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::FileCleanName() { if (!_p) return false; char *f = _p; Array n(Len() + 1, Alloc::StringBuffer); if (n.IsNull()) return false; // Remove drive letter case under the Windows platform. #if __PLATFORM_WINDOWS__ if ((Len() > 2) && (f[1] == ':')) if ((f[0] >= 'A') && (f[0] <= 'Z')) f[0] += 'a' - 'A'; #endif // Convert directory separator from backslash to forward slash. char *c = n; for (; f[0]; ++f) if (f[0] == '\\') f[0] = '/'; // Remove redundant forward slashes. f = _p; while (f[0]) { if (f[0] == '/') { f++; while (f[0] && (f[0] == '/')) f++; *c++ = '/'; } else *c++ = *f++; } c[0] = 0; // Replace illegal characters. f = n; #if __PLATFORM_WINDOWS__ if (!strcmp(f, "file:/")) f += 6; #endif if (f[0] == '@') // Allow internal mount points. f++; for (int i = 0; f[i]; ) { uint char_size = GetUtf8CharSize((const uchar *)&f[i]); if (char_size == 1) if ( // Allowed ranges. !((f[i] >= 'a') && (f[i] <= 'z')) && !((f[i] >= 'A') && (f[i] <= 'Z')) && !((f[i] >= '0') && (f[i] <= '9')) && // Allowed characters. (f[i] != ' ') && (f[i] != '.') && (f[i] != '_') && (f[i] != '/') && (f[i] != '-') && (f[i] != '!') && (f[i] != '+') && (f[i] != '(') && (f[i] != ')') && (f[i] != '#') && (f[i] != '\'') && (f[i] != '&') // Windows drive letter. #if __PLATFORM_WINDOWS__ && !((f[i] == ':') && (i == 1)) #endif ) f[i] = '_'; i += char_size; } // Now commit. Clear(); _p = n; Touch(); return true; } String String::FileGetExtension(const char *path) { if (!path) return ""; int n = (int)strlen(path); while (path[n] != '.' && (n >= 0)) n--; return n < 0 ? "" : &path[n + 1]; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ size_t String::strlen(const char *s) { size_t n = 0; if (s) for ( ; s[n]; ++n); return n; } bool String::strccmp(const char *sa, const char *sb) { if (!sa || !sb) return false; while (sa[0] && sb[0]) if (*sa++ != *sb++) return true; return false; } char *String::strfindchar(const char *f, const char c, const char *t) { if (t) while (f[0] && (f[0] != c) && (f < t)) f++; else while (f[0] && (f[0] != c)) f++; return const_cast (f); } void String::rmname(char *s) { if (!s) return; int n = (int)strlen(s) - 1; while ((n >= 0) && (s[n] != ':') && (s[n] != '\\') && (s[n] != '/')) n--; if (n < 0) s[0] = 0; else if (s[n + 1]) s[n + 1] = 0; } void String::rmpath(char *s) { String tmp(s); int n = (int)tmp.Len(); const char *p = tmp.c_str(); if (!p) return; while ((n >= 0) && (p[n] != '\\') && (p[n] != '/')) n--; if (n < 0) return; strcpy(s, &p[n+1]); } void String::rmext(char *s) { if (!s) return; int n = (int)strlen(s) - 1; while ((n >= 0) && (s[n] != '.')) { if ((s[n] == '\\') || (s[n] == '/') || (s[n] == ':')) break; n--; } if (n < 0) return; s[n] = 0; } void String::rmpathext(char *s) { rmpath(s); rmext(s); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::Allocate(uint size) { Touch(); if (!_p.Allocate(size + 1)) return false; Memory::Set(_p.c_ptr(), 0, _p.GetSize()); return true; } void String::Touch() { len = -1; hash = 0; } void String::Clear() { _p.Free(); Touch(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::equals(const char *s, const uint hash) const { if (hash && (hash != Hash())) return false; if (_p == s) return true; return (_p && !strcmp(s, _p)) ? true : false; } bool String::operator == (const String &b) const { return b.IsEmpty() ? IsEmpty() : equals(b, b.Hash()); } bool String::operator == (const char *s) const { return s ? equals(s) : IsEmpty(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool String::operator != (const String &b) const { return !(*this == b); } bool String::operator != (const char *s) const { return !(*this == s); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void String::operator += (const String &b) { if (!b.IsEmpty()) *this += b.c_str(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint String::strhash(const char *s) { uint hash = 0; if (s) while (s[0]) { hash = hash * 37 + s[0]; s++; } hash |= 1; return hash; } uint String::Hash() const { if (hash == 0) hash = strhash(_p); return hash; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void String::FileCutPath() { rmpath(_p); Touch(); } void String::FileSwapExtension(const char *ext) { rmext(_p); Touch(); *this += ext; } void String::FileCutExtension() { rmext(_p); Touch(); } void String::FileCutPathAndExtension() { rmpathext(_p); Touch(); } void String::FileCutName() { rmname(_p); Touch(); } //------------------------------------------------------------------------------