2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
53 #include <sys/types.h>
58 #if GMX_NATIVE_WINDOWS
59 # include <direct.h> // For _chdir() and _getcwd()
64 #include "gromacs/utility/cstringutil.h"
65 #include "gromacs/utility/datafilefinder.h"
66 #include "gromacs/utility/dir_separator.h"
67 #include "gromacs/utility/exceptions.h"
68 #include "gromacs/utility/fatalerror.h"
69 #include "gromacs/utility/mutex.h"
70 #include "gromacs/utility/path.h"
71 #include "gromacs/utility/programcontext.h"
72 #include "gromacs/utility/smalloc.h"
73 #include "gromacs/utility/stringutil.h"
75 /* we keep a linked list of all files opened through pipes (i.e.
76 compressed or .gzipped files. This way we can distinguish between them
77 without having to change the semantics of reading from/writing to files)
79 typedef struct t_pstack
82 struct t_pstack* prev;
85 static t_pstack* pstack = nullptr;
86 static bool bUnbuffered = false;
87 static int s_maxBackupCount = 0;
89 /* this linked list is an intrinsically globally shared object, so we have
90 to protect it with mutexes */
91 static gmx::Mutex pstack_mutex;
93 using Lock = gmx::lock_guard<gmx::Mutex>;
99 //! Global library file finder; stores the object set with setLibraryFileFinder().
100 const DataFileFinder* g_libFileFinder;
101 //! Default library file finder if nothing is set.
102 const DataFileFinder g_defaultLibFileFinder;
105 const DataFileFinder& getLibraryFileFinder()
107 if (g_libFileFinder != nullptr)
109 return *g_libFileFinder;
111 return g_defaultLibFileFinder;
114 void setLibraryFileFinder(const DataFileFinder* finder)
116 g_libFileFinder = finder;
121 void gmx_disable_file_buffering()
126 void gmx_set_max_backup_count(int count)
130 const char* env = getenv("GMX_MAXBACKUP");
133 // TODO: Check that the value is converted properly.
134 count = strtol(env, nullptr, 10);
142 // Use a reasonably low value for countmax; we might
143 // generate 4-5 files in each round, and we don't
144 // want to hit directory limits of 1024 or 2048 files.
148 s_maxBackupCount = count;
151 static void push_ps(FILE* fp)
155 Lock pstackLock(pstack_mutex);
168 #if (!HAVE_PIPES && !defined(__native_client__))
169 static FILE* popen(const char* /* nm */, const char* /* mode */)
171 gmx_impl("Sorry no pipes...");
176 static int pclose(FILE* /* fp */)
178 gmx_impl("Sorry no pipes...");
182 #endif /* !HAVE_PIPES && !defined(__native_client__) */
184 int gmx_ffclose(FILE* fp)
189 Lock pstackLock(pstack_mutex);
199 else if (ps->fp == fp)
205 pstack = pstack->prev;
210 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
214 if ((ps->prev != nullptr) && ps->prev->fp == fp)
216 if (ps->prev->fp != nullptr)
218 ret = pclose(ps->prev->fp);
221 ps->prev = ps->prev->prev;
237 void frewind(FILE* fp)
239 Lock pstackLock(pstack_mutex);
241 t_pstack* ps = pstack;
242 while (ps != nullptr)
246 fprintf(stderr, "Cannot rewind compressed file!\n");
254 int gmx_fseek(FILE* stream, gmx_off_t offset, int whence)
257 return fseeko(stream, offset, whence);
260 return _fseeki64(stream, offset, whence);
262 return fseek(stream, offset, whence);
267 gmx_off_t gmx_ftell(FILE* stream)
270 return ftello(stream);
274 return _ftelli64(stream);
276 return ftello64(stream);
279 return ftell(stream);
284 int gmx_truncate(const std::string& filename, gmx_off_t length)
286 #if GMX_NATIVE_WINDOWS && !GMX_FAHCORE
287 FILE* fp = fopen(filename.c_str(), "rb+");
293 int rc = _chsize_s(fileno(fp), length);
295 int rc = _chsize(fileno(fp), length);
300 return truncate(filename.c_str(), length);
304 static FILE* uncompress(const std::string& fn, const char* mode)
307 std::string buf = "uncompress -c < " + fn;
308 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
309 if ((fp = popen(buf.c_str(), mode)) == nullptr)
318 static FILE* gunzip(const std::string& fn, const char* mode)
321 std::string buf = "gunzip -c < ";
323 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
324 if ((fp = popen(buf.c_str(), mode)) == nullptr)
333 gmx_bool gmx_fexist(const std::string& fname)
341 test = fopen(fname.c_str(), "r");
344 /*Windows doesn't allow fopen of directory - so we need to check this seperately */
345 #if GMX_NATIVE_WINDOWS
346 DWORD attr = GetFileAttributes(fname.c_str());
347 return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
359 static std::string backup_fn(const std::string& file)
363 std::string directory = gmx::Path::getParentPath(file);
364 std::string fn = gmx::Path::getFilename(file);
366 if (directory.empty())
372 buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
374 } while ((count <= s_maxBackupCount) && gmx_fexist(buf));
376 /* Arbitrarily bail out */
377 if (count > s_maxBackupCount)
379 /* TODO: The error message is only accurate for code that starts with
380 * Gromacs command-line interface. */
382 "Won't make more than %d backups of %s for you.\n"
383 "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
384 s_maxBackupCount, fn.c_str());
390 void make_backup(const std::string& name)
392 if (s_maxBackupCount <= 0)
396 if (gmx_fexist(name))
398 auto backup = backup_fn(name);
399 if (rename(name.c_str(), backup.c_str()) == 0)
401 fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
405 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
410 FILE* gmx_ffopen(const std::string& file, const char* mode)
426 bRead = (mode[0] == 'r' && mode[1] != '+');
427 if (!bRead || gmx_fexist(file))
429 if ((ff = fopen(file.c_str(), mode)) == nullptr)
433 /* Check whether we should be using buffering (default) or not
436 const char* bufsize = nullptr;
437 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
439 /* Check whether to use completely unbuffered */
446 bs = strtol(bufsize, nullptr, 10);
456 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
458 gmx_file("Buffering File");
465 std::string compressedFileName = file;
466 compressedFileName += ".Z";
467 if (gmx_fexist(compressedFileName))
469 ff = uncompress(compressedFileName, mode);
473 compressedFileName = file;
474 compressedFileName += ".gz";
475 if (gmx_fexist(compressedFileName))
477 ff = gunzip(compressedFileName, mode);
491 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
496 const DataFileFinder& finder = getLibraryFileFinder();
497 result = finder.findFile(
498 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
500 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
504 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
506 return findLibraryFile(std::string(filename), bAddCWD, bFatal);
509 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
514 const DataFileFinder& finder = getLibraryFileFinder();
515 fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
517 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
521 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
523 return openLibraryFile(std::string(filename), bAddCWD, bFatal);
528 /*! \brief Use mkstemp (or similar function to make a new temporary
529 * file and (on non-Windows systems) return a file descriptor to it.
531 * \todo Use std::string and std::vector<char>. */
532 static int makeTemporaryFilename(char* buf)
536 if ((len = strlen(buf)) < 7)
538 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
540 for (int i = len - 6; (i < len); i++)
544 /* mktemp is dangerous and we should use mkstemp instead, but
545 * since windows doesnt support it we have to separate the cases.
546 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
549 #if GMX_NATIVE_WINDOWS
553 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
561 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
566 // TODO use std::string
567 void gmx_tmpnam(char* buf)
569 int fd = makeTemporaryFilename(buf);
570 #if !GMX_NATIVE_WINDOWS
575 // TODO use std::string
576 FILE* gmx_fopen_temporary(char* buf)
578 FILE* fpout = nullptr;
579 int fd = makeTemporaryFilename(buf);
581 #if GMX_NATIVE_WINDOWS
582 if ((fpout = fopen(buf, "w")) == NULL)
584 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
587 if ((fpout = fdopen(fd, "w")) == nullptr)
589 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
596 int gmx_file_rename(const char* oldname, const char* newname)
598 #if !GMX_NATIVE_WINDOWS
599 /* under unix, rename() is atomic (at least, it should be). */
600 return rename(oldname, newname);
602 if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
605 /* This just lets the F@H checksumming system know about the rename */
606 fcRename(oldname, newname);
617 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
619 gmx::FilePtr in(fopen(oldname, "rb"));
625 /* If we don't copy when empty, we postpone opening the file
626 until we're actually ready to write. */
630 out.reset(fopen(newname, "wb"));
637 /* the full copy buffer size: */
638 constexpr int FILECOPY_BUFSIZE = 1 << 16;
639 std::vector<char> buf(FILECOPY_BUFSIZE);
641 while (!feof(in.get()))
645 nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
651 /* so this is where we open when copy_if_empty is false:
652 here we know we read something. */
653 out.reset(fopen(newname, "wb"));
659 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
665 if (ferror(in.get()))
674 int gmx_fsync(FILE* fp)
681 /* get the file number */
687 GMX_UNUSED_VALUE(fp);
691 /* do the actual fsync */
702 /* We check for these error codes this way because POSIX requires them
703 to be defined, and using anything other than macros is unlikely: */
705 /* we don't want to report an error just because fsync() caught a signal.
706 For our purposes, we can just ignore this. */
707 if (rc && errno == EINTR)
713 /* we don't want to report an error just because we tried to fsync()
714 stdout, a socket or a pipe. */
715 if (rc && errno == EINVAL)
723 void gmx_chdir(const char* directory)
725 #if GMX_NATIVE_WINDOWS
726 int rc = _chdir(directory);
728 int rc = chdir(directory);
732 auto message = gmx::formatString("Cannot change directory to '%s'. Reason: %s", directory,
734 GMX_THROW(gmx::FileIOError(message));
738 void gmx_getcwd(char* buffer, size_t size)
740 #if GMX_NATIVE_WINDOWS
741 char* pdum = _getcwd(buffer, size);
743 char* pdum = getcwd(buffer, size);
747 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));