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