path.cpp

Engine/source/core/util/path.cpp

More...

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