TrueReality  v0.1.1912
FileUtils.cpp
Go to the documentation of this file.
1 /*
2 * True Reality Open Source Game and Simulation Engine
3 * Copyright © 2021 Acid Rain Studios LLC
4 *
5 * The Base of this class has been adopted from the Delta3D engine
6 *
7 * This library is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the Free
9 * Software Foundation; either version 3.0 of the License, or (at your option)
10 * any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * @author David Guthrie
22 * @author Maxim Serebrennik
23 */
24 #include <trUtil/FileUtils.h>
25 
26 #include <trUtil/Exception.h>
27 #include <trUtil/PlatformMacros.h>
28 #include <trUtil/StringUtils.h>
29 #include <trUtil/Logging/Log.h>
30 
31 #include <osg/Version>
32 #include <osg/Notify>
33 #include <osgDB/Archive>
34 #include <osgDB/FileNameUtils>
35 #include <osgDB/FileUtils>
36 #include <osgDB/ReadFile>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <stdexcept>
41 #include <algorithm>
42 #include <iostream>
43 #include <memory>
44 #include <cstdio>
45 #include <string>
46 #include <stack>
47 
48 
49 #ifdef TR_WIN
50 #include <direct.h>
51 #include <io.h>
52 #include <errno.h>
53 #include <stdio.h>
54 #define stat64 _stati64
55 #define mkdir(x,y) _mkdir((x))
56 
57 #if defined(_MT) || defined(_DLL)
58 _CRTIMP extern int * __cdecl _errno(void);
59 #define errno (*_errno())
60 #else // ndef _MT && ndef _DLL
61 _CRTIMP extern int errno;
62 #endif // _MT || _DLL
63 
64 //Linux and Mac OS X
65 #else
66 #include <sys/param.h>
67 #include <errno.h>
68 #include <unistd.h>
69 #include <string.h>
70 #endif
71 
72 
73 
74 //we only want to NOT use stat64, if it's not defined.
75 #ifndef stat64
76 #define stat64 stat
77 #endif
78 
79 
80 #ifndef S_ISREG
81 #define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
82 #endif
83 
84 #ifndef S_ISDIR
85 #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
86 #endif
87 
88 #ifdef MAX_PATH
89 #undef MAX_PATH
90 #endif
91 
92 #define MAX_PATH 1024
93 
94 #include <stack>
95 #include <queue>
96 
97 
98 namespace trUtil
99 {
100  osg::ref_ptr<FileUtils> FileUtils::mInstance;
101 
102 #ifdef TR_WIN
103  const char FileUtils::PATH_SEPARATOR = '\\';
104 #else
105  const char FileUtils::PATH_SEPARATOR = '/';
106 #endif
107 
108  // copy of function from osg to make osg 2.8 still work
109  static std::string GetPathRoot(const std::string& path)
110  {
111  // Test for unix root
112  if (path.empty()) return "";
113  if (path[0] == '/') return "/";
114  // Now test for Windows root
115  if (path.length()<2) return "";
116  if (path[1] == ':') return path.substr(0, 2); // We should check that path[0] is a letter, but as ':' is invalid in paths in other cases, that's not
117  return "";
118  }
119 
120  // temporary copy of osgDB::makeDirectory because of some bugs in it.
121  static bool iMakeDirectory(const std::string& path)
122  {
123  if (path.empty())
124  {
125  osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): cannot create an empty directory" << std::endl;
126  return false;
127  }
128 
129  struct stat64 stbuf;
130  if (stat64(path.c_str(), &stbuf) == 0)
131  {
132  if (S_ISDIR(stbuf.st_mode))
133  {
134  return true;
135  }
136  else
137  {
138  osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): " <<
139  path << " already exists and is not a directory!" << std::endl;
140  return false;
141  }
142  }
143 
144  std::string dir = path;
145  std::stack<std::string> paths;
146  while (true)
147  {
148  if (dir.empty())
149  {
150  break;
151  }
152 
153  if (stat64(dir.c_str(), &stbuf) < 0)
154  {
155  switch (errno)
156  {
157  case ENOENT:
158  case ENOTDIR:
159  paths.push(dir);
160  break;
161 
162  default:
163  osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): " << strerror(errno) << std::endl;
164  return false;
165  }
166  }
167  else
168  {
169  break;
170  }
171  dir = osgDB::getFilePath(std::string(dir));
172  }
173 
174  while (!paths.empty())
175  {
176  std::string dir2 = paths.top();
177 
178  if (mkdir(dir2.c_str(), 0755) < 0)
179  {
180  osg::notify(osg::DEBUG_INFO) << "osgDB::makeDirectory(): " << strerror(errno) << std::endl;
181  return false;
182  }
183  paths.pop();
184  }
185  return true;
186  }
187 
189  std::string FileUtils::RunCommand(const std::string cmd)
190  {
191  return RunCommand(cmd.c_str());
192  }
193 
195  std::string FileUtils::RunCommand(const char* cmd)
196  {
197  char buffer[128];
198  std::string result = "";
199 
200 #ifdef TR_WIN // If we are in windows
201  std::shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
202 #else
203  #ifdef TR_LINUX // If we are in Linux
204  std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
205  #endif
206 #endif
207  if (!pipe) throw std::runtime_error("popen() failed!");
208  while (!feof(pipe.get()))
209  {
210  if (fgets(buffer, 128, pipe.get()) != NULL)
211  result += buffer;
212  }
213  return result;
214  }
215 
217  bool FileUtils::FileExists(const std::string& strFile, bool caseInsensitive) const
218  {
219  return GetFileInfo(strFile, caseInsensitive).fileType != FILE_NOT_FOUND;
220  }
221 
223  void FileUtils::FileCopy(const std::string& strSrc, const std::string& strDest, bool bOverwrite) const
224  {
225  // Make absolutely certain these two strings don't point to the same file.
226  if (!IsSameFile(strSrc, strDest))
227  {
228  FILE* pSrcFile = nullptr;
229 
230  try
231  {
232  FILE* pDestFile;
233  struct stat tagStat;
234 
235  if (!FileExists(strSrc))
236  {
238  std::string("Source file does not exist: \"") + strSrc + "\"", __FILE__, __LINE__);
239  }
240 
241  // Open the source file for reading.
242  pSrcFile = fopen(strSrc.c_str(), "rb");
243  if (pSrcFile == nullptr)
244  {
246  std::string("Unable to open source file for reading: \"") + strSrc + "\"", __FILE__, __LINE__);
247  }
248 
249 
251  {
252  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Source file exists.");
253  }
254 
255  std::string destFile = strDest;
256 
257  FileType ft = GetFileInfo(strDest).fileType;
258 
259  // Check to see if the destination is a file or directory.
260  if (ft == DIRECTORY)
261  {
263  {
264  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination is a directory.");
265  }
266 
267  // If the file is a directory, append the base name of the source file to the destination
268  // to make the new file name.
269  if (strDest[strDest.size() - 1] != FileUtils::PATH_SEPARATOR)
270  {
271  destFile = strDest + FileUtils::PATH_SEPARATOR + osgDB::getSimpleFileName(strSrc);
272  }
273  else
274  {
275  destFile = strDest + osgDB::getSimpleFileName(strSrc);
276  }
277  }
278  else
279  {
281  {
282  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination is a file.");
283  }
284  }
285 
286  if (FileExists(destFile) && !bOverwrite)
287  {
289  std::string("Destination file exists, but overwriting is turned off: \"") + destFile + "\"", __FILE__, __LINE__);
290  }
291 
292  pDestFile = fopen(destFile.c_str(), "wb");
293 
294  if (pDestFile == nullptr)
295  {
296  // make sure to close the source file.
298  std::string("Unable to open destination for writing: \"") + destFile + "\"", __FILE__, __LINE__);
299  }
300 
301  try
302  {
304  {
305  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, "Destination opened for reading.");
306  }
307 
308  stat(strSrc.c_str(), &tagStat);
309  long i = 0;
310  char buffer[4096];
311  while (i < tagStat.st_size)
312  {
313  size_t readCount = fread(buffer, 1, 4096, pSrcFile);
314  if (readCount > 0)
315  {
316  size_t numWritten = fwrite(buffer, 1, readCount, pDestFile);
317  if (numWritten < readCount)
318  {
320  std::string("Unable to write to destinate file: \"") + destFile + "\"", __FILE__, __LINE__);
321  }
322  i += readCount;
323  }
324  }
325  fclose(pDestFile);
326  fclose(pSrcFile);
327  }
328  catch (trUtil::Exception&)
329  {
330  fclose(pDestFile);
331  throw;
332  }
333  }
334  catch (trUtil::Exception&)
335  {
336  fclose(pSrcFile);
337  throw;
338  }
339 
340  }
341 
342  // if the source equals the destination, this method is really a noop.
343  // (Not to mention the fact that if you attempt to copy a file onto itself
344  // in this manner then you will end up blowing it away).
345  }
346 
348  void FileUtils::FileMove(const std::string& strSrc, const std::string& strDest, bool bOverwrite) const
349  {
350  if (GetFileInfo(strSrc).fileType != REGULAR_FILE)
351  {
353  std::string("Source file was not found or is a Directory: \"") + strSrc + "\"", __FILE__, __LINE__);
354  }
355 
356  FileType ft = GetFileInfo(strDest).fileType;
357 
358  std::string destFile = strDest;
359 
360  // Check to see if the destination is a directory.
361  if (ft == DIRECTORY)
362  {
364  {
365  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination is a directory.");
366  }
367 
368  // Check to see if the destination is a file or directory.
369  if (strDest[strDest.size() - 1] != FileUtils::PATH_SEPARATOR)
370  {
371  destFile = strDest + FileUtils::PATH_SEPARATOR + osgDB::getSimpleFileName(strSrc);
372  }
373  else
374  {
375  destFile = strDest + osgDB::getSimpleFileName(strSrc);
376  }
377 
378  ft = GetFileInfo(destFile).fileType;
379  }
380  else
381  {
383  {
384  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination is a file.");
385  }
386  }
387 
388  if (ft != FILE_NOT_FOUND && !bOverwrite)
389  {
391  std::string("Destination file exists and the call was not set to overwrite: \"") + strDest + "\"", __FILE__, __LINE__);
392  }
393 
394  // first check to see if the file can be moved without copying it.
395  if (rename(strSrc.c_str(), destFile.c_str()) == 0)
396  {
397  return;
398  }
399 
401  {
402  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, "Rename failed, attempting to copy file and delete the source");
403  }
404 
405  // copy the file
406  FileCopy(strSrc, strDest, bOverwrite);
407 
408  // attempt to delete the original file.
409  if (unlink(strSrc.c_str()) != 0)
410  {
412  std::string("Unable to delete \"") + strSrc + "\" but file copied to new location.", __FILE__, __LINE__);
413  }
414  }
415 
417  void FileUtils::FileDelete(const std::string& strFile) const
418  {
419  FileType ft = GetFileInfo(strFile).fileType;
420 
421  // If the file does not exist, then ignore.
422  if (ft == FILE_NOT_FOUND)
423  {
424  return;
425  }
426 
427  if (ft != REGULAR_FILE)
428  {
430  std::string("File \"") + strFile + "\" is a directory.", __FILE__, __LINE__);
431  }
432 
433  if (unlink(strFile.c_str()) != 0)
434  {
436  std::string("Unable to delete \"") + strFile + "\".", __FILE__, __LINE__);
437  }
438  }
439 
441  const struct FileInfo FileUtils::GetFileInfo(const std::string& strFile, bool caseInsensitive) const
442  {
443  struct FileInfo info;
444 
445  if (!strFile.empty())
446  {
447  std::string filename = strFile;
448  CleanupFileString(filename);
449 
450  //we have a CWD in an archive and we have specified a relative path
452  {
453  if (!IsAbsolutePath(filename))
454  {
455  filename = ArchiveRelativeToAbsolute(filename);
456  }
457 
458  info = GetFileInfo_Internal(filename, caseInsensitive);
459  }
460  else
461  {
462  info = GetFileInfo_Internal(filename, caseInsensitive);
463  }
464  }
465  return info;
466  }
467 
468  //we use cleanup string on the path so this accept becomes very simple
469  class IsFileSeparator : public trUtil::UnaryFunction<char, bool>
470  {
471  public:
473  bool operator()(char c) const { return c == '/'; }
474 
475  };
476 
477  //-----------------------------------------------------------------------
478  std::string FileUtils::ArchiveRelativeToAbsolute(const std::string& relativeFile) const
479  {
480 
481 
482  std::string absDir = mCurrentDirectory;
483  CleanupFileString(absDir);
484 
485  std::vector<std::string> tokens;
487 
488 
489  std::vector<std::string>::iterator iter = tokens.begin();
490  std::vector<std::string>::iterator iterEnd = tokens.end();
491  for (; iter != iterEnd; ++iter)
492  {
493  std::string& curToken = *iter;
494 
495  if (curToken == ".")
496  {
497  //do nothing
498  }
499  else if (curToken == "..")
500  {
501  size_t lastSlash = absDir.find_last_of('/');
502  if (lastSlash != std::string::npos)
503  {
504  absDir = absDir.substr(0, lastSlash);
505  }
506 
507  }
508  else
509  {
510  absDir.append("/");
511  absDir.append(curToken);
512  }
513  }
514 
515  return absDir;
516  }
517 
518  //-----------------------------------------------------------------------
519  const struct FileInfo FileUtils::GetFileInfo_Internal(const std::string& strFile, bool caseInsensitive) const
520  {
521  struct FileInfo info;
522  struct stat tagStat;
523 
524  std::string archiveName;
525  std::string fileInArchive;
526 
527  bool isInArchive = SplitArchiveFilename(strFile, archiveName, fileInArchive);
528 
529  if (isInArchive)
530  {
531  const osgDB::ArchiveExtended* a = FindArchive(archiveName);
532  if (a == nullptr)
533  {
534  a = FindArchive(archiveName);
535  }
536 
537  if (a != nullptr)
538  {
539  info = GetFileInfoForFileInArchive(*a, fileInArchive);
540  }
541  }
542  else
543  {
544  // chop trailing slashes off
545  std::string choppedStr = strFile;
546  if (strFile.size() > 0 && (strFile[strFile.size() - 1] == '\\' ||
547  strFile[strFile.size() - 1] == '/'))
548  {
549  choppedStr = strFile.substr(0, strFile.length() - 1);
550  }
551 
552  if (caseInsensitive)
553  {
554  choppedStr = osgDB::findFileInDirectory(choppedStr, "", osgDB::CASE_INSENSITIVE);
555  }
556 
557  if (stat(choppedStr.c_str(), &tagStat) != 0)
558  {
559  // throw trUtil::FileNotFoundException( std::string("Cannot open file ") + choppedStr);
560  info.fileType = FILE_NOT_FOUND;
561  return info;
562  }
563 
564  info.extensionlessFileName = osgDB::getStrippedName(choppedStr);
565  info.path = osgDB::getFilePath(choppedStr);
566  info.baseName = osgDB::getSimpleFileName(choppedStr);
567  info.extension = osgDB::getFileExtension(choppedStr);
568  if (info.path.empty())
569  {
570  info.fileName = info.baseName;
571  }
572  else
573  {
574  info.fileName = info.path + PATH_SEPARATOR + info.baseName;
575  }
576 
577  info.size = tagStat.st_size;
578  info.lastModified = tagStat.st_mtime;
579 
580  if (S_ISDIR(tagStat.st_mode))
581  {
582  info.fileType = DIRECTORY;
583  }
584  else if (!archiveName.empty())
585  {
586  info.fileType = ARCHIVE;
587  }
588  else
589  {
590  // Anything else is a regular file, including special files
591  // this is incomplete, but not a case that we deemed necessary to handle.
592  // Symbolic links should NOT show up because stat was called, not lstat.
593  info.fileType = REGULAR_FILE;
594  }
595  }
596 
597  return info;
598  }
599 
601  void FileUtils::CleanupFileString(std::string& strFileOrDir) const
602  {
603  if (strFileOrDir.empty())
604  {
605  return;
606  }
607 
608  // remove duplicate path separators
609  for (unsigned int i = 0; i < strFileOrDir.length(); ++i)
610  {
611  if (strFileOrDir[i] == '/' || strFileOrDir[i] == '\\')
612  {
613  if (i > 0 && (strFileOrDir[i - 1] == '/' || strFileOrDir[i - 1] == '\\'))
614  {
615  strFileOrDir.erase(strFileOrDir.begin() + i);
616  --i;
617  }
618  strFileOrDir[i] = PATH_SEPARATOR;
619  }
620  }
621 
622  // get rid of trailing separators
623  if (strFileOrDir[strFileOrDir.length() - 1] == PATH_SEPARATOR)
624  {
625  strFileOrDir.erase(strFileOrDir.end() - 1);
626  }
627  }
628 
630  bool FileUtils::IsAbsolutePath(std::string strFileOrDir) const
631  {
632  // just in case, make sure we are using a valid path
633  CleanupFileString(strFileOrDir);
634 
635  // handle unix-style paths
636  if (strFileOrDir.length() > 0 && strFileOrDir[0] == '/')
637  {
638  return true;
639  }
640 
641  // handle windows-style paths
642  else if (strFileOrDir.length() > 1 && strFileOrDir[1] == ':')
643  {
644  return true;
645  }
646 
647  return false;
648  }
649 
651  void FileUtils::MakeDirectoryEX(std::string strDir)
652  {
653  if (strDir.empty())
654  {
655  return;
656  }
657 
658  // first make sure the directory string is clean
659  CleanupFileString(strDir);
660 
661  // this generated string is used to check each subdirectory in the path
662  std::string dir;
663 
664  // prepare tokenizer
665  auto buffer = new char[strDir.length() + 1];
666  strcpy(buffer, strDir.c_str());
667  char* tok = strtok(buffer, "/");
668 
669  // setup root directory
670  if (strDir[0] == '/')
671  {
672  dir = "/";
673  dir += tok;
674  }
675  else
676  {
677  dir = tok;
678  dir += "/";
679  }
680 
681  // traverse each subdirectory in path and create directories as needed
682  while (tok)
683  {
684  // debug
685  //std::cout << tok << std::endl;
686 
687  // create directory if necessary
688  if (!DirExists(dir))
689  {
690  MakeDirectory(dir);
691  }
692 
693  // get next subdirectory
694  tok = strtok(nullptr, "/");
695  if (tok)
696  {
697  if (dir[dir.length() - 1] != '/') { dir += '/'; }
698  dir += tok;
699  }
700  }
701 
702  // cleanup
703  delete[] buffer;
704  }
705 
707  void FileUtils::ChangeDirectory(const std::string& path)
708  {
709  if (!path.empty())
710  {
711  std::string filename = path;
712  CleanupFileString(filename);
713 
714  //we have a CWD in an archive and we have specified a relative path
716  {
717  if (!IsAbsolutePath(filename))
718  {
719  filename = ArchiveRelativeToAbsolute(filename);
720  }
721 
722  ChangeDirectoryInternal(filename);
723  }
724  else
725  {
726  ChangeDirectoryInternal(filename);
727  }
728 
729  mStackOfDirectories.clear();
730  }
731  }
732 
734  const std::string& FileUtils::CurrentDirectory() const
735  {
736  return mCurrentDirectory;
737  }
738 
740  void FileUtils::ChangeDirectoryInternal(const std::string& path)
741  {
742  std::string archiveName;
743  std::string fileInArchive;
744 
745  bool inArchive = SplitArchiveFilename(path, archiveName, fileInArchive);
746 
747  if (inArchive || !archiveName.empty())
748  {
749  osgDB::ArchiveExtended* a = FindArchive(archiveName);
750  if (a != nullptr)
751  {
752  if (inArchive)
753  {
754  FileInfo info = GetFileInfoForFileInArchive(*a, fileInArchive);
755  if (info.fileType == DIRECTORY)
756  {
757  mCurrentDirectory = archiveName + "/" + fileInArchive;
758  }
759  else
760  {
761  throw trUtil::FileNotFoundException(std::string("Cannot change directory into path: ") + path, __FILE__, __LINE__);
762  }
763  }
764  else
765  {
766  //it is the archive itself
767  mCurrentDirectory = osgDB::getRealPath(archiveName);
768  }
769  }
770  else
771  {
772  throw trUtil::FileNotFoundException(std::string("Cannot find valid archive for path: ") + path, __FILE__, __LINE__);
773  }
774  }
775  else
776  {
777  if (chdir(path.c_str()) == -1)
778  {
779  throw trUtil::FileNotFoundException(std::string("Cannot open directory ") + path, __FILE__, __LINE__);
780  }
781  char buf[512];
782  char* bufAddress = getcwd(buf, 512);
783  if (buf != bufAddress)
784  {
785  throw trUtil::FileUtilIOException(std::string("Cannot get current working directory"), __FILE__, __LINE__);
786  }
787 
788  mCurrentDirectory = buf;
789  }
790 
792  {
793  std::string message("Changed directory to \"");
794  message += mCurrentDirectory;
795  message += "\".";
796  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, message.c_str());
797  }
798  }
799 
801  void FileUtils::PushDirectory(const std::string& path)
802  {
804  {
805  std::string message("Pushing Directory \"");
806  message += path;
807  message += "\".";
808  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, message.c_str());
809  }
810  std::string old = mCurrentDirectory;
812  mStackOfDirectories.push_back(old);
813  }
814 
817  {
819  {
820  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Popping Directory.");
821  }
822 
823  if (mStackOfDirectories.empty())
824  {
825  return;
826  }
827 
829  mStackOfDirectories.pop_back();
830  }
831 
833  std::string FileUtils::GetAbsolutePath(const std::string& relativePath, bool removeFinalFile) const
834  {
835  //todo- add archive support here
836 
837  FileInfo fi = GetFileInfo(relativePath);
838  if (fi.fileType == FILE_NOT_FOUND)
839  {
840  throw trUtil::FileNotFoundException(std::string("Cannot open file or directory ") + relativePath, __FILE__, __LINE__);
841  }
842  else if (fi.fileType == ARCHIVE || fi.isInArchive)
843  {
844  throw trUtil::FileUtilIOException(std::string("Unable to get the absolute path to a file in an archive. ") + relativePath, __FILE__, __LINE__);
845  }
846 
847  std::string dir = relativePath;
848  if (fi.fileType == REGULAR_FILE)
849  {
850  dir = fi.path;
851  }
852 
853  if (dir.empty())
854  {
855  dir = ".";
856  }
857 
858  std::string result;
859  DirectoryPush dp(dir);
860  if (dp.GetSucceeded())
861  {
862  result = mCurrentDirectory;
863  if (!removeFinalFile && fi.fileType == REGULAR_FILE)
864  {
865  result += PATH_SEPARATOR + fi.baseName;
866  }
867  }
868  else
869  {
870  throw trUtil::FileUtilIOException(std::string("Cannot get absolute path for file. Cannot enter directory: ") + relativePath, __FILE__, __LINE__);
871  }
872  return result;
873  }
874 
876  std::string getFileExtensionIncludingDot(const std::string& fileName)
877  {
878 #if defined(OSG_VERSION_MAJOR) && defined(OSG_VERSION_MINOR) && OSG_VERSION_MAJOR <= 2 && OSG_VERSION_MINOR <= 6
879  //copied from osgDB/FileNameUtils.cpp (v2.8.0)
880  std::string::size_type dot = fileName.find_last_of('.');
881  if (dot == std::string::npos) return std::string("");
882  return std::string(fileName.begin() + dot, fileName.end());
883 #else
884  return osgDB::getFileExtensionIncludingDot(fileName);
885 #endif
886  }
887 
890  {
891  public:
893 
894  char operator() (char& elem) const
895  {
896  return tolower(elem);
897  }
898  };
899 
901  DirectoryContents FileUtils::DirGetFiles(const std::string& path,
902  const FileExtensionList& extensions) const
903  {
904  DirectoryContents dirContents;
905 
906  FileInfo ft = GetFileInfo(path);
907  if (ft.fileType == FILE_NOT_FOUND)
908  {
910  std::string("Path not Found: \"") + path + "\".", __FILE__, __LINE__);
911  }
912  else if (ft.fileType == REGULAR_FILE)
913  {
915  std::string("Path does not specify a directory: \"") + path + "\".", __FILE__, __LINE__);
916  }
917  else if (ft.fileType == ARCHIVE || ft.isInArchive)
918  {
919  std::string archiveName;
920  std::string fileInArchiveName;
921  std::string absoluteFilename = path;
922  if (!IsAbsolutePath(path))
923  {
924  absoluteFilename = ArchiveRelativeToAbsolute(path);
925  }
926 
927  SplitArchiveFilename(absoluteFilename, archiveName, fileInArchiveName);
928 
929  const osgDB::ArchiveExtended* a = FindArchive(archiveName);
930  if (a != nullptr)
931  {
932  DirGetFilesInArchive(*a, absoluteFilename, dirContents);
933  }
934  else
935  {
937  std::string("Archive must be opened to search into: \"") + path + "\".", __FILE__, __LINE__);
938  }
939  }
940  else //should be a directory
941  {
942  dirContents = osgDB::getDirectoryContents(path);
943  }
944 
945  if (extensions.empty())
946  {
947  return dirContents;
948  }
949 
950  DirectoryContents filteredContents;
951  // iterate over contents, looking for files that have the requested extensions
952  DirectoryContents::iterator dirItr = dirContents.begin();
953  while (dirItr != dirContents.end())
954  {
955  FileExtensionList::const_iterator extItr = extensions.begin();
956  while (extItr != extensions.end())
957  {
958  std::string testExt = getFileExtensionIncludingDot((*dirItr));
959  std::string validExt = (*extItr);
960 
961  if (trUtil::StringUtils::StrCompare(testExt, validExt, false) == 0)
962  {
963  filteredContents.push_back((*dirItr));
964  // stop when we find at least one match, to avoid duplicate file entries, in
965  // case extensions contains duplicate entries.
966  break;
967  }
968  ++extItr;
969  }
970  ++dirItr;
971  }
972 
973  return filteredContents;
974  }
975 
977  DirectoryContents FileUtils::DirGetSubs(const std::string& path) const
978  {
979  DirectoryContents vec;
980 
981  DirectoryContents dirCont = DirGetFiles(path);
982 
983  for (DirectoryContents::const_iterator i = dirCont.begin(); i != dirCont.end(); ++i)
984  {
985  if (GetFileInfo(path + PATH_SEPARATOR + *i).fileType == DIRECTORY && (*i != ".") && (*i != ".."))
986  {
987  vec.push_back(*i);
988  }
989  }
990 
991  return vec;
992  }
993 
995  void FileUtils::InternalDirCopy(const std::string& srcPath,
996  const std::string& destPath, bool bOverwrite) const
997  {
998  FileType destFileType = GetFileInfo(destPath).fileType;
999  //std::cout << "Copying " << srcPath << " to " << destPath << std::endl;
1000 
1001  if (destFileType == REGULAR_FILE)
1002  {
1004  std::string("The destination path must be a directory: \"") + destPath + "\"", __FILE__, __LINE__);
1005  }
1006 
1007  if (destFileType == FILE_NOT_FOUND)
1008  {
1009  MakeDirectory(destPath);
1010  }
1011 
1012  DirectoryContents contents = DirGetFiles(srcPath);
1013  for (DirectoryContents::iterator i = contents.begin(); i != contents.end(); ++i)
1014  {
1015  if (*i == "." || *i == "..")
1016  {
1017  continue;
1018  }
1019  const std::string newSrcPath = srcPath + PATH_SEPARATOR + *i;
1020  const std::string newDestPath = destPath + PATH_SEPARATOR + *i;
1021  FileInfo fi = GetFileInfo(newSrcPath);
1022  if (fi.fileType == DIRECTORY)
1023  {
1024  if (destPath.size() >= newSrcPath.size() &&
1025  destPath.substr(0, newSrcPath.size()) == newSrcPath &&
1026  (destPath.size() == newSrcPath.size() || destPath[newSrcPath.size()] == PATH_SEPARATOR))
1027  {
1029  {
1030  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_WARNING, __FUNCTION__, __LINE__, "Can't copy %s into itself.", newSrcPath.c_str());
1031  }
1032  }
1033  else
1034  {
1035  InternalDirCopy(newSrcPath, newDestPath, bOverwrite);
1036  }
1037  }
1038  else
1039  {
1040  FileCopy(newSrcPath, newDestPath, bOverwrite);
1041  }
1042  }
1043  }
1044 
1046  void FileUtils::DirCopy(const std::string& srcPath,
1047  const std::string& destPath, bool bOverwrite, bool copyContentsOnly) const
1048  {
1049  if (!DirExists(srcPath))
1050  {
1052  std::string("Source directory does not exist: \"") + srcPath + "\"", __FILE__, __LINE__);
1053  }
1054 
1055  FileType destFileType = GetFileInfo(destPath).fileType;
1056 
1057  if (destFileType == REGULAR_FILE)
1058  {
1060  std::string("The destination path must be a directory: \"") + destPath + "\"", __FILE__, __LINE__);
1061  }
1062 
1063  bool createDest = destFileType == FILE_NOT_FOUND;
1064 
1065  std::string fullSrcPath = GetAbsolutePath(srcPath);
1066  // from here, the code can assume srcPath exists.
1068  {
1069  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Source directory \"%s\" exists.", fullSrcPath.c_str());
1070  }
1071 
1072  std::string fullDestPath;
1073 
1074  if (createDest)
1075  {
1076  MakeDirectory(destPath);
1077  fullDestPath = GetAbsolutePath(destPath);
1079  {
1080  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination directory \"%s\" has been created.",
1081  fullDestPath.c_str());
1082  }
1083  }
1084  else
1085  {
1086  fullDestPath = GetAbsolutePath(destPath);
1088  {
1089  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__, "Destination directory \"%s\" exists.",
1090  fullDestPath.c_str());
1091  }
1092 
1093  if ((copyContentsOnly && fullSrcPath == fullDestPath)
1094  || (!copyContentsOnly && osgDB::getFilePath(fullSrcPath) == fullDestPath))
1095  {
1097  std::string("The source equals the destination: \"") + srcPath + "\"", __FILE__, __LINE__);
1098  }
1099 
1100  if (!copyContentsOnly)
1101  {
1102  const std::string& srcName = osgDB::getSimpleFileName(fullSrcPath);
1103  fullDestPath += PATH_SEPARATOR + srcName;
1104 
1105  if (DirExists(fullDestPath) && !bOverwrite)
1106  {
1108  std::string("Cannot overwrite directory (overwrite flag is false): \"") + srcPath + "\"",
1109  __FILE__, __LINE__);
1110  }
1111 
1112  if (!DirExists(fullDestPath))
1113  {
1114  MakeDirectory(fullDestPath);
1115  }
1116 
1118  {
1119  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1120  "Destination directory \"%s\" created - copyContentsOnly is false.",
1121  fullDestPath.c_str());
1122  }
1123  }
1124  else
1125  {
1127  {
1128  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1129  "Destination directory \"%s\" exists - copyContentsOnly is true.",
1130  fullDestPath.c_str());
1131  }
1132  }
1133  }
1134 
1135  InternalDirCopy(fullSrcPath, fullDestPath, bOverwrite);
1136  }
1137 
1139  bool FileUtils::DirDelete(const std::string& strDir, bool bRecursive)
1140  {
1141  if (bRecursive)
1142  {
1144  {
1145  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1146  "Attempting to recursively delete %s.", strDir.c_str());
1147  }
1148  try
1149  {
1150  PushDirectory(strDir);
1151  try
1152  {
1153  RecursDeleteDir(true);
1154  }
1155  catch (const trUtil::Exception&)
1156  {
1157  PopDirectory();
1158  throw;
1159  }
1160 
1161  PopDirectory();
1162  }
1163  catch (const trUtil::FileNotFoundException& ex)
1164  {
1165  // if we get a file not found trying to recurse into the top directory
1166  // then the directory does not exist, so there is no need to throw an exception.
1167  if (!DirExists(strDir))
1168  {
1170  {
1171  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1172  "Directory %s doesn't exist to delete. Ignoring.", strDir.c_str());
1174  }
1175  return true;
1176  }
1177  }
1178  catch (const trUtil::Exception&)
1179  {
1180  throw;
1181  }
1182  }
1183  else
1184  {
1186  {
1187  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1188  "Attempting to delete %s, but not recursively.", strDir.c_str());
1189  }
1190  }
1191 
1192  errno = 0;
1193  if (rmdir(strDir.c_str()) != 0)
1194  {
1195  if (!bRecursive && errno == ENOTEMPTY)
1196  {
1197  return false;
1198  }
1199  else if (errno == ENOENT)
1200  {
1202  {
1203  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1204  "Directory %s doesn't exist to delete. Ignoring.", strDir.c_str());
1205  }
1206  return true;
1207  }
1208  else
1209  {
1211  std::string("Unable to delete directory \"") + strDir + "\":" + strerror(errno), __FILE__, __LINE__);
1212  }
1213  }
1214 
1215  return true;
1216  }
1217 
1219  void FileUtils::MakeDirectory(const std::string& strDir) const
1220  {
1221  if (!iMakeDirectory(strDir))
1222  {
1223  FileType ft = GetFileInfo(strDir).fileType;
1224  if (ft == REGULAR_FILE)
1225  {
1226  throw trUtil::FileUtilIOException(std::string("Cannot create directory. ")
1227  + strDir + " is an existing non-directory file.", __FILE__, __LINE__);
1228  }
1229  else if (ft == DIRECTORY)
1230  {
1231  return;
1232  }
1233 
1234  if (!DirExists(osgDB::getFilePath(strDir)))
1235  {
1236  throw trUtil::FileNotFoundException(std::string("Cannot create directory ")
1237  + strDir + ". Parent directory doesn't exist.", __FILE__, __LINE__);
1238  }
1239  else
1240  {
1241  throw trUtil::FileUtilIOException(std::string("Cannot create directory ") + strDir + ".", __FILE__, __LINE__);
1242  }
1243  }
1244  }
1245 
1247  bool FileUtils::DirExists(const std::string& strDir, bool caseInsensitive) const
1248  {
1249  return GetFileInfo(strDir, caseInsensitive).fileType == DIRECTORY;
1250  }
1251 
1253  std::string FileUtils::RelativePath(const std::string& absolutePath, const std::string& file) const
1254  {
1255  if (!IsAbsolutePath(absolutePath) || !IsAbsolutePath(file))
1256  {
1257  return file;
1258  }
1259 
1260  std::string relativePath;
1261 
1262  if (file.empty() || absolutePath.empty())
1263  {
1264  return file;
1265  }
1266 
1267  const std::string PS(PATH_SEPARATOR, 1);
1268 
1269  std::string root1, root2;
1270  root1 = GetPathRoot(file);
1271  root2 = GetPathRoot(absolutePath);
1272 
1273  if (root1 != root2)
1274  {
1275  return file;
1276  }
1277 
1278  std::vector<std::string> absPathVec, filePathVec;
1279  IsPathSeparator pathSepFunc;
1280 
1281  trUtil::StringUtils::StringTokenizer<IsPathSeparator>::tokenize(absPathVec, absolutePath, pathSepFunc);
1283 
1284  size_t pointOfDivergence = 0;
1285 
1286  for (size_t i = 0; i < filePathVec.size() && i < absPathVec.size(); ++i)
1287  {
1288  if (filePathVec[i] != absPathVec[i])
1289  {
1290  break;
1291  }
1292  ++pointOfDivergence;
1293  }
1294 
1295  // Add ../ for each of the rest of the items in the absolute path.
1296  for (size_t i = pointOfDivergence; i < absPathVec.size(); ++i)
1297  {
1298  relativePath.append("../");
1299  }
1300 
1301  // Add the non matching items from the file path.
1302  for (size_t i = pointOfDivergence; i < filePathVec.size(); ++i)
1303  {
1304  relativePath.append(filePathVec[i]);
1305  // only put the slash in if it's not the last one.
1306  if (i < filePathVec.size() - 1)
1307  {
1308  relativePath.append("/");
1309  }
1310  }
1311 
1312 
1313 
1314  return relativePath;
1315  }
1316 
1318  bool FileUtils::IsSameFile(const std::string& file1, const std::string& file2) const
1319  {
1320  bool result = false;
1321 
1322  FileInfo fileInfo1 = GetFileInfo(file1);
1323  FileInfo fileInfo2 = GetFileInfo(file2);
1324 
1325  if (!fileInfo1.isInArchive && !fileInfo2.isInArchive)
1326  {
1327  std::string tmp1 = file1;
1328  std::string tmp2 = file2;
1329 
1330  try
1331  {
1332  if (!fileInfo1.path.empty())
1333  {
1334  tmp1 = GetAbsolutePath(file1);
1335  }
1336  if (!fileInfo2.path.empty())
1337  {
1338  tmp2 = GetAbsolutePath(file2);
1339  }
1340  }
1341  catch (...)
1342  {
1343  // This query method is not a good place to allow an exception through to the caller.
1344  }
1345 
1346  //the former, and non archive route..
1347  result = IsSameFile_Internal(tmp1, tmp2);
1348  }
1349  else
1350  {
1351  std::string file1ArchiveName = file1;
1352  std::string file2ArchiveName = file2;
1353  std::string file1InArchive, file2InArchive;
1354 
1355 
1356  if (!IsAbsolutePath(file1))
1357  {
1358  file1ArchiveName = ArchiveRelativeToAbsolute(file1);
1359  }
1360  if (!IsAbsolutePath(file2))
1361  {
1362  file2ArchiveName = ArchiveRelativeToAbsolute(file2);
1363  }
1364 
1365  SplitArchiveFilename(file1ArchiveName, file1ArchiveName, file1InArchive);
1366  SplitArchiveFilename(file2ArchiveName, file2ArchiveName, file2InArchive);
1367 
1368  //compare the actual archives first
1369  if (IsSameFile_Internal(file1ArchiveName, file2ArchiveName))
1370  {
1371  result = (file1InArchive == file2InArchive);
1372  }
1373 
1374  }
1375 
1376  return result;
1377  }
1378 
1379  //-----------------------------------------------------------------------
1380  bool FileUtils::IsSameFile_Internal(const std::string& file1, const std::string& file2) const
1381  {
1382  // If path names are different, we still could be pointing at the same file
1383  // on disk.
1384  struct stat stat1;
1385  struct stat stat2;
1386  memset(&stat1, 0, sizeof(struct stat));
1387  memset(&stat2, 0, sizeof(struct stat));
1388 
1389  if (stat(file1.c_str(), &stat1) != 0)
1390  {
1391  return false;
1392  }
1393 
1394  if (stat(file2.c_str(), &stat2) != 0)
1395  {
1396  return false;
1397  }
1398 
1399  // only Unix variants support inodes
1400 #ifndef WIN32
1401  if (stat1.st_ino == stat2.st_ino)
1402  {
1403  return true;
1404  }
1405 #else // WIN32 -- No inodes in Windows -- we'll have to imitate an inode's functionality.
1406  if (stat1.st_atime == stat2.st_atime &&
1407  stat1.st_ctime == stat2.st_ctime &&
1408  stat1.st_mtime == stat2.st_mtime &&
1409  stat1.st_gid == stat2.st_gid &&
1410  stat1.st_uid == stat2.st_uid &&
1411  stat1.st_mode == stat2.st_mode &&
1412  stat1.st_size == stat2.st_size)
1413  {
1414  // also make sure the file names (NOT paths) are the same
1415  std::string file1Name = file1.substr(file1.find_last_of("\\/"));
1416  // Windows is case insensitive
1417  std::transform(file1Name.begin(), file1Name.end(), file1Name.begin(), tolower);
1418  std::string file2Name = file2.substr(file2.find_last_of("\\/"));
1419  std::transform(file2Name.begin(), file2Name.end(), file2Name.begin(), tolower);
1420 
1421  if (file1Name == file2Name)
1422  {
1423  return true;
1424  }
1425  }
1426 #endif
1427 
1428  return false;
1429  }
1430 
1432  void FileUtils::RecursDeleteDir(bool bRecursive)
1433  {
1434  // this method assumes one is IN the directory that you want to delete.
1436 
1437  // iterate over all of the directory contents.
1438  for (DirectoryContents::const_iterator i = dirCont.begin(); i != dirCont.end(); ++i)
1439  {
1440  FileType ft = GetFileInfo(*i).fileType;
1441  if (ft == REGULAR_FILE)
1442  {
1443  // Delete regular files.
1444  errno = 0;
1445  if (unlink(i->c_str()) < 0)
1446  {
1448  std::string("Unable to delete directory \"") + *i + "\":" + strerror(errno), __FILE__, __LINE__);
1449  }
1450  }
1451  else if ((*i != ".") && (*i != "..") && ft == DIRECTORY && bRecursive)
1452  {
1453  // if it's a directory and it's not the "." or ".." special directories,
1454  // change into that directory and recurse.
1456  RecursDeleteDir(true);
1457  // now that the directory is empty, remove it.
1458  errno = 0;
1459  if (rmdir(i->c_str()) != 0)
1460  {
1461  if (errno == ENOENT)
1462  {
1464  {
1465  mLogger->LogMessage(trUtil::Logging::LogLevel::LOG_DEBUG, __FUNCTION__, __LINE__,
1466  "Directory %s doesn't exist to delete. Ignoring.", (mCurrentDirectory + PATH_SEPARATOR + *i).c_str());
1467  }
1468  return;
1469  }
1470  else
1471  {
1473  std::string("Unable to delete directory \"") + mCurrentDirectory + PATH_SEPARATOR + *i + "\":" + strerror(errno), __FILE__, __LINE__);
1474 
1475  }
1476  }
1477  }
1478  }
1479 
1480  // change up so that when the method ends, the directory is ready to be removed.
1481  ChangeDirectoryInternal(std::string(".."));
1482  }
1483 
1485  std::string FileUtils::ConcatPaths(const std::string& left, const std::string& right)
1486  {
1487 #if defined(OPENSCENEGRAPH_MAJOR_VERSION) && OPENSCENEGRAPH_MAJOR_VERSION <= 2 && defined(OPENSCENEGRAPH_MINOR_VERSION) && OPENSCENEGRAPH_MINOR_VERSION <= 8 && defined(OPENSCENEGRAPH_PATCH_VERSION) && OPENSCENEGRAPH_PATCH_VERSION < 3
1488  if (left.empty())
1489  {
1490  return right;
1491  }
1492 #endif
1493 
1494  return osgDB::concatPaths(left, right);
1495  }
1496 
1499  {
1500  mLogger = &trUtil::Logging::Log::GetInstance(std::string("fileutils.cpp"));
1501  // assign the current directory
1502  ChangeDirectory(".");
1503 
1504  //adding zip extension for use as archive
1505  osgDB::Registry::instance()->addArchiveExtension("zip");
1506  }
1507 
1510 
1511 
1513  bool FileUtils::SplitArchiveFilename(const std::string& fullFilename, std::string& archiveFilename, std::string& fileInArchive) const
1514  {
1515 
1516  std::string filename = fullFilename;
1517  CleanupFileString(filename);
1518 
1519 
1520  //osgDB::Registry::getArchiveExtensions() was submitted and should be released in OSG 3.0
1521 #if defined(OPENSCENEGRAPH_MAJOR_VERSION) && OPENSCENEGRAPH_MAJOR_VERSION >= 3
1522 
1523  osgDB::Registry* reg = osgDB::Registry::instance();
1524  const osgDB::Registry::ArchiveExtensionList& extensions = reg->getArchiveExtensions();
1525 
1526  osgDB::Registry::ArchiveExtensionList::const_iterator iter = extensions.begin();
1527  osgDB::Registry::ArchiveExtensionList::const_iterator iterEnd = extensions.end();
1528  for (; iter != iterEnd; ++iter)
1529  {
1530  const std::string& archiveExtension = *iter;
1531 #else
1532  std::string archiveExtension = ".zip";
1533  {
1534 #endif
1535 
1536  std::string::size_type positionArchive = filename.find(archiveExtension + '/');
1537  if (positionArchive == std::string::npos) positionArchive = filename.find(archiveExtension + '\\');
1538  if (positionArchive != std::string::npos)
1539  {
1540  std::string::size_type endArchive = positionArchive + archiveExtension.length();
1541  //copy filename -in case the function was called with filename and archivefilename the same string
1542  std::string filenameCopy = filename;
1543  archiveFilename = filenameCopy.substr(0, endArchive);
1544  fileInArchive = filenameCopy.substr(endArchive + 1, std::string::npos);
1545  return true;
1546  }
1547  else //maybe it is an archive
1548  {
1549  std::string::size_type positionArchive2 = filename.find(archiveExtension);
1550  if (positionArchive2 != std::string::npos)
1551  {
1552  archiveFilename = filename;
1553  return false;
1554  }
1555  }
1556  }
1557 
1558  return false;
1559  }
1560 
1562  trUtil::FileType FileUtils::GetFileTypeForFileInArchive(const osgDB::ArchiveExtended& a, const std::string& path) const
1563  {
1564  return GetFileTypeFromOSGDBFileType(a.getFileType(path));
1565  }
1566 
1567 
1569  void FileUtils::DirGetFilesInArchive(const osgDB::ArchiveExtended& a, const std::string& path, DirectoryContents& result) const
1570  {
1571 
1572  std::string archiveName;
1573  std::string fileInArchive;
1574 
1575  SplitArchiveFilename(path, archiveName, fileInArchive);
1576 
1577  result = a.getDirectoryContents(fileInArchive);
1578  }
1579 
1581  trUtil::FileInfo FileUtils::GetFileInfoForFileInArchive(const osgDB::ArchiveExtended& a, const std::string& strFile) const
1582  {
1583  trUtil::FileInfo info;
1584 
1585  // chop trailing slashes off
1586  std::string choppedStr = strFile;
1587  if (strFile.size() > 0 && (strFile[strFile.size() - 1] == '\\' ||
1588  strFile[strFile.size() - 1] == '/'))
1589  {
1590  choppedStr = strFile.substr(0, strFile.length() - 1);
1591  }
1592 
1593  info.fileType = GetFileTypeFromOSGDBFileType(a.getFileType(strFile));
1594 
1595  if (info.fileType != FILE_NOT_FOUND)
1596  {
1597 
1598  info.extensionlessFileName = osgDB::getStrippedName(choppedStr);
1599  info.baseName = osgDB::getSimpleFileName(choppedStr);
1600  info.fileName = choppedStr;
1601  info.path = osgDB::getFilePath(choppedStr);
1602  info.extension = osgDB::getFileExtension(choppedStr);
1603  info.isInArchive = true;
1604 
1605  //todo- how to get these?
1606  info.size = 0;
1607  info.lastModified = 0;
1608  }
1609 
1610  return info;
1611  }
1612 
1614  osgDB::ArchiveExtended* FileUtils::FindArchive(const std::string& filename) const
1615  {
1616  osg::ref_ptr<osgDB::Archive> archiveResult = nullptr;
1617  FileInfo archiveInfo = GetFileInfo(filename);
1618 
1619  if (archiveInfo.fileType != FILE_NOT_FOUND)
1620  {
1621  osgDB::Registry* reg = osgDB::Registry::instance();
1622 
1623  std::string archiveFilename;
1624  std::string strippedFilename;
1625  std::string absoluteFilename = filename;
1626 
1627  if (!IsAbsolutePath(filename))
1628  {
1629  absoluteFilename = ArchiveRelativeToAbsolute(filename);
1630  }
1631 
1632  SplitArchiveFilename(absoluteFilename, archiveFilename, strippedFilename);
1633 
1634  archiveResult = reg->getFromArchiveCache(archiveFilename);
1635  if (archiveResult == nullptr)
1636  {
1637  //attempt to load
1638  osg::ref_ptr<osgDB::ReaderWriter::Options> options = reg->getOptions() ?
1639  static_cast<osgDB::ReaderWriter::Options*>(reg->getOptions()->clone(osg::CopyOp::SHALLOW_COPY)) :
1640  new osgDB::ReaderWriter::Options;
1641 
1642  osgDB::ReaderWriter::ReadResult readResult = reg->openArchive(archiveFilename, osgDB::ReaderWriter::READ, 4096, options.get());
1643  if (readResult.success())
1644  {
1645  archiveResult = readResult.getArchive();
1646  }
1647 
1648  }
1649  }
1650 
1651  if (archiveResult.valid())
1652  {
1653  return dynamic_cast<osgDB::ArchiveExtended*>(archiveResult.get());
1654  }
1655  else
1656  {
1657  return nullptr;
1658  }
1659  }
1660 
1663  {
1664  if (ft == osgDB::REGULAR_FILE)
1665  {
1666  return trUtil::REGULAR_FILE;
1667  }
1668  else if (ft == osgDB::DIRECTORY)
1669  {
1670  return trUtil::DIRECTORY;
1671  }
1672  else
1673  {
1674  return trUtil::FILE_NOT_FOUND;
1675  }
1676 
1677  }
1678 
1680  bool FileUtils::ContainsArchiveExtension(const std::string& path) const
1681  {
1682 
1683  //osgDB::Registry::getArchiveExtensions() was submitted and should be released in OSG 3.0
1684 #if defined(OPENSCENEGRAPH_MAJOR_VERSION) && OPENSCENEGRAPH_MAJOR_VERSION >= 3
1685  osgDB::Registry* reg = osgDB::Registry::instance();
1686 
1687  const osgDB::Registry::ArchiveExtensionList& extensions = reg->getArchiveExtensions();
1688 
1689  osgDB::Registry::ArchiveExtensionList::const_iterator iter = extensions.begin();
1690  osgDB::Registry::ArchiveExtensionList::const_iterator iterEnd = extensions.end();
1691  for (; iter != iterEnd; ++iter)
1692  {
1693  const std::string& archiveExtension = *iter;
1694 #else
1695  std::string archiveExtension = ".zip";
1696  {
1697 #endif
1698 
1699  std::string::size_type positionArchive = path.find(archiveExtension);
1700  if (positionArchive != std::string::npos)
1701  {
1702  return true;
1703  }
1704  }
1705 
1706  return false;
1707  }
1708 
1710  osg::Object* FileUtils::ReadObject(const std::string& filename, osgDB::ReaderWriter::Options* options)
1711  {
1712  FileInfo info = GetFileInfo(filename);
1713  osgDB::Registry* reg = osgDB::Registry::instance();
1714 
1715  osg::Object* result = nullptr;
1716 
1717  if (info.fileType == ARCHIVE || info.isInArchive)
1718  {
1719  std::string archiveFilename;
1720  std::string strippedFilename;
1721  std::string absoluteFilename = filename;
1722 
1723  if (!IsAbsolutePath(filename))
1724  {
1725  absoluteFilename = ArchiveRelativeToAbsolute(filename);
1726  }
1727 
1728  SplitArchiveFilename(absoluteFilename, archiveFilename, strippedFilename);
1729 
1730  osg::Object* obj = reg->getFromObjectCache(strippedFilename);
1731  if (obj != nullptr)
1732  {
1733  result = obj;
1734  }
1735  else
1736  {
1737  osgDB::Archive* arch = FindArchive(archiveFilename);
1738  if (arch != nullptr)
1739  {
1740  osgDB::ReaderWriter::ReadResult readResult = arch->readObject(strippedFilename, options);
1741  if (readResult.validObject())
1742  {
1743  result = readResult.takeObject();
1744  if (result != nullptr && options != nullptr && (options->getObjectCacheHint() & osgDB::ReaderWriter::Options::CACHE_OBJECTS))
1745  {
1746  reg->addEntryToObjectCache(strippedFilename, result);
1747  }
1748  }
1749  }
1750  }
1751  }
1752  else if (info.fileType == REGULAR_FILE)
1753  {
1754  result = osgDB::readObjectFile(filename, options);
1755  }
1756 
1757  return result;
1758  }
1759 
1761  osg::Node* FileUtils::ReadNode(const std::string& filename, osgDB::ReaderWriter::Options* options)
1762  {
1763  FileInfo info = GetFileInfo(filename);
1764  osgDB::Registry* reg = osgDB::Registry::instance();
1765 
1766  osg::Node* result = nullptr;
1767 
1768  if (info.fileType == ARCHIVE || info.isInArchive)
1769  {
1770  std::string archiveFilename;
1771  std::string strippedFilename;
1772  std::string absoluteFilename = filename;
1773 
1774  if (!IsAbsolutePath(filename))
1775  {
1776  absoluteFilename = ArchiveRelativeToAbsolute(filename);
1777  }
1778 
1779  SplitArchiveFilename(absoluteFilename, archiveFilename, strippedFilename);
1780 
1781  osg::Object* obj = reg->getFromObjectCache(strippedFilename);
1782  if (obj != nullptr)
1783  {
1784  result = dynamic_cast<osg::Node*>(obj);
1785  }
1786  else
1787  {
1788  osgDB::Archive* arch = FindArchive(archiveFilename);
1789  if (arch != nullptr)
1790  {
1791  osgDB::ReaderWriter::ReadResult readResult = arch->readNode(strippedFilename, options);
1792  if (readResult.validNode())
1793  {
1794  result = readResult.takeNode();
1795 
1796  if (result != nullptr && options != nullptr && (options->getObjectCacheHint() & osgDB::ReaderWriter::Options::CACHE_NODES))
1797  {
1798  reg->addEntryToObjectCache(strippedFilename, result);
1799  }
1800  }
1801  }
1802  }
1803  }
1804  else // resume with usual loading
1805  {
1806  result = osgDB::readNodeFile(filename, options);
1807  }
1808 
1809  return result;
1810  }
1811 
1813  FileUtilIOException::FileUtilIOException(const std::string& message, const std::string& filename, unsigned int linenum)
1814  : trUtil::Exception(message, filename, linenum)
1815  {
1816  }
1817 
1819  FileNotFoundException::FileNotFoundException(const std::string& message, const std::string& filename, unsigned int linenum)
1820  : trUtil::Exception(message, filename, linenum)
1821  {
1822  }
1823 
1825  DirectoryPush::DirectoryPush(const std::string& dir)
1826  : mSucceeded(false)
1827  {
1828  try
1829  {
1831  mSucceeded = true;
1832  }
1833  catch (FileNotFoundException& ex)
1834  {
1835  // Eat it because we have a bool to say if it passed.
1836  mError = ex.ToString();
1837  }
1838  }
1841  {
1842  if (mSucceeded)
1843  {
1845  }
1846  }
1847 
1850  {
1851  return mSucceeded;
1852  }
1853 
1855  const std::string& DirectoryPush::GetError()
1856  {
1857  return mError;
1858  }
1859 }
std::string mCurrentDirectory
Definition: FileUtils.h:684
LOG_DEBUG
Definition: LogLevel.h:64
std::string ToString() const
Converts this exception to a string.
Definition: Exception.cpp:52
osgDB::ArchiveExtended * FindArchive(const std::string &archiveFileName) const
Used to search for archives by filename.
Definition: FileUtils.cpp:1614
void LogMessage(const std::string &cppFile, const std::string &method, int line, const std::string &msg, LogLevel logLevel) const
Logs a time-stamped message.
Definition: Log.cpp:85
trUtil::Logging::Log * mLogger
Definition: FileUtils.h:682
void FileDelete(const std::string &strFile) const
Deletes the given file.
Definition: FileUtils.cpp:417
static osg::ref_ptr< FileUtils > mInstance
Definition: FileUtils.h:680
FileNotFoundException(const std::string &message, const std::string &filename, unsigned int linenum)
Definition: FileUtils.cpp:1819
virtual ~FileUtils()
Destructor.
Definition: FileUtils.cpp:1509
Exception for signaling file utility i/o errors.
Definition: FileUtils.h:101
bool FileExists(const std::string &strFile, bool caseInsensitive=false) const
Queries if a given file exists.
Definition: FileUtils.cpp:217
A unary function created to have a compatibility to the old std::unary_function while still using c++...
Definition: UnaryFunction.h:41
This is the exception class used throughout the engine.
Definition: Exception.h:48
FileUtils()
Default constructor.
Definition: FileUtils.cpp:1498
osg::Node * ReadNode(const std::string &filename, osgDB::ReaderWriter::Options *options=NULL)
Reads a node.
Definition: FileUtils.cpp:1761
bool IsLevelEnabled(LogLevel logLevel) const
Queries if a level is enabled.
Definition: Log.cpp:286
osg::Object * ReadObject(const std::string &filename, osgDB::ReaderWriter::Options *options=NULL)
Reads an object.
Definition: FileUtils.cpp:1710
FileType GetFileTypeForFileInArchive(const osgDB::ArchiveExtended &a, const std::string &path) const
Gets file type for file in archive.
Definition: FileUtils.cpp:1562
Singleton class implementing basic file operations.
Definition: FileUtils.h:125
std::string RunCommand(const char *cmd)
Runs a command on the console, and returns the console printout in a form of a string.
Definition: FileUtils.cpp:195
void MakeDirectory(const std::string &strDir) const
creates a new directory from a path.
Definition: FileUtils.cpp:1219
std::string extensionlessFileName
the file name with extension
Definition: FileUtils.h:86
const struct FileInfo GetFileInfo(const std::string &strFile, bool caseInsensitive=false) const
Definition: FileUtils.cpp:441
struct holding information about a file.
Definition: FileUtils.h:81
static bool iMakeDirectory(const std::string &path)
Definition: FileUtils.cpp:121
bool operator()(char c) const
Definition: FileUtils.cpp:473
void DirGetFilesInArchive(const osgDB::ArchiveExtended &a, const std::string &path, DirectoryContents &result) const
Internal function used to find files within an archive subdirectory.
Definition: FileUtils.cpp:1569
void ChangeDirectory(const std::string &path)
Changes the current directory to the one given in "path." This will clear the stack of directories th...
Definition: FileUtils.cpp:707
void RecursDeleteDir(bool bRecursive)
Recurs delete dir.
Definition: FileUtils.cpp:1432
std::vector< std::string > DirectoryContents
Definition: FileUtils.h:60
std::vector< std::string > FileExtensionList
Definition: FileUtils.h:61
For tokenizing paths.
Definition: FileUtils.h:742
DirectoryContents DirGetSubs(const std::string &path) const
Dir get subs.
Definition: FileUtils.cpp:977
Exception for signalling file not found errors.
Definition: FileUtils.h:113
bool IsAbsolutePath(std::string strFileOrDir) const
Query whether a given string is an absolute path or not.
Definition: FileUtils.cpp:630
std::string ArchiveRelativeToAbsolute(const std::string &relativeFile) const
Archive relative to absolute.
Definition: FileUtils.cpp:478
DirectoryContents DirGetFiles(const std::string &path, const FileExtensionList &extensions=FileExtensionList()) const
Note: throws exceptions of type trUtil::Exception.
Definition: FileUtils.cpp:901
FileType GetFileTypeFromOSGDBFileType(osgDB::FileType ft) const
Gets file type from osgdb file type.
Definition: FileUtils.cpp:1662
FileType
Values that represent file types.
Definition: FileUtils.h:68
static std::string ConcatPaths(const std::string &left, const std::string &right)
Concatenates two paths adding a path separator in between if necessary.
Definition: FileUtils.cpp:1485
void FileMove(const std::string &strSrc, const std::string &strDest, bool bOverwrite) const
Moves a file.
Definition: FileUtils.cpp:348
void LogException(trUtil::Logging::LogLevel level=trUtil::Logging::LogLevel::LOG_ERROR) const
Logs the exception to the default logger.
Definition: Exception.cpp:60
time_t lastModified
when the file was last modified.
Definition: FileUtils.h:89
size_t size
the size of the file in bytes.
Definition: FileUtils.h:88
std::string RelativePath(const std::string &absolutePath, const std::string &file) const
Helper function that returns the relative path between absolutePath and file.
Definition: FileUtils.cpp:1253
#define S_ISDIR(x)
Definition: FileUtils.cpp:85
DirectoryPush(const std::string &dir)
Constructor.
Definition: FileUtils.cpp:1825
static void tokenize(std::vector< std::string > &tokens, const std::string &stringToParse, const Pred &predFxn=Pred())
The predicate should evaluate to true when applied to a separator.
Definition: StringUtils.h:99
void ChangeDirectoryInternal(const std::string &path)
Change directory internal.
Definition: FileUtils.cpp:740
std::string GetAbsolutePath(const std::string &relativePath, bool removeFinalFile=false) const
Converts a relative path to an absolute path.
Definition: FileUtils.cpp:833
bool DirExists(const std::string &strDir, bool caseInsensitive=false) const
Queries if a given dir exists.
Definition: FileUtils.cpp:1247
bool SplitArchiveFilename(const std::string &fullFilename, std::string &archiveFilename, std::string &fileInArchive) const
Splits a filename for a file within an archive into two parts, the archive filename and the actual fi...
Definition: FileUtils.cpp:1513
std::vector< std::string > mStackOfDirectories
Definition: FileUtils.h:685
const std::string & CurrentDirectory() const
Current directory.
Definition: FileUtils.cpp:734
FileType fileType
The enum value specifying the type of file.
Definition: FileUtils.h:90
void InternalDirCopy(const std::string &srcPath, const std::string &destPath, bool bOverwrite) const
Internal dir copy.
Definition: FileUtils.cpp:995
#define stat64
Definition: FileUtils.cpp:76
bool IsSameFile(const std::string &file1, const std::string &file2) const
It is possible for two different path strings to point at the same file on disk.
Definition: FileUtils.cpp:1318
std::string path
the path to the file
Definition: FileUtils.h:84
static FileUtils & GetInstance()
Character separating the parts of a file path.
Definition: FileUtils.h:137
bool isInArchive
true if the specified file lives within an archive
Definition: FileUtils.h:91
void PopDirectory()
sets the current directory to the last directory on the stack.
Definition: FileUtils.cpp:816
Namespace that holds various utility classes for the engine.
Definition: SmrtPtr.h:208
std::string fileName
the file name with full path
Definition: FileUtils.h:83
~DirectoryPush()
Destructor.
Definition: FileUtils.cpp:1840
void FileCopy(const std::string &strSrc, const std::string &strDest, bool bOverwrite) const
Copys a file.
Definition: FileUtils.cpp:223
void DirCopy(const std::string &srcPath, const std::string &destPath, bool bOverwrite, bool copyContentsOnly=false) const
Copys an entire directory.
Definition: FileUtils.cpp:1046
LOG_WARNING
Definition: LogLevel.h:64
FileUtilIOException(const std::string &message, const std::string &filename, unsigned int linenum)
Definition: FileUtils.cpp:1813
TR_UTIL_EXPORT int StrCompare(const std::string &one, const std::string &two, bool caseSensitive=true)
Compares strings like strcmp or stricmp or strcasecmp.
Definition: StringUtils.cpp:74
bool GetSucceeded()
Gets the succeeded.
Definition: FileUtils.cpp:1849
void PushDirectory(const std::string &path)
Changes the current directory to the one given in "path" and adds the previous current directory to a...
Definition: FileUtils.cpp:801
std::string mError
Definition: FileUtils.h:734
void MakeDirectoryEX(std::string strDir)
A more powerful version of the standard mkdir.
Definition: FileUtils.cpp:651
std::string getFileExtensionIncludingDot(const std::string &fileName)
Definition: FileUtils.cpp:876
void CleanupFileString(std::string &strFileOrDir) const
Ensure that the passed in string is fit for use as a file or dir string.
Definition: FileUtils.cpp:601
bool DirDelete(const std::string &strDir, bool bRecursive)
Deletes a directory.
Definition: FileUtils.cpp:1139
FileInfo GetFileInfoForFileInArchive(const osgDB::ArchiveExtended &a, const std::string &path) const
Gets file information for file in archive.
Definition: FileUtils.cpp:1581
std::string extension
the file extension
Definition: FileUtils.h:87
bool IsSameFile_Internal(const std::string &file1, const std::string &file2) const
Query if &#39;file1&#39; is same file internal.
Definition: FileUtils.cpp:1380
const std::string & GetError()
Gets the error.
Definition: FileUtils.cpp:1855
static const char PATH_SEPARATOR
Definition: FileUtils.h:128
Simple class to change directory where it automatically pop back out on destruction.
Definition: FileUtils.h:695
const struct FileInfo GetFileInfo_Internal(const std::string &strFile, bool caseInsensitive) const
Definition: FileUtils.cpp:519
static Log & GetInstance(const std::string &name=Log::LOG_DEFAULT_NAME)
Retrieve singleton instance of the log class for a give string name.
Definition: Log.cpp:203
static std::string GetPathRoot(const std::string &path)
Definition: FileUtils.cpp:109
std::string baseName
the file name with extension
Definition: FileUtils.h:85
bool ContainsArchiveExtension(const std::string &path) const
Query if &#39;path&#39; contains archive extension.
Definition: FileUtils.cpp:1680