Skip to content

Commit 61cdb9d

Browse files
committed
Update functions to a utility file
1 parent d2a3f61 commit 61cdb9d

File tree

5 files changed

+281
-227
lines changed

5 files changed

+281
-227
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ set(COMPLEX_SRCS
675675

676676
${COMPLEX_SOURCE_DIR}/Utilities/ArrayThreshold.cpp
677677
${COMPLEX_SOURCE_DIR}/Utilities/FilePathGenerator.cpp
678+
${COMPLEX_SOURCE_DIR}/Utilities/FileUtilities.cpp
678679
${COMPLEX_SOURCE_DIR}/Utilities/FilterUtilities.cpp
679680
${COMPLEX_SOURCE_DIR}/Utilities/TooltipGenerator.cpp
680681
${COMPLEX_SOURCE_DIR}/Utilities/TooltipRowItem.cpp

src/complex/Common/AtomicFile.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "AtomicFile.hpp"
22

3-
#include "complex/Utilities/FilterUtilities.hpp"
43
#include "complex/Utilities/FileUtilities.hpp"
4+
#include "complex/Utilities/FilterUtilities.hpp"
55

66
#include <fmt/format.h>
77

@@ -45,7 +45,7 @@ AtomicFile::AtomicFile(const std::string& filename, bool autoCommit)
4545
}
4646

4747
// Validate write permissions
48-
auto result = ValidateDirectoryWritePermission(m_FilePath, true);
48+
auto result = FileUtilities::ValidateDirectoryWritePermission(m_FilePath, true);
4949
if(result.invalid())
5050
{
5151
m_Result = MergeResults(m_Result, result);
@@ -77,7 +77,7 @@ AtomicFile::AtomicFile(fs::path&& filepath, bool autoCommit)
7777
}
7878

