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