You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2389 lines
42 KiB
2389 lines
42 KiB
/*****************************************************************************/
|
|
// Copyright 2006-2007 Adobe Systems Incorporated
|
|
// All Rights Reserved.
|
|
//
|
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in
|
|
// accordance with the terms of the Adobe license agreement accompanying it.
|
|
/*****************************************************************************/
|
|
|
|
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_string.cpp#2 $ */
|
|
/* $DateTime: 2012/07/31 22:04:34 $ */
|
|
/* $Change: 840853 $ */
|
|
/* $Author: tknoll $ */
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include "dng_string.h"
|
|
|
|
#include "dng_assertions.h"
|
|
#include "dng_exceptions.h"
|
|
#include "dng_flags.h"
|
|
#include "dng_mutex.h"
|
|
#include "dng_utils.h"
|
|
#include "dng_safe_arithmetic.h"
|
|
|
|
#if qMacOS
|
|
#include <TargetConditionals.h>
|
|
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
#include <MobileCoreServices/MobileCoreServices.h>
|
|
#else
|
|
#include <CoreServices/CoreServices.h>
|
|
#endif // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
#endif // qMacOS
|
|
|
|
#if qWinOS
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#if qiPhone || qAndroid || qLinux
|
|
#include <ctype.h> // for isdigit
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
const uint32 kREPLACEMENT_CHARACTER = 0x0000FFFD;
|
|
|
|
/*****************************************************************************/
|
|
|
|
// Returns the length of the zero-terminated string 's'. Throws a dng_exception
|
|
// if the length of 's' is too large to be represented as a uint32_t.
|
|
static uint32 strlenAsUint32(const char *s)
|
|
{
|
|
|
|
uint32 lengthAsUint32 = 0;
|
|
ConvertUnsigned(strlen(s), &lengthAsUint32);
|
|
|
|
return lengthAsUint32;
|
|
|
|
}
|
|
|
|
// Checks whether there is enough space left in the buffer pointed to by
|
|
// 'currentPos' to write at least 'space' elements of type T (to positions
|
|
// currentPos[0] through currentPos[space - 1]. Throws a dng_exception if there
|
|
// is not enough space left in the buffer.
|
|
// 'bufferEnd' should point one element beyond the end of the buffer. For
|
|
// example, if the buffer is "T buffer[3];", then bufferEnd should point to
|
|
// T + 3.
|
|
template <class T>
|
|
static void CheckSpaceLeftInBuffer(const T *currentPos,
|
|
const T *bufferEnd,
|
|
size_t space)
|
|
{
|
|
|
|
if (bufferEnd < currentPos || static_cast<size_t>(bufferEnd - currentPos) < space)
|
|
{
|
|
ThrowMemoryFull ("Buffer overrun");
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
// Throws an exception to notify the user of code that has not been security
|
|
// hardened and prevent execution of that code.
|
|
//
|
|
// Though the DNG SDK in general has been security-hardened, this does not apply
|
|
// to the following Mac-OS- and Windows-specific functions. Calls to
|
|
// ThrowNotHardened() have been added to these functions to alert callers of
|
|
// this fact.
|
|
//
|
|
// If you're trying to use a function that calls ThrowNotHardened(), you need to
|
|
// fix the security issues noted in the comment next to the ThrowNotHardened()
|
|
// call. Once you have fixed these issues, obtain a security review for the
|
|
// fixes. This may require fuzzing of the modified code on the target platform.
|
|
static void ThrowNotHardened()
|
|
{
|
|
ThrowProgramError ("This function has not been security-hardened");
|
|
}
|
|
|
|
#if qMacOS
|
|
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
|
|
static uint32 Extract_SystemEncoding (const dng_string &dngString,
|
|
dng_memory_data &buffer)
|
|
{
|
|
// TODO: Needs implementation.
|
|
ThrowProgramError ("Extract_SystemEncoding() not implemented on iOS");
|
|
return 0;
|
|
}
|
|
|
|
static void Assign_SystemEncoding (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
// TODO: Needs implementation.
|
|
ThrowProgramError ("Assign_SystemEncoding() not implemented on iOS");
|
|
|
|
}
|
|
|
|
static void Assign_JIS_X208_1990 (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
// TODO: Needs implementation.
|
|
ThrowProgramError ("Assign_JIS_X208_1990() not implemented on iOS");
|
|
}
|
|
|
|
#else
|
|
|
|
static void Assign_Multibyte (dng_string &dngString,
|
|
const char *otherString,
|
|
TextEncoding encoding)
|
|
{
|
|
|
|
// This function contains security-vulnerable code. Do not use.
|
|
// The particular vulnerabilities are:
|
|
// - Casting the result of strlen() to a uint32 may case truncation. (Use
|
|
// strlenAsUint32() instead.)
|
|
// - The computation of aBufSize and the subsequent addition of 1 in the
|
|
// call to the dng_memory_data constructor may wrap around.
|
|
ThrowNotHardened();
|
|
|
|
uint32 aSize = (uint32) strlen (otherString);
|
|
|
|
if (aSize > 0)
|
|
{
|
|
|
|
uint32 aBufSize = aSize * 6 + 256;
|
|
|
|
dng_memory_data aBuf (aBufSize + 1);
|
|
|
|
UnicodeMapping aMapping;
|
|
|
|
aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0,
|
|
kUnicodeNoSubset,
|
|
kUnicodeUTF8Format);
|
|
|
|
aMapping.otherEncoding = encoding;
|
|
aMapping.mappingVersion = kUnicodeUseLatestMapping;
|
|
|
|
TextToUnicodeInfo aInfo = NULL;
|
|
|
|
if (::CreateTextToUnicodeInfo (&aMapping, &aInfo) == noErr)
|
|
{
|
|
|
|
ByteCount aInput = 0;
|
|
ByteCount aOutput = 0;
|
|
|
|
::ConvertFromTextToUnicode (aInfo,
|
|
aSize,
|
|
otherString,
|
|
kUnicodeUseFallbacksMask |
|
|
kUnicodeLooseMappingsMask,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
aBufSize,
|
|
&aInput,
|
|
&aOutput,
|
|
(UniChar *) aBuf.Buffer ());
|
|
|
|
::DisposeTextToUnicodeInfo (&aInfo);
|
|
|
|
if (aOutput > 0 && aOutput <= aBufSize)
|
|
{
|
|
|
|
char *aBufChar = aBuf.Buffer_char ();
|
|
|
|
aBufChar [aOutput] = 0;
|
|
|
|
dngString.Set (aBufChar);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dngString.Clear ();
|
|
|
|
}
|
|
|
|
static uint32 Extract_Multibyte (const dng_string &dngString,
|
|
dng_memory_data &buffer,
|
|
TextEncoding encoding)
|
|
{
|
|
|
|
// This function contains security-vulnerable code. Do not use.
|
|
// The particular vulnerabilities are:
|
|
// - The computation of aBufSize may wrap around.
|
|
// - The computation of the argument to buffer.Allocate() may overflow; the
|
|
// conversion to uint32 is also problematic.
|
|
// - The signed-to-unsigned conversion in the return statement "
|
|
// return (uint32) aOutput;" may be problematic.
|
|
ThrowNotHardened();
|
|
|
|
uint32 aSize = dngString.Length ();
|
|
|
|
if (aSize > 0)
|
|
{
|
|
|
|
uint32 aBufSize = (aSize * 2) + 256;
|
|
|
|
dng_memory_data tempBuffer (aBufSize);
|
|
|
|
UnicodeMapping aMapping;
|
|
|
|
aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0,
|
|
kUnicodeNoSubset,
|
|
kUnicodeUTF8Format);
|
|
|
|
aMapping.otherEncoding = encoding;
|
|
aMapping.mappingVersion = kUnicodeUseLatestMapping;
|
|
|
|
UnicodeToTextInfo aInfo = NULL;
|
|
|
|
if (::CreateUnicodeToTextInfo (&aMapping, &aInfo) == noErr)
|
|
{
|
|
|
|
ByteCount aInput = 0;
|
|
ByteCount aOutput = 0;
|
|
|
|
::ConvertFromUnicodeToText (aInfo,
|
|
aSize,
|
|
(const UniChar *) dngString.Get (),
|
|
kUnicodeUseFallbacksMask |
|
|
kUnicodeLooseMappingsMask |
|
|
kUnicodeDefaultDirectionMask,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
aBufSize,
|
|
&aInput,
|
|
&aOutput,
|
|
tempBuffer.Buffer_char ());
|
|
|
|
::DisposeUnicodeToTextInfo (&aInfo);
|
|
|
|
if (aOutput > 0)
|
|
{
|
|
|
|
buffer.Allocate ((uint32) (aOutput + 1));
|
|
|
|
memcpy (buffer.Buffer (),
|
|
tempBuffer.Buffer (),
|
|
aOutput);
|
|
|
|
buffer.Buffer_char () [aOutput] = 0;
|
|
|
|
return (uint32) aOutput;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer.Allocate (1);
|
|
|
|
buffer.Buffer_char () [0] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void Assign_SystemEncoding (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
|
|
TextEncoding aEncoding;
|
|
|
|
::UpgradeScriptInfoToTextEncoding (smSystemScript,
|
|
kTextLanguageDontCare,
|
|
kTextRegionDontCare,
|
|
NULL,
|
|
&aEncoding);
|
|
|
|
Assign_Multibyte (dngString,
|
|
otherString,
|
|
aEncoding);
|
|
|
|
}
|
|
|
|
static uint32 Extract_SystemEncoding (const dng_string &dngString,
|
|
dng_memory_data &buffer)
|
|
{
|
|
|
|
TextEncoding aEncoding;
|
|
|
|
::UpgradeScriptInfoToTextEncoding (smSystemScript,
|
|
kTextLanguageDontCare,
|
|
kTextRegionDontCare,
|
|
NULL,
|
|
&aEncoding);
|
|
|
|
return Extract_Multibyte (dngString,
|
|
buffer,
|
|
aEncoding);
|
|
|
|
}
|
|
|
|
static void Assign_JIS_X208_1990 (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
|
|
Assign_Multibyte (dngString,
|
|
otherString,
|
|
kTextEncodingJIS_X0208_90);
|
|
|
|
}
|
|
|
|
#endif // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
#endif // qMacOS
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if qWinOS
|
|
|
|
static void Assign_Multibyte (dng_string &dngString,
|
|
const char *otherString,
|
|
UINT encoding)
|
|
{
|
|
|
|
// This function contains security-vulnerable code. Do not use.
|
|
// The particular vulnerabilities are:
|
|
// - Converting the return value of strlen() to int may cause overflow.
|
|
// - The computation of aBufChars and of the argument to the dng_memory_data
|
|
// constructor may overflow. Additionally, there is an implicit
|
|
// signed-to-unsigned conversion in the call to the dng_memory_data
|
|
// constructor.
|
|
ThrowNotHardened();
|
|
|
|
DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes");
|
|
|
|
int aSize = (int) strlen (otherString);
|
|
|
|
if (aSize > 0)
|
|
{
|
|
|
|
int aBufChars = aSize * 3 + 128;
|
|
|
|
dng_memory_data aBuf ((aBufChars + 1) << 1);
|
|
|
|
int aResult = ::MultiByteToWideChar (encoding,
|
|
0,
|
|
otherString,
|
|
aSize,
|
|
(WCHAR *) aBuf.Buffer (),
|
|
aBufChars);
|
|
|
|
if (aResult > 0 && aResult <= aBufChars)
|
|
{
|
|
|
|
uint16 * aUTF16 = aBuf.Buffer_uint16 ();
|
|
|
|
aUTF16 [aResult] = 0;
|
|
|
|
dngString.Set_UTF16 (aUTF16);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dngString.Clear ();
|
|
|
|
}
|
|
|
|
static uint32 Extract_Multibyte (const dng_string &dngString,
|
|
dng_memory_data &buffer,
|
|
UINT encoding)
|
|
{
|
|
|
|
// This function contains security-vulnerable code. Do not use.
|
|
// The particular vulnerabilities are:
|
|
// - Converting the return value of dngString.Get_UTF16() may cause
|
|
// overflow.
|
|
// - The computation of dBufSize may overflow.
|
|
// - The calls to the dng_memory_data constructor and to buffer.Allocate()
|
|
// trigger implicit conversions of int to uint32 that may be problematic.
|
|
// - The memcpy() call triggers an implicit conversion of aResult to a
|
|
// size_t, which may be problematic.
|
|
// - The conversion of aResult to a uint32 in the return statement may be
|
|
// problematic.
|
|
ThrowNotHardened();
|
|
|
|
DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes");
|
|
|
|
dng_memory_data sBuffer;
|
|
|
|
int aCount = dngString.Get_UTF16 (sBuffer);
|
|
|
|
int dBufSize = aCount * 2 + 256;
|
|
|
|
dng_memory_data dBuffer (dBufSize);
|
|
|
|
int aResult = ::WideCharToMultiByte (encoding,
|
|
0,
|
|
(WCHAR *) sBuffer.Buffer (),
|
|
aCount,
|
|
dBuffer.Buffer_char (),
|
|
dBufSize,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (aResult < 0)
|
|
aResult = 0;
|
|
|
|
buffer.Allocate (aResult + 1);
|
|
|
|
memcpy (buffer.Buffer (),
|
|
dBuffer.Buffer (),
|
|
aResult);
|
|
|
|
buffer.Buffer_char () [aResult] = 0;
|
|
|
|
return (uint32) aResult;
|
|
|
|
}
|
|
|
|
static void Assign_SystemEncoding (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
|
|
Assign_Multibyte (dngString,
|
|
otherString,
|
|
::GetACP ());
|
|
|
|
}
|
|
|
|
static uint32 Extract_SystemEncoding (const dng_string &dngString,
|
|
dng_memory_data &buffer)
|
|
{
|
|
|
|
return Extract_Multibyte (dngString,
|
|
buffer,
|
|
::GetACP ());
|
|
|
|
}
|
|
|
|
static void Assign_JIS_X208_1990 (dng_string &dngString,
|
|
const char *otherString)
|
|
{
|
|
|
|
// From MSDN documentation: 20932 = JIS X 0208-1990 & 0121-1990
|
|
|
|
const UINT kJIS = 20932;
|
|
|
|
Assign_Multibyte (dngString,
|
|
otherString,
|
|
kJIS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static bool IsASCII (const char *s)
|
|
{
|
|
|
|
if (!s)
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
|
|
uint8 c = (uint8) *(s++);
|
|
|
|
if (c == 0)
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (c & 0x80)
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_string::dng_string ()
|
|
|
|
: fData ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_string::dng_string (const dng_string &s)
|
|
|
|
: fData ()
|
|
|
|
{
|
|
|
|
Set (s.Get ());
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_string & dng_string::operator= (const dng_string &s)
|
|
{
|
|
|
|
if (this != &s)
|
|
{
|
|
|
|
Set (s.Get ());
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_string::~dng_string ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char * dng_string::Get () const
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
return fData.Buffer_char ();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::IsASCII () const
|
|
{
|
|
|
|
return ::IsASCII (Get ());
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set (const char *s)
|
|
{
|
|
|
|
// Measure the new length.
|
|
|
|
uint32 newLen = (s != NULL ? strlenAsUint32 (s) : 0);
|
|
|
|
// If it is a NULL string, then clear the buffer.
|
|
|
|
if (newLen == 0)
|
|
{
|
|
|
|
fData.Clear ();
|
|
|
|
}
|
|
|
|
// Else we need to copy the bytes.
|
|
|
|
else
|
|
{
|
|
|
|
uint32 oldLen = Length ();
|
|
|
|
// We might be setting this string to a sub-string of itself,
|
|
// so don't reallocate the data unless the string is getting
|
|
// longer.
|
|
|
|
if (newLen > oldLen)
|
|
{
|
|
|
|
fData.Clear ();
|
|
|
|
fData.Allocate (SafeUint32Add (newLen, 1));
|
|
|
|
}
|
|
|
|
char *d = fData.Buffer_char ();
|
|
|
|
for (uint32 k = 0; k <= newLen; k++)
|
|
{
|
|
|
|
d [k] = s [k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_ASCII (const char *s)
|
|
{
|
|
|
|
if (::IsASCII (s))
|
|
{
|
|
|
|
Set (s);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
Set_SystemEncoding (s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_UTF8 (const char *s)
|
|
{
|
|
|
|
uint32 len = strlenAsUint32 (s);
|
|
|
|
const char *sEnd = s + len;
|
|
|
|
// Worst case expansion is 1-byte characters expanding to
|
|
// replacement character, which requires 3 bytes.
|
|
|
|
const uint32 destBufferLength = SafeUint32Add (SafeUint32Mult (len, 3), 1);
|
|
dng_memory_data buffer (destBufferLength);
|
|
|
|
uint8 *d = buffer.Buffer_uint8 ();
|
|
uint8 * const destEnd = d + destBufferLength;
|
|
|
|
while (s < sEnd)
|
|
{
|
|
|
|
uint32 aChar = DecodeUTF8 (s, (uint32) (sEnd - s));
|
|
|
|
if (aChar > 0x7FFFFFFF)
|
|
{
|
|
aChar = kREPLACEMENT_CHARACTER;
|
|
}
|
|
|
|
#if qDNGValidate
|
|
|
|
if (aChar == kREPLACEMENT_CHARACTER)
|
|
{
|
|
ReportWarning ("Expected UTF-8 value is not valid UTF-8 (or contains a kREPLACEMENT_CHARACTER)");
|
|
}
|
|
|
|
#endif
|
|
|
|
if (aChar < 0x00000080)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*(d++) = (uint8) aChar;
|
|
}
|
|
|
|
else if (aChar < 0x00000800)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 2);
|
|
*(d++) = (uint8) ((aChar >> 6) | 0x000000C0);
|
|
*(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x00010000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 3);
|
|
*(d++) = (uint8) ( (aChar >> 12) | 0x000000E0);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x00200000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 4);
|
|
*(d++) = (uint8) ( (aChar >> 18) | 0x000000F0);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x04000000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 5);
|
|
*(d++) = (uint8) ( (aChar >> 24) | 0x000000F8);
|
|
*(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 6);
|
|
*(d++) = (uint8) ( (aChar >> 30) | 0x000000FC);
|
|
*(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
}
|
|
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*d = 0;
|
|
|
|
Set (buffer.Buffer_char ());
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_string::Get_SystemEncoding (dng_memory_data &buffer) const
|
|
{
|
|
|
|
if (IsASCII ())
|
|
{
|
|
|
|
uint32 len = Length ();
|
|
|
|
const uint32 destBufferLength = SafeUint32Add (len, 1);
|
|
buffer.Allocate (destBufferLength);
|
|
|
|
memcpy (buffer.Buffer (), Get (), destBufferLength);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
#if qMacOS || qWinOS
|
|
|
|
return Extract_SystemEncoding (*this, buffer);
|
|
|
|
#else
|
|
|
|
// Fallback logic to force the string to ASCII.
|
|
|
|
dng_string temp (*this);
|
|
|
|
temp.ForceASCII ();
|
|
|
|
return temp.Get_SystemEncoding (buffer);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_SystemEncoding (const char *s)
|
|
{
|
|
|
|
if (::IsASCII (s))
|
|
{
|
|
|
|
Set (s);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
#if qMacOS || qWinOS
|
|
|
|
Assign_SystemEncoding (*this, s);
|
|
|
|
#else
|
|
|
|
// Fallback logic that just grabs the ASCII characters and
|
|
// ignores the non-ASCII characters.
|
|
|
|
uint32 len = strlenAsUint32 (s);
|
|
|
|
const uint32 destBufferLength = SafeUint32Add (len, 1);
|
|
dng_memory_data buffer (destBufferLength);
|
|
|
|
uint8 *d = buffer.Buffer_uint8 ();
|
|
uint8 * const destEnd = d + destBufferLength;
|
|
|
|
while (*s)
|
|
{
|
|
|
|
uint8 c = (uint8) *(s++);
|
|
|
|
if ((c & 0x80) == 0)
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*(d++) = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*d = 0;
|
|
|
|
Set (buffer.Buffer_char ());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::ValidSystemEncoding () const
|
|
{
|
|
|
|
if (IsASCII ())
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
dng_memory_data buffer;
|
|
|
|
Get_SystemEncoding (buffer);
|
|
|
|
dng_string temp;
|
|
|
|
temp.Set_SystemEncoding (buffer.Buffer_char ());
|
|
|
|
return (*this == temp);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_JIS_X208_1990 (const char *s)
|
|
{
|
|
|
|
if (::IsASCII (s))
|
|
{
|
|
|
|
Set (s);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
#if qMacOS || qWinOS
|
|
|
|
Assign_JIS_X208_1990 (*this, s);
|
|
|
|
#else
|
|
|
|
// Fallback to the ASCII extraction logic.
|
|
|
|
Set_SystemEncoding (s);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_string::DecodeUTF8 (const char *&s,
|
|
uint32 maxBytes,
|
|
bool *isValid)
|
|
{
|
|
|
|
static const uint8 gUTF8Bytes [256] =
|
|
{
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
if (isValid)
|
|
{
|
|
*isValid = true;
|
|
}
|
|
|
|
const uint8 *nBuf = (const uint8 *) s;
|
|
|
|
uint32 aChar = nBuf [0];
|
|
|
|
uint32 aSize = gUTF8Bytes [aChar];
|
|
|
|
if (aSize > maxBytes)
|
|
{
|
|
|
|
s += maxBytes;
|
|
|
|
if (isValid)
|
|
{
|
|
*isValid = false;
|
|
}
|
|
|
|
return kREPLACEMENT_CHARACTER;
|
|
|
|
}
|
|
|
|
s += aSize;
|
|
|
|
for (uint32 extra = 1; extra < aSize; extra++)
|
|
{
|
|
|
|
if ((nBuf [extra] & 0xC0) != 0x80)
|
|
{
|
|
|
|
if (isValid)
|
|
{
|
|
*isValid = false;
|
|
}
|
|
|
|
return kREPLACEMENT_CHARACTER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (aSize)
|
|
{
|
|
|
|
case 0:
|
|
{
|
|
|
|
s++; // Don't get stuck in infinite loop
|
|
|
|
if (isValid)
|
|
{
|
|
*isValid = false;
|
|
}
|
|
|
|
return kREPLACEMENT_CHARACTER;
|
|
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
|
|
return aChar;
|
|
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
|
|
aChar = ((aChar << 6) + nBuf [1]) - (uint32) 0x00003080UL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
|
|
aChar = ((((aChar << 6) + nBuf [1])
|
|
<< 6) + nBuf [2]) - (uint32) 0x000E2080UL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
|
|
aChar = ((((((aChar << 6) + nBuf [1])
|
|
<< 6) + nBuf [2])
|
|
<< 6) + nBuf [3]) - (uint32) 0x03C82080UL;
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if (aChar < 0x7F || aChar > 0x0010FFFF)
|
|
{
|
|
|
|
if (isValid)
|
|
{
|
|
*isValid = false;
|
|
}
|
|
|
|
return kREPLACEMENT_CHARACTER;
|
|
|
|
}
|
|
|
|
return aChar;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::IsUTF8 (const char *s)
|
|
{
|
|
|
|
uint32 len = strlenAsUint32 (s);
|
|
|
|
const char *sEnd = s + len;
|
|
|
|
while (s < sEnd)
|
|
{
|
|
|
|
bool isValid = true;
|
|
|
|
(void) DecodeUTF8 (s, (uint32) (sEnd - s), &isValid);
|
|
|
|
if (!isValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_UTF8_or_System (const char *s)
|
|
{
|
|
|
|
if (::IsASCII (s))
|
|
{
|
|
|
|
Set (s);
|
|
|
|
}
|
|
|
|
else if (IsUTF8 (s))
|
|
{
|
|
|
|
Set_UTF8 (s);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
Set_SystemEncoding (s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_string::Get_UTF16 (dng_memory_data &buffer) const
|
|
{
|
|
|
|
uint32 count = 0;
|
|
|
|
const char *sPtr = Get ();
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
uint32 x = DecodeUTF8 (sPtr);
|
|
|
|
if (x <= 0x0000FFFF ||
|
|
x > 0x0010FFFF)
|
|
{
|
|
|
|
count = SafeUint32Add (count, 1);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
count = SafeUint32Add (count, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const uint32 destBufferLength = SafeUint32Add (count, 1);
|
|
buffer.Allocate (destBufferLength, sizeof (uint16));
|
|
|
|
uint16 *dPtr = buffer.Buffer_uint16 ();
|
|
uint16 * const destEnd = dPtr + destBufferLength;
|
|
|
|
sPtr = Get ();
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
uint32 x = DecodeUTF8 (sPtr);
|
|
|
|
if (x <= 0x0000FFFF)
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*(dPtr++) = (uint16) x;
|
|
|
|
}
|
|
|
|
else if (x > 0x0010FFFF)
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*(dPtr++) = (uint16) kREPLACEMENT_CHARACTER;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
x -= 0x00010000;
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 2);
|
|
*(dPtr++) = (uint16) ((x >> 10 ) + 0x0000D800);
|
|
*(dPtr++) = (uint16) ((x & 0x000003FF) + 0x0000DC00);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*dPtr = 0;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Set_UTF16 (const uint16 *s)
|
|
{
|
|
|
|
if (!s)
|
|
{
|
|
Clear ();
|
|
return;
|
|
}
|
|
|
|
bool swap = false;
|
|
|
|
if (s [0] == 0xFFFE) // Swapped byte order marker
|
|
{
|
|
swap = true;
|
|
s++;
|
|
}
|
|
|
|
else if (s [0] == 0xFEFF) // Non-swapped byte order marker
|
|
{
|
|
s++;
|
|
}
|
|
|
|
uint32 length16 = 0;
|
|
|
|
while (s [length16] != 0)
|
|
{
|
|
length16 = SafeUint32Add (length16, 1);
|
|
}
|
|
|
|
const uint16 *sEnd = s + length16;
|
|
|
|
const uint32 destBufferSize =
|
|
SafeUint32Add (SafeUint32Mult (length16, 6), 1);
|
|
dng_memory_data buffer (destBufferSize);
|
|
|
|
uint8 *d = buffer.Buffer_uint8 ();
|
|
uint8 * const destEnd = d + destBufferSize;
|
|
|
|
while (s < sEnd)
|
|
{
|
|
|
|
uint32 aChar = *s++;
|
|
|
|
if (swap)
|
|
{
|
|
aChar = ((aChar << 8) | (aChar >> 8)) & 0x0000FFFF;
|
|
}
|
|
|
|
if ((aChar >= 0x0000D800) && (aChar <= 0x0000DBFF) && (s < sEnd))
|
|
{
|
|
|
|
uint32 aLow = *s;
|
|
|
|
if (swap)
|
|
{
|
|
aLow = ((aLow << 8) | (aLow >> 8)) & 0x0000FFFF;
|
|
}
|
|
|
|
if ((aLow >= 0x0000DC00) && (aLow <= 0x0000DFFF))
|
|
{
|
|
|
|
aChar = ((aChar - 0x0000D800) << 10) +
|
|
(aLow - 0x0000DC00) +
|
|
0x00010000;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aChar > 0x7FFFFFFF)
|
|
{
|
|
aChar = kREPLACEMENT_CHARACTER;
|
|
}
|
|
|
|
if (aChar < 0x00000080)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*(d++) = (uint8) aChar;
|
|
}
|
|
|
|
else if (aChar < 0x00000800)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 2);
|
|
*(d++) = (uint8) ((aChar >> 6) | 0x000000C0);
|
|
*(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x00010000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 3);
|
|
*(d++) = (uint8) ( (aChar >> 12) | 0x000000E0);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x00200000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 4);
|
|
*(d++) = (uint8) ( (aChar >> 18) | 0x000000F0);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else if (aChar < 0x04000000)
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 5);
|
|
*(d++) = (uint8) ( (aChar >> 24) | 0x000000F8);
|
|
*(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
else
|
|
{
|
|
CheckSpaceLeftInBuffer (d, destEnd, 6);
|
|
*(d++) = (uint8) ( (aChar >> 30) | 0x000000FC);
|
|
*(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080);
|
|
*(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080);
|
|
}
|
|
|
|
}
|
|
|
|
CheckSpaceLeftInBuffer (d, destEnd, 1);
|
|
*d = 0;
|
|
|
|
Set (buffer.Buffer_char ());
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Clear ()
|
|
{
|
|
|
|
Set (NULL);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Truncate (uint32 maxBytes)
|
|
{
|
|
|
|
uint32 len = Length ();
|
|
|
|
if (len > maxBytes)
|
|
{
|
|
|
|
uint8 *s = fData.Buffer_uint8 ();
|
|
|
|
// Don't truncate on an extension character. Extensions characters
|
|
// in UTF-8 have the 0x80 bit set and the 0x40 bit clear.
|
|
|
|
while (maxBytes > 0 && ((s [maxBytes]) & 0xC0) == 0x80)
|
|
{
|
|
|
|
maxBytes--;
|
|
|
|
}
|
|
|
|
s [maxBytes] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::TrimTrailingBlanks ()
|
|
{
|
|
|
|
bool didTrim = false;
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
char *s = fData.Buffer_char ();
|
|
|
|
uint32 len = strlenAsUint32 (s);
|
|
|
|
while (len > 0 && s [len - 1] == ' ')
|
|
{
|
|
len--;
|
|
didTrim = true;
|
|
}
|
|
|
|
s [len] = 0;
|
|
|
|
}
|
|
|
|
return didTrim;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::TrimLeadingBlanks ()
|
|
{
|
|
|
|
bool didTrim = false;
|
|
|
|
const char *s = Get ();
|
|
|
|
while (*s == ' ')
|
|
{
|
|
s++;
|
|
didTrim = true;
|
|
}
|
|
|
|
if (didTrim)
|
|
{
|
|
Set (s);
|
|
}
|
|
|
|
return didTrim;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::IsEmpty () const
|
|
{
|
|
|
|
const char *s = Get ();
|
|
|
|
return *s == 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_string::Length () const
|
|
{
|
|
|
|
const char *s = Get ();
|
|
|
|
return strlenAsUint32 (s);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::operator== (const dng_string &s) const
|
|
{
|
|
|
|
const char *s1 = Get ();
|
|
const char *s2 = s.Get ();
|
|
|
|
return strcmp (s1, s2) == 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::Matches (const char *t,
|
|
const char *s,
|
|
bool case_sensitive)
|
|
{
|
|
|
|
while (*s != 0)
|
|
{
|
|
|
|
char c1 = *(s++);
|
|
char c2 = *(t++);
|
|
|
|
if (!case_sensitive)
|
|
{
|
|
c1 = ForceUppercase (c1);
|
|
c2 = ForceUppercase (c2);
|
|
}
|
|
|
|
if (c1 != c2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return (*t == 0);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::Matches (const char *s,
|
|
bool case_sensitive) const
|
|
{
|
|
|
|
return dng_string::Matches (Get (), s, case_sensitive);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::StartsWith (const char *s,
|
|
bool case_sensitive) const
|
|
{
|
|
|
|
const char *t = Get ();
|
|
|
|
while (*s != 0)
|
|
{
|
|
|
|
char c1 = *(s++);
|
|
char c2 = *(t++);
|
|
|
|
if (!case_sensitive)
|
|
{
|
|
c1 = ForceUppercase (c1);
|
|
c2 = ForceUppercase (c2);
|
|
}
|
|
|
|
if (c1 != c2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::EndsWith (const char *s,
|
|
bool case_sensitive) const
|
|
{
|
|
|
|
uint32 len1 = Length ();
|
|
|
|
uint32 len2 = strlenAsUint32 (s);
|
|
|
|
if (len1 < len2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const char *t = Get () + (len1 - len2);
|
|
|
|
while (*s != 0)
|
|
{
|
|
|
|
char c1 = *(s++);
|
|
char c2 = *(t++);
|
|
|
|
if (!case_sensitive)
|
|
{
|
|
c1 = ForceUppercase (c1);
|
|
c2 = ForceUppercase (c2);
|
|
}
|
|
|
|
if (c1 != c2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::Contains (const char *s,
|
|
bool case_sensitive,
|
|
int32 *match_offset) const
|
|
{
|
|
|
|
if (match_offset)
|
|
{
|
|
*match_offset = -1;
|
|
}
|
|
|
|
uint32 len1 = Length ();
|
|
|
|
uint32 len2 = strlenAsUint32 (s);
|
|
|
|
if (len1 < len2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 offsets = len1 - len2;
|
|
|
|
for (uint32 offset = 0; offset <= offsets; offset++)
|
|
{
|
|
|
|
const char *ss = s;
|
|
const char *tt = Get () + offset;
|
|
|
|
while (*ss != 0)
|
|
{
|
|
|
|
char c1 = *(ss++);
|
|
char c2 = *(tt++);
|
|
|
|
if (!case_sensitive)
|
|
{
|
|
c1 = ForceUppercase (c1);
|
|
c2 = ForceUppercase (c2);
|
|
}
|
|
|
|
if (c1 != c2)
|
|
{
|
|
goto tryNextOffset;
|
|
}
|
|
|
|
}
|
|
|
|
if (match_offset)
|
|
{
|
|
*match_offset = offset;
|
|
}
|
|
|
|
return true;
|
|
|
|
tryNextOffset: ;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::Replace (const char *old_string,
|
|
const char *new_string,
|
|
bool case_sensitive)
|
|
{
|
|
|
|
int32 match_offset = -1;
|
|
|
|
if (Contains (old_string,
|
|
case_sensitive,
|
|
&match_offset))
|
|
{
|
|
|
|
uint32 len1 = Length ();
|
|
|
|
uint32 len2 = strlenAsUint32 (old_string);
|
|
uint32 len3 = strlenAsUint32 (new_string);
|
|
|
|
if (len2 == len3)
|
|
{
|
|
|
|
strncpy (fData.Buffer_char () + match_offset,
|
|
new_string,
|
|
len3);
|
|
|
|
}
|
|
|
|
else if (len2 > len3)
|
|
{
|
|
|
|
strncpy (fData.Buffer_char () + match_offset,
|
|
new_string,
|
|
len3);
|
|
|
|
const char *s = fData.Buffer_char () + match_offset + len2;
|
|
char *d = fData.Buffer_char () + match_offset + len3;
|
|
|
|
uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination
|
|
|
|
for (uint32 j = 0; j < extra; j++)
|
|
{
|
|
*(d++) = *(s++);
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
// "len1 - len2" cannot wrap around because we know that if this
|
|
// string contains old_string, len1 >= len2 must hold.
|
|
dng_memory_data tempBuffer (
|
|
SafeUint32Add (SafeUint32Add (len1 - len2, len3), 1));
|
|
|
|
if (match_offset)
|
|
{
|
|
|
|
strncpy (tempBuffer.Buffer_char (),
|
|
fData .Buffer_char (),
|
|
match_offset);
|
|
|
|
}
|
|
|
|
if (len3)
|
|
{
|
|
|
|
strncpy (tempBuffer.Buffer_char () + match_offset,
|
|
new_string,
|
|
len3);
|
|
|
|
}
|
|
|
|
uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination
|
|
|
|
strncpy (tempBuffer.Buffer_char () + match_offset + len3,
|
|
fData .Buffer_char () + match_offset + len2,
|
|
extra);
|
|
|
|
Set (tempBuffer.Buffer_char ());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_string::TrimLeading (const char *s,
|
|
bool case_sensitive)
|
|
{
|
|
|
|
if (StartsWith (s, case_sensitive))
|
|
{
|
|
|
|
Set (Get () + strlenAsUint32 (s));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::Append (const char *s)
|
|
{
|
|
|
|
uint32 len2 = strlenAsUint32 (s);
|
|
|
|
if (len2)
|
|
{
|
|
|
|
uint32 len1 = Length ();
|
|
|
|
dng_memory_data temp (SafeUint32Add (SafeUint32Add (len1, len2), 1));
|
|
|
|
char *buffer = temp.Buffer_char ();
|
|
|
|
if (len1)
|
|
{
|
|
memcpy (buffer, Get (), len1);
|
|
}
|
|
|
|
memcpy (buffer + len1, s, len2 + 1);
|
|
|
|
Set (buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::SetUppercase ()
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
uint32 len = Length ();
|
|
|
|
char *dPtr = fData.Buffer_char ();
|
|
|
|
for (uint32 j = 0; j < len; j++)
|
|
{
|
|
|
|
char c = dPtr [j];
|
|
|
|
if (c >= 'a' && c <= 'z')
|
|
{
|
|
|
|
dPtr [j] = c - 'a' + 'A';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::SetLowercase ()
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
uint32 len = Length ();
|
|
|
|
char *dPtr = fData.Buffer_char ();
|
|
|
|
for (uint32 j = 0; j < len; j++)
|
|
{
|
|
|
|
char c = dPtr [j];
|
|
|
|
if (c >= 'A' && c <= 'Z')
|
|
{
|
|
|
|
dPtr [j] = c - 'A' + 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::SetLineEndings (char ending)
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
const char *sPtr = fData.Buffer_char ();
|
|
char *dPtr = fData.Buffer_char ();
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
char c = *(sPtr++);
|
|
|
|
char nc = sPtr [0];
|
|
|
|
if ((c == '\r' && nc == '\n') ||
|
|
(c == '\n' && nc == '\r'))
|
|
{
|
|
|
|
sPtr++;
|
|
|
|
if (ending)
|
|
{
|
|
*(dPtr++) = ending;
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == '\n' ||
|
|
c == '\r')
|
|
{
|
|
|
|
if (ending)
|
|
{
|
|
*(dPtr++) = ending;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
*(dPtr++) = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*dPtr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::StripLowASCII ()
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
const char *sPtr = fData.Buffer_char ();
|
|
char *dPtr = fData.Buffer_char ();
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
char c = *(sPtr++);
|
|
|
|
if (c == '\r' || c == '\n' || (uint8) c >= ' ')
|
|
{
|
|
|
|
*(dPtr++) = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*dPtr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_string::NormalizeAsCommaSeparatedNumbers ()
|
|
{
|
|
|
|
if (fData.Buffer ())
|
|
{
|
|
|
|
const char *sPtr = fData.Buffer_char ();
|
|
char *dPtr = fData.Buffer_char ();
|
|
|
|
bool commaInserted = false;
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
uint32 c = DecodeUTF8 (sPtr);
|
|
|
|
// Support number formats such as "3", "+3.0", "-3.1416", "314.16e-2",
|
|
// "0.31416E1", but no hex/octal number representations.
|
|
|
|
if (isdigit ((int) c) || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E')
|
|
{
|
|
|
|
*(dPtr++) = (char) c;
|
|
|
|
if (commaInserted)
|
|
{
|
|
|
|
commaInserted = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!commaInserted)
|
|
{
|
|
|
|
*(dPtr++) = ',';
|
|
|
|
commaInserted = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*dPtr = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
// Unicode to low-ASCII strings table.
|
|
|
|
struct UnicodeToLowASCIIEntry
|
|
{
|
|
uint32 unicode;
|
|
const char *ascii;
|
|
};
|
|
|
|
static const UnicodeToLowASCIIEntry kUnicodeToLowASCII [] =
|
|
{
|
|
{ 0x00A0, " " },
|
|
{ 0x00A1, "!" },
|
|
{ 0x00A9, "(C)" },
|
|
{ 0x00AA, "a" },
|
|
{ 0x00AB, "<<" },
|
|
{ 0x00AC, "!" },
|
|
{ 0x00AE, "(R)" },
|
|
{ 0x00B0, "dg" },
|
|
{ 0x00B1, "+-" },
|
|
{ 0x00B7, "." },
|
|
{ 0x00BA, "o" },
|
|
{ 0x00BB, ">>" },
|
|
{ 0x00BF, "?" },
|
|
{ 0x00C0, "A" },
|
|
{ 0x00C1, "A" },
|
|
{ 0x00C2, "A" },
|
|
{ 0x00C3, "A" },
|
|
{ 0x00C4, "A" },
|
|
{ 0x00C5, "A" },
|
|
{ 0x00C6, "AE" },
|
|
{ 0x00C7, "C" },
|
|
{ 0x00C8, "E" },
|
|
{ 0x00C9, "E" },
|
|
{ 0x00CA, "E" },
|
|
{ 0x00CB, "E" },
|
|
{ 0x00CC, "I" },
|
|
{ 0x00CD, "I" },
|
|
{ 0x00CE, "I" },
|
|
{ 0x00CF, "I" },
|
|
{ 0x00D1, "N" },
|
|
{ 0x00D2, "O" },
|
|
{ 0x00D3, "O" },
|
|
{ 0x00D4, "O" },
|
|
{ 0x00D5, "O" },
|
|
{ 0x00D6, "O" },
|
|
{ 0x00D8, "O" },
|
|
{ 0x00D9, "U" },
|
|
{ 0x00DA, "U" },
|
|
{ 0x00DB, "U" },
|
|
{ 0x00DC, "U" },
|
|
{ 0x00DD, "Y" },
|
|
{ 0x00E0, "a" },
|
|
{ 0x00E1, "a" },
|
|
{ 0x00E2, "a" },
|
|
{ 0x00E3, "a" },
|
|
{ 0x00E4, "a" },
|
|
{ 0x00E5, "a" },
|
|
{ 0x00E6, "ae" },
|
|
{ 0x00E7, "c" },
|
|
{ 0x00E8, "e" },
|
|
{ 0x00E9, "e" },
|
|
{ 0x00EA, "e" },
|
|
{ 0x00EB, "e" },
|
|
{ 0x00EC, "i" },
|
|
{ 0x00ED, "i" },
|
|
{ 0x00EE, "i" },
|
|
{ 0x00EF, "i" },
|
|
{ 0x00F1, "n" },
|
|
{ 0x00F2, "o" },
|
|
{ 0x00F3, "o" },
|
|
{ 0x00F4, "o" },
|
|
{ 0x00F5, "o" },
|
|
{ 0x00F6, "o" },
|
|
{ 0x00F7, "/" },
|
|
{ 0x00F8, "o" },
|
|
{ 0x00F9, "u" },
|
|
{ 0x00FA, "u" },
|
|
{ 0x00FB, "u" },
|
|
{ 0x00FC, "u" },
|
|
{ 0x00FD, "y" },
|
|
{ 0x00FF, "y" },
|
|
{ 0x0131, "i" },
|
|
{ 0x0152, "OE" },
|
|
{ 0x0153, "oe" },
|
|
{ 0x0178, "Y" },
|
|
{ 0x2013, "-" },
|
|
{ 0x2014, "-" },
|
|
{ 0x2018, "'" },
|
|
{ 0x2019, "'" },
|
|
{ 0x201A, "," },
|
|
{ 0x201C, "\"" },
|
|
{ 0x201D, "\"" },
|
|
{ 0x201E, ",," },
|
|
{ 0x2022, "." },
|
|
{ 0x2026, "..." },
|
|
{ 0x2039, "<" },
|
|
{ 0x203A, ">" },
|
|
{ 0x2044, "/" },
|
|
{ 0x2122, "TM" },
|
|
{ 0x2206, "d" },
|
|
{ 0x2211, "S" },
|
|
{ 0x2260, "!=" },
|
|
{ 0x2264, "<=" },
|
|
{ 0x2265, ">=" },
|
|
{ 0x2318, "#" },
|
|
{ 0xFB01, "fi" },
|
|
{ 0xFB02, "fl" }
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
void dng_string::ForceASCII ()
|
|
{
|
|
|
|
if (!IsASCII ())
|
|
{
|
|
|
|
uint32 tempBufferSize =
|
|
SafeUint32Add (SafeUint32Mult(Length(), 3), 1);
|
|
dng_memory_data tempBuffer (tempBufferSize);
|
|
|
|
char *dPtr = tempBuffer.Buffer_char ();
|
|
char * const destEnd = dPtr + tempBufferSize;
|
|
|
|
const char *sPtr = Get ();
|
|
|
|
while (*sPtr)
|
|
{
|
|
|
|
uint32 x = DecodeUTF8 (sPtr);
|
|
|
|
if (x <= 0x007F)
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*(dPtr++) = (char) x;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
const char *ascii = NULL;
|
|
|
|
const uint32 kTableEntrys = sizeof (kUnicodeToLowASCII ) /
|
|
sizeof (kUnicodeToLowASCII [0]);
|
|
|
|
for (uint32 entry = 0; entry < kTableEntrys; entry++)
|
|
{
|
|
|
|
if (kUnicodeToLowASCII [entry] . unicode == x)
|
|
{
|
|
|
|
ascii = kUnicodeToLowASCII [entry] . ascii;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ascii)
|
|
{
|
|
|
|
while (*ascii)
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*(dPtr++) = *(ascii++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*(dPtr++) ='?';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckSpaceLeftInBuffer (dPtr, destEnd, 1);
|
|
*dPtr = 0;
|
|
|
|
Set (tempBuffer.Buffer_char ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static dng_mutex gProtectUCCalls ("gProtectUCCalls");
|
|
|
|
/******************************************************************************/
|
|
|
|
int32 dng_string::Compare (const dng_string &s) const
|
|
{
|
|
|
|
#if qMacOS
|
|
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
|
|
// TODO: Needs implementation.
|
|
ThrowProgramError ("Compare() not implemented on iOS");
|
|
return 0;
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
dng_memory_data aStrA;
|
|
dng_memory_data aStrB;
|
|
|
|
uint32 aLenA = this->Get_UTF16 (aStrA);
|
|
uint32 aLenB = s .Get_UTF16 (aStrB);
|
|
|
|
if (aLenA > 0)
|
|
{
|
|
|
|
if (aLenB > 0)
|
|
{
|
|
|
|
// For some Mac OS versions anyway, UCCompareTextDefault is not
|
|
// thread safe.
|
|
|
|
dng_lock_mutex lockMutex (&gProtectUCCalls);
|
|
|
|
UCCollateOptions aOptions = kUCCollateStandardOptions |
|
|
kUCCollatePunctuationSignificantMask;
|
|
|
|
SInt32 aOrder = -1;
|
|
|
|
Boolean aEqual = false;
|
|
|
|
OSStatus searchStatus = ::UCCompareTextDefault (aOptions,
|
|
aStrA.Buffer_uint16 (),
|
|
aLenA,
|
|
aStrB.Buffer_uint16 (),
|
|
aLenB,
|
|
&aEqual,
|
|
&aOrder);
|
|
|
|
if (searchStatus == noErr)
|
|
{
|
|
|
|
if (aEqual || (aOrder == 0))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
else
|
|
{
|
|
return (aOrder > 0) ? 1 : -1;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
DNG_REPORT ("UCCompareTextDefault failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
if (aLenB > 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
|
|
|
#elif qWinOS
|
|
|
|
{
|
|
|
|
dng_memory_data aStrA;
|
|
dng_memory_data aStrB;
|
|
|
|
uint32 aLenA = this->Get_UTF16 (aStrA);
|
|
uint32 aLenB = s .Get_UTF16 (aStrB);
|
|
|
|
if (aLenA > 0)
|
|
{
|
|
|
|
if (aLenB > 0)
|
|
{
|
|
|
|
LCID locale = LOCALE_SYSTEM_DEFAULT;
|
|
|
|
DWORD aFlags = NORM_IGNOREWIDTH;
|
|
|
|
int aOrder = ::CompareStringW (locale,
|
|
aFlags,
|
|
(const WCHAR *) aStrA.Buffer_uint16 (),
|
|
aLenA,
|
|
(const WCHAR *) aStrB.Buffer_uint16 (),
|
|
aLenB);
|
|
|
|
if (aOrder == CSTR_EQUAL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
else if (aOrder == CSTR_GREATER_THAN)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
if (aLenB > 0)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Fallback to a pure Unicode sort order.
|
|
|
|
{
|
|
|
|
for (uint32 pass = 0; pass < 2; pass++)
|
|
{
|
|
|
|
const char *aPtr = Get ();
|
|
const char *bPtr = s.Get ();
|
|
|
|
while (*aPtr || *bPtr)
|
|
{
|
|
|
|
if (!bPtr)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
else if (!aPtr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
uint32 a = DecodeUTF8 (aPtr);
|
|
uint32 b = DecodeUTF8 (bPtr);
|
|
|
|
// Ignore case on first compare pass.
|
|
|
|
if (pass == 0)
|
|
{
|
|
|
|
if (a >= (uint32) 'a' && a <= (uint32) 'z')
|
|
{
|
|
a = a - (uint32) 'a' + (uint32) 'A';
|
|
}
|
|
|
|
if (b >= (uint32) 'a' && b <= (uint32) 'z')
|
|
{
|
|
b = b - (uint32) 'a' + (uint32) 'A';
|
|
}
|
|
|
|
}
|
|
|
|
if (b > a)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
else if (a < b)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|