7979
// Validate write permissions
80-
auto result = ValidateDirectoryWritePermission(m_FilePath, true);
80+
auto result = FileUtilities::ValidateDirectoryWritePermission(m_FilePath, true);
8181
if(result.invalid())
8282
{
8383
m_Result = MergeResults(m_Result, result);

src/complex/Parameters/FileSystemPathParameter.cpp

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,16 @@
22

33
#include "complex/Common/Any.hpp"
44
#include "complex/Common/StringLiteral.hpp"
5-
#include "complex/Utilities/StringUtilities.hpp"
65
#include "complex/Utilities/FileUtilities.hpp"
6+
#include "complex/Utilities/StringUtilities.hpp"
77

88
#include <fmt/core.h>
99

1010
#include <nlohmann/json.hpp>
1111

12-
#include <cctype>
1312
#include <filesystem>
14-
#include <iostream>
1513
#include <stdexcept>
1614

17-
#ifdef _WIN32
18-
#include <io.h>
19-
#define FSPP_ACCESS_FUNC_NAME _access
20-
#else
21-
#include <unistd.h>
22-
#define FSPP_ACCESS_FUNC_NAME access
23-
#endif
24-
2515
namespace fs = std::filesystem;
2616

2717
using namespace complex;
@@ -159,13 +149,13 @@ Result<> FileSystemPathParameter::validatePath(const ValueType& path) const
159149
switch(m_PathType)
160150
{
161151
case complex::FileSystemPathParameter::PathType::InputFile:
162-
return ValidateInputFile(path);
152+
return FileUtilities::ValidateInputFile(path);
163153
case complex::FileSystemPathParameter::PathType::InputDir:
164-
return ValidateInputDir(path);
154+
return FileUtilities::ValidateInputDir(path);
165155
case complex::FileSystemPathParameter::PathType::OutputFile:
166-
return ValidateOutputFile(path);
156+
return FileUtilities::ValidateOutputFile(path);
167157
case complex::FileSystemPathParameter::PathType::OutputDir:
168-
return ValidateOutputDir(path);
158+
return FileUtilities::ValidateOutputDir(path);
169159
}
170160

171161
return {};
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/* ============================================================================
2+
* Copyright (c) 2020 BlueQuartz Software, LLC
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without modification,
6+
* are permitted provided that the following conditions are met:
7+
*
8+
* Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* Redistributions in binary form must reproduce the above copyright notice, this
12+
* list of conditions and the following disclaimer in the documentation and/or
13+
* other materials provided with the distribution.
14+
*
15+
* Neither the names of any of the BlueQuartz Software contributors
16+
* may be used to endorse or promote products derived from this software without
17+
* specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28+
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*
30+
*
31+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
32+
#include "FileUtilities.hpp"
33+
34+
#include <fmt/format.h>
35+
36+
#include <cctype>
37+
#include <fstream>
38+
#include <string>
39+
40+
#ifdef _WIN32
41+
#include <io.h>
42+
#define FSPP_ACCESS_FUNC_NAME _access
43+
#else
44+
#include <unistd.h>
45+
#define FSPP_ACCESS_FUNC_NAME access
46+
#endif
47+
48+
using namespace complex;
49+
50+
#ifdef _WIN32
51+
constexpr int k_CheckWritable = 2;
52+
#else
53+
constexpr int k_CheckWritable = W_OK;
54+
#endif
55+
56+
constexpr int k_HasAccess = 0;
57+
58+
//-----------------------------------------------------------------------------
59+
bool FileUtilities::HasWriteAccess(const std::string& path)
60+
{
61+
return FSPP_ACCESS_FUNC_NAME(path.c_str(), k_CheckWritable) == k_HasAccess;
62+
}
63+
64+
//-----------------------------------------------------------------------------
65+
Result<> FileUtilities::ValidateInputFile(const fs::path& path)
66+
{
67+
if(!fs::exists(path))
68+
{
69+
return MakeErrorResult(-2, fmt::format("File System Path '{}' does not exist", path.string()));
70+
}
71+
72+
if(!fs::is_regular_file(path))
73+
{
74+
return MakeErrorResult(-3, fmt::format("File System Path '{}' is not a file", path.string()));
75+
}
76+
return {};
77+
}
78+
79+
//-----------------------------------------------------------------------------
80+
Result<> FileUtilities::ValidateInputDir(const fs::path& path)
81+
{
82+
if(!fs::exists(path))
83+
{
84+
return MakeErrorResult(-4, fmt::format("File System Path '{}' does not exist", path.string()));
85+
}
86+
if(!fs::is_directory(path))
87+
{
88+
return MakeErrorResult(-5, fmt::format("File System Path '{}' is not a file", path.string()));
89+
}
90+
return {};
91+
}
92+
93+
//-----------------------------------------------------------------------------
94+
Result<> FileUtilities::ValidateDirectoryWritePermission(const fs::path& path, bool isFile)
95+
{
96+
if(path.empty())
97+
{
98+
return MakeErrorResult(-16, "ValidateDirectoryWritePermission() error: given path was empty.");
99+
}
100+
101+
auto checkedPath = path;
102+
if(isFile)
103+
{
104+
checkedPath = checkedPath.parent_path();
105+
}
106+
// We now have the parent directory. Let us see if *any* part of the path exists
107+
108+
// If the path is relative, then make it absolute
109+
if(!checkedPath.is_absolute())
110+
{
111+
try
112+
{
113+
checkedPath = fs::absolute(checkedPath);
114+
} catch(const std::filesystem::filesystem_error& error)
115+
{
116+
return MakeErrorResult(-15, fmt::format("ValidateDirectoryWritePermission() threw an error: '{}'", error.what()));
117+
}
118+
}
119+
120+
auto rootPath = checkedPath.root_path();
121+
122+
// The idea here is to start walking up from the deepest directory and hopefully
123+
// find an existing directory. If we get to the top if the path and we are still
124+
// empty then:
125+
// On unix based systems not sure if it would happen. Even if the user set a path
126+
// to another drive that didn't exist, at some point you hit the '/' and then you
127+
// can try to create the directories.
128+
// On Windows the user put in a bogus drive letter which is just a hard failure
129+
// because we can't make up a new drive letter.
130+
while(!fs::exists(checkedPath) && checkedPath != rootPath)
131+
{
132+
checkedPath = checkedPath.parent_path();
133+
}
134+
135+
if(checkedPath.empty())
136+
{
137+
return MakeErrorResult(-19, "ValidateDirectoryWritePermission() resolved path was empty");
138+
}
139+
140+
if(!fs::exists(checkedPath))
141+
{
142+
return MakeErrorResult(-11, fmt::format("ValidateDirectoryWritePermission() error: The drive does not exist on this system: '{}'", checkedPath.string()));
143+
}
144+
145+
// We should be at the top of the tree with an existing directory.
146+
if(HasWriteAccess(checkedPath.string()))
147+
{
148+
return {};
149+
}
150+
return MakeErrorResult(-8, fmt::format("User does not have write permissions to path '{}'", path.string()));
151+
}
152+
153+
//-----------------------------------------------------------------------------
154+
Result<> FileUtilities::ValidateOutputFile(const fs::path& path)
155+
{
156+
auto result = ValidateDirectoryWritePermission(path, true);
157+
if(result.invalid())
158+
{
159+
return result;
160+
}
161+
if(!fs::exists(path))
162+
{
163+
return MakeWarningVoidResult(-6, fmt::format("File System Path '{}' does not exist. It will be created during execution.", path.string()));
164+
}
165+
return {};
166+
}
167+
168+
//-----------------------------------------------------------------------------
169+
Result<> FileUtilities::ValidateOutputDir(const fs::path& path)
170+
{
171+
auto result = ValidateDirectoryWritePermission(path, false);
172+
if(result.invalid())
173+
{
174+
return result;
175+
}
176+
if(!fs::exists(path))
177+
{
178+
return MakeWarningVoidResult(-7, fmt::format("File System Path '{}' does not exist. It will be created during execution.", path.string()));
179+
}
180+
return {};
181+
}
182+
183+
//-----------------------------------------------------------------------------
184+
Result<> FileUtilities::ValidateCSVFile(const std::string& filePath)
185+
{
186+
constexpr int64_t bufferSize = 2048;
187+
188+
auto absPath = fs::absolute(filePath);
189+
190+
if(!fs::exists({absPath}))
191+
{
192+
return MakeErrorResult(-300, fmt::format("File does not exist: {}", absPath.string()));
193+
}
194+
195+
// Obtain the file size
196+
const size_t fileSize = fs::file_size(absPath);
197+
198+
// Open the file
199+
std::ifstream in(absPath.c_str(), std::ios_base::binary);
200+
if(!in.is_open())
201+
{
202+
return MakeErrorResult(-301, fmt::format("Could not open file for reading: {}", absPath.string()));
203+
}
204+
205+
size_t actualSize = bufferSize;
206+
if(fileSize <= bufferSize)
207+
{
208+
actualSize = fileSize;
209+
}
210+
211+
// Allocate the buffer
212+
std::vector<char> buffer(actualSize, 0);
213+
214+
// Copy the file contents into the buffer
215+
try
216+
{
217+
in.read(buffer.data(), actualSize);
218+
} catch(const std::exception& e)
219+
{
220+
return MakeErrorResult(-302, fmt::format("There was an error reading the data from file: {}. Exception: {}", absPath.string(), e.what()));
221+
}
222+
223+
// Check the buffer for invalid characters, tab characters, new-line characters, and carriage return characters
224+
bool hasNewLines = false;
225+
bool hasCarriageReturns = false;
226+
bool hasTabs = false;
227+
// If the first line of the file is > 2048 then this will fail! (MJ)
228+
for(size_t i = 0; i < actualSize; i++)
229+
{
230+
const char currentChar = buffer[i];
231+
232+
if(currentChar < 32 && currentChar != 9 && currentChar != 10 && currentChar != 13)
233+
{
234+
// This is an unprintable character
235+
return MakeErrorResult(-303, fmt::format("Unprintable characters have been detected in file: {}. Please import a different file.", absPath.string()));
236+
}
237+
if(currentChar == 9)
238+
{
239+
hasTabs = true;
240+
}
241+
else if(currentChar == 10)
242+
{
243+
hasNewLines = true;
244+
}
245+
else if(currentChar == 13)
246+
{
247+
hasCarriageReturns = true;
248+
}
249+
}
250+
251+
if(!hasNewLines && !hasCarriageReturns && !hasTabs)
252+
{
253+
// This might be a binary file
254+
return MakeErrorResult(-304, fmt::format("The file \"{}\" might be a binary file, because line-feed, tab, or carriage return characters have not been detected. Using this file may crash the "
255+
"program or cause unexpected results. Please import a different file.",
256+
absPath.string()));
257+
}
258+
259+
return {};
260+
}

0 commit comments

Comments
 (0)