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