1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30 #include "idlib/math/Vector.h"
31 #include "idlib/Heap.h"
32 #include "framework/Common.h"
33
34 #include "idlib/Str.h"
35
36 #if !defined( ID_REDIRECT_NEWDELETE ) && !defined( MACOS_X )
37 #define USE_STRING_DATA_ALLOCATOR
38 #endif
39
40 #ifdef USE_STRING_DATA_ALLOCATOR
41 static idDynamicBlockAlloc<char, 1<<18, 128> stringDataAllocator;
42 #endif
43
44 idVec4 g_color_table[16] =
45 {
46 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
47 idVec4(1.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_RED
48 idVec4(0.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_GREEN
49 idVec4(1.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_YELLOW
50 idVec4(0.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_BLUE
51 idVec4(0.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_CYAN
52 idVec4(1.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_MAGENTA
53 idVec4(1.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_WHITE
54 idVec4(0.5f, 0.5f, 0.5f, 1.0f), // S_COLOR_GRAY
55 idVec4(0.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_BLACK
56 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
57 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
58 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
59 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
60 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
61 idVec4(0.0f, 0.0f, 0.0f, 1.0f),
62 };
63
64 const char *units[2][4] =
65 {
66 { "B", "KB", "MB", "GB" },
67 { "B/s", "KB/s", "MB/s", "GB/s" }
68 };
69
70 /*
71 ============
72 idStr::ColorForIndex
73 ============
74 */
ColorForIndex(int i)75 idVec4 & idStr::ColorForIndex( int i ) {
76 return g_color_table[ i & 15 ];
77 }
78
79 /*
80 ============
81 idStr::ReAllocate
82 ============
83 */
ReAllocate(int amount,bool keepold)84 void idStr::ReAllocate( int amount, bool keepold ) {
85 char *newbuffer;
86 int newsize;
87 int mod;
88
89 //assert( data );
90 assert( amount > 0 );
91
92 mod = amount % STR_ALLOC_GRAN;
93 if ( !mod ) {
94 newsize = amount;
95 }
96 else {
97 newsize = amount + STR_ALLOC_GRAN - mod;
98 }
99 alloced = newsize;
100
101 #ifdef USE_STRING_DATA_ALLOCATOR
102 newbuffer = stringDataAllocator.Alloc( alloced );
103 #else
104 newbuffer = new char[ alloced ];
105 #endif
106 if ( keepold && data ) {
107 data[ len ] = '\0';
108 strcpy( newbuffer, data );
109 }
110
111 if ( data && data != baseBuffer ) {
112 #ifdef USE_STRING_DATA_ALLOCATOR
113 stringDataAllocator.Free( data );
114 #else
115 delete [] data;
116 #endif
117 }
118
119 data = newbuffer;
120 }
121
122 /*
123 ============
124 idStr::FreeData
125 ============
126 */
FreeData(void)127 void idStr::FreeData( void ) {
128 if ( data && data != baseBuffer ) {
129 #ifdef USE_STRING_DATA_ALLOCATOR
130 stringDataAllocator.Free( data );
131 #else
132 delete[] data;
133 #endif
134 data = baseBuffer;
135 }
136 }
137
138 /*
139 ============
140 idStr::operator=
141 ============
142 */
operator =(const char * text)143 void idStr::operator=( const char *text ) {
144 int l;
145 int diff;
146 int i;
147
148 if ( !text ) {
149 // safe behaviour if NULL
150 EnsureAlloced( 1, false );
151 data[ 0 ] = '\0';
152 len = 0;
153 return;
154 }
155
156 if ( text == data ) {
157 return; // copying same thing
158 }
159
160 // check if we're aliasing
161 if ( text >= data && text <= data + len ) {
162 diff = text - data;
163
164 assert( strlen( text ) < (unsigned)len );
165
166 for ( i = 0; text[ i ]; i++ ) {
167 data[ i ] = text[ i ];
168 }
169
170 data[ i ] = '\0';
171
172 len -= diff;
173
174 return;
175 }
176
177 l = strlen( text );
178 EnsureAlloced( l + 1, false );
179 strcpy( data, text );
180 len = l;
181 }
182
183 /*
184 ============
185 idStr::FindChar
186
187 returns -1 if not found otherwise the index of the char
188 ============
189 */
FindChar(const char * str,const char c,int start,int end)190 int idStr::FindChar( const char *str, const char c, int start, int end ) {
191 int i;
192
193 if ( end == -1 ) {
194 end = strlen( str ) - 1;
195 }
196 for ( i = start; i <= end; i++ ) {
197 if ( str[i] == c ) {
198 return i;
199 }
200 }
201 return -1;
202 }
203
204 /*
205 ============
206 idStr::FindText
207
208 returns -1 if not found otherwise the index of the text
209 ============
210 */
FindText(const char * str,const char * text,bool casesensitive,int start,int end)211 int idStr::FindText( const char *str, const char *text, bool casesensitive, int start, int end ) {
212 int l, i, j;
213
214 if ( end == -1 ) {
215 end = strlen( str );
216 }
217 l = end - strlen( text );
218 for ( i = start; i <= l; i++ ) {
219 if ( casesensitive ) {
220 for ( j = 0; text[j]; j++ ) {
221 if ( str[i+j] != text[j] ) {
222 break;
223 }
224 }
225 } else {
226 for ( j = 0; text[j]; j++ ) {
227 if ( ::toupper( str[i+j] ) != ::toupper( text[j] ) ) {
228 break;
229 }
230 }
231 }
232 if ( !text[j] ) {
233 return i;
234 }
235 }
236 return -1;
237 }
238
239 /*
240 ============
241 idStr::Filter
242
243 Returns true if the string conforms the given filter.
244 Several metacharacter may be used in the filter.
245
246 * match any string of zero or more characters
247 ? match any single character
248 [abc...] match any of the enclosed characters; a hyphen can
249 be used to specify a range (e.g. a-z, A-Z, 0-9)
250
251 ============
252 */
Filter(const char * filter,const char * name,bool casesensitive)253 bool idStr::Filter( const char *filter, const char *name, bool casesensitive ) {
254 idStr buf;
255 int i, found, index;
256
257 while(*filter) {
258 if (*filter == '*') {
259 filter++;
260 buf.Empty();
261 for (i = 0; *filter; i++) {
262 if ( *filter == '*' || *filter == '?' || (*filter == '[' && *(filter+1) != '[') ) {
263 break;
264 }
265 buf += *filter;
266 if ( *filter == '[' ) {
267 filter++;
268 }
269 filter++;
270 }
271 if ( buf.Length() ) {
272 index = idStr(name).Find( buf.c_str(), casesensitive );
273 if ( index == -1 ) {
274 return false;
275 }
276 name += index + strlen(buf);
277 }
278 }
279 else if (*filter == '?') {
280 filter++;
281 name++;
282 }
283 else if (*filter == '[') {
284 if ( *(filter+1) == '[' ) {
285 if ( *name != '[' ) {
286 return false;
287 }
288 filter += 2;
289 name++;
290 }
291 else {
292 filter++;
293 found = false;
294 while(*filter && !found) {
295 if (*filter == ']' && *(filter+1) != ']') {
296 break;
297 }
298 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) {
299 if (casesensitive) {
300 if (*name >= *filter && *name <= *(filter+2)) {
301 found = true;
302 }
303 }
304 else {
305 if ( ::toupper(*name) >= ::toupper(*filter) && ::toupper(*name) <= ::toupper(*(filter+2)) ) {
306 found = true;
307 }
308 }
309 filter += 3;
310 }
311 else {
312 if (casesensitive) {
313 if (*filter == *name) {
314 found = true;
315 }
316 }
317 else {
318 if ( ::toupper(*filter) == ::toupper(*name) ) {
319 found = true;
320 }
321 }
322 filter++;
323 }
324 }
325 if (!found) {
326 return false;
327 }
328 while(*filter) {
329 if ( *filter == ']' && *(filter+1) != ']' ) {
330 break;
331 }
332 filter++;
333 }
334 filter++;
335 name++;
336 }
337 }
338 else {
339 if (casesensitive) {
340 if (*filter != *name) {
341 return false;
342 }
343 }
344 else {
345 if ( ::toupper(*filter) != ::toupper(*name) ) {
346 return false;
347 }
348 }
349 filter++;
350 name++;
351 }
352 }
353 return true;
354 }
355
356 /*
357 =============
358 idStr::StripMediaName
359
360 makes the string lower case, replaces backslashes with forward slashes, and removes extension
361 =============
362 */
StripMediaName(const char * name,idStr & mediaName)363 void idStr::StripMediaName( const char *name, idStr &mediaName ) {
364 char c;
365
366 mediaName.Empty();
367
368 for ( c = *name; c; c = *(++name) ) {
369 // truncate at an extension
370 if ( c == '.' ) {
371 break;
372 }
373 // convert backslashes to forward slashes
374 if ( c == '\\' ) {
375 mediaName.Append( '/' );
376 } else {
377 mediaName.Append( idStr::ToLower( c ) );
378 }
379 }
380 }
381
382 /*
383 =============
384 idStr::CheckExtension
385 =============
386 */
CheckExtension(const char * name,const char * ext)387 bool idStr::CheckExtension( const char *name, const char *ext ) {
388 const char *s1 = name + Length( name ) - 1;
389 const char *s2 = ext + Length( ext ) - 1;
390 int c1, c2, d;
391
392 do {
393 c1 = *s1--;
394 c2 = *s2--;
395
396 d = c1 - c2;
397 while( d ) {
398 if ( c1 <= 'Z' && c1 >= 'A' ) {
399 d += ('a' - 'A');
400 if ( !d ) {
401 break;
402 }
403 }
404 if ( c2 <= 'Z' && c2 >= 'A' ) {
405 d -= ('a' - 'A');
406 if ( !d ) {
407 break;
408 }
409 }
410 return false;
411 }
412 } while( s1 > name && s2 > ext );
413
414 return ( s1 >= name );
415 }
416
417 /*
418 =============
419 idStr::FloatArrayToString
420 =============
421 */
FloatArrayToString(const float * array,const int length,const int precision)422 const char *idStr::FloatArrayToString( const float *array, const int length, const int precision ) {
423 static int index = 0;
424 static char str[4][16384]; // in case called by nested functions
425 int i, n;
426 char format[16], *s;
427
428 // use an array of string so that multiple calls won't collide
429 s = str[ index ];
430 index = (index + 1) & 3;
431
432 idStr::snPrintf( format, sizeof( format ), "%%.%df", precision );
433 n = idStr::snPrintf( s, sizeof( str[0] ), format, array[0] );
434 if ( precision > 0 ) {
435 while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
436 while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
437 }
438 idStr::snPrintf( format, sizeof( format ), " %%.%df", precision );
439 for ( i = 1; i < length; i++ ) {
440 n += idStr::snPrintf( s + n, sizeof( str[0] ) - n, format, array[i] );
441 if ( precision > 0 ) {
442 while( n > 0 && s[n-1] == '0' ) s[--n] = '\0';
443 while( n > 0 && s[n-1] == '.' ) s[--n] = '\0';
444 }
445 }
446 return s;
447 }
448
449 /*
450 ============
451 idStr::Last
452
453 returns -1 if not found otherwise the index of the char
454 ============
455 */
Last(const char c) const456 int idStr::Last( const char c ) const {
457 int i;
458
459 for( i = Length(); i > 0; i-- ) {
460 if ( data[ i - 1 ] == c ) {
461 return i - 1;
462 }
463 }
464
465 return -1;
466 }
467
468 /*
469 ============
470 idStr::StripLeading
471 ============
472 */
StripLeading(const char c)473 void idStr::StripLeading( const char c ) {
474 while( data[ 0 ] == c ) {
475 memmove( &data[ 0 ], &data[ 1 ], len );
476 len--;
477 }
478 }
479
480 /*
481 ============
482 idStr::StripLeading
483 ============
484 */
StripLeading(const char * string)485 void idStr::StripLeading( const char *string ) {
486 int l;
487
488 l = strlen( string );
489 if ( l > 0 ) {
490 while ( !Cmpn( string, l ) ) {
491 memmove( data, data + l, len - l + 1 );
492 len -= l;
493 }
494 }
495 }
496
497 /*
498 ============
499 idStr::StripLeadingOnce
500 ============
501 */
StripLeadingOnce(const char * string)502 bool idStr::StripLeadingOnce( const char *string ) {
503 int l;
504
505 l = strlen( string );
506 if ( ( l > 0 ) && !Cmpn( string, l ) ) {
507 memmove( data, data + l, len - l + 1 );
508 len -= l;
509 return true;
510 }
511 return false;
512 }
513
514 /*
515 ============
516 idStr::StripTrailing
517 ============
518 */
StripTrailing(const char c)519 void idStr::StripTrailing( const char c ) {
520 int i;
521
522 for( i = Length(); i > 0 && data[ i - 1 ] == c; i-- ) {
523 data[ i - 1 ] = '\0';
524 len--;
525 }
526 }
527
528 /*
529 ============
530 idStr::StripLeading
531 ============
532 */
StripTrailing(const char * string)533 void idStr::StripTrailing( const char *string ) {
534 int l;
535
536 l = strlen( string );
537 if ( l > 0 ) {
538 while ( ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
539 len -= l;
540 data[len] = '\0';
541 }
542 }
543 }
544
545 /*
546 ============
547 idStr::StripTrailingOnce
548 ============
549 */
StripTrailingOnce(const char * string)550 bool idStr::StripTrailingOnce( const char *string ) {
551 int l;
552
553 l = strlen( string );
554 if ( ( l > 0 ) && ( len >= l ) && !Cmpn( string, data + len - l, l ) ) {
555 len -= l;
556 data[len] = '\0';
557 return true;
558 }
559 return false;
560 }
561
562 /*
563 ============
564 idStr::Replace
565 ============
566 */
Replace(const char * old,const char * nw)567 void idStr::Replace( const char *old, const char *nw ) {
568 int oldLen, newLen, i, j, count;
569 idStr oldString( data );
570
571 oldLen = strlen( old );
572 newLen = strlen( nw );
573
574 // Work out how big the new string will be
575 count = 0;
576 for( i = 0; i < oldString.Length(); i++ ) {
577 if( !idStr::Cmpn( &oldString[i], old, oldLen ) ) {
578 count++;
579 i += oldLen - 1;
580 }
581 }
582
583 if( count ) {
584 EnsureAlloced( len + ( ( newLen - oldLen ) * count ) + 2, false );
585
586 // Replace the old data with the new data
587 for( i = 0, j = 0; i < oldString.Length(); i++ ) {
588 if( !idStr::Cmpn( &oldString[i], old, oldLen ) ) {
589 memcpy( data + j, nw, newLen );
590 i += oldLen - 1;
591 j += newLen;
592 } else {
593 data[j] = oldString[i];
594 j++;
595 }
596 }
597 data[j] = 0;
598 len = strlen( data );
599 }
600 }
601
602 /*
603 ============
604 idStr::Mid
605 ============
606 */
Mid(int start,int len,idStr & result) const607 const char *idStr::Mid( int start, int len, idStr &result ) const {
608 int i;
609
610 result.Empty();
611
612 i = Length();
613 if ( i == 0 || len <= 0 || start >= i ) {
614 return NULL;
615 }
616
617 if ( start + len >= i ) {
618 len = i - start;
619 }
620
621 result.Append( &data[ start ], len );
622 return result;
623 }
624
625 /*
626 ============
627 idStr::Mid
628 ============
629 */
Mid(int start,int len) const630 idStr idStr::Mid( int start, int len ) const {
631 int i;
632 idStr result;
633
634 i = Length();
635 if ( i == 0 || len <= 0 || start >= i ) {
636 return result;
637 }
638
639 if ( start + len >= i ) {
640 len = i - start;
641 }
642
643 result.Append( &data[ start ], len );
644 return result;
645 }
646
647 /*
648 ============
649 idStr::StripTrailingWhitespace
650 ============
651 */
StripTrailingWhitespace(void)652 void idStr::StripTrailingWhitespace( void ) {
653 int i;
654
655 // cast to unsigned char to prevent stripping off high-ASCII characters
656 for( i = Length(); i > 0 && (unsigned char)(data[ i - 1 ]) <= ' '; i-- ) {
657 data[ i - 1 ] = '\0';
658 len--;
659 }
660 }
661
662 /*
663 ============
664 idStr::StripQuotes
665
666 Removes the quotes from the beginning and end of the string
667 ============
668 */
StripQuotes(void)669 idStr& idStr::StripQuotes ( void )
670 {
671 if ( data[0] != '\"' )
672 {
673 return *this;
674 }
675
676 // Remove the trailing quote first
677 if ( data[len-1] == '\"' )
678 {
679 data[len-1] = '\0';
680 len--;
681 }
682
683 // Strip the leading quote now
684 len--;
685 memmove( &data[ 0 ], &data[ 1 ], len );
686 data[len] = '\0';
687
688 return *this;
689 }
690
691 /*
692 =====================================================================
693
694 filename methods
695
696 =====================================================================
697 */
698
699 /*
700 ============
701 idStr::FileNameHash
702 ============
703 */
FileNameHash(void) const704 int idStr::FileNameHash( void ) const {
705 int i;
706 int hash;
707 char letter;
708
709 hash = 0;
710 i = 0;
711 while( data[i] != '\0' ) {
712 letter = idStr::ToLower( data[i] );
713 if ( letter == '.' ) {
714 break; // don't include extension
715 }
716 if ( letter =='\\' ) {
717 letter = '/';
718 }
719 hash += (int)(letter)*(i+119);
720 i++;
721 }
722 hash &= (FILE_HASH_SIZE-1);
723 return hash;
724 }
725
726 /*
727 ============
728 idStr::BackSlashesToSlashes
729 ============
730 */
BackSlashesToSlashes(void)731 idStr &idStr::BackSlashesToSlashes( void ) {
732 int i;
733
734 for ( i = 0; i < len; i++ ) {
735 if ( data[ i ] == '\\' ) {
736 data[ i ] = '/';
737 }
738 }
739 return *this;
740 }
741
742 /*
743 ============
744 idStr::SetFileExtension
745 ============
746 */
SetFileExtension(const char * extension)747 idStr &idStr::SetFileExtension( const char *extension ) {
748 StripFileExtension();
749 if ( *extension != '.' ) {
750 Append( '.' );
751 }
752 Append( extension );
753 return *this;
754 }
755
756 // DG: helper-function that returns true if the character c is a directory separator
757 // on the current platform
isDirSeparator(int c)758 static ID_INLINE bool isDirSeparator( int c )
759 {
760 if ( c == '/' ) {
761 return true;
762 }
763 #ifdef _WIN32
764 if ( c == '\\' ) {
765 return true;
766 }
767 #elif defined(__AROS__)
768 if ( c == ':' ) {
769 return true;
770 }
771 #endif
772 return false;
773 }
774 // DG end
775
776 /*
777 ============
778 idStr::StripFileExtension
779 ============
780 */
StripFileExtension(void)781 idStr &idStr::StripFileExtension( void ) {
782 int i;
783
784 for ( i = len-1; i >= 0; i-- ) {
785 // DG: we're at a directory separator, nothing to strip at filename
786 if ( isDirSeparator( data[i] ) ) {
787 break;
788 } // DG end
789 if ( data[i] == '.' ) {
790 data[i] = '\0';
791 len = i;
792 break;
793 }
794 }
795 return *this;
796 }
797
798 /*
799 ============
800 idStr::StripAbsoluteFileExtension
801 ============
802 */
StripAbsoluteFileExtension(void)803 idStr &idStr::StripAbsoluteFileExtension( void ) {
804 int i;
805 // FIXME DG: seems like this is unused, but it probably doesn't do what's expected
806 // (if you wanna strip .tar.gz this will fail with dots in path,
807 // if you indeed wanna strip the first dot in *path* (even in some directory) this is right)
808 for ( i = 0; i < len; i++ ) {
809 if ( data[i] == '.' ) {
810 data[i] = '\0';
811 len = i;
812 break;
813 }
814 }
815
816 return *this;
817 }
818
819 /*
820 ==================
821 idStr::DefaultFileExtension
822 ==================
823 */
DefaultFileExtension(const char * extension)824 idStr &idStr::DefaultFileExtension( const char *extension ) {
825 int i;
826
827 // do nothing if the string already has an extension
828 for ( i = len-1; i >= 0; i-- ) {
829 // DG: we're at a directory separator, there was no file extension
830 if ( isDirSeparator( data[i] ) ) {
831 break;
832 } // DG end
833 if ( data[i] == '.' ) {
834 return *this;
835 }
836 }
837 if ( *extension != '.' ) {
838 Append( '.' );
839 }
840 Append( extension );
841 return *this;
842 }
843
844 /*
845 ==================
846 idStr::DefaultPath
847 ==================
848 */
DefaultPath(const char * basepath)849 idStr &idStr::DefaultPath( const char *basepath ) {
850 if ( isDirSeparator( ( *this )[ 0 ] ) ) {
851 // absolute path location
852 return *this;
853 }
854
855 *this = basepath + *this;
856 return *this;
857 }
858
859 /*
860 ====================
861 idStr::AppendPath
862 ====================
863 */
AppendPath(const char * text)864 void idStr::AppendPath( const char *text ) {
865 int pos;
866 int i = 0;
867
868 if ( text && text[i] ) {
869 pos = len;
870 EnsureAlloced( len + strlen( text ) + 2 );
871
872 if ( pos ) {
873 if ( !isDirSeparator( data[ pos-1 ] ) ) {
874 data[ pos++ ] = '/';
875 }
876 }
877
878 if ( isDirSeparator( text[ i ] ) ) {
879 i++;
880 }
881
882 for ( ; text[ i ]; i++ ) {
883 if ( text[ i ] == '\\' ) {
884 data[ pos++ ] = '/';
885 } else {
886 data[ pos++ ] = text[ i ];
887 }
888 }
889 len = pos;
890 data[ pos ] = '\0';
891 }
892 }
893
894 /*
895 ==================
896 idStr::StripFilename
897 ==================
898 */
StripFilename(void)899 idStr &idStr::StripFilename( void ) {
900 int pos;
901
902 pos = Length() - 1;
903 while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos ] ) ) {
904 pos--;
905 }
906
907 if ( pos < 0 ) {
908 pos = 0;
909 }
910
911 CapLength( pos );
912 return *this;
913 }
914
915 /*
916 ==================
917 idStr::StripPath
918 ==================
919 */
StripPath(void)920 idStr &idStr::StripPath( void ) {
921 int pos;
922
923 pos = Length();
924 while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
925 pos--;
926 }
927
928 *this = Right( Length() - pos );
929 return *this;
930 }
931
932 /*
933 ====================
934 idStr::ExtractFilePath
935 ====================
936 */
ExtractFilePath(idStr & dest) const937 void idStr::ExtractFilePath( idStr &dest ) const {
938 int pos;
939
940 //
941 // back up until a \ or the start
942 //
943 pos = Length();
944 while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
945 pos--;
946 }
947
948 Left( pos, dest );
949 }
950
951 /*
952 ====================
953 idStr::ExtractFileName
954 ====================
955 */
ExtractFileName(idStr & dest) const956 void idStr::ExtractFileName( idStr &dest ) const {
957 int pos;
958
959 //
960 // back up until a \ or the start
961 //
962 pos = Length() - 1;
963 while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
964 pos--;
965 }
966
967 Right( Length() - pos, dest );
968 }
969
970 /*
971 ====================
972 idStr::ExtractFileBase
973 ====================
974 */
ExtractFileBase(idStr & dest) const975 void idStr::ExtractFileBase( idStr &dest ) const {
976 int pos;
977 int start;
978
979 //
980 // back up until a \ or the start
981 //
982 pos = Length() - 1;
983 while( ( pos > 0 ) && !isDirSeparator( ( *this )[ pos - 1 ] ) ) {
984 pos--;
985 }
986
987 start = pos;
988 while( ( pos < Length() ) && ( ( *this )[ pos ] != '.' ) ) {
989 pos++;
990 }
991
992 Mid( start, pos - start, dest );
993 }
994
995 /*
996 ====================
997 idStr::ExtractFileExtension
998 ====================
999 */
ExtractFileExtension(idStr & dest) const1000 void idStr::ExtractFileExtension( idStr &dest ) const {
1001 int pos;
1002
1003 //
1004 // back up until a . or the start
1005 //
1006 pos = Length() - 1;
1007 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '.' ) ) {
1008 pos--;
1009 if( isDirSeparator( ( *this )[ pos ] ) ) { // DG: check for directory separator
1010 // no extension in the whole filename
1011 dest.Empty();
1012 } // DG end
1013 }
1014
1015 if ( !pos ) {
1016 // no extension
1017 dest.Empty();
1018 } else {
1019 Right( Length() - pos, dest );
1020 }
1021 }
1022
1023
1024 /*
1025 =====================================================================
1026
1027 char * methods to replace library functions
1028
1029 =====================================================================
1030 */
1031
1032 /*
1033 ============
1034 idStr::IsNumeric
1035
1036 Checks a string to see if it contains only numerical values.
1037 ============
1038 */
IsNumeric(const char * s)1039 bool idStr::IsNumeric( const char *s ) {
1040 int i;
1041 bool dot;
1042
1043 if ( *s == '-' ) {
1044 s++;
1045 }
1046
1047 dot = false;
1048 for ( i = 0; s[i]; i++ ) {
1049 if ( !isdigit( s[i] ) ) {
1050 if ( ( s[ i ] == '.' ) && !dot ) {
1051 dot = true;
1052 continue;
1053 }
1054 return false;
1055 }
1056 }
1057
1058 return true;
1059 }
1060
1061 /*
1062 ============
1063 idStr::HasLower
1064
1065 Checks if a string has any lowercase chars
1066 ============
1067 */
HasLower(const char * s)1068 bool idStr::HasLower( const char *s ) {
1069 if ( !s ) {
1070 return false;
1071 }
1072
1073 while ( *s ) {
1074 if ( CharIsLower( *s ) ) {
1075 return true;
1076 }
1077 s++;
1078 }
1079
1080 return false;
1081 }
1082
1083 /*
1084 ============
1085 idStr::HasUpper
1086
1087 Checks if a string has any uppercase chars
1088 ============
1089 */
HasUpper(const char * s)1090 bool idStr::HasUpper( const char *s ) {
1091 if ( !s ) {
1092 return false;
1093 }
1094
1095 while ( *s ) {
1096 if ( CharIsUpper( *s ) ) {
1097 return true;
1098 }
1099 s++;
1100 }
1101
1102 return false;
1103 }
1104
1105 /*
1106 ================
1107 idStr::Cmp
1108 ================
1109 */
Cmp(const char * s1,const char * s2)1110 int idStr::Cmp( const char *s1, const char *s2 ) {
1111 int c1, c2, d;
1112
1113 do {
1114 c1 = *s1++;
1115 c2 = *s2++;
1116
1117 d = c1 - c2;
1118 if ( d ) {
1119 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1120 }
1121 } while( c1 );
1122
1123 return 0; // strings are equal
1124 }
1125
1126 /*
1127 ================
1128 idStr::Cmpn
1129 ================
1130 */
Cmpn(const char * s1,const char * s2,int n)1131 int idStr::Cmpn( const char *s1, const char *s2, int n ) {
1132 int c1, c2, d;
1133
1134 assert( n >= 0 );
1135
1136 do {
1137 c1 = *s1++;
1138 c2 = *s2++;
1139
1140 if ( !n-- ) {
1141 return 0; // strings are equal until end point
1142 }
1143
1144 d = c1 - c2;
1145 if ( d ) {
1146 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1147 }
1148 } while( c1 );
1149
1150 return 0; // strings are equal
1151 }
1152
1153 /*
1154 ================
1155 idStr::Icmp
1156 ================
1157 */
Icmp(const char * s1,const char * s2)1158 int idStr::Icmp( const char *s1, const char *s2 ) {
1159 int c1, c2, d;
1160
1161 do {
1162 c1 = *s1++;
1163 c2 = *s2++;
1164
1165 d = c1 - c2;
1166 while( d ) {
1167 if ( c1 <= 'Z' && c1 >= 'A' ) {
1168 d += ('a' - 'A');
1169 if ( !d ) {
1170 break;
1171 }
1172 }
1173 if ( c2 <= 'Z' && c2 >= 'A' ) {
1174 d -= ('a' - 'A');
1175 if ( !d ) {
1176 break;
1177 }
1178 }
1179 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1180 }
1181 } while( c1 );
1182
1183 return 0; // strings are equal
1184 }
1185
1186 /*
1187 ================
1188 idStr::Icmpn
1189 ================
1190 */
Icmpn(const char * s1,const char * s2,int n)1191 int idStr::Icmpn( const char *s1, const char *s2, int n ) {
1192 int c1, c2, d;
1193
1194 assert( n >= 0 );
1195
1196 do {
1197 c1 = *s1++;
1198 c2 = *s2++;
1199
1200 if ( !n-- ) {
1201 return 0; // strings are equal until end point
1202 }
1203
1204 d = c1 - c2;
1205 while( d ) {
1206 if ( c1 <= 'Z' && c1 >= 'A' ) {
1207 d += ('a' - 'A');
1208 if ( !d ) {
1209 break;
1210 }
1211 }
1212 if ( c2 <= 'Z' && c2 >= 'A' ) {
1213 d -= ('a' - 'A');
1214 if ( !d ) {
1215 break;
1216 }
1217 }
1218 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1219 }
1220 } while( c1 );
1221
1222 return 0; // strings are equal
1223 }
1224
1225 /*
1226 ================
1227 idStr::Icmp
1228 ================
1229 */
IcmpNoColor(const char * s1,const char * s2)1230 int idStr::IcmpNoColor( const char *s1, const char *s2 ) {
1231 int c1, c2, d;
1232
1233 do {
1234 while ( idStr::IsColor( s1 ) ) {
1235 s1 += 2;
1236 }
1237 while ( idStr::IsColor( s2 ) ) {
1238 s2 += 2;
1239 }
1240 c1 = *s1++;
1241 c2 = *s2++;
1242
1243 d = c1 - c2;
1244 while( d ) {
1245 if ( c1 <= 'Z' && c1 >= 'A' ) {
1246 d += ('a' - 'A');
1247 if ( !d ) {
1248 break;
1249 }
1250 }
1251 if ( c2 <= 'Z' && c2 >= 'A' ) {
1252 d -= ('a' - 'A');
1253 if ( !d ) {
1254 break;
1255 }
1256 }
1257 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1258 }
1259 } while( c1 );
1260
1261 return 0; // strings are equal
1262 }
1263
1264 /*
1265 ================
1266 idStr::IcmpPath
1267 ================
1268 */
IcmpPath(const char * s1,const char * s2)1269 int idStr::IcmpPath( const char *s1, const char *s2 ) {
1270 int c1, c2, d;
1271
1272 #if 0
1273 //#if !defined( _WIN32 )
1274 idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
1275 #endif
1276
1277 do {
1278 c1 = *s1++;
1279 c2 = *s2++;
1280
1281 d = c1 - c2;
1282 while( d ) {
1283 if ( c1 <= 'Z' && c1 >= 'A' ) {
1284 d += ('a' - 'A');
1285 if ( !d ) {
1286 break;
1287 }
1288 }
1289 if ( c1 == '\\' ) {
1290 d += ('/' - '\\');
1291 if ( !d ) {
1292 break;
1293 }
1294 }
1295 if ( c2 <= 'Z' && c2 >= 'A' ) {
1296 d -= ('a' - 'A');
1297 if ( !d ) {
1298 break;
1299 }
1300 }
1301 if ( c2 == '\\' ) {
1302 d -= ('/' - '\\');
1303 if ( !d ) {
1304 break;
1305 }
1306 }
1307 // make sure folders come first
1308 while( c1 ) {
1309 if ( c1 == '/' || c1 == '\\' ) {
1310 break;
1311 }
1312 c1 = *s1++;
1313 }
1314 while( c2 ) {
1315 if ( c2 == '/' || c2 == '\\' ) {
1316 break;
1317 }
1318 c2 = *s2++;
1319 }
1320 if ( c1 && !c2 ) {
1321 return -1;
1322 } else if ( !c1 && c2 ) {
1323 return 1;
1324 }
1325 // same folder depth so use the regular compare
1326 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1327 }
1328 } while( c1 );
1329
1330 return 0;
1331 }
1332
1333 /*
1334 ================
1335 idStr::IcmpnPath
1336 ================
1337 */
IcmpnPath(const char * s1,const char * s2,int n)1338 int idStr::IcmpnPath( const char *s1, const char *s2, int n ) {
1339 int c1, c2, d;
1340
1341 #if 0
1342 //#if !defined( _WIN32 )
1343 idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" );
1344 #endif
1345
1346 assert( n >= 0 );
1347
1348 do {
1349 c1 = *s1++;
1350 c2 = *s2++;
1351
1352 if ( !n-- ) {
1353 return 0; // strings are equal until end point
1354 }
1355
1356 d = c1 - c2;
1357 while( d ) {
1358 if ( c1 <= 'Z' && c1 >= 'A' ) {
1359 d += ('a' - 'A');
1360 if ( !d ) {
1361 break;
1362 }
1363 }
1364 if ( c1 == '\\' ) {
1365 d += ('/' - '\\');
1366 if ( !d ) {
1367 break;
1368 }
1369 }
1370 if ( c2 <= 'Z' && c2 >= 'A' ) {
1371 d -= ('a' - 'A');
1372 if ( !d ) {
1373 break;
1374 }
1375 }
1376 if ( c2 == '\\' ) {
1377 d -= ('/' - '\\');
1378 if ( !d ) {
1379 break;
1380 }
1381 }
1382 // make sure folders come first
1383 while( c1 ) {
1384 if ( c1 == '/' || c1 == '\\' ) {
1385 break;
1386 }
1387 c1 = *s1++;
1388 }
1389 while( c2 ) {
1390 if ( c2 == '/' || c2 == '\\' ) {
1391 break;
1392 }
1393 c2 = *s2++;
1394 }
1395 if ( c1 && !c2 ) {
1396 return -1;
1397 } else if ( !c1 && c2 ) {
1398 return 1;
1399 }
1400 // same folder depth so use the regular compare
1401 return ( INTSIGNBITNOTSET( d ) << 1 ) - 1;
1402 }
1403 } while( c1 );
1404
1405 return 0;
1406 }
1407
1408 /*
1409 =============
1410 idStr::Copynz
1411
1412 Safe strncpy that ensures a trailing zero
1413 =============
1414 */
Copynz(char * dest,const char * src,int destsize)1415 void idStr::Copynz( char *dest, const char *src, int destsize ) {
1416 if ( !src ) {
1417 idLib::common->Warning( "idStr::Copynz: NULL src" );
1418 return;
1419 }
1420 if ( destsize < 1 ) {
1421 idLib::common->Warning( "idStr::Copynz: destsize < 1" );
1422 return;
1423 }
1424
1425 strncpy( dest, src, destsize-1 );
1426 dest[destsize-1] = 0;
1427 }
1428
1429 /*
1430 ================
1431 idStr::Append
1432
1433 never goes past bounds or leaves without a terminating 0
1434 ================
1435 */
Append(char * dest,int size,const char * src)1436 void idStr::Append( char *dest, int size, const char *src ) {
1437 int l1;
1438
1439 l1 = strlen( dest );
1440 if ( l1 >= size ) {
1441 idLib::common->Error( "idStr::Append: already overflowed" );
1442 }
1443 idStr::Copynz( dest + l1, src, size - l1 );
1444 }
1445
1446 /*
1447 ================
1448 idStr::LengthWithoutColors
1449 ================
1450 */
LengthWithoutColors(const char * s)1451 int idStr::LengthWithoutColors( const char *s ) {
1452 int len;
1453 const char *p;
1454
1455 if ( !s ) {
1456 return 0;
1457 }
1458
1459 len = 0;
1460 p = s;
1461 while( *p ) {
1462 if ( idStr::IsColor( p ) ) {
1463 p += 2;
1464 continue;
1465 }
1466 p++;
1467 len++;
1468 }
1469
1470 return len;
1471 }
1472
1473 /*
1474 ================
1475 idStr::RemoveColors
1476 ================
1477 */
RemoveColors(char * string)1478 char *idStr::RemoveColors( char *string ) {
1479 char *d;
1480 char *s;
1481 int c;
1482
1483 s = string;
1484 d = string;
1485 while( (c = *s) != 0 ) {
1486 if ( idStr::IsColor( s ) ) {
1487 s++;
1488 }
1489 else {
1490 *d++ = c;
1491 }
1492 s++;
1493 }
1494 *d = '\0';
1495
1496 return string;
1497 }
1498
1499 /*
1500 ================
1501 idStr::snPrintf
1502 ================
1503 */
snPrintf(char * dest,int size,const char * fmt,...)1504 int idStr::snPrintf( char *dest, int size, const char *fmt, ...) {
1505 int len;
1506 va_list argptr;
1507 char buffer[32000]; // big, but small enough to fit in PPC stack
1508
1509 va_start( argptr, fmt );
1510 len = vsprintf( buffer, fmt, argptr );
1511 va_end( argptr );
1512 if ( len >= sizeof( buffer ) ) {
1513 idLib::common->Error( "idStr::snPrintf: overflowed buffer" );
1514 }
1515 if ( len >= size ) {
1516 idLib::common->Warning( "idStr::snPrintf: overflow of %i in %i\n", len, size );
1517 len = size;
1518 }
1519 idStr::Copynz( dest, buffer, size );
1520 return len;
1521 }
1522
1523 /*
1524 ============
1525 idStr::vsnPrintf
1526
1527 vsnprintf portability:
1528
1529 C99 standard: vsnprintf returns the number of characters (excluding the trailing
1530 '\0') which would have been written to the final string if enough space had been available
1531 snprintf and vsnprintf do not write more than size bytes (including the trailing '\0')
1532
1533 win32: _vsnprintf returns the number of characters written, not including the terminating null character,
1534 or a negative value if an output error occurs. If the number of characters to write exceeds count, then count
1535 characters are written and -1 is returned and no trailing '\0' is added.
1536
1537 idStr::vsnPrintf: always appends a trailing '\0', returns number of characters written (not including terminal \0)
1538 or returns -1 on failure or if the buffer would be overflowed.
1539 ============
1540 */
vsnPrintf(char * dest,int size,const char * fmt,va_list argptr)1541 int idStr::vsnPrintf( char *dest, int size, const char *fmt, va_list argptr ) {
1542 int ret;
1543
1544 #ifdef _WIN32
1545 #undef _vsnprintf
1546 ret = _vsnprintf( dest, size-1, fmt, argptr );
1547 #define _vsnprintf use_idStr_vsnPrintf
1548 #else
1549 #undef vsnprintf
1550 ret = vsnprintf( dest, size, fmt, argptr );
1551 #define vsnprintf use_idStr_vsnPrintf
1552 #endif
1553 dest[size-1] = '\0';
1554 if ( ret < 0 || ret >= size ) {
1555 return -1;
1556 }
1557 return ret;
1558 }
1559
1560 /*
1561 ============
1562 sprintf
1563
1564 Sets the value of the string using a printf interface.
1565 ============
1566 */
sprintf(idStr & string,const char * fmt,...)1567 int sprintf( idStr &string, const char *fmt, ... ) {
1568 int l;
1569 va_list argptr;
1570 char buffer[32000];
1571
1572 va_start( argptr, fmt );
1573 l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
1574 va_end( argptr );
1575 buffer[sizeof(buffer)-1] = '\0';
1576
1577 string = buffer;
1578 return l;
1579 }
1580
1581 /*
1582 ============
1583 vsprintf
1584
1585 Sets the value of the string using a vprintf interface.
1586 ============
1587 */
vsprintf(idStr & string,const char * fmt,va_list argptr)1588 int vsprintf( idStr &string, const char *fmt, va_list argptr ) {
1589 int l;
1590 char buffer[32000];
1591
1592 l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr );
1593 buffer[sizeof(buffer)-1] = '\0';
1594
1595 string = buffer;
1596 return l;
1597 }
1598
1599 /*
1600 ============
1601 va
1602
1603 does a varargs printf into a temp buffer
1604 NOTE: not thread safe
1605 ============
1606 */
va(const char * fmt,...)1607 char *va( const char *fmt, ... ) {
1608 va_list argptr;
1609 static int index = 0;
1610 static char string[4][16384]; // in case called by nested functions
1611 char *buf;
1612
1613 buf = string[index];
1614 index = (index + 1) & 3;
1615
1616 va_start( argptr, fmt );
1617 vsprintf( buf, fmt, argptr );
1618 va_end( argptr );
1619
1620 return buf;
1621 }
1622
1623
1624
1625 /*
1626 ============
1627 idStr::BestUnit
1628 ============
1629 */
BestUnit(const char * format,float value,Measure_t measure)1630 int idStr::BestUnit( const char *format, float value, Measure_t measure ) {
1631 int unit = 1;
1632 while ( unit <= 3 && ( 1 << ( unit * 10 ) < value ) ) {
1633 unit++;
1634 }
1635 unit--;
1636 value /= 1 << ( unit * 10 );
1637 sprintf( *this, format, value );
1638 *this += " ";
1639 *this += units[ measure ][ unit ];
1640 return unit;
1641 }
1642
1643 /*
1644 ============
1645 idStr::SetUnit
1646 ============
1647 */
SetUnit(const char * format,float value,int unit,Measure_t measure)1648 void idStr::SetUnit( const char *format, float value, int unit, Measure_t measure ) {
1649 value /= 1 << ( unit * 10 );
1650 sprintf( *this, format, value );
1651 *this += " ";
1652 *this += units[ measure ][ unit ];
1653 }
1654
1655 /*
1656 ================
1657 idStr::InitMemory
1658 ================
1659 */
InitMemory(void)1660 void idStr::InitMemory( void ) {
1661 #ifdef USE_STRING_DATA_ALLOCATOR
1662 stringDataAllocator.Init();
1663 #endif
1664 }
1665
1666 /*
1667 ================
1668 idStr::ShutdownMemory
1669 ================
1670 */
ShutdownMemory(void)1671 void idStr::ShutdownMemory( void ) {
1672 #ifdef USE_STRING_DATA_ALLOCATOR
1673 stringDataAllocator.Shutdown();
1674 #endif
1675 }
1676
1677 /*
1678 ================
1679 idStr::PurgeMemory
1680 ================
1681 */
PurgeMemory(void)1682 void idStr::PurgeMemory( void ) {
1683 #ifdef USE_STRING_DATA_ALLOCATOR
1684 stringDataAllocator.FreeEmptyBaseBlocks();
1685 #endif
1686 }
1687
1688 /*
1689 ================
1690 idStr::ShowMemoryUsage_f
1691 ================
1692 */
ShowMemoryUsage_f(const idCmdArgs & args)1693 void idStr::ShowMemoryUsage_f( const idCmdArgs &args ) {
1694 #ifdef USE_STRING_DATA_ALLOCATOR
1695 idLib::common->Printf( "%6d KB string memory (%d KB free in %d blocks, %d empty base blocks)\n",
1696 stringDataAllocator.GetBaseBlockMemory() >> 10, stringDataAllocator.GetFreeBlockMemory() >> 10,
1697 stringDataAllocator.GetNumFreeBlocks(), stringDataAllocator.GetNumEmptyBaseBlocks() );
1698 #endif
1699 }
1700
1701 /*
1702 ================
1703 idStr::FormatNumber
1704 ================
1705 */
1706 struct formatList_t {
1707 int gran;
1708 int count;
1709 };
1710
1711 // elements of list need to decend in size
1712 formatList_t formatList[] = {
1713 { 1000000000, 0 },
1714 { 1000000, 0 },
1715 { 1000, 0 }
1716 };
1717
1718 int numFormatList = sizeof(formatList) / sizeof( formatList[0] );
1719
1720
FormatNumber(int number)1721 idStr idStr::FormatNumber( int number ) {
1722 idStr string;
1723 bool hit;
1724
1725 // reset
1726 for ( int i = 0; i < numFormatList; i++ ) {
1727 formatList_t *li = formatList + i;
1728 li->count = 0;
1729 }
1730
1731 // main loop
1732 do {
1733 hit = false;
1734
1735 for ( int i = 0; i < numFormatList; i++ ) {
1736 formatList_t *li = formatList + i;
1737
1738 if ( number >= li->gran ) {
1739 li->count++;
1740 number -= li->gran;
1741 hit = true;
1742 break;
1743 }
1744 }
1745 } while ( hit );
1746
1747 // print out
1748 bool found = false;
1749
1750 for ( int i = 0; i < numFormatList; i++ ) {
1751 formatList_t *li = formatList + i;
1752
1753 if ( li->count ) {
1754 if ( !found ) {
1755 string += va( "%i,", li->count );
1756 } else {
1757 string += va( "%3.3i,", li->count );
1758 }
1759 found = true;
1760 }
1761 else if ( found ) {
1762 string += va( "%3.3i,", li->count );
1763 }
1764 }
1765
1766 if ( found ) {
1767 string += va( "%3.3i", number );
1768 }
1769 else {
1770 string += va( "%i", number );
1771 }
1772
1773 // pad to proper size
1774 int count = 11 - string.Length();
1775
1776 for ( int i = 0; i < count; i++ ) {
1777 string.Insert( " ", 0 );
1778 }
1779
1780 return string;
1781 }
1782