1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //
6 //  This software is distributed WITHOUT ANY WARRANTY; without even
7 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 //  PURPOSE.  See the above copyright notice for more information.
9 //============================================================================
10 #include <vtkm/io/FileUtils.h>
11 
12 #include <vtkm/cont/ErrorBadValue.h>
13 
14 #include <algorithm>
15 // TODO (nadavi): Once we get c++17 installed uncomment this
16 // #include <filesystem>
17 #include <errno.h>
18 #include <sys/stat.h>
19 
20 #ifdef _MSC_VER
21 #include <direct.h>
22 #endif
23 
24 
25 namespace vtkm
26 {
27 namespace io
28 {
29 
EndsWith(const std::string & value,const std::string & ending)30 bool EndsWith(const std::string& value, const std::string& ending)
31 {
32   if (ending.size() > value.size())
33   {
34     return false;
35   }
36   return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
37 }
38 
Filename(const std::string & filePath)39 std::string Filename(const std::string& filePath)
40 {
41   // TODO (nadavi): Once we get c++17 installed uncomment this
42   // std::filesystem::path path(filePath);
43   // return path.filename();
44 
45 #ifdef _MSC_VER
46   auto lastSlashPos = filePath.rfind(GetWindowsPathSeperator(filePath));
47 #else
48   auto lastSlashPos = filePath.rfind('/');
49 #endif
50   if (lastSlashPos != std::string::npos)
51   {
52     return filePath.substr(lastSlashPos + 1);
53   }
54   return filePath;
55 }
56 
ParentPath(const std::string & filePath)57 std::string ParentPath(const std::string& filePath)
58 {
59   // TODO (nadavi): Once we get c++17 installed uncomment this
60   // std::filesystem::path path(filePath);
61   // return path.parent_path();
62 
63 #ifdef _MSC_VER
64   auto lastSlashPos = filePath.rfind(GetWindowsPathSeperator(filePath));
65 #else
66   auto lastSlashPos = filePath.rfind('/');
67 #endif
68   if (lastSlashPos != std::string::npos)
69   {
70     return filePath.substr(0, lastSlashPos);
71   }
72   return "";
73 }
74 
CreateDirectoriesFromFilePath(const std::string & filePath)75 bool CreateDirectoriesFromFilePath(const std::string& filePath)
76 {
77   // TODO (nadavi): Once we get c++17 installed uncomment this
78   // auto dir = ParentPath(filePath);
79   // if (dir.empty())
80   // {
81   //   return false;
82   // }
83   // return std::filesystem::create_directories(dir);
84 
85   auto dir = ParentPath(filePath);
86   if (dir.empty())
87   {
88     return false;
89   }
90 
91 #ifdef _MSC_VER
92   auto ret = _mkdir(dir.c_str());
93 #else
94   mode_t mode = 0755;
95   auto ret = mkdir(dir.c_str(), mode);
96 #endif
97 
98   if (ret == 0)
99   {
100     return true;
101   }
102 
103   switch (errno)
104   {
105     case ENOENT:
106     {
107       if (!CreateDirectoriesFromFilePath(dir))
108       {
109         return false;
110       }
111       return CreateDirectoriesFromFilePath(filePath);
112     }
113     case EEXIST:
114       return false;
115     default:
116       return false;
117   }
118 }
119 
MergePaths(const std::string & filePathPrefix,const std::string & filePathSuffix)120 std::string MergePaths(const std::string& filePathPrefix, const std::string& filePathSuffix)
121 {
122   // TODO (nadavi): Once we get c++17 installed uncomment this
123   // NOTE: This function may not directly mimic the c++ filesystem '/' behavior
124   //       the edge case tests will probably need to be updated when switching.
125   // std::filesystem::path prefix(filePathPrefix);
126   // std::filesystem::path suffix(filePathSuffix);
127   // std::filesystem::path fullPath = prefix / suffix;
128   // return fullPath.string();
129 
130   auto prefix = filePathPrefix;
131   auto suffix = filePathSuffix;
132   char prefixPathSeperator = '/';
133   char suffixPathSeperator = '/';
134 
135   if (prefix.empty() && suffix.empty())
136   {
137     return "";
138   }
139   else if (prefix.empty())
140   {
141     return suffix;
142   }
143   else if (suffix.empty())
144   {
145     return prefix;
146   }
147 
148 #ifdef _MSC_VER
149   prefixPathSeperator = GetWindowsPathSeperator(prefix);
150   suffixPathSeperator = suffix[0] == '/' || suffix[0] == '\\' ? suffix[0] : prefixPathSeperator;
151 #endif
152 
153   if (prefix[prefix.length() - 1] == prefixPathSeperator)
154   {
155     prefix = prefix.substr(0, prefix.length() - 1);
156   }
157   if (suffix[0] == suffixPathSeperator)
158   {
159     suffix = suffix.substr(1, suffix.length());
160   }
161   return prefix + prefixPathSeperator + suffix;
162 }
163 
PrefixStringToFilename(const std::string & filePath,const std::string & prefix)164 std::string PrefixStringToFilename(const std::string& filePath, const std::string& prefix)
165 {
166   auto parentPath = ParentPath(filePath);
167   auto filename = Filename(filePath);
168   filename = prefix + filename;
169   return MergePaths(parentPath, filename);
170 }
171 
GetWindowsPathSeperator(const std::string & filePath)172 char GetWindowsPathSeperator(const std::string& filePath)
173 {
174   auto slashType = filePath.rfind('/');
175   if (slashType == std::string::npos)
176   {
177     return '\\';
178   }
179   return '/';
180 }
181 
182 } // namespace vtkm::io
183 } // namespace vtkm
184