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