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