NOW I do it right: #woxblox#
[divverent/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
64   if (pStr == 0)
65     pStr = "";
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 {
77   char *cp;
78   for (cp = string; *cp; ++cp)
79   {
80     if ('A' <= *cp && *cp <= 'Z')
81       *cp += 'a' - 'A';
82   }
83
84   return string;
85 }
86
87 inline char* strupr(char* string)
88 {
89   char *cp;
90   for (cp = string; *cp; ++cp)
91   {
92     if ('a' <= *cp && *cp <= 'z')
93       *cp += 'A' - 'a';
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   {
111     m_bIgnoreCase = true;
112     m_pStr = new char[1];
113     m_pStr[0] = '\0';
114   }
115
116   Str(char *p)
117   {
118     m_bIgnoreCase = true;
119     m_pStr = __StrDup(p);
120   }
121
122   Str(const char *p)
123   {
124     m_bIgnoreCase = true;
125     m_pStr = __StrDup(p);
126   }
127
128   Str(const unsigned char *p)
129   {
130     m_bIgnoreCase = true;
131     m_pStr = __StrDup(reinterpret_cast<const char *>(p));
132   }
133
134   Str(const char c)
135   {
136     m_bIgnoreCase = true;
137     m_pStr = new char[2];
138     m_pStr[0] = c;
139     m_pStr[1] = '\0';    
140   }
141
142   const char* GetBuffer() const
143   {
144     return m_pStr;
145   }
146
147   char* GetBuffer()
148   {
149     return m_pStr;
150   }
151
152   Str(const Str &s)
153   {
154     m_bIgnoreCase = true;
155     m_pStr = __StrDup(s.GetBuffer());
156   }
157
158   void Deallocate()
159   {
160     delete []m_pStr;
161     m_pStr = 0;
162   }
163
164   void Allocate(std::size_t n)
165   {
166     Deallocate();
167     m_pStr = new char[n];
168   }
169
170   void MakeEmpty()
171   {
172     Deallocate();
173     m_pStr = __StrDup("");
174   }
175
176   ~Str()
177   {
178     Deallocate();
179     // NOTE TTimo: someone explain this g_pStrWork to me?
180     if (g_pStrWork)
181       delete []g_pStrWork;
182     g_pStrWork = 0;
183   }
184
185   void MakeLower()
186   {
187     if (m_pStr)
188     {
189       strlwr(m_pStr);
190     }
191   }
192
193   void MakeUpper()
194   {
195     if (m_pStr)
196     {
197       strupr(m_pStr);
198     }
199   }
200
201   void TrimRight()
202     {
203       char* lpsz = m_pStr;
204       char* lpszLast = 0;
205       while (*lpsz != '\0')
206       {
207         if (isspace(*lpsz))
208         {
209           if (lpszLast == 0)
210             lpszLast = lpsz;
211         }
212         else
213           lpszLast = 0;
214         lpsz++;
215       }
216  
217       if (lpszLast != 0)
218       {
219         // truncate at trailing space start
220         *lpszLast = '\0';
221       }
222     }
223
224   void TrimLeft()
225     {
226       // find first non-space character
227       char* lpsz = m_pStr;
228       while (isspace(*lpsz))
229         lpsz++;
230  
231       // fix up data and length
232       std::size_t nDataLength = GetLength() - (lpsz - m_pStr);
233       memmove(m_pStr, lpsz, (nDataLength+1));
234     }
235
236   char* Find(const char *p)
237   {
238     return strstr(m_pStr, p);
239   }
240
241   // search starting at a given offset
242   char* Find(const char *p, std::size_t offset)
243   {
244     return strstr(m_pStr+offset, p);
245   }
246
247   char* Find(const char ch)
248   {
249     return strchr (m_pStr, ch);
250   }
251
252   char* ReverseFind(const char ch)
253   {
254     return strrchr(m_pStr, ch);
255   }
256
257   int Compare (const char* str) const
258   {
259     return strcmp (m_pStr, str);
260   }
261   
262   int CompareNoCase (const char* str) const
263   {
264     return strcasecmp (m_pStr, str);
265   }
266
267   std::size_t GetLength()
268   {
269     return (m_pStr) ? strlen(m_pStr) : 0;
270   }
271
272   const char* Left(std::size_t n)
273   {
274     delete []g_pStrWork;
275     if (n > 0)
276     {
277       g_pStrWork = new char[n+1];
278       strncpy(g_pStrWork, m_pStr, n);
279       g_pStrWork[n] = '\0';
280     }
281     else
282     {
283       g_pStrWork = "";
284       g_pStrWork = new char[1];
285       g_pStrWork[0] = '\0';
286     }
287     return g_pStrWork;
288   }
289
290   const char* Right(std::size_t n)
291   {
292     delete []g_pStrWork;
293     if (n > 0)
294     {
295       g_pStrWork = new char[n+1];
296       std::size_t nStart = GetLength() - n;
297       strncpy(g_pStrWork, &m_pStr[nStart], n);
298       g_pStrWork[n] = '\0';
299     }
300     else
301     {
302       g_pStrWork = new char[1];
303       g_pStrWork[0] = '\0';
304     }
305     return g_pStrWork;
306   }
307
308   const char* Mid(std::size_t nFirst) const
309   {
310     return Mid(nFirst, strlen (m_pStr) - nFirst);
311   }
312
313   const char* Mid(std::size_t first, std::size_t n) const
314   {
315     delete []g_pStrWork;
316     if (n > 0)
317     {
318       g_pStrWork = new char[n+1];
319       strncpy(g_pStrWork, m_pStr+first, n);
320       g_pStrWork[n] = '\0';
321     }
322     else
323     {
324       g_pStrWork = "";
325       g_pStrWork = new char[1];
326       g_pStrWork[0] = '\0';
327     }
328     return g_pStrWork;
329   }
330
331 #if 0 // defined(__G_LIB_H__)
332   void Format(const char* fmt, ...)
333   {
334     va_list args;
335     char *buffer;
336   
337     va_start (args, fmt);
338     buffer = g_strdup_vprintf (fmt, args);
339     va_end (args);
340
341     delete[] m_pStr;
342     m_pStr = __StrDup(buffer);
343     g_free (buffer);
344   }
345 #else
346   void Format(const char* fmt, ...)
347   {
348     char buffer[1024];
349
350     {
351       va_list args;
352       va_start (args, fmt);
353       vsnprintf(buffer, 1023, fmt, args);
354       va_end (args);
355     }
356
357     delete[] m_pStr;
358     m_pStr = __StrDup(buffer);
359   }
360 #endif
361
362   void SetAt(std::size_t n, char ch)
363   {
364     if (n < GetLength())
365       m_pStr[n] = ch;
366   }
367
368         // NOTE: unlike CString, this looses the pointer
369   void ReleaseBuffer(std::size_t n)
370   {
371     char* tmp = m_pStr;
372     tmp[n] = '\0';
373     m_pStr = __StrDup(tmp);
374     delete []tmp;
375   }
376   void ReleaseBuffer()
377   {
378     ReleaseBuffer(GetLength());
379   }
380
381   char* GetBufferSetLength(std::size_t n)
382   {
383     char *p = new char[n+1];
384     strncpy (p, m_pStr, n);
385                 p[n] = '\0';
386     delete []m_pStr;
387     m_pStr = p;
388     return m_pStr;
389   }
390
391   //  char& operator *() { return *m_pStr; }
392   //  char& operator *() const { return *const_cast<Str*>(this)->m_pStr; }
393   operator void*() { return m_pStr; }
394   operator char*() { return m_pStr; }
395   operator const char*() const{ return reinterpret_cast<const char*>(m_pStr); }
396   operator unsigned char*() { return reinterpret_cast<unsigned char*>(m_pStr); }
397   operator const unsigned char*() const { return reinterpret_cast<const unsigned char*>(m_pStr); }
398   Str& operator =(const Str& rhs)
399   {
400     if (&rhs != this)
401     {
402       delete[] m_pStr;
403       m_pStr = __StrDup(rhs.m_pStr);
404     }
405     return *this;
406   }
407   
408   Str& operator =(const char* pStr)
409   {
410     if (m_pStr != pStr)
411     {
412       delete[] m_pStr;
413       m_pStr = __StrDup(pStr);
414     }
415     return *this;
416   }
417
418   Str& operator +=(const char ch)
419   {
420     std::size_t len = GetLength();
421     char *p = new char[len + 1 + 1];
422
423     if (m_pStr)
424     {
425       strcpy(p, m_pStr);
426       delete[] m_pStr;
427     }
428
429     m_pStr = p;
430     m_pStr[len] = ch;
431     m_pStr[len+1] = '\0';
432
433     return *this;
434   }
435
436   Str& operator +=(const char *pStr)
437   {
438     if (pStr)
439     {
440       if (m_pStr)
441       {
442         char *p = new char[strlen(m_pStr) + strlen(pStr) + 1];
443         strcpy(p, m_pStr);
444         strcat(p, pStr);
445         delete[] m_pStr;
446         m_pStr = p;
447       }
448       else
449       {
450         m_pStr = __StrDup(pStr);
451       }
452     }
453     return *this;
454   }
455   
456
457   bool operator ==(const Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) == 0 : strcmp(m_pStr, rhs.m_pStr) == 0; }
458   bool operator ==(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) == 0 : strcmp(m_pStr, pStr) == 0; }
459   bool operator ==(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) == 0 : strcmp(m_pStr, pStr) == 0; }
460   bool operator !=(Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) != 0 : strcmp(m_pStr, rhs.m_pStr) != 0; }
461   bool operator !=(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) != 0 : strcmp(m_pStr, pStr) != 0; }
462   bool operator !=(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) != 0 : strcmp(m_pStr, pStr) != 0; }
463   bool operator <(const Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) < 0 : strcmp(m_pStr, rhs.m_pStr) < 0; }
464   bool operator <(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) < 0 : strcmp(m_pStr, pStr) < 0; }
465   bool operator <(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) < 0 : strcmp(m_pStr, pStr) < 0; }
466   bool operator >(const Str& rhs) const { return (m_bIgnoreCase) ? stricmp(m_pStr, rhs.m_pStr) > 0 : strcmp(m_pStr, rhs.m_pStr) > 0; }
467   bool operator >(char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) > 0 : strcmp(m_pStr, pStr) > 0; }
468   bool operator >(const char* pStr) const { return (m_bIgnoreCase) ? stricmp(m_pStr, pStr) > 0 : strcmp(m_pStr, pStr) > 0; }
469   char& operator [](std::size_t nIndex) { return m_pStr[nIndex]; }
470   const char& operator [](std::size_t nIndex) const { return m_pStr[nIndex]; }
471   const char GetAt (std::size_t nIndex) { return m_pStr[nIndex]; }
472 };
473
474
475 template<typename TextOutputStreamType>
476 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Str& str)
477 {
478   return ostream << str.GetBuffer();
479 }
480
481
482 inline void AddSlash(Str& strPath)
483 {
484   if (strPath.GetLength() > 0)
485   {
486     if ((strPath.GetAt(strPath.GetLength()-1) != '/') &&
487         (strPath.GetAt(strPath.GetLength()-1) != '\\'))
488       strPath += '/';
489   }
490 }
491
492 inline bool ExtractPath_and_Filename(const char* pPath, Str& strPath, Str& strFilename)
493 {
494   Str strPathName;
495   strPathName = pPath;
496   const char* substr = strPathName.ReverseFind('\\');
497   if (substr == 0)
498     // TTimo: try forward slash, some are using forward
499     substr = strPathName.ReverseFind('/');
500   if (substr != 0)
501   {
502     std::size_t nSlash = substr - strPathName.GetBuffer();
503     strPath = strPathName.Left(nSlash+1);
504     strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1);
505   }
506   else
507     strFilename = pPath;
508   return true;
509 }
510
511
512
513 #endif