Avoid stack overflow on Windows with CMake > 2.8.10.2
[alexxy/gromacs.git] / src / gmxlib / gmxfio.c
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, 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 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <ctype.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #ifdef HAVE_IO_H
45 #include <io.h>
46 #endif
47
48 #include "gmx_fatal.h"
49 #include "macros.h"
50 #include "smalloc.h"
51 #include "futil.h"
52 #include "filenm.h"
53 #include "string2.h"
54 #include "gmxfio.h"
55 #include "md5.h"
56
57 #ifdef GMX_THREAD_MPI
58 #include "thread_mpi.h"
59 #endif
60
61 #include "gmxfio_int.h"
62
63 /* This is the new improved and thread safe version of gmxfio. */
64
65
66
67 /* the list of open files is a linked list, with a dummy element at its head;
68        it is initialized when the first file is opened. */
69 static t_fileio *open_files = NULL;
70
71
72 #ifdef GMX_THREAD_MPI
73 /* this mutex locks the open_files structure so that no two threads can
74    modify it.
75
76    For now, we use this as a coarse grained lock on all file
77    insertion/deletion operations because it makes avoiding deadlocks
78    easier, and adds almost no overhead: the only overhead is during
79    opening and closing of files, or during global operations like
80    iterating along all open files. All these cases should be rare
81    during the simulation. */
82 static tMPI_Thread_mutex_t open_file_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
83 #endif
84
85
86 /* These simple lists define the I/O type for these files */
87 static const int ftpXDR[] =
88 { efTPR, efTRR, efEDR, efXTC, efMTX, efCPT };
89 static const int ftpASC[] =
90 { efTPA, efGRO, efPDB };
91 static const int ftpBIN[] =
92 { efTPB, efTRJ };
93
94 const char *itemstr[eitemNR] =
95 {
96     "[header]", "[inputrec]", "[box]", "[topology]", "[coordinates]",
97     "[velocities]", "[forces]"
98 };
99
100 const char *eioNames[eioNR] =
101 {
102     "REAL", "INT", "GMX_STE_T", "UCHAR", "NUCHAR", "USHORT", "RVEC", "NRVEC",
103     "IVEC", "STRING"
104 };
105
106
107
108 /* Comment strings for TPA only */
109 const char *comment_str[eitemNR] = {
110     "; The header holds information on the number of atoms etc. and on whether\n"
111     "; certain items are present in the file or not.\n"
112     "; \n"
113     ";                             WARNING\n"
114     ";                   DO NOT EDIT THIS FILE BY HAND\n"
115     "; The GROMACS preprocessor performs a lot of checks on your input that\n"
116     "; you ignore when editing this. Your simulation may crash because of this\n",
117     "; The inputrec holds the parameters for MD such as the number of steps,\n"
118     "; the timestep and the cut-offs.\n",
119     "; The simulation box in nm.\n",
120     "; The topology section describes the topology of the molecules\n"
121     "; i.e. bonds, angles and dihedrals etc. and also holds the force field\n"
122     "; parameters.\n",
123     "; The atomic coordinates in nm\n",
124     "; The atomic velocities in nm/ps\n",
125     "; The forces on the atoms in nm/ps^2\n"
126 };
127
128
129
130
131 /******************************************************************
132  *
133  * Internal functions:
134  *
135  ******************************************************************/
136
137 static int gmx_fio_int_flush(t_fileio* fio)
138 {
139     int rc = 0;
140
141     if (fio->fp)
142     {
143         rc = fflush(fio->fp);
144     }
145     else if (fio->xdr)
146     {
147         rc = fflush((FILE *) fio->xdr->x_private);
148     }
149
150     return rc;
151 }
152
153 /* returns TRUE if the file type ftp is in the set set */
154 static gmx_bool in_ftpset(int ftp, int nset, const int set[])
155 {
156     int      i;
157     gmx_bool bResult;
158
159     bResult = FALSE;
160     for (i = 0; (i < nset); i++)
161     {
162         if (ftp == set[i])
163         {
164             bResult = TRUE;
165         }
166     }
167
168     return bResult;
169 }
170
171
172
173 extern void gmx_fio_set_comment(t_fileio *fio, const char *comment)
174 {
175     fio->comment = comment;
176 }
177
178 extern void gmx_fio_unset_comment(t_fileio *fio)
179 {
180     fio->comment = NULL;
181 }
182
183
184 const char *gmx_fio_dbgstr(t_fileio *fio, const char *desc, char *buf)
185 {
186     if (!fio->bDebug)
187     {
188         /* set to empty string */
189         buf[0] = 0;
190     }
191     else
192     {
193         snprintf(buf, GMX_FIO_BUFLEN, "  ; %s %s", fio->comment ? fio->comment : "", desc);
194     }
195     return buf;
196 }
197
198
199 /* check the number of items given against the type */
200 void gmx_fio_check_nitem(t_fileio *fio, int eio, int nitem, const char *file,
201                          int line)
202 {
203     if ((nitem != 1) && !((eio == eioNRVEC) || (eio == eioNUCHAR)))
204     {
205         gmx_fatal(FARGS,
206                   "nitem (%d) may differ from 1 only for %s or %s, not   for %s"
207                   "(%s, %d)", nitem, eioNames[eioNUCHAR], eioNames[eioNRVEC],
208                   eioNames[eio], file, line);
209     }
210 }
211
212
213 /* output a data type error. */
214 void gmx_fio_fe(t_fileio *fio, int eio, const char *desc,
215                 const char *srcfile, int line)
216 {
217
218     gmx_fatal(FARGS, "Trying to %s %s type %d (%s), src %s, line %d",
219               fio->bRead ? "read" : "write", desc, eio,
220               ((eio >= 0) && (eio < eioNR)) ? eioNames[eio] : "unknown",
221               srcfile, line);
222 }
223
224
225 /* set the reader/writer functions based on the file type */
226 static void gmx_fio_set_iotype(t_fileio *fio)
227 {
228     if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
229     {
230 #ifdef USE_XDR
231         fio->iotp = &xdr_iotype;
232 #else
233         gmx_fatal(FARGS, "Sorry, no XDR");
234 #endif
235     }
236     else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
237     {
238         fio->iotp = &asc_iotype;
239     }
240     else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
241     {
242         fio->iotp = &bin_iotype;
243     }
244     else
245     {
246         fio->iotp = &dummy_iotype;
247     }
248 }
249
250
251 /* lock the mutex associated with this fio. This needs to be done for every
252    type of access to the fio's elements. */
253 void gmx_fio_lock(t_fileio *fio)
254 {
255 #ifdef GMX_THREAD_MPI
256     tMPI_Lock_lock(&(fio->mtx));
257 #endif
258 }
259 /* unlock the mutex associated with this fio.  */
260 void gmx_fio_unlock(t_fileio *fio)
261 {
262 #ifdef GMX_THREAD_MPI
263     tMPI_Lock_unlock(&(fio->mtx));
264 #endif
265 }
266
267 /* make a dummy head element, assuming we locked everything. */
268 static void gmx_fio_make_dummy(void)
269 {
270     if (!open_files)
271     {
272         snew(open_files, 1);
273         open_files->fp   = NULL;
274         open_files->fn   = NULL;
275         open_files->next = open_files;
276         open_files->prev = open_files;
277 #ifdef GMX_THREAD_MPI
278         tMPI_Lock_init(&(open_files->mtx));
279 #endif
280     }
281 }
282
283
284
285
286
287
288
289 /***********************************************************************
290  *
291  * FILE LIST OPERATIONS
292  *
293  ***********************************************************************/
294
295
296 /* insert a new t_fileio into the list */
297 static void gmx_fio_insert(t_fileio *fio)
298 {
299     t_fileio *prev;
300 #ifdef GMX_THREAD_MPI
301     /* first lock the big open_files mutex. */
302     tMPI_Thread_mutex_lock(&open_file_mutex);
303 #endif
304     /* now check whether the dummy element has been allocated,
305        and allocate it if it hasn't */
306     gmx_fio_make_dummy();
307
308     /* and lock the fio we got and the list's head **/
309     gmx_fio_lock(fio);
310     gmx_fio_lock(open_files);
311     prev = open_files->prev;
312     /* lock the element after the current one */
313     if (prev != open_files)
314     {
315         gmx_fio_lock(prev);
316     }
317
318     /* now do the actual insertion: */
319     fio->next        = open_files;
320     open_files->prev = fio;
321     prev->next       = fio;
322     fio->prev        = prev;
323
324     /* now unlock all our locks */
325     if (prev != open_files)
326     {
327         gmx_fio_unlock(prev);
328     }
329     gmx_fio_unlock(open_files);
330     gmx_fio_unlock(fio);
331
332 #ifdef GMX_THREAD_MPI
333     /* now unlock the big open_files mutex.  */
334     tMPI_Thread_mutex_unlock(&open_file_mutex);
335 #endif
336 }
337
338 /* remove a t_fileio into the list. We assume the fio is locked, and we leave
339    it locked.
340    NOTE: We also assume that the open_file_mutex has been locked */
341 static void gmx_fio_remove(t_fileio *fio)
342 {
343     t_fileio *prev;
344
345     /* lock prev, because we're changing it */
346     gmx_fio_lock(fio->prev);
347
348     /* now set the prev's pointer */
349     fio->prev->next = fio->next;
350     gmx_fio_unlock(fio->prev);
351
352     /* with the next ptr, we can simply lock while the original was locked */
353     gmx_fio_lock(fio->next);
354     fio->next->prev = fio->prev;
355     gmx_fio_unlock(fio->next);
356
357     /* and make sure we point nowhere in particular */
358     fio->next = fio->prev = fio;
359 }
360
361
362 /* get the first open file, or NULL if there is none.
363    Returns a locked fio. */
364 static t_fileio *gmx_fio_get_first(void)
365 {
366     t_fileio *ret;
367     /* first lock the big open_files mutex and the dummy's mutex */
368
369 #ifdef GMX_THREAD_MPI
370     /* first lock the big open_files mutex. */
371     tMPI_Thread_mutex_lock(&open_file_mutex);
372 #endif
373     gmx_fio_make_dummy();
374
375     gmx_fio_lock(open_files);
376     ret = open_files->next;
377
378
379     /* check whether there were any to begin with */
380     if (ret == open_files)
381     {
382         /* after this, the open_file pointer should never change */
383         ret = NULL;
384     }
385     else
386     {
387         gmx_fio_lock(open_files->next);
388     }
389     gmx_fio_unlock(open_files);
390
391
392     return ret;
393 }
394
395 /* get the next open file, or NULL if there is none.
396    Unlocks the previous fio and locks the next one. */
397 static t_fileio *gmx_fio_get_next(t_fileio *fio)
398 {
399     t_fileio *ret;
400
401     ret = fio->next;
402     /* check if that was the last one */
403     if (fio->next == open_files)
404     {
405         ret = NULL;
406 #ifdef GMX_THREAD_MPI
407         tMPI_Thread_mutex_unlock(&open_file_mutex);
408 #endif
409     }
410     else
411     {
412         gmx_fio_lock(ret);
413     }
414     gmx_fio_unlock(fio);
415
416     return ret;
417 }
418
419 /* Stop looping through the open_files.  Unlocks the global lock. */
420 static void gmx_fio_stop_getting_next(t_fileio *fio)
421 {
422     gmx_fio_unlock(fio);
423 #ifdef GMX_THREAD_MPI
424     tMPI_Thread_mutex_unlock(&open_file_mutex);
425 #endif
426 }
427
428
429
430
431 /*****************************************************************
432  *
433  *                     EXPORTED SECTION
434  *
435  *****************************************************************/
436 t_fileio *gmx_fio_open(const char *fn, const char *mode)
437 {
438     t_fileio *fio = NULL;
439     int       i;
440     char      newmode[5];
441     gmx_bool  bRead, bReadWrite;
442     int       xdrid;
443
444     if (fn2ftp(fn) == efTPA)
445     {
446         strcpy(newmode, mode);
447     }
448     else
449     {
450         /* sanitize the mode string */
451         if (strncmp(mode, "r+", 2) == 0)
452         {
453             strcpy(newmode, "r+");
454         }
455         else if (mode[0] == 'r')
456         {
457             strcpy(newmode, "r");
458         }
459         else if (strncmp(mode, "w+", 2) == 0)
460         {
461             strcpy(newmode, "w+");
462         }
463         else if (mode[0] == 'w')
464         {
465             strcpy(newmode, "w");
466         }
467         else if (strncmp(mode, "a+", 2) == 0)
468         {
469             strcpy(newmode, "a+");
470         }
471         else if (mode[0] == 'a')
472         {
473             strcpy(newmode, "a");
474         }
475         else
476         {
477             gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'", mode);
478         }
479     }
480
481     /* Check if it should be opened as a binary file */
482     if (strncmp(ftp2ftype(fn2ftp(fn)), "ASCII", 5))
483     {
484         /* Not ascii, add b to file mode */
485         if ((strchr(newmode, 'b') == NULL) && (strchr(newmode, 'B') == NULL))
486         {
487             strcat(newmode, "b");
488         }
489     }
490
491     snew(fio, 1);
492 #ifdef GMX_THREAD_MPI
493     tMPI_Lock_init(&(fio->mtx));
494 #endif
495     bRead      = (newmode[0] == 'r' && newmode[1] != '+');
496     bReadWrite = (newmode[1] == '+');
497     fio->fp    = NULL;
498     fio->xdr   = NULL;
499     if (fn)
500     {
501         fio->iFTP   = fn2ftp(fn);
502         fio->fn     = strdup(fn);
503         fio->bStdio = FALSE;
504
505         /* If this file type is in the list of XDR files, open it like that */
506         if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
507         {
508             /* First check whether we have to make a backup,
509              * only for writing, not for read or append.
510              */
511             if (newmode[0] == 'w')
512             {
513 #ifndef GMX_FAHCORE
514                 /* only make backups for normal gromacs */
515                 make_backup(fn);
516 #endif
517             }
518             else
519             {
520                 /* Check whether file exists */
521                 if (!gmx_fexist(fn))
522                 {
523                     gmx_open(fn);
524                 }
525             }
526             /* Open the file */
527             fio->fp = ffopen(fn, newmode);
528
529             /* determine the XDR direction */
530             if (newmode[0] == 'w' || newmode[0] == 'a')
531             {
532                 fio->xdrmode = XDR_ENCODE;
533             }
534             else
535             {
536                 fio->xdrmode = XDR_DECODE;
537             }
538
539             snew(fio->xdr, 1);
540             xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
541         }
542         else
543         {
544             /* If it is not, open it as a regular file */
545             fio->fp = ffopen(fn, newmode);
546         }
547
548         /* for appending seek to end of file to make sure ftell gives correct position
549          * important for checkpointing */
550         if (newmode[0] == 'a')
551         {
552             gmx_fseek(fio->fp, 0, SEEK_END);
553         }
554     }
555     else
556     {
557         /* Use stdin/stdout for I/O */
558         fio->iFTP   = efTPA;
559         fio->fp     = bRead ? stdin : stdout;
560         fio->fn     = strdup("STDIO");
561         fio->bStdio = TRUE;
562     }
563     fio->bRead             = bRead;
564     fio->bReadWrite        = bReadWrite;
565     fio->bDouble           = (sizeof(real) == sizeof(double));
566     fio->bDebug            = FALSE;
567     fio->bOpen             = TRUE;
568     fio->bLargerThan_off_t = FALSE;
569
570     /* set the reader/writer functions */
571     gmx_fio_set_iotype(fio);
572
573     /* and now insert this file into the list of open files. */
574     gmx_fio_insert(fio);
575     return fio;
576 }
577
578 static int gmx_fio_close_locked(t_fileio *fio)
579 {
580     int rc = 0;
581
582     if (!fio->bOpen)
583     {
584         gmx_fatal(FARGS, "File %s closed twice!\n", fio->fn);
585     }
586
587     if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
588     {
589         xdr_destroy(fio->xdr);
590         sfree(fio->xdr);
591     }
592
593     /* Don't close stdin and stdout! */
594     if (!fio->bStdio && fio->fp != NULL)
595     {
596         rc = ffclose(fio->fp); /* fclose returns 0 if happy */
597
598     }
599     fio->bOpen = FALSE;
600
601     return rc;
602 }
603
604 int gmx_fio_close(t_fileio *fio)
605 {
606     int rc = 0;
607
608 #ifdef GMX_THREAD_MPI
609     /* first lock the big open_files mutex. */
610     /* We don't want two processes operating on the list at the same time */
611     tMPI_Thread_mutex_lock(&open_file_mutex);
612 #endif
613
614     gmx_fio_lock(fio);
615     /* first remove it from the list */
616     gmx_fio_remove(fio);
617     rc = gmx_fio_close_locked(fio);
618     gmx_fio_unlock(fio);
619
620     sfree(fio);
621
622 #ifdef GMX_THREAD_MPI
623     tMPI_Thread_mutex_unlock(&open_file_mutex);
624 #endif
625
626     return rc;
627 }
628
629 /* close only fp but keep FIO entry. */
630 int gmx_fio_fp_close(t_fileio *fio)
631 {
632     int rc = 0;
633     gmx_fio_lock(fio);
634     if (!in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR) && !fio->bStdio)
635     {
636         rc      = ffclose(fio->fp); /* fclose returns 0 if happy */
637         fio->fp = NULL;
638     }
639     gmx_fio_unlock(fio);
640
641     return rc;
642 }
643
644 FILE * gmx_fio_fopen(const char *fn, const char *mode)
645 {
646     FILE     *fp, *ret;
647     t_fileio *fio;
648
649     fio = gmx_fio_open(fn, mode);
650     gmx_fio_lock(fio);
651     ret = fio->fp;
652     gmx_fio_unlock(fio);
653
654     return ret;
655 }
656
657 int gmx_fio_fclose(FILE *fp)
658 {
659     t_fileio *cur;
660     t_fileio *found = NULL;
661     int       rc    = -1;
662
663     cur = gmx_fio_get_first();
664     while (cur)
665     {
666         if (cur->fp == fp)
667         {
668             rc = gmx_fio_close_locked(cur);
669             gmx_fio_remove(cur);
670             gmx_fio_stop_getting_next(cur);
671             break;
672         }
673         cur = gmx_fio_get_next(cur);
674     }
675
676     return rc;
677 }
678
679 /* internal variant of get_file_md5 that operates on a locked file */
680 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
681                                     unsigned char digest[])
682 {
683     /*1MB: large size important to catch almost identical files */
684 #define CPT_CHK_LEN  1048576
685     md5_state_t    state;
686     unsigned char *buf;
687     gmx_off_t      read_len;
688     gmx_off_t      seek_offset;
689     int            ret = -1;
690
691     seek_offset = offset - CPT_CHK_LEN;
692     if (seek_offset < 0)
693     {
694         seek_offset = 0;
695     }
696     read_len = offset - seek_offset;
697
698
699     if (fio->fp && fio->bReadWrite)
700     {
701         ret = gmx_fseek(fio->fp, seek_offset, SEEK_SET);
702         if (ret)
703         {
704             gmx_fseek(fio->fp, 0, SEEK_END);
705         }
706     }
707     if (ret) /*either no fp, not readwrite, or fseek not successful */
708     {
709         return -1;
710     }
711
712     snew(buf, CPT_CHK_LEN);
713     /* the read puts the file position back to offset */
714     if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
715     {
716         /* not fatal: md5sum check to prevent overwriting files
717          * works (less safe) without
718          * */
719         if (ferror(fio->fp))
720         {
721             fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
722                     strerror(errno));
723         }
724         else if (feof(fio->fp))
725         {
726             /*
727              * For long runs that checkpoint frequently but write e.g. logs
728              * infrequently we don't want to issue lots of warnings before we
729              * have written anything to the log.
730              */
731             if (0)
732             {
733                 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
734             }
735         }
736         else
737         {
738             fprintf(
739                     stderr,
740                     "\nTrying to get md5sum: Unknown reason for short read: %s\n",
741                     fio->fn);
742         }
743
744         gmx_fseek(fio->fp, 0, SEEK_END);
745
746         ret = -1;
747     }
748     gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
749                                         it gives problems otherwise*/
750
751     if (debug)
752     {
753         fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
754     }
755
756     if (!ret)
757     {
758         md5_init(&state);
759         md5_append(&state, buf, read_len);
760         md5_finish(&state, digest);
761         ret = read_len;
762     }
763     sfree(buf);
764     return ret;
765 }
766
767
768 /*
769  * fio: file to compute md5 for
770  * offset: starting pointer of region to use for md5
771  * digest: return array of md5 sum
772  */
773 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
774                          unsigned char digest[])
775 {
776     int ret;
777
778     gmx_fio_lock(fio);
779     ret = gmx_fio_int_get_file_md5(fio, offset, digest);
780     gmx_fio_unlock(fio);
781
782     return ret;
783 }
784
785 /* The fio_mutex should ALWAYS be locked when this function is called */
786 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
787 {
788     char buf[STRLEN];
789
790     /* Flush the file, so we are sure it is written */
791     if (gmx_fio_int_flush(fio))
792     {
793         char buf[STRLEN];
794         sprintf(
795                 buf,
796                 "Cannot write file '%s'; maybe you are out of disk space?",
797                 fio->fn);
798         gmx_file(buf);
799     }
800
801     /* We cannot count on XDR being able to write 64-bit integers,
802        so separate into high/low 32-bit values.
803        In case the filesystem has 128-bit offsets we only care
804        about the first 64 bits - we'll have to fix
805        this when exabyte-size output files are common...
806      */
807     *offset = gmx_ftell(fio->fp);
808
809     return 0;
810 }
811
812 int gmx_fio_check_file_position(t_fileio *fio)
813 {
814     /* If gmx_off_t is 4 bytes we can not store file offset > 2 GB.
815      * If we do not have ftello, we will play it safe.
816      */
817 #if (SIZEOF_GMX_OFF_T == 4 || !defined HAVE_FSEEKO)
818     gmx_off_t offset;
819
820     gmx_fio_lock(fio);
821     gmx_fio_int_get_file_position(fio, &offset);
822     /* We have a 4 byte offset,
823      * make sure that we will detect out of range for all possible cases.
824      */
825     if (offset < 0 || offset > 2147483647)
826     {
827         fio->bLargerThan_off_t = TRUE;
828     }
829     gmx_fio_unlock(fio);
830 #endif
831
832     return 0;
833 }
834
835 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
836                                       int                  *p_nfiles)
837 {
838     int                   i, nfiles, rc, nalloc;
839     int                   pos_hi, pos_lo;
840     long                  pos;
841     gmx_file_position_t * outputfiles;
842     char                  buf[STRLEN];
843     t_fileio             *cur;
844
845     nfiles = 0;
846
847     /* pre-allocate 100 files */
848     nalloc = 100;
849     snew(outputfiles, nalloc);
850
851     cur = gmx_fio_get_first();
852     while (cur)
853     {
854         /* Skip the checkpoint files themselves, since they could be open when
855            we call this routine... */
856         /* also skip debug files (shoud be the only iFTP==efNR) */
857         if (cur->bOpen &&
858             !cur->bRead &&
859             !cur->bStdio &&
860             cur->iFTP != efCPT &&
861             cur->iFTP != efNR)
862         {
863             int ret;
864             /* This is an output file currently open for writing, add it */
865             if (nfiles == nalloc)
866             {
867                 nalloc += 100;
868                 srenew(outputfiles, nalloc);
869             }
870
871             strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
872
873             /* Get the file position */
874             if (cur->bLargerThan_off_t)
875             {
876                 /* -1 signals out of range */
877                 outputfiles[nfiles].offset      = -1;
878                 outputfiles[nfiles].chksum_size = -1;
879             }
880             else
881             {
882                 gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
883 #ifndef GMX_FAHCORE
884                 outputfiles[nfiles].chksum_size
885                     = gmx_fio_int_get_file_md5(cur,
886                                                outputfiles[nfiles].offset,
887                                                outputfiles[nfiles].chksum);
888 #endif
889             }
890
891             nfiles++;
892         }
893
894         cur = gmx_fio_get_next(cur);
895     }
896     *p_nfiles      = nfiles;
897     *p_outputfiles = outputfiles;
898
899     return 0;
900 }
901
902
903 void gmx_fio_checktype(t_fileio *fio)
904 {
905     if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
906     {
907         return;
908     }
909     else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
910     {
911         return;
912     }
913     else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
914     {
915         return;
916     }
917     else
918     {
919         gmx_fatal(FARGS, "Can not read/write topologies to file type %s",
920                   ftp2ext(fio->iFTP));
921     }
922
923 }
924
925
926 void gmx_fio_setprecision(t_fileio *fio, gmx_bool bDouble)
927 {
928     gmx_fio_lock(fio);
929     fio->bDouble = bDouble;
930     gmx_fio_unlock(fio);
931 }
932
933 gmx_bool gmx_fio_getdebug(t_fileio *fio)
934 {
935     gmx_bool ret;
936
937     gmx_fio_lock(fio);
938     ret = fio->bDebug;
939     gmx_fio_unlock(fio);
940
941     return ret;
942 }
943
944 void gmx_fio_setdebug(t_fileio *fio, gmx_bool bDebug)
945 {
946     gmx_fio_lock(fio);
947     fio->bDebug = bDebug;
948     gmx_fio_unlock(fio);
949 }
950
951 char *gmx_fio_getname(t_fileio *fio)
952 {
953     char *ret;
954     gmx_fio_lock(fio);
955     ret = fio->fn;
956     gmx_fio_unlock(fio);
957
958     return ret;
959 }
960
961 int gmx_fio_getftp(t_fileio* fio)
962 {
963     int ret;
964
965     gmx_fio_lock(fio);
966     ret = fio->iFTP;
967     gmx_fio_unlock(fio);
968
969     return ret;
970 }
971
972 void gmx_fio_rewind(t_fileio* fio)
973 {
974     gmx_fio_lock(fio);
975
976     if (fio->xdr)
977     {
978         xdr_destroy(fio->xdr);
979         frewind(fio->fp);
980         xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
981     }
982     else
983     {
984         frewind(fio->fp);
985     }
986     gmx_fio_unlock(fio);
987 }
988
989
990 int gmx_fio_flush(t_fileio* fio)
991 {
992     int ret;
993
994     gmx_fio_lock(fio);
995     ret = gmx_fio_int_flush(fio);
996     gmx_fio_unlock(fio);
997
998     return ret;
999 }
1000
1001
1002
1003 static int gmx_fio_int_fsync(t_fileio *fio)
1004 {
1005     int rc    = 0;
1006     int filen = -1;
1007
1008
1009     if (fio->fp)
1010     {
1011         rc = gmx_fsync(fio->fp);
1012     }
1013     else if (fio->xdr) /* this should normally not happen */
1014     {
1015         rc = gmx_fsync((FILE*) fio->xdr->x_private);
1016         /* ^ is this actually OK? */
1017     }
1018
1019     return rc;
1020 }
1021
1022
1023 int gmx_fio_fsync(t_fileio *fio)
1024 {
1025     int rc;
1026
1027     gmx_fio_lock(fio);
1028     rc = gmx_fio_int_fsync(fio);
1029     gmx_fio_unlock(fio);
1030
1031     return rc;
1032 }
1033
1034
1035
1036 t_fileio *gmx_fio_all_output_fsync(void)
1037 {
1038     t_fileio *ret = NULL;
1039     t_fileio *cur;
1040
1041     cur = gmx_fio_get_first();
1042     while (cur)
1043     {
1044         /* skip debug files (shoud be the only iFTP==efNR) */
1045         if (cur->bOpen &&
1046             !cur->bRead &&
1047             !cur->bStdio &&
1048             cur->iFTP != efNR)
1049         {
1050             /* if any of them fails, return failure code */
1051             int rc = gmx_fio_int_fsync(cur);
1052             if (rc != 0 && !ret)
1053             {
1054                 ret = cur;
1055             }
1056         }
1057         cur = gmx_fio_get_next(cur);
1058     }
1059
1060     /* in addition, we force these to be written out too, if they're being
1061        redirected. We don't check for errors because errors most likely mean
1062        that they're not redirected. */
1063     fflush(stdout);
1064     fflush(stderr);
1065 #if (defined(HAVE_FSYNC))
1066     /* again, fahcore defines HAVE_FSYNC and fsync() */
1067     fsync(STDOUT_FILENO);
1068     fsync(STDERR_FILENO);
1069 #endif
1070
1071     return ret;
1072 }
1073
1074
1075 gmx_off_t gmx_fio_ftell(t_fileio* fio)
1076 {
1077     gmx_off_t ret = 0;
1078
1079     gmx_fio_lock(fio);
1080     if (fio->fp)
1081     {
1082         ret = gmx_ftell(fio->fp);
1083     }
1084     gmx_fio_unlock(fio);
1085     return ret;
1086 }
1087
1088 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
1089 {
1090     int rc;
1091
1092     gmx_fio_lock(fio);
1093     if (fio->fp)
1094     {
1095         rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
1096     }
1097     else
1098     {
1099         gmx_file(fio->fn);
1100         rc = -1;
1101     }
1102     gmx_fio_unlock(fio);
1103     return rc;
1104 }
1105
1106 FILE *gmx_fio_getfp(t_fileio *fio)
1107 {
1108     FILE *ret = NULL;
1109
1110     gmx_fio_lock(fio);
1111     if (fio->fp)
1112     {
1113         ret = fio->fp;
1114     }
1115     gmx_fio_unlock(fio);
1116     return ret;
1117 }
1118
1119 XDR *gmx_fio_getxdr(t_fileio* fio)
1120 {
1121     XDR *ret = NULL;
1122
1123     gmx_fio_lock(fio);
1124     if (fio->xdr)
1125     {
1126         ret = fio->xdr;
1127     }
1128     gmx_fio_unlock(fio);
1129
1130     return ret;
1131 }
1132
1133 gmx_bool gmx_fio_getread(t_fileio* fio)
1134 {
1135     gmx_bool ret;
1136
1137     gmx_fio_lock(fio);
1138     ret = fio->bRead;
1139     gmx_fio_unlock(fio);
1140
1141     return ret;
1142 }
1143
1144 int xtc_seek_frame(t_fileio *fio, int frame, int natoms)
1145 {
1146     int ret;
1147
1148     gmx_fio_lock(fio);
1149     ret = xdr_xtc_seek_frame(frame, fio->fp, fio->xdr, natoms);
1150     gmx_fio_unlock(fio);
1151
1152     return ret;
1153 }
1154
1155 int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
1156 {
1157     int ret;
1158
1159     gmx_fio_lock(fio);
1160     ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
1161     gmx_fio_unlock(fio);
1162
1163     return ret;
1164 }