mirror of
https://github.com/UberGames/EF2GameSource.git
synced 2024-11-14 16:30:34 +00:00
547 lines
10 KiB
C++
547 lines
10 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /EF2/Code/DLLs/game/str.cpp $
|
|
// $Revision:: 9 $
|
|
// $Author:: Singlis $
|
|
// $Date:: 9/26/03 4:08p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
//
|
|
// DESCRIPTION:
|
|
// Simple, DLL portable string class
|
|
//
|
|
// WARNING: This file is shared between game, cgame and possibly the user interface.
|
|
// It is instanced in each one of these directories because of the way that SourceSafe works.
|
|
//
|
|
|
|
#include "str.h"
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <game/q_shared.h>
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(disable : 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
|
|
#pragma warning(disable : 4710) // function 'blah' not inlined
|
|
#endif
|
|
|
|
#if defined( GAME_DLL )
|
|
|
|
#include "g_local.h"
|
|
#define STR_Error gi.Error
|
|
|
|
#elif defined ( CGAME_DLL )
|
|
|
|
#include "cg_local.h"
|
|
#define STR_Error cgi.Error
|
|
|
|
#else
|
|
|
|
//#include "client.h"
|
|
#define STR_Error Com_Error
|
|
|
|
#endif
|
|
|
|
static const int STR_ALLOC_GRAN = 20;
|
|
|
|
char *str::tolower( char *s1 )
|
|
{
|
|
char *s;
|
|
|
|
s = s1;
|
|
while( *s )
|
|
{
|
|
*s = ::tolower( *s );
|
|
s++;
|
|
}
|
|
|
|
return s1;
|
|
}
|
|
|
|
char *str::toupper( char *s1 )
|
|
{
|
|
char *s;
|
|
|
|
s = s1;
|
|
while( *s )
|
|
{
|
|
*s = ::toupper( *s );
|
|
s++;
|
|
}
|
|
|
|
return s1;
|
|
}
|
|
|
|
int str::icmpn( const char *s1, const char *s2, int n )
|
|
{
|
|
int c1;
|
|
int c2;
|
|
|
|
do
|
|
{
|
|
c1 = *s1++;
|
|
c2 = *s2++;
|
|
|
|
if ( !n-- )
|
|
{
|
|
// strings are equal until end point
|
|
return 0;
|
|
}
|
|
|
|
if ( c1 != c2 )
|
|
{
|
|
if ( c1 >= 'a' && c1 <= 'z' )
|
|
{
|
|
c1 -= ( 'a' - 'A' );
|
|
}
|
|
|
|
if ( c2 >= 'a' && c2 <= 'z' )
|
|
{
|
|
c2 -= ( 'a' - 'A' );
|
|
}
|
|
|
|
if ( c1 < c2 )
|
|
{
|
|
// strings less than
|
|
return -1;
|
|
}
|
|
else if ( c1 > c2 )
|
|
{
|
|
// strings greater than
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
while( c1 );
|
|
|
|
// strings are equal
|
|
return 0;
|
|
}
|
|
|
|
int str::icmp( const char *s1, const char *s2 )
|
|
{
|
|
int c1;
|
|
int c2;
|
|
|
|
do
|
|
{
|
|
c1 = *s1++;
|
|
c2 = *s2++;
|
|
|
|
if ( c1 != c2 )
|
|
{
|
|
if ( c1 >= 'a' && c1 <= 'z' )
|
|
{
|
|
c1 -= ( 'a' - 'A' );
|
|
}
|
|
|
|
if ( c2 >= 'a' && c2 <= 'z' )
|
|
{
|
|
c2 -= ( 'a' - 'A' );
|
|
}
|
|
|
|
if ( c1 < c2 )
|
|
{
|
|
// strings less than
|
|
return -1;
|
|
}
|
|
else if ( c1 > c2 )
|
|
{
|
|
// strings greater than
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
while( c1 );
|
|
|
|
// strings are equal
|
|
return 0;
|
|
}
|
|
|
|
int str::cmpn( const char *s1, const char *s2, int n )
|
|
{
|
|
int c1;
|
|
int c2;
|
|
|
|
do
|
|
{
|
|
c1 = *s1++;
|
|
c2 = *s2++;
|
|
|
|
if ( !n-- )
|
|
{
|
|
// strings are equal until end point
|
|
return 0;
|
|
}
|
|
|
|
if ( c1 < c2 )
|
|
{
|
|
// strings less than
|
|
return -1;
|
|
}
|
|
else if ( c1 > c2 )
|
|
{
|
|
// strings greater than
|
|
return 1;
|
|
}
|
|
}
|
|
while( c1 );
|
|
|
|
// strings are equal
|
|
return 0;
|
|
}
|
|
|
|
int str::cmp( const char *s1, const char *s2 )
|
|
{
|
|
int c1;
|
|
int c2;
|
|
|
|
do
|
|
{
|
|
c1 = *s1++;
|
|
c2 = *s2++;
|
|
|
|
if ( c1 < c2 )
|
|
{
|
|
// strings less than
|
|
return -1;
|
|
}
|
|
else if ( c1 > c2 )
|
|
{
|
|
// strings greater than
|
|
return 1;
|
|
}
|
|
}
|
|
while( c1 );
|
|
|
|
// strings are equal
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
============
|
|
IsNumeric
|
|
|
|
Checks a string to see if it contains only numerical values.
|
|
============
|
|
*/
|
|
bool str::isNumeric( const char *str )
|
|
{
|
|
int len;
|
|
int i;
|
|
bool dot;
|
|
|
|
if ( *str == '-' )
|
|
{
|
|
str++;
|
|
}
|
|
|
|
dot = false;
|
|
len = strlen( str );
|
|
for( i = 0; i < len; i++ )
|
|
{
|
|
if ( !isdigit( str[ i ] ) )
|
|
{
|
|
if ( ( str[ i ] == '.' ) && !dot )
|
|
{
|
|
dot = true;
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
str operator+( const str& a, const float b )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
str result( a );
|
|
|
|
sprintf( text, "%f", b );
|
|
result.append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
str operator+( const str& a, const int b )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
str result( a );
|
|
|
|
sprintf( text, "%d", b );
|
|
result.append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
str operator+( const str& a, const unsigned b )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
str result( a );
|
|
|
|
sprintf( text, "%u", b );
|
|
result.append( text );
|
|
|
|
return result;
|
|
}
|
|
|
|
str& str::operator+=( const float a )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
sprintf( text, "%f", a );
|
|
append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
str& str::operator+=( const int a )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
sprintf( text, "%d", a );
|
|
append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
str& str::operator+=( const unsigned a )
|
|
{
|
|
char text[ 20 ];
|
|
|
|
sprintf( text, "%u", a );
|
|
append( text );
|
|
|
|
return *this;
|
|
}
|
|
|
|
void str::CapLength( int newlen )
|
|
{
|
|
assert ( data );
|
|
|
|
if ( length() <= newlen )
|
|
return;
|
|
|
|
data[newlen] = 0;
|
|
len = newlen;
|
|
}
|
|
|
|
void str::EnsureAlloced( int amount, bool keepold )
|
|
{
|
|
if ( !data )
|
|
data = buffer;
|
|
|
|
char *newbuffer;
|
|
bool wasalloced = ( alloced != 0 );
|
|
int old_size;
|
|
|
|
// See if enough already allocated
|
|
|
|
if ( amount <= alloced )
|
|
return;
|
|
|
|
old_size = alloced;
|
|
|
|
assert ( amount );
|
|
|
|
// Calculate new alloc size
|
|
|
|
int newsize, mod;
|
|
|
|
mod = amount % STR_ALLOC_GRAN;
|
|
|
|
if ( !mod )
|
|
newsize = amount;
|
|
else
|
|
newsize = amount + STR_ALLOC_GRAN - mod;
|
|
|
|
alloced = newsize;
|
|
|
|
// Allocate the new buffer
|
|
|
|
newbuffer = new char[ alloced ];
|
|
|
|
if ( !newbuffer )
|
|
STR_Error( ERR_FATAL, "Str: failed on allocation of %i bytes", alloced );
|
|
|
|
if ( wasalloced && keepold )
|
|
{
|
|
strncpy ( newbuffer, data, old_size );
|
|
}
|
|
|
|
// Delete old buffer if necessary
|
|
|
|
if ( data != buffer )
|
|
{
|
|
delete [] data;
|
|
}
|
|
|
|
data = newbuffer;
|
|
}
|
|
|
|
void str::BackSlashesToSlashes( void )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0 ; i < len ; i++ )
|
|
{
|
|
if ( data[i] == '\\' )
|
|
data[i] = '/';
|
|
}
|
|
}
|
|
|
|
void str::snprintf( char *dst, int size, const char *fmt, ... )
|
|
{
|
|
char buffer[0x10000];
|
|
int len;
|
|
va_list argptr;
|
|
|
|
va_start (argptr,fmt);
|
|
len = vsprintf (buffer,fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
assert ( len < size );
|
|
|
|
strncpy (dst, buffer, size-1);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(disable : 4189) // local variable is initialized but not referenced
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
TestStringClass
|
|
|
|
This is a fairly rigorous test of the str class's functionality.
|
|
Because of the fairly global and subtle ramifications of a bug occuring
|
|
in this class, it should be run after any changes to the class.
|
|
Add more tests as functionality is changed. Tests should include
|
|
any possible bounds violation and NULL data tests.
|
|
=================
|
|
*/
|
|
void TestStringClass( void )
|
|
{
|
|
char ch; // ch == ?
|
|
str *t; // t == ?
|
|
str a; // a.len == 0, a.data == "\0"
|
|
str b; // b.len == 0, b.data == "\0"
|
|
str c( "test" ); // c.len == 4, c.data == "test\0"
|
|
str d( c ); // d.len == 4, d.data == "test\0"
|
|
str e( reinterpret_cast<const char *>(NULL) );
|
|
// e.len == 0, e.data == "\0" ASSERT!
|
|
int i; // i == ?
|
|
|
|
i = a.length(); // i == 0
|
|
i = c.length(); // i == 4
|
|
|
|
const char *s1 = a.c_str(); // s1 == "\0"
|
|
const char *s2 = c.c_str(); // s2 == "test\0"
|
|
|
|
t = new str(); // t->len == 0, t->data == "\0"
|
|
delete t; // t == ?
|
|
|
|
b = "test"; // b.len == 4, b.data == "test\0"
|
|
t = new str( "test" ); // t->len == 4, t->data == "test\0"
|
|
delete t; // t == ?
|
|
|
|
a = c; // a.len == 4, a.data == "test\0"
|
|
// a = "";
|
|
a = NULL; // a.len == 0, a.data == "\0" ASSERT!
|
|
a = c + d; // a.len == 8, a.data == "testtest\0"
|
|
a = c + "wow"; // a.len == 7, a.data == "testwow\0"
|
|
a = c + reinterpret_cast<const char *>(NULL);
|
|
// a.len == 4, a.data == "test\0" ASSERT!
|
|
a = "this" + d; // a.len == 8, a.data == "thistest\0"
|
|
a = reinterpret_cast<const char *>(NULL) + d;
|
|
// a.len == 4, a.data == "test\0" ASSERT!
|
|
a += c; // a.len == 8, a.data == "testtest\0"
|
|
a += "wow"; // a.len == 11, a.data == "testtestwow\0"
|
|
a += reinterpret_cast<const char *>(NULL);
|
|
// a.len == 11, a.data == "testtestwow\0" ASSERT!
|
|
|
|
a = "test"; // a.len == 4, a.data == "test\0"
|
|
ch = a[ 0 ]; // ch == 't'
|
|
ch = a[ -1 ]; // ch == 0 ASSERT!
|
|
ch = a[ 1000 ]; // ch == 0 ASSERT!
|
|
ch = a[ 0 ]; // ch == 't'
|
|
ch = a[ 1 ]; // ch == 'e'
|
|
ch = a[ 2 ]; // ch == 's'
|
|
ch = a[ 3 ]; // ch == 't'
|
|
ch = a[ 4 ]; // ch == '\0' ASSERT!
|
|
ch = a[ 5 ]; // ch == '\0' ASSERT!
|
|
|
|
a[ 1 ] = 'b'; // a.len == 4, a.data == "tbst\0"
|
|
a[ -1 ] = 'b'; // a.len == 4, a.data == "tbst\0" ASSERT!
|
|
a[ 0 ] = '0'; // a.len == 4, a.data == "0bst\0"
|
|
a[ 1 ] = '1'; // a.len == 4, a.data == "01st\0"
|
|
a[ 2 ] = '2'; // a.len == 4, a.data == "012t\0"
|
|
a[ 3 ] = '3'; // a.len == 4, a.data == "0123\0"
|
|
a[ 4 ] = '4'; // a.len == 4, a.data == "0123\0" ASSERT!
|
|
a[ 5 ] = '5'; // a.len == 4, a.data == "0123\0" ASSERT!
|
|
a[ 7 ] = '7'; // a.len == 4, a.data == "0123\0" ASSERT!
|
|
|
|
a = "test"; // a.len == 4, a.data == "test\0"
|
|
b = "no"; // b.len == 2, b.data == "no\0"
|
|
|
|
i = ( a == b ); // i == 0
|
|
i = ( a == c ); // i == 1
|
|
|
|
i = ( a == "blow" ); // i == 0
|
|
i = ( a == "test" ); // i == 1
|
|
i = ( a == NULL ); // i == 0 ASSERT!
|
|
|
|
i = ( "test" == b ); // i == 0
|
|
i = ( "test" == a ); // i == 1
|
|
i = ( NULL == a ); // i == 0 ASSERT!
|
|
|
|
i = ( a != b ); // i == 1
|
|
i = ( a != c ); // i == 0
|
|
|
|
i = ( a != "blow" ); // i == 1
|
|
i = ( a != "test" ); // i == 0
|
|
i = ( a != NULL ); // i == 1 ASSERT!
|
|
|
|
i = ( "test" != b ); // i == 1
|
|
i = ( "test" != a ); // i == 0
|
|
i = ( NULL != a ); // i == 1 ASSERT!
|
|
|
|
a = "test"; // a.data == "test"
|
|
b = a; // b.data == "test"
|
|
|
|
a = "not"; // a.data == "not", b.data == "test"
|
|
|
|
a = b; // a.data == b.data == "test"
|
|
|
|
a += b; // a.data == "testtest", b.data = "test"
|
|
|
|
a = b;
|
|
|
|
a[1] = '1'; // a.data = "t1st", b.data = "test"
|
|
|
|
a = ""; // a.data == "", a.len == 0
|
|
|
|
for( i = 0 ; i < ( STRING_PREALLOC_SIZE + 5 ) ; i++ )
|
|
{
|
|
a += "a";
|
|
}
|
|
|
|
t = new str( "testtesttesttesttesttesttesttest" ); // t->len == 32
|
|
delete t; // t == ?
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(default : 4189) // local variable is initialized but not referenced
|
|
#pragma warning(disable : 4514) // unreferenced inline function has been removed
|
|
#endif
|