Fix GPU X buffer ops with empty domain
[gromacs.git] / src / gromacs / utility / futil.cpp
blob99cb8f97b1a60e0777b77c7ca811d732a3f239ea
1 /*
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.
37 #include "gmxpre.h"
39 #include "futil.h"
41 #include "config.h"
43 #include <cerrno>
44 #include <cstdio>
45 #include <cstdlib>
46 #include <cstring>
48 #include <tuple>
50 #include <fcntl.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #if GMX_NATIVE_WINDOWS
58 #include <direct.h> // For _chdir() and _getcwd()
59 #include <io.h>
60 #include <windows.h>
61 #endif
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 {
79 FILE *fp;
80 struct t_pstack *prev;
81 } t_pstack;
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>;
93 namespace gmx
95 namespace
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;
101 } // namespace
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;
117 } // namespace gmx
119 void gmx_disable_file_buffering()
121 bUnbuffered = true;
124 void gmx_set_max_backup_count(int count)
126 if (count < 0)
128 const char *env = getenv("GMX_MAXBACKUP");
129 if (env != nullptr)
131 // TODO: Check that the value is converted properly.
132 count = strtol(env, nullptr, 10);
133 if (count < 0)
135 count = 0;
138 else
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.
143 count = 99;
146 s_maxBackupCount = count;
149 static void push_ps(FILE *fp)
151 t_pstack *ps;
153 Lock pstackLock(pstack_mutex);
155 snew(ps, 1);
156 ps->fp = fp;
157 ps->prev = pstack;
158 pstack = ps;
161 #if GMX_FAHCORE
162 /* don't use pipes!*/
163 #define popen fah_fopen
164 #define pclose fah_fclose
165 #define SKIP_FFOPS 1
166 #else
167 #ifdef gmx_ffclose
168 #undef gmx_ffclose
169 #endif
170 #if (!HAVE_PIPES && !defined(__native_client__))
171 static FILE *popen(const char *nm, const char *mode)
173 gmx_impl("Sorry no pipes...");
175 return NULL;
178 static int pclose(FILE *fp)
180 gmx_impl("Sorry no pipes...");
182 return 0;
184 #endif /* !HAVE_PIPES && !defined(__native_client__) */
185 #endif /* GMX_FAHCORE */
187 int gmx_ffclose(FILE *fp)
189 #ifdef SKIP_FFOPS
190 return fclose(fp);
191 #else
192 t_pstack *ps, *tmp;
193 int ret = 0;
195 Lock pstackLock(pstack_mutex);
197 ps = pstack;
198 if (ps == nullptr)
200 if (fp != nullptr)
202 ret = fclose(fp);
205 else if (ps->fp == fp)
207 if (fp != nullptr)
209 ret = pclose(fp);
211 pstack = pstack->prev;
212 sfree(ps);
214 else
216 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
218 ps = ps->prev;
220 if ((ps->prev != nullptr) && ps->prev->fp == fp)
222 if (ps->prev->fp != nullptr)
224 ret = pclose(ps->prev->fp);
226 tmp = ps->prev;
227 ps->prev = ps->prev->prev;
228 sfree(tmp);
230 else
232 if (fp != nullptr)
234 ret = fclose(fp);
239 return ret;
240 #endif
244 void frewind(FILE *fp)
246 Lock pstackLock(pstack_mutex);
248 t_pstack *ps = pstack;
249 while (ps != nullptr)
251 if (ps->fp == fp)
253 fprintf(stderr, "Cannot rewind compressed file!\n");
254 return;
256 ps = ps->prev;
258 rewind(fp);
261 int gmx_fseek(FILE *stream, gmx_off_t offset, int whence)
263 #if HAVE_FSEEKO
264 return fseeko(stream, offset, whence);
265 #else
266 #if HAVE__FSEEKI64
267 return _fseeki64(stream, offset, whence);
268 #else
269 return fseek(stream, offset, whence);
270 #endif
271 #endif
274 gmx_off_t gmx_ftell(FILE *stream)
276 #if HAVE_FSEEKO
277 return ftello(stream);
278 #else
279 #if HAVE__FSEEKI64
280 #ifndef __MINGW32__
281 return _ftelli64(stream);
282 #else
283 return ftello64(stream);
284 #endif
285 #else
286 return ftell(stream);
287 #endif
288 #endif
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+");
295 if (fp == NULL)
297 return -1;
299 #ifdef _MSC_VER
300 int rc = _chsize_s(fileno(fp), length);
301 #else
302 int rc = _chsize(fileno(fp), length);
303 #endif
304 fclose(fp);
305 return rc;
306 #else
307 return truncate(filename.c_str(), length);
308 #endif
311 static FILE *uncompress(const std::string &fn, const char *mode)
313 FILE *fp;
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)
318 gmx_open(fn);
320 push_ps(fp);
322 return fp;
325 static FILE *gunzip(const std::string &fn, const char *mode)
327 FILE *fp;
328 std::string buf = "gunzip -c < ";
329 buf += fn;
330 fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
331 if ((fp = popen(buf.c_str(), mode)) == nullptr)
333 gmx_open(fn);
335 push_ps(fp);
337 return fp;
340 gmx_bool gmx_fexist(const std::string &fname)
342 FILE *test;
344 if (fname.empty())
346 return FALSE;
348 test = fopen(fname.c_str(), "r");
349 if (test == nullptr)
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);
355 #else
356 return FALSE;
357 #endif
359 else
361 fclose(test);
362 return TRUE;
366 static std::string backup_fn(const std::string &file)
368 int count = 1;
370 std::string directory = gmx::Path::getParentPath(file);
371 std::string fn = gmx::Path::getFilename(file);
372 std::string buf;
373 if (directory.empty())
375 directory = ".";
379 buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
380 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());
394 return buf;
397 void make_backup(const std::string &name)
399 if (s_maxBackupCount <= 0)
401 return;
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());
411 else
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)
420 #ifdef SKIP_FFOPS
421 return fopen(file, mode);
422 #else
423 FILE *ff = nullptr;
424 gmx_bool bRead;
425 int bs;
427 if (file.empty())
429 return nullptr;
432 if (mode[0] == 'w')
434 make_backup(file);
437 bRead = (mode[0] == 'r' && mode[1] != '+');
438 if (!bRead || gmx_fexist(file))
440 if ((ff = fopen(file.c_str(), mode)) == nullptr)
442 gmx_file(file);
444 /* Check whether we should be using buffering (default) or not
445 * (for debugging)
447 const char *bufsize = nullptr;
448 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
450 /* Check whether to use completely unbuffered */
451 if (bUnbuffered)
453 bs = 0;
455 else
457 bs = strtol(bufsize, nullptr, 10);
459 if (bs <= 0)
461 setbuf(ff, nullptr);
463 else
465 char *ptr;
466 snew(ptr, bs+8);
467 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
469 gmx_file("Buffering File");
474 else
476 std::string compressedFileName = file;
477 compressedFileName += ".Z";
478 if (gmx_fexist(compressedFileName))
480 ff = uncompress(compressedFileName, mode);
482 else
484 compressedFileName = file;
485 compressedFileName += ".gz";
486 if (gmx_fexist(compressedFileName))
488 ff = gunzip(compressedFileName, mode);
490 else
492 gmx_file(file);
496 return ff;
497 #endif
500 namespace gmx
503 std::string findLibraryFile(const std::string &filename, bool bAddCWD, bool bFatal)
505 std::string result;
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;
514 return result;
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)
524 FilePtr fp;
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;
533 return fp;
536 FilePtr openLibraryFile(const char *filename, bool bAddCWD, bool bFatal)
538 return openLibraryFile(std::string(filename), bAddCWD, bFatal);
541 } // namespace gmx
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)
549 int len;
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++)
557 buf[i] = 'X';
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.
563 int fd;
564 #if GMX_NATIVE_WINDOWS
565 _mktemp(buf);
566 if (buf == NULL)
568 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
569 strerror(errno));
571 fd = 0;
572 #else
573 fd = mkstemp(buf);
575 if (fd < 0)
577 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
578 strerror(errno));
580 #endif
581 return fd;
583 // TODO use std::string
584 void gmx_tmpnam(char *buf)
586 int fd = makeTemporaryFilename(buf);
587 #if !GMX_NATIVE_WINDOWS
588 close(fd);
589 #endif
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);
603 #else
604 if ((fpout = fdopen(fd, "w")) == nullptr)
606 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
608 #endif
610 return fpout;
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);
618 #else
619 if (MoveFileEx(oldname, newname,
620 MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
622 return 0;
624 else
626 return 1;
628 #endif
631 int gmx_file_copy(const char *oldname, const char *newname, gmx_bool copy_if_empty)
633 gmx::FilePtr in(fopen(oldname, "rb"));
634 if (!in)
636 return 1;
639 /* If we don't copy when empty, we postpone opening the file
640 until we're actually ready to write. */
641 gmx::FilePtr out;
642 if (copy_if_empty)
644 out.reset(fopen(newname, "wb"));
645 if (!out)
647 return 1;
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()))
657 size_t nread;
659 nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
660 if (nread > 0)
662 size_t ret;
663 if (!out)
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"));
668 if (!out)
670 return 1;
673 ret = fwrite(buf.data(), sizeof(char), nread, out.get());
674 if (ret != nread)
676 return 1;
679 if (ferror(in.get()))
681 return 1;
684 return 0;
688 int gmx_fsync(FILE *fp)
690 int rc = 0;
692 #if GMX_FAHCORE
693 /* the fahcore defines its own os-independent fsync */
694 rc = fah_fsync(fp);
695 #else /* GMX_FAHCORE */
697 int fn;
699 /* get the file number */
700 #if HAVE_FILENO
701 fn = fileno(fp);
702 #elif HAVE__FILENO
703 fn = _fileno(fp);
704 #else
705 fn = -1;
706 #endif
708 /* do the actual fsync */
709 if (fn >= 0)
711 #if HAVE_FSYNC
712 rc = fsync(fn);
713 #elif HAVE__COMMIT
714 rc = _commit(fn);
715 #endif
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: */
722 #ifdef EINTR
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)
727 rc = 0;
729 #endif
730 #ifdef EINVAL
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)
735 rc = 0;
737 #endif
738 return rc;
741 void gmx_chdir(const char *directory)
743 #if GMX_NATIVE_WINDOWS
744 int rc = _chdir(directory);
745 #else
746 int rc = chdir(directory);
747 #endif
748 if (rc != 0)
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);
759 #else
760 char *pdum = getcwd(buffer, size);
761 #endif
762 if (pdum == nullptr)
764 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s",
765 strerror(errno));