*
* Copyright (c) 1991-2000, University of Groningen, The Netherlands.
* Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
#include <cstdlib>
#include <cstring>
+#include <mutex>
+#include <tuple>
+
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif
#if GMX_NATIVE_WINDOWS
-#include <direct.h> // For _chdir() and _getcwd()
-#include <io.h>
-#include <windows.h>
+# include <direct.h> // For _chdir() and _getcwd()
+# include <io.h>
+# include <windows.h>
#endif
#include "gromacs/utility/cstringutil.h"
#include "gromacs/utility/dir_separator.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/mutex.h"
#include "gromacs/utility/path.h"
#include "gromacs/utility/programcontext.h"
#include "gromacs/utility/smalloc.h"
compressed or .gzipped files. This way we can distinguish between them
without having to change the semantics of reading from/writing to files)
*/
-typedef struct t_pstack {
- FILE *fp;
- struct t_pstack *prev;
+typedef struct t_pstack
+{
+ FILE* fp;
+ struct t_pstack* prev;
} t_pstack;
-static t_pstack *pstack = nullptr;
-static bool bUnbuffered = false;
-static int s_maxBackupCount = 0;
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+static t_pstack* pstack = nullptr;
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+static bool bUnbuffered = false;
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+static int s_maxBackupCount = 0;
/* this linked list is an intrinsically globally shared object, so we have
to protect it with mutexes */
-static gmx::Mutex pstack_mutex;
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+static std::mutex pstack_mutex;
-using Lock = gmx::lock_guard<gmx::Mutex>;
+using Lock = std::lock_guard<std::mutex>;
namespace gmx
{
namespace
{
//! Global library file finder; stores the object set with setLibraryFileFinder().
-const DataFileFinder *g_libFileFinder;
+const DataFileFinder* g_libFileFinder; //NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
//! Default library file finder if nothing is set.
-const DataFileFinder g_defaultLibFileFinder;
-} // namespace
+const DataFileFinder g_defaultLibFileFinder;
+} // namespace
-const DataFileFinder &getLibraryFileFinder()
+const DataFileFinder& getLibraryFileFinder()
{
if (g_libFileFinder != nullptr)
{
return g_defaultLibFileFinder;
}
-void setLibraryFileFinder(const DataFileFinder *finder)
+void setLibraryFileFinder(const DataFileFinder* finder)
{
g_libFileFinder = finder;
}
{
if (count < 0)
{
- const char *env = getenv("GMX_MAXBACKUP");
+ const char* env = getenv("GMX_MAXBACKUP");
if (env != nullptr)
{
// TODO: Check that the value is converted properly.
s_maxBackupCount = count;
}
-static void push_ps(FILE *fp)
+static void push_ps(FILE* fp)
{
- t_pstack *ps;
+ t_pstack* ps = nullptr;
- Lock pstackLock(pstack_mutex);
+ Lock pstackLock(pstack_mutex);
snew(ps, 1);
ps->fp = fp;
}
#if GMX_FAHCORE
-/* don't use pipes!*/
-#define popen fah_fopen
-#define pclose fah_fclose
-#define SKIP_FFOPS 1
-#else
-#ifdef gmx_ffclose
-#undef gmx_ffclose
+# ifdef gmx_ffclose
+# undef gmx_ffclose
+# endif
#endif
#if (!HAVE_PIPES && !defined(__native_client__))
-static FILE *popen(const char *nm, const char *mode)
+static FILE* popen(const char* /* nm */, const char* /* mode */)
{
gmx_impl("Sorry no pipes...");
return NULL;
}
-static int pclose(FILE *fp)
+static int pclose(FILE* /* fp */)
{
gmx_impl("Sorry no pipes...");
return 0;
}
#endif /* !HAVE_PIPES && !defined(__native_client__) */
-#endif /* GMX_FAHCORE */
-int gmx_ffclose(FILE *fp)
+int gmx_ffclose(FILE* fp)
{
-#ifdef SKIP_FFOPS
- return fclose(fp);
-#else
- t_pstack *ps, *tmp;
- int ret = 0;
+ int ret = 0;
- Lock pstackLock(pstack_mutex);
+ Lock pstackLock(pstack_mutex);
- ps = pstack;
+ t_pstack* ps = pstack;
if (ps == nullptr)
{
if (fp != nullptr)
{
ret = pclose(ps->prev->fp);
}
- tmp = ps->prev;
- ps->prev = ps->prev->prev;
+ t_pstack* tmp = ps->prev;
+ ps->prev = ps->prev->prev;
sfree(tmp);
}
else
}
return ret;
-#endif
}
-void frewind(FILE *fp)
+void frewind(FILE* fp)
{
- Lock pstackLock(pstack_mutex);
+ Lock pstackLock(pstack_mutex);
- t_pstack *ps = pstack;
+ t_pstack* ps = pstack;
while (ps != nullptr)
{
if (ps->fp == fp)
rewind(fp);
}
-int gmx_fseek(FILE *stream, gmx_off_t offset, int whence)
+int gmx_fseek(FILE* stream, gmx_off_t offset, int whence)
{
#if HAVE_FSEEKO
return fseeko(stream, offset, whence);
#else
-#if HAVE__FSEEKI64
+# if HAVE__FSEEKI64
return _fseeki64(stream, offset, whence);
-#else
+# else
return fseek(stream, offset, whence);
-#endif
+# endif
#endif
}
-gmx_off_t gmx_ftell(FILE *stream)
+gmx_off_t gmx_ftell(FILE* stream)
{
#if HAVE_FSEEKO
return ftello(stream);
#else
-#if HAVE__FSEEKI64
-#ifndef __MINGW32__
+# if HAVE__FSEEKI64
+# ifndef __MINGW32__
return _ftelli64(stream);
-#else
+# else
return ftello64(stream);
-#endif
-#else
+# endif
+# else
return ftell(stream);
-#endif
+# endif
#endif
}
-int gmx_truncate(const char *filename, gmx_off_t length)
+int gmx_truncate(const std::string& filename, gmx_off_t length)
{
-#if GMX_NATIVE_WINDOWS
- FILE *fp = fopen(filename, "rb+");
+#if GMX_NATIVE_WINDOWS && !GMX_FAHCORE
+ FILE* fp = fopen(filename.c_str(), "rb+");
if (fp == NULL)
{
return -1;
}
-#ifdef _MSC_VER
+# ifdef _MSC_VER
int rc = _chsize_s(fileno(fp), length);
-#else
+# else
int rc = _chsize(fileno(fp), length);
-#endif
+# endif
fclose(fp);
return rc;
#else
- return truncate(filename, length);
+ return truncate(filename.c_str(), length);
#endif
}
-static FILE *uncompress(const char *fn, const char *mode)
+static FILE* uncompress(const std::string& fn, const char* mode)
{
- FILE *fp;
- char buf[256];
-
- sprintf(buf, "uncompress -c < %s", fn);
- fprintf(stderr, "Going to execute '%s'\n", buf);
- if ((fp = popen(buf, mode)) == nullptr)
+ FILE* fp = nullptr;
+ std::string buf = "uncompress -c < " + fn;
+ fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
+ if ((fp = popen(buf.c_str(), mode)) == nullptr)
{
gmx_open(fn);
}
return fp;
}
-static FILE *gunzip(const char *fn, const char *mode)
+static FILE* gunzip(const std::string& fn, const char* mode)
{
- FILE *fp;
- char buf[256];
-
- sprintf(buf, "gunzip -c < %s", fn);
- fprintf(stderr, "Going to execute '%s'\n", buf);
- if ((fp = popen(buf, mode)) == nullptr)
+ FILE* fp = nullptr;
+ std::string buf = "gunzip -c < ";
+ buf += fn;
+ fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
+ if ((fp = popen(buf.c_str(), mode)) == nullptr)
{
gmx_open(fn);
}
return fp;
}
-gmx_bool gmx_fexist(const char *fname)
+gmx_bool gmx_fexist(const std::string& fname)
{
- FILE *test;
-
- if (fname == nullptr)
+ if (fname.empty())
{
return FALSE;
}
- test = fopen(fname, "r");
+ FILE* test = fopen(fname.c_str(), "r");
if (test == nullptr)
{
- /*Windows doesn't allow fopen of directory - so we need to check this seperately */
- #if GMX_NATIVE_WINDOWS
- DWORD attr = GetFileAttributes(fname);
+/*Windows doesn't allow fopen of directory - so we need to check this separately */
+#if GMX_NATIVE_WINDOWS
+ DWORD attr = GetFileAttributes(fname.c_str());
return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
- #else
+#else
return FALSE;
- #endif
+#endif
}
else
{
}
}
-static char *backup_fn(const char *file)
+static std::string backup_fn(const std::string& file)
{
- int i, count = 1;
- char *directory, *fn;
- char *buf;
+ int count = 1;
- smalloc(buf, GMX_PATH_MAX);
-
- for (i = strlen(file)-1; ((i > 0) && (file[i] != DIR_SEPARATOR)); i--)
+ std::string directory = gmx::Path::getParentPath(file);
+ std::string fn = gmx::Path::getFilename(file);
+ std::string buf;
+ if (directory.empty())
{
- ;
- }
- /* Must check whether i > 0, i.e. whether there is a directory
- * in the file name. In that case we overwrite the / sign with
- * a '\0' to end the directory string .
- */
- if (i > 0)
- {
- directory = gmx_strdup(file);
- directory[i] = '\0';
- fn = gmx_strdup(file+i+1);
- }
- else
- {
- directory = gmx_strdup(".");
- fn = gmx_strdup(file);
+ directory = ".";
}
do
{
- sprintf(buf, "%s/#%s.%d#", directory, fn, count);
+ buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
count++;
- }
- while ((count <= s_maxBackupCount) && gmx_fexist(buf));
+ } while ((count <= s_maxBackupCount) && gmx_fexist(buf));
/* Arbitrarily bail out */
if (count > s_maxBackupCount)
{
/* TODO: The error message is only accurate for code that starts with
* Gromacs command-line interface. */
- gmx_fatal(FARGS, "Won't make more than %d backups of %s for you.\n"
+ gmx_fatal(FARGS,
+ "Won't make more than %d backups of %s for you.\n"
"The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
- s_maxBackupCount, fn);
+ s_maxBackupCount,
+ fn.c_str());
}
- sfree(directory);
- sfree(fn);
-
return buf;
}
-void make_backup(const char *name)
+void make_backup(const std::string& name)
{
if (s_maxBackupCount <= 0)
{
}
if (gmx_fexist(name))
{
- char *backup = backup_fn(name);
- if (rename(name, backup) == 0)
+ auto backup = backup_fn(name);
+ if (rename(name.c_str(), backup.c_str()) == 0)
{
- fprintf(stderr, "\nBack Off! I just backed up %s to %s\n",
- name, backup);
+ fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
}
else
{
- fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name, backup);
+ fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
}
- sfree(backup);
}
}
-FILE *gmx_ffopen(const char *file, const char *mode)
+FILE* gmx_ffopen(const std::string& file, const char* mode)
{
-#ifdef SKIP_FFOPS
- return fopen(file, mode);
-#else
- FILE *ff = nullptr;
- char buf[256], *bufsize = nullptr, *ptr;
- gmx_bool bRead;
- int bs;
+ FILE* ff = nullptr;
- if (file == nullptr)
+ if (file.empty())
{
return nullptr;
}
make_backup(file);
}
- bRead = (mode[0] == 'r' && mode[1] != '+');
- strcpy(buf, file);
- if (!bRead || gmx_fexist(buf))
+ bool bRead = (mode[0] == 'r' && mode[1] != '+');
+ if (!bRead || gmx_fexist(file))
{
- if ((ff = fopen(buf, mode)) == nullptr)
+ if ((ff = fopen(file.c_str(), mode)) == nullptr)
{
- gmx_file(buf);
+ gmx_file(file);
}
/* Check whether we should be using buffering (default) or not
* (for debugging)
*/
+ const char* bufsize = nullptr;
if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
{
/* Check whether to use completely unbuffered */
- if (bUnbuffered)
- {
- bs = 0;
- }
- else
- {
- bs = strtol(bufsize, nullptr, 10);
- }
+ const int bs = bUnbuffered ? 0 : strtol(bufsize, nullptr, 10);
if (bs <= 0)
{
setbuf(ff, nullptr);
}
else
{
- snew(ptr, bs+8);
+ char* ptr = nullptr;
+ snew(ptr, bs + 8);
if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
{
gmx_file("Buffering File");
}
else
{
- sprintf(buf, "%s.Z", file);
- if (gmx_fexist(buf))
+ std::string compressedFileName = file;
+ compressedFileName += ".Z";
+ if (gmx_fexist(compressedFileName))
{
- ff = uncompress(buf, mode);
+ ff = uncompress(compressedFileName, mode);
}
else
{
- sprintf(buf, "%s.gz", file);
- if (gmx_fexist(buf))
+ compressedFileName = file;
+ compressedFileName += ".gz";
+ if (gmx_fexist(compressedFileName))
{
- ff = gunzip(buf, mode);
+ ff = gunzip(compressedFileName, mode);
}
else
{
}
}
return ff;
-#endif
}
namespace gmx
{
-std::string findLibraryFile(const std::string &filename, bool bAddCWD, bool bFatal)
+std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
{
std::string result;
try
{
- const DataFileFinder &finder = getLibraryFileFinder();
- result = finder.findFile(DataFileOptions(filename)
- .includeCurrentDir(bAddCWD)
- .throwIfNotFound(bFatal));
+ const DataFileFinder& finder = getLibraryFileFinder();
+ result = finder.findFile(
+ DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
}
- GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
+ GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
return result;
}
-std::string findLibraryFile(const char *filename, bool bAddCWD, bool bFatal)
+std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
{
return findLibraryFile(std::string(filename), bAddCWD, bFatal);
}
-FilePtr openLibraryFile(const std::string &filename, bool bAddCWD, bool bFatal)
+FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
{
FilePtr fp;
try
{
- const DataFileFinder &finder = getLibraryFileFinder();
- fp = finder.openFile(DataFileOptions(filename)
- .includeCurrentDir(bAddCWD)
- .throwIfNotFound(bFatal));
+ const DataFileFinder& finder = getLibraryFileFinder();
+ fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
}
- GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
+ GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
return fp;
}
-FilePtr openLibraryFile(const char *filename, bool bAddCWD, bool bFatal)
+FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
{
return openLibraryFile(std::string(filename), bAddCWD, bFatal);
}
} // namespace gmx
-void gmx_tmpnam(char *buf)
+/*! \brief Use mkstemp (or similar function to make a new temporary
+ * file and (on non-Windows systems) return a file descriptor to it.
+ *
+ * Note: not thread-safe on non-Windows systems
+ *
+ * \todo Use std::string and std::vector<char>. */
+static int makeTemporaryFilename(char* buf)
{
- int i, len;
+ int len = 0;
if ((len = strlen(buf)) < 7)
{
gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
}
- for (i = len-6; (i < len); i++)
+ for (int i = len - 6; (i < len); i++)
{
buf[i] = 'X';
}
_mktemp(buf);
if (buf == NULL)
{
- gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
- strerror(errno));
+ gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
}
+ int fd = 0;
#else
int fd = mkstemp(buf);
+ /* mkstemp creates 0600 files - respect umask instead */
+ mode_t currUmask = umask(0);
+ umask(currUmask);
+ fchmod(fd, 0666 & ~currUmask);
+
if (fd < 0)
{
- gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
- strerror(errno));
+ gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
}
+#endif
+ return fd;
+}
+// TODO use std::string
+void gmx_tmpnam(char* buf)
+{
+ int fd = makeTemporaryFilename(buf);
+#if !GMX_NATIVE_WINDOWS
close(fd);
#endif
- /* name in Buf should now be OK and file is CLOSED */
}
-FILE *gmx_fopen_temporary(char *buf)
+// TODO use std::string
+FILE* gmx_fopen_temporary(char* buf)
{
- int i, len;
- FILE *fpout = nullptr;
+ FILE* fpout = nullptr;
+ int fd = makeTemporaryFilename(buf);
- if ((len = strlen(buf)) < 7)
- {
- gmx_fatal(FARGS, "Buf passed to gmx_fopentmp must be at least 7 bytes long");
- }
- for (i = len-6; (i < len); i++)
- {
- buf[i] = 'X';
- }
- /* mktemp is dangerous and we should use mkstemp instead, but
- * since windows doesnt support it we have to separate the cases.
- * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
- */
#if GMX_NATIVE_WINDOWS
- _mktemp(buf);
- if (buf == NULL)
- {
- gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
- strerror(errno));
- }
if ((fpout = fopen(buf, "w")) == NULL)
{
gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
}
#else
- int fd = mkstemp(buf);
- if (fd < 0)
- {
- gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
- strerror(errno));
- }
if ((fpout = fdopen(fd, "w")) == nullptr)
{
gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
}
#endif
- /* name in Buf should now be OK and file is open */
return fpout;
}
-int gmx_file_rename(const char *oldname, const char *newname)
+void gmx_file_rename(const char* oldname, const char* newname)
{
+ int code;
#if !GMX_NATIVE_WINDOWS
/* under unix, rename() is atomic (at least, it should be). */
- return rename(oldname, newname);
+ code = rename(oldname, newname);
#else
- if (MoveFileEx(oldname, newname,
- MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
+ if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
- return 0;
+# if GMX_FAHCORE
+ /* This just lets the F@H checksumming system know about the rename */
+ fcRename(oldname, newname);
+# endif
+ code = 0;
}
else
{
- return 1;
+ code = 1;
}
#endif
+ if (code != 0)
+ {
+ auto errorMsg = gmx::formatString("Failed to rename %s to %s.", oldname, newname);
+ GMX_THROW(gmx::FileIOError(errorMsg));
+ }
}
-int gmx_file_copy(const char *oldname, const char *newname, gmx_bool copy_if_empty)
+int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
{
gmx::FilePtr in(fopen(oldname, "rb"));
if (!in)
}
/* the full copy buffer size: */
- constexpr int FILECOPY_BUFSIZE = 1<<16;
+ constexpr int FILECOPY_BUFSIZE = 1 << 16;
std::vector<char> buf(FILECOPY_BUFSIZE);
while (!feof(in.get()))
{
- size_t nread;
-
- nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
+ size_t nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
if (nread > 0)
{
- size_t ret;
+ size_t ret = 0;
if (!out)
{
/* so this is where we open when copy_if_empty is false:
}
-int gmx_fsync(FILE *fp)
+int gmx_fsync(FILE* fp)
{
int rc = 0;
-#if GMX_FAHCORE
- /* the fahcore defines its own os-independent fsync */
- rc = fah_fsync(fp);
-#else /* GMX_FAHCORE */
{
- int fn;
-
/* get the file number */
#if HAVE_FILENO
- fn = fileno(fp);
+ int fn = fileno(fp);
#elif HAVE__FILENO
- fn = _fileno(fp);
+ int fn = _fileno(fp);
#else
- fn = -1;
+ GMX_UNUSED_VALUE(fp);
+ int fn = -1;
#endif
/* do the actual fsync */
#endif
}
}
-#endif /* GMX_FAHCORE */
/* We check for these error codes this way because POSIX requires them
to be defined, and using anything other than macros is unlikely: */
return rc;
}
-void gmx_chdir(const char *directory)
+void gmx_chdir(const char* directory)
{
#if GMX_NATIVE_WINDOWS
int rc = _chdir(directory);
#else
- int rc = chdir(directory);
+ int rc = chdir(directory);
#endif
if (rc != 0)
{
- gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s",
- directory, strerror(errno));
+ auto message = gmx::formatString(
+ "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
+ GMX_THROW(gmx::FileIOError(message));
}
}
-void gmx_getcwd(char *buffer, size_t size)
+void gmx_getcwd(char* buffer, size_t size)
{
#if GMX_NATIVE_WINDOWS
- char *pdum = _getcwd(buffer, size);
+ char* pdum = _getcwd(buffer, size);
#else
- char *pdum = getcwd(buffer, size);
+ char* pdum = getcwd(buffer, size);
#endif
if (pdum == nullptr)
{
- gmx_fatal(FARGS, "Cannot get working directory. Reason: %s",
- strerror(errno));
+ gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));
}
}