More fixes from clang-tidy-7
[gromacs.git] / src / gromacs / utility / futil.cpp
bloba98e3d01c364b802d7f7a563701b4677873c4a55
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, 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 <fcntl.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #if GMX_NATIVE_WINDOWS
56 #include <direct.h> // For _chdir() and _getcwd()
57 #include <io.h>
58 #include <windows.h>
59 #endif
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 {
77 FILE *fp;
78 struct t_pstack *prev;
79 } t_pstack;
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>;
91 namespace gmx
93 namespace
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;
99 } // namespace
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;
115 } // namespace gmx
117 void gmx_disable_file_buffering()
119 bUnbuffered = true;
122 void gmx_set_max_backup_count(int count)
124 if (count < 0)
126 const char *env = getenv("GMX_MAXBACKUP");
127 if (env != nullptr)
129 // TODO: Check that the value is converted properly.
130 count = strtol(env, nullptr, 10);
131 if (count < 0)
133 count = 0;
136 else
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.
141 count = 99;
144 s_maxBackupCount = count;
147 static void push_ps(FILE *fp)
149 t_pstack *ps;
151 Lock pstackLock(pstack_mutex);
153 snew(ps, 1);
154 ps->fp = fp;
155 ps->prev = pstack;
156 pstack = ps;
159 #ifdef GMX_FAHCORE
160 /* don't use pipes!*/
161 #define popen fah_fopen
162 #define pclose fah_fclose
163 #define SKIP_FFOPS 1
164 #else
165 #ifdef gmx_ffclose
166 #undef gmx_ffclose
167 #endif
168 #if (!HAVE_PIPES && !defined(__native_client__))
169 static FILE *popen(const char *nm, const char *mode)
171 gmx_impl("Sorry no pipes...");
173 return NULL;
176 static int pclose(FILE *fp)
178 gmx_impl("Sorry no pipes...");
180 return 0;
182 #endif /* !HAVE_PIPES && !defined(__native_client__) */
183 #endif /* GMX_FAHCORE */
185 int gmx_ffclose(FILE *fp)
187 #ifdef SKIP_FFOPS
188 return fclose(fp);
189 #else
190 t_pstack *ps, *tmp;
191 int ret = 0;
193 Lock pstackLock(pstack_mutex);
195 ps = pstack;
196 if (ps == nullptr)
198 if (fp != nullptr)
200 ret = fclose(fp);
203 else if (ps->fp == fp)
205 if (fp != nullptr)
207 ret = pclose(fp);
209 pstack = pstack->prev;
210 sfree(ps);
212 else
214 while ((ps->prev != nullptr) && (ps->prev->fp != fp))
216 ps = ps->prev;
218 if ((ps->prev != nullptr) && ps->prev->fp == fp)
220 if (ps->prev->fp != nullptr)
222 ret = pclose(ps->prev->fp);
224 tmp = ps->prev;
225 ps->prev = ps->prev->prev;
226 sfree(tmp);
228 else
230 if (fp != nullptr)
232 ret = fclose(fp);
237 return ret;
238 #endif
242 void frewind(FILE *fp)
244 Lock pstackLock(pstack_mutex);
246 t_pstack *ps = pstack;
247 while (ps != nullptr)
249 if (ps->fp == fp)
251 fprintf(stderr, "Cannot rewind compressed file!\n");
252 return;
254 ps = ps->prev;
256 rewind(fp);
259 int gmx_fseek(FILE *stream, gmx_off_t offset, int whence)
261 #if HAVE_FSEEKO
262 return fseeko(stream, offset, whence);
263 #else
264 #if HAVE__FSEEKI64
265 return _fseeki64(stream, offset, whence);
266 #else
267 return fseek(stream, offset, whence);
268 #endif
269 #endif
272 gmx_off_t gmx_ftell(FILE *stream)
274 #if HAVE_FSEEKO
275 return ftello(stream);
276 #else
277 #if HAVE__FSEEKI64
278 #ifndef __MINGW32__
279 return _ftelli64(stream);
280 #else
281 return ftello64(stream);
282 #endif
283 #else
284 return ftell(stream);
285 #endif
286 #endif
289 int gmx_truncate(const char *filename, gmx_off_t length)
291 #if GMX_NATIVE_WINDOWS
292 FILE *fp = fopen(filename, "rb+");
293 if (fp == NULL)
295 return -1;
297 #ifdef _MSC_VER
298 int rc = _chsize_s(fileno(fp), length);
299 #else
300 int rc = _chsize(fileno(fp), length);
301 #endif
302 fclose(fp);
303 return rc;
304 #else
305 return truncate(filename, length);
306 #endif
309 static FILE *uncompress(const char *fn, const char *mode)
311 FILE *fp;
312 char buf[256];
314 sprintf(buf, "uncompress -c < %s", fn);
315 fprintf(stderr, "Going to execute '%s'\n", buf);
316 if ((fp = popen(buf, mode)) == nullptr)
318 gmx_open(fn);
320 push_ps(fp);
322 return fp;
325 static FILE *gunzip(const char *fn, const char *mode)
327 FILE *fp;
328 char buf[256];
330 sprintf(buf, "gunzip -c < %s", fn);
331 fprintf(stderr, "Going to execute '%s'\n", buf);
332 if ((fp = popen(buf, mode)) == nullptr)
334 gmx_open(fn);
336 push_ps(fp);
338 return fp;
341 gmx_bool gmx_fexist(const char *fname)
343 FILE *test;
345 if (fname == nullptr)
347 return FALSE;
349 test = fopen(fname, "r");
350 if (test == nullptr)
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);
356 #else
357 return FALSE;
358 #endif
360 else
362 fclose(test);
363 return TRUE;
367 static char *backup_fn(const char *file)
369 int i, count = 1;
370 char *directory, *fn;
371 char *buf;
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 .
383 if (i > 0)
385 directory = gmx_strdup(file);
386 directory[i] = '\0';
387 fn = gmx_strdup(file+i+1);
389 else
391 directory = gmx_strdup(".");
392 fn = gmx_strdup(file);
396 sprintf(buf, "%s/#%s.%d#", directory, fn, count);
397 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);
411 sfree(directory);
412 sfree(fn);
414 return buf;
417 void make_backup(const char *name)
419 if (s_maxBackupCount <= 0)
421 return;
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",
429 name, backup);
431 else
433 fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name, backup);
435 sfree(backup);
439 FILE *gmx_ffopen(const char *file, const char *mode)
441 #ifdef SKIP_FFOPS
442 return fopen(file, mode);
443 #else
444 FILE *ff = nullptr;
445 char buf[256], *bufsize = nullptr, *ptr;
446 gmx_bool bRead;
447 int bs;
449 if (file == nullptr)
451 return nullptr;
454 if (mode[0] == 'w')
456 make_backup(file);
459 bRead = (mode[0] == 'r' && mode[1] != '+');
460 strcpy(buf, file);
461 if (!bRead || gmx_fexist(buf))
463 if ((ff = fopen(buf, mode)) == nullptr)
465 gmx_file(buf);
467 /* Check whether we should be using buffering (default) or not
468 * (for debugging)
470 if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
472 /* Check whether to use completely unbuffered */
473 if (bUnbuffered)
475 bs = 0;
477 else
479 bs = strtol(bufsize, nullptr, 10);
481 if (bs <= 0)
483 setbuf(ff, nullptr);
485 else
487 snew(ptr, bs+8);
488 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
490 gmx_file("Buffering File");
495 else
497 sprintf(buf, "%s.Z", file);
498 if (gmx_fexist(buf))
500 ff = uncompress(buf, mode);
502 else
504 sprintf(buf, "%s.gz", file);
505 if (gmx_fexist(buf))
507 ff = gunzip(buf, mode);
509 else
511 gmx_file(file);
515 return ff;
516 #endif
520 char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal)
524 const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
525 std::string result =
526 finder.findFile(gmx::DataFileOptions(file)
527 .includeCurrentDir(bAddCWD)
528 .throwIfNotFound(bFatal));
529 if (!result.empty())
531 return gmx_strdup(result.c_str());
534 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
535 return nullptr;
538 FILE *low_libopen(const char *file, gmx_bool bFatal)
542 const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
543 FILE *fp =
544 finder.openFile(gmx::DataFileOptions(file)
545 .includeCurrentDir(true)
546 .throwIfNotFound(bFatal));
547 return fp;
549 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
552 char *gmxlibfn(const char *file)
554 return low_gmxlibfn(file, TRUE, TRUE);
557 FILE *libopen(const char *file)
559 return low_libopen(file, TRUE);
562 void gmx_tmpnam(char *buf)
564 int i, len;
566 if ((len = strlen(buf)) < 7)
568 gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
570 for (i = len-6; (i < len); i++)
572 buf[i] = 'X';
574 /* mktemp is dangerous and we should use mkstemp instead, but
575 * since windows doesnt support it we have to separate the cases.
576 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
578 #if GMX_NATIVE_WINDOWS
579 _mktemp(buf);
580 if (buf == NULL)
582 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
583 strerror(errno));
585 #else
586 int fd = mkstemp(buf);
588 if (fd < 0)
590 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
591 strerror(errno));
593 close(fd);
594 #endif
595 /* name in Buf should now be OK and file is CLOSED */
598 FILE *gmx_fopen_temporary(char *buf)
600 int i, len;
601 FILE *fpout = nullptr;
603 if ((len = strlen(buf)) < 7)
605 gmx_fatal(FARGS, "Buf passed to gmx_fopentmp must be at least 7 bytes long");
607 for (i = len-6; (i < len); i++)
609 buf[i] = 'X';
611 /* mktemp is dangerous and we should use mkstemp instead, but
612 * since windows doesnt support it we have to separate the cases.
613 * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
615 #if GMX_NATIVE_WINDOWS
616 _mktemp(buf);
617 if (buf == NULL)
619 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
620 strerror(errno));
622 if ((fpout = fopen(buf, "w")) == NULL)
624 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
626 #else
627 int fd = mkstemp(buf);
628 if (fd < 0)
630 gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
631 strerror(errno));
633 if ((fpout = fdopen(fd, "w")) == nullptr)
635 gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
637 #endif
638 /* name in Buf should now be OK and file is open */
640 return fpout;
643 int gmx_file_rename(const char *oldname, const char *newname)
645 #if !GMX_NATIVE_WINDOWS
646 /* under unix, rename() is atomic (at least, it should be). */
647 return rename(oldname, newname);
648 #else
649 if (MoveFileEx(oldname, newname,
650 MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
652 return 0;
654 else
656 return 1;
658 #endif
661 int gmx_file_copy(const char *oldname, const char *newname, gmx_bool copy_if_empty)
663 /* the full copy buffer size: */
664 #define FILECOPY_BUFSIZE (1<<16)
665 FILE *in = nullptr;
666 FILE *out = nullptr;
667 char *buf;
669 snew(buf, FILECOPY_BUFSIZE);
671 in = fopen(oldname, "rb");
672 if (!in)
674 goto error;
677 /* If we don't copy when empty, we postpone opening the file
678 until we're actually ready to write. */
679 if (copy_if_empty)
681 out = fopen(newname, "wb");
682 if (!out)
684 goto error;
688 while (!feof(in))
690 size_t nread;
692 nread = fread(buf, sizeof(char), FILECOPY_BUFSIZE, in);
693 if (nread > 0)
695 size_t ret;
696 if (!out)
698 /* so this is where we open when copy_if_empty is false:
699 here we know we read something. */
700 out = fopen(newname, "wb");
701 if (!out)
703 goto error;
706 ret = fwrite(buf, sizeof(char), nread, out);
707 if (ret != nread)
709 goto error;
712 if (ferror(in))
714 goto error;
717 sfree(buf);
718 fclose(in);
719 fclose(out);
720 return 0;
721 error:
722 sfree(buf);
723 if (in)
725 fclose(in);
727 if (out)
729 fclose(out);
731 return 1;
732 #undef FILECOPY_BUFSIZE
736 int gmx_fsync(FILE *fp)
738 int rc = 0;
740 #ifdef GMX_FAHCORE
741 /* the fahcore defines its own os-independent fsync */
742 rc = fah_fsync(fp);
743 #else /* GMX_FAHCORE */
745 int fn;
747 /* get the file number */
748 #if HAVE_FILENO
749 fn = fileno(fp);
750 #elif HAVE__FILENO
751 fn = _fileno(fp);
752 #else
753 fn = -1;
754 #endif
756 /* do the actual fsync */
757 if (fn >= 0)
759 #if HAVE_FSYNC
760 rc = fsync(fn);
761 #elif HAVE__COMMIT
762 rc = _commit(fn);
763 #endif
766 #endif /* GMX_FAHCORE */
768 /* We check for these error codes this way because POSIX requires them
769 to be defined, and using anything other than macros is unlikely: */
770 #ifdef EINTR
771 /* we don't want to report an error just because fsync() caught a signal.
772 For our purposes, we can just ignore this. */
773 if (rc && errno == EINTR)
775 rc = 0;
777 #endif
778 #ifdef EINVAL
779 /* we don't want to report an error just because we tried to fsync()
780 stdout, a socket or a pipe. */
781 if (rc && errno == EINVAL)
783 rc = 0;
785 #endif
786 return rc;
789 void gmx_chdir(const char *directory)
791 #if GMX_NATIVE_WINDOWS
792 int rc = _chdir(directory);
793 #else
794 int rc = chdir(directory);
795 #endif
796 if (rc != 0)
798 gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s",
799 directory, strerror(errno));
803 void gmx_getcwd(char *buffer, size_t size)
805 #if GMX_NATIVE_WINDOWS
806 char *pdum = _getcwd(buffer, size);
807 #else
808 char *pdum = getcwd(buffer, size);
809 #endif
810 if (pdum == nullptr)
812 gmx_fatal(FARGS, "Cannot get working directory. Reason: %s",
813 strerror(errno));