[q3map2] Unwind script stack in case of script loading error.
[xonotic/netradiant.git] / libs / str.h
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 #include "globaldefs.h"
35
36 //
37 // class Str
38 // loose replacement for CString from MFC
39 //
40
41 #include <string.h>
42 #include <ctype.h>
43
44 #include <stdio.h>
45 #include <stdarg.h>
46
47 #include <cstdio>
48
49 #if GDEF_COMPILER_MSVC
50 #define strcasecmp strcmpi
51 #if _MSC_VER < 1400
52 #define vsnprintf std::vsnprintf
53 #endif
54 #else
55 #include <cstddef>
56 #endif
57
58 // NOTE TTimo __StrDup was initially implemented in pakstuff.cpp
59 //   causing a bunch of issues for broader targets that use Str.h (such as plugins and modules)
60 //   Q_StrDup should be used now, using a #define __StrDup for easy transition
61
62 #define __StrDup Q_StrDup
63
64 inline char* Q_StrDup( const char* pStr ){
65         if ( pStr == 0 ) {
66                 pStr = "";
67         }
68
69         return strcpy( new char[strlen( pStr ) + 1], pStr );
70 }
71
72 #if !GDEF_OS_WINDOWS
73 #define strcmpi strcasecmp
74 #define stricmp strcasecmp
75 #define strnicmp strncasecmp
76
77 inline char* strlwr( char* string ){
78         char *cp;
79         for ( cp = string; *cp; ++cp )
80         {
81                 if ( 'A' <= *cp && *cp <= 'Z' ) {
82                         *cp += 'a' - 'A';
83                 }
84         }
85
86         return string;
87 }
88
89 inline char* strupr( char* string ){
90         char *cp;
91         for ( cp = string; *cp; ++cp )
92         {
93                 if ( 'a' <= *cp && *cp <= 'z' ) {
94                         *cp += 'A' - 'a';
95                 }
96         }
97
98         return string;
99 }
100 #endif
101
102 static char *g_pStrWork = 0;
103
104 class Str
105 {
106 protected:
107 bool m_bIgnoreCase;
108 char *m_pStr;
109
110 public:
111 Str(){
112         m_bIgnoreCase = true;
113         m_pStr = new char[1];
114         m_pStr[0] = '\0';
115 }
116
117 Str( char *p ){
118         m_bIgnoreCase = true;
119         m_pStr = __StrDup( p );
120 }
121
122 Str( const char *p ){
123         m_bIgnoreCase = true;
124         m_pStr = __StrDup( p );
125 }
126
127 Str( const unsigned char *p ){
128         m_bIgnoreCase = true;
129         m_pStr = __StrDup( reinterpret_cast<const char *>( p ) );
130 }
131
132 Str( const char c ){
133         m_bIgnoreCase = true;
134         m_pStr = new char[2];
135         m_pStr[0] = c;
136         m_pStr[1] = '\0';
137 }
138
139 const char* GetBuffer() const {
140         return m_pStr;
141 }
142
143 char* GetBuffer(){
144         return m_pStr;
145 }
146
147 Str( const Str &s ){
148         m_bIgnoreCase = true;
149         m_pStr = __StrDup( s.GetBuffer() );
150 }
151
152 void Deallocate(){
153         delete []m_pStr;
154         m_pStr = 0;
155 }
156
157 void Allocate( std::size_t n ){
158         Deallocate();
159         m_pStr = new char[n];
160 }
161
162 void MakeEmpty(){
163         Deallocate();
164         m_pStr = __StrDup( "" );
165 }
166
167 ~Str(){
168         Deallocate();
169         // NOTE TTimo: someone explain this g_pStrWork to me?
170         if ( g_pStrWork ) {
171                 delete []g_pStrWork;
172         }
173         g_pStrWork = 0;
174 }
175
176 void MakeLower(){
177         if ( m_pStr ) {
178                 strlwr( m_pStr );
179         }
180 }
181
182 void MakeUpper(){
183         if ( m_pStr ) {
184                 strupr( m_pStr );
185         }
186 }
187
188 void TrimRight(){
189         char* lpsz = m_pStr;
190         char* lpszLast = 0;
191         while ( *lpsz != '\0' )
192         {
193                 if ( isspace( *lpsz ) ) {
194                         if ( lpszLast == 0 ) {
195                                 lpszLast = lpsz;
196                         }
197                 }
198                 else{
199                         lpszLast = 0;
200                 }
201                 lpsz++;
202         }
203
204         if ( lpszLast != 0 ) {
205                 // truncate at trailing space start
206                 *lpszLast = '\0';
207         }
208 }
209
210 void TrimLeft(){
211         // find first non-space character
212         char* lpsz = m_pStr;
213         while ( isspace( *lpsz ) )
214                 lpsz++;
215
216         // fix up data and length
217         std::size_t nDataLength = GetLength() - ( lpsz - m_pStr );
218         memmove( m_pStr, lpsz, ( nDataLength + 1 ) );
219 }
220
221 char* Find( const char *p ){
222         return strstr( m_pStr, p );
223 }
224
225 // search starting at a given offset
226 char* Find( const char *p, std::size_t offset ){
227         return strstr( m_pStr + offset, p );
228 }
229
230 char* Find( const char ch ){
231         return strchr( m_pStr, ch );
232 }
233
234 char* ReverseFind( const char ch ){
235         return strrchr( m_pStr, ch );
236 }
237
238 int Compare( const char* str ) const {
239         return strcmp( m_pStr, str );
240 }
241
242 int CompareNoCase( const char* str ) const {
243         return strcasecmp( m_pStr, str );
244 }
245
246 std::size_t GetLength(){
247         return ( m_pStr ) ? strlen( m_pStr ) : 0;
248 }
249
250 const char* Left( std::size_t n ){
251         delete []g_pStrWork;
252         if ( n > 0 ) {
253                 g_pStrWork = new char[n + 1];
254                 strncpy( g_pStrWork, m_pStr, n );
255                 g_pStrWork[n] = '\0';
256         }
257         else
258         {
259                 g_pStrWork = new char[1];
260                 g_pStrWork[0] = '\0';
261         }
262         return g_pStrWork;
263 }
264
265 const char* Right( std::size_t n ){
266         delete []g_pStrWork;
267         if ( n > 0 ) {
268                 g_pStrWork = new char[n + 1];
269                 std::size_t nStart = GetLength() - n;
270                 strncpy( g_pStrWork, &m_pStr[nStart], n );
271                 g_pStrWork[n] = '\0';
272         }
273         else
274         {
275                 g_pStrWork = new char[1];
276                 g_pStrWork[0] = '\0';
277         }
278         return g_pStrWork;
279 }
280
281 const char* Mid( std::size_t nFirst ) const {
282         return Mid( nFirst, strlen( m_pStr ) - nFirst );
283 }
284
285 const char* Mid( std::size_t first, std::size_t n ) const {
286         delete []g_pStrWork;
287         if ( n > 0 ) {
288                 g_pStrWork = new char[n + 1];
289                 strncpy( g_pStrWork, m_pStr + first, n );
290                 g_pStrWork[n] = '\0';
291         }
292         else
293         {
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
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
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
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 }
342 void ReleaseBuffer(){
343         ReleaseBuffer( GetLength() );
344 }
345
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]; }
432 char GetAt( std::size_t nIndex ) { return m_pStr[nIndex]; }
433 };
434
435
436 template<typename TextOutputStreamType>
437 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Str& str ){
438         return ostream << str.GetBuffer();
439 }
440
441
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
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