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 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
86 static t_pstack* pstack = nullptr;
87 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
88 static bool bUnbuffered = false;
89 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
90 static int s_maxBackupCount = 0;
92 /* this linked list is an intrinsically globally shared object, so we have
93 to protect it with mutexes */
94 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
95 static std::mutex pstack_mutex;
97 using Lock = std::lock_guard<std::mutex>;
103 //! Global library file finder; stores the object set with setLibraryFileFinder().
104 const DataFileFinder* g_libFileFinder; //NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
105 //! Default library file finder if nothing is set.
106 const DataFileFinder g_defaultLibFileFinder;
109 const DataFileFinder& getLibraryFileFinder()
111 if (g_libFileFinder != nullptr)
113 return *g_libFileFinder;
115 return g_defaultLibFileFinder;
118 void setLibraryFileFinder(const DataFileFinder* finder)
120 g_libFileFinder = finder;
125 void gmx_disable_file_buffering()
130 void gmx_set_max_backup_count(int count)
134 const char* env = getenv("GMX_MAXBACKUP");
137 // TODO: Check that the value is converted properly.
138 count = strtol(env, nullptr, 10);
146 // Use a reasonably low value for countmax; we might
147 // generate 4-5 files in each round, and we don't
148 // want to hit directory limits of 1024 or 2048 files.
152 s_maxBackupCount = count;
155 static void push_ps(FILE* fp)
157 t_pstack* ps = nullptr;
159 Lock pstackLock(pstack_mutex);
172 #if (!HAVE_PIPES && !defined(__native_client__))
173 static FILE* popen(const char* /* nm */, const char* /* mode */)
175 gmx_impl("Sorry no pipes...");
180 static int pclose(FILE* /* fp */)
182 gmx_impl("Sorry no pipes...");
186 #endif /* !HAVE_PIPES && !defined(__native_client__) */
188 int gmx_ffclose(FILE* fp)
192 Lock pstackLock(pstack_mutex);
194 t_pstack* ps = pstack;
202 else if (ps->fp == fp)
208 pstack = pstack->prev;
213 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
217 if ((ps->prev != nullptr) && ps->prev->fp == fp)
219 if (ps->prev->fp != nullptr)
221 ret = pclose(ps->prev->fp);
223 t_pstack* tmp = ps->prev;
224 ps->prev = ps->prev->prev;
240 void frewind(FILE* fp)
242 Lock pstackLock(pstack_mutex);
244 t_pstack* ps = pstack;
245 while (ps != nullptr)
249 fprintf(stderr, "Cannot rewind compressed file!\n");
257 int gmx_fseek(FILE* stream, gmx_off_t offset, int whence)
260 return fseeko(stream, offset, whence);
263 return _fseeki64(stream, offset, whence);
265 return fseek(stream, offset, whence);
270 gmx_off_t gmx_ftell(FILE* stream)
273 return ftello(stream);
277 return _ftelli64(stream);
279 return ftello64(stream);
282 return ftell(stream);
287 int gmx_truncate(const std::string& filename, gmx_off_t length)
289 #if GMX_NATIVE_WINDOWS && !GMX_FAHCORE
290 FILE* fp = fopen(filename.c_str(), "rb+");
296 int rc = _chsize_s(fileno(fp), length);
298 int rc = _chsize(fileno(fp), length);
303 return truncate(filename.c_str(), length);
307 static FILE* uncompress(const std::string& fn, const char* mode)
310 std::string buf = "uncompress -c < " + fn;
311 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
312 if ((fp = popen(buf.c_str(), mode)) == nullptr)
321 static FILE* gunzip(const std::string& fn, const char* mode)
324 std::string buf = "gunzip -c < ";
326 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
327 if ((fp = popen(buf.c_str(), mode)) == nullptr)
336 gmx_bool gmx_fexist(const std::string& fname)
342 FILE* test = fopen(fname.c_str(), "r");
345 /*Windows doesn't allow fopen of directory - so we need to check this separately */
346 #if GMX_NATIVE_WINDOWS
347 DWORD attr = GetFileAttributes(fname.c_str());
348 return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
360 static std::string backup_fn(const std::string& file)
364 std::string directory = gmx::Path::getParentPath(file);
365 std::string fn = gmx::Path::getFilename(file);
367 if (directory.empty())
373 buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
375 } while ((count <= s_maxBackupCount) && gmx_fexist(buf));
377 /* Arbitrarily bail out */
378 if (count > s_maxBackupCount)
380 /* TODO: The error message is only accurate for code that starts with
381 * Gromacs command-line interface. */
383 "Won't make more than %d backups of %s for you.\n"
384 "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
392 void make_backup(const std::string& name)
394 if (s_maxBackupCount <= 0)
398 if (gmx_fexist(name))
400 auto backup = backup_fn(name);
401 if (rename(name.c_str(), backup.c_str()) == 0)
403 fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
407 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
412 FILE* gmx_ffopen(const std::string& file, const char* mode)
426 bool 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 */
440 const int bs = bUnbuffered ? 0 : strtol(bufsize, nullptr, 10);
449 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
451 gmx_file("Buffering File");
458 std::string compressedFileName = file;
459 compressedFileName += ".Z";
460 if (gmx_fexist(compressedFileName))
462 ff = uncompress(compressedFileName, mode);
466 compressedFileName = file;
467 compressedFileName += ".gz";
468 if (gmx_fexist(compressedFileName))
470 ff = gunzip(compressedFileName, mode);
484 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
489 const DataFileFinder& finder = getLibraryFileFinder();
490 result = finder.findFile(
491 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
493 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
497 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
499 return findLibraryFile(std::string(filename), bAddCWD, bFatal);
502 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
507 const DataFileFinder& finder = getLibraryFileFinder();
508 fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
510 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
514 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
516 return openLibraryFile(std::string(filename), bAddCWD, bFatal);
521 /*! \brief Use mkstemp (or similar function to make a new temporary
522 * file and (on non-Windows systems) return a file descriptor to it.
524 * Note: not thread-safe on non-Windows systems
526 * \todo Use std::string and std::vector<char>. */
527 static int makeTemporaryFilename(char* buf)
531 if ((len = strlen(buf)) < 7)
533 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
535 for (int i = len - 6; (i < len); i++)
539 /* mktemp is dangerous and we should use mkstemp instead, but
540 * since windows doesnt support it we have to separate the cases.
541 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
543 #if GMX_NATIVE_WINDOWS
547 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
551 int fd = mkstemp(buf);
553 /* mkstemp creates 0600 files - respect umask instead */
554 mode_t currUmask = umask(0);
556 fchmod(fd, 0666 & ~currUmask);
560 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
565 // TODO use std::string
566 void gmx_tmpnam(char* buf)
568 int fd = makeTemporaryFilename(buf);
569 #if !GMX_NATIVE_WINDOWS
574 // TODO use std::string
575 FILE* gmx_fopen_temporary(char* buf)
577 FILE* fpout = nullptr;
578 int fd = makeTemporaryFilename(buf);
580 #if GMX_NATIVE_WINDOWS
581 if ((fpout = fopen(buf, "w")) == NULL)
583 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
586 if ((fpout = fdopen(fd, "w")) == nullptr)
588 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
595 void 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 code = 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 auto errorMsg = gmx::formatString("Failed to rename %s to %s.", oldname, newname);
618 GMX_THROW(gmx::FileIOError(errorMsg));
622 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
624 gmx::FilePtr in(fopen(oldname, "rb"));
630 /* If we don't copy when empty, we postpone opening the file
631 until we're actually ready to write. */
635 out.reset(fopen(newname, "wb"));
642 /* the full copy buffer size: */
643 constexpr int FILECOPY_BUFSIZE = 1 << 16;
644 std::vector<char> buf(FILECOPY_BUFSIZE);
646 while (!feof(in.get()))
648 size_t nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
654 /* so this is where we open when copy_if_empty is false:
655 here we know we read something. */
656 out.reset(fopen(newname, "wb"));
662 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
668 if (ferror(in.get()))
677 int gmx_fsync(FILE* fp)
682 /* get the file number */
686 int fn = _fileno(fp);
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));