fstreamPointers.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 OpenFOAM Foundation
9  Copyright (C) 2018-2024 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 "fstreamPointer.H"
30 #include "OCountStream.H"
31 #include "OSspecific.H"
32 #include <cstdio>
33 
34 // HAVE_LIBZ defined externally
35 // #define HAVE_LIBZ
36 
37 #ifdef HAVE_LIBZ
38 #include "gzstream.h"
39 #endif /* HAVE_LIBZ */
40 
41 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
42 
44 {
45  #ifdef HAVE_LIBZ
46  return true;
47  #else
48  return false;
49  #endif
50 }
51 
52 
54 {
55  #ifdef HAVE_LIBZ
56  return true;
57  #else
58  return false;
59  #endif
60 }
61 
62 
63 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
64 
66 (
67  const fileName& pathname,
68  IOstreamOption streamOpt // Currently unused
69 )
70 :
71  ptr_()
72 {
73  open(pathname, streamOpt);
74 }
75 
76 
78 (
79  const fileName& pathname
80 )
81 :
82  ptr_()
83 {
84  open(pathname);
85 }
86 
87 
89 :
90  ptr_(),
91  mode_(modeType::NONE)
92 {}
93 
94 
96 :
97  ptr_(new Foam::ocountstream),
98  mode_(modeType::NONE)
99 {}
100 
101 
103 (
104  const fileName& pathname,
105  IOstreamOption streamOpt,
107  bool atomic
108 )
109 :
110  ptr_(),
111  mode_(modeType::NONE)
112 {
113  // Leave std::ios_base::trunc implicitly handled to make things
114  // easier for append mode.
115 
116  std::ios_base::openmode openmode
117  (
118  std::ios_base::out | std::ios_base::binary
119  );
120 
122  {
123  openmode |= std::ios_base::app;
124 
125  // Cannot append to gzstream
127 
128  // Cannot use append + atomic operation, without lots of extra work
129  atomic = false;
130  }
132  {
133  // Handle an "append-like" mode by opening "r+b" and NOT as "ab"
134  // - file already exists: Sets read position to start
135  // - file does not exist: Error
136 
137  openmode |= std::ios_base::in; // [SIC] - use read bit, not append!
138 
139  // Cannot append to gzstream
141 
142  // Cannot use append + atomic operation, without lots of extra work
143  atomic = false;
144  }
145 
146 
147  // When opening new files, remove file variants out of the way.
148  // Eg, opening "file1"
149  // - remove old "file1.gz" (compressed)
150  // - also remove old "file1" if it is a symlink and we are not appending
151  //
152  // Not writing into symlinked files avoids problems with symlinked
153  // initial fields (eg, 0/U -> ../0.orig/U)
154 
155  const fileName pathname_gz(pathname + ".gz");
156  const fileName pathname_tmp(pathname + "~tmp~");
157 
158  fileName::Type fType = fileName::Type::UNDEFINED;
159 
160  if (IOstreamOption::COMPRESSED == streamOpt.compression())
161  {
162  // Output compression requested.
163 
164  #ifdef HAVE_LIBZ
165  // TBD:
166  // atomic = true; // Always treat COMPRESSED like an atomic
167 
168  const fileName& target = (atomic ? pathname_tmp : pathname_gz);
169 
170  // Remove old uncompressed version (if any)
171  fType = Foam::type(pathname, false);
172  if (fType == fileName::SYMLINK || fType == fileName::FILE)
173  {
174  Foam::rm(pathname);
175  }
176 
177  // Avoid writing into symlinked files (non-append mode)
178  if (atomic || (append == IOstreamOption::NO_APPEND))
179  {
180  fType = Foam::type(target, false);
181  if (fType == fileName::SYMLINK)
182  {
183  Foam::rm(target);
184  }
185  }
186 
187  ptr_.reset(new ogzstream(target, openmode));
188 
189  #else /* HAVE_LIBZ */
190 
192 
193  Warning
194  << nl
195  << "No write support for gz compressed files (libz)"
196  << " : downgraded to UNCOMPRESSED" << nl
197  << "file: " << pathname_gz << endl;
198 
199  #endif /* HAVE_LIBZ */
200  }
201 
202  if (IOstreamOption::COMPRESSED != streamOpt.compression())
203  {
204  const fileName& target = (atomic ? pathname_tmp : pathname);
205 
206  // Remove old compressed version (if any)
207  fType = Foam::type(pathname_gz, false);
208  if (fType == fileName::SYMLINK || fType == fileName::FILE)
209  {
210  Foam::rm(pathname_gz);
211  }
212 
213  // Avoid writing into symlinked files (non-append mode)
214  if (atomic || (append == IOstreamOption::NO_APPEND))
215  {
216  fType = Foam::type(target, false);
217  if (fType == fileName::SYMLINK)
218  {
219  Foam::rm(target);
220  }
221  }
222 
223  // File pointer (std::ofstream)
224  auto filePtr = std::make_unique<std::ofstream>(target, openmode);
225 
227  {
228  // Final handling for append 'app' (always non-atomic)
229 
230  // Set output position to the end (like std::ios_base::ate)
231  // but only to test if the file had a size.
232  // No real performance problem since any subsequent write
233  // will do the same anyhow.
234 
235  filePtr->seekp(0, std::ios_base::end);
236  if (filePtr->tellp() <= 0)
237  {
238  // Did not open an existing file
240  }
241  }
243  {
244  // Final handling for append 'ate' (always non-atomic)
245 
246  if (filePtr->good())
247  {
248  // Success if file already exists.
249  // Set output position to the end - like std::ios_base::ate
250 
251  filePtr->seekp(0, std::ios_base::end);
252 
253  if (filePtr->tellp() <= 0)
254  {
255  // Did not open an existing file
257  }
258  }
259  else
260  {
261  // Error if file does not already exist.
262  // Reopen as regular output mode only.
263 
264  // Did not open an existing file
266 
267  if (filePtr->is_open())
268  {
269  filePtr->close();
270  }
271  filePtr->clear();
272  filePtr->open
273  (
274  target,
275  (std::ios_base::out | std::ios_base::binary)
276  );
277  }
278  }
279  ptr_.reset(filePtr.release());
280  }
281 
282  // Is appending to an existing file
284  {
285  mode_ = modeType::APPENDING;
286  }
287 
288  // An atomic output operation (normally not appending!)
289  if (atomic)
290  {
291  mode_ |= modeType::ATOMIC;
292  }
293 }
294 
295 
297 (
298  const fileName& pathname,
301  bool atomic
302 )
303 :
304  ofstreamPointer
305  (
306  pathname,
307  IOstreamOption(IOstreamOption::ASCII, comp),
308  append,
309  atomic
310  )
311 {}
312 
313 
314 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
315 
317 (
318  const fileName& pathname,
319  IOstreamOption streamOpt // Currently unused
320 )
321 {
322  // Forcibly close old stream (if any)
323  ptr_.reset(nullptr);
324 
325  const std::ios_base::openmode openmode
326  (
327  std::ios_base::in | std::ios_base::binary
328  );
329 
330  ptr_.reset(new std::ifstream(pathname, openmode));
331 
332  if (!ptr_->good())
333  {
334  // Try compressed version instead
335 
336  const fileName pathname_gz(pathname + ".gz");
337 
338  if (Foam::isFile(pathname_gz, false))
339  {
340  #ifdef HAVE_LIBZ
341 
342  ptr_.reset(new igzstream(pathname_gz, openmode));
343 
344  #else /* HAVE_LIBZ */
345 
346  FatalError
347  << "No read support for gz compressed files (libz)"
348  << " : could use 'gunzip' from the command-line" << nl
349  << "file: " << pathname_gz << endl
350  << exit(FatalError);
351 
352  #endif /* HAVE_LIBZ */
353  }
354  else
355  {
356  // TBD:
357  // Can also fallback and open .orig files too
358  //
359  // auto* file = dynamic_cast<std::ifstream*>(ptr_.get());
360  // file->open(pathname + ".orig", mode);
361  }
362  }
363 }
364 
365 
366 void Foam::ifstreamPointer::reopen_gz(const std::string& pathname)
367 {
368  #ifdef HAVE_LIBZ
369  auto* gz = dynamic_cast<igzstream*>(ptr_.get());
370 
371  if (gz)
372  {
373  // Special treatment for gzstream
374  gz->close();
375  gz->clear();
376 
377  gz->open
378  (
379  pathname + ".gz",
380  (std::ios_base::in | std::ios_base::binary)
381  );
382  return;
383  }
384  #endif /* HAVE_LIBZ */
385 }
386 
387 
388 void Foam::ofstreamPointer::reopen(const std::string& pathname)
389 {
390  #ifdef HAVE_LIBZ
391  auto* gz = dynamic_cast<ogzstream*>(ptr_.get());
392 
393  if (gz)
394  {
395  // Special treatment for gzstream
396  gz->close();
397  gz->clear();
398 
399  if (mode_ & modeType::ATOMIC)
400  {
401  gz->open
402  (
403  pathname + "~tmp~",
404  (std::ios_base::out | std::ios_base::binary)
405  );
406  }
407  else
408  {
409  gz->open
410  (
411  pathname + ".gz",
412  (std::ios_base::out | std::ios_base::binary)
413  );
414  }
415  return;
416  }
417  #endif /* HAVE_LIBZ */
418 
419  auto* file = dynamic_cast<std::ofstream*>(ptr_.get());
420 
421  if (file)
422  {
423  if (file->is_open())
424  {
425  file->close();
426  }
427  file->clear();
428 
429  // Invalidate the appending into existing file information
430  // since rewind usually means overwrite
431  mode_ &= ~modeType::APPENDING;
432 
433  if (mode_ & modeType::ATOMIC)
434  {
435  file->open
436  (
437  pathname + "~tmp~",
438  (std::ios_base::out | std::ios_base::binary)
439  );
440  }
441  else
442  {
443  file->open
444  (
445  pathname,
446  (std::ios_base::out | std::ios_base::binary)
447  );
448  }
449  return;
450  }
451 }
452 
453 
454 void Foam::ofstreamPointer::close(const std::string& pathname)
455 {
456  // Invalidate the appending into existing file information
457  mode_ &= ~modeType::APPENDING;
458 
459  if (pathname.empty() || !(mode_ & modeType::ATOMIC))
460  {
461  return;
462  }
463 
464  #ifdef HAVE_LIBZ
465  auto* gz = dynamic_cast<ogzstream*>(ptr_.get());
466 
467  if (gz)
468  {
469  // Special treatment for gzstream
470  gz->close();
471  gz->clear();
472 
473  std::rename
474  (
475  (pathname + "~tmp~").c_str(),
476  (pathname + ".gz").c_str()
477  );
478  return;
479  }
480  #endif /* HAVE_LIBZ */
481 
482  auto* file = dynamic_cast<std::ofstream*>(ptr_.get());
483 
484  if (file)
485  {
486  if (file->is_open())
487  {
488  file->close();
489  }
490  file->clear();
491 
492  std::rename
493  (
494  (pathname + "~tmp~").c_str(),
495  pathname.c_str()
496  );
497  return;
498  }
499 }
500 
501 
504 {
505  #ifdef HAVE_LIBZ
506  if (dynamic_cast<const igzstream*>(ptr_.get()))
507  {
508  return IOstreamOption::compressionType::COMPRESSED;
509  }
510  #endif /* HAVE_LIBZ */
511 
512  return IOstreamOption::compressionType::UNCOMPRESSED;
513 }
514 
515 
518 {
519  #ifdef HAVE_LIBZ
520  if (dynamic_cast<const ogzstream*>(ptr_.get()))
521  {
522  return IOstreamOption::compressionType::COMPRESSED;
523  }
524  #endif /* HAVE_LIBZ */
525 
526  return IOstreamOption::compressionType::UNCOMPRESSED;
527 }
528 
529 
530 // ************************************************************************* //
A symbolic link.
Definition: fileName.H:86
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
compressionType
Compression treatment (UNCOMPRESSED | COMPRESSED)
static bool supports_gz() noexcept
True if compiled with libz support.
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
IOstreamOption::compressionType whichCompression() const
Which compression type?
Trivial output stream for calculating byte counts.
Definition: OCountStream.H:47
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
static bool supports_gz() noexcept
True if compiled with libz support.
ofstreamPointer() noexcept
Default construct (empty)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
append (seek end each write)
void reopen_gz(const std::string &pathname)
Special &#39;rewind&#39; method for compressed stream.
A simple container for options an IOstream can normally have.
autoPtr< OFstream > filePtr
Definition: createFields.H:37
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
A regular file.
Definition: fileName.H:84
no append (truncates existing)
void reopen(const std::string &pathname)
Reopen for compressed/non-compressed. Discards append status.
const direction noexcept
Definition: Scalar.H:258
append (seek end after open)
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:201
ifstreamPointer() noexcept=default
Default construct (empty)
compressionType compression() const noexcept
Get the stream compression.
appendType
File appending (NO_APPEND | APPEND_APP | APPEND_ATE)
void close(const std::string &pathname)
Close stream and rename file.
messageStream Warning
Warning stream (stdout output on master, null elsewhere), with additional &#39;FOAM Warning&#39; header text...
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:877
rAUs append(new volScalarField(IOobject::groupName("rAU", phase1.name()), 1.0/(U1Eqn.A()+byDt(max(phase1.residualAlpha() - alpha1, scalar(0)) *rho1))))
IOstreamOption::compressionType whichCompression() const
Which compression type?
void open(const fileName &pathname, IOstreamOption streamOpt=IOstreamOption())
Attempts to open the specified file for reading.
Type
Enumerations to handle directory entry types.
Definition: fileName.H:81
Namespace for OpenFOAM.
bool rm(const fileName &file)
Remove a file (or its gz equivalent), returning true if successful.
Definition: POSIX.C:1404