transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / splines / util_str.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 //need to rewrite this\r
23 \r
24 #include "util_str.h"\r
25 #include <stdlib.h>\r
26 #include <ctype.h>\r
27 #include <stdio.h>\r
28 #include <stdarg.h>\r
29 \r
30 #ifdef _WIN32\r
31 #pragma warning(disable : 4244)     // 'conversion' conversion from 'type1' to 'type2', possible loss of data\r
32 #pragma warning(disable : 4710)     // function 'blah' not inlined\r
33 #endif\r
34 \r
35 static const int STR_ALLOC_GRAN = 20;\r
36 \r
37 // screwy but intentional\r
38 #ifdef __APPLE_BUG__\r
39 char *idStr::__tolower\r
40 #else\r
41 char *idStr::tolower\r
42 #endif\r
43    (\r
44    char *s1\r
45    )\r
46    \r
47    {\r
48    char *s;\r
49 \r
50    s = s1;\r
51         while( *s )\r
52       {\r
53       *s = ::tolower( *s );\r
54                 s++;\r
55            }\r
56    \r
57    return s1;\r
58    }\r
59 \r
60 // screwy but intentional\r
61 #ifdef __APPLE_BUG__\r
62 char *idStr::__toupper\r
63 #else\r
64 char *idStr::toupper\r
65 #endif\r
66    (\r
67    char *s1\r
68    )\r
69    \r
70    {\r
71    char *s;\r
72 \r
73    s = s1;\r
74         while( *s )\r
75       {\r
76       *s = ::toupper( *s );\r
77                 s++;\r
78            }\r
79    \r
80    return s1;\r
81    }\r
82 \r
83 int idStr::icmpn\r
84    (\r
85    const char *s1, \r
86    const char *s2, \r
87    int n\r
88    )\r
89    \r
90    {\r
91         int c1;\r
92    int c2;\r
93         \r
94         do \r
95       {\r
96                 c1 = *s1++;\r
97                 c2 = *s2++;\r
98 \r
99                 if ( !n-- )\r
100          {\r
101          // idStrings are equal until end point\r
102                         return 0;\r
103                    }\r
104                 \r
105                 if ( c1 != c2 )\r
106          {\r
107                         if ( c1 >= 'a' && c1 <= 'z' )\r
108             {\r
109                                 c1 -= ( 'a' - 'A' );\r
110                            }\r
111 \r
112                         if ( c2 >= 'a' && c2 <= 'z' )\r
113             {\r
114                                 c2 -= ( 'a' - 'A' );\r
115                            }\r
116 \r
117                         if ( c1 < c2 )\r
118             {\r
119             // strings less than\r
120                                 return -1;\r
121                            }\r
122          else if ( c1 > c2 ) \r
123             {\r
124             // strings greater than\r
125             return 1;\r
126             }\r
127                    }\r
128            } \r
129    while( c1 );\r
130         \r
131    // strings are equal\r
132         return 0;\r
133    }\r
134 \r
135 int idStr::icmp\r
136    (\r
137    const char *s1,\r
138    const char *s2\r
139    )\r
140    \r
141    {\r
142         int c1;\r
143    int c2;\r
144         \r
145         do \r
146       {\r
147                 c1 = *s1++;\r
148                 c2 = *s2++;\r
149 \r
150                 if ( c1 != c2 )\r
151          {\r
152                         if ( c1 >= 'a' && c1 <= 'z' )\r
153             {\r
154                                 c1 -= ( 'a' - 'A' );\r
155                            }\r
156 \r
157                         if ( c2 >= 'a' && c2 <= 'z' )\r
158             {\r
159                                 c2 -= ( 'a' - 'A' );\r
160                            }\r
161 \r
162                         if ( c1 < c2 )\r
163             {\r
164             // strings less than\r
165                                 return -1;\r
166                            }\r
167          else if ( c1 > c2 ) \r
168             {\r
169             // strings greater than\r
170             return 1;\r
171             }\r
172                    }\r
173            } \r
174    while( c1 );\r
175         \r
176    // strings are equal\r
177         return 0;\r
178    }\r
179 \r
180 int idStr::cmpn\r
181    (\r
182    const char *s1, \r
183    const char *s2, \r
184    int n\r
185    )\r
186    \r
187    {\r
188         int c1;\r
189    int c2;\r
190         \r
191         do \r
192       {\r
193                 c1 = *s1++;\r
194                 c2 = *s2++;\r
195 \r
196                 if ( !n-- )\r
197          {\r
198          // strings are equal until end point\r
199                         return 0;\r
200                    }\r
201                 \r
202                 if ( c1 < c2 )\r
203          {\r
204          // strings less than\r
205                         return -1;\r
206                         }\r
207       else if ( c1 > c2 ) \r
208          {\r
209          // strings greater than\r
210          return 1;\r
211          }\r
212            } \r
213    while( c1 );\r
214         \r
215    // strings are equal\r
216         return 0;\r
217    }\r
218 \r
219 int idStr::cmp\r
220    (\r
221    const char *s1, \r
222    const char *s2\r
223    )\r
224    \r
225    {\r
226         int c1;\r
227    int c2;\r
228         \r
229         do \r
230       {\r
231                 c1 = *s1++;\r
232                 c2 = *s2++;\r
233 \r
234                 if ( c1 < c2 )\r
235          {\r
236          // strings less than\r
237                         return -1;\r
238                         }\r
239       else if ( c1 > c2 ) \r
240          {\r
241          // strings greater than\r
242          return 1;\r
243          }\r
244            } \r
245    while( c1 );\r
246         \r
247    // strings are equal\r
248         return 0;\r
249    }\r
250 \r
251 /*\r
252 ============\r
253 IsNumeric\r
254 \r
255 Checks a string to see if it contains only numerical values.\r
256 ============\r
257 */\r
258 bool idStr::isNumeric\r
259    (\r
260    const char *str\r
261    )\r
262 \r
263    {\r
264         int len;\r
265         int i;\r
266         bool dot;\r
267 \r
268         if ( *str == '-' )\r
269                 {\r
270                 str++;\r
271                 }\r
272 \r
273         dot = false;\r
274         len = strlen( str );\r
275         for( i = 0; i < len; i++ )\r
276                 {\r
277                 if ( !isdigit( str[ i ] ) )\r
278                         {\r
279                         if ( ( str[ i ] == '.' ) && !dot )\r
280                                 {\r
281                                 dot = true;\r
282                                 continue;\r
283                                 }\r
284                         return false;\r
285                         }\r
286                 }\r
287 \r
288         return true;\r
289    }\r
290 \r
291 idStr operator+\r
292    (\r
293    const idStr& a,\r
294    const float b\r
295    )\r
296 \r
297    {\r
298    char text[ 20 ];\r
299 \r
300         idStr result( a );\r
301 \r
302    sprintf( text, "%f", b );\r
303         result.append( text );\r
304 \r
305         return result;\r
306    }\r
307 \r
308 idStr operator+\r
309    (\r
310    const idStr& a,\r
311    const int b\r
312    )\r
313 \r
314    {\r
315    char text[ 20 ];\r
316 \r
317         idStr result( a );\r
318 \r
319    sprintf( text, "%d", b );\r
320         result.append( text );\r
321 \r
322         return result;\r
323    }\r
324 \r
325 idStr operator+\r
326    (\r
327    const idStr& a,\r
328    const unsigned b\r
329    )\r
330 \r
331    {\r
332    char text[ 20 ];\r
333 \r
334         idStr result( a );\r
335 \r
336    sprintf( text, "%u", b );\r
337         result.append( text );\r
338 \r
339         return result;\r
340    }\r
341 \r
342 idStr& idStr::operator+=\r
343         (\r
344         const float a\r
345         )\r
346 \r
347         {\r
348    char text[ 20 ];\r
349 \r
350    sprintf( text, "%f", a );\r
351         append( text );\r
352 \r
353    return *this;\r
354         }\r
355 \r
356 idStr& idStr::operator+=\r
357         (\r
358         const int a\r
359         )\r
360 \r
361         {\r
362    char text[ 20 ];\r
363 \r
364    sprintf( text, "%d", a );\r
365         append( text );\r
366 \r
367    return *this;\r
368         }\r
369 \r
370 idStr& idStr::operator+=\r
371         (\r
372         const unsigned a\r
373         )\r
374 \r
375         {\r
376    char text[ 20 ];\r
377 \r
378    sprintf( text, "%u", a );\r
379         append( text );\r
380 \r
381    return *this;\r
382         }\r
383 \r
384 void idStr::CapLength \r
385    (\r
386    int newlen \r
387    )\r
388 \r
389    {\r
390    assert ( m_data );\r
391    \r
392    if ( length() <= newlen )\r
393       return;\r
394 \r
395    EnsureDataWritable ();\r
396 \r
397    m_data->data[newlen] = 0;\r
398    m_data->len = newlen;\r
399    }\r
400 \r
401 void idStr::EnsureDataWritable \r
402    (\r
403    void\r
404    )\r
405 \r
406    {\r
407    assert ( m_data );\r
408    strdata *olddata;\r
409    int len;\r
410 \r
411    if ( !m_data->refcount )\r
412       return;\r
413 \r
414    olddata = m_data;\r
415    len = length();\r
416 \r
417    m_data = new strdata;\r
418 \r
419    EnsureAlloced ( len + 1, false );\r
420    strncpy ( m_data->data, olddata->data, len+1 );\r
421    m_data->len = len;\r
422 \r
423    olddata->DelRef ();\r
424    }\r
425 \r
426 void idStr::EnsureAlloced (int amount, bool keepold) {\r
427 \r
428         if ( !m_data ) {\r
429       m_data = new strdata();\r
430         }\r
431    \r
432         // Now, let's make sure it's writable\r
433         EnsureDataWritable ();\r
434 \r
435         char *newbuffer;\r
436         bool wasalloced = ( m_data->alloced != 0 );\r
437 \r
438         if ( amount < m_data->alloced ) {\r
439                 return;\r
440         }\r
441 \r
442         assert ( amount );\r
443         if ( amount == 1 ) {\r
444                 m_data->alloced = 1;\r
445         } else {\r
446                 int newsize, mod;\r
447                 mod = amount % STR_ALLOC_GRAN;\r
448                 if ( !mod ) {\r
449                         newsize = amount;\r
450                 } else {\r
451          newsize = amount + STR_ALLOC_GRAN - mod;\r
452                 }\r
453                 m_data->alloced = newsize;\r
454         }\r
455 \r
456         newbuffer = new char[m_data->alloced];\r
457         if ( wasalloced && keepold ) {\r
458                 strcpy ( newbuffer, m_data->data );\r
459         }\r
460 \r
461         if ( m_data->data ) {\r
462                 delete [] m_data->data;\r
463     }\r
464         m_data->data = newbuffer;\r
465 }\r
466 \r
467 void idStr::BackSlashesToSlashes\r
468    (\r
469    void\r
470    )\r
471 \r
472    {\r
473    int i;\r
474 \r
475    EnsureDataWritable ();\r
476 \r
477    for ( i=0; i < m_data->len; i++ )\r
478       {\r
479       if ( m_data->data[i] == '\\' )\r
480          m_data->data[i] = '/';\r
481       }\r
482    }\r
483 \r
484 void idStr::snprintf \r
485    (\r
486    char *dst,\r
487    int size,\r
488    const char *fmt, \r
489    ...\r
490    )\r
491 \r
492    {\r
493    char buffer[0x10000];\r
494         int             len;\r
495         va_list         argptr;\r
496 \r
497         va_start (argptr,fmt);\r
498         len = vsprintf (buffer,fmt,argptr);\r
499         va_end (argptr);\r
500         \r
501    assert ( len < size );\r
502 \r
503    strncpy (dst, buffer, size-1);\r
504    }\r
505 \r
506 #ifdef _WIN32\r
507 #pragma warning(disable : 4189)         // local variable is initialized but not referenced\r
508 #endif\r
509 \r
510 /*\r
511 =================\r
512 TestStringClass\r
513 \r
514 This is a fairly rigorous test of the idStr class's functionality.\r
515 Because of the fairly global and subtle ramifications of a bug occuring\r
516 in this class, it should be run after any changes to the class.\r
517 Add more tests as functionality is changed.  Tests should include\r
518 any possible bounds violation and NULL data tests.\r
519 =================\r
520 */\r
521 void TestStringClass\r
522         (\r
523         void \r
524         )\r
525 \r
526         {\r
527         char    ch;                                                     // ch == ?\r
528         idStr   *t;                                                     // t == ?\r
529         idStr   a;                                                              // a.len == 0, a.data == "\0"\r
530         idStr   b;                                                              // b.len == 0, b.data == "\0"\r
531         idStr   c( "test" );                            // c.len == 4, c.data == "test\0"\r
532         idStr   d( c );                                         // d.len == 4, d.data == "test\0"\r
533         idStr   e( reinterpret_cast<const char *>(NULL) );                                      \r
534                                  // e.len == 0, e.data == "\0"                                  ASSERT!\r
535         int     i;                                                              // i == ?\r
536 \r
537         i = a.length();                                 // i == 0\r
538         i = c.length();                                 // i == 4\r
539 \r
540         const char *s1 = a.c_str();     // s1 == "\0"\r
541         const char *s2 = c.c_str();     // s2 == "test\0"\r
542 \r
543         t = new idStr();                                                // t->len == 0, t->data == "\0"\r
544         delete t;                                                       // t == ?\r
545 \r
546         b = "test";                                                     // b.len == 4, b.data == "test\0"\r
547         t = new idStr( "test" );                        // t->len == 4, t->data == "test\0"\r
548         delete t;                                                       // t == ?\r
549 \r
550         a = c;                                                          // a.len == 4, a.data == "test\0"\r
551 //   a = "";\r
552    a = NULL;                                                    // a.len == 0, a.data == "\0"                                   ASSERT!\r
553         a = c + d;                                                      // a.len == 8, a.data == "testtest\0"\r
554         a = c + "wow";                                          // a.len == 7, a.data == "testwow\0"\r
555         a = c + reinterpret_cast<const char *>(NULL);\r
556                                  // a.len == 4, a.data == "test\0"                      ASSERT!\r
557         a = "this" + d;                                 // a.len == 8, a.data == "thistest\0"\r
558         a = reinterpret_cast<const char *>(NULL) + d;\r
559                                  // a.len == 4, a.data == "test\0"                      ASSERT!\r
560         a += c;                                                         // a.len == 8, a.data == "testtest\0"\r
561         a += "wow";                                                     // a.len == 11, a.data == "testtestwow\0"\r
562         a += reinterpret_cast<const char *>(NULL);\r
563                                  // a.len == 11, a.data == "testtestwow\0"      ASSERT!\r
564 \r
565         a = "test";                                                     // a.len == 4, a.data == "test\0"\r
566         ch = a[ 0 ];                                            // ch == 't'\r
567         ch = a[ -1 ];                                           // ch == 0                                                                                      ASSERT!\r
568         ch = a[ 1000 ];                                 // ch == 0                                                                                      ASSERT!\r
569         ch = a[ 0 ];                                            // ch == 't'\r
570         ch = a[ 1 ];                                            // ch == 'e'\r
571         ch = a[ 2 ];                                            // ch == 's'\r
572         ch = a[ 3 ];                                            // ch == 't'\r
573         ch = a[ 4 ];                                            // ch == '\0'                                                                           ASSERT!\r
574         ch = a[ 5 ];                                            // ch == '\0'                                                                           ASSERT!\r
575 \r
576         a[ 1 ] = 'b';                                           // a.len == 4, a.data == "tbst\0"\r
577         a[ -1 ] = 'b';                                          // a.len == 4, a.data == "tbst\0"                       ASSERT!\r
578         a[ 0 ] = '0';                                           // a.len == 4, a.data == "0bst\0"\r
579         a[ 1 ] = '1';                                           // a.len == 4, a.data == "01st\0"\r
580         a[ 2 ] = '2';                                           // a.len == 4, a.data == "012t\0"\r
581         a[ 3 ] = '3';                                           // a.len == 4, a.data == "0123\0"\r
582         a[ 4 ] = '4';                                           // a.len == 4, a.data == "0123\0"                       ASSERT!\r
583         a[ 5 ] = '5';                                           // a.len == 4, a.data == "0123\0"                       ASSERT!\r
584         a[ 7 ] = '7';                                           // a.len == 4, a.data == "0123\0"                       ASSERT!\r
585 \r
586         a = "test";                                                     // a.len == 4, a.data == "test\0"\r
587         b = "no";                                                       // b.len == 2, b.data == "no\0"\r
588 \r
589         i = ( a == b );                                 // i == 0\r
590         i = ( a == c );                                 // i == 1\r
591 \r
592         i = ( a == "blow" );                            // i == 0\r
593         i = ( a == "test" );                            // i == 1\r
594         i = ( a == NULL );                              // i == 0                                                                                       ASSERT!\r
595 \r
596         i = ( "test" == b );                            // i == 0\r
597         i = ( "test" == a );                            // i == 1\r
598         i = ( NULL == a );                              // i == 0                                                                                       ASSERT!\r
599 \r
600         i = ( a != b );                                 // i == 1\r
601         i = ( a != c );                                 // i == 0\r
602 \r
603         i = ( a != "blow" );                            // i == 1\r
604         i = ( a != "test" );                            // i == 0\r
605         i = ( a != NULL );                              // i == 1                                                                                       ASSERT!\r
606 \r
607         i = ( "test" != b );                            // i == 1\r
608         i = ( "test" != a );                            // i == 0\r
609         i = ( NULL != a );                              // i == 1                                                                                       ASSERT!\r
610 \r
611    a = "test";                   // a.data == "test"\r
612    b = a;                        // b.data == "test"\r
613 \r
614    a = "not";                   // a.data == "not", b.data == "test"\r
615 \r
616    a = b;                        // a.data == b.data == "test"\r
617 \r
618    a += b;                       // a.data == "testtest", b.data = "test"\r
619 \r
620    a = b;\r
621 \r
622    a[1] = '1';                   // a.data = "t1st", b.data = "test"\r
623         }\r
624 \r
625 #ifdef _WIN32\r
626 #pragma warning(default : 4189)         // local variable is initialized but not referenced\r
627 #pragma warning(disable : 4514)     // unreferenced inline function has been removed\r
628 #endif\r