1 /* 2 ----------------------------------------------------------------------------- 3 This source file is part of OGRE 4 (Object-oriented Graphics Rendering Engine) 5 For the latest info, see http://www.ogre3d.org/ 6 7 Copyright (c) 2000-2013 Torus Knot Software Ltd 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 ----------------------------------------------------------------------------- 27 */ 28 #include "OgreStableHeaders.h" 29 #include "OgreString.h" 30 #include "OgreStringVector.h" 31 32 namespace Ogre { 33 34 //----------------------------------------------------------------------- 35 const String StringUtil::BLANK; 36 //----------------------------------------------------------------------- trim(String & str,bool left,bool right)37 void StringUtil::trim(String& str, bool left, bool right) 38 { 39 /* 40 size_t lspaces, rspaces, len = length(), i; 41 42 lspaces = rspaces = 0; 43 44 if( left ) 45 { 46 // Find spaces / tabs on the left 47 for( i = 0; 48 i < len && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r'); 49 ++lspaces, ++i ); 50 } 51 52 if( right && lspaces < len ) 53 { 54 // Find spaces / tabs on the right 55 for( i = len - 1; 56 i >= 0 && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r'); 57 rspaces++, i-- ); 58 } 59 60 *this = substr(lspaces, len-lspaces-rspaces); 61 */ 62 static const String delims = " \t\r"; 63 if(right) 64 str.erase(str.find_last_not_of(delims)+1); // trim right 65 if(left) 66 str.erase(0, str.find_first_not_of(delims)); // trim left 67 } 68 69 //----------------------------------------------------------------------- split(const String & str,const String & delims,unsigned int maxSplits,bool preserveDelims)70 StringVector StringUtil::split( const String& str, const String& delims, unsigned int maxSplits, bool preserveDelims) 71 { 72 StringVector ret; 73 // Pre-allocate some space for performance 74 ret.reserve(maxSplits ? maxSplits+1 : 10); // 10 is guessed capacity for most case 75 76 unsigned int numSplits = 0; 77 78 // Use STL methods 79 size_t start, pos; 80 start = 0; 81 do 82 { 83 pos = str.find_first_of(delims, start); 84 if (pos == start) 85 { 86 // Do nothing 87 start = pos + 1; 88 } 89 else if (pos == String::npos || (maxSplits && numSplits == maxSplits)) 90 { 91 // Copy the rest of the string 92 ret.push_back( str.substr(start) ); 93 break; 94 } 95 else 96 { 97 // Copy up to delimiter 98 ret.push_back( str.substr(start, pos - start) ); 99 100 if(preserveDelims) 101 { 102 // Sometimes there could be more than one delimiter in a row. 103 // Loop until we don't find any more delims 104 size_t delimStart = pos, delimPos; 105 delimPos = str.find_first_not_of(delims, delimStart); 106 if (delimPos == String::npos) 107 { 108 // Copy the rest of the string 109 ret.push_back( str.substr(delimStart) ); 110 } 111 else 112 { 113 ret.push_back( str.substr(delimStart, delimPos - delimStart) ); 114 } 115 } 116 117 start = pos + 1; 118 } 119 // parse up to next real data 120 start = str.find_first_not_of(delims, start); 121 ++numSplits; 122 123 } while (pos != String::npos); 124 125 126 127 return ret; 128 } 129 //----------------------------------------------------------------------- tokenise(const String & str,const String & singleDelims,const String & doubleDelims,unsigned int maxSplits)130 StringVector StringUtil::tokenise( const String& str, const String& singleDelims, const String& doubleDelims, unsigned int maxSplits) 131 { 132 StringVector ret; 133 // Pre-allocate some space for performance 134 ret.reserve(maxSplits ? maxSplits+1 : 10); // 10 is guessed capacity for most case 135 136 unsigned int numSplits = 0; 137 String delims = singleDelims + doubleDelims; 138 139 // Use STL methods 140 size_t start, pos; 141 char curDoubleDelim = 0; 142 start = 0; 143 do 144 { 145 if (curDoubleDelim != 0) 146 { 147 pos = str.find(curDoubleDelim, start); 148 } 149 else 150 { 151 pos = str.find_first_of(delims, start); 152 } 153 154 if (pos == start) 155 { 156 char curDelim = str.at(pos); 157 if (doubleDelims.find_first_of(curDelim) != String::npos) 158 { 159 curDoubleDelim = curDelim; 160 } 161 // Do nothing 162 start = pos + 1; 163 } 164 else if (pos == String::npos || (maxSplits && numSplits == maxSplits)) 165 { 166 if (curDoubleDelim != 0) 167 { 168 //Missing closer. Warn or throw exception? 169 } 170 // Copy the rest of the string 171 ret.push_back( str.substr(start) ); 172 break; 173 } 174 else 175 { 176 if (curDoubleDelim != 0) 177 { 178 curDoubleDelim = 0; 179 } 180 181 // Copy up to delimiter 182 ret.push_back( str.substr(start, pos - start) ); 183 start = pos + 1; 184 } 185 if (curDoubleDelim == 0) 186 { 187 // parse up to next real data 188 start = str.find_first_not_of(singleDelims, start); 189 } 190 191 ++numSplits; 192 193 } while (start != String::npos); 194 195 return ret; 196 } 197 //----------------------------------------------------------------------- toLowerCase(String & str)198 void StringUtil::toLowerCase(String& str) 199 { 200 std::transform( 201 str.begin(), 202 str.end(), 203 str.begin(), 204 tolower); 205 } 206 207 //----------------------------------------------------------------------- toUpperCase(String & str)208 void StringUtil::toUpperCase(String& str) 209 { 210 std::transform( 211 str.begin(), 212 str.end(), 213 str.begin(), 214 toupper); 215 } 216 //----------------------------------------------------------------------- startsWith(const String & str,const String & pattern,bool lowerCase)217 bool StringUtil::startsWith(const String& str, const String& pattern, bool lowerCase) 218 { 219 size_t thisLen = str.length(); 220 size_t patternLen = pattern.length(); 221 if (thisLen < patternLen || patternLen == 0) 222 return false; 223 224 String startOfThis = str.substr(0, patternLen); 225 if (lowerCase) 226 { 227 String lowerCasePattern = pattern; 228 StringUtil::toLowerCase(lowerCasePattern); 229 StringUtil::toLowerCase(startOfThis); 230 return (startOfThis == lowerCasePattern); 231 } 232 233 return (startOfThis == pattern); 234 } 235 //----------------------------------------------------------------------- endsWith(const String & str,const String & pattern,bool lowerCase)236 bool StringUtil::endsWith(const String& str, const String& pattern, bool lowerCase) 237 { 238 size_t thisLen = str.length(); 239 size_t patternLen = pattern.length(); 240 if (thisLen < patternLen || patternLen == 0) 241 return false; 242 243 String endOfThis = str.substr(thisLen - patternLen, patternLen); 244 if (lowerCase) 245 { 246 String lowerCasePattern = pattern; 247 StringUtil::toLowerCase(lowerCasePattern); 248 StringUtil::toLowerCase(endOfThis); 249 return (endOfThis == lowerCasePattern); 250 } 251 252 return (endOfThis == pattern); 253 } 254 //----------------------------------------------------------------------- standardisePath(const String & init)255 String StringUtil::standardisePath(const String& init) 256 { 257 String path = init; 258 259 std::replace( path.begin(), path.end(), '\\', '/' ); 260 if( path[path.length() - 1] != '/' ) 261 path += '/'; 262 263 return path; 264 } 265 //----------------------------------------------------------------------- normalizeFilePath(const String & init,bool makeLowerCase)266 String StringUtil::normalizeFilePath(const String& init, bool makeLowerCase) 267 { 268 const char* bufferSrc = init.c_str(); 269 int pathLen = (int)init.size(); 270 int indexSrc = 0; 271 int indexDst = 0; 272 int metaPathArea = 0; 273 274 char reservedBuf[1024]; 275 char* bufferDst = reservedBuf; 276 bool isDestAllocated = false; 277 if (pathLen > 1023) 278 { 279 //if source path is to long ensure we don't do a buffer overrun by allocating some 280 //new memory 281 isDestAllocated = true; 282 bufferDst = new char[pathLen + 1]; 283 } 284 285 //The outer loop loops over directories 286 while (indexSrc < pathLen) 287 { 288 if ((bufferSrc[indexSrc] == '\\') || (bufferSrc[indexSrc] == '/')) 289 { 290 //check if we have a directory delimiter if so skip it (we should already 291 //have written such a delimiter by this point 292 ++indexSrc; 293 continue; 294 } 295 else 296 { 297 //check if there is a directory to skip of type ".\" 298 if ((bufferSrc[indexSrc] == '.') && 299 ((bufferSrc[indexSrc + 1] == '\\') || (bufferSrc[indexSrc + 1] == '/'))) 300 { 301 indexSrc += 2; 302 continue; 303 } 304 305 //check if there is a directory to skip of type "..\" 306 else if ((bufferSrc[indexSrc] == '.') && (bufferSrc[indexSrc + 1] == '.') && 307 ((bufferSrc[indexSrc + 2] == '\\') || (bufferSrc[indexSrc + 2] == '/'))) 308 { 309 if (indexDst > metaPathArea) 310 { 311 //skip a directory backward in the destination path 312 do { 313 --indexDst; 314 } 315 while ((indexDst > metaPathArea) && (bufferDst[indexDst - 1] != '/')); 316 indexSrc += 3; 317 continue; 318 } 319 else 320 { 321 //we are about to write "..\" to the destination buffer 322 //ensure we will not remove this in future "skip directories" 323 metaPathArea += 3; 324 } 325 } 326 } 327 328 //transfer the current directory name from the source to the destination 329 while (indexSrc < pathLen) 330 { 331 char curChar = bufferSrc[indexSrc]; 332 if (makeLowerCase) curChar = tolower(curChar); 333 if ((curChar == '\\') || (curChar == '/')) curChar = '/'; 334 bufferDst[indexDst] = curChar; 335 ++indexDst; 336 ++indexSrc; 337 if (curChar == '/') break; 338 } 339 } 340 bufferDst[indexDst] = 0; 341 342 String normalized(bufferDst); 343 if (isDestAllocated) 344 { 345 delete[] bufferDst; 346 } 347 348 return normalized; 349 } 350 //----------------------------------------------------------------------- splitFilename(const String & qualifiedName,String & outBasename,String & outPath)351 void StringUtil::splitFilename(const String& qualifiedName, 352 String& outBasename, String& outPath) 353 { 354 String path = qualifiedName; 355 // Replace \ with / first 356 std::replace( path.begin(), path.end(), '\\', '/' ); 357 // split based on final / 358 size_t i = path.find_last_of('/'); 359 360 if (i == String::npos) 361 { 362 outPath.clear(); 363 outBasename = qualifiedName; 364 } 365 else 366 { 367 outBasename = path.substr(i+1, path.size() - i - 1); 368 outPath = path.substr(0, i+1); 369 } 370 371 } 372 //----------------------------------------------------------------------- splitBaseFilename(const Ogre::String & fullName,Ogre::String & outBasename,Ogre::String & outExtention)373 void StringUtil::splitBaseFilename(const Ogre::String& fullName, 374 Ogre::String& outBasename, Ogre::String& outExtention) 375 { 376 size_t i = fullName.find_last_of("."); 377 if (i == Ogre::String::npos) 378 { 379 outExtention.clear(); 380 outBasename = fullName; 381 } 382 else 383 { 384 outExtention = fullName.substr(i+1); 385 outBasename = fullName.substr(0, i); 386 } 387 } 388 // ---------------------------------------------------------------------------------------------------------------------------------------------- splitFullFilename(const Ogre::String & qualifiedName,Ogre::String & outBasename,Ogre::String & outExtention,Ogre::String & outPath)389 void StringUtil::splitFullFilename( const Ogre::String& qualifiedName, 390 Ogre::String& outBasename, Ogre::String& outExtention, Ogre::String& outPath ) 391 { 392 Ogre::String fullName; 393 splitFilename( qualifiedName, fullName, outPath ); 394 splitBaseFilename( fullName, outBasename, outExtention ); 395 } 396 //----------------------------------------------------------------------- match(const String & str,const String & pattern,bool caseSensitive)397 bool StringUtil::match(const String& str, const String& pattern, bool caseSensitive) 398 { 399 String tmpStr = str; 400 String tmpPattern = pattern; 401 if (!caseSensitive) 402 { 403 StringUtil::toLowerCase(tmpStr); 404 StringUtil::toLowerCase(tmpPattern); 405 } 406 407 String::const_iterator strIt = tmpStr.begin(); 408 String::const_iterator patIt = tmpPattern.begin(); 409 String::const_iterator lastWildCardIt = tmpPattern.end(); 410 while (strIt != tmpStr.end() && patIt != tmpPattern.end()) 411 { 412 if (*patIt == '*') 413 { 414 lastWildCardIt = patIt; 415 // Skip over looking for next character 416 ++patIt; 417 if (patIt == tmpPattern.end()) 418 { 419 // Skip right to the end since * matches the entire rest of the string 420 strIt = tmpStr.end(); 421 } 422 else 423 { 424 // scan until we find next pattern character 425 while(strIt != tmpStr.end() && *strIt != *patIt) 426 ++strIt; 427 } 428 } 429 else 430 { 431 if (*patIt != *strIt) 432 { 433 if (lastWildCardIt != tmpPattern.end()) 434 { 435 // The last wildcard can match this incorrect sequence 436 // rewind pattern to wildcard and keep searching 437 patIt = lastWildCardIt; 438 lastWildCardIt = tmpPattern.end(); 439 } 440 else 441 { 442 // no wildwards left 443 return false; 444 } 445 } 446 else 447 { 448 ++patIt; 449 ++strIt; 450 } 451 } 452 453 } 454 // If we reached the end of both the pattern and the string, we succeeded 455 if (patIt == tmpPattern.end() && strIt == tmpStr.end()) 456 { 457 return true; 458 } 459 else 460 { 461 return false; 462 } 463 464 } 465 //----------------------------------------------------------------------- replaceAll(const String & source,const String & replaceWhat,const String & replaceWithWhat)466 const String StringUtil::replaceAll(const String& source, const String& replaceWhat, const String& replaceWithWhat) 467 { 468 String result = source; 469 String::size_type pos = 0; 470 while(1) 471 { 472 pos = result.find(replaceWhat,pos); 473 if (pos == String::npos) break; 474 result.replace(pos,replaceWhat.size(),replaceWithWhat); 475 pos += replaceWithWhat.size(); 476 } 477 return result; 478 } 479 480 } 481