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
{
80 struct t_pstack
*prev
;
83 static t_pstack
*pstack
= nullptr;
84 static bool bUnbuffered
= false;
85 static int s_maxBackupCount
= 0;
87 /* this linked list is an intrinsically globally shared object, so we have
88 to protect it with mutexes */
89 static gmx::Mutex pstack_mutex
;
91 using Lock
= gmx::lock_guard
<gmx::Mutex
>;
97 //! Global library file finder; stores the object set with setLibraryFileFinder().
98 const DataFileFinder
*g_libFileFinder
;
99 //! Default library file finder if nothing is set.
100 const DataFileFinder g_defaultLibFileFinder
;
103 const DataFileFinder
&getLibraryFileFinder()
105 if (g_libFileFinder
!= nullptr)
107 return *g_libFileFinder
;
109 return g_defaultLibFileFinder
;
112 void setLibraryFileFinder(const DataFileFinder
*finder
)
114 g_libFileFinder
= finder
;
119 void gmx_disable_file_buffering()
124 void gmx_set_max_backup_count(int count
)
128 const char *env
= getenv("GMX_MAXBACKUP");
131 // TODO: Check that the value is converted properly.
132 count
= strtol(env
, nullptr, 10);
140 // Use a reasonably low value for countmax; we might
141 // generate 4-5 files in each round, and we don't
142 // want to hit directory limits of 1024 or 2048 files.
146 s_maxBackupCount
= count
;
149 static void push_ps(FILE *fp
)
153 Lock
pstackLock(pstack_mutex
);
162 /* don't use pipes!*/
163 #define popen fah_fopen
164 #define pclose fah_fclose
170 #if (!HAVE_PIPES && !defined(__native_client__))
171 static FILE *popen(const char *nm
, const char *mode
)
173 gmx_impl("Sorry no pipes...");
178 static int pclose(FILE *fp
)
180 gmx_impl("Sorry no pipes...");
184 #endif /* !HAVE_PIPES && !defined(__native_client__) */
185 #endif /* GMX_FAHCORE */
187 int gmx_ffclose(FILE *fp
)
195 Lock
pstackLock(pstack_mutex
);
205 else if (ps
->fp
== fp
)
211 pstack
= pstack
->prev
;
216 while ((ps
->prev
!= nullptr) && (ps
->prev
->fp
!= fp
))
220 if ((ps
->prev
!= nullptr) && ps
->prev
->fp
== fp
)
222 if (ps
->prev
->fp
!= nullptr)
224 ret
= pclose(ps
->prev
->fp
);
227 ps
->prev
= ps
->prev
->prev
;
244 void frewind(FILE *fp
)
246 Lock
pstackLock(pstack_mutex
);
248 t_pstack
*ps
= pstack
;
249 while (ps
!= nullptr)
253 fprintf(stderr
, "Cannot rewind compressed file!\n");
261 int gmx_fseek(FILE *stream
, gmx_off_t offset
, int whence
)
264 return fseeko(stream
, offset
, whence
);
267 return _fseeki64(stream
, offset
, whence
);
269 return fseek(stream
, offset
, whence
);
274 gmx_off_t
gmx_ftell(FILE *stream
)
277 return ftello(stream
);
281 return _ftelli64(stream
);
283 return ftello64(stream
);
286 return ftell(stream
);
291 int gmx_truncate(const std::string
&filename
, gmx_off_t length
)
293 #if GMX_NATIVE_WINDOWS
294 FILE *fp
= fopen(filename
.c_str(), "rb+");
300 int rc
= _chsize_s(fileno(fp
), length
);
302 int rc
= _chsize(fileno(fp
), length
);
307 return truncate(filename
.c_str(), length
);
311 static FILE *uncompress(const std::string
&fn
, const char *mode
)
314 std::string buf
= "uncompress -c < " + fn
;
315 fprintf(stderr
, "Going to execute '%s'\n", buf
.c_str());
316 if ((fp
= popen(buf
.c_str(), mode
)) == nullptr)
325 static FILE *gunzip(const std::string
&fn
, const char *mode
)
328 std::string buf
= "gunzip -c < ";
330 fprintf(stderr
, "Going to execute '%s'\n", buf
.c_str());
331 if ((fp
= popen(buf
.c_str(), mode
)) == nullptr)
340 gmx_bool
gmx_fexist(const std::string
&fname
)
348 test
= fopen(fname
.c_str(), "r");
351 /*Windows doesn't allow fopen of directory - so we need to check this seperately */
352 #if GMX_NATIVE_WINDOWS
353 DWORD attr
= GetFileAttributes(fname
.c_str());
354 return (attr
!= INVALID_FILE_ATTRIBUTES
) && (attr
& FILE_ATTRIBUTE_DIRECTORY
);
366 static std::string
backup_fn(const std::string
&file
)
370 std::string directory
= gmx::Path::getParentPath(file
);
371 std::string fn
= gmx::Path::getFilename(file
);
373 if (directory
.empty())
379 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. */
389 gmx_fatal(FARGS
, "Won't make more than %d backups of %s for you.\n"
390 "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
391 s_maxBackupCount
, fn
.c_str());
397 void make_backup(const std::string
&name
)
399 if (s_maxBackupCount
<= 0)
403 if (gmx_fexist(name
))
405 auto backup
= backup_fn(name
);
406 if (rename(name
.c_str(), backup
.c_str()) == 0)
408 fprintf(stderr
, "\nBack Off! I just backed up %s to %s\n",
409 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(DataFileOptions(filename
)
510 .includeCurrentDir(bAddCWD
)
511 .throwIfNotFound(bFatal
));
513 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
517 std::string
findLibraryFile(const char *filename
, bool bAddCWD
, bool bFatal
)
519 return findLibraryFile(std::string(filename
), bAddCWD
, bFatal
);
522 FilePtr
openLibraryFile(const std::string
&filename
, bool bAddCWD
, bool bFatal
)
527 const DataFileFinder
&finder
= getLibraryFileFinder();
528 fp
= finder
.openFile(DataFileOptions(filename
)
529 .includeCurrentDir(bAddCWD
)
530 .throwIfNotFound(bFatal
));
532 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
536 FilePtr
openLibraryFile(const char *filename
, bool bAddCWD
, bool bFatal
)
538 return openLibraryFile(std::string(filename
), bAddCWD
, bFatal
);
543 /*! \brief Use mkstemp (or similar function to make a new temporary
544 * file and (on non-Windows systems) return a file descriptor to it.
546 * \todo Use std::string and std::vector<char>. */
547 static int makeTemporaryFilename(char *buf
)
551 if ((len
= strlen(buf
)) < 7)
553 gmx_fatal(FARGS
, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
555 for (int i
= len
-6; (i
< len
); i
++)
559 /* mktemp is dangerous and we should use mkstemp instead, but
560 * since windows doesnt support it we have to separate the cases.
561 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
564 #if GMX_NATIVE_WINDOWS
568 gmx_fatal(FARGS
, "Error creating temporary file %s: %s", buf
,
577 gmx_fatal(FARGS
, "Error creating temporary file %s: %s", buf
,
583 // TODO use std::string
584 void gmx_tmpnam(char *buf
)
586 int fd
= makeTemporaryFilename(buf
);
587 #if !GMX_NATIVE_WINDOWS
592 // TODO use std::string
593 FILE *gmx_fopen_temporary(char *buf
)
595 FILE *fpout
= nullptr;
596 int fd
= makeTemporaryFilename(buf
);
598 #if GMX_NATIVE_WINDOWS
599 if ((fpout
= fopen(buf
, "w")) == NULL
)
601 gmx_fatal(FARGS
, "Cannot open temporary file %s", buf
);
604 if ((fpout
= fdopen(fd
, "w")) == nullptr)
606 gmx_fatal(FARGS
, "Cannot open temporary file %s", buf
);
613 int gmx_file_rename(const char *oldname
, const char *newname
)
615 #if !GMX_NATIVE_WINDOWS
616 /* under unix, rename() is atomic (at least, it should be). */
617 return rename(oldname
, newname
);
619 if (MoveFileEx(oldname
, newname
,
620 MOVEFILE_REPLACE_EXISTING
|MOVEFILE_WRITE_THROUGH
))
631 int gmx_file_copy(const char *oldname
, const char *newname
, gmx_bool copy_if_empty
)
633 gmx::FilePtr
in(fopen(oldname
, "rb"));
639 /* If we don't copy when empty, we postpone opening the file
640 until we're actually ready to write. */
644 out
.reset(fopen(newname
, "wb"));
651 /* the full copy buffer size: */
652 constexpr int FILECOPY_BUFSIZE
= 1<<16;
653 std::vector
<char> buf(FILECOPY_BUFSIZE
);
655 while (!feof(in
.get()))
659 nread
= fread(buf
.data(), sizeof(char), FILECOPY_BUFSIZE
, in
.get());
665 /* so this is where we open when copy_if_empty is false:
666 here we know we read something. */
667 out
.reset(fopen(newname
, "wb"));
673 ret
= fwrite(buf
.data(), sizeof(char), nread
, out
.get());
679 if (ferror(in
.get()))
688 int gmx_fsync(FILE *fp
)
693 /* the fahcore defines its own os-independent fsync */
695 #else /* GMX_FAHCORE */
699 /* get the file number */
708 /* do the actual fsync */
718 #endif /* GMX_FAHCORE */
720 /* We check for these error codes this way because POSIX requires them
721 to be defined, and using anything other than macros is unlikely: */
723 /* we don't want to report an error just because fsync() caught a signal.
724 For our purposes, we can just ignore this. */
725 if (rc
&& errno
== EINTR
)
731 /* we don't want to report an error just because we tried to fsync()
732 stdout, a socket or a pipe. */
733 if (rc
&& errno
== EINVAL
)
741 void gmx_chdir(const char *directory
)
743 #if GMX_NATIVE_WINDOWS
744 int rc
= _chdir(directory
);
746 int rc
= chdir(directory
);
750 gmx_fatal(FARGS
, "Cannot change directory to '%s'. Reason: %s",
751 directory
, strerror(errno
));
755 void gmx_getcwd(char *buffer
, size_t size
)
757 #if GMX_NATIVE_WINDOWS
758 char *pdum
= _getcwd(buffer
, size
);
760 char *pdum
= getcwd(buffer
, size
);
764 gmx_fatal(FARGS
, "Cannot get working directory. Reason: %s",