Newer
Older
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2021 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "fileName.H"
#include "fileOperation.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const char* const Foam::fileName::typeName = "fileName";
int Foam::fileName::debug(Foam::debug::debugSwitch(fileName::typeName, 0));
int Foam::fileName::allowSpaceInFileName
(
#ifdef _WIN32
1 // Windows: expect spaces to occur
Foam::debug::infoSwitch("allowSpaceInFileName", 0)
const Foam::fileName Foam::fileName::null;
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
// doClean:
// - remove duplicate slashes, "/./" and "/../" components.
//
// checkValid:
// - similar to stripInvalid (but silent)
//
// return True if the content changed
static bool cleanFileName
std::string& str,
const bool doClean,
const bool checkValid
const auto maxLen = str.length();
std::string::size_type nChar = 0;
// Preserve UNC \\server\path (windows)
// - MS-windows only, but handle for other systems
// since there is no collision with this pattern
if (maxLen > 2 && str[0] == '\\' && str[1] == '\\')
{
nChar += 2;
}
char prev = 0;
auto top = std::string::npos; // Not yet found
bool changed = false;
for (auto src = nChar; src < maxLen; /*nil*/)
{
char c = str[src++];
// Treat raw backslash like a path separator.
// There is no "normal" way for these to be there
// (except for an OS that uses them), but can cause issues
// when writing strings, shell commands etc.
if (c == '\\')
{
c = '/';
str[nChar] = c;
changed = true;
}
else if (checkValid && !Foam::fileName::valid(c))
{
// Ignore invalid chars
// Could explicitly allow space character or rely on
// allowSpaceInFileName via fileName::valid()
continue;
if (c == '/' && top == std::string::npos)
{
// Top-level slash not previously determined
top = (src-1);
}
if (doClean && prev == '/')
// Repeated '/' - skip it
if (c == '/')
{
continue;
}
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Could be "/./", "/../" or a trailing "/."
if (c == '.')
{
// Trailing "/." - skip it
if (src >= maxLen)
{
break;
}
// Peek at the next character
const char c1 = str[src];
// Found "/./" - skip over it
if (c1 == '/' || c1 == '\\')
{
++src;
continue;
}
// Trailing "/.." or intermediate "/../"
if
(
c1 == '.'
&&
(
src+1 >= maxLen
|| str[src+1] == '/' || str[src+1] == '\\'
)
)
{
// Backtrack to find the parent directory
// Minimum of 3 characters: '/x/../'
// Strip it, provided it is above the top point
std::string::size_type parent;
if
(
nChar > 2
&& top != std::string::npos
&& (parent = str.rfind('/', nChar-2)) != std::string::npos
&& parent >= top
)
{
nChar = parent + 1; // Retain '/' from the parent
src += 2;
continue;
}
// Bad resolution, eg 'abc/../../'
// Retain the sequence, but move the top to avoid it being
// considered a valid parent later
top = nChar + 2;
}
}
str[nChar++] = prev = c;
// Remove trailing '/'
if (doClean && nChar > 1 && str[nChar-1] == '/')
--nChar;
str.erase(nChar);
return changed || (nChar != maxLen);
}
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
bool Foam::fileName::clean(std::string& str)
{
return cleanFileName(str, true, false); // clean, checkValid = false
}
Foam::fileName Foam::fileName::validate
(
const std::string& str,
const bool doClean
)
{
fileName out(str, false); // copy, no stripping
cleanFileName(out, doClean, true); // checkValid = true
return out;
}
Foam::fileName Foam::fileName::concat
(
const std::string& s1,
const std::string& s2,
const char delim
)
{
const auto n1 = s1.length();
const auto n2 = s2.length();
fileName out;
out.reserve(n1 + n2 + 1);
out += s1;
if (n1 && n2 && s1.back() != delim && s2.front() != delim)
// Add delimiter
out += delim;
}
out += s2;
// Could also remove trailing '/', if desired.
return out;
}
bool Foam::fileName::equals(const std::string& s1, const std::string& s2)
{
// Do not use (s1 == s2) or s1.compare(s2) first since this would
// potentially be doing the comparison twice.
std::string::size_type i1 = 0;
std::string::size_type i2 = 0;
const auto n1 = s1.length();
const auto n2 = s2.length();
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//Info<< "compare " << s1 << " == " << s2 << endl;
while (i1 < n1 && i2 < n2)
{
//Info<< "check '" << s1[i1] << "' vs '" << s2[i2] << "'" << endl;
if (s1[i1] != s2[i2])
{
return false;
}
// Increment to next positions and also skip repeated slashes
do
{
++i1;
}
while (s1[i1] == '/');
do
{
++i2;
}
while (s2[i2] == '/');
}
//Info<< "return: " << Switch(i1 == n1 && i2 == n2) << endl;
// Equal if it made it all the way through both strings
return (i1 == n1 && i2 == n2);
}
bool Foam::fileName::isBackup(const std::string& s)
{
return true;
}
// Now check the extension
if (dot == npos)
{
return false;
}
!s.compare(dot, npos, "bak") || !s.compare(dot, npos, "BAK")
|| !s.compare(dot, npos, "old") || !s.compare(dot, npos, "save")
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fileName::fileName(const UList<word>& list)
size_type len = 0;
for (const word& item : list)
len += 1 + item.length(); // Include space for '/' needed
for (const word& item : list)
if (length()) operator+=('/');
operator+=(item);
}
}
}
Foam::fileName::fileName(std::initializer_list<word> list)
size_type len = 0;
for (const word& item : list)
len += 1 + item.length(); // Include space for '/' needed
for (const word& item : list)
if (length()) operator+=('/');
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::fileName::Type Foam::fileName::type
(
bool followLink,
bool checkGzip
) const
Type t = ::Foam::type(*this, followLink);
if (checkGzip && (Type::UNDEFINED == t) && size())
{
// Also check for gzip file?
t = ::Foam::type(*this + ".gz", followLink);
}
return t;
Foam::fileName& Foam::fileName::toAbsolute()
{
if (!isAbsolute(*this))
f.clean(); // Remove unneeded ".."
bool Foam::fileName::clean()
{
return fileName::clean(*this);
}
std::string Foam::fileName::nameLessExt(const std::string& str)
{
auto beg = str.rfind('/');
auto dot = str.rfind('.');
if (beg == npos)
beg = 0;
}
else
{
++beg;
}
if (dot != npos && dot <= beg)
{
dot = npos;
}
if (dot == npos)
{
return str.substr(beg);
return str.substr(beg, dot - beg);
}
Foam::fileName Foam::fileName::relative
(
const fileName& parent,
{
const auto top = parent.size();
const fileName& f = *this;
// Everything after "parent/xxx/yyy" -> "xxx/yyy"
//
// case-relative:
// "parent/xxx/yyy" -> "<case>/xxx/yyy"
top && (f.size() > (top+1)) && f[top] == '/'
{
return "<case>"/f.substr(top+1);
}
else
{
return f.substr(top+1);
}
}
else if (caseTag && f.size() && !f.isAbsolute())
Foam::wordList Foam::fileName::components(const char delim) const
const auto parsed = stringOps::split<string>(*this, delim);
label i = 0;
for (const auto& sub : parsed)
// Could easily filter out '.' here too
words[i] = sub.str();
++i;
// As a plain wordList
return words;
}
Foam::word Foam::fileName::component
(
const size_type cmpt,
const char delim
const auto parsed = stringOps::split<string>(*this, delim);
if (parsed.size())
if (cmpt == std::string::npos)
{
return parsed.last().str();
}
else if (cmpt < parsed.size())
{
return parsed[cmpt].str();
}
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
Foam::fileName& Foam::fileName::operator/=(const string& other)
{
fileName& s = *this;
if (s.size())
{
if (other.size())
{
// Two non-empty strings: can concatenate
if (s.back() != '/' && other.front() != '/')
{
s += '/';
}
}
}
else if (other.size())
{
// The first string is empty
s = other;
}
return *this;
}
// * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
Foam::fileName Foam::operator/(const string& s1, const string& s2)
// Two non-empty strings: can concatenate
if (s1.back() == '/' || s2.front() == '/')
// The second string was empty
// The first string is empty
// Both strings are empty
return fileName();
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
Foam::fileName Foam::search(const word& file, const fileName& directory)
{
// Search current directory for the file
for
(
const fileName& item
: fileHandler().readDir(directory, fileName::FILE)
)
return directory/item;
}
}
// If not found search each of the sub-directories
for
(
const fileName& item
: fileHandler().readDir(directory, fileName::DIRECTORY)
)
fileName path = search(file, directory/item);
{
return path;
}
}
// ************************************************************************* //