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,2021, 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.
54 #include <sys/types.h>
59 #if GMX_NATIVE_WINDOWS
60 # include <direct.h> // For _chdir() and _getcwd()
65 #include "gromacs/utility/cstringutil.h"
66 #include "gromacs/utility/datafilefinder.h"
67 #include "gromacs/utility/dir_separator.h"
68 #include "gromacs/utility/exceptions.h"
69 #include "gromacs/utility/fatalerror.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 std::mutex pstack_mutex;
93 using Lock = std::lock_guard<std::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.",
391 void make_backup(const std::string& name)
393 if (s_maxBackupCount <= 0)
397 if (gmx_fexist(name))
399 auto backup = backup_fn(name);
400 if (rename(name.c_str(), backup.c_str()) == 0)
402 fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
406 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
411 FILE* gmx_ffopen(const std::string& file, const char* mode)
427 bRead = (mode[0] == 'r' && mode[1] != '+');
428 if (!bRead || gmx_fexist(file))
430 if ((ff = fopen(file.c_str(), mode)) == nullptr)
434 /* Check whether we should be using buffering (default) or not
437 const char* bufsize = nullptr;
438 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
440 /* Check whether to use completely unbuffered */
447 bs = strtol(bufsize, nullptr, 10);
457 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
459 gmx_file("Buffering File");
466 std::string compressedFileName = file;
467 compressedFileName += ".Z";
468 if (gmx_fexist(compressedFileName))
470 ff = uncompress(compressedFileName, mode);
474 compressedFileName = file;
475 compressedFileName += ".gz";
476 if (gmx_fexist(compressedFileName))
478 ff = gunzip(compressedFileName, mode);
492 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
497 const DataFileFinder& finder = getLibraryFileFinder();
498 result = finder.findFile(
499 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
501 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
505 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
507 return findLibraryFile(std::string(filename), bAddCWD, bFatal);
510 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
515 const DataFileFinder& finder = getLibraryFileFinder();
516 fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
518 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
522 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
524 return openLibraryFile(std::string(filename), bAddCWD, bFatal);
529 /*! \brief Use mkstemp (or similar function to make a new temporary
530 * file and (on non-Windows systems) return a file descriptor to it.
532 * \todo Use std::string and std::vector<char>. */
533 static int makeTemporaryFilename(char* buf)
537 if ((len = strlen(buf)) < 7)
539 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
541 for (int i = len - 6; (i < len); i++)
545 /* mktemp is dangerous and we should use mkstemp instead, but
546 * since windows doesnt support it we have to separate the cases.
547 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
550 #if GMX_NATIVE_WINDOWS
554 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
562 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
567 // TODO use std::string
568 void gmx_tmpnam(char* buf)
570 int fd = makeTemporaryFilename(buf);
571 #if !GMX_NATIVE_WINDOWS
576 // TODO use std::string
577 FILE* gmx_fopen_temporary(char* buf)
579 FILE* fpout = nullptr;
580 int fd = makeTemporaryFilename(buf);
582 #if GMX_NATIVE_WINDOWS
583 if ((fpout = fopen(buf, "w")) == NULL)
585 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
588 if ((fpout = fdopen(fd, "w")) == nullptr)
590 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
597 int gmx_file_rename(const char* oldname, const char* newname)
599 #if !GMX_NATIVE_WINDOWS
600 /* under unix, rename() is atomic (at least, it should be). */
601 return rename(oldname, newname);
603 if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
606 /* This just lets the F@H checksumming system know about the rename */
607 fcRename(oldname, newname);
618 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
620 gmx::FilePtr in(fopen(oldname, "rb"));
626 /* If we don't copy when empty, we postpone opening the file
627 until we're actually ready to write. */
631 out.reset(fopen(newname, "wb"));
638 /* the full copy buffer size: */
639 constexpr int FILECOPY_BUFSIZE = 1 << 16;
640 std::vector<char> buf(FILECOPY_BUFSIZE);
642 while (!feof(in.get()))
646 nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
652 /* so this is where we open when copy_if_empty is false:
653 here we know we read something. */
654 out.reset(fopen(newname, "wb"));
660 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
666 if (ferror(in.get()))
675 int gmx_fsync(FILE* fp)
682 /* get the file number */
688 GMX_UNUSED_VALUE(fp);
692 /* do the actual fsync */
703 /* We check for these error codes this way because POSIX requires them
704 to be defined, and using anything other than macros is unlikely: */
706 /* we don't want to report an error just because fsync() caught a signal.
707 For our purposes, we can just ignore this. */
708 if (rc && errno == EINTR)
714 /* we don't want to report an error just because we tried to fsync()
715 stdout, a socket or a pipe. */
716 if (rc && errno == EINVAL)
724 void gmx_chdir(const char* directory)
726 #if GMX_NATIVE_WINDOWS
727 int rc = _chdir(directory);
729 int rc = chdir(directory);
733 auto message = gmx::formatString(
734 "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
735 GMX_THROW(gmx::FileIOError(message));
739 void gmx_getcwd(char* buffer, size_t size)
741 #if GMX_NATIVE_WINDOWS
742 char* pdum = _getcwd(buffer, size);
744 char* pdum = getcwd(buffer, size);
748 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));