2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2001-11-03 10:52:42 +00:00
|
|
|
#include "RageUtil.h"
|
2005-05-19 23:29:39 +00:00
|
|
|
#include "RageMath.h"
|
2003-07-03 02:41:27 +00:00
|
|
|
#include "RageLog.h"
|
2003-12-07 23:43:58 +00:00
|
|
|
#include "RageFile.h"
|
2005-09-04 23:04:21 +00:00
|
|
|
#include "Foreach.h"
|
2005-12-22 03:10:04 +00:00
|
|
|
#include "LocalizedString.h"
|
2005-12-31 06:52:04 +00:00
|
|
|
#include "LuaFunctions.h"
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2002-10-24 07:09:38 +00:00
|
|
|
#include <numeric>
|
2004-04-05 05:22:32 +00:00
|
|
|
#include <ctime>
|
2003-01-06 02:48:14 +00:00
|
|
|
#include <map>
|
2003-07-03 09:22:03 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
2003-02-15 00:32:32 +00:00
|
|
|
|
2004-10-07 04:10:45 +00:00
|
|
|
int randseed = time(NULL);
|
|
|
|
|
|
|
|
|
|
// From "Numerical Recipes in C".
|
|
|
|
|
float RandomFloat( int &seed )
|
|
|
|
|
{
|
|
|
|
|
const int MASK = 123459876;
|
|
|
|
|
seed ^= MASK;
|
|
|
|
|
|
|
|
|
|
const int IA = 16807;
|
|
|
|
|
const int IM = 2147483647;
|
|
|
|
|
|
|
|
|
|
const int IQ = 127773;
|
|
|
|
|
const int IR = 2836;
|
|
|
|
|
|
|
|
|
|
long k = seed / IQ;
|
|
|
|
|
seed = IA*(seed-k*IQ)-IR*k;
|
|
|
|
|
if( seed < 0 )
|
|
|
|
|
seed += IM;
|
|
|
|
|
|
|
|
|
|
const float AM = .999999f / IM;
|
|
|
|
|
float ans = AM * seed;
|
|
|
|
|
|
|
|
|
|
seed ^= MASK;
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
2003-01-03 05:26:15 +00:00
|
|
|
|
2003-07-30 20:31:50 +00:00
|
|
|
RandomGen::RandomGen( unsigned long seed_ )
|
|
|
|
|
{
|
|
|
|
|
seed = seed_;
|
|
|
|
|
if(seed == 0)
|
|
|
|
|
seed = time(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RandomGen::operator() ( int maximum )
|
|
|
|
|
{
|
2004-10-07 04:10:45 +00:00
|
|
|
return int(RandomFloat( seed ) * maximum);
|
2003-07-30 20:31:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-04-16 22:49:40 +00:00
|
|
|
void fapproach( float& val, float other_val, float to_move )
|
|
|
|
|
{
|
2004-06-16 00:38:31 +00:00
|
|
|
ASSERT_M( to_move >= 0, ssprintf("to_move: %f", to_move) );
|
2003-04-16 22:49:40 +00:00
|
|
|
if( val == other_val )
|
|
|
|
|
return;
|
|
|
|
|
float fDelta = other_val - val;
|
|
|
|
|
float fSign = fDelta / fabsf( fDelta );
|
|
|
|
|
float fToMove = fSign*to_move;
|
|
|
|
|
if( fabsf(fToMove) > fabsf(fDelta) )
|
|
|
|
|
fToMove = fDelta; // snap
|
|
|
|
|
val += fToMove;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-05 20:18:02 +00:00
|
|
|
/* Return a positive x mod y. */
|
|
|
|
|
float fmodfp(float x, float y)
|
|
|
|
|
{
|
|
|
|
|
x = fmodf(x, y); /* x is [-y,y] */
|
|
|
|
|
x += y; /* x is [0,y*2] */
|
|
|
|
|
x = fmodf(x, y); /* x is [0,y] */
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
int power_of_two( int iInput )
|
2002-12-20 06:28:22 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
int iValue = 1;
|
2002-12-20 06:28:22 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
while ( iValue < iInput )
|
|
|
|
|
iValue <<= 1;
|
2002-12-20 06:28:22 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
return iValue;
|
2002-12-20 06:28:22 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool IsAnInt( const RString &s )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
2003-05-22 21:13:02 +00:00
|
|
|
if( !s.size() )
|
2001-12-28 10:15:59 +00:00
|
|
|
return false;
|
|
|
|
|
|
2002-12-17 06:47:19 +00:00
|
|
|
for( int i=0; s[i]; i++ )
|
2001-12-28 10:15:59 +00:00
|
|
|
if( s[i] < '0' || s[i] > '9' )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool IsHexVal( const RString &s )
|
2003-01-03 22:51:01 +00:00
|
|
|
{
|
2003-05-22 21:13:29 +00:00
|
|
|
if( !s.size() )
|
2003-01-03 22:51:01 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for( int i=0; s[i]; i++ )
|
|
|
|
|
if( !(s[i] >= '0' && s[i] <= '9') &&
|
|
|
|
|
!(toupper(s[i]) >= 'A' && toupper(s[i]) <= 'F'))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
float HHMMSSToSeconds( const RString &sHHMMSS )
|
2002-04-28 20:42:32 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
vector<RString> arrayBits;
|
2004-02-22 19:51:46 +00:00
|
|
|
split( sHHMMSS, ":", arrayBits, false );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
2002-10-31 02:11:52 +00:00
|
|
|
while( arrayBits.size() < 3 )
|
2002-10-24 19:55:09 +00:00
|
|
|
arrayBits.insert(arrayBits.begin(), "0" ); // pad missing bits
|
2002-04-28 20:42:32 +00:00
|
|
|
|
|
|
|
|
float fSeconds = 0;
|
|
|
|
|
fSeconds += atoi( arrayBits[0] ) * 60 * 60;
|
|
|
|
|
fSeconds += atoi( arrayBits[1] ) * 60;
|
2004-08-10 20:57:59 +00:00
|
|
|
fSeconds += strtof( arrayBits[2], NULL );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
|
|
|
|
return fSeconds;
|
|
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString SecondsToHHMMSS( float fSecs )
|
2002-08-01 13:42:56 +00:00
|
|
|
{
|
2003-06-24 20:21:09 +00:00
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sReturn = ssprintf( "%02d:%02d:%02d", iMinsDisplay/60, iMinsDisplay%60, iSecsDisplay );
|
2004-02-22 19:51:46 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString SecondsToMMSSMsMs( float fSecs )
|
2004-02-22 19:51:46 +00:00
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2004-06-25 23:39:40 +00:00
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 100 );
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sReturn = ssprintf( "%02d:%02d.%02d", iMinsDisplay, iSecsDisplay, min(99,iLeftoverDisplay) );
|
2004-02-22 19:51:46 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString SecondsToMSSMsMs( float fSecs )
|
2005-05-08 22:03:41 +00:00
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
|
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 100 );
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sReturn = ssprintf( "%01d:%02d.%02d", iMinsDisplay, iSecsDisplay, min(99,iLeftoverDisplay) );
|
2005-05-08 22:03:41 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString SecondsToMMSSMsMsMs( float fSecs )
|
2004-04-06 23:21:27 +00:00
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2004-06-25 23:39:40 +00:00
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 1000 );
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sReturn = ssprintf( "%02d:%02d.%03d", iMinsDisplay, iSecsDisplay, min(999,iLeftoverDisplay) );
|
2004-04-06 23:21:27 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString PrettyPercent( float fNumerator, float fDenominator)
|
2004-02-22 19:51:46 +00:00
|
|
|
{
|
2004-02-22 21:22:27 +00:00
|
|
|
return ssprintf("%0.2f%%",fNumerator/fDenominator*100);
|
2004-02-22 19:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString Commify( int iNum )
|
2004-02-22 19:51:46 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sNum = ssprintf("%d",iNum);
|
|
|
|
|
RString sReturn;
|
2004-02-22 21:13:27 +00:00
|
|
|
for( unsigned i=0; i<sNum.length(); i++ )
|
2003-06-24 20:21:09 +00:00
|
|
|
{
|
2004-02-22 19:51:46 +00:00
|
|
|
char cDigit = sNum[sNum.length()-1-i];
|
|
|
|
|
if( i!=0 && i%3 == 0 )
|
|
|
|
|
sReturn = ',' + sReturn;
|
|
|
|
|
sReturn = cDigit + sReturn;
|
2002-10-16 18:25:14 +00:00
|
|
|
}
|
2002-10-02 05:42:59 +00:00
|
|
|
return sReturn;
|
2002-08-01 13:42:56 +00:00
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2005-12-21 09:56:16 +00:00
|
|
|
static LocalizedString NUM_PREFIX ( "RageUtil", "NumPrefix" );
|
|
|
|
|
static LocalizedString NUM_ST ( "RageUtil", "NumSt" );
|
|
|
|
|
static LocalizedString NUM_ND ( "RageUtil", "NumNd" );
|
|
|
|
|
static LocalizedString NUM_RD ( "RageUtil", "NumRd" );
|
|
|
|
|
static LocalizedString NUM_TH ( "RageUtil", "NumTh" );
|
2005-12-20 08:35:47 +00:00
|
|
|
RString FormatNumberAndSuffix( int i )
|
2005-03-31 02:26:37 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sSuffix;
|
2005-03-31 02:26:37 +00:00
|
|
|
switch( i%10 )
|
|
|
|
|
{
|
2005-12-21 09:56:16 +00:00
|
|
|
case 1: sSuffix = NUM_ST; break;
|
|
|
|
|
case 2: sSuffix = NUM_ND; break;
|
|
|
|
|
case 3: sSuffix = NUM_RD; break;
|
|
|
|
|
default: sSuffix = NUM_TH; break;
|
2005-03-31 02:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "11th", "113th", etc.
|
|
|
|
|
if( ((i%100) / 10) == 1 )
|
2005-12-21 09:56:16 +00:00
|
|
|
sSuffix = NUM_TH;
|
2005-03-31 02:26:37 +00:00
|
|
|
|
2005-12-21 09:56:16 +00:00
|
|
|
return NUM_PREFIX.GetValue() + ssprintf("%i", i) + sSuffix;
|
2005-03-31 02:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-14 11:19:18 +00:00
|
|
|
struct tm GetLocalTime()
|
2004-02-14 08:27:09 +00:00
|
|
|
{
|
|
|
|
|
const time_t t = time(NULL);
|
|
|
|
|
struct tm tm;
|
|
|
|
|
localtime_r( &t, &tm );
|
2004-02-14 11:19:18 +00:00
|
|
|
return tm;
|
2004-02-14 08:27:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ssprintf( const char *fmt, ...)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
va_list va;
|
|
|
|
|
va_start(va, fmt);
|
2001-11-03 10:52:42 +00:00
|
|
|
return vssprintf(fmt, va);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
RString vssprintf( const char *fmt, va_list argList )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
RString sStr;
|
|
|
|
|
sStr.FormatV( fmt, argList );
|
|
|
|
|
return sStr;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2002-12-09 03:09:59 +00:00
|
|
|
#ifdef WIN32
|
2005-05-11 04:43:54 +00:00
|
|
|
|
2003-07-14 22:23:04 +00:00
|
|
|
#ifdef _XBOX
|
2005-05-11 04:43:54 +00:00
|
|
|
# include <D3DX8Core.h>
|
2003-07-14 22:23:04 +00:00
|
|
|
#else
|
2005-05-11 04:43:54 +00:00
|
|
|
# include <windows.h>
|
|
|
|
|
# include <dxerr8.h>
|
2005-12-21 06:19:18 +00:00
|
|
|
# if defined(_MSC_VER)
|
2005-05-11 04:43:54 +00:00
|
|
|
# pragma comment(lib, "dxerr8.lib")
|
|
|
|
|
# endif
|
2003-07-14 22:23:04 +00:00
|
|
|
#endif
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString hr_ssprintf( int hr, const char *fmt, ...)
|
2002-12-09 03:09:59 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
va_list va;
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
RString s = vssprintf( fmt, va );
|
|
|
|
|
va_end(va);
|
2002-12-09 03:09:59 +00:00
|
|
|
|
2003-07-14 22:23:04 +00:00
|
|
|
#ifdef _XBOX
|
2003-07-11 03:15:28 +00:00
|
|
|
char szError[1024] = "";
|
|
|
|
|
D3DXGetErrorString( hr, szError, sizeof(szError) );
|
2003-07-14 22:23:04 +00:00
|
|
|
#else
|
|
|
|
|
const char *szError = DXGetErrorString8( hr );
|
|
|
|
|
#endif
|
|
|
|
|
|
2003-07-11 03:15:28 +00:00
|
|
|
return s + ssprintf( " (%s)", szError );
|
2002-12-09 03:09:59 +00:00
|
|
|
}
|
2002-12-21 08:40:40 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString werr_ssprintf( int err, const char *fmt, ...)
|
2002-12-21 08:40:40 +00:00
|
|
|
{
|
2003-05-22 05:28:37 +00:00
|
|
|
char buf[1024] = "";
|
|
|
|
|
#ifndef _XBOX
|
2002-12-21 08:40:40 +00:00
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
|
|
|
0, err, 0, buf, sizeof(buf), NULL);
|
2003-05-22 05:28:37 +00:00
|
|
|
#endif
|
2002-12-21 08:40:40 +00:00
|
|
|
|
2003-12-21 07:04:33 +00:00
|
|
|
/* Why is FormatMessage returning text ending with \r\n? */
|
2005-12-20 08:35:47 +00:00
|
|
|
RString text = buf;
|
2003-12-21 07:04:33 +00:00
|
|
|
text.Replace( "\n", "" );
|
|
|
|
|
text.Replace( "\r", " " ); /* foo\r\nbar -> foo bar */
|
|
|
|
|
TrimRight( text ); /* "foo\r\n" -> "foo" */
|
|
|
|
|
|
|
|
|
|
va_list va;
|
2006-01-20 06:07:37 +00:00
|
|
|
va_start(va, fmt);
|
|
|
|
|
RString s = vssprintf( fmt, va );
|
|
|
|
|
va_end(va);
|
2002-12-21 08:40:40 +00:00
|
|
|
|
2003-12-21 07:04:33 +00:00
|
|
|
return s += ssprintf( " (%s)", text.c_str() );
|
2002-12-21 08:40:40 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ConvertWstringToACP( wstring s )
|
2004-09-20 00:03:09 +00:00
|
|
|
{
|
|
|
|
|
if( s.empty() )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2004-09-20 00:03:09 +00:00
|
|
|
|
|
|
|
|
int iBytes = WideCharToMultiByte( CP_ACP, 0, s.data(), s.size(),
|
|
|
|
|
NULL, 0, NULL, FALSE );
|
|
|
|
|
ASSERT_M( iBytes > 0, werr_ssprintf( GetLastError(), "WideCharToMultiByte" ).c_str() );
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ret;
|
2004-09-20 00:03:09 +00:00
|
|
|
WideCharToMultiByte( CP_ACP, 0, s.data(), s.size(),
|
|
|
|
|
ret.GetBuf( iBytes ), iBytes, NULL, FALSE );
|
|
|
|
|
ret.RelBuf( iBytes );
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-03 19:23:50 +00:00
|
|
|
RString ConvertUTF8ToACP( const RString &s )
|
2004-09-20 00:03:09 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
return ConvertWstringToACP( RStringToWstring(s) );
|
2004-09-20 00:03:09 +00:00
|
|
|
}
|
|
|
|
|
|
2002-12-09 03:09:59 +00:00
|
|
|
#endif
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2005-12-28 06:32:33 +00:00
|
|
|
/* ISO-639-1 codes: http://www.loc.gov/standards/iso639-2/langcodes.html
|
|
|
|
|
* native forms: http://people.w3.org/rishida/names/languages.html
|
|
|
|
|
* We don't use 3-letter codes, so we don't bother supporting them. */
|
2005-12-28 05:43:26 +00:00
|
|
|
static const LanguageInfo g_langs[] =
|
2005-10-28 17:23:54 +00:00
|
|
|
{
|
2005-12-28 05:43:26 +00:00
|
|
|
{"AA", "Afar", ""},
|
|
|
|
|
{"AB", "Abkhazian", "аҧсуа бызшәа"},
|
|
|
|
|
{"AF", "Afrikaans", "Afrikaans"},
|
|
|
|
|
{"AM", "Amharic", ""},
|
|
|
|
|
{"AR", "Arabic", "العربية"},
|
|
|
|
|
{"AS", "Assamese", ""},
|
|
|
|
|
{"AY", "Aymara", ""},
|
|
|
|
|
{"AZ", "Azerbaijani", "azərbaycan"},
|
|
|
|
|
{"BA", "Bashkir", ""},
|
|
|
|
|
{"BE", "Byelorussian", "Беларуская"},
|
|
|
|
|
{"BG", "Bulgarian", "Български"},
|
|
|
|
|
{"BH", "Bihari", ""},
|
|
|
|
|
{"BI", "Bislama", ""},
|
|
|
|
|
{"BN", "Bengali", ""},
|
|
|
|
|
{"BO", "Tibetan", ""},
|
|
|
|
|
{"BR", "Breton", ""},
|
|
|
|
|
{"CA", "Catalan", "Català"},
|
|
|
|
|
{"CO", "Corsican", ""},
|
|
|
|
|
{"CS", "Czech", "čeština"},
|
|
|
|
|
{"CY", "Welsh", "Cymraeg"},
|
|
|
|
|
{"DA", "Danish", "Dansk"},
|
|
|
|
|
{"DE", "German", "Deutsch"},
|
|
|
|
|
{"DZ", "Bhutani", ""},
|
|
|
|
|
{"EL", "Greek", "Ελληνικά"},
|
|
|
|
|
{"EN", "English", "English"},
|
|
|
|
|
{"EO", "Esperanto", ""},
|
|
|
|
|
{"ES", "Spanish", "Español"},
|
|
|
|
|
{"ET", "Estonian", "Eesti"},
|
|
|
|
|
{"EU", "Basque", "euskera"},
|
|
|
|
|
{"FA", "Persian", "فارسی"},
|
|
|
|
|
{"FI", "Finnish", "Suomi"},
|
|
|
|
|
{"FJ", "Fiji", ""},
|
|
|
|
|
{"FO", "Faeroese", ""},
|
2006-01-27 08:16:22 +00:00
|
|
|
{"FR", "French", "Français"},
|
2005-12-28 05:43:26 +00:00
|
|
|
{"FY", "Frisian", ""},
|
|
|
|
|
{"GA", "Irish", "Gaeilge"},
|
|
|
|
|
{"GD", "Gaelic", ""},
|
|
|
|
|
{"GL", "Galician", "Galego"},
|
|
|
|
|
{"GN", "Guarani", ""},
|
|
|
|
|
{"GU", "Gujarati", ""},
|
|
|
|
|
{"HA", "Hausa", "Hausa"},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"HE", "Hebrew", "עברית"},
|
2005-12-28 05:43:26 +00:00
|
|
|
{"HI", "Hindi", "हिंदी"},
|
|
|
|
|
{"HR", "Croatian", "Hrvatski"},
|
|
|
|
|
{"HU", "Hungarian", "Magyar"},
|
|
|
|
|
{"HY", "Armenian", "Հայերեն"},
|
|
|
|
|
{"IA", "Interlingua", ""},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"ID", "Indonesian", "Bahasa indonesia"},
|
2005-12-28 05:43:26 +00:00
|
|
|
{"IE", "Interlingue", ""},
|
|
|
|
|
{"IK", "Inupiak", ""},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"IN", "Indonesian", "Bahasa indonesia"}, // compatibility
|
2005-12-28 05:43:26 +00:00
|
|
|
{"IS", "Icelandic", "Íslenska"},
|
2006-01-27 08:16:22 +00:00
|
|
|
{"IT", "Italian", "Italiano"},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"IW", "Hebrew", "עברית"}, // compatibility
|
2005-12-28 05:43:26 +00:00
|
|
|
{"JA", "Japanese", "日本語"},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"JI", "Yiddish", ""}, // compatibility
|
2005-12-28 05:43:26 +00:00
|
|
|
{"JW", "Javanese", ""},
|
|
|
|
|
{"KA", "Georgian", ""},
|
|
|
|
|
{"KK", "Kazakh", "Қазақ"},
|
|
|
|
|
{"KL", "Greenlandic", ""},
|
|
|
|
|
{"KM", "Cambodian", ""},
|
|
|
|
|
{"KN", "Kannada", "ಕನ್ನಡ"},
|
|
|
|
|
{"KO", "Korean", "한국어"},
|
|
|
|
|
{"KS", "Kashmiri", ""},
|
|
|
|
|
{"KU", "Kurdish", ""},
|
|
|
|
|
{"KY", "Kirghiz", "Кыргыз"},
|
|
|
|
|
{"LA", "Latin", ""},
|
|
|
|
|
{"LN", "Lingala", ""},
|
|
|
|
|
{"LO", "Laothian", ""},
|
|
|
|
|
{"LT", "Lithuanian", "Lietuviškai"},
|
|
|
|
|
{"LV", "Latvian", "Latviešu"},
|
|
|
|
|
{"MG", "Malagasy", ""},
|
|
|
|
|
{"MI", "Maori", ""},
|
|
|
|
|
{"MK", "Macedonian", "Македонски"},
|
|
|
|
|
{"ML", "Malayalam", ""},
|
|
|
|
|
{"MN", "Mongolian", ""},
|
|
|
|
|
{"MO", "Moldavian", ""},
|
|
|
|
|
{"MR", "Marathi", ""},
|
|
|
|
|
{"MS", "Malay", "Bahasa melayu"},
|
|
|
|
|
{"MT", "Maltese", "Malti"},
|
|
|
|
|
{"MY", "Burmese", ""},
|
|
|
|
|
{"NA", "Nauru", ""},
|
|
|
|
|
{"NE", "Nepali", ""},
|
|
|
|
|
{"NL", "Dutch", "Nederlands"},
|
|
|
|
|
{"NO", "Norwegian", "Norsk"},
|
|
|
|
|
{"OC", "Occitan", ""},
|
|
|
|
|
{"OM", "Oromo", ""},
|
|
|
|
|
{"OR", "Oriya", ""},
|
|
|
|
|
{"PA", "Punjabi", ""},
|
|
|
|
|
{"PL", "Polish", "Polski"},
|
|
|
|
|
{"PS", "Pashto", "پښتو"},
|
|
|
|
|
{"PT", "Portuguese", "português"},
|
|
|
|
|
{"QU", "Quechua", ""},
|
|
|
|
|
{"RM", "Rhaeto-Romance", ""},
|
|
|
|
|
{"RN", "Kirundi", "Kirundi"},
|
|
|
|
|
{"RO", "Romanian", "Română"},
|
|
|
|
|
{"RU", "Russian", "Pyccĸий"},
|
|
|
|
|
{"RW", "Kinyarwanda", "Kinyarwanda"},
|
|
|
|
|
{"SA", "Sanskrit", ""},
|
|
|
|
|
{"SD", "Sindhi", ""},
|
|
|
|
|
{"SG", "Sangro", ""},
|
|
|
|
|
{"SH", "Serbo-Croatian", ""},
|
|
|
|
|
{"SI", "Singhalese", ""},
|
|
|
|
|
{"SK", "Slovak", "Slovenčina"},
|
|
|
|
|
{"SL", "Slovenian", "Slovenščina"},
|
|
|
|
|
{"SM", "Samoan", ""},
|
|
|
|
|
{"SN", "Shona", ""},
|
|
|
|
|
{"SO", "Somali", "Somali"},
|
|
|
|
|
{"SQ", "Albanian", "Shqip"},
|
|
|
|
|
{"SR", "Serbian", "Srpski"},
|
|
|
|
|
{"SS", "Siswati", ""},
|
|
|
|
|
{"ST", "Sesotho", ""},
|
|
|
|
|
{"SU", "Sudanese", ""},
|
|
|
|
|
{"SV", "Swedish", "svenska"},
|
|
|
|
|
{"SW", "Swahili", "Kiswahili"},
|
|
|
|
|
{"TA", "Tamil", ""},
|
|
|
|
|
{"TE", "Tegulu", "తెలుగు"},
|
|
|
|
|
{"TG", "Tajik", ""},
|
|
|
|
|
{"TH", "Thai", "ภาษาไทย"},
|
|
|
|
|
{"TI", "Tigrinya", ""},
|
|
|
|
|
{"TK", "Turkmen", ""},
|
|
|
|
|
{"TL", "Tagalog", ""},
|
|
|
|
|
{"TN", "Setswana", ""},
|
|
|
|
|
{"TO", "Tonga", ""},
|
|
|
|
|
{"TR", "Turkish", "Tϋrkçe"},
|
|
|
|
|
{"TS", "Tsonga", ""},
|
|
|
|
|
{"TT", "Tatar", ""},
|
|
|
|
|
{"TW", "Twi", ""},
|
|
|
|
|
{"UK", "Ukrainian", "Українська"},
|
|
|
|
|
{"UR", "Urdu", "اردو"},
|
|
|
|
|
{"UZ", "Uzbek", "o'zbek"},
|
|
|
|
|
{"VI", "Vietnamese", "Tiếng Việt"},
|
|
|
|
|
{"VO", "Volapuk", ""},
|
|
|
|
|
{"WO", "Wolof", "Wolof"},
|
2005-12-28 06:32:33 +00:00
|
|
|
{"XH", "Xhosa", "isiXhosa"},
|
|
|
|
|
{"YI", "Yiddish", ""},
|
2005-12-28 05:43:26 +00:00
|
|
|
{"YO", "Yoruba", "Yorùbá"},
|
|
|
|
|
{"ZH", "Chinese", "中文"},
|
|
|
|
|
{"ZU", "Zulu", "isiZulu"},
|
|
|
|
|
};
|
|
|
|
|
|
2005-12-28 19:21:08 +00:00
|
|
|
void GetLanguageInfos( vector<const LanguageInfo*> &vAddTo )
|
2005-12-28 05:43:26 +00:00
|
|
|
{
|
2005-12-29 02:03:14 +00:00
|
|
|
for( unsigned i=0; i<ARRAYSIZE(g_langs); ++i )
|
2005-10-28 17:23:54 +00:00
|
|
|
{
|
2005-12-28 05:43:26 +00:00
|
|
|
// Only use languages in the intersection of Windows languages
|
|
|
|
|
// and languages that had native forms on the site listed above.
|
|
|
|
|
// Obscure languages are dropped so that they don't clutter up the
|
|
|
|
|
// smtools UI.
|
|
|
|
|
if( g_langs[i].szNativeName == RString() )
|
|
|
|
|
continue;
|
2005-12-28 19:21:08 +00:00
|
|
|
vAddTo.push_back( &g_langs[i] );
|
2005-10-28 17:23:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-28 19:21:08 +00:00
|
|
|
const LanguageInfo *GetLanguageInfo( const RString &sIsoCode )
|
|
|
|
|
{
|
2005-12-29 02:03:14 +00:00
|
|
|
for( unsigned i=0; i<ARRAYSIZE(g_langs); ++i )
|
2005-12-28 19:21:08 +00:00
|
|
|
{
|
|
|
|
|
// Only use languages in the intersection of Windows languages
|
|
|
|
|
// and languages that had native forms on the site listed above.
|
|
|
|
|
// Obscure languages are dropped so that they don't clutter up the
|
|
|
|
|
// smtools UI.
|
|
|
|
|
if( sIsoCode.CompareNoCase(g_langs[i].szIsoCode) == 0 )
|
|
|
|
|
return &g_langs[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
RString join( const RString &sDeliminator, const vector<RString> &sSource)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-12-23 08:22:21 +00:00
|
|
|
if( sSource.empty() )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2002-06-27 17:49:10 +00:00
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
RString sTmp;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
for( unsigned iNum = 0; iNum < sSource.size()-1; iNum++ )
|
|
|
|
|
{
|
|
|
|
|
sTmp += sSource[iNum];
|
|
|
|
|
sTmp += sDeliminator;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
2005-12-23 08:22:21 +00:00
|
|
|
sTmp += sSource.back();
|
|
|
|
|
return sTmp;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
RString join( const RString &sDelimitor, vector<RString>::const_iterator begin, vector<RString>::const_iterator end )
|
2003-12-07 22:54:50 +00:00
|
|
|
{
|
|
|
|
|
if( begin == end )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2003-12-07 22:54:50 +00:00
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
RString sRet;
|
2003-12-07 22:54:50 +00:00
|
|
|
while( begin != end )
|
|
|
|
|
{
|
2005-12-23 08:22:21 +00:00
|
|
|
sRet += *begin;
|
2003-12-07 22:54:50 +00:00
|
|
|
++begin;
|
|
|
|
|
if( begin != end )
|
2005-12-23 08:22:21 +00:00
|
|
|
sRet += sDelimitor;
|
2003-12-07 22:54:50 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
return sRet;
|
2003-12-07 22:54:50 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 03:59:07 +00:00
|
|
|
template <class S>
|
2004-10-03 14:41:28 +00:00
|
|
|
static int DelimitorLength( const S &Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return Delimitor.size();
|
|
|
|
|
}
|
|
|
|
|
|
2004-11-08 07:12:17 +00:00
|
|
|
static int DelimitorLength( char Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DelimitorLength( wchar_t Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-03 14:41:28 +00:00
|
|
|
template <class S, class C>
|
|
|
|
|
void do_split( const S &Source, const C Delimitor, vector<S> &AddIt, const bool bIgnoreEmpty )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-04-24 06:32:51 +00:00
|
|
|
/* Short-circuit if the source is empty; we want to return an empty vector if
|
|
|
|
|
* the string is empty, even if bIgnoreEmpty is true. */
|
|
|
|
|
if( Source.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t startpos = 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
do {
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos;
|
2004-10-03 14:41:28 +00:00
|
|
|
pos = Source.find( Delimitor, startpos );
|
2003-10-28 22:37:20 +00:00
|
|
|
if( pos == Source.npos )
|
|
|
|
|
pos = Source.size();
|
|
|
|
|
|
|
|
|
|
if( pos-startpos > 0 || !bIgnoreEmpty )
|
|
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
/* Optimization: if we're copying the whole string, avoid substr; this
|
|
|
|
|
* allows this copy to be refcounted, which is much faster. */
|
|
|
|
|
if( startpos == 0 && pos-startpos == Source.size() )
|
|
|
|
|
AddIt.push_back(Source);
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
const S AddRString = Source.substr(startpos, pos-startpos);
|
|
|
|
|
AddIt.push_back(AddRString);
|
2004-10-03 14:41:28 +00:00
|
|
|
}
|
2003-10-28 22:37:20 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2004-10-03 14:41:28 +00:00
|
|
|
startpos = pos+DelimitorLength(Delimitor);
|
2003-01-05 03:59:07 +00:00
|
|
|
} while ( startpos <= Source.size() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
void split( const RString &sSource, const RString &sDelimitor, vector<RString> &asAddIt, const bool bIgnoreEmpty )
|
2003-01-05 03:59:07 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
if( sDelimitor.size() == 1 )
|
|
|
|
|
do_split( sSource, sDelimitor[0], asAddIt, bIgnoreEmpty );
|
2004-11-08 07:12:17 +00:00
|
|
|
else
|
2005-12-28 06:07:17 +00:00
|
|
|
do_split( sSource, sDelimitor, asAddIt, bIgnoreEmpty );
|
2003-01-05 03:59:07 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
void split( const wstring &sSource, const wstring &sDelimitor, vector<wstring> &asAddIt, const bool bIgnoreEmpty )
|
2003-01-05 03:59:07 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
if( sDelimitor.size() == 1 )
|
|
|
|
|
do_split( sSource, sDelimitor[0], asAddIt, bIgnoreEmpty );
|
2004-11-08 07:12:17 +00:00
|
|
|
else
|
2005-12-28 06:07:17 +00:00
|
|
|
do_split( sSource, sDelimitor, asAddIt, bIgnoreEmpty );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-15 05:34:24 +00:00
|
|
|
/* Use:
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString str="a,b,c";
|
2003-12-15 05:34:24 +00:00
|
|
|
int start = 0, size = -1;
|
|
|
|
|
while( 1 )
|
|
|
|
|
{
|
2006-02-11 03:41:05 +00:00
|
|
|
do_split( str, ",", start, size );
|
|
|
|
|
if( start == str.size() )
|
2003-12-15 05:34:24 +00:00
|
|
|
break;
|
2006-02-11 03:41:05 +00:00
|
|
|
str[start] = 'Q';
|
2003-12-15 05:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
template <class S>
|
|
|
|
|
void do_split( const S &Source, const S &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
if( size != -1 )
|
|
|
|
|
{
|
|
|
|
|
/* Start points to the beginning of the last delimiter. Move it up. */
|
|
|
|
|
begin += size+Delimitor.size();
|
|
|
|
|
begin = min( begin, len );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
|
|
|
|
|
|
if( bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
/* Skip delims. */
|
|
|
|
|
while( begin + Delimitor.size() < Source.size() &&
|
|
|
|
|
!Source.compare( begin, Delimitor.size(), Delimitor ) )
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Where's the string function to find within a substring? C++ strings apparently
|
|
|
|
|
* are missing that ... */
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos;
|
2003-12-15 05:34:24 +00:00
|
|
|
if( Delimitor.size() == 1 )
|
|
|
|
|
pos = Source.find( Delimitor[0], begin );
|
|
|
|
|
else
|
|
|
|
|
pos = Source.find( Delimitor, begin );
|
|
|
|
|
if( pos == Source.npos || (int) pos > len )
|
|
|
|
|
pos = len;
|
|
|
|
|
size = pos - begin;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void split( const RString &Source, const RString &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
2003-12-15 05:34:24 +00:00
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, len, bIgnoreEmpty );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const wstring &Source, const wstring &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, len, bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void split( const RString &Source, const RString &Delimitor, int &begin, int &size, const bool bIgnoreEmpty )
|
2003-12-15 05:34:24 +00:00
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, Source.size(), bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const wstring &Source, const wstring &Delimitor, int &begin, int &size, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, Source.size(), bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/*
|
|
|
|
|
* foo\fum\ -> "foo\fum\", "", ""
|
|
|
|
|
* c:\foo\bar.txt -> "c:\foo\", "bar", ".txt"
|
|
|
|
|
* \\foo\fum -> "\\foo\", "fum", ""
|
|
|
|
|
*/
|
2005-12-20 08:35:47 +00:00
|
|
|
void splitpath( const RString &Path, RString& Dir, RString& Filename, RString& Ext )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2003-01-16 22:49:22 +00:00
|
|
|
Dir = Filename = Ext = "";
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
vector<RString> mat;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
/*
|
|
|
|
|
* One level of escapes for the regex, one for C. Ew.
|
2003-01-23 04:22:07 +00:00
|
|
|
* This is really:
|
2006-01-20 06:07:37 +00:00
|
|
|
* ^(.*[\\/])?(.*)$
|
|
|
|
|
*/
|
2003-01-23 04:22:07 +00:00
|
|
|
static Regex sep("^(.*[\\\\/])?(.*)$");
|
2003-07-07 23:14:50 +00:00
|
|
|
bool check = sep.Compare(Path, mat);
|
|
|
|
|
ASSERT(check);
|
2003-01-16 22:49:22 +00:00
|
|
|
|
|
|
|
|
Dir = mat[0];
|
2005-12-20 08:35:47 +00:00
|
|
|
const RString Base = mat[1];
|
2003-01-16 22:49:22 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
/* ^(.*)(\.[^\.]+)$ */
|
|
|
|
|
static Regex SplitExt("^(.*)(\\.[^\\.]+)$");
|
2003-10-29 21:22:28 +00:00
|
|
|
if( SplitExt.Compare(Base, mat) )
|
2003-01-16 22:49:22 +00:00
|
|
|
{
|
|
|
|
|
Filename = mat[0];
|
|
|
|
|
Ext = mat[1];
|
|
|
|
|
} else
|
2003-10-29 21:22:28 +00:00
|
|
|
Filename = Base;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2003-10-29 20:59:40 +00:00
|
|
|
/* "foo.bar", "baz" -> "foo.baz"
|
|
|
|
|
* "foo", "baz" -> "foo.baz"
|
|
|
|
|
* "foo.bar", "" -> "foo" */
|
2005-12-20 08:35:47 +00:00
|
|
|
RString SetExtension( const RString &path, const RString &ext )
|
2003-09-04 19:28:39 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString Dir, FName, OldExt;
|
2003-09-04 19:28:39 +00:00
|
|
|
splitpath( path, Dir, FName, OldExt );
|
2003-10-29 20:59:40 +00:00
|
|
|
return Dir + FName + (ext.size()? ".":"") + ext;
|
2003-09-04 19:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString GetExtension( const RString &sPath )
|
2003-09-03 18:22:55 +00:00
|
|
|
{
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos = sPath.rfind( '.' );
|
2003-10-22 05:49:26 +00:00
|
|
|
if( pos == sPath.npos )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2003-10-22 05:49:26 +00:00
|
|
|
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t slash = sPath.find( '/', pos );
|
2003-10-22 05:49:26 +00:00
|
|
|
if( slash != sPath.npos )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString(); /* rare: path/dir.ext/fn */
|
2003-10-22 05:49:26 +00:00
|
|
|
|
|
|
|
|
return sPath.substr( pos+1, sPath.size()-pos+1 );
|
2003-09-03 18:22:55 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString GetFileNameWithoutExtension( const RString &sPath )
|
2005-05-31 01:17:37 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sThrowAway, sFName;
|
2005-05-31 01:17:37 +00:00
|
|
|
splitpath( sPath, sThrowAway, sFName, sThrowAway );
|
|
|
|
|
return sFName;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:00:06 +00:00
|
|
|
void MakeValidFilename( RString &sName )
|
2005-12-29 01:20:33 +00:00
|
|
|
{
|
|
|
|
|
wstring wsName = RStringToWstring( sName );
|
|
|
|
|
wstring wsInvalid = L"/\\:*?\"<>|";
|
|
|
|
|
for( unsigned i = 0; i < wsName.size(); ++i )
|
|
|
|
|
{
|
|
|
|
|
wchar_t w = wsName[i];
|
|
|
|
|
if( w >= 32 &&
|
|
|
|
|
w < 126 &&
|
|
|
|
|
wsInvalid.find_first_of(w) == wsInvalid.npos )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if( w == L'"' )
|
|
|
|
|
{
|
|
|
|
|
wsName[i] = L'\'';
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We could replace with closest matches in ASCII: convert the character to UTF-8
|
|
|
|
|
* NFD (decomposed) (maybe NFKD?), and see if the first character is ASCII.
|
|
|
|
|
*
|
|
|
|
|
* This is useless for non-Western languages, since we'll replace the whole filename.
|
|
|
|
|
*/
|
|
|
|
|
wsName[i] = '_';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sName = WStringToRString( wsName );
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-22 21:18:43 +00:00
|
|
|
int g_argc = 0;
|
|
|
|
|
char **g_argv = NULL;
|
|
|
|
|
|
|
|
|
|
void SetCommandlineArguments( int argc, char **argv )
|
|
|
|
|
{
|
|
|
|
|
g_argc = argc;
|
|
|
|
|
g_argv = argv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Search for the commandline argument given; eg. "test" searches for the
|
|
|
|
|
* option "--test". All commandline arguments are getopt_long style: --foo;
|
|
|
|
|
* short arguments (-x) are not supported. (These are not intended for
|
|
|
|
|
* common, general use, so having short options isn't currently needed.)
|
|
|
|
|
* If argument is non-NULL, accept an argument.
|
|
|
|
|
*/
|
2005-12-20 08:35:47 +00:00
|
|
|
bool GetCommandlineArgument( const RString &option, RString *argument, int iIndex )
|
2005-11-22 21:18:43 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
const RString optstr = "--" + option;
|
2005-11-22 21:18:43 +00:00
|
|
|
|
|
|
|
|
for( int arg = 1; arg < g_argc; ++arg )
|
|
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
const RString CurArgument = g_argv[arg];
|
2005-11-22 21:18:43 +00:00
|
|
|
|
|
|
|
|
const size_t i = CurArgument.find( "=" );
|
2005-12-20 08:35:47 +00:00
|
|
|
RString CurOption = CurArgument.substr(0,i);
|
2005-11-22 21:18:43 +00:00
|
|
|
if( CurOption.CompareNoCase(optstr) )
|
|
|
|
|
continue; /* no match */
|
|
|
|
|
|
|
|
|
|
/* Found it. */
|
|
|
|
|
if( iIndex )
|
|
|
|
|
{
|
|
|
|
|
--iIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( argument )
|
|
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
if( i != RString::npos )
|
2005-11-22 21:18:43 +00:00
|
|
|
*argument = CurArgument.substr( i+1 );
|
|
|
|
|
else
|
|
|
|
|
*argument = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString GetCwd()
|
2003-09-05 06:20:55 +00:00
|
|
|
{
|
2003-11-13 00:39:36 +00:00
|
|
|
#ifdef _XBOX
|
2003-12-07 04:20:07 +00:00
|
|
|
return SYS_BASE_PATH;
|
2003-11-13 00:39:36 +00:00
|
|
|
#else
|
2003-09-05 06:20:55 +00:00
|
|
|
char buf[PATH_MAX];
|
|
|
|
|
bool ret = getcwd(buf, PATH_MAX) != NULL;
|
|
|
|
|
ASSERT(ret);
|
|
|
|
|
return buf;
|
2003-11-13 00:39:36 +00:00
|
|
|
#endif
|
2003-09-05 06:20:55 +00:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
/*
|
|
|
|
|
* Calculate a standard CRC32. iCRC should be initialized to 0.
|
|
|
|
|
* References:
|
|
|
|
|
* http://www.theorem.com/java/CRC32.java,
|
|
|
|
|
* http://www.faqs.org/rfcs/rfc1952.html
|
|
|
|
|
*/
|
2004-12-12 00:54:44 +00:00
|
|
|
void CRC32( unsigned int &iCRC, const void *pVoidBuffer, size_t iSize )
|
2002-05-19 01:59:48 +00:00
|
|
|
{
|
2003-05-22 20:32:08 +00:00
|
|
|
static unsigned tab[256];
|
|
|
|
|
static bool initted = false;
|
2004-12-11 23:33:11 +00:00
|
|
|
if( !initted )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
|
|
|
|
initted = true;
|
|
|
|
|
const unsigned POLY = 0xEDB88320;
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
for( int i = 0; i < 256; ++i )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
|
|
|
|
tab[i] = i;
|
2004-12-11 23:33:11 +00:00
|
|
|
for( int j = 0; j < 8; ++j )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
2004-12-11 23:33:11 +00:00
|
|
|
if( tab[i] & 1 )
|
|
|
|
|
tab[i] = (tab[i] >> 1) ^ POLY;
|
|
|
|
|
else
|
|
|
|
|
tab[i] >>= 1;
|
2003-05-22 20:32:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2004-12-12 01:39:30 +00:00
|
|
|
iCRC ^= 0xFFFFFFFF;
|
|
|
|
|
|
2004-12-12 00:54:44 +00:00
|
|
|
const char *pBuffer = (const char *) pVoidBuffer;
|
2004-12-11 23:33:11 +00:00
|
|
|
for( unsigned i = 0; i < iSize; ++i )
|
|
|
|
|
iCRC = (iCRC >> 8) ^ tab[(iCRC ^ pBuffer[i]) & 0xFF];
|
2004-12-12 01:39:30 +00:00
|
|
|
|
|
|
|
|
iCRC ^= 0xFFFFFFFF;
|
2004-12-11 23:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
unsigned int GetHashForString ( const RString &s )
|
2004-12-11 23:33:11 +00:00
|
|
|
{
|
|
|
|
|
unsigned crc = 0;
|
|
|
|
|
CRC32( crc, s.data(), s.size() );
|
2003-05-22 20:32:08 +00:00
|
|
|
return crc;
|
2002-05-19 01:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
|
2003-12-07 22:54:50 +00:00
|
|
|
/* Return true if "dir" is empty or does not exist. */
|
2005-12-21 07:52:23 +00:00
|
|
|
bool DirectoryIsEmpty( const RString &sDir )
|
2003-12-07 22:54:50 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
if( sDir.empty() )
|
2003-12-07 22:54:50 +00:00
|
|
|
return true;
|
2005-12-21 07:52:23 +00:00
|
|
|
if( !DoesFileExist(sDir) )
|
2003-12-07 22:54:50 +00:00
|
|
|
return true;
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
vector<RString> asFileNames;
|
2005-12-21 07:52:23 +00:00
|
|
|
GetDirListing( sDir, asFileNames );
|
2003-12-07 22:54:50 +00:00
|
|
|
return asFileNames.empty();
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
bool CompareRStringsAsc( const RString &sStr1, const RString &sStr2 )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
return sStr1.CompareNoCase( sStr2 ) < 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
bool CompareRStringsDesc( const RString &sStr1, const RString &sStr2 )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
return sStr1.CompareNoCase( sStr2 ) > 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void SortRStringArray( vector<RString> &arrayRStrings, const bool bSortAscending )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
sort( arrayRStrings.begin(), arrayRStrings.end(),
|
2005-12-28 06:07:17 +00:00
|
|
|
bSortAscending?CompareRStringsAsc:CompareRStringsDesc );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
2001-11-26 08:28:48 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
float calc_mean( const float *pStart, const float *pEnd )
|
2002-10-24 07:09:38 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
return accumulate( pStart, pEnd, 0.f ) / distance( pStart, pEnd );
|
2002-10-24 07:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
float calc_stddev( const float *pStart, const float *pEnd )
|
2002-10-24 07:09:38 +00:00
|
|
|
{
|
|
|
|
|
/* Calculate the mean. */
|
2005-12-21 07:52:23 +00:00
|
|
|
float fMean = calc_mean( pStart, pEnd );
|
2002-10-24 07:09:38 +00:00
|
|
|
|
|
|
|
|
/* Calculate stddev. */
|
2005-12-21 07:52:23 +00:00
|
|
|
float fDev = 0.0f;
|
|
|
|
|
for( const float *i=pStart; i != pEnd; ++i )
|
|
|
|
|
fDev += (*i - fMean) * (*i - fMean);
|
|
|
|
|
fDev /= distance( pStart, pEnd ) - 1;
|
|
|
|
|
fDev = sqrtf( fDev );
|
2002-10-24 07:09:38 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
return fDev;
|
2002-10-24 07:09:38 +00:00
|
|
|
}
|
2002-05-27 08:23:27 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
void TrimLeft( RString &sStr, const char *s )
|
2002-10-31 08:04:34 +00:00
|
|
|
{
|
|
|
|
|
int n = 0;
|
2005-12-21 07:52:23 +00:00
|
|
|
while( n < int(sStr.size()) && strchr(s, sStr[n]) )
|
2002-10-31 08:04:34 +00:00
|
|
|
n++;
|
|
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
sStr.erase( sStr.begin(), sStr.begin()+n );
|
2002-10-31 08:04:34 +00:00
|
|
|
}
|
2002-05-27 08:23:27 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
void TrimRight( RString &sStr, const char *s )
|
2002-10-31 08:04:34 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
int n = sStr.size();
|
|
|
|
|
while( n > 0 && strchr(s, sStr[n-1]) )
|
2002-10-31 08:04:34 +00:00
|
|
|
n--;
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
/* Delete from n to the end. If n == sStr.size(), nothing is deleted;
|
2002-11-01 20:48:36 +00:00
|
|
|
* if n == 0, the whole string is erased. */
|
2005-12-21 07:52:23 +00:00
|
|
|
sStr.erase( sStr.begin()+n, sStr.end() );
|
2002-10-31 08:04:34 +00:00
|
|
|
}
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
void StripCrnl( RString &s )
|
2003-02-14 22:28:29 +00:00
|
|
|
{
|
2003-07-10 01:55:37 +00:00
|
|
|
while( s.size() && (s[s.size()-1] == '\r' || s[s.size()-1] == '\n') )
|
2005-12-21 07:52:23 +00:00
|
|
|
s.erase( s.size()-1 );
|
2003-02-14 22:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool BeginsWith( const RString &sTestThis, const RString &sBeginning )
|
2005-10-27 04:51:47 +00:00
|
|
|
{
|
|
|
|
|
ASSERT( !sBeginning.empty() );
|
2005-10-27 06:11:59 +00:00
|
|
|
return sTestThis.compare( 0, sBeginning.length(), sBeginning ) == 0;
|
2005-10-27 04:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool EndsWith( const RString &sTestThis, const RString &sEnding )
|
2005-09-02 00:14:07 +00:00
|
|
|
{
|
|
|
|
|
ASSERT( !sEnding.empty() );
|
|
|
|
|
if( sTestThis.size() < sEnding.size() )
|
|
|
|
|
return false;
|
2005-10-27 06:11:59 +00:00
|
|
|
return sTestThis.compare( sTestThis.length()-sEnding.length(), sEnding.length(), sEnding ) == 0;
|
2005-09-02 00:14:07 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-09 00:08:10 +00:00
|
|
|
RString URLEncode( const RString &sStr )
|
|
|
|
|
{
|
|
|
|
|
RString sOutput;
|
|
|
|
|
for( unsigned k = 0; k < sStr.size(); k++ )
|
|
|
|
|
{
|
|
|
|
|
char t = sStr[k];
|
|
|
|
|
if( t >= '!' && t <= 'z' )
|
|
|
|
|
sOutput += t;
|
|
|
|
|
else
|
2006-02-09 00:08:40 +00:00
|
|
|
sOutput += "%" + ssprintf( "%02X", t );
|
2006-02-09 00:08:10 +00:00
|
|
|
}
|
|
|
|
|
return sOutput;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void StripCvs( vector<RString> &vs )
|
2005-06-23 08:05:09 +00:00
|
|
|
{
|
|
|
|
|
for( unsigned i=0; i<vs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( vs[i].Right(3).CompareNoCase("CVS") == 0 )
|
|
|
|
|
vs.erase( vs.begin()+i );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-01 22:02:41 +00:00
|
|
|
/* path is a .redir pathname. Read it and return the real one. */
|
2005-12-21 07:52:23 +00:00
|
|
|
RString DerefRedir( const RString &_path )
|
2003-01-01 22:02:41 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
RString sPath = _path;
|
2005-03-11 06:09:56 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<100; i++ )
|
2003-07-10 11:47:28 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
if( GetExtension(sPath) != "redir" )
|
|
|
|
|
return sPath;
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sNewFileName;
|
2005-12-21 07:52:23 +00:00
|
|
|
GetFileContents( sPath, sNewFileName, true );
|
2003-02-16 05:12:27 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
/* Empty is invalid. */
|
|
|
|
|
if( sNewFileName == "" )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
FixSlashesInPlace( sNewFileName );
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
RString sPath2 = Dirname(sPath) + sNewFileName;
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
CollapsePath( sPath2 );
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
sPath2 += "*";
|
2005-03-11 06:09:56 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
vector<RString> matches;
|
2005-12-21 07:52:23 +00:00
|
|
|
GetDirListing( sPath2, matches, false, true );
|
2005-03-11 06:09:56 +00:00
|
|
|
|
|
|
|
|
if( matches.empty() )
|
2005-12-21 07:52:23 +00:00
|
|
|
RageException::Throw( "The redirect '%s' references a file '%s' which doesn't exist.", sPath.c_str(), sPath2.c_str() );
|
2005-03-11 06:09:56 +00:00
|
|
|
else if( matches.size() > 1 )
|
2005-12-21 07:52:23 +00:00
|
|
|
RageException::Throw( "The redirect '%s' references a file '%s' with multiple matches.", sPath.c_str(), sPath2.c_str() );
|
2005-03-11 06:09:56 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
sPath = matches[0];
|
2005-03-11 06:09:56 +00:00
|
|
|
}
|
2003-09-23 05:39:47 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
RageException::Throw( "Circular redirect '%s'", sPath.c_str() );
|
2003-01-01 22:02:41 +00:00
|
|
|
}
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool GetFileContents( const RString &sPath, RString &sOut, bool bOneLine )
|
2003-03-15 00:16:45 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
/* Don't warn if the file doesn't exist, but do warn if it exists and fails to open. */
|
|
|
|
|
if( !IsAFile(sPath) )
|
|
|
|
|
return false;
|
|
|
|
|
|
2004-06-06 20:56:46 +00:00
|
|
|
RageFile file;
|
2005-05-17 02:20:43 +00:00
|
|
|
if( !file.Open(sPath) )
|
2004-06-06 20:56:46 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
LOG->Warn( "GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sData;
|
2005-05-20 00:12:43 +00:00
|
|
|
int iGot;
|
|
|
|
|
if( bOneLine )
|
|
|
|
|
iGot = file.GetLine( sData );
|
|
|
|
|
else
|
|
|
|
|
iGot = file.Read( sData, file.GetFileSize() );
|
|
|
|
|
|
|
|
|
|
if( iGot == -1 )
|
2004-06-06 20:56:46 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
LOG->Warn( "GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
}
|
2005-05-20 00:12:43 +00:00
|
|
|
|
|
|
|
|
if( bOneLine )
|
|
|
|
|
StripCrnl( sData );
|
2003-11-30 06:20:25 +00:00
|
|
|
|
2005-05-17 02:20:43 +00:00
|
|
|
sOut = sData;
|
|
|
|
|
return true;
|
2003-03-15 00:16:45 +00:00
|
|
|
}
|
|
|
|
|
|
2003-06-27 20:09:29 +00:00
|
|
|
#include "pcre/pcre.h"
|
|
|
|
|
void Regex::Compile()
|
|
|
|
|
{
|
|
|
|
|
const char *error;
|
|
|
|
|
int offset;
|
2005-12-28 06:07:17 +00:00
|
|
|
m_pReg = pcre_compile( m_sPattern.c_str(), PCRE_CASELESS, &error, &offset, NULL );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
if( m_pReg == NULL )
|
2005-12-28 06:07:17 +00:00
|
|
|
RageException::Throw( "Invalid regex: \"%s\" (%s)", m_sPattern.c_str(), error );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
int iRet = pcre_fullinfo( (pcre *) m_pReg, NULL, PCRE_INFO_CAPTURECOUNT, &m_iBackrefs );
|
|
|
|
|
ASSERT( iRet >= 0 );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
++m_iBackrefs;
|
2006-01-20 06:07:37 +00:00
|
|
|
ASSERT( m_iBackrefs < 128 );
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
void Regex::Set( const RString &sStr )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
|
|
|
|
Release();
|
2006-01-20 06:07:37 +00:00
|
|
|
m_sPattern = sStr;
|
2003-06-27 20:09:29 +00:00
|
|
|
Compile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Regex::Release()
|
|
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
pcre_free( m_pReg );
|
2005-12-28 06:07:17 +00:00
|
|
|
m_pReg = NULL;
|
2005-12-29 02:36:46 +00:00
|
|
|
m_sPattern = RString();
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
Regex::Regex( const RString &sStr )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
m_pReg = NULL;
|
|
|
|
|
Set( sStr );
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
Regex::Regex( const Regex &rhs )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
m_pReg = NULL;
|
2006-01-20 06:07:37 +00:00
|
|
|
Set( rhs.m_sPattern );
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
Regex &Regex::operator=( const Regex &rhs )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
if( this != &rhs )
|
|
|
|
|
Set( rhs.m_sPattern );
|
2003-06-27 20:09:29 +00:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex::~Regex()
|
|
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
Release();
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
bool Regex::Compare( const RString &sStr )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
int iMat[128*3];
|
2005-12-28 06:07:17 +00:00
|
|
|
int iRet = pcre_exec( (pcre *) m_pReg, NULL, sStr.data(), sStr.size(), 0, 0, iMat, 128*3 );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
if( iRet < -1 )
|
|
|
|
|
RageException::Throw( "Unexpected return from pcre_exec('%s'): %i", m_sPattern.c_str(), iRet );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
return iRet >= 0;
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
bool Regex::Compare( const RString &sStr, vector<RString> &asMatches )
|
2003-06-27 20:09:29 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
asMatches.clear();
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
int iMat[128*3];
|
2005-12-28 06:07:17 +00:00
|
|
|
int iRet = pcre_exec( (pcre *) m_pReg, NULL, sStr.data(), sStr.size(), 0, 0, iMat, 128*3 );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
if( iRet < -1 )
|
|
|
|
|
RageException::Throw( "Unexpected return from pcre_exec('%s'): %i", m_sPattern.c_str(), iRet );
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
if( iRet == -1 )
|
2003-06-27 20:09:29 +00:00
|
|
|
return false;
|
|
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
for( unsigned i = 1; i < m_iBackrefs; ++i )
|
|
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
const int iStart = iMat[i*2], end = iMat[i*2+1];
|
2006-01-20 06:07:37 +00:00
|
|
|
if( iStart == -1 )
|
|
|
|
|
asMatches.push_back( RString() ); /* no match */
|
|
|
|
|
else
|
|
|
|
|
asMatches.push_back( sStr.substr(iStart, end - iStart) );
|
|
|
|
|
}
|
2003-06-27 20:09:29 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
return true;
|
2003-06-27 20:09:29 +00:00
|
|
|
}
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2005-10-06 07:01:58 +00:00
|
|
|
// Arguments and behavior are the same are similar to
|
|
|
|
|
// http://us3.php.net/manual/en/function.preg-replace.php
|
2005-12-21 07:52:23 +00:00
|
|
|
bool Regex::Replace( const RString &sReplacement, const RString &sSubject, RString &sOut )
|
2005-10-06 07:01:58 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
vector<RString> asMatches;
|
2005-12-21 07:52:23 +00:00
|
|
|
if( !Compare(sSubject, asMatches) )
|
2005-10-06 07:01:58 +00:00
|
|
|
return false;
|
|
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
sOut = sReplacement;
|
2005-10-06 07:01:58 +00:00
|
|
|
|
|
|
|
|
// TODO: optimize me by iterating only once over the string
|
2005-12-21 07:52:23 +00:00
|
|
|
for( unsigned i=0; i<asMatches.size(); i++ )
|
2005-10-06 07:01:58 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sFrom = ssprintf( "\\${%d}", i );
|
2005-12-21 07:52:23 +00:00
|
|
|
RString sTo = asMatches[i];
|
|
|
|
|
sOut.Replace(sFrom, sTo);
|
2005-10-06 07:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
return true;
|
2005-10-06 07:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2003-01-06 01:19:22 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
/* Given a UTF-8 byte, return the length of the codepoint (if a start code)
|
|
|
|
|
* or 0 if it's a continuation byte. */
|
|
|
|
|
int utf8_get_char_len( char p )
|
|
|
|
|
{
|
|
|
|
|
if( !(p & 0x80) ) return 1; /* 0xxxxxxx - 1 */
|
|
|
|
|
if( !(p & 0x40) ) return 1; /* 10xxxxxx - continuation */
|
|
|
|
|
if( !(p & 0x20) ) return 2; /* 110xxxxx */
|
|
|
|
|
if( !(p & 0x10) ) return 3; /* 1110xxxx */
|
|
|
|
|
if( !(p & 0x08) ) return 4; /* 11110xxx */
|
|
|
|
|
if( !(p & 0x04) ) return 5; /* 111110xx */
|
|
|
|
|
if( !(p & 0x02) ) return 6; /* 1111110x */
|
|
|
|
|
return 1; /* 1111111x */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool is_utf8_continuation_byte( char c )
|
2003-01-06 01:19:22 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
return (c & 0xC0) == 0x80;
|
2003-05-18 04:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* Decode one codepoint at start; advance start and place the result in ch. If
|
|
|
|
|
* the encoded string is invalid, false is returned. */
|
2005-12-20 08:35:47 +00:00
|
|
|
bool utf8_to_wchar_ec( const RString &s, unsigned &start, wchar_t &ch )
|
2003-05-18 04:36:03 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start >= s.size() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if( is_utf8_continuation_byte( s[start] ) || /* misplaced continuation byte */
|
|
|
|
|
(s[start] & 0xFE) == 0xFE ) /* 0xFE, 0xFF */
|
|
|
|
|
{
|
|
|
|
|
start += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int len = utf8_get_char_len( s[start] );
|
|
|
|
|
|
|
|
|
|
const int first_byte_mask[] = { -1, 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
|
|
|
|
|
|
2004-05-18 03:15:13 +00:00
|
|
|
ch = wchar_t(s[start] & first_byte_mask[len]);
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
for( int i = 1; i < len; ++i )
|
2003-05-18 04:36:03 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start+i >= s.size() )
|
|
|
|
|
{
|
|
|
|
|
/* We expected a continuation byte, but didn't get one. Return error, and point
|
|
|
|
|
* start at the unexpected byte; it's probably a new sequence. */
|
|
|
|
|
start += i;
|
2003-05-18 04:36:03 +00:00
|
|
|
return false;
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-18 03:15:13 +00:00
|
|
|
char byte = s[start+i];
|
2004-05-17 21:28:26 +00:00
|
|
|
if( !is_utf8_continuation_byte(byte) )
|
|
|
|
|
{
|
|
|
|
|
/* We expected a continuation byte, but didn't get one. Return error, and point
|
|
|
|
|
* start at the unexpected byte; it's probably a new sequence. */
|
|
|
|
|
start += i;
|
2003-06-30 08:38:24 +00:00
|
|
|
return false;
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
ch = (ch << 6) | byte & 0x3F;
|
|
|
|
|
}
|
2003-05-18 04:36:03 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
bool bValid = true;
|
|
|
|
|
{
|
|
|
|
|
unsigned c1 = (unsigned) s[start] & 0xFF;
|
|
|
|
|
unsigned c2 = (unsigned) s[start+1] & 0xFF;
|
|
|
|
|
int c = (c1 << 8) + c2;
|
|
|
|
|
if( (c & 0xFE00) == 0xC000 ||
|
|
|
|
|
(c & 0xFFE0) == 0xE080 ||
|
|
|
|
|
(c & 0xFFF0) == 0xF080 ||
|
|
|
|
|
(c & 0xFFF8) == 0xF880 ||
|
|
|
|
|
(c & 0xFFFC) == 0xFC80 )
|
|
|
|
|
{
|
|
|
|
|
bValid = false;
|
|
|
|
|
}
|
2003-05-18 04:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
if( ch == 0xFFFE || ch == 0xFFFF )
|
|
|
|
|
bValid = false;
|
|
|
|
|
|
|
|
|
|
start += len;
|
|
|
|
|
return bValid;
|
2003-01-06 01:19:22 +00:00
|
|
|
}
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* Like utf8_to_wchar_ec, but only does enough error checking to prevent crashing. */
|
2005-12-20 08:35:47 +00:00
|
|
|
bool utf8_to_wchar( const RString &s, unsigned &start, wchar_t &ch )
|
2003-01-05 05:31:59 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start >= s.size() )
|
|
|
|
|
return false;
|
2003-01-06 01:19:22 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
int len = utf8_get_char_len( s[start] );
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start+len > s.size() )
|
|
|
|
|
{
|
|
|
|
|
/* We don't have room for enough continuation bytes. Return error. */
|
|
|
|
|
start += len;
|
|
|
|
|
ch = L'?';
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
switch( len )
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
ch = (s[start+0] & 0x7F);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
ch = ( (s[start+0] & 0x1F) << 6 ) |
|
|
|
|
|
(s[start+1] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
ch = ( (s[start+0] & 0x0F) << 12 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+2] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
ch = ( (s[start+0] & 0x07) << 18 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 12 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+3] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
ch = ( (s[start+0] & 0x03) << 24 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 18 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 12 ) |
|
|
|
|
|
( (s[start+3] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+4] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
ch = ( (s[start+0] & 0x01) << 30 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 24 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 18 ) |
|
|
|
|
|
( (s[start+3] & 0x3F) << 12) |
|
|
|
|
|
( (s[start+4] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+5] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start += len;
|
|
|
|
|
return true;
|
2003-01-05 05:31:59 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-06 04:23:41 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* UTF-8 encode ch and append to out. */
|
2005-12-20 08:35:47 +00:00
|
|
|
void wchar_to_utf8( wchar_t ch, RString &out )
|
2003-01-05 05:31:59 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( ch < 0x80 ) { out.append( 1, (char) ch ); return; }
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
int cbytes = 0;
|
|
|
|
|
if( ch < 0x800 ) cbytes = 1;
|
|
|
|
|
else if( ch < 0x10000 ) cbytes = 2;
|
|
|
|
|
else if( ch < 0x200000 ) cbytes = 3;
|
|
|
|
|
else if( ch < 0x4000000 ) cbytes = 4;
|
|
|
|
|
else cbytes = 5;
|
2003-01-05 05:31:59 +00:00
|
|
|
|
|
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
int shift = cbytes*6;
|
|
|
|
|
const int init_masks[] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
|
|
|
|
out.append( 1, (char) (init_masks[cbytes-1] | (ch>>shift)) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < cbytes; ++i )
|
|
|
|
|
{
|
|
|
|
|
int shift = (cbytes-i-1)*6;
|
|
|
|
|
out.append( 1, (char) (0x80 | ((ch>>shift)&0x3F)) );
|
2003-01-05 05:31:59 +00:00
|
|
|
}
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
wchar_t utf8_get_char( const RString &s )
|
2004-05-17 21:28:26 +00:00
|
|
|
{
|
|
|
|
|
unsigned start = 0;
|
|
|
|
|
wchar_t ret;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ret ) )
|
|
|
|
|
return INVALID_CHAR;
|
2003-01-05 05:31:59 +00:00
|
|
|
return ret;
|
|
|
|
|
}
|
2003-01-06 01:35:05 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Replace invalid sequences in s. */
|
2005-12-20 08:35:47 +00:00
|
|
|
void utf8_sanitize( RString &s )
|
2003-01-06 07:43:14 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ret;
|
2004-05-17 21:28:26 +00:00
|
|
|
for( unsigned start = 0; start < s.size(); )
|
|
|
|
|
{
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ch ) )
|
2004-05-18 03:15:13 +00:00
|
|
|
ch = INVALID_CHAR;
|
2003-01-06 07:43:14 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
wchar_to_utf8( ch, ret );
|
|
|
|
|
}
|
2003-01-06 07:43:14 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
s = ret;
|
2003-01-06 07:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool utf8_is_valid( const RString &s )
|
2004-05-17 21:28:26 +00:00
|
|
|
{
|
|
|
|
|
for( unsigned start = 0; start < s.size(); )
|
|
|
|
|
{
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ch ) )
|
|
|
|
|
return false;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2004-05-17 21:28:26 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-03 01:02:45 +00:00
|
|
|
/* Windows tends to drop garbage BOM characters at the start of UTF-8 text files.
|
|
|
|
|
* Remove them. */
|
2005-12-20 08:35:47 +00:00
|
|
|
void utf8_remove_bom( RString &sLine )
|
2004-12-03 01:02:45 +00:00
|
|
|
{
|
|
|
|
|
if( !sLine.compare(0, 3, "\xef\xbb\xbf") )
|
|
|
|
|
sLine.erase(0, 3);
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:33:35 +00:00
|
|
|
const wchar_t INVALID_CHAR = 0xFFFD; /* U+FFFD REPLACEMENT CHARACTER */
|
2003-01-06 01:35:05 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
wstring RStringToWstring( const RString &s )
|
2004-05-17 21:28:26 +00:00
|
|
|
{
|
|
|
|
|
wstring ret;
|
2004-10-03 14:41:28 +00:00
|
|
|
ret.reserve( s.size() );
|
2004-05-17 21:28:26 +00:00
|
|
|
for( unsigned start = 0; start < s.size(); )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
char c = s[start];
|
|
|
|
|
if( !(c&0x80) )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
/* ASCII fast path */
|
|
|
|
|
ret += c;
|
|
|
|
|
++start;
|
|
|
|
|
continue;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2004-10-03 14:41:28 +00:00
|
|
|
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar( s, start, ch ) )
|
|
|
|
|
ch = INVALID_CHAR;
|
|
|
|
|
ret += ch;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
return ret;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
RString WStringToRString( const wstring &sStr )
|
2004-05-17 21:28:26 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
RString sRet;
|
2004-05-17 21:28:26 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
for( unsigned i = 0; i < sStr.size(); ++i )
|
|
|
|
|
wchar_to_utf8( sStr[i], sRet );
|
2004-05-17 21:28:26 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
return sRet;
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString WcharToUTF8( wchar_t c )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ret;
|
2004-05-17 21:28:26 +00:00
|
|
|
wchar_to_utf8( c, ret );
|
|
|
|
|
return ret;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2003-01-06 02:48:14 +00:00
|
|
|
|
2005-09-04 23:04:21 +00:00
|
|
|
/* &a; -> a */
|
2005-12-20 08:35:47 +00:00
|
|
|
void ReplaceEntityText( RString &sText, const map<RString,RString> &m )
|
2005-09-04 23:04:21 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sRet;
|
2005-09-04 23:04:21 +00:00
|
|
|
|
|
|
|
|
size_t iOffset = 0;
|
|
|
|
|
while( iOffset != sText.size() )
|
|
|
|
|
{
|
|
|
|
|
size_t iStart = sText.find( '&', iOffset );
|
|
|
|
|
if( iStart == sText.npos )
|
|
|
|
|
{
|
|
|
|
|
/* Optimization: if we didn't replace anything at all, do nothing. */
|
|
|
|
|
if( iOffset == 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Append the rest of the string.
|
|
|
|
|
sRet.append( sText, iOffset, sRet.npos );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Append the text between iOffset and iStart.
|
|
|
|
|
sRet.append( sText, iOffset, iStart-iOffset );
|
|
|
|
|
iOffset += iStart-iOffset;
|
|
|
|
|
|
|
|
|
|
// Optimization: stop early on "&", so "&&&&&&&&&&&" isn't n^2.
|
|
|
|
|
size_t iEnd = sText.find_first_of( "&;", iStart+1 );
|
|
|
|
|
if( iEnd == sText.npos || sText[iEnd] == '&' )
|
|
|
|
|
{
|
|
|
|
|
/* & with no matching ;, or two & in a row. Append the & and
|
|
|
|
|
* continue. */
|
|
|
|
|
sRet.append( sText, iStart, 1 );
|
|
|
|
|
++iOffset;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sElement = sText.substr( iStart+1, iEnd-iStart-1 );
|
2005-09-04 23:04:21 +00:00
|
|
|
sElement.MakeLower();
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
map<RString,RString>::const_iterator it = m.find( sElement );
|
2005-09-04 23:04:21 +00:00
|
|
|
if( it == m.end() )
|
|
|
|
|
{
|
|
|
|
|
sRet.append( sText, iStart, iEnd-iStart+1 );
|
|
|
|
|
iOffset = iEnd + 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
const RString &sTo = it->second;
|
2005-09-04 23:04:21 +00:00
|
|
|
sRet.append( sTo );
|
|
|
|
|
iOffset = iEnd + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sText = sRet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* abcd -> &a; &b; &c; &d; */
|
2005-12-20 08:35:47 +00:00
|
|
|
void ReplaceEntityText( RString &sText, const map<char,RString> &m )
|
2005-09-04 23:04:21 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sFind;
|
2005-09-04 23:04:21 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
FOREACHM_CONST( char, RString, m, c )
|
2005-09-04 23:04:21 +00:00
|
|
|
sFind.append( 1, c->first );
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sRet;
|
2005-09-04 23:04:21 +00:00
|
|
|
|
|
|
|
|
size_t iOffset = 0;
|
|
|
|
|
while( iOffset != sText.size() )
|
|
|
|
|
{
|
|
|
|
|
size_t iStart = sText.find_first_of( sFind, iOffset );
|
|
|
|
|
if( iStart == sText.npos )
|
|
|
|
|
{
|
|
|
|
|
/* Optimization: if we didn't replace anything at all, do nothing. */
|
|
|
|
|
if( iOffset == 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Append the rest of the string.
|
|
|
|
|
sRet.append( sText, iOffset, sRet.npos );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Append the text between iOffset and iStart.
|
|
|
|
|
sRet.append( sText, iOffset, iStart-iOffset );
|
|
|
|
|
iOffset += iStart-iOffset;
|
|
|
|
|
|
|
|
|
|
char sElement = sText[iStart];
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
map<char,RString>::const_iterator it = m.find( sElement );
|
2005-09-04 23:04:21 +00:00
|
|
|
ASSERT( it != m.end() );
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
const RString &sTo = it->second;
|
2005-09-04 23:04:21 +00:00
|
|
|
sRet.append( 1, '&' );
|
|
|
|
|
sRet.append( sTo );
|
|
|
|
|
sRet.append( 1, ';' );
|
|
|
|
|
++iOffset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sText = sRet;
|
|
|
|
|
}
|
2003-02-15 00:21:40 +00:00
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
/* Replace &#nnnn; (decimal) and &xnnnn; (hex) with corresponding UTF-8 characters. */
|
2005-12-28 06:07:17 +00:00
|
|
|
void Replace_Unicode_Markers( RString &sText )
|
2003-01-06 02:48:14 +00:00
|
|
|
{
|
2005-12-28 06:07:17 +00:00
|
|
|
unsigned iStart = 0;
|
|
|
|
|
while( iStart < sText.size() )
|
2003-01-06 02:48:14 +00:00
|
|
|
{
|
|
|
|
|
/* Look for &#digits; */
|
2005-12-28 06:07:17 +00:00
|
|
|
bool bHex = false;
|
|
|
|
|
size_t iPos = sText.find( "&#", iStart );
|
|
|
|
|
if( iPos == sText.npos )
|
|
|
|
|
{
|
|
|
|
|
bHex = true;
|
|
|
|
|
iPos = sText.find( "&x", iStart );
|
2003-01-06 02:48:14 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
if( iPos == sText.npos )
|
|
|
|
|
break;
|
|
|
|
|
iStart = iPos+1;
|
2003-01-06 02:48:14 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
unsigned p = iPos;
|
2003-01-06 02:48:14 +00:00
|
|
|
p += 2;
|
|
|
|
|
|
|
|
|
|
/* Found &# or &x. Is it followed by digits and a semicolon? */
|
2005-12-28 06:07:17 +00:00
|
|
|
if( p >= sText.size() )
|
|
|
|
|
continue;
|
2003-01-06 02:48:14 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
int iNumDigits = 0;
|
|
|
|
|
while( p < sText.size() &&
|
|
|
|
|
(bHex && isxdigit(sText[p])) || (!bHex && isdigit(sText[p])) )
|
2003-01-06 02:48:14 +00:00
|
|
|
{
|
|
|
|
|
p++;
|
2005-12-28 06:07:17 +00:00
|
|
|
iNumDigits++;
|
2003-01-06 02:48:14 +00:00
|
|
|
}
|
2005-12-28 06:07:17 +00:00
|
|
|
|
|
|
|
|
if( !iNumDigits )
|
|
|
|
|
continue; /* must have at least one digit */
|
|
|
|
|
if( p >= sText.size() || sText[p] != ';' )
|
|
|
|
|
continue;
|
2003-01-06 02:48:14 +00:00
|
|
|
p++;
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
int iNum;
|
|
|
|
|
if( bHex )
|
|
|
|
|
sscanf( sText.c_str()+iPos, "&x%x;", &iNum );
|
|
|
|
|
else
|
|
|
|
|
sscanf( sText.c_str()+iPos, "&#%i;", &iNum );
|
|
|
|
|
if( iNum > 0xFFFF )
|
|
|
|
|
iNum = INVALID_CHAR;
|
2003-01-28 00:45:01 +00:00
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
sText.replace( iPos, p-iPos, WcharToUTF8(wchar_t(iNum)) );
|
2003-01-06 02:48:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-09 23:53:14 +00:00
|
|
|
/* Form a string to identify a wchar_t with ASCII. */
|
2005-12-21 07:52:23 +00:00
|
|
|
RString WcharDisplayText( wchar_t c )
|
2003-01-09 23:53:14 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
RString sChr;
|
|
|
|
|
sChr = ssprintf( "U+%4.4x", c );
|
|
|
|
|
if( c < 128 )
|
|
|
|
|
sChr += ssprintf( " ('%c')", char(c) );
|
|
|
|
|
return sChr;
|
2003-01-09 23:53:14 +00:00
|
|
|
}
|
2003-01-23 04:22:07 +00:00
|
|
|
|
|
|
|
|
/* Return the last named component of dir:
|
|
|
|
|
* a/b/c -> c
|
|
|
|
|
* a/b/c/ -> c
|
|
|
|
|
*/
|
2005-12-21 07:52:23 +00:00
|
|
|
RString Basename( const RString &sDir )
|
2003-01-23 04:22:07 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
size_t iEnd = sDir.find_last_not_of( "/\\" );
|
|
|
|
|
if( iEnd == sDir.npos )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
2003-10-29 20:18:04 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
size_t iStart = sDir.find_last_of( "/\\", iEnd );
|
|
|
|
|
if( iStart == sDir.npos )
|
|
|
|
|
iStart = 0;
|
2003-10-29 20:18:04 +00:00
|
|
|
else
|
2005-12-21 07:52:23 +00:00
|
|
|
++iStart;
|
2003-10-29 20:18:04 +00:00
|
|
|
|
2005-12-21 07:52:23 +00:00
|
|
|
return sDir.substr( iStart, iEnd-iStart+1 );
|
2003-10-29 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-28 06:07:17 +00:00
|
|
|
/*
|
|
|
|
|
* Return all but the last named component of dir:
|
|
|
|
|
*
|
2003-10-29 20:18:04 +00:00
|
|
|
* a/b/c -> a/b/
|
|
|
|
|
* a/b/c/ -> a/b/
|
|
|
|
|
* c/ -> ./
|
|
|
|
|
* /foo -> /
|
|
|
|
|
* / -> /
|
|
|
|
|
*/
|
2005-12-20 08:35:47 +00:00
|
|
|
RString Dirname( const RString &dir )
|
2003-10-29 20:18:04 +00:00
|
|
|
{
|
2006-01-20 06:07:37 +00:00
|
|
|
/* Special case: "/" -> "/". */
|
|
|
|
|
if( dir.size() == 1 && dir[0] == '/' )
|
|
|
|
|
return "/";
|
2003-10-29 20:18:04 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
int pos = dir.size()-1;
|
|
|
|
|
/* Skip trailing slashes. */
|
|
|
|
|
while( pos >= 0 && dir[pos] == '/' )
|
|
|
|
|
--pos;
|
2003-10-29 20:18:04 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
/* Skip the last component. */
|
|
|
|
|
while( pos >= 0 && dir[pos] != '/' )
|
|
|
|
|
--pos;
|
2003-01-23 04:22:07 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
if( pos < 0 )
|
|
|
|
|
return "./";
|
2003-01-23 04:22:07 +00:00
|
|
|
|
2006-01-20 06:07:37 +00:00
|
|
|
return dir.substr(0, pos+1);
|
2003-01-23 04:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString Capitalize( const RString &s )
|
2003-04-13 04:50:08 +00:00
|
|
|
{
|
2005-12-21 07:52:23 +00:00
|
|
|
if( s.empty() )
|
2005-12-20 08:35:47 +00:00
|
|
|
return RString();
|
|
|
|
|
RString s2 = s;
|
2004-01-03 04:30:34 +00:00
|
|
|
/* XXX: utf-8 */
|
2004-01-27 21:03:43 +00:00
|
|
|
if( !(s2[0] & 0x80) )
|
2004-01-03 04:30:34 +00:00
|
|
|
s2[0] = (char) toupper( s2[0] );
|
2004-01-02 08:43:14 +00:00
|
|
|
return s2;
|
2003-04-13 04:50:08 +00:00
|
|
|
}
|
2003-10-20 03:57:02 +00:00
|
|
|
|
|
|
|
|
char char_traits_char_nocase::uptab[256] =
|
|
|
|
|
{
|
|
|
|
|
'\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0A','\x0B','\x0C','\x0D','\x0E','\x0F',
|
|
|
|
|
'\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1A','\x1B','\x1C','\x1D','\x1E','\x1F',
|
|
|
|
|
'\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2A','\x2B','\x2C','\x2D','\x2E','\x2F',
|
|
|
|
|
'\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3A','\x3B','\x3C','\x3D','\x3E','\x3F',
|
|
|
|
|
'\x40','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4A','\x4B','\x4C','\x4D','\x4E','\x4F',
|
|
|
|
|
'\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5A','\x5B','\x5C','\x5D','\x5E','\x5F',
|
|
|
|
|
'\x60','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4A','\x4B','\x4C','\x4D','\x4E','\x4F',
|
|
|
|
|
'\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5A','\x7B','\x7C','\x7D','\x7E','\x7F',
|
|
|
|
|
'\x80','\x81','\x82','\x83','\x84','\x85','\x86','\x87','\x88','\x89','\x8A','\x8B','\x8C','\x8D','\x8E','\x8F',
|
|
|
|
|
'\x90','\x91','\x92','\x93','\x94','\x95','\x96','\x97','\x98','\x99','\x9A','\x9B','\x9C','\x9D','\x9E','\x9F',
|
|
|
|
|
'\xA0','\xA1','\xA2','\xA3','\xA4','\xA5','\xA6','\xA7','\xA8','\xA9','\xAA','\xAB','\xAC','\xAD','\xAE','\xAF',
|
|
|
|
|
'\xB0','\xB1','\xB2','\xB3','\xB4','\xB5','\xB6','\xB7','\xB8','\xB9','\xBA','\xBB','\xBC','\xBD','\xBE','\xBF',
|
|
|
|
|
'\xC0','\xC1','\xC2','\xC3','\xC4','\xC5','\xC6','\xC7','\xC8','\xC9','\xCA','\xCB','\xCC','\xCD','\xCE','\xCF',
|
|
|
|
|
'\xD0','\xD1','\xD2','\xD3','\xD4','\xD5','\xD6','\xD7','\xD8','\xD9','\xDA','\xDB','\xDC','\xDD','\xDE','\xDF',
|
|
|
|
|
'\xE0','\xE1','\xE2','\xE3','\xE4','\xE5','\xE6','\xE7','\xE8','\xE9','\xEA','\xEB','\xEC','\xED','\xEE','\xEF',
|
|
|
|
|
'\xF0','\xF1','\xF2','\xF3','\xF4','\xF5','\xF6','\xF7','\xF8','\xF9','\xFA','\xFB','\xFC','\xFD','\xFE','\xFF',
|
|
|
|
|
};
|
2003-12-08 04:02:43 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void FixSlashesInPlace( RString &sPath )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
|
|
|
|
for( unsigned i = 0; i < sPath.size(); ++i )
|
|
|
|
|
if( sPath[i] == '\\' )
|
|
|
|
|
sPath[i] = '/';
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString FixSlashes( RString sPath )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
|
|
|
|
FixSlashesInPlace( sPath );
|
2006-01-20 06:07:37 +00:00
|
|
|
return sPath;
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Keep trailing slashes, since that can be used to illustrate that a path always
|
2004-02-27 05:50:57 +00:00
|
|
|
* represents a directory.
|
2003-12-21 02:20:53 +00:00
|
|
|
*
|
|
|
|
|
* foo/bar -> foo/bar
|
|
|
|
|
* foo/bar/ -> foo/bar/
|
|
|
|
|
* foo///bar/// -> foo/bar/
|
|
|
|
|
* foo/bar/./baz -> foo/bar/baz
|
|
|
|
|
* foo/bar/../baz -> foo/baz
|
2004-04-06 04:21:06 +00:00
|
|
|
* ../foo -> ../foo
|
|
|
|
|
* ../../foo -> ../../foo
|
2004-02-27 05:50:57 +00:00
|
|
|
* ./foo -> foo (if bRemoveLeadingDot), ./foo (if !bRemoveLeadingDot)
|
2005-08-29 04:45:59 +00:00
|
|
|
* ./ -> ./
|
|
|
|
|
* ./// -> ./
|
2003-12-21 02:20:53 +00:00
|
|
|
*/
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
void CollapsePath( RString &sPath, bool bRemoveLeadingDot )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sOut;
|
2005-07-26 05:26:14 +00:00
|
|
|
sOut.reserve( sPath.size() );
|
2003-12-21 02:20:53 +00:00
|
|
|
|
2005-07-26 05:26:14 +00:00
|
|
|
size_t iPos = 0;
|
|
|
|
|
size_t iNext;
|
|
|
|
|
for( ; iPos < sPath.size(); iPos = iNext )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2005-07-26 05:26:14 +00:00
|
|
|
/* Find the next slash. */
|
|
|
|
|
iNext = sPath.find( '/', iPos );
|
2005-12-20 08:35:47 +00:00
|
|
|
if( iNext == RString::npos )
|
2005-07-26 05:26:14 +00:00
|
|
|
iNext = sPath.size();
|
|
|
|
|
else
|
|
|
|
|
++iNext;
|
|
|
|
|
|
|
|
|
|
/* Strip extra slashes, but don't remove slashes from the beginning of the string. */
|
|
|
|
|
if( iNext - iPos == 1 && sPath[iPos] == '/' )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2005-07-26 05:26:14 +00:00
|
|
|
if( !sOut.empty() )
|
|
|
|
|
continue;
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
2005-07-26 05:26:14 +00:00
|
|
|
|
|
|
|
|
/* If this is a dot, skip it. */
|
|
|
|
|
if( iNext - iPos == 2 && sPath[iPos] == '.' && sPath[iPos+1] == '/' )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2005-07-26 05:26:14 +00:00
|
|
|
if( bRemoveLeadingDot || !sOut.empty() )
|
|
|
|
|
continue;
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
2005-07-26 05:26:14 +00:00
|
|
|
|
|
|
|
|
/* If this is two dots, */
|
|
|
|
|
if( iNext - iPos == 3 && sPath[iPos] == '.' && sPath[iPos+1] == '.' && sPath[iPos+2] == '/' )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2005-07-26 05:26:14 +00:00
|
|
|
/* If this is the first path element (nothing to delete), or all we have is a slash,
|
|
|
|
|
* leave it. */
|
|
|
|
|
if( sOut.empty() || (sOut.size() == 1 && sOut[0] == '/') )
|
|
|
|
|
{
|
|
|
|
|
sOut.append( sPath, iPos, iNext-iPos );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search backwards for the previous path element. */
|
|
|
|
|
size_t iPrev = sOut.rfind( '/', sOut.size()-2 );
|
2005-12-20 08:35:47 +00:00
|
|
|
if( iPrev == RString::npos )
|
2005-07-26 05:26:14 +00:00
|
|
|
iPrev = 0;
|
|
|
|
|
else
|
|
|
|
|
++iPrev;
|
|
|
|
|
|
|
|
|
|
/* If the previous element is also .., leave it. */
|
|
|
|
|
bool bLastIsTwoDots = (sOut.size() - iPrev == 3 && sOut[iPrev] == '.' && sOut[iPrev+1] == '.' );
|
|
|
|
|
if( bLastIsTwoDots )
|
|
|
|
|
{
|
|
|
|
|
sOut.append( sPath, iPos, iNext-iPos );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sOut.erase( iPrev );
|
|
|
|
|
continue;
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
2005-07-26 05:26:14 +00:00
|
|
|
|
|
|
|
|
sOut.append( sPath, iPos, iNext-iPos );
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
2005-07-26 05:26:14 +00:00
|
|
|
|
|
|
|
|
sOut.swap( sPath );
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-08 04:02:43 +00:00
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool FromString( const RString &sValue, int &out )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
if( sscanf( sValue.c_str(), "%d", &out ) == 1 )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
out = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool FromString( const RString &sValue, unsigned &out )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
if( sscanf( sValue.c_str(), "%u", &out ) == 1 )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
out = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool FromString( const RString &sValue, float &out )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
const char *endptr = sValue.data() + sValue.size();
|
|
|
|
|
out = strtof( sValue, (char **) &endptr );
|
|
|
|
|
return endptr != sValue.data();
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool FromString( const RString &sValue, bool &out )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
if( sValue.size() == 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
out = (atoi(sValue) != 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ToString( int value )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
return ssprintf( "%i", value );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ToString( unsigned value )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
return ssprintf( "%u", value );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ToString( float value )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
return ssprintf( "%f", value );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString ToString( bool value )
|
2004-12-07 01:43:06 +00:00
|
|
|
{
|
|
|
|
|
return ssprintf( "%i", value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-12-08 04:02:43 +00:00
|
|
|
//
|
|
|
|
|
// Helper function for reading/writing scores
|
|
|
|
|
//
|
2005-12-23 08:22:21 +00:00
|
|
|
bool FileRead( RageFileBasic& f, RString& sOut )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2005-12-23 08:22:21 +00:00
|
|
|
if( f.AtEOF() )
|
2003-12-08 04:02:43 +00:00
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
if( f.GetLine(sOut) == -1 )
|
|
|
|
|
return false;
|
2003-12-08 04:02:43 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
bool FileRead( RageFileBasic& f, int& iOut )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString s;
|
2005-12-23 08:22:21 +00:00
|
|
|
if( !FileRead(f, s) )
|
2003-12-08 04:02:43 +00:00
|
|
|
return false;
|
|
|
|
|
iOut = atoi(s);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
bool FileRead( RageFileBasic& f, unsigned& uOut )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString s;
|
2005-12-23 08:22:21 +00:00
|
|
|
if( !FileRead(f, s) )
|
2003-12-08 04:02:43 +00:00
|
|
|
return false;
|
|
|
|
|
uOut = atoi(s);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
bool FileRead( RageFileBasic& f, float& fOut )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString s;
|
2005-12-23 08:22:21 +00:00
|
|
|
if( !FileRead(f, s) )
|
2003-12-08 04:02:43 +00:00
|
|
|
return false;
|
2004-08-10 20:57:59 +00:00
|
|
|
fOut = strtof( s, NULL );
|
2003-12-08 04:02:43 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
void FileWrite( RageFileBasic& f, const RString& sWrite )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( sWrite );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
void FileWrite( RageFileBasic& f, int iWrite )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( ssprintf("%d", iWrite) );
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
void FileWrite( RageFileBasic& f, size_t uWrite )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2004-01-27 21:03:43 +00:00
|
|
|
f.PutLine( ssprintf("%i", (int)uWrite) );
|
2003-12-08 04:02:43 +00:00
|
|
|
}
|
|
|
|
|
|
2005-12-23 08:22:21 +00:00
|
|
|
void FileWrite( RageFileBasic& f, float fWrite )
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( ssprintf("%f", fWrite) );
|
|
|
|
|
}
|
2003-12-08 10:27:45 +00:00
|
|
|
|
2006-01-21 12:18:26 +00:00
|
|
|
bool FileCopy( const RString &sSrcFile, const RString &sDstFile )
|
2003-12-08 10:27:45 +00:00
|
|
|
{
|
2004-02-17 22:12:39 +00:00
|
|
|
if( !sSrcFile.CompareNoCase(sDstFile) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Tried to copy \"%s\" over itself", sSrcFile.c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-08 10:27:45 +00:00
|
|
|
RageFile in;
|
|
|
|
|
if( !in.Open(sSrcFile, RageFile::READ) )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
RageFile out;
|
|
|
|
|
if( !out.Open(sDstFile, RageFile::WRITE) )
|
|
|
|
|
return false;
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
RString sError;
|
2005-05-21 06:51:55 +00:00
|
|
|
if( !FileCopy(in, out, sError) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "FileCopy(%s,%s): %s",
|
|
|
|
|
sSrcFile.c_str(), sDstFile.c_str(), sError.c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-20 08:35:47 +00:00
|
|
|
bool FileCopy( RageFileBasic &in, RageFileBasic &out, RString &sError, bool *bReadError )
|
2005-05-21 06:51:55 +00:00
|
|
|
{
|
2005-04-25 21:05:29 +00:00
|
|
|
while(1)
|
|
|
|
|
{
|
2005-12-20 08:35:47 +00:00
|
|
|
RString data;
|
2005-04-25 21:05:29 +00:00
|
|
|
if( in.Read(data, 1024*32) == -1 )
|
|
|
|
|
{
|
2005-05-21 06:51:55 +00:00
|
|
|
sError = ssprintf( "read error: %s", in.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = true;
|
2005-04-25 21:05:29 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if( data.empty() )
|
|
|
|
|
break;
|
|
|
|
|
int i = out.Write(data);
|
|
|
|
|
if( i == -1 )
|
|
|
|
|
{
|
2005-05-21 06:51:55 +00:00
|
|
|
sError = ssprintf( "write error: %s", out.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = false;
|
2005-04-25 21:05:29 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-05-21 06:51:55 +00:00
|
|
|
|
2003-12-21 09:18:06 +00:00
|
|
|
if( out.Flush() == -1 )
|
2005-05-21 06:51:55 +00:00
|
|
|
{
|
|
|
|
|
sError = ssprintf( "write error: %s", out.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = false;
|
2003-12-21 09:18:06 +00:00
|
|
|
return false;
|
2005-05-21 06:51:55 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-21 09:18:06 +00:00
|
|
|
return true;
|
2003-12-08 10:27:45 +00:00
|
|
|
}
|
2004-05-06 02:40:33 +00:00
|
|
|
|
2005-12-31 06:52:04 +00:00
|
|
|
LuaFunction( SecondsToMSSMsMs, SecondsToMSSMsMs( FArg(1) ) )
|
|
|
|
|
LuaFunction( FormatNumberAndSuffix, FormatNumberAndSuffix( IArg(1) ) )
|
|
|
|
|
LuaFunction( Basename, Basename( SArg(1) ) )
|
|
|
|
|
|
2004-05-06 02:40:33 +00:00
|
|
|
/*
|
2005-11-22 21:18:43 +00:00
|
|
|
* Copyright (c) 2001-2005 Chris Danford, Glenn Maynard
|
2004-05-06 02:40:33 +00:00
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|