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 seperately */
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 * \todo Use std::string and std::vector<char>. */
525 static int makeTemporaryFilename(char* buf)
529 if ((len = strlen(buf)) < 7)
531 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
533 for (int i = len - 6; (i < len); i++)
537 /* mktemp is dangerous and we should use mkstemp instead, but
538 * since windows doesnt support it we have to separate the cases.
539 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
541 #if GMX_NATIVE_WINDOWS
545 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
549 int fd = mkstemp(buf);
553 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
558 // TODO use std::string
559 void gmx_tmpnam(char* buf)
561 int fd = makeTemporaryFilename(buf);
562 #if !GMX_NATIVE_WINDOWS
567 // TODO use std::string
568 FILE* gmx_fopen_temporary(char* buf)
570 FILE* fpout = nullptr;
571 int fd = makeTemporaryFilename(buf);
573 #if GMX_NATIVE_WINDOWS
574 if ((fpout = fopen(buf, "w")) == NULL)
576 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
579 if ((fpout = fdopen(fd, "w")) == nullptr)
581 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
588 int gmx_file_rename(const char* oldname, const char* newname)
590 #if !GMX_NATIVE_WINDOWS
591 /* under unix, rename() is atomic (at least, it should be). */
592 return rename(oldname, newname);
594 if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
597 /* This just lets the F@H checksumming system know about the rename */
598 fcRename(oldname, newname);
609 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
611 gmx::FilePtr in(fopen(oldname, "rb"));
617 /* If we don't copy when empty, we postpone opening the file
618 until we're actually ready to write. */
622 out.reset(fopen(newname, "wb"));
629 /* the full copy buffer size: */
630 constexpr int FILECOPY_BUFSIZE = 1 << 16;
631 std::vector<char> buf(FILECOPY_BUFSIZE);
633 while (!feof(in.get()))
635 size_t nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
641 /* so this is where we open when copy_if_empty is false:
642 here we know we read something. */
643 out.reset(fopen(newname, "wb"));
649 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
655 if (ferror(in.get()))
664 int gmx_fsync(FILE* fp)
669 /* get the file number */
673 int fn = _fileno(fp);
675 GMX_UNUSED_VALUE(fp);
679 /* do the actual fsync */
690 /* We check for these error codes this way because POSIX requires them
691 to be defined, and using anything other than macros is unlikely: */
693 /* we don't want to report an error just because fsync() caught a signal.
694 For our purposes, we can just ignore this. */
695 if (rc && errno == EINTR)
701 /* we don't want to report an error just because we tried to fsync()
702 stdout, a socket or a pipe. */
703 if (rc && errno == EINVAL)
711 void gmx_chdir(const char* directory)
713 #if GMX_NATIVE_WINDOWS
714 int rc = _chdir(directory);
716 int rc = chdir(directory);
720 auto message = gmx::formatString(
721 "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
722 GMX_THROW(gmx::FileIOError(message));
726 void gmx_getcwd(char* buffer, size_t size)
728 #if GMX_NATIVE_WINDOWS
729 char* pdum = _getcwd(buffer, size);
731 char* pdum = getcwd(buffer, size);
735 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));