path.cpp
Engine/source/core/util/path.cpp
Namespaces:
namespace
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "core/util/path.h" 25 26 27namespace Torque 28{ 29 30//----------------------------------------------------------------------------- 31 32void Path::_split(String name) 33{ 34 S32 pos = 0; 35 S32 idx = 0; 36 37 // Make sure we have platform separators 38 name = PathToPlatform(name); 39 40 // root: 41 idx = name.find(':'); 42 if (idx >= 0) 43 { 44 mRoot = name.substr(0,idx); 45 pos = idx + 1; 46 } 47 else if( name[ 0 ] == '/' ) 48 { 49 mRoot = "/"; 50 } 51 else 52 { 53 mRoot = ""; 54 } 55 56 // Extract path and strip trailing '/' 57 idx = name.find('/', 0, String::Right); 58 if (idx >= pos) 59 { 60 S32 len = idx - pos; 61 mPath = name.substr(pos,len? len: 1); 62 mPath = Path::CleanSeparators(mPath); 63 pos = idx + 1; 64 } 65 else 66 { 67 mPath = ""; 68 } 69 70 // file.ext 71 idx = name.find('.', 0, String::Right); 72 if (idx >= pos) 73 { 74 mFile = name.substr(pos,idx - pos); 75 mExt = name.substr(idx + 1,name.length() - idx - 1); 76 } 77 else 78 { 79 mFile = name.substr(pos,name.length() - pos); 80 mExt = ""; 81 } 82} 83 84String Path::_join() const 85{ 86 String name; 87 if( mRoot != '/' ) 88 name = Path::Join(mRoot, ':', mPath); 89 else 90 name = mPath; 91 name = Path::Join(name, '/', mFile); 92 name = Path::Join(name, '.', mExt); 93 return name; 94} 95 96String Path::CleanSeparators(String path) 97{ 98 return path.replace( '\\', '/' ); 99} 100 101String Path::CompressPath(String path) 102{ 103 // Remove "./" and "../" references. A ".." will also result in the 104 // removal of the proceeding directory. 105 // Also cleans separators as in CleanSeparators(). 106 // Assumes there are no trailing "/" 107 108 // Start by cleaning separators 109 path = Path::CleanSeparators(path); 110 111 U32 src = 0; 112 U32 dst = 0; 113 114 while (path[src]) 115 { 116 if (path[src] == '/' && path[src + 1] == '/') 117 { 118 src += 1; 119 continue; 120 } 121 else if (path[src] == '.') 122 { 123 if (path[src + 1] == 0) 124 { 125 if (dst && path[dst - 1] == '/') 126 dst--; 127 src++; 128 break; 129 } 130 else if (path[src + 1] == '/') 131 { 132 src += 2; 133 continue; 134 } 135 else if (path[src + 1] == '.') 136 { 137 if (path[src + 2] == 0) 138 { 139 if (dst && path[dst - 1] == '/') 140 dst = path.find('/', dst - 1, String::Right); 141 src += 2; 142 break; 143 } 144 if (dst && path[dst - 1] == '/') 145 dst = path.find('/', dst - 1, String::Right) + 1; 146 else 147 dst += 3; 148 149 src += 3; 150 continue; 151 } 152 } 153 154 if (dst != src) 155 { 156 String end = path.substr(src, path.length() - src); 157 if (dst > 0 && end[(String::SizeType)0] == '/' && path[dst-1] == '/') 158 end = end.substr(1, end.length() - 1); 159 path.replace(dst, path.length() - dst, end); 160 src = dst; 161 } 162 else 163 { 164 src++; 165 dst++; 166 } 167 } 168 169 if (src - dst) 170 path.erase(dst, src - dst); 171 172 return path; 173} 174 175Torque::Path Path::MakeRelativePath( const Path &makeRelative, const Path &relativeTo, U32 mode ) 176{ 177 // We need to find the part of makeRelative that starts to diverge from 178 // relativeTo. We only need to check up to the end of makeRelative or realtiveTo 179 // (whichever comes first). 180 U32 minDirCount = getMin(makeRelative.getDirectoryCount(), relativeTo.getDirectoryCount()); 181 182 // Store the index of the directory where we diverge. If we never diverge this 183 // will end up being the same as the number of directories in makeRelative 184 U32 divergeDirIdx = 0; 185 186 for (divergeDirIdx = 0; divergeDirIdx < minDirCount; divergeDirIdx++) 187 { 188 // If our directories don't match then this is the diverge directory 189 if (!makeRelative.getDirectory(divergeDirIdx).equal(relativeTo.getDirectory(divergeDirIdx), mode)) 190 break; 191 } 192 193 // Get the part of makeRelative's path after it diverged from relativeTo's path 194 String uniquePath; 195 196 // If we never diverged then divergeDirIdx will be equal to the number of 197 // directories in makeRelative and this loop will immediately exit 198 for (U32 i = divergeDirIdx; i < makeRelative.getDirectoryCount(); i++) 199 uniquePath += makeRelative.getDirectory(i) + "/"; 200 201 // Go ahead and add the full file name 202 uniquePath += makeRelative.getFullFileName(); 203 204 // Now calculate the relative offset 205 String offsetPath; 206 207 U32 numOffset = relativeTo.getDirectoryCount() - divergeDirIdx; 208 209 // Push back an "up" for each directory we are offset 210 for (U32 i = 0; i < numOffset; i++) 211 offsetPath += "../"; 212 213 return offsetPath + uniquePath; 214} 215 216String Path::Join(const String& a,String::ValueType s,const String& b) 217{ 218 switch (s) 219 { 220 case '/': 221 { 222 if (b.isEmpty() || (b.length() == 1 && (b.c_str()[0] == '/'))) 223 return a; 224 225 if (a.isEmpty()) 226 return b; 227 228 String::ValueType c = a[a.length()-1]; 229 230 if (c == ':' || ((c == '/') ^ (b.c_str()[0] == '/'))) 231 return a + b; 232 233 if (c == '/' && b.c_str()[0] == '/') 234 return a.substr(0,a.length() - 1) + b; 235 break; 236 } 237 238 case ':': 239 { 240 if (a.isEmpty()) 241 return b; 242 243 if (b.isEmpty()) 244 return a + ':'; 245 break; 246 } 247 248 case '.': 249 { 250 if (b.isEmpty()) 251 return a; 252 253 if (a.isEmpty()) 254 return '.' + b; 255 break; 256 } 257 258 default: 259 break; 260 } 261 262 return a + s + b; 263} 264 265bool Path::appendPath( const Path &p ) 266{ 267 mPath = CompressPath(Join(mPath,'/',p.getPath())); 268 mIsDirtyPath = true; 269 return true; 270} 271 272const String &Path::getFullFileName() const 273{ 274 if ( mIsDirtyFileName ) 275 { 276 mFullFileName = mFile; 277 278 if (mExt.isNotEmpty()) 279 mFullFileName += '.' + mExt; 280 mIsDirtyFileName = false; 281 } 282 283 return mFullFileName; 284 285} 286 287const String& Path::getFullPath() const 288{ 289 if ( mIsDirtyPath ) 290 { 291 mFullPath = _join(); 292 mIsDirtyPath = false; 293 } 294 295 return mFullPath; 296} 297 298String Path::getFullPathWithoutRoot() const 299{ 300 return Torque::Path::Join(getPath(), '/', getFullFileName()); 301} 302 303String Path::getRootAndPath() const 304{ 305 if( mRoot != '/' ) 306 return Path::Join(mRoot, ':', mPath); 307 else 308 return mPath; 309} 310 311const String& Path::setRoot(const String &s) 312{ 313 if ( mRoot != s ) 314 { 315 mIsDirtyPath = true; 316 mRoot = s; 317 } 318 319 return mRoot; 320} 321 322const String& Path::setPath(const String &s) 323{ 324 String clean = CleanSeparators(s); 325 326 if ( mPath != clean ) 327 { 328 mIsDirtyPath = true; 329 mPath = clean; 330 } 331 332 return mPath; 333} 334 335const String& Path::setFileName(const String &s) 336{ 337 if ( mFile != s ) 338 { 339 mIsDirtyPath = true; 340 mIsDirtyFileName = true; 341 mFile = s; 342 } 343 344 return mFile; 345} 346 347const String& Path::setExtension(const String &s) 348{ 349 if ( mExt != s ) 350 { 351 mIsDirtyPath = true; 352 mIsDirtyFileName = true; 353 mExt = s; 354 } 355 356 return mExt; 357} 358 359bool Path::isDirectory() const 360{ 361 return mFile.isEmpty() && mExt.isEmpty(); 362} 363 364bool Path::isRelative() const 365{ 366 return (mPath.isEmpty() || mPath.c_str()[0] != '/'); 367} 368 369bool Path::isAbsolute() const 370{ 371 return (!mPath.isEmpty() && mPath.c_str()[0] == '/'); 372} 373 374U32 Path::getDirectoryCount() const 375{ 376 if (mPath.isEmpty()) 377 return 0; 378 379 U32 count = 0; 380 U32 offset = 0; 381 382 if (mPath.c_str()[0] == '/') 383 offset++; 384 385 while (offset < mPath.length()) 386 { 387 if (mPath[offset++] == '/') 388 count++; 389 } 390 391 return count + 1; 392} 393 394String Path::getDirectory(U32 count) const 395{ 396 if (mPath.isEmpty()) 397 return String(); 398 399 U32 offset = 0; 400 401 if (mPath.c_str()[0] == '/') 402 offset++; 403 404 while (count && offset < mPath.length()) 405 { 406 if (mPath[offset++] == '/') 407 count--; 408 } 409 410 U32 end = offset; 411 412 while (end < mPath.length() && mPath[end] != '/') 413 end++; 414 415 return mPath.substr(offset,end - offset); 416} 417 418//----------------------------------------------------------------------------- 419 420String PathToPlatform(String file) 421{ 422 if (Path::OsSeparator != '/') 423 file.replace( Path::OsSeparator, '/' ); 424 425 return file; 426} 427 428String PathToOS(String file) 429{ 430 if (Path::OsSeparator != '/') 431 file.replace( '/', Path::OsSeparator ); 432 433 return file; 434} 435 436} // Namespace 437 438