blob: 6f2e055e28b62630441b562ab96f2922a4918e83 [file] [log] [blame]
/*
* Copyright (C) 2001 Apple Computer, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
This implementation uses CFMutableStringRefs as a rep for the actual
string data. Reps may be shared between QString instances, and follow
a copy-on-write sematic. If you change the implementation be sure to
copy the rep before calling any of the CF functions that might mutate
the string.
*/
#include <Foundation/Foundation.h>
#include <kwqdebug.h>
#include <qstring.h>
#include <qregexp.h>
#include <stdio.h>
#ifndef USING_BORROWED_QSTRING
// QString class ===============================================================
// constants -------------------------------------------------------------------
const QString QString::null;
static CFMutableStringRef
getNullCFString()
{
static CFStringRef ref = CFSTR("");
CFRetain(ref);
return (CFMutableStringRef)ref;
}
static const QChar *
getNullQCharString()
{
static QChar nullCharacter;
return &nullCharacter;
}
// static member functions -----------------------------------------------------
QString QString::number(int n)
{
QString qs;
qs.setNum(n);
return qs;
}
QString QString::number(uint n)
{
QString qs;
qs.setNum(n);
return qs;
}
QString QString::number(long n)
{
QString qs;
qs.setNum(n);
return qs;
}
QString QString::number(ulong n)
{
QString qs;
qs.setNum(n);
return qs;
}
QString QString::number(double n)
{
QString qs;
qs.setNum(n);
return qs;
}
#ifdef USING_BORROWED_KURL
QString QString::fromLocal8Bit(const char *chs, int len)
{
// FIXME: is MacRoman the correct encoding?
return fromStringWithEncoding(chs, len, kCFStringEncodingMacRoman);
}
#endif // USING_BORROWED_KURL
QString QString::fromStringWithEncoding(const char *chs, int len,
CFStringEncoding encoding)
{
QString qs;
if (chs && *chs) {
qs.s = CFStringCreateMutable(kCFAllocatorDefault, 0);
if (qs.s) {
if (len < 0) {
// append null-terminated string
CFStringAppendCString(qs.s, chs, encoding);
} else {
// append length-specified string
// FIXME: can we find some way of not using this temporary?
char *buf = CFAllocatorAllocate(kCFAllocatorDefault, len + 1, 0);
strncpy(buf, chs, len);
*(buf + len) = '\0';
CFStringAppendCString(qs.s, buf, encoding);
CFAllocatorDeallocate(kCFAllocatorDefault, buf);
}
}
}
return qs;
}
QString QString::fromCFMutableString(CFMutableStringRef cfs)
{
QString qs;
// shared copy
if (cfs) {
CFRetain(cfs);
qs.s = cfs;
}
return qs;
}
QString QString::fromCFString(CFStringRef cfs)
{
CFMutableStringRef ref;
QString qs;
ref = CFStringCreateMutableCopy(NULL, 0, cfs);
qs = QString::fromCFMutableString(ref);
CFRelease(ref);
return qs;
}
QString QString::gstring_toQString(CFMutableStringRef *ref, UniChar *uchars, int len)
{
if (*ref == 0)
*ref = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, uchars, len, len, kCFAllocatorDefault);
else
CFStringSetExternalCharactersNoCopy(*ref, uchars, len, len);
return QString::fromCFMutableString(*ref);
}
CFMutableStringRef QString::gstring_toCFString(CFMutableStringRef *ref, UniChar *uchars, int len)
{
if (*ref == 0)
*ref = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, uchars, len, len, kCFAllocatorDefault);
else
CFStringSetExternalCharactersNoCopy(*ref, uchars, len, len);
return *ref;
}
// constructors, copy constructors, and destructors ----------------------------
QString::QString()
{
s = getNullCFString();
cache = NULL;
}
QString::~QString()
{
CFRelease(s);
if (cache)
CFAllocatorDeallocate(kCFAllocatorDefault, cache);
}
QString::QString(QChar qc)
{
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCharacters(s, &qc.c, 1);
cache = NULL;
}
QString::QString(const QByteArray &qba)
{
if (qba.size() && *qba.data()) {
CFStringRef tmp = CFStringCreateWithBytes
(kCFAllocatorDefault, (const UInt8 *) qba.data(), qba.size(),
kCFStringEncodingISOLatin1, false);
s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, tmp);
CFRelease(tmp);
} else
s = getNullCFString();
cache = NULL;
}
QString::QString(const QChar *qcs, uint len)
{
if (qcs || len) {
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCharacters(s, &qcs->c, len);
} else
s = getNullCFString();
cache = NULL;
}
QString::QString(const char *chs)
{
if (chs && *chs) {
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCString(s, chs, kCFStringEncodingISOLatin1);
} else
s = getNullCFString();
cache = NULL;
}
QString::QString(const char *chs, int len)
{
if (len > 0) {
CFStringRef tmp = CFStringCreateWithBytes
(kCFAllocatorDefault, (const UInt8 *)chs, len,
kCFStringEncodingISOLatin1, false);
s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, tmp);
CFRelease(tmp);
} else
s = getNullCFString();
cache = NULL;
}
QString::QString(const QString &qs)
{
// shared copy
CFRetain(qs.s);
s = qs.s;
cache = NULL;
}
// assignment operators --------------------------------------------------------
QString &QString::operator=(const QString &qs)
{
// shared copy
CFRetain(qs.s);
CFRelease(s);
s = qs.s;
flushCache();
return *this;
}
QString &QString::operator=(const QCString &qcs)
{
return *this = QString(qcs);
}
QString &QString::operator=(const char *chs)
{
return *this = QString(chs);
}
QString &QString::operator=(QChar qc)
{
return *this = QString(qc);
}
QString &QString::operator=(char ch)
{
return *this = QString(QChar(ch));
}
// member functions ------------------------------------------------------------
QChar QString::at(uint index) const
{
CFIndex signedIndex = index;
CFIndex len = CFStringGetLength(s);
if (signedIndex < len)
return CFStringGetCharacterAtIndex(s, signedIndex);
return 0;
}
const QChar *QString::unicode() const
{
const UniChar *ucs = CFStringGetCharactersPtr(s);
if (ucs)
return (QChar *) ucs;
uint len = length();
if (len == 0)
return getNullQCharString();
if (cache == NULL || * (int *) cache != CacheUnicode) {
flushCache();
cache = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(int) + len * sizeof (UniChar), 0);
* (int *) cache = CacheUnicode;
CFStringGetCharacters(s, CFRangeMake(0, len), (UniChar *) ((int *) cache + 1));
}
return (QChar *) ((int *) cache + 1);
}
const char *QString::latin1() const
{
const char *chs = CFStringGetCStringPtr(s, kCFStringEncodingISOLatin1);
if (chs)
return chs;
uint len = length();
if (len == 0)
return "";
if (cache == NULL || * (int *) cache != CacheLatin1) {
flushCache();
cache = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(int) + len + 1, 0);
* (int *) cache = CacheLatin1;
if (!CFStringGetCString(s, (char *) ((int *) cache + 1), len + 1, kCFStringEncodingISOLatin1)) {
* (char *) ((int *) cache + 1) = '\0';
}
}
return (char *) ((int *) cache + 1);
}
QCString QString::utf8() const
{
return convertToQCString(kCFStringEncodingUTF8);
}
QCString QString::local8Bit() const
{
return convertToQCString(kCFStringEncodingMacRoman);
}
bool QString::isNull() const
{
return s == getNullCFString();
}
int QString::find(QChar qc, int index) const
{
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (len && (index >= 0) && (index < len)) {
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, len));
for (CFIndex i = index; i < len; i++)
if (qc.c == CFStringGetCharacterFromInlineBuffer(&buf, i))
return i;
}
return -1;
}
int QString::find(char ch, int index) const
{
return find(QChar(ch), index);
}
int QString::find(const QString &qs, int index) const
{
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (len && (index >= 0) && (index < len)) {
CFRange r;
CFRange start = CFRangeMake(index, len - index);
if (CFStringFindWithOptions(s, qs.s, start, 0, &r))
return r.location;
}
return -1;
}
#ifdef DEBUG_FIND_COUNTER
static int findCount = 0;
static int findExpensiveCount = 0;
static int findCheapCount = 0;
#endif
const int caseDelta = ('a' - 'A');
int QString::find(const char *chs, int index, bool caseSensitive) const
{
int pos = -1;
if (chs) {
#ifdef DEBUG_FIND_COUNTER
findCount++;
#endif
const UniChar *internalBuffer = CFStringGetCharactersPtr(s);
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (internalBuffer == 0){
#ifdef DEBUG_FIND_COUNTER
findExpensiveCount++;
if (findCount % 500 == 0)
fprintf (stdout, "findCount = %d, expensive = %d, cheap = %d\n", findCount, findExpensiveCount, findCheapCount);
#endif
if (len && (index >= 0) && (index < len)) {
CFStringRef tmp = CFStringCreateWithCStringNoCopy(
kCFAllocatorDefault, chs, kCFStringEncodingISOLatin1,
kCFAllocatorNull);
CFRange r;
if (CFStringFindWithOptions(s, tmp,
CFRangeMake(index, len - index),
caseSensitive ? 0 : kCFCompareCaseInsensitive, &r)) {
pos = r.location;
}
CFRelease(tmp);
}
}
else {
#ifdef DEBUG_FIND_COUNTER
findCheapCount++;
if (findCount % 500 == 0)
fprintf (stdout, "findCount = %d, expensive = %d, cheap = %d\n", findCount, findExpensiveCount, findCheapCount);
#endif
if (len && (index >= 0) && (index < len)) {
UniChar firstC, c1, c2;
const char *_chs;
int remaining = len - index, found = -1;
int compareToLength = strlen(chs);
internalBuffer = &internalBuffer[index];
_chs = chs + 1;
firstC = (UniChar)(*chs);
while (remaining >= compareToLength){
if (*internalBuffer++ == firstC){
const UniChar *compareTo = internalBuffer;
found = len - remaining;
while ( (c2 = (UniChar)(*_chs++)) ){
c1 = (UniChar)(*compareTo++);
if (!caseSensitive){
if (c2 >= 'a' && c2 <= 'z'){
if (c1 == c2 || c1 == c2 - caseDelta)
continue;
}
else if (c2 >= 'A' && c2 <= 'Z'){
if (c1 == c2 || c1 == c2 + caseDelta)
continue;
}
else if (c1 == c2)
continue;
}
else if (c1 == c2)
continue;
break;
}
if (c2 == 0)
return found;
_chs = chs + 1;
}
remaining--;
}
}
}
}
return pos;
}
int QString::find(const QRegExp &qre, int index) const
{
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (len && (index >= 0) && (index < len))
return qre.match(*this, index);
return -1;
}
int QString::findRev(char ch, int index) const
{
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (len && (index <= len)) {
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, index));
for (CFIndex i = index; i >= 0; i--) {
if (ch == CFStringGetCharacterFromInlineBuffer(&buf, i))
return i;
}
}
return -1;
}
int QString::findRev(const char *chs, int index) const
{
int pos = -1;
if (chs) {
CFIndex len = CFStringGetLength(s);
if (index < 0)
index += len;
if (len && (index <= len)) {
CFStringRef tmp = CFStringCreateWithCStringNoCopy(
kCFAllocatorDefault, chs, kCFStringEncodingISOLatin1,
kCFAllocatorNull);
CFRange r;
if (CFStringFindWithOptions(s, tmp, CFRangeMake(0, index),
kCFCompareBackwards, &r)) {
pos = r.location;
}
CFRelease(tmp);
}
}
return pos;
}
#ifdef DEBUG_CONTAINS_COUNTER
static int containsCount = 0;
#endif
int QString::contains(char ch) const
{
int c = 0;
CFIndex len = CFStringGetLength(s);
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, len));
for (CFIndex i = 0; i < len; i++)
if (ch == CFStringGetCharacterFromInlineBuffer(&buf, i))
c++;
return c;
}
int QString::contains(const char *chs, bool cs) const
{
int c = 0;
if (chs) {
#ifdef DEBUG_CONTAINS_COUNTER
containsCount++;
if (containsCount % 500 == 0)
fprintf (stdout, "containsCount = %d\n", containsCount);
#endif
CFStringRef tmp = CFStringCreateWithCStringNoCopy(
kCFAllocatorDefault, chs, kCFStringEncodingISOLatin1,
kCFAllocatorNull);
CFIndex pos = 0;
CFIndex len = CFStringGetLength(s);
while (pos < len) {
CFRange r;
if (!CFStringFindWithOptions(s, tmp,
CFRangeMake(pos, len - pos),
cs ? 0 : kCFCompareCaseInsensitive, &r)) {
break;
}
c++;
// move to next possible overlapping match
pos += r.location + 1;
}
CFRelease(tmp);
}
return c;
}
short QString::toShort(bool *ok, int base) const
{
bool neg;
short n = convertToNumber(ok, base, &neg);
return neg ? -n : n;
}
ushort QString::toUShort(bool *ok, int base) const
{
return convertToNumber(ok, base, NULL);
}
int QString::toInt(bool *ok, int base) const
{
bool neg;
int n = convertToNumber(ok, base, &neg);
return neg ? -n : n;
}
uint QString::toUInt(bool *ok, int base) const
{
return convertToNumber(ok, base, NULL);
}
long QString::toLong(bool *ok, int base) const
{
bool neg;
long n = convertToNumber(ok, base, &neg);
return neg ? -n : n;
}
ulong QString::toULong(bool *ok, int base) const
{
return convertToNumber(ok, base, NULL);
}
double QString::toDouble(bool *ok) const
{
double n;
n = CFStringGetDoubleValue(s);
if (ok) {
// NOTE: since CFStringGetDoubleValue returns 0.0 on error there is no
// way to know if "n" is valid in that case
//
// EXTRA NOTE: We can't assume 0.0 is bad, since it totally breaks
// html like border="0". So, only trigger breakage if the char
// at index 0 is neither a '0' nor a '.' nor a '-'.
*ok = true;
if (n == 0.0) {
UniChar uc = CFStringGetLength(s) == 0 ? 0 : CFStringGetCharacterAtIndex(s, 0);
if (uc != '0' && uc != '.' && uc != '-') {
*ok = false;
}
}
}
return n;
}
QString QString::arg(const QString &replacement, int width) const
{
QString qs;
if (CFStringGetLength(s)) {
qs.s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, s);
}
if (qs.s) {
CFIndex pos = 0;
UniChar found = 0;
CFIndex len = CFStringGetLength(qs.s);
if (len) {
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(qs.s, &buf, CFRangeMake(0, len));
// find position of lowest numerical position marker
for (CFIndex i = 0; i < len; i++) {
UniChar uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
if ((uc == '%') && ((i + 1) < len)) {
UniChar uc2 = CFStringGetCharacterFromInlineBuffer(&buf,
i + 1);
if ((uc2 >= '0') && (uc2 <= '9')) {
if (!found || (uc2 < found)) {
found = uc2;
pos = i;
}
}
}
}
}
CFIndex mlen;
if (found) {
mlen = 2;
} else {
// append space and then replacement text at end of string
CFStringAppend(qs.s, CFSTR(" "));
pos = len + 1;
mlen = 0;
}
if (replacement.s) {
CFStringReplace(qs.s, CFRangeMake(pos, mlen), replacement.s);
if (width) {
CFIndex rlen = CFStringGetLength(replacement.s);
CFIndex padding;
if (width < 0) {
padding = -width;
pos += rlen;
} else {
padding = width;
}
padding -= rlen;
if (padding > 0) {
CFMutableStringRef tmp =
CFStringCreateMutable(kCFAllocatorDefault, 0);
if (tmp) {
CFStringPad(tmp, CFSTR(" "), padding, 0);
CFStringInsert(qs.s, pos, tmp);
CFRelease(tmp);
}
}
}
}
}
return qs;
}
QString QString::arg(short replacement, int width) const
{
return arg(number((int)replacement), width);
}
QString QString::arg(ushort replacement, int width) const
{
return arg(number((uint)replacement), width);
}
QString QString::arg(int replacement, int width) const
{
return arg(number(replacement), width);
}
QString QString::arg(uint replacement, int width) const
{
return arg(number(replacement), width);
}
QString QString::arg(long replacement, int width) const
{
return arg(number(replacement), width);
}
QString QString::arg(ulong replacement, int width) const
{
return arg(number(replacement), width);
}
QString QString::arg(double replacement, int width) const
{
return arg(number(replacement), width);
}
QString QString::left(uint width) const
{
return leftRight(width, true);
}
QString QString::right(uint width) const
{
return leftRight(width, false);
}
QString QString::mid(uint index, uint width) const
{
QString qs;
CFIndex len = CFStringGetLength(s);
if (len && (index < (uint)len) && width) {
if (!((index == 0) && (width >= (uint)len))) {
if (width > (len - index)) {
width = len - index;
}
CFStringRef tmp = CFStringCreateWithSubstring(
kCFAllocatorDefault, s, CFRangeMake(index, width));
if (tmp) {
qs.s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0,
tmp);
CFRelease(tmp);
}
} else {
CFRetain(s);
qs.s = s;
}
}
return qs;
}
QString QString::copy() const
{
// does not need to be a deep copy
return *this;
}
QString QString::lower() const
{
QString qs;
if (CFStringGetLength(s)) {
qs.s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, s);
CFStringLowercase(qs.s, NULL);
}
return qs;
}
QString QString::stripWhiteSpace() const
{
QString qs;
CFIndex len = CFStringGetLength(s);
if (len) {
static CFCharacterSetRef wscs = CFCharacterSetGetPredefined(
kCFCharacterSetWhitespaceAndNewline);
if (CFCharacterSetIsCharacterMember(wscs,
CFStringGetCharacterAtIndex(s, 0))
|| CFCharacterSetIsCharacterMember(wscs,
CFStringGetCharacterAtIndex(s, len - 1))) {
qs.s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, s);
CFStringTrimWhitespace(qs.s);
} else {
CFRetain(s);
qs.s = s;
}
}
return qs;
}
QString QString::simplifyWhiteSpace() const
{
QString qs;
CFIndex len = CFStringGetLength(s);
if (len) {
qs.s = CFStringCreateMutable(kCFAllocatorDefault, 0);
static CFCharacterSetRef wscs = CFCharacterSetGetPredefined(
kCFCharacterSetWhitespaceAndNewline);
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, len));
bool chars = false;
bool space = false;
for (CFIndex i = 0; i < len; i++) {
UniChar uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
if (CFCharacterSetIsCharacterMember(wscs, uc)) {
if (chars)
space = true;
} else {
if (space) {
UniChar spc = ' ';
CFStringAppendCharacters(qs.s, &spc, 1);
space = false;
}
CFStringAppendCharacters(qs.s, &uc, 1);
chars = true;
}
}
} else {
CFRetain(s);
qs.s = s;
}
return qs;
}
QString &QString::setUnicode(const QChar *qcs, uint len)
{
flushCache();
CFRelease(s);
if (qcs && len) {
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCharacters(s, &qcs->c, len);
} else {
s = getNullCFString();
}
return *this;
}
QString &QString::setLatin1(const char *chs)
{
flushCache();
CFRelease(s);
if (chs && *chs) {
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCString(s, chs, kCFStringEncodingISOLatin1);
} else {
s = getNullCFString();
}
return *this;
}
QString &QString::setNum(short n)
{
return setNum((long)n);
}
QString &QString::setNum(ushort n)
{
return setNum((ulong)n);
}
QString &QString::setNum(int n)
{
const int capacity = 64;
char buf[capacity];
buf[snprintf(buf, capacity - 1, "%d", n)] = '\0';
return setLatin1(buf);
}
QString &QString::setNum(uint n)
{
const int capacity = 64;
char buf[capacity];
buf[snprintf(buf, capacity - 1, "%u", n)] = '\0';
return setLatin1(buf);
}
QString &QString::setNum(long n)
{
const int capacity = 64;
char buf[capacity];
buf[snprintf(buf, capacity - 1, "%D", n)] = '\0';
return setLatin1(buf);
}
QString &QString::setNum(ulong n)
{
const int capacity = 64;
char buf[capacity];
buf[snprintf(buf, capacity - 1, "%U", n)] = '\0';
return setLatin1(buf);
}
QString &QString::setNum(double n)
{
const int capacity = 64;
char buf[capacity];
buf[snprintf(buf, capacity - 1, "%.6lg", n)] = '\0';
return setLatin1(buf);
}
QString &QString::sprintf(const char *format, ...)
{
va_list args;
va_start(args, format);
flushCache();
CFRelease(s);
if (format && *format) {
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringRef f = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
format, kCFStringEncodingISOLatin1, kCFAllocatorNull);
if (f) {
CFStringRef tmp = CFStringCreateWithFormatAndArguments(
kCFAllocatorDefault, NULL, f, args);
if (tmp) {
CFStringReplaceAll(s, tmp);
CFRelease(tmp);
}
CFRelease(f);
}
} else {
s = getNullCFString();
}
va_end(args);
return *this;
}
QString &QString::prepend(const QString &qs)
{
return insert(0, qs);
}
QString &QString::append(const QString &qs)
{
return insert(length(), qs);
}
void QString::_copyIfNeededInternalString()
{
if (CFGetRetainCount(s) != 1) {
CFMutableStringRef tmp = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, s);
CFRelease(s);
s = tmp;
}
}
QString &QString::insert(uint index, const QString &qs)
{
flushCache();
_copyIfNeededInternalString();
if (index < (uint) CFStringGetLength(s))
CFStringInsert(s, index, qs.s);
else
CFStringAppend(s, qs.s);
return *this;
}
QString &QString::insert(uint index, QChar qc)
{
flushCache();
_copyIfNeededInternalString();
UniChar uch = qc.unicode();
if (index < (uint) CFStringGetLength(s)) {
CFStringRef chs = CFStringCreateWithCharactersNoCopy(NULL, &uch, 1, kCFAllocatorNull);
CFStringInsert(s, index, chs);
CFRelease(chs);
} else {
CFStringAppendCharacters(s, &uch, 1);
}
return *this;
}
QString &QString::insert(uint index, char ch)
{
flushCache();
_copyIfNeededInternalString();
UniChar uch = (uchar) ch;
if (index < (uint) CFStringGetLength(s)) {
CFStringRef chs = CFStringCreateWithCharactersNoCopy(NULL, &uch, 1, kCFAllocatorNull);
CFStringInsert(s, index, chs);
CFRelease(chs);
} else {
CFStringAppendCharacters(s, &uch, 1);
}
return *this;
}
QString &QString::remove(uint index, uint width)
{
flushCache();
CFIndex len = CFStringGetLength(s);
if (len && (index < (uint)len) && width) {
if (width > (len - index)) {
width = len - index;
}
_copyIfNeededInternalString();
CFStringDelete(s, CFRangeMake(index, width));
}
return *this;
}
QString &QString::replace(const QRegExp &qre, const QString &qs)
{
int len = qs.length();
for (int i = 0; i < CFStringGetLength(s); i += len) {
int width = 0;
i = qre.match(*this, i, &width, false);
flushCache();
if ((i < 0) || !width) {
break;
}
CFRange r = CFRangeMake(i, width);
_copyIfNeededInternalString();
if (len) {
CFStringReplace(s, r, qs.s);
} else {
CFStringDelete(s, r);
}
}
return *this;
}
void QString::truncate(uint newLen)
{
flushCache();
_copyIfNeededInternalString();
CFIndex len = CFStringGetLength(s);
if (len && (newLen < (uint)len)) {
CFStringDelete(s, CFRangeMake(newLen, len - newLen));
}
}
void QString::fill(QChar qc, int len)
{
flushCache();
if (len < 0)
len = CFStringGetLength(s);
CFRelease(s);
if (len <= 0)
s = getNullCFString();
else {
UniChar *ucs = CFAllocatorAllocate(kCFAllocatorDefault, len * sizeof (UniChar), 0);
for (int i = 0; i < len; i++)
ucs[i] = qc.c;
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCharacters(s, ucs, len);
CFAllocatorDeallocate(kCFAllocatorDefault, ucs);
}
}
void QString::compose()
{
// FIXME: unimplemented because we don't do ligatures yet
_logNotYetImplemented();
}
QString QString::visual()
{
// FIXME: unimplemented because we don't do BIDI yet
_logNotYetImplemented();
return QString(*this);
}
// operators -------------------------------------------------------------------
QString &QString::operator+=(const QString &qs)
{
return insert(length(), qs);
}
QString &QString::operator+=(QChar qc)
{
return insert(length(), qc);
}
QString &QString::operator+=(char ch)
{
return insert(length(), ch);
}
// private member functions ----------------------------------------------------
void QString::flushCache() const
{
if (cache) {
CFAllocatorDeallocate(kCFAllocatorDefault, cache);
cache = NULL;
}
}
QCString QString::convertToQCString(CFStringEncoding enc) const
{
uint len = length();
if (len) {
char *chs = CFAllocatorAllocate(kCFAllocatorDefault, len + 1, 0);
if (chs) {
if (!CFStringGetCString(s, chs, len + 1, enc)) {
*reinterpret_cast<char *>(chs) = '\0';
}
QCString qcs = QCString(chs);
CFAllocatorDeallocate(kCFAllocatorDefault, chs);
return qcs;
}
}
return QCString();
}
ulong QString::convertToNumber(bool *ok, int base, bool *neg) const
{
ulong n = 0;
bool valid = false;
bool negative = false;
if (s) {
CFIndex len = CFStringGetLength(s);
if (len) {
CFStringInlineBuffer buf;
UniChar uc;
static CFCharacterSetRef wscs =
CFCharacterSetGetPredefined(
kCFCharacterSetWhitespaceAndNewline);
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, len));
CFIndex i;
// ignore any leading whitespace
for (i = 0; i < len; i++) {
uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
if (!CFCharacterSetIsCharacterMember(wscs, uc)) {
break;
}
}
if (neg) {
// is there a sign?
if (i < len) {
uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
if (uc == '-') {
i++;
negative = true;
} else if (uc == '+') {
i++;
}
}
}
ulong max = negative ? LONG_MAX : ULONG_MAX;
// is there a number?
while (i < len) {
uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
// NOTE: ignore anything other than base 10 and base 16
if ((uc >= '0') && (uc <= '9')) {
if (n > (max / base)) {
valid = false;
break;
}
n = (n * base) + (uc - '0');
} else if (base == 16) {
if ((uc >= 'A') && (uc <= 'F')) {
if (n > (max / base)) {
valid = false;
break;
}
n = (n * base) + (10 + (uc - 'A'));
} else if ((uc >= 'a') && (uc <= 'f')) {
if (n > (max / base)) {
valid = false;
break;
}
n = (n * base) + (10 + (uc - 'a'));
} else {
break;
}
} else {
break;
}
valid = true;
i++;
}
// ignore any trailing whitespace
while (i < len) {
uc = CFStringGetCharacterFromInlineBuffer(&buf, i);
if (!CFCharacterSetIsCharacterMember(wscs, uc)) {
valid = false;
break;
}
i++;
}
}
}
if (ok) {
*ok = valid;
}
if (neg) {
*neg = negative;
}
return valid ? n : 0;
}
QString QString::leftRight(uint width, bool left) const
{
QString qs;
if (s) {
CFIndex len = CFStringGetLength(s);
if (len && width) {
if ((uint)len > width) {
CFStringRef tmp = CFStringCreateWithSubstring(
kCFAllocatorDefault, s, left ? CFRangeMake(0, width)
: CFRangeMake(len - width, width));
qs.s = CFStringCreateMutableCopy(kCFAllocatorDefault, 0,
tmp);
CFRelease(tmp);
} else {
CFRetain(s);
qs.s = s;
}
}
}
return qs;
}
int QString::compareToLatin1(const char *chs) const
{
if (!chs || !*chs) {
if (length() == 0)
return kCFCompareEqualTo;
return kCFCompareGreaterThan;
}
CFIndex len = CFStringGetLength(s);
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(s, &buf, CFRangeMake(0, len));
for (CFIndex i = 0; i < len; i++) {
UniChar c1 = CFStringGetCharacterFromInlineBuffer(&buf, i);
UniChar c2 = (uchar) chs[i];
if (c1 < c2)
return kCFCompareLessThan;
if (c1 > c2)
return kCFCompareGreaterThan;
}
if (chs[len] == 0)
return kCFCompareEqualTo;
return kCFCompareGreaterThan;
}
// operators associated with QString ===========================================
bool operator==(const QString &qs1, const QString &qs2)
{
return CFEqual(qs1.s, qs2.s);
}
bool operator==(const QString &qs, const char *chs)
{
if (!chs || !*chs) {
if (qs.length() == 0)
return kCFCompareEqualTo;
return kCFCompareGreaterThan;
}
CFIndex len = CFStringGetLength(qs.s);
CFIndex chsLen = strlen(chs);
if (len != chsLen)
return false;
CFStringInlineBuffer buf;
CFStringInitInlineBuffer(qs.s, &buf, CFRangeMake(0, len));
for (CFIndex i = 0; i < len; i++) {
UniChar c1 = CFStringGetCharacterFromInlineBuffer(&buf, i);
UniChar c2 = (uchar) chs[i];
if (c1 != c2)
return false;
}
return true;
}
QString operator+(const QString &qs1, const QString &qs2)
{
QString tmp(qs1);
tmp += qs2;
return tmp;
}
QString operator+(const QString &qs, const char *chs)
{
QString tmp(qs);
tmp += chs;
return tmp;
}
QString operator+(const QString &qs, QChar qc)
{
QString tmp(qs);
tmp += qc;
return tmp;
}
QString operator+(const QString &qs, char ch)
{
QString tmp(qs);
tmp += ch;
return tmp;
}
QString operator+(const char *chs, const QString &qs)
{
QString tmp(chs);
tmp += qs;
return tmp;
}
QString operator+(QChar qc, const QString &qs)
{
QString tmp = qc;
tmp += qs;
return tmp;
}
QString operator+(char ch, const QString &qs)
{
QString tmp = QChar(ch);
tmp += qs;
return tmp;
}
// class QConstString ==========================================================
// constructors, copy constructors, and destructors ----------------------------
QConstString::QConstString(QChar *qcs, uint len)
{
if (qcs || len) {
// NOTE: use instead of CFStringCreateWithCharactersNoCopy function to
// guarantee backing store is not copied even though string is mutable
//s = CFStringCreateMutableWithExternalCharactersNoCopy(
// kCFAllocatorDefault, &qcs->c, len, len, kCFAllocatorNull);
s = CFStringCreateMutable(kCFAllocatorDefault, 0);
CFStringAppendCharacters(s, &qcs->c, len);
} else {
s = getNullCFString();
}
cache = NULL;
}
// member functions ------------------------------------------------------------
#else // USING_BORROWED_QSTRING
// This will help to keep the linker from complaining about empty archives
void KWQString_Dummy() {}
#endif // USING_BORROWED_QSTRING