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