mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-11 05:01:25 +00:00
4d9970b148
Rename ATTRIBUTE_PRINTF to - ID_STATIC_ATTRIBUTE_PRINTF - ID_INSTANCE_ATTRIBUTE_PRINTF since for instance functions, this has to be taken into account, too. Add format analysis to idLib, DeclManager and idTokenParser functions. Add support for clang.
1428 lines
33 KiB
C++
1428 lines
33 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
Copyright (C) 2012 Robert Beckebans
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#ifndef __STR_H__
|
|
#define __STR_H__
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
Character string
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
#define ASSERT_ENUM_STRING( string, index ) ( 1 / (int)!( string - index ) ) ? #string : ""
|
|
|
|
enum utf8Encoding_t
|
|
{
|
|
UTF8_PURE_ASCII, // no characters with values > 127
|
|
UTF8_ENCODED_BOM, // characters > 128 encoded with UTF8, but no byte-order-marker at the beginning
|
|
UTF8_ENCODED_NO_BOM, // characters > 128 encoded with UTF8, with a byte-order-marker at the beginning
|
|
UTF8_INVALID, // has values > 127 but isn't valid UTF8
|
|
UTF8_INVALID_BOM // has a byte-order-marker at the beginning, but isn't valuid UTF8 -- it's messed up
|
|
};
|
|
|
|
// these library functions should not be used for cross platform compatibility
|
|
#define strcmp idStr::Cmp // use_idStr_Cmp
|
|
#define strncmp use_idStr_Cmpn
|
|
|
|
#if defined( StrCmpN )
|
|
#undef StrCmpN
|
|
#endif
|
|
#define StrCmpN use_idStr_Cmpn
|
|
|
|
#if defined( strcmpi )
|
|
#undef strcmpi
|
|
#endif
|
|
#define strcmpi use_idStr_Icmp
|
|
|
|
#if defined( StrCmpI )
|
|
#undef StrCmpI
|
|
#endif
|
|
#define StrCmpI use_idStr_Icmp
|
|
|
|
#if defined( StrCmpNI )
|
|
#undef StrCmpNI
|
|
#endif
|
|
#define StrCmpNI use_idStr_Icmpn
|
|
|
|
#define stricmp idStr::Icmp // use_idStr_Icmp
|
|
|
|
#undef strcasecmp // DG: redefining this without undefining it causes tons of compiler warnings
|
|
|
|
#define _stricmp use_idStr_Icmp
|
|
#define strcasecmp use_idStr_Icmp
|
|
#define strnicmp use_idStr_Icmpn
|
|
#define _strnicmp use_idStr_Icmpn
|
|
#define _memicmp use_idStr_Icmpn
|
|
|
|
#define snprintf use_idStr_snPrintf
|
|
#define _snprintf use_idStr_snPrintf
|
|
#define vsnprintf use_idStr_vsnPrintf
|
|
#define _vsnprintf use_idStr_vsnPrintf
|
|
|
|
class idVec4;
|
|
|
|
#ifndef FILE_HASH_SIZE
|
|
#define FILE_HASH_SIZE 1024
|
|
#endif
|
|
|
|
// color escape character
|
|
const int C_COLOR_ESCAPE = '^';
|
|
const int C_COLOR_DEFAULT = '0';
|
|
const int C_COLOR_RED = '1';
|
|
const int C_COLOR_GREEN = '2';
|
|
const int C_COLOR_YELLOW = '3';
|
|
const int C_COLOR_BLUE = '4';
|
|
const int C_COLOR_CYAN = '5';
|
|
const int C_COLOR_ORANGE = '6';
|
|
const int C_COLOR_WHITE = '7';
|
|
const int C_COLOR_GRAY = '8';
|
|
const int C_COLOR_BLACK = '9';
|
|
|
|
// color escape string
|
|
#define S_COLOR_DEFAULT "^0"
|
|
#define S_COLOR_RED "^1"
|
|
#define S_COLOR_GREEN "^2"
|
|
#define S_COLOR_YELLOW "^3"
|
|
#define S_COLOR_BLUE "^4"
|
|
#define S_COLOR_CYAN "^5"
|
|
#define S_COLOR_ORANGE "^6"
|
|
#define S_COLOR_WHITE "^7"
|
|
#define S_COLOR_GRAY "^8"
|
|
#define S_COLOR_BLACK "^9"
|
|
|
|
// make idStr a multiple of 16 bytes long
|
|
// don't make too large to keep memory requirements to a minimum
|
|
const int STR_ALLOC_BASE = 20;
|
|
const int STR_ALLOC_GRAN = 32;
|
|
|
|
typedef enum
|
|
{
|
|
MEASURE_SIZE = 0,
|
|
MEASURE_BANDWIDTH
|
|
} Measure_t;
|
|
|
|
class idStr
|
|
{
|
|
|
|
public:
|
|
idStr();
|
|
idStr( const idStr& text );
|
|
idStr( const idStr& text, int start, int end );
|
|
idStr( const char* text );
|
|
idStr( const char* text, int start, int end );
|
|
explicit idStr( const bool b );
|
|
explicit idStr( const char c );
|
|
explicit idStr( const int i );
|
|
explicit idStr( const unsigned u );
|
|
explicit idStr( const float f );
|
|
~idStr();
|
|
|
|
size_t Size() const;
|
|
const char* c_str() const;
|
|
operator const char* () const;
|
|
operator const char* ();
|
|
|
|
char operator[]( int index ) const;
|
|
char& operator[]( int index );
|
|
|
|
void operator=( const idStr& text );
|
|
void operator=( const char* text );
|
|
|
|
friend idStr operator+( const idStr& a, const idStr& b );
|
|
friend idStr operator+( const idStr& a, const char* b );
|
|
friend idStr operator+( const char* a, const idStr& b );
|
|
|
|
friend idStr operator+( const idStr& a, const float b );
|
|
friend idStr operator+( const idStr& a, const int b );
|
|
friend idStr operator+( const idStr& a, const unsigned b );
|
|
friend idStr operator+( const idStr& a, const bool b );
|
|
friend idStr operator+( const idStr& a, const char b );
|
|
|
|
idStr& operator+=( const idStr& a );
|
|
idStr& operator+=( const char* a );
|
|
idStr& operator+=( const float a );
|
|
idStr& operator+=( const char a );
|
|
idStr& operator+=( const int a );
|
|
idStr& operator+=( const unsigned a );
|
|
idStr& operator+=( const bool a );
|
|
|
|
// case sensitive compare
|
|
friend bool operator==( const idStr& a, const idStr& b );
|
|
friend bool operator==( const idStr& a, const char* b );
|
|
friend bool operator==( const char* a, const idStr& b );
|
|
|
|
// case sensitive compare
|
|
friend bool operator!=( const idStr& a, const idStr& b );
|
|
friend bool operator!=( const idStr& a, const char* b );
|
|
friend bool operator!=( const char* a, const idStr& b );
|
|
|
|
// case sensitive compare
|
|
int Cmp( const char* text ) const;
|
|
int Cmpn( const char* text, int n ) const;
|
|
int CmpPrefix( const char* text ) const;
|
|
|
|
// case insensitive compare
|
|
int Icmp( const char* text ) const;
|
|
int Icmpn( const char* text, int n ) const;
|
|
int IcmpPrefix( const char* text ) const;
|
|
|
|
// case insensitive compare ignoring color
|
|
int IcmpNoColor( const char* text ) const;
|
|
|
|
// compares paths and makes sure folders come first
|
|
int IcmpPath( const char* text ) const;
|
|
int IcmpnPath( const char* text, int n ) const;
|
|
int IcmpPrefixPath( const char* text ) const;
|
|
|
|
int Length() const;
|
|
int Allocated() const;
|
|
void Empty();
|
|
bool IsEmpty() const;
|
|
void Clear();
|
|
void Append( const char a );
|
|
void Append( const idStr& text );
|
|
void Append( const char* text );
|
|
void Append( const char* text, int len );
|
|
void Insert( const char a, int index );
|
|
void Insert( const char* text, int index );
|
|
void ToLower();
|
|
void ToUpper();
|
|
bool IsNumeric() const;
|
|
bool IsColor() const;
|
|
bool HasLower() const;
|
|
bool HasUpper() const;
|
|
int LengthWithoutColors() const;
|
|
idStr& RemoveColors();
|
|
void CapLength( int );
|
|
void Fill( const char ch, int newlen );
|
|
|
|
ID_INLINE int UTF8Length();
|
|
ID_INLINE uint32 UTF8Char( int& idx );
|
|
static int UTF8Length( const byte* s );
|
|
static ID_INLINE uint32 UTF8Char( const char* s, int& idx );
|
|
static uint32 UTF8Char( const byte* s, int& idx );
|
|
void AppendUTF8Char( uint32 c );
|
|
ID_INLINE void ConvertToUTF8();
|
|
static bool IsValidUTF8( const uint8* s, const int maxLen, utf8Encoding_t& encoding );
|
|
static ID_INLINE bool IsValidUTF8( const char* s, const int maxLen, utf8Encoding_t& encoding )
|
|
{
|
|
return IsValidUTF8( ( const uint8* )s, maxLen, encoding );
|
|
}
|
|
static ID_INLINE bool IsValidUTF8( const uint8* s, const int maxLen );
|
|
static ID_INLINE bool IsValidUTF8( const char* s, const int maxLen )
|
|
{
|
|
return IsValidUTF8( ( const uint8* )s, maxLen );
|
|
}
|
|
|
|
int Find( const char c, int start = 0, int end = -1 ) const;
|
|
int Find( const char* text, bool casesensitive = true, int start = 0, int end = -1 ) const;
|
|
bool Filter( const char* filter, bool casesensitive ) const;
|
|
int Last( const char c ) const; // return the index to the last occurance of 'c', returns -1 if not found
|
|
const char* Left( int len, idStr& result ) const; // store the leftmost 'len' characters in the result
|
|
const char* Right( int len, idStr& result ) const; // store the rightmost 'len' characters in the result
|
|
const char* Mid( int start, int len, idStr& result ) const; // store 'len' characters starting at 'start' in result
|
|
idStr Left( int len ) const; // return the leftmost 'len' characters
|
|
idStr Right( int len ) const; // return the rightmost 'len' characters
|
|
idStr Mid( int start, int len ) const; // return 'len' characters starting at 'start'
|
|
void Format( VERIFY_FORMAT_STRING const char* fmt, ... ); // perform a threadsafe sprintf to the string
|
|
static idStr FormatInt( const int num, bool isCash = false ); // formats an integer as a value with commas
|
|
static idStr FormatCash( const int num )
|
|
{
|
|
return FormatInt( num, true );
|
|
}
|
|
void StripLeading( const char c ); // strip char from front as many times as the char occurs
|
|
void StripLeading( const char* string ); // strip string from front as many times as the string occurs
|
|
bool StripLeadingOnce( const char* string ); // strip string from front just once if it occurs
|
|
void StripTrailing( const char c ); // strip char from end as many times as the char occurs
|
|
void StripTrailing( const char* string ); // strip string from end as many times as the string occurs
|
|
bool StripTrailingOnce( const char* string ); // strip string from end just once if it occurs
|
|
void Strip( const char c ); // strip char from front and end as many times as the char occurs
|
|
void Strip( const char* string ); // strip string from front and end as many times as the string occurs
|
|
void StripTrailingWhitespace(); // strip trailing white space characters
|
|
idStr& StripQuotes(); // strip quotes around string
|
|
bool Replace( const char* old, const char* nw );
|
|
bool ReplaceChar( const char old, const char nw );
|
|
ID_INLINE void CopyRange( const char* text, int start, int end );
|
|
|
|
// file name methods
|
|
int FileNameHash() const; // hash key for the filename (skips extension)
|
|
idStr& BackSlashesToSlashes(); // convert slashes
|
|
idStr& SlashesToBackSlashes(); // convert slashes
|
|
idStr& SetFileExtension( const char* extension ); // set the given file extension
|
|
idStr& StripFileExtension(); // remove any file extension
|
|
idStr& StripAbsoluteFileExtension(); // remove any file extension looking from front (useful if there are multiple .'s)
|
|
idStr& DefaultFileExtension( const char* extension ); // if there's no file extension use the default
|
|
idStr& DefaultPath( const char* basepath ); // if there's no path use the default
|
|
void AppendPath( const char* text ); // append a partial path
|
|
idStr& StripFilename(); // remove the filename from a path
|
|
idStr& StripPath(); // remove the path from the filename
|
|
void ExtractFilePath( idStr& dest ) const; // copy the file path to another string
|
|
void ExtractFileName( idStr& dest ) const; // copy the filename to another string
|
|
void ExtractFileBase( idStr& dest ) const; // copy the filename minus the extension to another string
|
|
void ExtractFileExtension( idStr& dest ) const; // copy the file extension to another string
|
|
bool CheckExtension( const char* ext );
|
|
|
|
// char * methods to replace library functions
|
|
static int Length( const char* s );
|
|
static char* ToLower( char* s );
|
|
static char* ToUpper( char* s );
|
|
static bool IsNumeric( const char* s );
|
|
static bool IsColor( const char* s );
|
|
static bool HasLower( const char* s );
|
|
static bool HasUpper( const char* s );
|
|
static int LengthWithoutColors( const char* s );
|
|
static char* RemoveColors( char* s );
|
|
static int Cmp( const char* s1, const char* s2 );
|
|
static int Cmpn( const char* s1, const char* s2, int n );
|
|
static int Icmp( const char* s1, const char* s2 );
|
|
static int Icmpn( const char* s1, const char* s2, int n );
|
|
static int IcmpNoColor( const char* s1, const char* s2 );
|
|
static int IcmpPath( const char* s1, const char* s2 ); // compares paths and makes sure folders come first
|
|
static int IcmpnPath( const char* s1, const char* s2, int n ); // compares paths and makes sure folders come first
|
|
static void Append( char* dest, int size, const char* src );
|
|
static void Copynz( char* dest, const char* src, int destsize );
|
|
static int snPrintf( char* dest, int size, VERIFY_FORMAT_STRING const char* fmt, ... );
|
|
static int vsnPrintf( char* dest, int size, const char* fmt, va_list argptr );
|
|
static int FindChar( const char* str, const char c, int start = 0, int end = -1 );
|
|
static int FindText( const char* str, const char* text, bool casesensitive = true, int start = 0, int end = -1 );
|
|
static bool Filter( const char* filter, const char* name, bool casesensitive );
|
|
static void StripMediaName( const char* name, idStr& mediaName );
|
|
static bool CheckExtension( const char* name, const char* ext );
|
|
static const char* FloatArrayToString( const float* array, const int length, const int precision );
|
|
static const char* CStyleQuote( const char* str );
|
|
static const char* CStyleUnQuote( const char* str );
|
|
|
|
// hash keys
|
|
static int Hash( const char* string );
|
|
static int Hash( const char* string, int length );
|
|
static int IHash( const char* string ); // case insensitive
|
|
static int IHash( const char* string, int length ); // case insensitive
|
|
|
|
// character methods
|
|
static char ToLower( char c );
|
|
static char ToUpper( char c );
|
|
static bool CharIsPrintable( int c );
|
|
static bool CharIsLower( int c );
|
|
static bool CharIsUpper( int c );
|
|
static bool CharIsAlpha( int c );
|
|
static bool CharIsNumeric( int c );
|
|
static bool CharIsNewLine( char c );
|
|
static bool CharIsTab( char c );
|
|
static int ColorIndex( int c );
|
|
static idVec4& ColorForIndex( int i );
|
|
|
|
friend int sprintf( idStr& dest, const char* fmt, ... );
|
|
friend int vsprintf( idStr& dest, const char* fmt, va_list ap );
|
|
|
|
void ReAllocate( int amount, bool keepold ); // reallocate string data buffer
|
|
void FreeData(); // free allocated string memory
|
|
|
|
// format value in the given measurement with the best unit, returns the best unit
|
|
int BestUnit( const char* format, float value, Measure_t measure );
|
|
// format value in the requested unit and measurement
|
|
void SetUnit( const char* format, float value, int unit, Measure_t measure );
|
|
|
|
static void InitMemory();
|
|
static void ShutdownMemory();
|
|
static void PurgeMemory();
|
|
static void ShowMemoryUsage_f( const idCmdArgs& args );
|
|
|
|
int DynamicMemoryUsed() const;
|
|
static idStr FormatNumber( int number );
|
|
|
|
protected:
|
|
int len;
|
|
char* data;
|
|
int allocedAndFlag; // top bit is used to store a flag that indicates if the string data is static or not
|
|
char baseBuffer[ STR_ALLOC_BASE ];
|
|
|
|
void EnsureAlloced( int amount, bool keepold = true ); // ensure string data buffer is large anough
|
|
|
|
// sets the data point to the specified buffer... note that this ignores makes the passed buffer empty and ignores
|
|
// anything currently in the idStr's dynamic buffer. This method is intended to be called only from a derived class's constructor.
|
|
ID_INLINE void SetStaticBuffer( char* buffer, const int bufferLength );
|
|
|
|
private:
|
|
// initialize string using base buffer... call ONLY FROM CONSTRUCTOR
|
|
ID_INLINE void Construct();
|
|
|
|
static const uint32 STATIC_BIT = 31;
|
|
static const uint32 STATIC_MASK = 1u << STATIC_BIT;
|
|
static const uint32 ALLOCED_MASK = STATIC_MASK - 1;
|
|
|
|
|
|
ID_INLINE int GetAlloced() const
|
|
{
|
|
return allocedAndFlag & ALLOCED_MASK;
|
|
}
|
|
ID_INLINE void SetAlloced( const int a )
|
|
{
|
|
allocedAndFlag = ( allocedAndFlag & STATIC_MASK ) | ( a & ALLOCED_MASK );
|
|
}
|
|
|
|
ID_INLINE bool IsStatic() const
|
|
{
|
|
return ( allocedAndFlag & STATIC_MASK ) != 0;
|
|
}
|
|
ID_INLINE void SetStatic( const bool isStatic )
|
|
{
|
|
allocedAndFlag = ( allocedAndFlag & ALLOCED_MASK ) | ( isStatic << STATIC_BIT );
|
|
}
|
|
|
|
public:
|
|
static const int INVALID_POSITION = -1;
|
|
};
|
|
|
|
char* va( VERIFY_FORMAT_STRING const char* fmt, ... ) ID_STATIC_ATTRIBUTE_PRINTF( 1, 2 );
|
|
|
|
/*
|
|
================================================================================================
|
|
|
|
Sort routines for sorting idList<idStr>
|
|
|
|
================================================================================================
|
|
*/
|
|
|
|
class idSort_Str : public idSort_Quick< idStr, idSort_Str >
|
|
{
|
|
public:
|
|
int Compare( const idStr& a, const idStr& b ) const
|
|
{
|
|
return a.Icmp( b );
|
|
}
|
|
};
|
|
|
|
class idSort_PathStr : public idSort_Quick< idStr, idSort_PathStr >
|
|
{
|
|
public:
|
|
int Compare( const idStr& a, const idStr& b ) const
|
|
{
|
|
return a.IcmpPath( b );
|
|
}
|
|
};
|
|
|
|
/*
|
|
========================
|
|
idStr::Construct
|
|
========================
|
|
*/
|
|
ID_INLINE void idStr::Construct()
|
|
{
|
|
SetStatic( false );
|
|
SetAlloced( STR_ALLOC_BASE );
|
|
data = baseBuffer;
|
|
len = 0;
|
|
data[ 0 ] = '\0';
|
|
#ifdef ID_DEBUG_UNINITIALIZED_MEMORY
|
|
memset( baseBuffer, 0, sizeof( baseBuffer ) );
|
|
#endif
|
|
}
|
|
|
|
|
|
ID_INLINE void idStr::EnsureAlloced( int amount, bool keepold )
|
|
{
|
|
// static string's can't reallocate
|
|
if( IsStatic() )
|
|
{
|
|
release_assert( amount <= GetAlloced() );
|
|
return;
|
|
}
|
|
if( amount > GetAlloced() )
|
|
{
|
|
ReAllocate( amount, keepold );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::SetStaticBuffer
|
|
========================
|
|
*/
|
|
ID_INLINE void idStr::SetStaticBuffer( char* buffer, const int bufferLength )
|
|
{
|
|
// this should only be called on a freshly constructed idStr
|
|
assert( data == baseBuffer );
|
|
data = buffer;
|
|
len = 0;
|
|
SetAlloced( bufferLength );
|
|
SetStatic( true );
|
|
}
|
|
|
|
ID_INLINE idStr::idStr()
|
|
{
|
|
Construct();
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const idStr& text )
|
|
{
|
|
Construct();
|
|
int l;
|
|
|
|
l = text.Length();
|
|
EnsureAlloced( l + 1 );
|
|
strcpy( data, text.data );
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const idStr& text, int start, int end )
|
|
{
|
|
Construct();
|
|
int i;
|
|
int l;
|
|
|
|
if( end > text.Length() )
|
|
{
|
|
end = text.Length();
|
|
}
|
|
if( start > text.Length() )
|
|
{
|
|
start = text.Length();
|
|
}
|
|
else if( start < 0 )
|
|
{
|
|
start = 0;
|
|
}
|
|
|
|
l = end - start;
|
|
if( l < 0 )
|
|
{
|
|
l = 0;
|
|
}
|
|
|
|
EnsureAlloced( l + 1 );
|
|
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
data[ i ] = text[ start + i ];
|
|
}
|
|
|
|
data[ l ] = '\0';
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const char* text )
|
|
{
|
|
Construct();
|
|
int l;
|
|
|
|
if( text )
|
|
{
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
l = ( int )strlen( text );
|
|
// RB end
|
|
EnsureAlloced( l + 1 );
|
|
strcpy( data, text );
|
|
len = l;
|
|
}
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const char* text, int start, int end )
|
|
{
|
|
Construct();
|
|
int i;
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
int l = ( int )strlen( text );
|
|
// RB end
|
|
|
|
if( end > l )
|
|
{
|
|
end = l;
|
|
}
|
|
if( start > l )
|
|
{
|
|
start = l;
|
|
}
|
|
else if( start < 0 )
|
|
{
|
|
start = 0;
|
|
}
|
|
|
|
l = end - start;
|
|
if( l < 0 )
|
|
{
|
|
l = 0;
|
|
}
|
|
|
|
EnsureAlloced( l + 1 );
|
|
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
data[ i ] = text[ start + i ];
|
|
}
|
|
|
|
data[ l ] = '\0';
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const bool b )
|
|
{
|
|
Construct();
|
|
EnsureAlloced( 2 );
|
|
data[ 0 ] = b ? '1' : '0';
|
|
data[ 1 ] = '\0';
|
|
len = 1;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const char c )
|
|
{
|
|
Construct();
|
|
EnsureAlloced( 2 );
|
|
data[ 0 ] = c;
|
|
data[ 1 ] = '\0';
|
|
len = 1;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const int i )
|
|
{
|
|
Construct();
|
|
char text[ 64 ];
|
|
int l;
|
|
|
|
l = sprintf( text, "%d", i );
|
|
EnsureAlloced( l + 1 );
|
|
strcpy( data, text );
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const unsigned u )
|
|
{
|
|
Construct();
|
|
char text[ 64 ];
|
|
int l;
|
|
|
|
l = sprintf( text, "%u", u );
|
|
EnsureAlloced( l + 1 );
|
|
strcpy( data, text );
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::idStr( const float f )
|
|
{
|
|
Construct();
|
|
char text[ 64 ];
|
|
int l;
|
|
|
|
l = idStr::snPrintf( text, sizeof( text ), "%f", f );
|
|
while( l > 0 && text[l - 1] == '0' ) text[--l] = '\0';
|
|
while( l > 0 && text[l - 1] == '.' ) text[--l] = '\0';
|
|
EnsureAlloced( l + 1 );
|
|
strcpy( data, text );
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr::~idStr()
|
|
{
|
|
FreeData();
|
|
}
|
|
|
|
ID_INLINE size_t idStr::Size() const
|
|
{
|
|
return sizeof( *this ) + Allocated();
|
|
}
|
|
|
|
ID_INLINE const char* idStr::c_str() const
|
|
{
|
|
return data;
|
|
}
|
|
|
|
ID_INLINE idStr::operator const char* ()
|
|
{
|
|
return c_str();
|
|
}
|
|
|
|
ID_INLINE idStr::operator const char* () const
|
|
{
|
|
return c_str();
|
|
}
|
|
|
|
ID_INLINE char idStr::operator[]( int index ) const
|
|
{
|
|
assert( ( index >= 0 ) && ( index <= len ) );
|
|
return data[ index ];
|
|
}
|
|
|
|
ID_INLINE char& idStr::operator[]( int index )
|
|
{
|
|
assert( ( index >= 0 ) && ( index <= len ) );
|
|
return data[ index ];
|
|
}
|
|
|
|
ID_INLINE void idStr::operator=( const idStr& text )
|
|
{
|
|
int l;
|
|
|
|
l = text.Length();
|
|
EnsureAlloced( l + 1, false );
|
|
memcpy( data, text.data, l );
|
|
data[l] = '\0';
|
|
len = l;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const idStr& b )
|
|
{
|
|
idStr result( a );
|
|
result.Append( b );
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const char* b )
|
|
{
|
|
idStr result( a );
|
|
result.Append( b );
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const char* a, const idStr& b )
|
|
{
|
|
idStr result( a );
|
|
result.Append( b );
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const bool b )
|
|
{
|
|
idStr result( a );
|
|
result.Append( b ? "true" : "false" );
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const char b )
|
|
{
|
|
idStr result( a );
|
|
result.Append( b );
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const float b )
|
|
{
|
|
char text[ 64 ];
|
|
idStr result( a );
|
|
|
|
sprintf( text, "%f", b );
|
|
result.Append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const int b )
|
|
{
|
|
char text[ 64 ];
|
|
idStr result( a );
|
|
|
|
sprintf( text, "%d", b );
|
|
result.Append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr operator+( const idStr& a, const unsigned b )
|
|
{
|
|
char text[ 64 ];
|
|
idStr result( a );
|
|
|
|
sprintf( text, "%u", b );
|
|
result.Append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const float a )
|
|
{
|
|
char text[ 64 ];
|
|
|
|
sprintf( text, "%f", a );
|
|
Append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const int a )
|
|
{
|
|
char text[ 64 ];
|
|
|
|
sprintf( text, "%d", a );
|
|
Append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const unsigned a )
|
|
{
|
|
char text[ 64 ];
|
|
|
|
sprintf( text, "%u", a );
|
|
Append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const idStr& a )
|
|
{
|
|
Append( a );
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const char* a )
|
|
{
|
|
Append( a );
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const char a )
|
|
{
|
|
Append( a );
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::operator+=( const bool a )
|
|
{
|
|
Append( a ? "true" : "false" );
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE bool operator==( const idStr& a, const idStr& b )
|
|
{
|
|
return ( !idStr::Cmp( a.data, b.data ) );
|
|
}
|
|
|
|
ID_INLINE bool operator==( const idStr& a, const char* b )
|
|
{
|
|
assert( b );
|
|
return ( !idStr::Cmp( a.data, b ) );
|
|
}
|
|
|
|
ID_INLINE bool operator==( const char* a, const idStr& b )
|
|
{
|
|
assert( a );
|
|
return ( !idStr::Cmp( a, b.data ) );
|
|
}
|
|
|
|
ID_INLINE bool operator!=( const idStr& a, const idStr& b )
|
|
{
|
|
return !( a == b );
|
|
}
|
|
|
|
ID_INLINE bool operator!=( const idStr& a, const char* b )
|
|
{
|
|
return !( a == b );
|
|
}
|
|
|
|
ID_INLINE bool operator!=( const char* a, const idStr& b )
|
|
{
|
|
return !( a == b );
|
|
}
|
|
|
|
ID_INLINE int idStr::Cmp( const char* text ) const
|
|
{
|
|
assert( text );
|
|
return idStr::Cmp( data, text );
|
|
}
|
|
|
|
ID_INLINE int idStr::Cmpn( const char* text, int n ) const
|
|
{
|
|
assert( text );
|
|
return idStr::Cmpn( data, text, n );
|
|
}
|
|
|
|
ID_INLINE int idStr::CmpPrefix( const char* text ) const
|
|
{
|
|
assert( text );
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
return idStr::Cmpn( data, text, ( int )strlen( text ) );
|
|
// RB end
|
|
}
|
|
|
|
ID_INLINE int idStr::Icmp( const char* text ) const
|
|
{
|
|
assert( text );
|
|
return idStr::Icmp( data, text );
|
|
}
|
|
|
|
ID_INLINE int idStr::Icmpn( const char* text, int n ) const
|
|
{
|
|
assert( text );
|
|
return idStr::Icmpn( data, text, n );
|
|
}
|
|
|
|
ID_INLINE int idStr::IcmpPrefix( const char* text ) const
|
|
{
|
|
assert( text );
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
return idStr::Icmpn( data, text, ( int )strlen( text ) );
|
|
// RB end
|
|
}
|
|
|
|
ID_INLINE int idStr::IcmpNoColor( const char* text ) const
|
|
{
|
|
assert( text );
|
|
return idStr::IcmpNoColor( data, text );
|
|
}
|
|
|
|
ID_INLINE int idStr::IcmpPath( const char* text ) const
|
|
{
|
|
assert( text );
|
|
return idStr::IcmpPath( data, text );
|
|
}
|
|
|
|
ID_INLINE int idStr::IcmpnPath( const char* text, int n ) const
|
|
{
|
|
assert( text );
|
|
return idStr::IcmpnPath( data, text, n );
|
|
}
|
|
|
|
ID_INLINE int idStr::IcmpPrefixPath( const char* text ) const
|
|
{
|
|
assert( text );
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
return idStr::IcmpnPath( data, text, ( int )strlen( text ) );
|
|
// RB end
|
|
}
|
|
|
|
ID_INLINE int idStr::Length() const
|
|
{
|
|
return len;
|
|
}
|
|
|
|
ID_INLINE int idStr::Allocated() const
|
|
{
|
|
if( data != baseBuffer )
|
|
{
|
|
return GetAlloced();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ID_INLINE void idStr::Empty()
|
|
{
|
|
EnsureAlloced( 1 );
|
|
data[ 0 ] = '\0';
|
|
len = 0;
|
|
}
|
|
|
|
ID_INLINE bool idStr::IsEmpty() const
|
|
{
|
|
return ( idStr::Cmp( data, "" ) == 0 );
|
|
}
|
|
|
|
ID_INLINE void idStr::Clear()
|
|
{
|
|
if( IsStatic() )
|
|
{
|
|
len = 0;
|
|
data[ 0 ] = '\0';
|
|
return;
|
|
}
|
|
FreeData();
|
|
Construct();
|
|
}
|
|
|
|
ID_INLINE void idStr::Append( const char a )
|
|
{
|
|
EnsureAlloced( len + 2 );
|
|
data[ len ] = a;
|
|
len++;
|
|
data[ len ] = '\0';
|
|
}
|
|
|
|
ID_INLINE void idStr::Append( const idStr& text )
|
|
{
|
|
int newLen;
|
|
int i;
|
|
|
|
newLen = len + text.Length();
|
|
EnsureAlloced( newLen + 1 );
|
|
for( i = 0; i < text.len; i++ )
|
|
{
|
|
data[ len + i ] = text[ i ];
|
|
}
|
|
len = newLen;
|
|
data[ len ] = '\0';
|
|
}
|
|
|
|
ID_INLINE void idStr::Append( const char* text )
|
|
{
|
|
int newLen;
|
|
int i;
|
|
|
|
if( text )
|
|
{
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
newLen = len + ( int )strlen( text );
|
|
// RB end
|
|
EnsureAlloced( newLen + 1 );
|
|
for( i = 0; text[ i ]; i++ )
|
|
{
|
|
data[ len + i ] = text[ i ];
|
|
}
|
|
len = newLen;
|
|
data[ len ] = '\0';
|
|
}
|
|
}
|
|
|
|
ID_INLINE void idStr::Append( const char* text, int l )
|
|
{
|
|
int newLen;
|
|
int i;
|
|
|
|
if( text && l )
|
|
{
|
|
newLen = len + l;
|
|
EnsureAlloced( newLen + 1 );
|
|
for( i = 0; text[ i ] && i < l; i++ )
|
|
{
|
|
data[ len + i ] = text[ i ];
|
|
}
|
|
len = newLen;
|
|
data[ len ] = '\0';
|
|
}
|
|
}
|
|
|
|
ID_INLINE void idStr::Insert( const char a, int index )
|
|
{
|
|
int i, l;
|
|
|
|
if( index < 0 )
|
|
{
|
|
index = 0;
|
|
}
|
|
else if( index > len )
|
|
{
|
|
index = len;
|
|
}
|
|
|
|
l = 1;
|
|
EnsureAlloced( len + l + 1 );
|
|
for( i = len; i >= index; i-- )
|
|
{
|
|
data[i + l] = data[i];
|
|
}
|
|
data[index] = a;
|
|
len++;
|
|
}
|
|
|
|
ID_INLINE void idStr::Insert( const char* text, int index )
|
|
{
|
|
int i, l;
|
|
|
|
if( index < 0 )
|
|
{
|
|
index = 0;
|
|
}
|
|
else if( index > len )
|
|
{
|
|
index = len;
|
|
}
|
|
|
|
// RB: 64 bit fixes, conversion from 'size_t' to 'int', possible loss of data
|
|
l = ( int )strlen( text );
|
|
// RB end
|
|
EnsureAlloced( len + l + 1 );
|
|
for( i = len; i >= index; i-- )
|
|
{
|
|
data[i + l] = data[i];
|
|
}
|
|
for( i = 0; i < l; i++ )
|
|
{
|
|
data[index + i] = text[i];
|
|
}
|
|
len += l;
|
|
}
|
|
|
|
ID_INLINE void idStr::ToLower()
|
|
{
|
|
for( int i = 0; data[i]; i++ )
|
|
{
|
|
if( CharIsUpper( data[i] ) )
|
|
{
|
|
data[i] += ( 'a' - 'A' );
|
|
}
|
|
}
|
|
}
|
|
|
|
ID_INLINE void idStr::ToUpper()
|
|
{
|
|
for( int i = 0; data[i]; i++ )
|
|
{
|
|
if( CharIsLower( data[i] ) )
|
|
{
|
|
data[i] -= ( 'a' - 'A' );
|
|
}
|
|
}
|
|
}
|
|
|
|
ID_INLINE bool idStr::IsNumeric() const
|
|
{
|
|
return idStr::IsNumeric( data );
|
|
}
|
|
|
|
ID_INLINE bool idStr::IsColor() const
|
|
{
|
|
return idStr::IsColor( data );
|
|
}
|
|
|
|
ID_INLINE bool idStr::HasLower() const
|
|
{
|
|
return idStr::HasLower( data );
|
|
}
|
|
|
|
ID_INLINE bool idStr::HasUpper() const
|
|
{
|
|
return idStr::HasUpper( data );
|
|
}
|
|
|
|
ID_INLINE idStr& idStr::RemoveColors()
|
|
{
|
|
idStr::RemoveColors( data );
|
|
len = Length( data );
|
|
return *this;
|
|
}
|
|
|
|
ID_INLINE int idStr::LengthWithoutColors() const
|
|
{
|
|
return idStr::LengthWithoutColors( data );
|
|
}
|
|
|
|
ID_INLINE void idStr::CapLength( int newlen )
|
|
{
|
|
if( len <= newlen )
|
|
{
|
|
return;
|
|
}
|
|
data[ newlen ] = 0;
|
|
len = newlen;
|
|
}
|
|
|
|
ID_INLINE void idStr::Fill( const char ch, int newlen )
|
|
{
|
|
EnsureAlloced( newlen + 1 );
|
|
len = newlen;
|
|
memset( data, ch, len );
|
|
data[ len ] = 0;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::UTF8Length
|
|
========================
|
|
*/
|
|
ID_INLINE int idStr::UTF8Length()
|
|
{
|
|
return UTF8Length( ( byte* )data );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::UTF8Char
|
|
========================
|
|
*/
|
|
ID_INLINE uint32 idStr::UTF8Char( int& idx )
|
|
{
|
|
return UTF8Char( ( byte* )data, idx );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::ConvertToUTF8
|
|
========================
|
|
*/
|
|
ID_INLINE void idStr::ConvertToUTF8()
|
|
{
|
|
idStr temp( *this );
|
|
Clear();
|
|
for( int index = 0; index < temp.Length(); ++index )
|
|
{
|
|
AppendUTF8Char( temp[index] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::UTF8Char
|
|
========================
|
|
*/
|
|
ID_INLINE uint32 idStr::UTF8Char( const char* s, int& idx )
|
|
{
|
|
return UTF8Char( ( byte* )s, idx );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::IsValidUTF8
|
|
========================
|
|
*/
|
|
ID_INLINE bool idStr::IsValidUTF8( const uint8* s, const int maxLen )
|
|
{
|
|
utf8Encoding_t encoding;
|
|
return IsValidUTF8( s, maxLen, encoding );
|
|
}
|
|
|
|
ID_INLINE int idStr::Find( const char c, int start, int end ) const
|
|
{
|
|
if( end == -1 )
|
|
{
|
|
end = len;
|
|
}
|
|
return idStr::FindChar( data, c, start, end );
|
|
}
|
|
|
|
ID_INLINE int idStr::Find( const char* text, bool casesensitive, int start, int end ) const
|
|
{
|
|
if( end == -1 )
|
|
{
|
|
end = len;
|
|
}
|
|
return idStr::FindText( data, text, casesensitive, start, end );
|
|
}
|
|
|
|
ID_INLINE bool idStr::Filter( const char* filter, bool casesensitive ) const
|
|
{
|
|
return idStr::Filter( filter, data, casesensitive );
|
|
}
|
|
|
|
ID_INLINE const char* idStr::Left( int len, idStr& result ) const
|
|
{
|
|
return Mid( 0, len, result );
|
|
}
|
|
|
|
ID_INLINE const char* idStr::Right( int len, idStr& result ) const
|
|
{
|
|
if( len >= Length() )
|
|
{
|
|
result = *this;
|
|
return result;
|
|
}
|
|
return Mid( Length() - len, len, result );
|
|
}
|
|
|
|
ID_INLINE idStr idStr::Left( int len ) const
|
|
{
|
|
return Mid( 0, len );
|
|
}
|
|
|
|
ID_INLINE idStr idStr::Right( int len ) const
|
|
{
|
|
if( len >= Length() )
|
|
{
|
|
return *this;
|
|
}
|
|
return Mid( Length() - len, len );
|
|
}
|
|
|
|
ID_INLINE void idStr::Strip( const char c )
|
|
{
|
|
StripLeading( c );
|
|
StripTrailing( c );
|
|
}
|
|
|
|
ID_INLINE void idStr::Strip( const char* string )
|
|
{
|
|
StripLeading( string );
|
|
StripTrailing( string );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CheckExtension( const char* ext )
|
|
{
|
|
return idStr::CheckExtension( data, ext );
|
|
}
|
|
|
|
ID_INLINE int idStr::Length( const char* s )
|
|
{
|
|
int i;
|
|
for( i = 0; s[i]; i++ ) {}
|
|
return i;
|
|
}
|
|
|
|
ID_INLINE char* idStr::ToLower( char* s )
|
|
{
|
|
for( int i = 0; s[i]; i++ )
|
|
{
|
|
if( CharIsUpper( s[i] ) )
|
|
{
|
|
s[i] += ( 'a' - 'A' );
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
ID_INLINE char* idStr::ToUpper( char* s )
|
|
{
|
|
for( int i = 0; s[i]; i++ )
|
|
{
|
|
if( CharIsLower( s[i] ) )
|
|
{
|
|
s[i] -= ( 'a' - 'A' );
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
ID_INLINE int idStr::Hash( const char* string )
|
|
{
|
|
int i, hash = 0;
|
|
for( i = 0; *string != '\0'; i++ )
|
|
{
|
|
hash += ( *string++ ) * ( i + 119 );
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
ID_INLINE int idStr::Hash( const char* string, int length )
|
|
{
|
|
int i, hash = 0;
|
|
for( i = 0; i < length; i++ )
|
|
{
|
|
hash += ( *string++ ) * ( i + 119 );
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
ID_INLINE int idStr::IHash( const char* string )
|
|
{
|
|
int i, hash = 0;
|
|
for( i = 0; *string != '\0'; i++ )
|
|
{
|
|
hash += ToLower( *string++ ) * ( i + 119 );
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
ID_INLINE int idStr::IHash( const char* string, int length )
|
|
{
|
|
int i, hash = 0;
|
|
for( i = 0; i < length; i++ )
|
|
{
|
|
hash += ToLower( *string++ ) * ( i + 119 );
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
ID_INLINE bool idStr::IsColor( const char* s )
|
|
{
|
|
return ( s[0] == C_COLOR_ESCAPE && s[1] != '\0' && s[1] != ' ' );
|
|
}
|
|
|
|
ID_INLINE char idStr::ToLower( char c )
|
|
{
|
|
if( c <= 'Z' && c >= 'A' )
|
|
{
|
|
return ( c + ( 'a' - 'A' ) );
|
|
}
|
|
return c;
|
|
}
|
|
|
|
ID_INLINE char idStr::ToUpper( char c )
|
|
{
|
|
if( c >= 'a' && c <= 'z' )
|
|
{
|
|
return ( c - ( 'a' - 'A' ) );
|
|
}
|
|
return c;
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsPrintable( int c )
|
|
{
|
|
// test for regular ascii and western European high-ascii chars
|
|
return ( c >= 0x20 && c <= 0x7E ) || ( c >= 0xA1 && c <= 0xFF );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsLower( int c )
|
|
{
|
|
// test for regular ascii and western European high-ascii chars
|
|
return ( c >= 'a' && c <= 'z' ) || ( c >= 0xE0 && c <= 0xFF );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsUpper( int c )
|
|
{
|
|
// test for regular ascii and western European high-ascii chars
|
|
return ( c <= 'Z' && c >= 'A' ) || ( c >= 0xC0 && c <= 0xDF );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsAlpha( int c )
|
|
{
|
|
// test for regular ascii and western European high-ascii chars
|
|
return ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ||
|
|
( c >= 0xC0 && c <= 0xFF ) );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsNumeric( int c )
|
|
{
|
|
return ( c <= '9' && c >= '0' );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsNewLine( char c )
|
|
{
|
|
return ( c == '\n' || c == '\r' || c == '\v' );
|
|
}
|
|
|
|
ID_INLINE bool idStr::CharIsTab( char c )
|
|
{
|
|
return ( c == '\t' );
|
|
}
|
|
|
|
ID_INLINE int idStr::ColorIndex( int c )
|
|
{
|
|
return ( c & 15 );
|
|
}
|
|
|
|
ID_INLINE int idStr::DynamicMemoryUsed() const
|
|
{
|
|
return ( data == baseBuffer ) ? 0 : GetAlloced();
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idStr::CopyRange
|
|
========================
|
|
*/
|
|
ID_INLINE void idStr::CopyRange( const char* text, int start, int end )
|
|
{
|
|
int l = end - start;
|
|
if( l < 0 )
|
|
{
|
|
l = 0;
|
|
}
|
|
|
|
EnsureAlloced( l + 1 );
|
|
|
|
for( int i = 0; i < l; i++ )
|
|
{
|
|
data[ i ] = text[ start + i ];
|
|
}
|
|
|
|
data[ l ] = '\0';
|
|
len = l;
|
|
}
|
|
|
|
#endif /* !__STR_H__ */
|