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-2023 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 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
56 
57 namespace
58 {
59 
60 // doClean:
61 // - remove duplicate slashes, "/./" and "/../" components.
62 //
63 // checkValid:
64 // - similar to stripInvalid (but silent)
65 //
66 // return True if the content changed
67 static bool cleanFileName
68 (
69  std::string& str,
70  const bool doClean,
71  const bool checkValid
72 )
73 {
74  const auto maxLen = str.length();
75  std::string::size_type nChar = 0;
76 
77  // Preserve UNC \\server\path (windows)
78  // - MS-windows only, but handle for other systems
79  // since there is no collision with this pattern
80  if (maxLen > 2 && str[0] == '\\' && str[1] == '\\')
81  {
82  nChar += 2;
83  }
84 
85  char prev = 0;
86  auto top = std::string::npos; // Not yet found
87  bool changed = false;
88 
89  for (auto src = nChar; src < maxLen; /*nil*/)
90  {
91  char c = str[src++];
92 
93  // Treat raw backslash like a path separator.
94  // There is no "normal" way for these to be there
95  // (except for an OS that uses them), but can cause issues
96  // when writing strings, shell commands etc.
97  if (c == '\\')
98  {
99  c = '/';
100  str[nChar] = c;
101  changed = true;
102  }
103  else if (checkValid && !Foam::fileName::valid(c))
104  {
105  // Ignore invalid chars
106  // Could explicitly allow space character or rely on
107  // allowSpaceInFileName via fileName::valid()
108  continue;
109  }
110 
111  if (c == '/' && top == std::string::npos)
112  {
113  // Top-level slash not previously determined
114  top = (src-1);
115  }
116 
117  if (doClean && prev == '/')
118  {
119  // Repeated '/' - skip it
120  if (c == '/')
121  {
122  continue;
123  }
124 
125  // Could be "/./", "/../" or a trailing "/."
126  if (c == '.')
127  {
128  // Trailing "/." - skip it
129  if (src >= maxLen)
130  {
131  break;
132  }
133 
134  // Peek at the next character
135  const char c1 = str[src];
136 
137  // Found "/./" - skip over it
138  if (c1 == '/' || c1 == '\\')
139  {
140  ++src;
141  continue;
142  }
143 
144  // Trailing "/.." or intermediate "/../"
145  if
146  (
147  c1 == '.'
148  &&
149  (
150  src+1 >= maxLen
151  || str[src+1] == '/' || str[src+1] == '\\'
152  )
153  )
154  {
155  // Backtrack to find the parent directory
156  // Minimum of 3 characters: '/x/../'
157  // Strip it, provided it is above the top point
158 
159  std::string::size_type parent;
160  if
161  (
162  nChar > 2
163  && top != std::string::npos
164  && (parent = str.rfind('/', nChar-2)) != std::string::npos
165  && parent >= top
166  )
167  {
168  nChar = parent + 1; // Retain '/' from the parent
169  src += 2;
170  continue;
171  }
172 
173  // Bad resolution, eg 'abc/../../'
174  // Retain the sequence, but move the top to avoid it being
175  // considered a valid parent later
176  top = nChar + 2;
177  }
178  }
179  }
180 
181  str[nChar++] = prev = c;
182  }
183 
184  // Remove trailing '/'
185  if (doClean && nChar > 1 && str[nChar-1] == '/')
186  {
187  --nChar;
188  }
189 
190  str.erase(nChar);
191  return changed || (nChar != maxLen);
192 }
193 
194 } // End namespace Foam
195 
196 
197 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
198 
199 bool Foam::fileName::clean(std::string& str)
200 {
201  return cleanFileName(str, true, false); // clean, checkValid = false
202 }
203 
204 
206 (
207  const std::string& str,
208  const bool doClean
209 )
210 {
211  fileName out(str, false); // copy, no stripping
212  cleanFileName(out, doClean, true); // checkValid = true
213  return out;
214 }
215 
216 
218 (
219  const std::string& s1,
220  const std::string& s2,
221  const char delim
222 )
223 {
224  const auto n1 = s1.length();
225  const auto n2 = s2.length();
226 
227  fileName out;
228  out.reserve(n1 + n2 + 1);
229 
230  out += s1;
231 
232  if (n1 && n2 && s1.back() != delim && s2.front() != delim)
233  {
234  // Add delimiter
235  out += delim;
236  }
237 
238  out += s2;
239 
240  // Could also remove trailing '/', if desired.
241  return out;
242 }
243 
244 
245 bool Foam::fileName::equals(const std::string& s1, const std::string& s2)
246 {
247  // Do not use (s1 == s2) or s1.compare(s2) first since this would
248  // potentially be doing the comparison twice.
249 
250  std::string::size_type i1 = 0;
251  std::string::size_type i2 = 0;
252 
253  const auto n1 = s1.length();
254  const auto n2 = s2.length();
255 
256  //Info<< "compare " << s1 << " == " << s2 << endl;
257  while (i1 < n1 && i2 < n2)
258  {
259  //Info<< "check '" << s1[i1] << "' vs '" << s2[i2] << "'" << endl;
260 
261  if (s1[i1] != s2[i2])
262  {
263  return false;
264  }
265 
266  // Increment to next positions and also skip repeated slashes
267  do
268  {
269  ++i1;
270  }
271  while (s1[i1] == '/');
272 
273  do
274  {
275  ++i2;
276  }
277  while (s2[i2] == '/');
278  }
279  //Info<< "return: " << Switch(i1 == n1 && i2 == n2) << endl;
280 
281  // Equal if it made it all the way through both strings
282  return (i1 == n1 && i2 == n2);
283 }
284 
285 
286 bool Foam::fileName::isBackup(const std::string& s)
287 {
288  if (s.empty())
289  {
290  return false;
291  }
292  else if (s.back() == '~')
293  {
294  return true;
295  }
296 
297  // Now check the extension
298  auto dot = find_ext(s);
299 
300  if (dot == npos)
301  {
302  return false;
303  }
304 
305  ++dot;
306 
307  return
308  (
309  !s.compare(dot, npos, "bak") || !s.compare(dot, npos, "BAK")
310  || !s.compare(dot, npos, "old") || !s.compare(dot, npos, "save")
311  );
312 }
313 
314 
315 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
316 
318 {
319  size_type len = 0;
320  for (const word& item : list)
321  {
322  len += 1 + item.length(); // Include space for '/' needed
323  }
324  reserve(len);
325 
326  for (const word& item : list)
327  {
328  if (item.length())
329  {
330  if (length()) operator+=('/');
331  operator+=(item);
332  }
333  }
334 }
335 
336 
337 Foam::fileName::fileName(std::initializer_list<word> list)
338 {
339  size_type len = 0;
340  for (const word& item : list)
341  {
342  len += 1 + item.length(); // Include space for '/' needed
343  }
344  reserve(len);
345 
346  for (const word& item : list)
347  {
348  if (item.length())
349  {
350  if (length()) operator+=('/');
351  operator+=(item);
352  }
353  }
354 }
355 
356 
357 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
358 
360 (
361  bool followLink,
362  bool checkGzip
363 ) const
364 {
365  Type t = ::Foam::type(*this, followLink);
366 
367  if (checkGzip && (Type::UNDEFINED == t) && size())
368  {
369  // Also check for gzip file?
370  t = ::Foam::type(*this + ".gz", followLink);
371  }
372 
373  return t;
374 }
375 
376 
378 {
379  fileName& f = *this;
380 
381  if (!f.isAbsolute())
382  {
383  f = Foam::cwd()/f;
384  }
386  f.clean(); // Remove unneeded ".."
387 
388  return *this;
389 }
390 
393 {
394  return fileName::clean(*this);
395 }
396 
397 
398 std::string Foam::fileName::stem(const std::string& str)
399 {
400  auto beg = str.rfind('/');
401  auto dot = str.rfind('.');
402 
403  if (beg == npos)
404  {
405  beg = 0;
406  }
407  else
408  {
409  ++beg;
410  }
411 
412  if (dot != npos && dot <= beg)
413  {
414  dot = npos;
415  }
416 
417  if (dot == npos)
418  {
419  return str.substr(beg);
420  }
421 
422  return str.substr(beg, dot - beg);
423 }
424 
425 
426 Foam::fileName& Foam::fileName::replace_name(const word& newName)
427 {
428  const auto len = newName.length();
429 
430  if (len)
431  {
432  auto beg = rfind('/');
433 
434  if (beg == npos)
435  {
436  fileName::assign(newName);
437  }
438  else
439  {
440  ++beg;
441  replace(beg, length()-beg, newName);
442  }
443  }
444 
445  return *this;
446 }
447 
448 
450 (
451  const fileName& parent,
452  const bool caseTag
453 ) const
454 {
455  const auto top = parent.length();
456  const fileName& f = *this;
457 
458  // Everything after "parent/xxx/yyy" -> "xxx/yyy"
459  //
460  // case-relative:
461  // "parent/xxx/yyy" -> "<case>/xxx/yyy"
462  // "parent/constant/xxx/yyy" -> "<constant>/xxx/yyy"
463  // "parent/system/xxx/yyy" -> "<system>/xxx/yyy"
464  //
465  // as per stringOps::inplaceExpand()
466 
467  if
468  (
469  top && (f.length() > (top+1)) && f[top] == '/'
470  && f.starts_with(parent)
471  )
472  {
473  if (caseTag)
474  {
475  const auto trailing = f.find('/', top+1);
476 
477  if (npos != trailing)
478  {
479  switch (trailing-top-1)
480  {
481  case 6: // "system"
482  {
483  if (!compare((top+1), 6, "system"))
484  {
485  return "<system>"/f.substr(trailing+1);
486  }
487  break;
488  }
489  case 8: // "constant"
490  {
491  if (!compare((top+1), 8, "constant"))
492  {
493  return "<constant>"/f.substr(trailing+1);
494  }
495  break;
496  }
497  }
498  }
499  return "<case>"/f.substr(top+1);
500  }
501  else
502  {
503  return f.substr(top+1);
504  }
505  }
506  else if (caseTag && f.length() && !f.isAbsolute())
507  {
508  const auto trailing = f.find('/');
509 
510  if (npos != trailing)
511  {
512  switch (trailing)
513  {
514  case 6: // "system"
515  {
516  if (!compare(0, 6, "system"))
517  {
518  return "<system>"/f.substr(trailing+1);
519  }
520  break;
521  }
522  case 8: // "constant"
523  {
524  if (!compare(0, 8, "constant"))
525  {
526  return "<constant>"/f.substr(trailing+1);
527  }
528  break;
529  }
530  }
531  }
532  return "<case>"/f;
533  }
534 
535  return f;
536 }
537 
538 
539 Foam::wordList Foam::fileName::components(const char delim) const
540 {
541  const auto parsed = stringOps::split<string>(*this, delim);
542 
543  wordList words(parsed.size());
544 
545  label i = 0;
546  for (const auto& sub : parsed)
547  {
548  // Could easily filter out '.' here too
549  words[i] = sub.str();
550  ++i;
551  }
553  // As a plain wordList
554  return words;
555 }
556 
557 
559 (
560  const size_type cmpt,
561  const char delim
562 ) const
563 {
564  const auto parsed = stringOps::split<string>(*this, delim);
565 
566  if (parsed.size())
567  {
568  if (cmpt == std::string::npos)
569  {
570  return parsed.back().str();
571  }
572  else if (cmpt < parsed.size())
573  {
574  return parsed[cmpt].str();
575  }
576  }
578  return word();
579 }
580 
581 
582 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
583 
584 Foam::fileName& Foam::fileName::operator/=(const string& other)
585 {
586  fileName& s = *this;
587 
588  if (s.size())
589  {
590  if (other.size())
591  {
592  // Two non-empty strings: can concatenate
593 
594  if (s.back() != '/' && other.front() != '/')
595  {
596  s += '/';
597  }
598 
599  s += other;
600  }
601  }
602  else if (other.size())
603  {
604  // The first string is empty
605  s = other;
606  }
608  return *this;
609 }
610 
611 
612 // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
613 
614 Foam::fileName Foam::operator/(const string& s1, const string& s2)
615 {
616  if (s1.length())
617  {
618  if (s2.length())
619  {
620  // Two non-empty strings: can concatenate
621 
622  if (s1.back() == '/' || s2.front() == '/')
623  {
624  return fileName(s1 + s2);
625  }
626  else
627  {
628  return fileName(s1 + '/' + s2);
629  }
630  }
631 
632  // The second string was empty
633  return s1;
634  }
635 
636  if (s2.length())
637  {
638  // The first string is empty
639  return s2;
640  }
641 
642  // Both strings are empty
643  return fileName();
644 }
645 
646 
647 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
648 
649 Foam::fileName Foam::search(const word& file, const fileName& directory)
650 {
651  // Search current directory for the file
652  for
653  (
654  const fileName& item
655  : fileHandler().readDir(directory, fileName::FILE)
656  )
657  {
658  if (item == file)
659  {
660  return directory/item;
661  }
662  }
663 
664  // If not found search each of the sub-directories
665  for
666  (
667  const fileName& item
668  : fileHandler().readDir(directory, fileName::DIRECTORY)
669  )
670  {
671  fileName path = search(file, directory/item);
672  if (!path.empty())
673  {
674  return path;
675  }
676  }
677 
678  return fileName();
679 }
680 
681 
682 // ************************************************************************* //
type
Types of root.
Definition: Roots.H:52
static std::string::size_type length(const char *s)
Length of the character sequence (with nullptr protection)
Definition: string.H:258
static int debug
Debugging.
Definition: fileName.H:101
A class for handling file names.
Definition: fileName.H:72
fileName & operator/=(const string &other)
Append a path element with &#39;/&#39; separator.
Definition: fileName.C:577
bool clean()
Cleanup filename (inplace)
Definition: fileName.C:385
word stem() const
Return basename, without extension.
Definition: fileNameI.H:217
int debugSwitch(const char *name, const int deflt=0)
Lookup debug switch or add default value.
Definition: debug.C:222
static const fileName null
An empty fileName.
Definition: fileName.H:111
Type type(bool followLink=true, bool checkGzip=false) const
Return the directory entry type: UNDEFINED, FILE, DIRECTORY (or SYMLINK).
Definition: fileName.C:353
refPtr< fileOperation > fileHandler(std::nullptr_t)
Delete current file handler - forwards to fileOperation::handler()
dimensionedScalar operator/(const scalar s1, const dimensionedScalar &ds2)
static const char *const typeName
The typeName.
Definition: fileName.H:96
static bool equals(const std::string &s1, const std::string &s2)
This is a specialized (possibly slower) version of compare() that ignores duplicate or trailing slash...
Definition: fileName.C:238
int infoSwitch(const char *name, const int deflt=0)
Lookup info switch or add default value.
Definition: debug.C:228
void dot(FieldField< Field1, typename innerProduct< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
word component(const size_type cmpt, const char delim='/') const
Return a single component of the path or empty if out of range.
Definition: fileName.C:552
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
bool assign(const token &tok)
Assign from word or string token.
Definition: fileNameIO.C:35
fileName relative(const fileName &parent, const bool caseTag=false) const
Return a relative name by stripping off the parent directory where possible.
Definition: fileName.C:443
fileName()=default
Default construct.
A class for handling words, derived from Foam::string.
Definition: word.H:63
A regular file.
Definition: fileName.H:84
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:68
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:105
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:173
fileName & toAbsolute()
Convert from relative to absolute.
Definition: fileName.C:370
wordList components(const char delim='/') const
Return path components as wordList.
Definition: fileName.C:532
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
labelList f(nPoints)
static bool valid(char c)
Is this character valid for a fileName?
Definition: fileNameI.H:95
static fileName concat(const std::string &s1, const std::string &s2, const char delim='/')
Join two strings with a path separator (&#39;/&#39; by default).
Definition: fileName.C:211
fileName & replace_name(const word &newName)
Replace basename (part beyond last /) with a new name.
Definition: fileName.C:419
List< word > wordList
List of word.
Definition: fileName.H:59
static int allowSpaceInFileName
Allow space character in fileName. To be used with caution.
Definition: fileName.H:106
bool isBackup() const
Return true if file name ends with "~", ".bak", ".old", ".save".
Definition: fileNameI.H:157
static fileName validate(const std::string &, const bool doClean=true)
Construct fileName without invalid characters, possibly applying other transformations such as changi...
Definition: fileName.C:199
const dimensionedScalar c
Speed of light in a vacuum.
triangles reserve(surf.size())
fileName cwd()
The physical or logical current working directory path name.
Definition: POSIX.C:590
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
fileName search(const word &file, const fileName &directory)
Recursively search the given directory for the file.
Definition: fileName.C:642
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))
fileNameList readDir(const fileName &directory, const fileName::Type type=fileName::Type::FILE, const bool filtergz=true, const bool followLink=true)
Read a directory and return the entries as a fileName List.
Definition: POSIX.C:963
Type
Enumerations to handle directory entry types.
Definition: fileName.H:81