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