Apply re-formatting to C++ in src/ tree.
[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,
385                   fn.c_str());
386     }
387
388     return buf;
389 }
390
391 void make_backup(const std::string& name)
392 {
393     if (s_maxBackupCount <= 0)
394     {
395         return;
396     }
397     if (gmx_fexist(name))
398     {
399         auto backup = backup_fn(name);
400         if (rename(name.c_str(), backup.c_str()) == 0)
401         {
402             fprintf(stderr, "\nBack Off! I just backed up %s to %s\n", name.c_str(), backup.c_str());
403         }
404         else
405         {
406             fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name.c_str(), backup.c_str());
407         }
408     }
409 }
410
411 FILE* gmx_ffopen(const std::string& file, const char* mode)
412 {
413     FILE*    ff = nullptr;
414     gmx_bool bRead;
415     int      bs;
416
417     if (file.empty())
418     {
419         return nullptr;
420     }
421
422     if (mode[0] == 'w')
423     {
424         make_backup(file);
425     }
426
427     bRead = (mode[0] == 'r' && mode[1] != '+');
428     if (!bRead || gmx_fexist(file))
429     {
430         if ((ff = fopen(file.c_str(), mode)) == nullptr)
431         {
432             gmx_file(file);
433         }
434         /* Check whether we should be using buffering (default) or not
435          * (for debugging)
436          */
437         const char* bufsize = nullptr;
438         if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
439         {
440             /* Check whether to use completely unbuffered */
441             if (bUnbuffered)
442             {
443                 bs = 0;
444             }
445             else
446             {
447                 bs = strtol(bufsize, nullptr, 10);
448             }
449             if (bs <= 0)
450             {
451                 setbuf(ff, nullptr);
452             }
453             else
454             {
455                 char* ptr;
456                 snew(ptr, bs + 8);
457                 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
458                 {
459                     gmx_file("Buffering File");
460                 }
461             }
462         }
463     }
464     else
465     {
466         std::string compressedFileName = file;
467         compressedFileName += ".Z";
468         if (gmx_fexist(compressedFileName))
469         {
470             ff = uncompress(compressedFileName, mode);
471         }
472         else
473         {
474             compressedFileName = file;
475             compressedFileName += ".gz";
476             if (gmx_fexist(compressedFileName))
477             {
478                 ff = gunzip(compressedFileName, mode);
479             }
480             else
481             {
482                 gmx_file(file);
483             }
484         }
485     }
486     return ff;
487 }
488
489 namespace gmx
490 {
491
492 std::string findLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
493 {
494     std::string result;
495     try
496     {
497         const DataFileFinder& finder = getLibraryFileFinder();
498         result                       = finder.findFile(
499                 DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
500     }
501     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
502     return result;
503 }
504
505 std::string findLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
506 {
507     return findLibraryFile(std::string(filename), bAddCWD, bFatal);
508 }
509
510 FilePtr openLibraryFile(const std::string& filename, bool bAddCWD, bool bFatal)
511 {
512     FilePtr fp;
513     try
514     {
515         const DataFileFinder& finder = getLibraryFileFinder();
516         fp = finder.openFile(DataFileOptions(filename).includeCurrentDir(bAddCWD).throwIfNotFound(bFatal));
517     }
518     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
519     return fp;
520 }
521
522 FilePtr openLibraryFile(const char* filename, bool bAddCWD, bool bFatal)
523 {
524     return openLibraryFile(std::string(filename), bAddCWD, bFatal);
525 }
526
527 } // namespace gmx
528
529 /*! \brief Use mkstemp (or similar function to make a new temporary
530  * file and (on non-Windows systems) return a file descriptor to it.
531  *
532  * \todo Use std::string and std::vector<char>. */
533 static int makeTemporaryFilename(char* buf)
534 {
535     int len;
536
537     if ((len = strlen(buf)) < 7)
538     {
539         gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
540     }
541     for (int i = len - 6; (i < len); i++)
542     {
543         buf[i] = 'X';
544     }
545     /* mktemp is dangerous and we should use mkstemp instead, but
546      * since windows doesnt support it we have to separate the cases.
547      * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
548      */
549     int fd;
550 #if GMX_NATIVE_WINDOWS
551     _mktemp(buf);
552     if (buf == NULL)
553     {
554         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
555     }
556     fd = 0;
557 #else
558     fd = mkstemp(buf);
559
560     if (fd < 0)
561     {
562         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf, strerror(errno));
563     }
564 #endif
565     return fd;
566 }
567 // TODO use std::string
568 void gmx_tmpnam(char* buf)
569 {
570     int fd = makeTemporaryFilename(buf);
571 #if !GMX_NATIVE_WINDOWS
572     close(fd);
573 #endif
574 }
575
576 // TODO use std::string
577 FILE* gmx_fopen_temporary(char* buf)
578 {
579     FILE* fpout = nullptr;
580     int   fd    = makeTemporaryFilename(buf);
581
582 #if GMX_NATIVE_WINDOWS
583     if ((fpout = fopen(buf, "w")) == NULL)
584     {
585         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
586     }
587 #else
588     if ((fpout = fdopen(fd, "w")) == nullptr)
589     {
590         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
591     }
592 #endif
593
594     return fpout;
595 }
596
597 int gmx_file_rename(const char* oldname, const char* newname)
598 {
599 #if !GMX_NATIVE_WINDOWS
600     /* under unix, rename() is atomic (at least, it should be). */
601     return rename(oldname, newname);
602 #else
603     if (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
604     {
605 #    if GMX_FAHCORE
606         /* This just lets the F@H checksumming system know about the rename */
607         fcRename(oldname, newname);
608 #    endif
609         return 0;
610     }
611     else
612     {
613         return 1;
614     }
615 #endif
616 }
617
618 int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty)
619 {
620     gmx::FilePtr in(fopen(oldname, "rb"));
621     if (!in)
622     {
623         return 1;
624     }
625
626     /* If we don't copy when empty, we postpone opening the file
627        until we're actually ready to write. */
628     gmx::FilePtr out;
629     if (copy_if_empty)
630     {
631         out.reset(fopen(newname, "wb"));
632         if (!out)
633         {
634             return 1;
635         }
636     }
637
638     /* the full copy buffer size: */
639     constexpr int     FILECOPY_BUFSIZE = 1 << 16;
640     std::vector<char> buf(FILECOPY_BUFSIZE);
641
642     while (!feof(in.get()))
643     {
644         size_t nread;
645
646         nread = fread(buf.data(), sizeof(char), FILECOPY_BUFSIZE, in.get());
647         if (nread > 0)
648         {
649             size_t ret;
650             if (!out)
651             {
652                 /* so this is where we open when copy_if_empty is false:
653                    here we know we read something. */
654                 out.reset(fopen(newname, "wb"));
655                 if (!out)
656                 {
657                     return 1;
658                 }
659             }
660             ret = fwrite(buf.data(), sizeof(char), nread, out.get());
661             if (ret != nread)
662             {
663                 return 1;
664             }
665         }
666         if (ferror(in.get()))
667         {
668             return 1;
669         }
670     }
671     return 0;
672 }
673
674
675 int gmx_fsync(FILE* fp)
676 {
677     int rc = 0;
678
679     {
680         int fn;
681
682         /* get the file number */
683 #if HAVE_FILENO
684         fn = fileno(fp);
685 #elif HAVE__FILENO
686         fn = _fileno(fp);
687 #else
688         GMX_UNUSED_VALUE(fp);
689         fn = -1;
690 #endif
691
692         /* do the actual fsync */
693         if (fn >= 0)
694         {
695 #if HAVE_FSYNC
696             rc = fsync(fn);
697 #elif HAVE__COMMIT
698             rc = _commit(fn);
699 #endif
700         }
701     }
702
703     /* We check for these error codes this way because POSIX requires them
704        to be defined, and using anything other than macros is unlikely: */
705 #ifdef EINTR
706     /* we don't want to report an error just because fsync() caught a signal.
707        For our purposes, we can just ignore this. */
708     if (rc && errno == EINTR)
709     {
710         rc = 0;
711     }
712 #endif
713 #ifdef EINVAL
714     /* we don't want to report an error just because we tried to fsync()
715        stdout, a socket or a pipe. */
716     if (rc && errno == EINVAL)
717     {
718         rc = 0;
719     }
720 #endif
721     return rc;
722 }
723
724 void gmx_chdir(const char* directory)
725 {
726 #if GMX_NATIVE_WINDOWS
727     int rc = _chdir(directory);
728 #else
729     int   rc   = chdir(directory);
730 #endif
731     if (rc != 0)
732     {
733         auto message = gmx::formatString(
734                 "Cannot change directory to '%s'. Reason: %s", directory, strerror(errno));
735         GMX_THROW(gmx::FileIOError(message));
736     }
737 }
738
739 void gmx_getcwd(char* buffer, size_t size)
740 {
741 #if GMX_NATIVE_WINDOWS
742     char* pdum = _getcwd(buffer, size);
743 #else
744     char* pdum = getcwd(buffer, size);
745 #endif
746     if (pdum == nullptr)
747     {
748         gmx_fatal(FARGS, "Cannot get working directory. Reason: %s", strerror(errno));
749     }
750 }