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