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