Prepare for GROMACS 2020.5
[alexxy/gromacs.git] / src / gromacs / utility / futil.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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,2020, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "futil.h"
40
41 #include "config.h"
42
43 #include <cerrno>
44 #include <cstdio>
45 #include <cstdlib>
46 #include <cstring>
47
48 #include <tuple>
49
50 #include <fcntl.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53
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
62
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"
73
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)
77  */
78 typedef struct t_pstack
79 {
80     FILE*            fp;
81     struct t_pstack* prev;
82 } t_pstack;
83
84 static t_pstack* pstack           = nullptr;
85 static bool      bUnbuffered      = false;
86 static int       s_maxBackupCount = 0;
87
88 /* this linked list is an intrinsically globally shared object, so we have
89    to protect it with mutexes */
90 static gmx::Mutex pstack_mutex;
91
92 using Lock = gmx::lock_guard<gmx::Mutex>;
93
94 namespace gmx
95 {
96 namespace
97 {
98 //! Global library file finder; stores the object set with setLibraryFileFinder().
99 const DataFileFinder* g_libFileFinder;
100 //! Default library file finder if nothing is set.
101 const DataFileFinder g_defaultLibFileFinder;
102 } // namespace
103
104 const DataFileFinder& getLibraryFileFinder()
105 {
106     if (g_libFileFinder != nullptr)
107     {
108         return *g_libFileFinder;
109     }
110     return g_defaultLibFileFinder;
111 }
112
113 void setLibraryFileFinder(const DataFileFinder* finder)
114 {
115     g_libFileFinder = finder;
116 }
117
118 } // namespace gmx
119
120 void gmx_disable_file_buffering()
121 {
122     bUnbuffered = true;
123 }
124
125 void gmx_set_max_backup_count(int count)
126 {
127     if (count < 0)
128     {
129         const char* env = getenv("GMX_MAXBACKUP");
130         if (env != nullptr)
131         {
132             // TODO: Check that the value is converted properly.
133             count = strtol(env, nullptr, 10);
134             if (count < 0)
135             {
136                 count = 0;
137             }
138         }
139         else
140         {
141             // Use a reasonably low value for countmax; we might
142             // generate 4-5 files in each round, and we don't
143             // want to hit directory limits of 1024 or 2048 files.
144             count = 99;
145         }
146     }
147     s_maxBackupCount = count;
148 }
149
150 static void push_ps(FILE* fp)
151 {
152     t_pstack* ps;
153
154     Lock pstackLock(pstack_mutex);
155
156     snew(ps, 1);
157     ps->fp   = fp;
158     ps->prev = pstack;
159     pstack   = ps;
160 }
161
162 #if GMX_FAHCORE
163 #    ifdef gmx_ffclose
164 #        undef gmx_ffclose
165 #    endif
166 #endif
167 #if (!HAVE_PIPES && !defined(__native_client__))
168 static FILE* popen(const char* nm, const char* mode)
169 {
170     gmx_impl("Sorry no pipes...");
171
172     return NULL;
173 }
174
175 static int pclose(FILE* fp)
176 {
177     gmx_impl("Sorry no pipes...");
178
179     return 0;
180 }
181 #endif /* !HAVE_PIPES && !defined(__native_client__) */
182
183 int gmx_ffclose(FILE* fp)
184 {
185     t_pstack *ps, *tmp;
186     int       ret = 0;
187
188     Lock pstackLock(pstack_mutex);
189
190     ps = pstack;
191     if (ps == nullptr)
192     {
193         if (fp != nullptr)
194         {
195             ret = fclose(fp);
196         }
197     }
198     else if (ps->fp == fp)
199     {
200         if (fp != nullptr)
201         {
202             ret = pclose(fp);
203         }
204         pstack = pstack->prev;
205         sfree(ps);
206     }
207     else
208     {
209         while ((ps->prev != nullptr) && (ps->prev->fp != fp))
210         {
211             ps = ps->prev;
212         }
213         if ((ps->prev != nullptr) && ps->prev->fp == fp)
214         {
215             if (ps->prev->fp != nullptr)
216             {
217                 ret = pclose(ps->prev->fp);
218             }
219             tmp      = ps->prev;
220             ps->prev = ps->prev->prev;
221             sfree(tmp);
222         }
223         else
224         {
225             if (fp != nullptr)
226             {
227                 ret = fclose(fp);
228             }
229         }
230     }
231
232     return ret;
233 }
234
235
236 void frewind(FILE* fp)
237 {
238     Lock pstackLock(pstack_mutex);
239
240     t_pstack* ps = pstack;
241     while (ps != nullptr)
242     {
243         if (ps->fp == fp)
244         {
245             fprintf(stderr, "Cannot rewind compressed file!\n");
246             return;
247         }
248         ps = ps->prev;
249     }
250     rewind(fp);
251 }
252
253 int gmx_fseek(FILE* stream, gmx_off_t offset, int whence)
254 {
255 #if HAVE_FSEEKO
256     return fseeko(stream, offset, whence);
257 #else
258 #    if HAVE__FSEEKI64
259     return _fseeki64(stream, offset, whence);
260 #    else
261     return fseek(stream, offset, whence);
262 #    endif
263 #endif
264 }
265
266 gmx_off_t gmx_ftell(FILE* stream)
267 {
268 #if HAVE_FSEEKO
269     return ftello(stream);
270 #else
271 #    if HAVE__FSEEKI64
272 #        ifndef __MINGW32__
273     return _ftelli64(stream);
274 #        else
275     return ftello64(stream);
276 #        endif
277 #    else
278     return ftell(stream);
279 #    endif
280 #endif
281 }
282
283 int gmx_truncate(const std::string& filename, gmx_off_t length)
284 {
285 #if GMX_NATIVE_WINDOWS && !GMX_FAHCORE
286     FILE* fp = fopen(filename.c_str(), "rb+");
287     if (fp == NULL)
288     {
289         return -1;
290     }
291 #    ifdef _MSC_VER
292     int rc = _chsize_s(fileno(fp), length);
293 #    else
294     int rc = _chsize(fileno(fp), length);
295 #    endif
296     fclose(fp);
297     return rc;
298 #else
299     return truncate(filename.c_str(), length);
300 #endif
301 }
302
303 static FILE* uncompress(const std::string& fn, const char* mode)
304 {
305     FILE*       fp;
306     std::string buf = "uncompress -c < " + fn;
307     fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
308     if ((fp = popen(buf.c_str(), mode)) == nullptr)
309     {
310         gmx_open(fn);
311     }
312     push_ps(fp);
313
314     return fp;
315 }
316
317 static FILE* gunzip(const std::string& fn, const char* mode)
318 {
319     FILE*       fp;
320     std::string buf = "gunzip -c < ";
321     buf += fn;
322     fprintf(stderr, "Going to execute '%s'\n", buf.c_str());
323     if ((fp = popen(buf.c_str(), mode)) == nullptr)
324     {
325         gmx_open(fn);
326     }
327     push_ps(fp);
328
329     return fp;
330 }
331
332 gmx_bool gmx_fexist(const std::string& fname)
333 {
334     FILE* test;
335
336     if (fname.empty())
337     {
338         return FALSE;
339     }
340     test = fopen(fname.c_str(), "r");
341     if (test == nullptr)
342     {
343 /*Windows doesn't allow fopen of directory - so we need to check this seperately */
344 #if GMX_NATIVE_WINDOWS
345         DWORD attr = GetFileAttributes(fname.c_str());
346         return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
347 #else
348         return FALSE;
349 #endif
350     }
351     else
352     {
353         fclose(test);
354         return TRUE;
355     }
356 }
357
358 static std::string backup_fn(const std::string& file)
359 {
360     int count = 1;
361
362     std::string directory = gmx::Path::getParentPath(file);
363     std::string fn        = gmx::Path::getFilename(file);
364     std::string buf;
365     if (directory.empty())
366     {
367         directory = ".";
368     }
369     do
370     {
371         buf = gmx::formatString("%s/#%s.%d#", directory.c_str(), fn.c_str(), count);
372         count++;
373     } while ((count <= s_maxBackupCount) && gmx_fexist(buf));
374
375     /* Arbitrarily bail out */
376     if (count > s_maxBackupCount)
377     {
378         /* TODO: The error message is only accurate for code that starts with
379          * Gromacs command-line interface. */
380         gmx_fatal(FARGS,
381                   "Won't make more than %d backups of %s for you.\n"
382                   "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
383                   s_maxBackupCount, fn.c_str());
384     }
385
386     return buf;
387 }
388
389 void make_backup(const std::string& name)
390 {
391     if (s_maxBackupCount <= 0)
392     {
393         return;
394     }
395     if (gmx_fexist(name))
396     {
397         auto backup = backup_fn(name);
398         if (rename(name.c_str(), backup.c_str()) == 0)
399         {
400             fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
401         }
402         else
403         {
404             fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
405         }
406     }
407 }
408
409 FILE* gmx_ffopen(const std::string& file, const char* mode)
410 {
411     FILE*    ff = nullptr;
412     gmx_bool bRead;
413     int      bs;
414
415     if (file.empty())
416     {
417         return nullptr;
418     }
419
420     if (mode[0] == 'w')
421     {
422         make_backup(file);
423     }
424
425     bRead = (mode[0] == 'r' && mode[1] != '+');
426     if (!bRead || gmx_fexist(file))
427     {
428         if ((ff = fopen(file.c_str(), mode)) == nullptr)
429         {
430             gmx_file(file);
431         }
432         /* Check whether we should be using buffering (default) or not
433          * (for debugging)
434          */
435         const char* bufsize = nullptr;
436         if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
437         {
438             /* Check whether to use completely unbuffered */
439             if (bUnbuffered)
440             {
441                 bs = 0;
442             }
443             else
444             {
445                 bs = strtol(bufsize, nullptr, 10);
446             }
447             if (bs <= 0)
448             {
449                 setbuf(ff, nullptr);
450             }
451             else
452             {
453                 char* ptr;
454                 snew(ptr, bs + 8);
455                 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
456                 {
457                     gmx_file("Buffering File");
458                 }
459             }
460         }
461     }
462     else
463     {
464         std::string compressedFileName = file;
465         compressedFileName += ".Z";
466         if (gmx_fexist(compressedFileName))
467         {
468             ff = uncompress(compressedFileName, mode);
469         }
470         else
471         {
472             compressedFileName = file;
473             compressedFileName += ".gz";
474             if (gmx_fexist(compressedFileName))
475             {
476                 ff = gunzip(compressedFileName, mode);
477             }
478             else
479             {
480                 gmx_file(file);
481             }
482         }
483     }
484     return ff;
485 }
486
487 namespace gmx
488 {
489
490 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
491 {
492     std::string result;
493     try
494     {
495         const DataFileFinder& finder = getLibraryFileFinder();
496         result                       = finder.findFile(
497                 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
498     }
499     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
500     return result;
501 }
502
503 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
504 {
505     return findLibraryFile(std::string(filename), bAddCWD, bFatal);
506 }
507
508 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
509 {
510     FilePtr fp;
511     try
512     {
513         const DataFileFinder& finder = getLibraryFileFinder();
514         fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
515     }
516     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
517     return fp;
518 }
519
520 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
521 {
522     return openLibraryFile(std::string(filename), bAddCWD, bFatal);
523 }
524
525 } // namespace gmx
526
527 /*! \brief Use mkstemp (or similar function to make a new temporary
528  * file and (on non-Windows systems) return a file descriptor to it.
529  *
530  * \todo Use std::string and std::vector<char>. */
531 static int makeTemporaryFilename(char* buf)
532 {
533     int len;
534
535     if ((len = strlen(buf)) < 7)
536     {
537         gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
538     }
539     for (int i = len - 6; (i < len); i++)
540     {
541         buf[i] = 'X';
542     }
543     /* mktemp is dangerous and we should use mkstemp instead, but
544      * since windows doesnt support it we have to separate the cases.
545      * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
546      */
547     int fd;
548 #if GMX_NATIVE_WINDOWS
549     _mktemp(buf);
550     if (buf == NULL)
551     {
552         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
553     }
554     fd = 0;
555 #else
556     fd = mkstemp(buf);
557
558     if (fd < 0)
559     {
560         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
561     }
562 #endif
563     return fd;
564 }
565 // TODO use std::string
566 void gmx_tmpnam(char* buf)
567 {
568     int fd = makeTemporaryFilename(buf);
569 #if !GMX_NATIVE_WINDOWS
570     close(fd);
571 #endif
572 }
573
574 // TODO use std::string
575 FILE* gmx_fopen_temporary(char* buf)
576 {
577     FILE* fpout = nullptr;
578     int   fd    = makeTemporaryFilename(buf);
579
580 #if GMX_NATIVE_WINDOWS
581     if ((fpout = fopen(buf, "w")) == NULL)
582     {
583         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
584     }
585 #else
586     if ((fpout = fdopen(fd, "w")) == nullptr)
587     {
588         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
589     }
590 #endif
591
592     return fpout;
593 }
594
595 int gmx_file_rename(const char* oldname, const char* newname)
596 {
597 #if !GMX_NATIVE_WINDOWS
598     /* under unix, rename() is atomic (at least, it should be). */
599     return rename(oldname, newname);
600 #else
601     if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
602     {
603 #    if GMX_FAHCORE
604         /* This just lets the F@H checksumming system know about the rename */
605         fcRename(oldname, newname);
606 #    endif
607         return 0;
608     }
609     else
610     {
611         return 1;
612     }
613 #endif
614 }
615
616 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
617 {
618     gmx::FilePtr in(fopen(oldname, "rb"));
619     if (!in)
620     {
621         return 1;
622     }
623
624     /* If we don't copy when empty, we postpone opening the file
625        until we're actually ready to write. */
626     gmx::FilePtr out;
627     if (copy_if_empty)
628     {
629         out.reset(fopen(newname, "wb"));
630         if (!out)
631         {
632             return 1;
633         }
634     }
635
636     /* the full copy buffer size: */
637     constexpr int     FILECOPY_BUFSIZE = 1 << 16;
638     std::vector<char> buf(FILECOPY_BUFSIZE);
639
640     while (!feof(in.get()))
641     {
642         size_t nread;
643
644         nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
645         if (nread > 0)
646         {
647             size_t ret;
648             if (!out)
649             {
650                 /* so this is where we open when copy_if_empty is false:
651                    here we know we read something. */
652                 out.reset(fopen(newname, "wb"));
653                 if (!out)
654                 {
655                     return 1;
656                 }
657             }
658             ret = fwrite(buf.data(), sizeof(char), nread, out.get());
659             if (ret != nread)
660             {
661                 return 1;
662             }
663         }
664         if (ferror(in.get()))
665         {
666             return 1;
667         }
668     }
669     return 0;
670 }
671
672
673 int gmx_fsync(FILE* fp)
674 {
675     int rc = 0;
676
677     {
678         int fn;
679
680         /* get the file number */
681 #if HAVE_FILENO
682         fn = fileno(fp);
683 #elif HAVE__FILENO
684         fn = _fileno(fp);
685 #else
686         fn = -1;
687 #endif
688
689         /* do the actual fsync */
690         if (fn >= 0)
691         {
692 #if HAVE_FSYNC
693             rc = fsync(fn);
694 #elif HAVE__COMMIT
695             rc = _commit(fn);
696 #endif
697         }
698     }
699
700     /* We check for these error codes this way because POSIX requires them
701        to be defined, and using anything other than macros is unlikely: */
702 #ifdef EINTR
703     /* we don't want to report an error just because fsync() caught a signal.
704        For our purposes, we can just ignore this. */
705     if (rc && errno == EINTR)
706     {
707         rc = 0;
708     }
709 #endif
710 #ifdef EINVAL
711     /* we don't want to report an error just because we tried to fsync()
712        stdout, a socket or a pipe. */
713     if (rc && errno == EINVAL)
714     {
715         rc = 0;
716     }
717 #endif
718     return rc;
719 }
720
721 void gmx_chdir(const char* directory)
722 {
723 #if GMX_NATIVE_WINDOWS
724     int rc = _chdir(directory);
725 #else
726     int   rc   = chdir(directory);
727 #endif
728     if (rc != 0)
729     {
730         gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
731     }
732 }
733
734 void gmx_getcwd(char* buffer, size_t size)
735 {
736 #if GMX_NATIVE_WINDOWS
737     char* pdum = _getcwd(buffer, size);
738 #else
739     char* pdum = getcwd(buffer, size);
740 #endif
741     if (pdum == nullptr)
742     {
743         gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));
744     }
745 }