fileName.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2016-2019 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "fileName.H"
30 #include "wordRe.H"
31 #include "wordList.H"
32 #include "DynamicList.H"
33 #include "OSspecific.H"
34 #include "fileOperation.H"
35 #include "stringOps.H"
36 
37 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
38 
39 const char* const Foam::fileName::typeName = "fileName";
40 
41 int Foam::fileName::debug(Foam::debug::debugSwitch(fileName::typeName, 0));
42 
44 (
45  #ifdef _WIN32
46  1 // Windows: expect spaces to occur
47  #else
48  Foam::debug::infoSwitch("allowSpaceInFileName", 0)
49  #endif
50 );
51 
53 
54 
55 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
56 
58 (
59  const std::string& s,
60  const bool doClean
61 )
62 {
63  // The logic is very similar to stripInvalid,
64  // but silently removes bad characters
65 
66  fileName out;
67  out.resize(s.length());
68 
69  std::string::size_type len = 0;
70 
71  auto iter = s.cbegin();
72 
73  #ifdef _WIN32
74  // Preserve UNC \\server-name\...
75  if (s.length() > 2 && s[0] == '\\' && s[1] == '\\')
76  {
77  len += 2;
78  ++iter;
79  ++iter;
80  }
81  #endif
82 
83  char prev = 0;
84  for (/*nil*/; iter != s.cend(); ++iter)
85  {
86  char c = *iter;
87 
88  // Treat raw backslash like a path separator. There is no "normal"
89  // way for these to be there (except for an OS that uses them), but
90  // could also cause issues when writing strings, shell commands etc.
91 
92  if (c == '\\')
93  {
94  c = '/';
95  }
96 
97  // Could explicitly allow space character or rely on
98  // allowSpaceInFileName via fileName::valid()
99 
100  if (fileName::valid(c))
101  {
102  if (doClean && prev == '/' && c == '/')
103  {
104  // Avoid repeated '/';
105  continue;
106  }
107 
108  // Only track valid chars
109  out[len++] = prev = c;
110  }
111  }
112 
113  if (doClean && prev == '/' && len > 1)
114  {
115  // Avoid trailing '/'
116  --len;
117  }
118 
119  out.resize(len);
120 
121  return out;
122 }
123 
124 
126 (
127  const std::string& s1,
128  const std::string& s2,
129  const char delim
130 )
131 {
132  const auto n1 = s1.length();
133  const auto n2 = s2.length();
134 
135  fileName out;
136  out.reserve(n1 + n2 + 1);
137 
138  out += s1;
139 
140  if (n1 && n2 && s1.back() != delim && s2.front() != delim)
141  {
142  // Add delimiter
143  out += delim;
144  }
145 
146  out += s2;
147 
148  // Could also remove trailing '/', if desired.
149  return out;
150 }
151 
152 
153 bool Foam::fileName::equals(const std::string& s1, const std::string& s2)
154 {
155  // Do not use (s1 == s2) or s1.compare(s2) first since this would
156  // potentially be doing the comparison twice.
157 
158  std::string::size_type i1 = 0;
159  std::string::size_type i2 = 0;
160 
161  const auto n1 = s1.length();
162  const auto n2 = s2.length();
163 
164  //Info<< "compare " << s1 << " == " << s2 << endl;
165  while (i1 < n1 && i2 < n2)
166  {
167  //Info<< "check '" << s1[i1] << "' vs '" << s2[i2] << "'" << endl;
168 
169  if (s1[i1] != s2[i2])
170  {
171  return false;
172  }
173 
174  // Increment to next positions and also skip repeated slashes
175  do
176  {
177  ++i1;
178  }
179  while (s1[i1] == '/');
180 
181  do
182  {
183  ++i2;
184  }
185  while (s2[i2] == '/');
186  }
187  //Info<< "return: " << Switch(i1 == n1 && i2 == n2) << endl;
188 
189  // Equal if it made it all the way through both strings
190  return (i1 == n1 && i2 == n2);
191 }
192 
193 
194 bool Foam::fileName::isBackup(const std::string& s)
195 {
196  if (s.empty())
197  {
198  return false;
199  }
200  else if (s.back() == '~')
201  {
202  return true;
203  }
204 
205  // Now check the extension
206  auto dot = find_ext(s);
207 
208  if (dot == npos)
209  {
210  return false;
211  }
212 
213  ++dot;
214 
215  return
216  (
217  !s.compare(dot, npos, "bak") || !s.compare(dot, npos, "BAK")
218  || !s.compare(dot, npos, "old") || !s.compare(dot, npos, "save")
219  );
220 }
221 
222 
223 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
224 
226 {
227  size_type len = 0;
228  for (const word& item : list)
229  {
230  len += 1 + item.length(); // Include space for '/' needed
231  }
232  reserve(len);
233 
234  for (const word& item : list)
235  {
236  if (item.length())
237  {
238  if (length()) operator+=('/');
239  operator+=(item);
240  }
241  }
242 }
243 
244 
245 Foam::fileName::fileName(std::initializer_list<word> list)
246 {
247  size_type len = 0;
248  for (const word& item : list)
249  {
250  len += 1 + item.length(); // Include space for '/' needed
251  }
252  reserve(len);
253 
254  for (const word& item : list)
255  {
256  if (item.length())
257  {
258  if (length()) operator+=('/');
259  operator+=(item);
260  }
261  }
262 }
263 
264 
265 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
266 
268 (
269  bool followLink,
270  bool checkGzip
271 ) const
272 {
273  Type t = ::Foam::type(*this, followLink);
274 
275  if (checkGzip && (Type::UNDEFINED == t) && size())
276  {
277  // Also check for gzip file?
278  t = ::Foam::type(*this + ".gz", followLink);
279  }
280 
281  return t;
282 }
283 
284 
286 {
287  if (!isAbsolute(*this))
288  {
289  fileName& f = *this;
290  f = cwd()/f;
291  f.clean();
292  }
293 
294  return *this;
295 }
296 
297 
298 bool Foam::fileName::clean(std::string& str)
299 {
300  // Start with the top slash found - we are never allowed to go above it
301  char prev = '/';
302  auto top = str.find(prev);
303 
304  // No slashes - nothing to do
305  if (top == std::string::npos)
306  {
307  return false;
308  }
309 
310  // Number of output characters
311  auto nChar = top+1;
312 
313  const auto maxLen = str.length();
314 
315  for (auto src = nChar; src < maxLen; /*nil*/)
316  {
317  const char c = str[src++];
318 
319  if (prev == '/')
320  {
321  // Repeated '/' - skip it
322  if (c == '/')
323  {
324  continue;
325  }
326 
327  // Could be "/./", "/../" or a trailing "/."
328  if (c == '.')
329  {
330  // Trailing "/." - skip it
331  if (src >= maxLen)
332  {
333  break;
334  }
335 
336  // Peek at the next character
337  const char c1 = str[src];
338 
339  // Found "/./" - skip it
340  if (c1 == '/')
341  {
342  ++src;
343  continue;
344  }
345 
346  // Trailing "/.." or intermediate "/../"
347  if (c1 == '.' && (src+1 >= maxLen || str[src+1] == '/'))
348  {
349  string::size_type parent;
350 
351  // Backtrack to find the parent directory
352  // Minimum of 3 characters: '/x/../'
353  // Strip it, provided it is above the top point
354  if
355  (
356  nChar > 2
357  && (parent = str.rfind('/', nChar-2)) != string::npos
358  && parent >= top
359  )
360  {
361  nChar = parent + 1; // Retain '/' from the parent
362  src += 2;
363  continue;
364  }
365 
366  // Bad resolution, eg 'abc/../../'
367  // Retain the sequence, but move the top to avoid it being
368  // considered a valid parent later
369  top = nChar + 2;
370  }
371  }
372  }
373  str[nChar++] = prev = c;
374  }
375 
376  // Remove trailing slash
377  if (nChar > 1 && str[nChar-1] == '/')
378  {
379  nChar--;
380  }
381 
382  str.resize(nChar);
383 
384  return (nChar != maxLen);
385 }
386 
387 
389 {
390  return fileName::clean(*this);
391 }
392 
393 
395 {
396  fileName cleaned(*this);
397  fileName::clean(cleaned);
398  return cleaned;
399 }
400 
401 
402 std::string Foam::fileName::nameLessExt(const std::string& str)
403 {
404  auto beg = str.rfind('/');
405  auto dot = str.rfind('.');
406 
407  if (beg == npos)
408  {
409  beg = 0;
410  }
411  else
412  {
413  ++beg;
414  }
415 
416  if (dot != npos && dot <= beg)
417  {
418  dot = npos;
419  }
420 
421  if (dot == npos)
422  {
423  return str.substr(beg);
424  }
425 
426  return str.substr(beg, dot - beg);
427 }
428 
429 
431 (
432  const fileName& parent,
433  const bool caseTag
434 ) const
435 {
436  const auto top = parent.size();
437  const fileName& f = *this;
438 
439  // Everything after "parent/xxx/yyy" -> "xxx/yyy"
440  //
441  // case-relative:
442  // "parent/xxx/yyy" -> "<case>/xxx/yyy"
443  if
444  (
445  top && (f.size() > (top+1)) && f[top] == '/'
446  && f.starts_with(parent)
447  )
448  {
449  if (caseTag)
450  {
451  return "<case>"/f.substr(top+1);
452  }
453  else
454  {
455  return f.substr(top+1);
456  }
457  }
458  else if (caseTag && f.size() && !f.isAbsolute())
459  {
460  return "<case>"/f;
461  }
462 
463  return f;
464 }
465 
466 
467 bool Foam::fileName::hasExt(const wordRe& ending) const
468 {
469  return string::hasExt(ending);
470 }
471 
472 
474 {
475  const auto parsed = stringOps::split<string>(*this, delim);
476 
477  wordList words(parsed.size());
478 
479  label i = 0;
480  for (const auto& sub : parsed)
481  {
482  // Could easily filter out '.' here too
483  words[i] = sub.str();
484  ++i;
485  }
486 
487  // As a plain wordList
488  return words;
489 }
490 
491 
493 (
494  const size_type cmpt,
495  const char delim
496 ) const
497 {
498  const auto parsed = stringOps::split<string>(*this, delim);
499 
500  if (cmpt < parsed.size())
501  {
502  return parsed[cmpt].str();
503  }
504 
505  return word();
506 }
507 
508 
509 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
510 
512 {
513  fileName& s = *this;
514 
515  if (s.size())
516  {
517  if (other.size())
518  {
519  // Two non-empty strings: can concatenate
520 
521  if (s.back() != '/' && other.front() != '/')
522  {
523  s += '/';
524  }
525 
526  s += other;
527  }
528  }
529  else if (other.size())
530  {
531  // The first string is empty
532  s = other;
533  }
534 
535  return *this;
536 }
537 
538 
539 // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
540 
541 Foam::fileName Foam::operator/(const string& s1, const string& s2)
542 {
543  if (s1.length())
544  {
545  if (s2.length())
546  {
547  // Two non-empty strings: can concatenate
548 
549  if (s1.back() == '/' || s2.front() == '/')
550  {
551  return fileName(s1 + s2);
552  }
553  else
554  {
555  return fileName(s1 + '/' + s2);
556  }
557  }
558 
559  // The second string was empty
560  return s1;
561  }
562 
563  if (s2.length())
564  {
565  // The first string is empty
566  return s2;
567  }
568 
569  // Both strings are empty
570  return fileName();
571 }
572 
573 
574 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
575 
576 Foam::fileName Foam::search(const word& file, const fileName& directory)
577 {
578  // Search current directory for the file
579  for
580  (
581  const fileName& item
582  : fileHandler().readDir(directory, fileName::FILE)
583  )
584  {
585  if (item == file)
586  {
587  return directory/item;
588  }
589  }
590 
591  // If not found search each of the sub-directories
592  for
593  (
594  const fileName& item
595  : fileHandler().readDir(directory, fileName::DIRECTORY)
596  )
597  {
598  fileName path = search(file, directory/item);
599  if (!path.empty())
600  {
601  return path;
602  }
603  }
604 
605  return fileName();
606 }
607 
608 
609 // ************************************************************************* //
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::string::hasExt
bool hasExt() const
Return true if it has an extension or simply ends with a '.'.
Definition: stringI.H:56
Foam::fileName::FILE
A file.
Definition: fileName.H:79
Foam::fileName::components
wordList components(const char delim='/') const
Return path components as wordList.
Definition: fileName.C:473
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::fileName::validate
static fileName validate(const std::string &s, const bool doClean=true)
Definition: fileName.C:58
s
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Definition: gmvOutputSpray.H:25
Foam::debug::debugSwitch
int debugSwitch(const char *name, const int deflt=0)
Lookup debug switch or add default value.
Definition: debug.C:225
Foam::dot
void dot(FieldField< Field1, typename innerProduct< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
Definition: FieldFieldFunctions.C:944
Foam::fileName::debug
static int debug
Debugging.
Definition: fileName.H:91
Foam::fileName::Type
Type
Enumerations to handle directory entry types.
Definition: fileName.H:76
Foam::fileName::nameLessExt
word nameLessExt() const
Return basename, without extension.
Definition: fileNameI.H:234
Foam::fileName::allowSpaceInFileName
static int allowSpaceInFileName
Allow space character in fileName. To be used with caution.
Definition: fileName.H:94
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1354
Foam::fileName::toAbsolute
fileName & toAbsolute()
Convert from relative to absolute.
Definition: fileName.C:285
Foam::fileName::relative
fileName relative(const fileName &parent, const bool caseTag=false) const
Definition: fileName.C:431
Foam::fileName::isBackup
bool isBackup() const
Return true if file name ends with "~", ".bak", ".old", ".save".
Definition: fileNameI.H:162
Foam::fileName::operator/=
fileName & operator/=(const string &other)
Append a path element with '/' separator.
Definition: fileName.C:511
Foam::wordRe
A wordRe is a Foam::word, but can contain a regular expression for matching words or strings.
Definition: wordRe.H:72
wordList.H
Foam::debug::infoSwitch
int infoSwitch(const char *name, const int deflt=0)
Lookup info switch or add default value.
Definition: debug.C:231
Foam::constant::physicoChemical::c1
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
Foam::fileName::typeName
static const char *const typeName
The typeName.
Definition: fileName.H:88
Foam::fileName::hasExt
bool hasExt() const
Return true if it has an extension or simply ends with a '.'.
Definition: fileNameI.H:174
wordRe.H
Foam::fileName::type
Type type(bool followLink=true, bool checkGzip=false) const
Definition: fileName.C:268
fileName.H
Foam::fileName::component
word component(const size_type cmpt, const char delim='/') const
Return a single component of the path.
Definition: fileName.C:493
size_type
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:76
Foam::fileName::equals
static bool equals(const std::string &s1, const std::string &s2)
Definition: fileName.C:153
fileOperation.H
Foam::fileName::fileName
fileName()=default
Construct null.
Foam::operator/
dimensionedScalar operator/(const scalar s1, const dimensionedScalar &ds2)
Definition: dimensionedScalar.C:68
Foam::fileName::null
static const fileName null
An empty fileName.
Definition: fileName.H:97
f
labelList f(nPoints)
Foam::List< word >
Foam::UList
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:103
Foam::fileName::DIRECTORY
A directory.
Definition: fileName.H:80
path
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
Foam::search
fileName search(const word &file, const fileName &directory)
Recursively search the given directory for the file.
Definition: fileName.C:576
Foam::fileName::concat
static fileName concat(const std::string &s1, const std::string &s2, const char delim='/')
Join two strings with a path separator ('/' by default).
Definition: fileName.C:126
Foam::roots::type
type
Types of root.
Definition: Roots.H:54
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
Foam::cwd
fileName cwd()
The physical or logical current working directory path name.
Definition: MSwindows.C:468
DynamicList.H
Foam::fileName::clean
bool clean()
Cleanup filename inplace.
Definition: fileName.C:388
stringOps.H
Foam::readDir
fileNameList readDir(const fileName &directory, const fileName::Type type=fileName::FILE, const bool filtergz=true, const bool followLink=true)
Read a directory and return the entries as a fileName List.
Definition: MSwindows.C:707