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, 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.
50 #include <sys/types.h>
55 #if GMX_NATIVE_WINDOWS
56 #include <direct.h> // For _chdir() and _getcwd()
61 #include "gromacs/utility/cstringutil.h"
62 #include "gromacs/utility/datafilefinder.h"
63 #include "gromacs/utility/dir_separator.h"
64 #include "gromacs/utility/exceptions.h"
65 #include "gromacs/utility/fatalerror.h"
66 #include "gromacs/utility/mutex.h"
67 #include "gromacs/utility/path.h"
68 #include "gromacs/utility/programcontext.h"
69 #include "gromacs/utility/smalloc.h"
70 #include "gromacs/utility/stringutil.h"
72 /* we keep a linked list of all files opened through pipes (i.e.
73 compressed or .gzipped files. This way we can distinguish between them
74 without having to change the semantics of reading from/writing to files)
76 typedef struct t_pstack {
78 struct t_pstack *prev;
81 static t_pstack *pstack = nullptr;
82 static bool bUnbuffered = false;
83 static int s_maxBackupCount = 0;
85 /* this linked list is an intrinsically globally shared object, so we have
86 to protect it with mutexes */
87 static gmx::Mutex pstack_mutex;
89 using Lock = gmx::lock_guard<gmx::Mutex>;
95 //! Global library file finder; stores the object set with setLibraryFileFinder().
96 const DataFileFinder *g_libFileFinder;
97 //! Default library file finder if nothing is set.
98 const DataFileFinder g_defaultLibFileFinder;
101 const DataFileFinder &getLibraryFileFinder()
103 if (g_libFileFinder != nullptr)
105 return *g_libFileFinder;
107 return g_defaultLibFileFinder;
110 void setLibraryFileFinder(const DataFileFinder *finder)
112 g_libFileFinder = finder;
117 void gmx_disable_file_buffering(void)
122 void gmx_set_max_backup_count(int count)
126 const char *env = getenv("GMX_MAXBACKUP");
129 // TODO: Check that the value is converted properly.
130 count = strtol(env, nullptr, 10);
138 // Use a reasonably low value for countmax; we might
139 // generate 4-5 files in each round, and we don't
140 // want to hit directory limits of 1024 or 2048 files.
144 s_maxBackupCount = count;
147 static void push_ps(FILE *fp)
151 Lock pstackLock(pstack_mutex);
160 /* don't use pipes!*/
161 #define popen fah_fopen
162 #define pclose fah_fclose
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__) */
183 #endif /* GMX_FAHCORE */
185 int gmx_ffclose(FILE *fp)
193 Lock pstackLock(pstack_mutex);
203 else if (ps->fp == fp)
209 pstack = pstack->prev;
214 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
218 if ((ps->prev != nullptr) && ps->prev->fp == fp)
220 if (ps->prev->fp != nullptr)
222 ret = pclose(ps->prev->fp);
225 ps->prev = ps->prev->prev;
242 void frewind(FILE *fp)
244 Lock pstackLock(pstack_mutex);
246 t_pstack *ps = pstack;
247 while (ps != nullptr)
251 fprintf(stderr, "Cannot rewind compressed file!\n");
259 int gmx_fseek(FILE *stream, gmx_off_t offset, int whence)
262 return fseeko(stream, offset, whence);
265 return _fseeki64(stream, offset, whence);
267 return fseek(stream, offset, whence);
272 gmx_off_t gmx_ftell(FILE *stream)
275 return ftello(stream);
279 return _ftelli64(stream);
281 return ftello64(stream);
284 return ftell(stream);
289 int gmx_truncate(const char *filename, gmx_off_t length)
291 #if GMX_NATIVE_WINDOWS
292 FILE *fp = fopen(filename, "rb+");
298 int rc = _chsize_s(fileno(fp), length);
300 int rc = _chsize(fileno(fp), length);
305 return truncate(filename, length);
309 static FILE *uncompress(const char *fn, const char *mode)
314 sprintf(buf, "uncompress -c < %s", fn);
315 fprintf(stderr, "Going to execute '%s'\n", buf);
316 if ((fp = popen(buf, mode)) == nullptr)
325 static FILE *gunzip(const char *fn, const char *mode)
330 sprintf(buf, "gunzip -c < %s", fn);
331 fprintf(stderr, "Going to execute '%s'\n", buf);
332 if ((fp = popen(buf, mode)) == nullptr)
341 gmx_bool gmx_fexist(const char *fname)
345 if (fname == nullptr)
349 test = fopen(fname, "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);
355 return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
367 static char *backup_fn(const char *file)
370 char *directory, *fn;
373 smalloc(buf, GMX_PATH_MAX);
375 for (i = strlen(file)-1; ((i > 0) && (file[i] != DIR_SEPARATOR)); i--)
379 /* Must check whether i > 0, i.e. whether there is a directory
380 * in the file name. In that case we overwrite the / sign with
381 * a '\0' to end the directory string .
385 directory = gmx_strdup(file);
387 fn = gmx_strdup(file+i+1);
391 directory = gmx_strdup(".");
392 fn = gmx_strdup(file);
396 sprintf(buf, "%s/#%s.%d#", directory, fn, count);
399 while ((count <= s_maxBackupCount) && gmx_fexist(buf));
401 /* Arbitrarily bail out */
402 if (count > s_maxBackupCount)
404 /* TODO: The error message is only accurate for code that starts with
405 * Gromacs command-line interface. */
406 gmx_fatal(FARGS, "Won't make more than %d backups of %s for you.\n"
407 "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
408 s_maxBackupCount, fn);
417 void make_backup(const char *name)
419 if (s_maxBackupCount <= 0)
423 if (gmx_fexist(name))
425 char *backup = backup_fn(name);
426 if (rename(name, backup) == 0)
428 fprintf(stderr, "\nBack Off! I just backed up %s to %s\n",
433 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name, backup);
439 FILE *gmx_ffopen(const char *file, const char *mode)
442 return fopen(file, mode);
445 char buf[256], *bufsize = nullptr, *ptr;
459 bRead = (mode[0] == 'r' && mode[1] != '+');
461 if (!bRead || gmx_fexist(buf))
463 if ((ff = fopen(buf, mode)) == nullptr)
467 /* Check whether we should be using buffering (default) or not
470 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
472 /* Check whether to use completely unbuffered */
479 bs = strtol(bufsize, nullptr, 10);
488 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
490 gmx_file("Buffering File");
497 sprintf(buf, "%s.Z", file);
500 ff = uncompress(buf, mode);
504 sprintf(buf, "%s.gz", file);
507 ff = gunzip(buf, mode);
520 char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal)
524 const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
526 finder.findFile(gmx::DataFileOptions(file)
527 .includeCurrentDir(bAddCWD)
528 .throwIfNotFound(bFatal));
531 return gmx_strdup(result.c_str());
534 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
538 FILE *low_libopen(const char *file, gmx_bool bFatal)
542 const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
544 finder.openFile(gmx::DataFileOptions(file)
545 .includeCurrentDir(true)
546 .throwIfNotFound(bFatal));
549 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
553 char *gmxlibfn(const char *file)
555 return low_gmxlibfn(file, TRUE, TRUE);
558 FILE *libopen(const char *file)
560 return low_libopen(file, TRUE);
563 void gmx_tmpnam(char *buf)
567 if ((len = strlen(buf)) < 7)
569 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
571 for (i = len-6; (i < len); i++)
575 /* mktemp is dangerous and we should use mkstemp instead, but
576 * since windows doesnt support it we have to separate the cases.
577 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
579 #if GMX_NATIVE_WINDOWS
583 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
587 int fd = mkstemp(buf);
591 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
598 /* name in Buf should now be OK and file is CLOSED */
603 FILE *gmx_fopen_temporary(char *buf)
606 FILE *fpout = nullptr;
608 if ((len = strlen(buf)) < 7)
610 gmx_fatal(FARGS, "Buf passed to gmx_fopentmp must be at least 7 bytes long");
612 for (i = len-6; (i < len); i++)
616 /* mktemp is dangerous and we should use mkstemp instead, but
617 * since windows doesnt support it we have to separate the cases.
618 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
620 #if GMX_NATIVE_WINDOWS
624 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
627 if ((fpout = fopen(buf, "w")) == NULL)
629 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
632 int fd = mkstemp(buf);
635 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
638 if ((fpout = fdopen(fd, "w")) == nullptr)
640 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
643 /* name in Buf should now be OK and file is open */
648 int gmx_file_rename(const char *oldname, const char *newname)
650 #if !GMX_NATIVE_WINDOWS
651 /* under unix, rename() is atomic (at least, it should be). */
652 return rename(oldname, newname);
654 if (MoveFileEx(oldname, newname,
655 MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
666 int gmx_file_copy(const char *oldname, const char *newname, gmx_bool copy_if_empty)
668 /* the full copy buffer size: */
669 #define FILECOPY_BUFSIZE (1<<16)
674 snew(buf, FILECOPY_BUFSIZE);
676 in = fopen(oldname, "rb");
682 /* If we don't copy when empty, we postpone opening the file
683 until we're actually ready to write. */
686 out = fopen(newname, "wb");
697 nread = fread(buf, sizeof(char), FILECOPY_BUFSIZE, in);
703 /* so this is where we open when copy_if_empty is false:
704 here we know we read something. */
705 out = fopen(newname, "wb");
711 ret = fwrite(buf, sizeof(char), nread, out);
737 #undef FILECOPY_BUFSIZE
741 int gmx_fsync(FILE *fp)
746 /* the fahcore defines its own os-independent fsync */
748 #else /* GMX_FAHCORE */
752 /* get the file number */
761 /* do the actual fsync */
771 #endif /* GMX_FAHCORE */
773 /* We check for these error codes this way because POSIX requires them
774 to be defined, and using anything other than macros is unlikely: */
776 /* we don't want to report an error just because fsync() caught a signal.
777 For our purposes, we can just ignore this. */
778 if (rc && errno == EINTR)
784 /* we don't want to report an error just because we tried to fsync()
785 stdout, a socket or a pipe. */
786 if (rc && errno == EINVAL)
794 void gmx_chdir(const char *directory)
796 #if GMX_NATIVE_WINDOWS
797 int rc = _chdir(directory);
799 int rc = chdir(directory);
803 gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s",
804 directory, strerror(errno));
808 void gmx_getcwd(char *buffer, size_t size)
810 #if GMX_NATIVE_WINDOWS
811 char *pdum = _getcwd(buffer, size);
813 char *pdum = getcwd(buffer, size);
817 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s",