clang-tidy modernize
[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, 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 <fcntl.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif
55 #if GMX_NATIVE_WINDOWS
56 #include <direct.h>   // For _chdir() and _getcwd()
57 #include <io.h>
58 #include <windows.h>
59 #endif
60
61 #include "gromacs/utility/cstringutil.h"
62 #include "gromacs/utility/datafilefinder.h"
63 #include "gromacs/utility/dir_separator.h"
64 #include "gromacs/utility/exceptions.h"
65 #include "gromacs/utility/fatalerror.h"
66 #include "gromacs/utility/mutex.h"
67 #include "gromacs/utility/path.h"
68 #include "gromacs/utility/programcontext.h"
69 #include "gromacs/utility/smalloc.h"
70 #include "gromacs/utility/stringutil.h"
71
72 /* we keep a linked list of all files opened through pipes (i.e.
73    compressed or .gzipped files. This way we can distinguish between them
74    without having to change the semantics of reading from/writing to files)
75  */
76 typedef struct t_pstack {
77     FILE            *fp;
78     struct t_pstack *prev;
79 } t_pstack;
80
81 static t_pstack    *pstack           = nullptr;
82 static bool         bUnbuffered      = false;
83 static int          s_maxBackupCount = 0;
84
85 /* this linked list is an intrinsically globally shared object, so we have
86    to protect it with mutexes */
87 static gmx::Mutex pstack_mutex;
88
89 using Lock = gmx::lock_guard<gmx::Mutex>;
90
91 namespace gmx
92 {
93 namespace
94 {
95 //! Global library file finder; stores the object set with setLibraryFileFinder().
96 const DataFileFinder *g_libFileFinder;
97 //! Default library file finder if nothing is set.
98 const DataFileFinder  g_defaultLibFileFinder;
99 }   // namespace
100
101 const DataFileFinder &getLibraryFileFinder()
102 {
103     if (g_libFileFinder != nullptr)
104     {
105         return *g_libFileFinder;
106     }
107     return g_defaultLibFileFinder;
108 }
109
110 void setLibraryFileFinder(const DataFileFinder *finder)
111 {
112     g_libFileFinder = finder;
113 }
114
115 } // namespace gmx
116
117 void gmx_disable_file_buffering()
118 {
119     bUnbuffered = true;
120 }
121
122 void gmx_set_max_backup_count(int count)
123 {
124     if (count < 0)
125     {
126         const char *env = getenv("GMX_MAXBACKUP");
127         if (env != nullptr)
128         {
129             // TODO: Check that the value is converted properly.
130             count = strtol(env, nullptr, 10);
131             if (count < 0)
132             {
133                 count = 0;
134             }
135         }
136         else
137         {
138             // Use a reasonably low value for countmax; we might
139             // generate 4-5 files in each round, and we don't
140             // want to hit directory limits of 1024 or 2048 files.
141             count = 99;
142         }
143     }
144     s_maxBackupCount = count;
145 }
146
147 static void push_ps(FILE *fp)
148 {
149     t_pstack *ps;
150
151     Lock      pstackLock(pstack_mutex);
152
153     snew(ps, 1);
154     ps->fp   = fp;
155     ps->prev = pstack;
156     pstack   = ps;
157 }
158
159 #ifdef GMX_FAHCORE
160 /* don't use pipes!*/
161 #define popen fah_fopen
162 #define pclose fah_fclose
163 #define SKIP_FFOPS 1
164 #else
165 #ifdef gmx_ffclose
166 #undef gmx_ffclose
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 #endif /* GMX_FAHCORE */
184
185 int gmx_ffclose(FILE *fp)
186 {
187 #ifdef SKIP_FFOPS
188     return fclose(fp);
189 #else
190     t_pstack *ps, *tmp;
191     int       ret = 0;
192
193     Lock      pstackLock(pstack_mutex);
194
195     ps = pstack;
196     if (ps == nullptr)
197     {
198         if (fp != nullptr)
199         {
200             ret = fclose(fp);
201         }
202     }
203     else if (ps->fp == fp)
204     {
205         if (fp != nullptr)
206         {
207             ret = pclose(fp);
208         }
209         pstack = pstack->prev;
210         sfree(ps);
211     }
212     else
213     {
214         while ((ps->prev != nullptr) && (ps->prev->fp != fp))
215         {
216             ps = ps->prev;
217         }
218         if ((ps->prev != nullptr) && ps->prev->fp == fp)
219         {
220             if (ps->prev->fp != nullptr)
221             {
222                 ret = pclose(ps->prev->fp);
223             }
224             tmp      = ps->prev;
225             ps->prev = ps->prev->prev;
226             sfree(tmp);
227         }
228         else
229         {
230             if (fp != nullptr)
231             {
232                 ret = fclose(fp);
233             }
234         }
235     }
236
237     return ret;
238 #endif
239 }
240
241
242 void frewind(FILE *fp)
243 {
244     Lock      pstackLock(pstack_mutex);
245
246     t_pstack *ps = pstack;
247     while (ps != nullptr)
248     {
249         if (ps->fp == fp)
250         {
251             fprintf(stderr, "Cannot rewind compressed file!\n");
252             return;
253         }
254         ps = ps->prev;
255     }
256     rewind(fp);
257 }
258
259 int gmx_fseek(FILE *stream, gmx_off_t offset, int whence)
260 {
261 #if HAVE_FSEEKO
262     return fseeko(stream, offset, whence);
263 #else
264 #if HAVE__FSEEKI64
265     return _fseeki64(stream, offset, whence);
266 #else
267     return fseek(stream, offset, whence);
268 #endif
269 #endif
270 }
271
272 gmx_off_t gmx_ftell(FILE *stream)
273 {
274 #if HAVE_FSEEKO
275     return ftello(stream);
276 #else
277 #if HAVE__FSEEKI64
278 #ifndef __MINGW32__
279     return _ftelli64(stream);
280 #else
281     return ftello64(stream);
282 #endif
283 #else
284     return ftell(stream);
285 #endif
286 #endif
287 }
288
289 int gmx_truncate(const char *filename, gmx_off_t length)
290 {
291 #if GMX_NATIVE_WINDOWS
292     FILE *fp = fopen(filename, "rb+");
293     if (fp == NULL)
294     {
295         return -1;
296     }
297 #ifdef _MSC_VER
298     int rc = _chsize_s(fileno(fp), length);
299 #else
300     int rc = _chsize(fileno(fp), length);
301 #endif
302     fclose(fp);
303     return rc;
304 #else
305     return truncate(filename, length);
306 #endif
307 }
308
309 static FILE *uncompress(const char *fn, const char *mode)
310 {
311     FILE *fp;
312     char  buf[256];
313
314     sprintf(buf, "uncompress -c < %s", fn);
315     fprintf(stderr, "Going to execute '%s'\n", buf);
316     if ((fp = popen(buf, mode)) == nullptr)
317     {
318         gmx_open(fn);
319     }
320     push_ps(fp);
321
322     return fp;
323 }
324
325 static FILE *gunzip(const char *fn, const char *mode)
326 {
327     FILE *fp;
328     char  buf[256];
329
330     sprintf(buf, "gunzip -c < %s", fn);
331     fprintf(stderr, "Going to execute '%s'\n", buf);
332     if ((fp = popen(buf, mode)) == nullptr)
333     {
334         gmx_open(fn);
335     }
336     push_ps(fp);
337
338     return fp;
339 }
340
341 gmx_bool gmx_fexist(const char *fname)
342 {
343     FILE *test;
344
345     if (fname == nullptr)
346     {
347         return FALSE;
348     }
349     test = fopen(fname, "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);
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 char *backup_fn(const char *file)
368 {
369     int          i, count = 1;
370     char        *directory, *fn;
371     char        *buf;
372
373     smalloc(buf, GMX_PATH_MAX);
374
375     for (i = strlen(file)-1; ((i > 0) && (file[i] != DIR_SEPARATOR)); i--)
376     {
377         ;
378     }
379     /* Must check whether i > 0, i.e. whether there is a directory
380      * in the file name. In that case we overwrite the / sign with
381      * a '\0' to end the directory string .
382      */
383     if (i > 0)
384     {
385         directory    = gmx_strdup(file);
386         directory[i] = '\0';
387         fn           = gmx_strdup(file+i+1);
388     }
389     else
390     {
391         directory    = gmx_strdup(".");
392         fn           = gmx_strdup(file);
393     }
394     do
395     {
396         sprintf(buf, "%s/#%s.%d#", directory, fn, count);
397         count++;
398     }
399     while ((count <= s_maxBackupCount) && gmx_fexist(buf));
400
401     /* Arbitrarily bail out */
402     if (count > s_maxBackupCount)
403     {
404         /* TODO: The error message is only accurate for code that starts with
405          * Gromacs command-line interface. */
406         gmx_fatal(FARGS, "Won't make more than %d backups of %s for you.\n"
407                   "The env.var. GMX_MAXBACKUP controls this maximum, -1 disables backups.",
408                   s_maxBackupCount, fn);
409     }
410
411     sfree(directory);
412     sfree(fn);
413
414     return buf;
415 }
416
417 void make_backup(const char *name)
418 {
419     if (s_maxBackupCount <= 0)
420     {
421         return;
422     }
423     if (gmx_fexist(name))
424     {
425         char *backup = backup_fn(name);
426         if (rename(name, backup) == 0)
427         {
428             fprintf(stderr, "\nBack Off! I just backed up %s to %s\n",
429                     name, backup);
430         }
431         else
432         {
433             fprintf(stderr, "\nSorry couldn't backup %s to %s\n", name, backup);
434         }
435         sfree(backup);
436     }
437 }
438
439 FILE *gmx_ffopen(const char *file, const char *mode)
440 {
441 #ifdef SKIP_FFOPS
442     return fopen(file, mode);
443 #else
444     FILE    *ff = nullptr;
445     char     buf[256], *bufsize = nullptr, *ptr;
446     gmx_bool bRead;
447     int      bs;
448
449     if (file == nullptr)
450     {
451         return nullptr;
452     }
453
454     if (mode[0] == 'w')
455     {
456         make_backup(file);
457     }
458
459     bRead = (mode[0] == 'r' && mode[1] != '+');
460     strcpy(buf, file);
461     if (!bRead || gmx_fexist(buf))
462     {
463         if ((ff = fopen(buf, mode)) == nullptr)
464         {
465             gmx_file(buf);
466         }
467         /* Check whether we should be using buffering (default) or not
468          * (for debugging)
469          */
470         if (bUnbuffered || ((bufsize = getenv("GMX_LOG_BUFFER")) != nullptr))
471         {
472             /* Check whether to use completely unbuffered */
473             if (bUnbuffered)
474             {
475                 bs = 0;
476             }
477             else
478             {
479                 bs = strtol(bufsize, nullptr, 10);
480             }
481             if (bs <= 0)
482             {
483                 setbuf(ff, nullptr);
484             }
485             else
486             {
487                 snew(ptr, bs+8);
488                 if (setvbuf(ff, ptr, _IOFBF, bs) != 0)
489                 {
490                     gmx_file("Buffering File");
491                 }
492             }
493         }
494     }
495     else
496     {
497         sprintf(buf, "%s.Z", file);
498         if (gmx_fexist(buf))
499         {
500             ff = uncompress(buf, mode);
501         }
502         else
503         {
504             sprintf(buf, "%s.gz", file);
505             if (gmx_fexist(buf))
506             {
507                 ff = gunzip(buf, mode);
508             }
509             else
510             {
511                 gmx_file(file);
512             }
513         }
514     }
515     return ff;
516 #endif
517 }
518
519
520 char *low_gmxlibfn(const char *file, gmx_bool bAddCWD, gmx_bool bFatal)
521 {
522     try
523     {
524         const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
525         std::string                result =
526             finder.findFile(gmx::DataFileOptions(file)
527                                 .includeCurrentDir(bAddCWD)
528                                 .throwIfNotFound(bFatal));
529         if (!result.empty())
530         {
531             return gmx_strdup(result.c_str());
532         }
533     }
534     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
535     return nullptr;
536 }
537
538 FILE *low_libopen(const char *file, gmx_bool bFatal)
539 {
540     try
541     {
542         const gmx::DataFileFinder &finder = gmx::getLibraryFileFinder();
543         FILE *fp =
544             finder.openFile(gmx::DataFileOptions(file)
545                                 .includeCurrentDir(true)
546                                 .throwIfNotFound(bFatal));
547         return fp;
548     }
549     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
550 }
551
552 char *gmxlibfn(const char *file)
553 {
554     return low_gmxlibfn(file, TRUE, TRUE);
555 }
556
557 FILE *libopen(const char *file)
558 {
559     return low_libopen(file, TRUE);
560 }
561
562 void gmx_tmpnam(char *buf)
563 {
564     int i, len;
565
566     if ((len = strlen(buf)) < 7)
567     {
568         gmx_fatal(FARGS, "Buf passed to gmx_tmpnam must be at least 7 bytes long");
569     }
570     for (i = len-6; (i < len); i++)
571     {
572         buf[i] = 'X';
573     }
574     /* mktemp is dangerous and we should use mkstemp instead, but
575      * since windows doesnt support it we have to separate the cases.
576      * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
577      */
578 #if GMX_NATIVE_WINDOWS
579     _mktemp(buf);
580     if (buf == NULL)
581     {
582         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
583                   strerror(errno));
584     }
585 #else
586     int fd = mkstemp(buf);
587
588     if (fd < 0)
589     {
590         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
591                   strerror(errno));
592     }
593     close(fd);
594 #endif
595     /* name in Buf should now be OK and file is CLOSED */
596 }
597
598 FILE *gmx_fopen_temporary(char *buf)
599 {
600     int   i, len;
601     FILE *fpout = nullptr;
602
603     if ((len = strlen(buf)) < 7)
604     {
605         gmx_fatal(FARGS, "Buf passed to gmx_fopentmp must be at least 7 bytes long");
606     }
607     for (i = len-6; (i < len); i++)
608     {
609         buf[i] = 'X';
610     }
611     /* mktemp is dangerous and we should use mkstemp instead, but
612      * since windows doesnt support it we have to separate the cases.
613      * 20090307: mktemp deprecated, use iso c++ _mktemp instead.
614      */
615 #if GMX_NATIVE_WINDOWS
616     _mktemp(buf);
617     if (buf == NULL)
618     {
619         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
620                   strerror(errno));
621     }
622     if ((fpout = fopen(buf, "w")) == NULL)
623     {
624         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
625     }
626 #else
627     int fd = mkstemp(buf);
628     if (fd < 0)
629     {
630         gmx_fatal(FARGS, "Error creating temporary file %s: %s", buf,
631                   strerror(errno));
632     }
633     if ((fpout = fdopen(fd, "w")) == nullptr)
634     {
635         gmx_fatal(FARGS, "Cannot open temporary file %s", buf);
636     }
637 #endif
638     /* name in Buf should now be OK and file is open */
639
640     return fpout;
641 }
642
643 int gmx_file_rename(const char *oldname, const char *newname)
644 {
645 #if !GMX_NATIVE_WINDOWS
646     /* under unix, rename() is atomic (at least, it should be). */
647     return rename(oldname, newname);
648 #else
649     if (MoveFileEx(oldname, newname,
650                    MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
651     {
652         return 0;
653     }
654     else
655     {
656         return 1;
657     }
658 #endif
659 }
660
661 int gmx_file_copy(const char *oldname, const char *newname, gmx_bool copy_if_empty)
662 {
663 /* the full copy buffer size: */
664 #define FILECOPY_BUFSIZE (1<<16)
665     FILE *in  = nullptr;
666     FILE *out = nullptr;
667     char *buf;
668
669     snew(buf, FILECOPY_BUFSIZE);
670
671     in = fopen(oldname, "rb");
672     if (!in)
673     {
674         goto error;
675     }
676
677     /* If we don't copy when empty, we postpone opening the file
678        until we're actually ready to write. */
679     if (copy_if_empty)
680     {
681         out = fopen(newname, "wb");
682         if (!out)
683         {
684             goto error;
685         }
686     }
687
688     while (!feof(in))
689     {
690         size_t nread;
691
692         nread = fread(buf, sizeof(char), FILECOPY_BUFSIZE, in);
693         if (nread > 0)
694         {
695             size_t ret;
696             if (!out)
697             {
698                 /* so this is where we open when copy_if_empty is false:
699                    here we know we read something. */
700                 out = fopen(newname, "wb");
701                 if (!out)
702                 {
703                     goto error;
704                 }
705             }
706             ret = fwrite(buf, sizeof(char), nread, out);
707             if (ret != nread)
708             {
709                 goto error;
710             }
711         }
712         if (ferror(in))
713         {
714             goto error;
715         }
716     }
717     sfree(buf);
718     fclose(in);
719     fclose(out);
720     return 0;
721 error:
722     sfree(buf);
723     if (in)
724     {
725         fclose(in);
726     }
727     if (out)
728     {
729         fclose(out);
730     }
731     return 1;
732 #undef FILECOPY_BUFSIZE
733 }
734
735
736 int gmx_fsync(FILE *fp)
737 {
738     int rc = 0;
739
740 #ifdef GMX_FAHCORE
741     /* the fahcore defines its own os-independent fsync */
742     rc = fah_fsync(fp);
743 #else /* GMX_FAHCORE */
744     {
745         int fn;
746
747         /* get the file number */
748 #if HAVE_FILENO
749         fn = fileno(fp);
750 #elif HAVE__FILENO
751         fn = _fileno(fp);
752 #else
753         fn = -1;
754 #endif
755
756         /* do the actual fsync */
757         if (fn >= 0)
758         {
759 #if HAVE_FSYNC
760             rc = fsync(fn);
761 #elif HAVE__COMMIT
762             rc = _commit(fn);
763 #endif
764         }
765     }
766 #endif /* GMX_FAHCORE */
767
768     /* We check for these error codes this way because POSIX requires them
769        to be defined, and using anything other than macros is unlikely: */
770 #ifdef EINTR
771     /* we don't want to report an error just because fsync() caught a signal.
772        For our purposes, we can just ignore this. */
773     if (rc && errno == EINTR)
774     {
775         rc = 0;
776     }
777 #endif
778 #ifdef EINVAL
779     /* we don't want to report an error just because we tried to fsync()
780        stdout, a socket or a pipe. */
781     if (rc && errno == EINVAL)
782     {
783         rc = 0;
784     }
785 #endif
786     return rc;
787 }
788
789 void gmx_chdir(const char *directory)
790 {
791 #if GMX_NATIVE_WINDOWS
792     int rc = _chdir(directory);
793 #else
794     int rc = chdir(directory);
795 #endif
796     if (rc != 0)
797     {
798         gmx_fatal(FARGS, "Cannot change directory to '%s'. Reason: %s",
799                   directory, strerror(errno));
800     }
801 }
802
803 void gmx_getcwd(char *buffer, size_t size)
804 {
805 #if GMX_NATIVE_WINDOWS
806     char *pdum = _getcwd(buffer, size);
807 #else
808     char *pdum = getcwd(buffer, size);
809 #endif
810     if (pdum == nullptr)
811     {
812         gmx_fatal(FARGS, "Cannot get working directory. Reason: %s",
813                   strerror(errno));
814     }
815 }