]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/str.h
deduplicate empty string affectation
[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 = new char[1];
258                 g_pStrWork[0] = '\0';
259         }
260         return g_pStrWork;
261 }
262
263 const char* Right( std::size_t n ){
264         delete []g_pStrWork;
265         if ( n > 0 ) {
266                 g_pStrWork = new char[n + 1];
267                 std::size_t nStart = GetLength() - n;
268                 strncpy( g_pStrWork, &m_pStr[nStart], n );
269                 g_pStrWork[n] = '\0';
270         }
271         else
272         {
273                 g_pStrWork = new char[1];
274                 g_pStrWork[0] = '\0';
275         }
276         return g_pStrWork;
277 }
278
279 const char* Mid( std::size_t nFirst ) const {
280         return Mid( nFirst, strlen( m_pStr ) - nFirst );
281 }
282
283 const char* Mid( std::size_t first, std::size_t n ) const {
284         delete []g_pStrWork;
285         if ( n > 0 ) {
286                 g_pStrWork = new char[n + 1];
287                 strncpy( g_pStrWork, m_pStr + first, n );
288                 g_pStrWork[n] = '\0';
289         }
290         else
291         {
292                 g_pStrWork = new char[1];
293                 g_pStrWork[0] = '\0';
294         }
295         return g_pStrWork;
296 }
297
298 #if 0 // defined(__G_LIB_H__)
299 void Format( const char* fmt, ... ){
300         va_list args;
301         char *buffer;
302
303         va_start( args, fmt );
304         buffer = g_strdup_vprintf( fmt, args );
305         va_end( args );
306
307         delete[] m_pStr;
308         m_pStr = __StrDup( buffer );
309         g_free( buffer );
310 }
311 #else
312 void Format( const char* fmt, ... ){
313         char buffer[1024];
314
315         {
316                 va_list args;
317                 va_start( args, fmt );
318                 vsnprintf( buffer, 1023, fmt, args );
319                 va_end( args );
320         }
321
322         delete[] m_pStr;
323         m_pStr = __StrDup( buffer );
324 }
325 #endif
326
327 void SetAt( std::size_t n, char ch ){
328         if ( n < GetLength() ) {
329                 m_pStr[n] = ch;
330         }
331 }
332
333 // NOTE: unlike CString, this looses the pointer
334 void ReleaseBuffer( std::size_t n ){
335         char* tmp = m_pStr;
336         tmp[n] = '\0';
337         m_pStr = __StrDup( tmp );
338         delete []tmp;
339 }
340 void ReleaseBuffer(){
341         ReleaseBuffer( GetLength() );
342 }
343
344 char* GetBufferSetLength( std::size_t n ){
345         char *p = new char[n + 1];
346         strncpy( p, m_pStr, n );
347         p[n] = '\0';
348         delete []m_pStr;
349         m_pStr = p;
350         return m_pStr;
351 }
352
353 //  char& operator *() { return *m_pStr; }
354 //  char& operator *() const { return *const_cast<Str*>(this)->m_pStr; }
355 operator void*() {
356         return m_pStr;
357 }
358 operator char*() {
359         return m_pStr;
360 }
361 operator const char*() const { return reinterpret_cast<const char*>( m_pStr ); }
362 operator unsigned char*() {
363         return reinterpret_cast<unsigned char*>( m_pStr );
364 }
365 operator const unsigned char*() const { return reinterpret_cast<const unsigned char*>( m_pStr ); }
366 Str& operator =( const Str& rhs ){
367         if ( &rhs != this ) {
368                 delete[] m_pStr;
369                 m_pStr = __StrDup( rhs.m_pStr );
370         }
371         return *this;
372 }
373
374 Str& operator =( const char* pStr ){
375         if ( m_pStr != pStr ) {
376                 delete[] m_pStr;
377                 m_pStr = __StrDup( pStr );
378         }
379         return *this;
380 }
381
382 Str& operator +=( const char ch ){
383         std::size_t len = GetLength();
384         char *p = new char[len + 1 + 1];
385
386         if ( m_pStr ) {
387                 strcpy( p, m_pStr );
388                 delete[] m_pStr;
389         }
390
391         m_pStr = p;
392         m_pStr[len] = ch;
393         m_pStr[len + 1] = '\0';
394
395         return *this;
396 }
397
398 Str& operator +=( const char *pStr ){
399         if ( pStr ) {
400                 if ( m_pStr ) {
401                         char *p = new char[strlen( m_pStr ) + strlen( pStr ) + 1];
402                         strcpy( p, m_pStr );
403                         strcat( p, pStr );
404                         delete[] m_pStr;
405                         m_pStr = p;
406                 }
407                 else
408                 {
409                         m_pStr = __StrDup( pStr );
410                 }
411         }
412         return *this;
413 }
414
415
416 bool operator ==( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) == 0 : strcmp( m_pStr, rhs.m_pStr ) == 0; }
417 bool operator ==( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) == 0 : strcmp( m_pStr, pStr ) == 0; }
418 bool operator ==( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) == 0 : strcmp( m_pStr, pStr ) == 0; }
419 bool operator !=( Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) != 0 : strcmp( m_pStr, rhs.m_pStr ) != 0; }
420 bool operator !=( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) != 0 : strcmp( m_pStr, pStr ) != 0; }
421 bool operator !=( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) != 0 : strcmp( m_pStr, pStr ) != 0; }
422 bool operator <( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) < 0 : strcmp( m_pStr, rhs.m_pStr ) < 0; }
423 bool operator <( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) < 0 : strcmp( m_pStr, pStr ) < 0; }
424 bool operator <( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) < 0 : strcmp( m_pStr, pStr ) < 0; }
425 bool operator >( const Str& rhs ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, rhs.m_pStr ) > 0 : strcmp( m_pStr, rhs.m_pStr ) > 0; }
426 bool operator >( char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) > 0 : strcmp( m_pStr, pStr ) > 0; }
427 bool operator >( const char* pStr ) const { return ( m_bIgnoreCase ) ? stricmp( m_pStr, pStr ) > 0 : strcmp( m_pStr, pStr ) > 0; }
428 char& operator []( std::size_t nIndex ) { return m_pStr[nIndex]; }
429 const char& operator []( std::size_t nIndex ) const { return m_pStr[nIndex]; }
430 const char GetAt( std::size_t nIndex ) { return m_pStr[nIndex]; }
431 };
432
433
434 template<typename TextOutputStreamType>
435 inline TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const Str& str ){
436         return ostream << str.GetBuffer();
437 }
438
439
440 inline void AddSlash( Str& strPath ){
441         if ( strPath.GetLength() > 0 ) {
442                 if ( ( strPath.GetAt( strPath.GetLength() - 1 ) != '/' ) &&
443                          ( strPath.GetAt( strPath.GetLength() - 1 ) != '\\' ) ) {
444                         strPath += '/';
445                 }
446         }
447 }
448
449 inline bool ExtractPath_and_Filename( const char* pPath, Str& strPath, Str& strFilename ){
450         Str strPathName;
451         strPathName = pPath;
452         const char* substr = strPathName.ReverseFind( '\\' );
453         if ( substr == 0 ) {
454                 // TTimo: try forward slash, some are using forward
455                 substr = strPathName.ReverseFind( '/' );
456         }
457         if ( substr != 0 ) {
458                 std::size_t nSlash = substr - strPathName.GetBuffer();
459                 strPath = strPathName.Left( nSlash + 1 );
460                 strFilename = strPathName.Right( strPathName.GetLength() - nSlash - 1 );
461         }
462         else{
463                 strFilename = pPath;
464         }
465         return true;
466 }
467
468
469
470 #endif