Str.cpp (39829B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition 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 BFG Edition 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 "precompiled.h" 30 #pragma hdrstop 31 32 #ifdef USE_STRING_DATA_ALLOCATOR 33 static idDynamicBlockAlloc<char, 1<<18, 128, TAG_STRING> stringDataAllocator; 34 #endif 35 36 idVec4 g_color_table[16] = 37 { 38 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 39 idVec4(1.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_RED 40 idVec4(0.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_GREEN 41 idVec4(1.0f, 1.0f, 0.0f, 1.0f), // S_COLOR_YELLOW 42 idVec4(0.0f, 0.0f, 1.0f, 1.0f), // S_COLOR_BLUE 43 idVec4(0.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_CYAN 44 idVec4(1.0f, 0.5f, 0.0f, 1.0f), // S_COLOR_ORANGE 45 idVec4(1.0f, 1.0f, 1.0f, 1.0f), // S_COLOR_WHITE 46 idVec4(0.5f, 0.5f, 0.5f, 1.0f), // S_COLOR_GRAY 47 idVec4(0.0f, 0.0f, 0.0f, 1.0f), // S_COLOR_BLACK 48 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 49 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 50 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 51 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 52 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 53 idVec4(0.0f, 0.0f, 0.0f, 1.0f), 54 }; 55 56 const char *units[2][4] = 57 { 58 { "B", "KB", "MB", "GB" }, 59 { "B/s", "KB/s", "MB/s", "GB/s" } 60 }; 61 62 /* 63 ============ 64 idStr::ColorForIndex 65 ============ 66 */ 67 idVec4 & idStr::ColorForIndex( int i ) { 68 return g_color_table[ i & 15 ]; 69 } 70 71 /* 72 ============ 73 idStr::ReAllocate 74 ============ 75 */ 76 void idStr::ReAllocate( int amount, bool keepold ) { 77 char *newbuffer; 78 int newsize; 79 int mod; 80 81 //assert( data ); 82 assert( amount > 0 ); 83 84 mod = amount % STR_ALLOC_GRAN; 85 if ( !mod ) { 86 newsize = amount; 87 } 88 else { 89 newsize = amount + STR_ALLOC_GRAN - mod; 90 } 91 SetAlloced( newsize ); 92 93 #ifdef USE_STRING_DATA_ALLOCATOR 94 newbuffer = stringDataAllocator.Alloc( GetAlloced() ); 95 #else 96 newbuffer = new (TAG_STRING) char[ GetAlloced() ]; 97 #endif 98 if ( keepold && data ) { 99 data[ len ] = '\0'; 100 strcpy( newbuffer, data ); 101 } 102 103 if ( data && data != baseBuffer ) { 104 #ifdef USE_STRING_DATA_ALLOCATOR 105 stringDataAllocator.Free( data ); 106 #else 107 delete [] data; 108 #endif 109 } 110 111 data = newbuffer; 112 } 113 114 /* 115 ============ 116 idStr::FreeData 117 ============ 118 */ 119 void idStr::FreeData() { 120 if ( IsStatic() ) { 121 return; 122 } 123 124 if ( data && data != baseBuffer ) { 125 #ifdef USE_STRING_DATA_ALLOCATOR 126 stringDataAllocator.Free( data ); 127 #else 128 delete[] data; 129 #endif 130 data = baseBuffer; 131 } 132 } 133 134 /* 135 ============ 136 idStr::operator= 137 ============ 138 */ 139 void idStr::operator=( const char *text ) { 140 int l; 141 int diff; 142 int i; 143 144 if ( !text ) { 145 // safe behavior if NULL 146 EnsureAlloced( 1, false ); 147 data[ 0 ] = '\0'; 148 len = 0; 149 return; 150 } 151 152 if ( text == data ) { 153 return; // copying same thing 154 } 155 156 // check if we're aliasing 157 if ( text >= data && text <= data + len ) { 158 diff = text - data; 159 160 assert( strlen( text ) < (unsigned)len ); 161 162 for ( i = 0; text[ i ]; i++ ) { 163 data[ i ] = text[ i ]; 164 } 165 166 data[ i ] = '\0'; 167 168 len -= diff; 169 170 return; 171 } 172 173 l = strlen( text ); 174 EnsureAlloced( l + 1, false ); 175 strcpy( data, text ); 176 len = l; 177 } 178 179 /* 180 ============ 181 idStr::FindChar 182 183 returns -1 if not found otherwise the index of the char 184 ============ 185 */ 186 int idStr::FindChar( const char *str, const char c, int start, int end ) { 187 int i; 188 189 if ( end == -1 ) { 190 end = strlen( str ) - 1; 191 } 192 for ( i = start; i <= end; i++ ) { 193 if ( str[i] == c ) { 194 return i; 195 } 196 } 197 return -1; 198 } 199 200 /* 201 ============ 202 idStr::FindText 203 204 returns -1 if not found otherwise the index of the text 205 ============ 206 */ 207 int idStr::FindText( const char *str, const char *text, bool casesensitive, int start, int end ) { 208 int l, i, j; 209 210 if ( end == -1 ) { 211 end = strlen( str ); 212 } 213 l = end - strlen( text ); 214 for ( i = start; i <= l; i++ ) { 215 if ( casesensitive ) { 216 for ( j = 0; text[j]; j++ ) { 217 if ( str[i+j] != text[j] ) { 218 break; 219 } 220 } 221 } else { 222 for ( j = 0; text[j]; j++ ) { 223 if ( ::toupper( str[i+j] ) != ::toupper( text[j] ) ) { 224 break; 225 } 226 } 227 } 228 if ( !text[j] ) { 229 return i; 230 } 231 } 232 return -1; 233 } 234 235 /* 236 ============ 237 idStr::Filter 238 239 Returns true if the string conforms the given filter. 240 Several metacharacter may be used in the filter. 241 242 * match any string of zero or more characters 243 ? match any single character 244 [abc...] match any of the enclosed characters; a hyphen can 245 be used to specify a range (e.g. a-z, A-Z, 0-9) 246 247 ============ 248 */ 249 bool idStr::Filter( const char *filter, const char *name, bool casesensitive ) { 250 idStr buf; 251 int i, found, index; 252 253 while(*filter) { 254 if (*filter == '*') { 255 filter++; 256 buf.Empty(); 257 for (i = 0; *filter; i++) { 258 if ( *filter == '*' || *filter == '?' || (*filter == '[' && *(filter+1) != '[') ) { 259 break; 260 } 261 buf += *filter; 262 if ( *filter == '[' ) { 263 filter++; 264 } 265 filter++; 266 } 267 if ( buf.Length() ) { 268 index = idStr(name).Find( buf.c_str(), casesensitive ); 269 if ( index == -1 ) { 270 return false; 271 } 272 name += index + strlen(buf); 273 } 274 } 275 else if (*filter == '?') { 276 filter++; 277 name++; 278 } 279 else if (*filter == '[') { 280 if ( *(filter+1) == '[' ) { 281 if ( *name != '[' ) { 282 return false; 283 } 284 filter += 2; 285 name++; 286 } 287 else { 288 filter++; 289 found = false; 290 while(*filter && !found) { 291 if (*filter == ']' && *(filter+1) != ']') { 292 break; 293 } 294 if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']')) { 295 if (casesensitive) { 296 if (*name >= *filter && *name <= *(filter+2)) { 297 found = true; 298 } 299 } 300 else { 301 if ( ::toupper(*name) >= ::toupper(*filter) && ::toupper(*name) <= ::toupper(*(filter+2)) ) { 302 found = true; 303 } 304 } 305 filter += 3; 306 } 307 else { 308 if (casesensitive) { 309 if (*filter == *name) { 310 found = true; 311 } 312 } 313 else { 314 if ( ::toupper(*filter) == ::toupper(*name) ) { 315 found = true; 316 } 317 } 318 filter++; 319 } 320 } 321 if (!found) { 322 return false; 323 } 324 while(*filter) { 325 if ( *filter == ']' && *(filter+1) != ']' ) { 326 break; 327 } 328 filter++; 329 } 330 filter++; 331 name++; 332 } 333 } 334 else { 335 if (casesensitive) { 336 if (*filter != *name) { 337 return false; 338 } 339 } 340 else { 341 if ( ::toupper(*filter) != ::toupper(*name) ) { 342 return false; 343 } 344 } 345 filter++; 346 name++; 347 } 348 } 349 return true; 350 } 351 352 /* 353 ============= 354 idStr::StripMediaName 355 356 makes the string lower case, replaces backslashes with forward slashes, and removes extension 357 ============= 358 */ 359 void idStr::StripMediaName( const char *name, idStr &mediaName ) { 360 char c; 361 362 mediaName.Empty(); 363 364 for ( c = *name; c; c = *(++name) ) { 365 // truncate at an extension 366 if ( c == '.' ) { 367 break; 368 } 369 // convert backslashes to forward slashes 370 if ( c == '\\' ) { 371 mediaName.Append( '/' ); 372 } else { 373 mediaName.Append( idStr::ToLower( c ) ); 374 } 375 } 376 } 377 378 /* 379 ============= 380 idStr::CheckExtension 381 ============= 382 */ 383 bool idStr::CheckExtension( const char *name, const char *ext ) { 384 const char *s1 = name + Length( name ) - 1; 385 const char *s2 = ext + Length( ext ) - 1; 386 int c1, c2, d; 387 388 do { 389 c1 = *s1--; 390 c2 = *s2--; 391 392 d = c1 - c2; 393 while( d ) { 394 if ( c1 <= 'Z' && c1 >= 'A' ) { 395 d += ('a' - 'A'); 396 if ( !d ) { 397 break; 398 } 399 } 400 if ( c2 <= 'Z' && c2 >= 'A' ) { 401 d -= ('a' - 'A'); 402 if ( !d ) { 403 break; 404 } 405 } 406 return false; 407 } 408 } while( s1 > name && s2 > ext ); 409 410 return ( s1 >= name ); 411 } 412 413 /* 414 ============= 415 idStr::FloatArrayToString 416 ============= 417 */ 418 const char *idStr::FloatArrayToString( const float *array, const int length, const int precision ) { 419 static int index = 0; 420 static char str[4][16384]; // in case called by nested functions 421 int i, n; 422 char format[16], *s; 423 424 // use an array of string so that multiple calls won't collide 425 s = str[ index ]; 426 index = (index + 1) & 3; 427 428 idStr::snPrintf( format, sizeof( format ), "%%.%df", precision ); 429 n = idStr::snPrintf( s, sizeof( str[0] ), format, array[0] ); 430 if ( precision > 0 ) { 431 while( n > 0 && s[n-1] == '0' ) s[--n] = '\0'; 432 while( n > 0 && s[n-1] == '.' ) s[--n] = '\0'; 433 } 434 idStr::snPrintf( format, sizeof( format ), " %%.%df", precision ); 435 for ( i = 1; i < length; i++ ) { 436 n += idStr::snPrintf( s + n, sizeof( str[0] ) - n, format, array[i] ); 437 if ( precision > 0 ) { 438 while( n > 0 && s[n-1] == '0' ) s[--n] = '\0'; 439 while( n > 0 && s[n-1] == '.' ) s[--n] = '\0'; 440 } 441 } 442 return s; 443 } 444 445 /* 446 ======================== 447 idStr::CStyleQuote 448 ======================== 449 */ 450 const char *idStr::CStyleQuote( const char *str ) { 451 static int index = 0; 452 static char buffers[4][16384]; // in case called by nested functions 453 unsigned int i; 454 char *buf; 455 456 buf = buffers[index]; 457 index = ( index + 1 ) & 3; 458 459 buf[0] = '\"'; 460 for ( i = 1; i < sizeof( buffers[0] ) - 2; i++ ) { 461 int c = *str++; 462 switch( c ) { 463 case '\0': buf[i++] = '\"'; buf[i] = '\0'; return buf; 464 case '\\': buf[i++] = '\\'; buf[i] = '\\'; break; 465 case '\n': buf[i++] = '\\'; buf[i] = 'n'; break; 466 case '\r': buf[i++] = '\\'; buf[i] = 'r'; break; 467 case '\t': buf[i++] = '\\'; buf[i] = 't'; break; 468 case '\v': buf[i++] = '\\'; buf[i] = 'v'; break; 469 case '\b': buf[i++] = '\\'; buf[i] = 'b'; break; 470 case '\f': buf[i++] = '\\'; buf[i] = 'f'; break; 471 case '\a': buf[i++] = '\\'; buf[i] = 'a'; break; 472 case '\'': buf[i++] = '\\'; buf[i] = '\''; break; 473 case '\"': buf[i++] = '\\'; buf[i] = '\"'; break; 474 case '\?': buf[i++] = '\\'; buf[i] = '\?'; break; 475 default: buf[i] = c; break; 476 } 477 } 478 buf[i++] = '\"'; 479 buf[i] = '\0'; 480 return buf; 481 } 482 483 /* 484 ======================== 485 idStr::CStyleUnQuote 486 ======================== 487 */ 488 const char *idStr::CStyleUnQuote( const char *str ) { 489 if ( str[0] != '\"' ) { 490 return str; 491 } 492 493 static int index = 0; 494 static char buffers[4][16384]; // in case called by nested functions 495 unsigned int i; 496 char *buf; 497 498 buf = buffers[index]; 499 index = ( index + 1 ) & 3; 500 501 str++; 502 for ( i = 0; i < sizeof( buffers[0] ) - 1; i++ ) { 503 int c = *str++; 504 if ( c == '\0' ) { 505 break; 506 } else if ( c == '\\' ) { 507 c = *str++; 508 switch( c ) { 509 case '\\': buf[i] = '\\'; break; 510 case 'n': buf[i] = '\n'; break; 511 case 'r': buf[i] = '\r'; break; 512 case 't': buf[i] = '\t'; break; 513 case 'v': buf[i] = '\v'; break; 514 case 'b': buf[i] = '\b'; break; 515 case 'f': buf[i] = '\f'; break; 516 case 'a': buf[i] = '\a'; break; 517 case '\'': buf[i] = '\''; break; 518 case '\"': buf[i] = '\"'; break; 519 case '\?': buf[i] = '\?'; break; 520 } 521 } else { 522 buf[i] = c; 523 } 524 } 525 assert( buf[i-1] == '\"' ); 526 buf[i-1] = '\0'; 527 return buf; 528 } 529 530 /* 531 ============ 532 idStr::Last 533 534 returns -1 if not found otherwise the index of the char 535 ============ 536 */ 537 int idStr::Last( const char c ) const { 538 int i; 539 540 for( i = Length(); i > 0; i-- ) { 541 if ( data[ i - 1 ] == c ) { 542 return i - 1; 543 } 544 } 545 546 return -1; 547 } 548 549 /* 550 ======================== 551 idStr::Format 552 553 perform a threadsafe sprintf to the string 554 ======================== 555 */ 556 void idStr::Format( const char *fmt, ... ) { 557 va_list argptr; 558 char text[MAX_PRINT_MSG]; 559 560 va_start( argptr, fmt ); 561 int len = idStr::vsnPrintf( text, sizeof( text ) - 1, fmt, argptr ); 562 va_end( argptr ); 563 text[ sizeof( text ) - 1 ] = '\0'; 564 565 if ( (size_t)len >= sizeof( text ) - 1 ) { 566 idLib::common->FatalError( "Tried to set a large buffer using %s", fmt ); 567 } 568 *this = text; 569 } 570 571 /* 572 ======================== 573 idStr::FormatInt 574 575 Formats integers with commas for readability. 576 ======================== 577 */ 578 idStr idStr::FormatInt( const int num, bool isCash ) { 579 idStr val = va( "%d", num ); 580 int len = val.Length(); 581 for ( int i = 0 ; i < ( ( len - 1 ) / 3 ); i++ ) { 582 int pos = val.Length() - ( ( i + 1 ) * 3 + i ); 583 if ( pos > 1 || val[0] != '-' ) { 584 val.Insert( ',', pos ); 585 } 586 } 587 588 if ( isCash ) { 589 val.Insert( '$', val[0] == '-' ? 1 : 0 ); 590 } 591 592 return val; 593 } 594 595 /* 596 ============ 597 idStr::StripLeading 598 ============ 599 */ 600 void idStr::StripLeading( const char c ) { 601 while( data[ 0 ] == c ) { 602 memmove( &data[ 0 ], &data[ 1 ], len ); 603 len--; 604 } 605 } 606 607 /* 608 ============ 609 idStr::StripLeading 610 ============ 611 */ 612 void idStr::StripLeading( const char *string ) { 613 int l; 614 615 l = strlen( string ); 616 if ( l > 0 ) { 617 while ( !Cmpn( string, l ) ) { 618 memmove( data, data + l, len - l + 1 ); 619 len -= l; 620 } 621 } 622 } 623 624 /* 625 ============ 626 idStr::StripLeadingOnce 627 ============ 628 */ 629 bool idStr::StripLeadingOnce( const char *string ) { 630 int l; 631 632 l = strlen( string ); 633 if ( ( l > 0 ) && !Cmpn( string, l ) ) { 634 memmove( data, data + l, len - l + 1 ); 635 len -= l; 636 return true; 637 } 638 return false; 639 } 640 641 /* 642 ============ 643 idStr::StripTrailing 644 ============ 645 */ 646 void idStr::StripTrailing( const char c ) { 647 int i; 648 649 for( i = Length(); i > 0 && data[ i - 1 ] == c; i-- ) { 650 data[ i - 1 ] = '\0'; 651 len--; 652 } 653 } 654 655 /* 656 ============ 657 idStr::StripLeading 658 ============ 659 */ 660 void idStr::StripTrailing( const char *string ) { 661 int l; 662 663 l = strlen( string ); 664 if ( l > 0 ) { 665 while ( ( len >= l ) && !Cmpn( string, data + len - l, l ) ) { 666 len -= l; 667 data[len] = '\0'; 668 } 669 } 670 } 671 672 /* 673 ============ 674 idStr::StripTrailingOnce 675 ============ 676 */ 677 bool idStr::StripTrailingOnce( const char *string ) { 678 int l; 679 680 l = strlen( string ); 681 if ( ( l > 0 ) && ( len >= l ) && !Cmpn( string, data + len - l, l ) ) { 682 len -= l; 683 data[len] = '\0'; 684 return true; 685 } 686 return false; 687 } 688 689 /* 690 ============ 691 idStr::Replace 692 ============ 693 */ 694 bool idStr::ReplaceChar( const char old, const char nw ) { 695 bool replaced = false; 696 for ( int i = 0; i < Length(); i++ ) { 697 if ( data[i] == old ) { 698 data[i] = nw; 699 replaced = true; 700 } 701 } 702 return replaced; 703 } 704 705 /* 706 ============ 707 idStr::Replace 708 ============ 709 */ 710 bool idStr::Replace( const char *old, const char *nw ) { 711 int oldLen = strlen( old ); 712 int newLen = strlen( nw ); 713 714 // Work out how big the new string will be 715 int count = 0; 716 for ( int i = 0; i < Length(); i++ ) { 717 if ( idStr::Cmpn( &data[i], old, oldLen ) == 0 ) { 718 count++; 719 i += oldLen - 1; 720 } 721 } 722 723 if ( count ) { 724 idStr oldString( data ); 725 726 EnsureAlloced( len + ( ( newLen - oldLen ) * count ) + 2, false ); 727 728 // Replace the old data with the new data 729 int j = 0; 730 for ( int i = 0; i < oldString.Length(); i++ ) { 731 if ( idStr::Cmpn( &oldString[i], old, oldLen ) == 0 ) { 732 memcpy( data + j, nw, newLen ); 733 i += oldLen - 1; 734 j += newLen; 735 } else { 736 data[j] = oldString[i]; 737 j++; 738 } 739 } 740 data[j] = 0; 741 len = strlen( data ); 742 return true; 743 } 744 return false; 745 } 746 747 /* 748 ============ 749 idStr::Mid 750 ============ 751 */ 752 const char *idStr::Mid( int start, int len, idStr &result ) const { 753 int i; 754 755 result.Empty(); 756 757 i = Length(); 758 if ( i == 0 || len <= 0 || start >= i ) { 759 return NULL; 760 } 761 762 if ( start + len >= i ) { 763 len = i - start; 764 } 765 766 result.Append( &data[ start ], len ); 767 return result; 768 } 769 770 /* 771 ============ 772 idStr::Mid 773 ============ 774 */ 775 idStr idStr::Mid( int start, int len ) const { 776 int i; 777 idStr result; 778 779 i = Length(); 780 if ( i == 0 || len <= 0 || start >= i ) { 781 return result; 782 } 783 784 if ( start + len >= i ) { 785 len = i - start; 786 } 787 788 result.Append( &data[ start ], len ); 789 return result; 790 } 791 792 /* 793 ============ 794 idStr::StripTrailingWhitespace 795 ============ 796 */ 797 void idStr::StripTrailingWhitespace() { 798 int i; 799 800 // cast to unsigned char to prevent stripping off high-ASCII characters 801 for( i = Length(); i > 0 && (unsigned char)(data[ i - 1 ]) <= ' '; i-- ) { 802 data[ i - 1 ] = '\0'; 803 len--; 804 } 805 } 806 807 /* 808 ============ 809 idStr::StripQuotes 810 811 Removes the quotes from the beginning and end of the string 812 ============ 813 */ 814 idStr& idStr::StripQuotes () 815 { 816 if ( data[0] != '\"' ) 817 { 818 return *this; 819 } 820 821 // Remove the trailing quote first 822 if ( data[len-1] == '\"' ) 823 { 824 data[len-1] = '\0'; 825 len--; 826 } 827 828 // Strip the leading quote now 829 len--; 830 memmove( &data[ 0 ], &data[ 1 ], len ); 831 data[len] = '\0'; 832 833 return *this; 834 } 835 836 /* 837 ===================================================================== 838 839 filename methods 840 841 ===================================================================== 842 */ 843 844 /* 845 ============ 846 idStr::FileNameHash 847 ============ 848 */ 849 int idStr::FileNameHash() const { 850 int i; 851 long hash; 852 char letter; 853 854 hash = 0; 855 i = 0; 856 while( data[i] != '\0' ) { 857 letter = idStr::ToLower( data[i] ); 858 if ( letter == '.' ) { 859 break; // don't include extension 860 } 861 if ( letter =='\\' ) { 862 letter = '/'; 863 } 864 hash += (long)(letter)*(i+119); 865 i++; 866 } 867 hash &= (FILE_HASH_SIZE-1); 868 return hash; 869 } 870 871 /* 872 ============ 873 idStr::BackSlashesToSlashes 874 ============ 875 */ 876 idStr &idStr::BackSlashesToSlashes() { 877 int i; 878 879 for ( i = 0; i < len; i++ ) { 880 if ( data[ i ] == '\\' ) { 881 data[ i ] = '/'; 882 } 883 } 884 return *this; 885 } 886 887 /* 888 ============ 889 idStr::SlashesToBackSlashes 890 ============ 891 */ 892 idStr &idStr::SlashesToBackSlashes() { 893 int i; 894 895 for ( i = 0; i < len; i++ ) { 896 if ( data[ i ] == '/' ) { 897 data[ i ] = '\\'; 898 } 899 } 900 return *this; 901 } 902 903 /* 904 ============ 905 idStr::SetFileExtension 906 ============ 907 */ 908 idStr &idStr::SetFileExtension( const char *extension ) { 909 StripFileExtension(); 910 if ( *extension != '.' ) { 911 Append( '.' ); 912 } 913 Append( extension ); 914 return *this; 915 } 916 917 /* 918 ============ 919 idStr::StripFileExtension 920 ============ 921 */ 922 idStr &idStr::StripFileExtension() { 923 int i; 924 925 for ( i = len-1; i >= 0; i-- ) { 926 if ( data[i] == '.' ) { 927 data[i] = '\0'; 928 len = i; 929 break; 930 } 931 } 932 return *this; 933 } 934 935 /* 936 ============ 937 idStr::StripAbsoluteFileExtension 938 ============ 939 */ 940 idStr &idStr::StripAbsoluteFileExtension() { 941 int i; 942 943 for ( i = 0; i < len; i++ ) { 944 if ( data[i] == '.' ) { 945 data[i] = '\0'; 946 len = i; 947 break; 948 } 949 } 950 951 return *this; 952 } 953 954 /* 955 ================== 956 idStr::DefaultFileExtension 957 ================== 958 */ 959 idStr &idStr::DefaultFileExtension( const char *extension ) { 960 int i; 961 962 // do nothing if the string already has an extension 963 for ( i = len-1; i >= 0; i-- ) { 964 if ( data[i] == '.' ) { 965 return *this; 966 } 967 } 968 if ( *extension != '.' ) { 969 Append( '.' ); 970 } 971 Append( extension ); 972 return *this; 973 } 974 975 /* 976 ================== 977 idStr::DefaultPath 978 ================== 979 */ 980 idStr &idStr::DefaultPath( const char *basepath ) { 981 if ( ( ( *this )[ 0 ] == '/' ) || ( ( *this )[ 0 ] == '\\' ) ) { 982 // absolute path location 983 return *this; 984 } 985 986 *this = basepath + *this; 987 return *this; 988 } 989 990 /* 991 ==================== 992 idStr::AppendPath 993 ==================== 994 */ 995 void idStr::AppendPath( const char *text ) { 996 int pos; 997 int i = 0; 998 999 if ( text && text[i] ) { 1000 pos = len; 1001 EnsureAlloced( len + strlen( text ) + 2 ); 1002 1003 if ( pos ) { 1004 if ( data[ pos-1 ] != '/' ) { 1005 data[ pos++ ] = '/'; 1006 } 1007 } 1008 if ( text[i] == '/' ) { 1009 i++; 1010 } 1011 1012 for ( ; text[ i ]; i++ ) { 1013 if ( text[ i ] == '\\' ) { 1014 data[ pos++ ] = '/'; 1015 } else { 1016 data[ pos++ ] = text[ i ]; 1017 } 1018 } 1019 len = pos; 1020 data[ pos ] = '\0'; 1021 } 1022 } 1023 1024 /* 1025 ================== 1026 idStr::StripFilename 1027 ================== 1028 */ 1029 idStr &idStr::StripFilename() { 1030 int pos; 1031 1032 pos = Length() - 1; 1033 while( ( pos > 0 ) && ( ( *this )[ pos ] != '/' ) && ( ( *this )[ pos ] != '\\' ) ) { 1034 pos--; 1035 } 1036 1037 if ( pos < 0 ) { 1038 pos = 0; 1039 } 1040 1041 CapLength( pos ); 1042 return *this; 1043 } 1044 1045 /* 1046 ================== 1047 idStr::StripPath 1048 ================== 1049 */ 1050 idStr &idStr::StripPath() { 1051 int pos; 1052 1053 pos = Length(); 1054 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) { 1055 pos--; 1056 } 1057 1058 *this = Right( Length() - pos ); 1059 return *this; 1060 } 1061 1062 /* 1063 ==================== 1064 idStr::ExtractFilePath 1065 ==================== 1066 */ 1067 void idStr::ExtractFilePath( idStr &dest ) const { 1068 int pos; 1069 1070 // 1071 // back up until a \ or the start 1072 // 1073 pos = Length(); 1074 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) { 1075 pos--; 1076 } 1077 1078 Left( pos, dest ); 1079 } 1080 1081 /* 1082 ==================== 1083 idStr::ExtractFileName 1084 ==================== 1085 */ 1086 void idStr::ExtractFileName( idStr &dest ) const { 1087 int pos; 1088 1089 // 1090 // back up until a \ or the start 1091 // 1092 pos = Length() - 1; 1093 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) { 1094 pos--; 1095 } 1096 1097 Right( Length() - pos, dest ); 1098 } 1099 1100 /* 1101 ==================== 1102 idStr::ExtractFileBase 1103 ==================== 1104 */ 1105 void idStr::ExtractFileBase( idStr &dest ) const { 1106 int pos; 1107 int start; 1108 1109 // 1110 // back up until a \ or the start 1111 // 1112 pos = Length() - 1; 1113 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '/' ) && ( ( *this )[ pos - 1 ] != '\\' ) ) { 1114 pos--; 1115 } 1116 1117 start = pos; 1118 while( ( pos < Length() ) && ( ( *this )[ pos ] != '.' ) ) { 1119 pos++; 1120 } 1121 1122 Mid( start, pos - start, dest ); 1123 } 1124 1125 /* 1126 ==================== 1127 idStr::ExtractFileExtension 1128 ==================== 1129 */ 1130 void idStr::ExtractFileExtension( idStr &dest ) const { 1131 int pos; 1132 1133 // 1134 // back up until a . or the start 1135 // 1136 pos = Length() - 1; 1137 while( ( pos > 0 ) && ( ( *this )[ pos - 1 ] != '.' ) ) { 1138 pos--; 1139 } 1140 1141 if ( !pos ) { 1142 // no extension 1143 dest.Empty(); 1144 } else { 1145 Right( Length() - pos, dest ); 1146 } 1147 } 1148 1149 1150 /* 1151 ===================================================================== 1152 1153 char * methods to replace library functions 1154 1155 ===================================================================== 1156 */ 1157 1158 /* 1159 ============ 1160 idStr::IsNumeric 1161 1162 Checks a string to see if it contains only numerical values. 1163 ============ 1164 */ 1165 bool idStr::IsNumeric( const char *s ) { 1166 int i; 1167 bool dot; 1168 1169 if ( *s == '-' ) { 1170 s++; 1171 } 1172 1173 dot = false; 1174 for ( i = 0; s[i]; i++ ) { 1175 if ( !isdigit( (const unsigned char)s[i] ) ) { 1176 if ( ( s[ i ] == '.' ) && !dot ) { 1177 dot = true; 1178 continue; 1179 } 1180 return false; 1181 } 1182 } 1183 1184 return true; 1185 } 1186 1187 /* 1188 ============ 1189 idStr::HasLower 1190 1191 Checks if a string has any lowercase chars 1192 ============ 1193 */ 1194 bool idStr::HasLower( const char *s ) { 1195 if ( !s ) { 1196 return false; 1197 } 1198 1199 while ( *s ) { 1200 if ( CharIsLower( *s ) ) { 1201 return true; 1202 } 1203 s++; 1204 } 1205 1206 return false; 1207 } 1208 1209 /* 1210 ============ 1211 idStr::HasUpper 1212 1213 Checks if a string has any uppercase chars 1214 ============ 1215 */ 1216 bool idStr::HasUpper( const char *s ) { 1217 if ( !s ) { 1218 return false; 1219 } 1220 1221 while ( *s ) { 1222 if ( CharIsUpper( *s ) ) { 1223 return true; 1224 } 1225 s++; 1226 } 1227 1228 return false; 1229 } 1230 1231 /* 1232 ================ 1233 idStr::Cmp 1234 ================ 1235 */ 1236 int idStr::Cmp( const char *s1, const char *s2 ) { 1237 int c1, c2, d; 1238 1239 do { 1240 c1 = *s1++; 1241 c2 = *s2++; 1242 1243 d = c1 - c2; 1244 if ( d ) { 1245 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1246 } 1247 } while( c1 ); 1248 1249 return 0; // strings are equal 1250 } 1251 1252 /* 1253 ================ 1254 idStr::Cmpn 1255 ================ 1256 */ 1257 int idStr::Cmpn( const char *s1, const char *s2, int n ) { 1258 int c1, c2, d; 1259 1260 assert( n >= 0 ); 1261 1262 do { 1263 c1 = *s1++; 1264 c2 = *s2++; 1265 1266 if ( !n-- ) { 1267 return 0; // strings are equal until end point 1268 } 1269 1270 d = c1 - c2; 1271 if ( d ) { 1272 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1273 } 1274 } while( c1 ); 1275 1276 return 0; // strings are equal 1277 } 1278 1279 /* 1280 ================ 1281 idStr::Icmp 1282 ================ 1283 */ 1284 int idStr::Icmp( const char *s1, const char *s2 ) { 1285 int c1, c2, d; 1286 1287 do { 1288 c1 = *s1++; 1289 c2 = *s2++; 1290 1291 d = c1 - c2; 1292 while( d ) { 1293 if ( c1 <= 'Z' && c1 >= 'A' ) { 1294 d += ('a' - 'A'); 1295 if ( !d ) { 1296 break; 1297 } 1298 } 1299 if ( c2 <= 'Z' && c2 >= 'A' ) { 1300 d -= ('a' - 'A'); 1301 if ( !d ) { 1302 break; 1303 } 1304 } 1305 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1306 } 1307 } while( c1 ); 1308 1309 return 0; // strings are equal 1310 } 1311 1312 /* 1313 ================ 1314 idStr::Icmpn 1315 ================ 1316 */ 1317 int idStr::Icmpn( const char *s1, const char *s2, int n ) { 1318 int c1, c2, d; 1319 1320 assert( n >= 0 ); 1321 1322 do { 1323 c1 = *s1++; 1324 c2 = *s2++; 1325 1326 if ( !n-- ) { 1327 return 0; // strings are equal until end point 1328 } 1329 1330 d = c1 - c2; 1331 while( d ) { 1332 if ( c1 <= 'Z' && c1 >= 'A' ) { 1333 d += ('a' - 'A'); 1334 if ( !d ) { 1335 break; 1336 } 1337 } 1338 if ( c2 <= 'Z' && c2 >= 'A' ) { 1339 d -= ('a' - 'A'); 1340 if ( !d ) { 1341 break; 1342 } 1343 } 1344 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1345 } 1346 } while( c1 ); 1347 1348 return 0; // strings are equal 1349 } 1350 1351 /* 1352 ================ 1353 idStr::Icmp 1354 ================ 1355 */ 1356 int idStr::IcmpNoColor( const char *s1, const char *s2 ) { 1357 int c1, c2, d; 1358 1359 do { 1360 while ( idStr::IsColor( s1 ) ) { 1361 s1 += 2; 1362 } 1363 while ( idStr::IsColor( s2 ) ) { 1364 s2 += 2; 1365 } 1366 c1 = *s1++; 1367 c2 = *s2++; 1368 1369 d = c1 - c2; 1370 while( d ) { 1371 if ( c1 <= 'Z' && c1 >= 'A' ) { 1372 d += ('a' - 'A'); 1373 if ( !d ) { 1374 break; 1375 } 1376 } 1377 if ( c2 <= 'Z' && c2 >= 'A' ) { 1378 d -= ('a' - 'A'); 1379 if ( !d ) { 1380 break; 1381 } 1382 } 1383 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1384 } 1385 } while( c1 ); 1386 1387 return 0; // strings are equal 1388 } 1389 1390 /* 1391 ================ 1392 idStr::IcmpPath 1393 ================ 1394 */ 1395 int idStr::IcmpPath( const char *s1, const char *s2 ) { 1396 int c1, c2, d; 1397 1398 #if 0 1399 //#if !defined( ID_PC_WIN ) 1400 idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" ); 1401 #endif 1402 1403 do { 1404 c1 = *s1++; 1405 c2 = *s2++; 1406 1407 d = c1 - c2; 1408 while( d ) { 1409 if ( c1 <= 'Z' && c1 >= 'A' ) { 1410 d += ('a' - 'A'); 1411 if ( !d ) { 1412 break; 1413 } 1414 } 1415 if ( c1 == '\\' ) { 1416 d += ('/' - '\\'); 1417 if ( !d ) { 1418 break; 1419 } 1420 } 1421 if ( c2 <= 'Z' && c2 >= 'A' ) { 1422 d -= ('a' - 'A'); 1423 if ( !d ) { 1424 break; 1425 } 1426 } 1427 if ( c2 == '\\' ) { 1428 d -= ('/' - '\\'); 1429 if ( !d ) { 1430 break; 1431 } 1432 } 1433 // make sure folders come first 1434 while( c1 ) { 1435 if ( c1 == '/' || c1 == '\\' ) { 1436 break; 1437 } 1438 c1 = *s1++; 1439 } 1440 while( c2 ) { 1441 if ( c2 == '/' || c2 == '\\' ) { 1442 break; 1443 } 1444 c2 = *s2++; 1445 } 1446 if ( c1 && !c2 ) { 1447 return -1; 1448 } else if ( !c1 && c2 ) { 1449 return 1; 1450 } 1451 // same folder depth so use the regular compare 1452 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1453 } 1454 } while( c1 ); 1455 1456 return 0; 1457 } 1458 1459 /* 1460 ================ 1461 idStr::IcmpnPath 1462 ================ 1463 */ 1464 int idStr::IcmpnPath( const char *s1, const char *s2, int n ) { 1465 int c1, c2, d; 1466 1467 #if 0 1468 //#if !defined( ID_PC_WIN ) 1469 idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" ); 1470 #endif 1471 1472 assert( n >= 0 ); 1473 1474 do { 1475 c1 = *s1++; 1476 c2 = *s2++; 1477 1478 if ( !n-- ) { 1479 return 0; // strings are equal until end point 1480 } 1481 1482 d = c1 - c2; 1483 while( d ) { 1484 if ( c1 <= 'Z' && c1 >= 'A' ) { 1485 d += ('a' - 'A'); 1486 if ( !d ) { 1487 break; 1488 } 1489 } 1490 if ( c1 == '\\' ) { 1491 d += ('/' - '\\'); 1492 if ( !d ) { 1493 break; 1494 } 1495 } 1496 if ( c2 <= 'Z' && c2 >= 'A' ) { 1497 d -= ('a' - 'A'); 1498 if ( !d ) { 1499 break; 1500 } 1501 } 1502 if ( c2 == '\\' ) { 1503 d -= ('/' - '\\'); 1504 if ( !d ) { 1505 break; 1506 } 1507 } 1508 // make sure folders come first 1509 while( c1 ) { 1510 if ( c1 == '/' || c1 == '\\' ) { 1511 break; 1512 } 1513 c1 = *s1++; 1514 } 1515 while( c2 ) { 1516 if ( c2 == '/' || c2 == '\\' ) { 1517 break; 1518 } 1519 c2 = *s2++; 1520 } 1521 if ( c1 && !c2 ) { 1522 return -1; 1523 } else if ( !c1 && c2 ) { 1524 return 1; 1525 } 1526 // same folder depth so use the regular compare 1527 return ( INT32_SIGNBITNOTSET( d ) << 1 ) - 1; 1528 } 1529 } while( c1 ); 1530 1531 return 0; 1532 } 1533 1534 /* 1535 ============= 1536 idStr::Copynz 1537 1538 Safe strncpy that ensures a trailing zero 1539 ============= 1540 */ 1541 void idStr::Copynz( char *dest, const char *src, int destsize ) { 1542 if ( !src ) { 1543 idLib::common->Warning( "idStr::Copynz: NULL src" ); 1544 return; 1545 } 1546 if ( destsize < 1 ) { 1547 idLib::common->Warning( "idStr::Copynz: destsize < 1" ); 1548 return; 1549 } 1550 1551 strncpy( dest, src, destsize-1 ); 1552 dest[destsize-1] = 0; 1553 } 1554 1555 /* 1556 ================ 1557 idStr::Append 1558 1559 never goes past bounds or leaves without a terminating 0 1560 ================ 1561 */ 1562 void idStr::Append( char *dest, int size, const char *src ) { 1563 int l1; 1564 1565 l1 = strlen( dest ); 1566 if ( l1 >= size ) { 1567 idLib::common->Error( "idStr::Append: already overflowed" ); 1568 } 1569 idStr::Copynz( dest + l1, src, size - l1 ); 1570 } 1571 1572 /* 1573 ======================== 1574 idStr::IsValidUTF8 1575 ======================== 1576 */ 1577 bool idStr::IsValidUTF8( const uint8 * s, const int maxLen, utf8Encoding_t & encoding ) { 1578 struct local_t { 1579 static int GetNumEncodedUTF8Bytes( const uint8 c ) { 1580 if ( c < 0x80 ) { 1581 return 1; 1582 } else if ( ( c >> 5 ) == 0x06 ) { 1583 // 2 byte encoding - the next byte must begin with 1584 return 2; 1585 } else if ( ( c >> 4 ) == 0x0E ) { 1586 // 3 byte encoding 1587 return 3; 1588 } else if ( ( c >> 5 ) == 0x1E ) { 1589 // 4 byte encoding 1590 return 4; 1591 } 1592 // this isnt' a valid UTF-8 precursor character 1593 return 0; 1594 } 1595 static bool RemainingCharsAreUTF8FollowingBytes( const uint8 * s, const int curChar, const int maxLen, const int num ) { 1596 if ( maxLen - curChar < num ) { 1597 return false; 1598 } 1599 for ( int i = curChar + 1; i <= curChar + num; i++ ) { 1600 if ( s[ i ] == '\0' ) { 1601 return false; 1602 } 1603 if ( ( s[ i ] >> 6 ) != 0x02 ) { 1604 return false; 1605 } 1606 } 1607 return true; 1608 } 1609 }; 1610 1611 // check for byte-order-marker 1612 encoding = UTF8_PURE_ASCII; 1613 utf8Encoding_t utf8Type = UTF8_ENCODED_NO_BOM; 1614 if ( maxLen > 3 && s[ 0 ] == 0xEF && s[ 1 ] == 0xBB && s[ 2 ] == 0xBF ) { 1615 utf8Type = UTF8_ENCODED_BOM; 1616 } 1617 1618 for ( int i = 0; s[ i ] != '\0' && i < maxLen; i++ ) { 1619 int numBytes = local_t::GetNumEncodedUTF8Bytes( s[ i ] ); 1620 if ( numBytes == 1 ) { 1621 continue; // just low ASCII 1622 } else if ( numBytes == 2 ) { 1623 // 2 byte encoding - the next byte must begin with bit pattern 10 1624 if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 1 ) ) { 1625 return false; 1626 } 1627 // skip over UTF-8 character 1628 i += 1; 1629 encoding = utf8Type; 1630 } else if ( numBytes == 3 ) { 1631 // 3 byte encoding - the next 2 bytes must begin with bit pattern 10 1632 if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 2 ) ) { 1633 return false; 1634 } 1635 // skip over UTF-8 character 1636 i += 2; 1637 encoding = utf8Type; 1638 } else if ( numBytes == 4 ) { 1639 // 4 byte encoding - the next 3 bytes must begin with bit pattern 10 1640 if ( !local_t::RemainingCharsAreUTF8FollowingBytes( s, i, maxLen, 3 ) ) { 1641 return false; 1642 } 1643 // skip over UTF-8 character 1644 i += 3; 1645 encoding = utf8Type; 1646 } else { 1647 // this isnt' a valid UTF-8 character 1648 if ( utf8Type == UTF8_ENCODED_BOM ) { 1649 encoding = UTF8_INVALID_BOM; 1650 } else { 1651 encoding = UTF8_INVALID; 1652 } 1653 return false; 1654 } 1655 } 1656 return true; 1657 } 1658 1659 /* 1660 ======================== 1661 idStr::UTF8Length 1662 ======================== 1663 */ 1664 int idStr::UTF8Length( const byte * s ) { 1665 int mbLen = 0; 1666 int charLen = 0; 1667 while ( s[ mbLen ] != '\0' ) { 1668 uint32 cindex; 1669 cindex = s[ mbLen ]; 1670 if ( cindex < 0x80 ) { 1671 mbLen++; 1672 } else { 1673 int trailing = 0; 1674 if ( cindex >= 0xc0 ) { 1675 static const byte trailingBytes[ 64 ] = { 1676 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1677 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 1678 }; 1679 trailing = trailingBytes[ cindex - 0xc0 ]; 1680 } 1681 mbLen += trailing + 1; 1682 } 1683 charLen++; 1684 } 1685 return charLen; 1686 } 1687 1688 1689 /* 1690 ======================== 1691 idStr::AppendUTF8Char 1692 ======================== 1693 */ 1694 void idStr::AppendUTF8Char( uint32 c ) { 1695 if ( c < 0x80 ) { 1696 Append( ( char)c ); 1697 } else if ( c < 0x800 ) { // 11 bits 1698 Append( (char)( 0xC0 | ( c >> 6 ) ) ); 1699 Append( (char)( 0x80 | ( c & 0x3F ) ) ); 1700 } else if ( c < 0x10000 ) { // 16 bits 1701 Append( (char)( 0xE0 | ( c >> 12 ) ) ); 1702 Append( (char)( 0x80 | ( ( c >> 6 ) & 0x3F ) ) ); 1703 Append( (char)( 0x80 | ( c & 0x3F ) ) ); 1704 } else if ( c < 0x200000 ) { // 21 bits 1705 Append( (char)( 0xF0 | ( c >> 18 ) ) ); 1706 Append( (char)( 0x80 | ( ( c >> 12 ) & 0x3F ) ) ); 1707 Append( (char)( 0x80 | ( ( c >> 6 ) & 0x3F ) ) ); 1708 Append( (char)( 0x80 | ( c & 0x3F ) ) ); 1709 } else { 1710 // UTF-8 can encode up to 6 bytes. Why don't we support that? 1711 // This is an invalid Unicode character 1712 Append( '?' ); 1713 } 1714 } 1715 1716 /* 1717 ======================== 1718 idStr::UTF8Char 1719 ======================== 1720 */ 1721 uint32 idStr::UTF8Char( const byte * s, int & idx ) { 1722 if ( idx >= 0 ) { 1723 while ( s[ idx ] != '\0' ) { 1724 uint32 cindex = s[ idx ]; 1725 if ( cindex < 0x80 ) { 1726 idx++; 1727 return cindex; 1728 } 1729 int trailing = 0; 1730 if ( cindex >= 0xc0 ) { 1731 static const byte trailingBytes[ 64 ] = { 1732 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1733 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 1734 }; 1735 trailing = trailingBytes[ cindex - 0xc0 ]; 1736 } 1737 static const uint32 trailingMask[ 6 ] = { 0x0000007f, 0x0000001f, 0x0000000f, 0x00000007, 0x00000003, 0x00000001 }; 1738 cindex &= trailingMask[ trailing ]; 1739 while ( trailing-- > 0 ) { 1740 cindex <<= 6; 1741 cindex += s[ ++idx ] & 0x0000003f; 1742 } 1743 idx++; 1744 return cindex; 1745 } 1746 } 1747 idx++; 1748 return 0; // return a null terminator if out of range 1749 } 1750 1751 /* 1752 ================ 1753 idStr::LengthWithoutColors 1754 ================ 1755 */ 1756 int idStr::LengthWithoutColors( const char *s ) { 1757 int len; 1758 const char *p; 1759 1760 if ( !s ) { 1761 return 0; 1762 } 1763 1764 len = 0; 1765 p = s; 1766 while( *p ) { 1767 if ( idStr::IsColor( p ) ) { 1768 p += 2; 1769 continue; 1770 } 1771 p++; 1772 len++; 1773 } 1774 1775 return len; 1776 } 1777 1778 /* 1779 ================ 1780 idStr::RemoveColors 1781 ================ 1782 */ 1783 char *idStr::RemoveColors( char *string ) { 1784 char *d; 1785 char *s; 1786 int c; 1787 1788 s = string; 1789 d = string; 1790 while( (c = *s) != 0 ) { 1791 if ( idStr::IsColor( s ) ) { 1792 s++; 1793 } 1794 else { 1795 *d++ = c; 1796 } 1797 s++; 1798 } 1799 *d = '\0'; 1800 1801 return string; 1802 } 1803 1804 /* 1805 ================ 1806 idStr::snPrintf 1807 ================ 1808 */ 1809 int idStr::snPrintf( char *dest, int size, const char *fmt, ...) { 1810 int len; 1811 va_list argptr; 1812 char buffer[32000]; // big, but small enough to fit in PPC stack 1813 1814 va_start( argptr, fmt ); 1815 len = vsprintf( buffer, fmt, argptr ); 1816 va_end( argptr ); 1817 if ( len >= sizeof( buffer ) ) { 1818 idLib::common->Error( "idStr::snPrintf: overflowed buffer" ); 1819 } 1820 if ( len >= size ) { 1821 idLib::common->Warning( "idStr::snPrintf: overflow of %i in %i\n", len, size ); 1822 len = size; 1823 } 1824 idStr::Copynz( dest, buffer, size ); 1825 return len; 1826 } 1827 1828 /* 1829 ============ 1830 idStr::vsnPrintf 1831 1832 vsnprintf portability: 1833 1834 C99 standard: vsnprintf returns the number of characters (excluding the trailing 1835 '\0') which would have been written to the final string if enough space had been available 1836 snprintf and vsnprintf do not write more than size bytes (including the trailing '\0') 1837 1838 win32: _vsnprintf returns the number of characters written, not including the terminating null character, 1839 or a negative value if an output error occurs. If the number of characters to write exceeds count, then count 1840 characters are written and -1 is returned and no trailing '\0' is added. 1841 1842 idStr::vsnPrintf: always appends a trailing '\0', returns number of characters written (not including terminal \0) 1843 or returns -1 on failure or if the buffer would be overflowed. 1844 ============ 1845 */ 1846 int idStr::vsnPrintf( char *dest, int size, const char *fmt, va_list argptr ) { 1847 int ret; 1848 1849 #undef _vsnprintf 1850 ret = _vsnprintf( dest, size-1, fmt, argptr ); 1851 #define _vsnprintf use_idStr_vsnPrintf 1852 dest[size-1] = '\0'; 1853 if ( ret < 0 || ret >= size ) { 1854 return -1; 1855 } 1856 return ret; 1857 } 1858 1859 /* 1860 ============ 1861 sprintf 1862 1863 Sets the value of the string using a printf interface. 1864 ============ 1865 */ 1866 int sprintf( idStr &string, const char *fmt, ... ) { 1867 int l; 1868 va_list argptr; 1869 char buffer[32000]; 1870 1871 va_start( argptr, fmt ); 1872 l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr ); 1873 va_end( argptr ); 1874 buffer[sizeof(buffer)-1] = '\0'; 1875 1876 string = buffer; 1877 return l; 1878 } 1879 1880 /* 1881 ============ 1882 vsprintf 1883 1884 Sets the value of the string using a vprintf interface. 1885 ============ 1886 */ 1887 int vsprintf( idStr &string, const char *fmt, va_list argptr ) { 1888 int l; 1889 char buffer[32000]; 1890 1891 l = idStr::vsnPrintf( buffer, sizeof(buffer)-1, fmt, argptr ); 1892 buffer[sizeof(buffer)-1] = '\0'; 1893 1894 string = buffer; 1895 return l; 1896 } 1897 1898 /* 1899 ============ 1900 va 1901 1902 does a varargs printf into a temp buffer 1903 NOTE: not thread safe 1904 ============ 1905 */ 1906 char *va( const char *fmt, ... ) { 1907 va_list argptr; 1908 static int index = 0; 1909 static char string[4][16384]; // in case called by nested functions 1910 char *buf; 1911 1912 buf = string[index]; 1913 index = (index + 1) & 3; 1914 1915 va_start( argptr, fmt ); 1916 vsprintf( buf, fmt, argptr ); 1917 va_end( argptr ); 1918 1919 return buf; 1920 } 1921 1922 1923 1924 /* 1925 ============ 1926 idStr::BestUnit 1927 ============ 1928 */ 1929 int idStr::BestUnit( const char *format, float value, Measure_t measure ) { 1930 int unit = 1; 1931 while ( unit <= 3 && ( 1 << ( unit * 10 ) < value ) ) { 1932 unit++; 1933 } 1934 unit--; 1935 value /= 1 << ( unit * 10 ); 1936 sprintf( *this, format, value ); 1937 *this += " "; 1938 *this += units[ measure ][ unit ]; 1939 return unit; 1940 } 1941 1942 /* 1943 ============ 1944 idStr::SetUnit 1945 ============ 1946 */ 1947 void idStr::SetUnit( const char *format, float value, int unit, Measure_t measure ) { 1948 value /= 1 << ( unit * 10 ); 1949 sprintf( *this, format, value ); 1950 *this += " "; 1951 *this += units[ measure ][ unit ]; 1952 } 1953 1954 /* 1955 ================ 1956 idStr::InitMemory 1957 ================ 1958 */ 1959 void idStr::InitMemory() { 1960 #ifdef USE_STRING_DATA_ALLOCATOR 1961 stringDataAllocator.Init(); 1962 #endif 1963 } 1964 1965 /* 1966 ================ 1967 idStr::ShutdownMemory 1968 ================ 1969 */ 1970 void idStr::ShutdownMemory() { 1971 #ifdef USE_STRING_DATA_ALLOCATOR 1972 stringDataAllocator.Shutdown(); 1973 #endif 1974 } 1975 1976 /* 1977 ================ 1978 idStr::PurgeMemory 1979 ================ 1980 */ 1981 void idStr::PurgeMemory() { 1982 #ifdef USE_STRING_DATA_ALLOCATOR 1983 stringDataAllocator.FreeEmptyBaseBlocks(); 1984 #endif 1985 } 1986 1987 /* 1988 ================ 1989 idStr::ShowMemoryUsage_f 1990 ================ 1991 */ 1992 void idStr::ShowMemoryUsage_f( const idCmdArgs &args ) { 1993 #ifdef USE_STRING_DATA_ALLOCATOR 1994 idLib::common->Printf( "%6d KB string memory (%d KB free in %d blocks, %d empty base blocks)\n", 1995 stringDataAllocator.GetBaseBlockMemory() >> 10, stringDataAllocator.GetFreeBlockMemory() >> 10, 1996 stringDataAllocator.GetNumFreeBlocks(), stringDataAllocator.GetNumEmptyBaseBlocks() ); 1997 #endif 1998 } 1999 2000 /* 2001 ================ 2002 idStr::FormatNumber 2003 ================ 2004 */ 2005 struct formatList_t { 2006 int gran; 2007 int count; 2008 }; 2009 2010 // elements of list need to decend in size 2011 formatList_t formatList[] = { 2012 { 1000000000, 0 }, 2013 { 1000000, 0 }, 2014 { 1000, 0 } 2015 }; 2016 2017 int numFormatList = sizeof(formatList) / sizeof( formatList[0] ); 2018 2019 2020 idStr idStr::FormatNumber( int number ) { 2021 idStr string; 2022 bool hit; 2023 2024 // reset 2025 for ( int i = 0; i < numFormatList; i++ ) { 2026 formatList_t *li = formatList + i; 2027 li->count = 0; 2028 } 2029 2030 // main loop 2031 do { 2032 hit = false; 2033 2034 for ( int i = 0; i < numFormatList; i++ ) { 2035 formatList_t *li = formatList + i; 2036 2037 if ( number >= li->gran ) { 2038 li->count++; 2039 number -= li->gran; 2040 hit = true; 2041 break; 2042 } 2043 } 2044 } while ( hit ); 2045 2046 // print out 2047 bool found = false; 2048 2049 for ( int i = 0; i < numFormatList; i++ ) { 2050 formatList_t *li = formatList + i; 2051 2052 if ( li->count ) { 2053 if ( !found ) { 2054 string += va( "%i,", li->count ); 2055 } else { 2056 string += va( "%3.3i,", li->count ); 2057 } 2058 found = true; 2059 } 2060 else if ( found ) { 2061 string += va( "%3.3i,", li->count ); 2062 } 2063 } 2064 2065 if ( found ) { 2066 string += va( "%3.3i", number ); 2067 } 2068 else { 2069 string += va( "%i", number ); 2070 } 2071 2072 // pad to proper size 2073 int count = 11 - string.Length(); 2074 2075 for ( int i = 0; i < count; i++ ) { 2076 string.Insert( " ", 0 ); 2077 } 2078 2079 return string; 2080 } 2081 2082 CONSOLE_COMMAND( testStrId, "prints a localized string", 0 ) { 2083 if ( args.Argc() != 2 ) { 2084 idLib::Printf( "need a str id like 'STR_SWF_ACCEPT' without the hash, it gets parsed as a separate argument\n" ); 2085 return; 2086 } 2087 2088 idStrId str( va( "#%s", args.Argv( 1 ) ) ); 2089 idLib::Printf( "%s = %s\n", args.Argv( 1 ), str.GetLocalizedString() ); 2090 }