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-2021 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  if (!isAbsolute(*this))
380  {
381  fileName& f = *this;
382  f = cwd()/f;
383  f.clean(); // Remove unneeded ".."
384  }
385 
386  return *this;
387 }
388 
389 
391 {
392  return fileName::clean(*this);
393 }
394 
395 
396 std::string Foam::fileName::nameLessExt(const std::string& str)
397 {
398  auto beg = str.rfind('/');
399  auto dot = str.rfind('.');
400 
401  if (beg == npos)
402  {
403  beg = 0;
404  }
405  else
406  {
407  ++beg;
408  }
409 
410  if (dot != npos && dot <= beg)
411  {
412  dot = npos;
413  }
414 
415  if (dot == npos)
416  {
417  return str.substr(beg);
418  }
419 
420  return str.substr(beg, dot - beg);
421 }
422 
423 
425 (
426  const fileName& parent,
427  const bool caseTag
428 ) const
429 {
430  const auto top = parent.size();
431  const fileName& f = *this;
432 
433  // Everything after "parent/xxx/yyy" -> "xxx/yyy"
434  //
435  // case-relative:
436  // "parent/xxx/yyy" -> "<case>/xxx/yyy"
437  if
438  (
439  top && (f.size() > (top+1)) && f[top] == '/'
440  && f.starts_with(parent)
441  )
442  {
443  if (caseTag)
444  {
445  return "<case>"/f.substr(top+1);
446  }
447  else
448  {
449  return f.substr(top+1);
450  }
451  }
452  else if (caseTag && f.size() && !f.isAbsolute())
453  {
454  return "<case>"/f;
455  }
456 
457  return f;
458 }
459 
460 
462 {
463  const auto parsed = stringOps::split<string>(*this, delim);
464 
465  wordList words(parsed.size());
466 
467  label i = 0;
468  for (const auto& sub : parsed)
469  {
470  // Could easily filter out '.' here too
471  words[i] = sub.str();
472  ++i;
473  }
474 
475  // As a plain wordList
476  return words;
477 }
478 
479 
481 (
482  const size_type cmpt,
483  const char delim
484 ) const
485 {
486  const auto parsed = stringOps::split<string>(*this, delim);
487 
488  if (parsed.size())
489  {
490  if (cmpt == std::string::npos)
491  {
492  return parsed.last().str();
493  }
494  else if (cmpt < parsed.size())
495  {
496  return parsed[cmpt].str();
497  }
498  }
499 
500  return word();
501 }
502 
503 
504 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
505 
507 {
508  fileName& s = *this;
509 
510  if (s.size())
511  {
512  if (other.size())
513  {
514  // Two non-empty strings: can concatenate
515 
516  if (s.back() != '/' && other.front() != '/')
517  {
518  s += '/';
519  }
520 
521  s += other;
522  }
523  }
524  else if (other.size())
525  {
526  // The first string is empty
527  s = other;
528  }
529 
530  return *this;
531 }
532 
533 
534 // * * * * * * * * * * * * * * * Global Operators * * * * * * * * * * * * * //
535 
536 Foam::fileName Foam::operator/(const string& s1, const string& s2)
537 {
538  if (s1.length())
539  {
540  if (s2.length())
541  {
542  // Two non-empty strings: can concatenate
543 
544  if (s1.back() == '/' || s2.front() == '/')
545  {
546  return fileName(s1 + s2);
547  }
548  else
549  {
550  return fileName(s1 + '/' + s2);
551  }
552  }
553 
554  // The second string was empty
555  return s1;
556  }
557 
558  if (s2.length())
559  {
560  // The first string is empty
561  return s2;
562  }
563 
564  // Both strings are empty
565  return fileName();
566 }
567 
568 
569 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
570 
571 Foam::fileName Foam::search(const word& file, const fileName& directory)
572 {
573  // Search current directory for the file
574  for
575  (
576  const fileName& item
577  : fileHandler().readDir(directory, fileName::FILE)
578  )
579  {
580  if (item == file)
581  {
582  return directory/item;
583  }
584  }
585 
586  // If not found search each of the sub-directories
587  for
588  (
589  const fileName& item
590  : fileHandler().readDir(directory, fileName::DIRECTORY)
591  )
592  {
593  fileName path = search(file, directory/item);
594  if (!path.empty())
595  {
596  return path;
597  }
598  }
599 
600  return fileName();
601 }
602 
603 
604 // ************************************************************************* //
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::fileName::FILE
A file.
Definition: fileName.H:83
Foam::fileName::components
wordList components(const char delim='/') const
Return path components as wordList.
Definition: fileName.C:461
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
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:95
Foam::fileName::Type
Type
Enumerations to handle directory entry types.
Definition: fileName.H:80
Foam::fileName::nameLessExt
word nameLessExt() const
Return basename, without extension.
Definition: fileNameI.H:224
Foam::fileName::allowSpaceInFileName
static int allowSpaceInFileName
Allow space character in fileName. To be used with caution.
Definition: fileName.H:98
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1485
Foam::fileName::valid
static bool valid(char c)
Is this character valid for a fileName?
Definition: fileNameI.H:102
Foam::fileName::toAbsolute
fileName & toAbsolute()
Convert from relative to absolute.
Definition: fileName.C:377
Foam::fileName::relative
fileName relative(const fileName &parent, const bool caseTag=false) const
Definition: fileName.C:425
Foam::fileName::isBackup
bool isBackup() const
Return true if file name ends with "~", ".bak", ".old", ".save".
Definition: fileNameI.H:164
Foam::fileName::operator/=
fileName & operator/=(const string &other)
Append a path element with '/' separator.
Definition: fileName.C:506
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:92
wordRe.H
Foam::fileName::type
Type type(bool followLink=true, bool checkGzip=false) const
Definition: fileName.C:360
fileName.H
Foam::fileName::component
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:481
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:245
fileOperation.H
Foam::fileName::fileName
fileName()=default
Default construct.
Foam::fileName::validate
static fileName validate(const std::string &, const bool doClean=true)
Definition: fileName.C:206
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:101
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:84
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:571
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:218
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:390
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