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