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