1 /*
2    Copyright (c) 2001, Loki software, inc.
3    All rights reserved.
4 
5    Redistribution and use in source and binary forms, with or without modification,
6    are permitted provided that the following conditions are met:
7 
8    Redistributions of source code must retain the above copyright notice, this list
9    of conditions and the following disclaimer.
10 
11    Redistributions in binary form must reproduce the above copyright notice, this
12    list of conditions and the following disclaimer in the documentation and/or
13    other materials provided with the distribution.
14 
15    Neither the name of Loki software nor the names of its contributors may be used
16    to endorse or promote products derived from this software without specific prior
17    written permission.
18 
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22    DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23    DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifndef __STR__
32 #define __STR__
33 
34 //
35 // class Str
36 // loose replacement for CString from MFC
37 //
38 
39 #include <string.h>
40 #include <ctype.h>
41 
42 #include <stdio.h>
43 #include <stdarg.h>
44 
45 #include <cstdio>
46 
47 #ifdef _MSC_VER
48 #define strcasecmp strcmpi
49 #if _MSC_VER < 1400
50 #define vsnprintf std::vsnprintf
51 #endif
52 #else
53 #include <cstddef>
54 #endif
55 
56 // NOTE TTimo __StrDup was initially implemented in pakstuff.cpp
57 //   causing a bunch of issues for broader targets that use Str.h (such as plugins and modules)
58 //   Q_StrDup should be used now, using a #define __StrDup for easy transition
59 
60 #define __StrDup Q_StrDup
61 
Q_StrDup(const char * pStr)62 inline char* Q_StrDup( const char* pStr ){
63 	if ( pStr == 0 ) {
64 		pStr = "";
65 	}
66 
67 	return strcpy( new char[strlen( pStr ) + 1], pStr );
68 }
69 
70 #if !defined( WIN32 )
71 #define strcmpi strcasecmp
72 #define stricmp strcasecmp
73 #define strnicmp strncasecmp
74 
strlwr(char * string)75 inline char* strlwr( char* string ){
76 	char *cp;
77 	for ( cp = string; *cp; ++cp )
78 	{
79 		if ( 'A' <= *cp && *cp <= 'Z' ) {
80 			*cp += 'a' - 'A';
81 		}
82 	}
83 
84 	return string;
85 }
86 
strupr(char * string)87 inline char* strupr( char* string ){
88 	char *cp;
89 	for ( cp = string; *cp; ++cp )
90 	{
91 		if ( 'a' <= *cp && *cp <= 'z' ) {
92 			*cp += 'A' - 'a';
93 		}
94 	}
95 
96 	return string;
97 }
98 #endif
99 
100 static char *g_pStrWork = 0;
101 
102 class Str
103 {
104 protected:
105 bool m_bIgnoreCase;
106 char *m_pStr;
107 
108 public:
Str()109 Str(){
110 	m_bIgnoreCase = true;
111 	m_pStr = new char[1];
112 	m_pStr[0] = '\0';
113 }
114 
Str(char * p)115 Str( char *p ){
116 	m_bIgnoreCase = true;
117 	m_pStr = __StrDup( p );
118 }
119 
Str(const char * p)120 Str( const char *p ){
121 	m_bIgnoreCase = true;
122 	m_pStr = __StrDup( p );
123 }
124 
Str(const unsigned char * p)125 Str( const unsigned char *p ){
126 	m_bIgnoreCase = true;
127 	m_pStr = __StrDup( reinterpret_cast<const char *>( p ) );
128 }
129 
Str(const char c)130 Str( const char c ){
131 	m_bIgnoreCase = true;
132 	m_pStr = new char[2];
133 	m_pStr[0] = c;
134 	m_pStr[1] = '\0';
135 }
136 
GetBuffer()137 const char* GetBuffer() const {
138 	return m_pStr;
139 }
140 
GetBuffer()141 char* GetBuffer(){
142 	return m_pStr;
143 }
144 
Str(const Str & s)145 Str( const Str &s ){
146 	m_bIgnoreCase = true;
147 	m_pStr = __StrDup( s.GetBuffer() );
148 }
149 
Deallocate()150 void Deallocate(){
151 	delete []m_pStr;
152 	m_pStr = 0;
153 }
154 
Allocate(std::size_t n)155 void Allocate( std::size_t n ){
156 	Deallocate();
157 	m_pStr = new char[n];
158 }
159 
MakeEmpty()160 void MakeEmpty(){
161 	Deallocate();
162 	m_pStr = __StrDup( "" );
163 }
164 
~Str()165 ~Str(){
166 	Deallocate();
167 	// NOTE TTimo: someone explain this g_pStrWork to me?
168 	if ( g_pStrWork ) {
169 		delete []g_pStrWork;
170 	}
171 	g_pStrWork = 0;
172 }
173 
MakeLower()174 void MakeLower(){
175 	if ( m_pStr ) {
176 		strlwr( m_pStr );
177 	}
178 }
179 
MakeUpper()180 void MakeUpper(){
181 	if ( m_pStr ) {
182 		strupr( m_pStr );
183 	}
184 }
185 
TrimRight()186 void TrimRight(){
187 	char* lpsz = m_pStr;
188 	char* lpszLast = 0;
189 	while ( *lpsz != '\0' )
190 	{
191 		if ( isspace( *lpsz ) ) {
192 			if ( lpszLast == 0 ) {
193 				lpszLast = lpsz;
194 			}
195 		}
196 		else{
197 			lpszLast = 0;
198 		}
199 		lpsz++;
200 	}
201 
202 	if ( lpszLast != 0 ) {
203 		// truncate at trailing space start
204 		*lpszLast = '\0';
205 	}
206 }
207 
TrimLeft()208 void TrimLeft(){
209 	// find first non-space character
210 	char* lpsz = m_pStr;
211 	while ( isspace( *lpsz ) )
212 		lpsz++;
213 
214 	// fix up data and length
215 	std::size_t nDataLength = GetLength() - ( lpsz - m_pStr );
216 	memmove( m_pStr, lpsz, ( nDataLength + 1 ) );
217 }
218 
Find(const char * p)219 char* Find( const char *p ){
220 	return strstr( m_pStr, p );
221 }
222 
223 // search starting at a given offset
Find(const char * p,std::size_t offset)224 char* Find( const char *p, std::size_t offset ){
225 	return strstr( m_pStr + offset, p );
226 }
227 
Find(const char ch)228 char* Find( const char ch ){
229 	return strchr( m_pStr, ch );
230 }
231 
ReverseFind(const char ch)232 char* ReverseFind( const char ch ){
233 	return strrchr( m_pStr, ch );
234 }
235 
Compare(const char * str)236 int Compare( const char* str ) const {
237 	return strcmp( m_pStr, str );
238 }
239 
CompareNoCase(const char * str)240 int CompareNoCase( const char* str ) const {
241 	return strcasecmp( m_pStr, str );
242 }
243 
GetLength()244 std::size_t GetLength(){
245 	return ( m_pStr ) ? strlen( m_pStr ) : 0;
246 }
247 
Left(std::size_t n)248 const char* Left( std::size_t n ){
249 	delete []g_pStrWork;
250 	if ( n > 0 ) {
251 		g_pStrWork = new char[n + 1];
252 		strncpy( g_pStrWork, m_pStr, n );
253 		g_pStrWork[n] = '\0';
254 	}
255 	else
256 	{
257 		g_pStrWork = "";
258 		g_pStrWork = new char[1];
259 		g_pStrWork[0] = '\0';
260 	}
261 	return g_pStrWork;
262 }
263 
Right(std::size_t n)264 const char* Right( std::size_t n ){
265 	delete []g_pStrWork;
266 	if ( n > 0 ) {
267 		g_pStrWork = new char[n + 1];
268 		std::size_t nStart = GetLength() - n;
269 		strncpy( g_pStrWork, &m_pStr[nStart], n );
270 		g_pStrWork[n] = '\0';
271 	}
272 	else
273 	{
274 		g_pStrWork = new char[1];
275 		g_pStrWork[0] = '\0';
276 	}
277 	return g_pStrWork;
278 }
279 
Mid(std::size_t nFirst)280 const char* Mid( std::size_t nFirst ) const {
281 	return Mid( nFirst, strlen( m_pStr ) - nFirst );
282 }
283 
Mid(std::size_t first,std::size_t n)284 const char* Mid( std::size_t first, std::size_t n ) const {
285 	delete []g_pStrWork;
286 	if ( n > 0 ) {
287 		g_pStrWork = new char[n + 1];
288 		strncpy( g_pStrWork, m_pStr + first, n );
289 		g_pStrWork[n] = '\0';
290 	}
291 	else
292 	{
293 		g_pStrWork = "";
294 		g_pStrWork = new char[1];
295 		g_pStrWork[0] = '\0';
296 	}
297 	return g_pStrWork;
298 }
299 
300 #if 0 // defined(__G_LIB_H__)
301 void Format( const char* fmt, ... ){
302 	va_list args;
303 	char *buffer;
304 
305 	va_start( args, fmt );
306 	buffer = g_strdup_vprintf( fmt, args );
307 	va_end( args );
308 
309 	delete[] m_pStr;
310 	m_pStr = __StrDup( buffer );
311 	g_free( buffer );
312 }
313 #else
Format(const char * fmt,...)314 void Format( const char* fmt, ... ){
315 	char buffer[1024];
316 
317 	{
318 		va_list args;
319 		va_start( args, fmt );
320 		vsnprintf( buffer, 1023, fmt, args );
321 		va_end( args );
322 	}
323 
324 	delete[] m_pStr;
325 	m_pStr = __StrDup( buffer );
326 }
327 #endif
328 
SetAt(std::size_t n,char ch)329 void SetAt( std::size_t n, char ch ){
330 	if ( n < GetLength() ) {
331 		m_pStr[n] = ch;
332 	}
333 }
334 
335 // NOTE: unlike CString, this looses the pointer
ReleaseBuffer(std::size_t n)336 void ReleaseBuffer( std::size_t n ){
337 	char* tmp = m_pStr;
338 	tmp[n] = '\0';
339 	m_pStr = __StrDup( tmp );
340 	delete []tmp;
341 }
ReleaseBuffer()342 void ReleaseBuffer(){
343 	ReleaseBuffer( GetLength() );
344 }
345 
GetBufferSetLength(std::size_t n)346 char* GetBufferSetLength( std::size_t n ){
347 	char *p = new char[n + 1];
348 	strncpy( p, m_pStr, n );
349 	p[n] = '\0';
350 	delete []m_pStr;
351 	m_pStr = p;
352 	return m_pStr;
353 }
354 
355 //  char& operator *() { return *m_pStr; }
356 //  char& operator *() const { return *const_cast<Str*>(this)->m_pStr; }
357 operator void*() {
358 	return m_pStr;
359 }
360 operator char*() {
361 	return m_pStr;
362 }
363 operator const char*() const { return reinterpret_cast<const char*>( m_pStr ); }
364 operator unsigned char*() {
365 	return reinterpret_cast<unsigned char*>( m_pStr );
366 }
367 operator const unsigned char*() const { return reinterpret_cast<const unsigned char*>( m_pStr ); }
368 Str& operator =( const Str& rhs ){
369 	if ( &rhs != this ) {
370 		delete[] m_pStr;
371 		m_pStr = __StrDup( rhs.m_pStr );
372 	}
373 	return *this;
374 }
375 
376 Str& operator =( const char* pStr ){
377 	if ( m_pStr != pStr ) {
378 		delete[] m_pStr;
379 		m_pStr = __StrDup( pStr );
380 	}
381 	return *this;
382 }
383 
384 Str& operator +=( const char ch ){
385 	std::size_t len = GetLength();
386 	char *p = new char[len + 1 + 1];
387 
388 	if ( m_pStr ) {
389 		strcpy( p, m_pStr );
390 		delete[] m_pStr;
391 	}
392 
393 	m_pStr = p;
394 	m_pStr[len] = ch;
395 	m_pStr[len + 1] = '\0';
396 
397 	return *this;
398 }
399 
400 Str& operator +=( const char *pStr ){
401 	if ( pStr ) {
402 		if ( m_pStr ) {
403 			char *p = new char[strlen( m_pStr ) + strlen( pStr ) + 1];
404 			strcpy( p, m_pStr );
405 			strcat( p, pStr );
406 			delete[] m_pStr;
407 			m_pStr = p;
408 		}
409 		else
410 		{
411 			m_pStr = __StrDup( pStr );
412 		}
413 	}
414 	return *this;
415 }
416 
417 
418 bool operator ==( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) == 0 : strcmp( m_pStr, rhs.m_pStr ) == 0; }
419 bool operator ==( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) == 0 : strcmp( m_pStr, pStr ) == 0; }
420 bool operator ==( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) == 0 : strcmp( m_pStr, pStr ) == 0; }
421 bool operator !=( Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) != 0 : strcmp( m_pStr, rhs.m_pStr ) != 0; }
422 bool operator !=( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) != 0 : strcmp( m_pStr, pStr ) != 0; }
423 bool operator !=( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) != 0 : strcmp( m_pStr, pStr ) != 0; }
424 bool operator <( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) < 0 : strcmp( m_pStr, rhs.m_pStr ) < 0; }
425 bool operator <( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) < 0 : strcmp( m_pStr, pStr ) < 0; }
426 bool operator <( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) < 0 : strcmp( m_pStr, pStr ) < 0; }
427 bool operator >( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) > 0 : strcmp( m_pStr, rhs.m_pStr ) > 0; }
428 bool operator >( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) > 0 : strcmp( m_pStr, pStr ) > 0; }
429 bool operator >( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) > 0 : strcmp( m_pStr, pStr ) > 0; }
430 char& operator []( std::size_t nIndex ) { return m_pStr[nIndex]; }
431 const char& operator []( std::size_t nIndex ) const { return m_pStr[nIndex]; }
GetAt(std::size_t nIndex)432 const char GetAt( std::size_t nIndex ) { return m_pStr[nIndex]; }
433 };
434 
435 
436 template<typename TextOutputStreamType>
ostream_write(TextOutputStreamType & ostream,const Str & str)437 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Str& str ){
438 	return ostream << str.GetBuffer();
439 }
440 
441 
AddSlash(Str & strPath)442 inline void AddSlash( Str& strPath ){
443 	if ( strPath.GetLength() > 0 ) {
444 		if ( ( strPath.GetAt( strPath.GetLength() - 1 ) != '/' ) &&
445 			 ( strPath.GetAt( strPath.GetLength() - 1 ) != '\\' ) ) {
446 			strPath += '/';
447 		}
448 	}
449 }
450 
ExtractPath_and_Filename(const char * pPath,Str & strPath,Str & strFilename)451 inline bool ExtractPath_and_Filename( const char* pPath, Str& strPath, Str& strFilename ){
452 	Str strPathName;
453 	strPathName = pPath;
454 	const char* substr = strPathName.ReverseFind( '\\' );
455 	if ( substr == 0 ) {
456 		// TTimo: try forward slash, some are using forward
457 		substr = strPathName.ReverseFind( '/' );
458 	}
459 	if ( substr != 0 ) {
460 		std::size_t nSlash = substr - strPathName.GetBuffer();
461 		strPath = strPathName.Left( nSlash + 1 );
462 		strFilename = strPathName.Right( strPathName.GetLength() - nSlash - 1 );
463 	}
464 	else{
465 		strFilename = pPath;
466 	}
467 	return true;
468 }
469 
470 
471 
472 #endif
473