Fix MSYS2 issues
[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 //
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
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
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
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:
109 Str(){
110         m_bIgnoreCase = true;
111         m_pStr = new char[1];
112         m_pStr[0] = '\0';
113 }
114
115 Str( char *p ){
116         m_bIgnoreCase = true;
117         m_pStr = __StrDup( p );
118 }
119
120 Str( const char *p ){
121         m_bIgnoreCase = true;
122         m_pStr = __StrDup( p );
123 }
124
125 Str( const unsigned char *p ){
126         m_bIgnoreCase = true;
127         m_pStr = __StrDup( reinterpret_cast<const char *>( p ) );
128 }
129
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
137 const char* GetBuffer() const {
138         return m_pStr;
139 }
140
141 char* GetBuffer(){
142         return m_pStr;
143 }
144
145 Str( const Str &s ){
146         m_bIgnoreCase = true;
147         m_pStr = __StrDup( s.GetBuffer() );
148 }
149
150 void Deallocate(){
151         delete []m_pStr;
152         m_pStr = 0;
153 }
154
155 void Allocate( std::size_t n ){
156         Deallocate();
157         m_pStr = new char[n];
158 }
159
160 void MakeEmpty(){
161         Deallocate();
162         m_pStr = __StrDup( "" );
163 }
164
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
174 void MakeLower(){
175         if ( m_pStr ) {
176                 strlwr( m_pStr );
177         }
178 }
179
180 void MakeUpper(){
181         if ( m_pStr ) {
182                 strupr( m_pStr );
183         }
184 }
185
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
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
219 char* Find( const char *p ){
220         return strstr( m_pStr, p );
221 }
222
223 // search starting at a given offset
224 char* Find( const char *p, std::size_t offset ){
225         return strstr( m_pStr + offset, p );
226 }
227
228 char* Find( const char ch ){
229         return strchr( m_pStr, ch );
230 }
231
232 char* ReverseFind( const char ch ){
233         return strrchr( m_pStr, ch );
234 }
235
236 int Compare( const char* str ) const {
237         return strcmp( m_pStr, str );
238 }
239
240 int CompareNoCase( const char* str ) const {
241         return strcasecmp( m_pStr, str );
242 }
243
244 std::size_t GetLength(){
245         return ( m_pStr ) ? strlen( m_pStr ) : 0;
246 }
247
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
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
280 const char* Mid( std::size_t nFirst ) const {
281         return Mid( nFirst, strlen( m_pStr ) - nFirst );
282 }
283
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
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 const 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