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,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
52 #include <sys/types.h>
57 #if GMX_NATIVE_WINDOWS
58 # include <direct.h> // For _chdir() and _getcwd()
63 #include "gromacs/utility/cstringutil.h"
64 #include "gromacs/utility/datafilefinder.h"
65 #include "gromacs/utility/dir_separator.h"
66 #include "gromacs/utility/exceptions.h"
67 #include "gromacs/utility/fatalerror.h"
68 #include "gromacs/utility/mutex.h"
69 #include "gromacs/utility/path.h"
70 #include "gromacs/utility/programcontext.h"
71 #include "gromacs/utility/smalloc.h"
72 #include "gromacs/utility/stringutil.h"
74 /* we keep a linked list of all files opened through pipes (i.e.
75 compressed or .gzipped files. This way we can distinguish between them
76 without having to change the semantics of reading from/writing to files)
78 typedef struct t_pstack
81 struct t_pstack* prev;
84 static t_pstack* pstack = nullptr;
85 static bool bUnbuffered = false;
86 static int s_maxBackupCount = 0;
88 /* this linked list is an intrinsically globally shared object, so we have
89 to protect it with mutexes */
90 static gmx::Mutex pstack_mutex;
92 using Lock = gmx::lock_guard<gmx::Mutex>;
98 //! Global library file finder; stores the object set with setLibraryFileFinder().
99 const DataFileFinder* g_libFileFinder;
100 //! Default library file finder if nothing is set.
101 const DataFileFinder g_defaultLibFileFinder;
104 const DataFileFinder& getLibraryFileFinder()
106 if (g_libFileFinder != nullptr)
108 return *g_libFileFinder;
110 return g_defaultLibFileFinder;
113 void setLibraryFileFinder(const DataFileFinder* finder)
115 g_libFileFinder = finder;
120 void gmx_disable_file_buffering()
125 void gmx_set_max_backup_count(int count)
129 const char* env = getenv("GMX_MAXBACKUP");
132 // TODO: Check that the value is converted properly.
133 count = strtol(env, nullptr, 10);
141 // Use a reasonably low value for countmax; we might
142 // generate 4-5 files in each round, and we don't
143 // want to hit directory limits of 1024 or 2048 files.
147 s_maxBackupCount = count;
150 static void push_ps(FILE* fp)
154 Lock pstackLock(pstack_mutex);
163 /* don't use pipes!*/
164 # define popen fah_fopen
165 # define pclose fah_fclose
166 # define SKIP_FFOPS 1
171 # if (!HAVE_PIPES && !defined(__native_client__))
172 static FILE* popen(const char* nm, const char* mode)
174 gmx_impl("Sorry no pipes...");
179 static int pclose(FILE* fp)
181 gmx_impl("Sorry no pipes...");
185 # endif /* !HAVE_PIPES && !defined(__native_client__) */
186 #endif /* GMX_FAHCORE */
188 int gmx_ffclose(FILE* fp)
196 Lock pstackLock(pstack_mutex);
206 else if (ps->fp == fp)
212 pstack = pstack->prev;
217 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
221 if ((ps->prev != nullptr) && ps->prev->fp == fp)
223 if (ps->prev->fp != nullptr)
225 ret = pclose(ps->prev->fp);
228 ps->prev = ps->prev->prev;
245 void frewind(FILE* fp)
247 Lock pstackLock(pstack_mutex);
249 t_pstack* ps = pstack;
250 while (ps != nullptr)
254 fprintf(stderr, "Cannot rewind compressed file!\n");
262 int gmx_fseek(FILE* stream, gmx_off_t offset, int whence)
265 return fseeko(stream, offset, whence);
268 return _fseeki64(stream, offset, whence);
270 return fseek(stream, offset, whence);
275 gmx_off_t gmx_ftell(FILE* stream)
278 return ftello(stream);
282 return _ftelli64(stream);
284 return ftello64(stream);
287 return ftell(stream);
292 int gmx_truncate(const std::string& filename, gmx_off_t length)
294 #if GMX_NATIVE_WINDOWS
295 FILE* fp = fopen(filename.c_str(), "rb+");
301 int rc = _chsize_s(fileno(fp), length);
303 int rc = _chsize(fileno(fp), length);
308 return truncate(filename.c_str(), length);
312 static FILE* uncompress(const std::string& fn, const char* mode)
315 std::string buf = "uncompress -c < " + fn;
316 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
317 if ((fp = popen(buf.c_str(), mode)) == nullptr)
326 static FILE* gunzip(const std::string& fn, const char* mode)
329 std::string buf = "gunzip -c < ";
331 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
332 if ((fp = popen(buf.c_str(), mode)) == nullptr)
341 gmx_bool gmx_fexist(const std::string& fname)
349 test = fopen(fname.c_str(), "r");
352 /*Windows doesn't allow fopen of directory - so we need to check this seperately */
353 #if GMX_NATIVE_WINDOWS
354 DWORD attr = GetFileAttributes(fname.c_str());
355 return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
367 static std::string backup_fn(const std::string& file)
371 std::string directory = gmx::Path::getParentPath(file);
372 std::string fn = gmx::Path::getFilename(file);
374 if (directory.empty())
380 buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
382 } while ((count <= s_maxBackupCount) && gmx_fexist(buf));
384 /* Arbitrarily bail out */
385 if (count > s_maxBackupCount)
387 /* TODO: The error message is only accurate for code that starts with
388 * Gromacs command-line interface. */
390 "Won't make more than %d backups of %s for you.\n"
391 "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
392 s_maxBackupCount, fn.c_str());
398 void make_backup(const std::string& name)
400 if (s_maxBackupCount <= 0)
404 if (gmx_fexist(name))
406 auto backup = backup_fn(name);
407 if (rename(name.c_str(), backup.c_str()) == 0)
409 fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
413 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
418 FILE* gmx_ffopen(const std::string& file, const char* mode)
421 return fopen(file, mode);
437 bRead = (mode[0] == 'r' && mode[1] != '+');
438 if (!bRead || gmx_fexist(file))
440 if ((ff = fopen(file.c_str(), mode)) == nullptr)
444 /* Check whether we should be using buffering (default) or not
447 const char* bufsize = nullptr;
448 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
450 /* Check whether to use completely unbuffered */
457 bs = strtol(bufsize, nullptr, 10);
467 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
469 gmx_file("Buffering File");
476 std::string compressedFileName = file;
477 compressedFileName += ".Z";
478 if (gmx_fexist(compressedFileName))
480 ff = uncompress(compressedFileName, mode);
484 compressedFileName = file;
485 compressedFileName += ".gz";
486 if (gmx_fexist(compressedFileName))
488 ff = gunzip(compressedFileName, mode);
503 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
508 const DataFileFinder& finder = getLibraryFileFinder();
509 result = finder.findFile(
510 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
512 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
516 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
518 return findLibraryFile(std::string(filename), bAddCWD, bFatal);
521 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
526 const DataFileFinder& finder = getLibraryFileFinder();
527 fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
529 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
533 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
535 return openLibraryFile(std::string(filename), bAddCWD, bFatal);
540 /*! \brief Use mkstemp (or similar function to make a new temporary
541 * file and (on non-Windows systems) return a file descriptor to it.
543 * \todo Use std::string and std::vector<char>. */
544 static int makeTemporaryFilename(char* buf)
548 if ((len = strlen(buf)) < 7)
550 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
552 for (int i = len - 6; (i < len); i++)
556 /* mktemp is dangerous and we should use mkstemp instead, but
557 * since windows doesnt support it we have to separate the cases.
558 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
561 #if GMX_NATIVE_WINDOWS
565 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
573 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
578 // TODO use std::string
579 void gmx_tmpnam(char* buf)
581 int fd = makeTemporaryFilename(buf);
582 #if !GMX_NATIVE_WINDOWS
587 // TODO use std::string
588 FILE* gmx_fopen_temporary(char* buf)
590 FILE* fpout = nullptr;
591 int fd = makeTemporaryFilename(buf);
593 #if GMX_NATIVE_WINDOWS
594 if ((fpout = fopen(buf, "w")) == NULL)
596 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
599 if ((fpout = fdopen(fd, "w")) == nullptr)
601 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
608 int gmx_file_rename(const char* oldname, const char* newname)
610 #if !GMX_NATIVE_WINDOWS
611 /* under unix, rename() is atomic (at least, it should be). */
612 return rename(oldname, newname);
614 if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
625 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
627 gmx::FilePtr in(fopen(oldname, "rb"));
633 /* If we don't copy when empty, we postpone opening the file
634 until we're actually ready to write. */
638 out.reset(fopen(newname, "wb"));
645 /* the full copy buffer size: */
646 constexpr int FILECOPY_BUFSIZE = 1 << 16;
647 std::vector<char> buf(FILECOPY_BUFSIZE);
649 while (!feof(in.get()))
653 nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
659 /* so this is where we open when copy_if_empty is false:
660 here we know we read something. */
661 out.reset(fopen(newname, "wb"));
667 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
673 if (ferror(in.get()))
682 int gmx_fsync(FILE* fp)
687 /* the fahcore defines its own os-independent fsync */
689 #else /* GMX_FAHCORE */
693 /* get the file number */
702 /* do the actual fsync */
712 #endif /* GMX_FAHCORE */
714 /* We check for these error codes this way because POSIX requires them
715 to be defined, and using anything other than macros is unlikely: */
717 /* we don't want to report an error just because fsync() caught a signal.
718 For our purposes, we can just ignore this. */
719 if (rc && errno == EINTR)
725 /* we don't want to report an error just because we tried to fsync()
726 stdout, a socket or a pipe. */
727 if (rc && errno == EINVAL)
735 void gmx_chdir(const char* directory)
737 #if GMX_NATIVE_WINDOWS
738 int rc = _chdir(directory);
740 int rc = chdir(directory);
744 gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
748 void gmx_getcwd(char* buffer, size_t size)
750 #if GMX_NATIVE_WINDOWS
751 char* pdum = _getcwd(buffer, size);
753 char* pdum = getcwd(buffer, size);
757 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));