Merge branch 'release-4-6'
[alexxy/gromacs.git] / src / gromacs / fileio / gmxfio.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team,
6  * Copyright (c) 2013, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <ctype.h>
42 #include <stdio.h>
43 #include <errno.h>
44 #ifdef HAVE_IO_H
45 #include <io.h>
46 #endif
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #include "gmx_fatal.h"
52 #include "macros.h"
53 #include "smalloc.h"
54 #include "futil.h"
55 #include "filenm.h"
56 #include "string2.h"
57 #include "gmxfio.h"
58 #include "md5.h"
59
60 #include "gromacs/legacyheaders/thread_mpi/threads.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, efMTX, efCPT };
88 static const int ftpASC[] =
89 { efTPA, efGRO, efPDB };
90 static const int ftpBIN[] =
91 { efTPB, efTRJ };
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     = 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             /* Open the file */
517             fio->fp = ffopen(fn, newmode);
518
519             /* determine the XDR direction */
520             if (newmode[0] == 'w' || newmode[0] == 'a')
521             {
522                 fio->xdrmode = XDR_ENCODE;
523             }
524             else
525             {
526                 fio->xdrmode = XDR_DECODE;
527             }
528
529             snew(fio->xdr, 1);
530             xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
531         }
532         else
533         {
534             /* If it is not, open it as a regular file */
535             fio->fp = ffopen(fn, newmode);
536         }
537
538         /* for appending seek to end of file to make sure ftell gives correct position
539          * important for checkpointing */
540         if (newmode[0] == 'a')
541         {
542             gmx_fseek(fio->fp, 0, SEEK_END);
543         }
544     }
545     else
546     {
547         /* Use stdin/stdout for I/O */
548         fio->iFTP   = efTPA;
549         fio->fp     = bRead ? stdin : stdout;
550         fio->fn     = strdup("STDIO");
551         fio->bStdio = TRUE;
552     }
553     fio->bRead             = bRead;
554     fio->bReadWrite        = bReadWrite;
555     fio->bDouble           = (sizeof(real) == sizeof(double));
556     fio->bDebug            = FALSE;
557     fio->bOpen             = TRUE;
558     fio->bLargerThan_off_t = FALSE;
559
560     /* set the reader/writer functions */
561     gmx_fio_set_iotype(fio);
562
563     /* and now insert this file into the list of open files. */
564     gmx_fio_insert(fio);
565     return fio;
566 }
567
568 static int gmx_fio_close_locked(t_fileio *fio)
569 {
570     int rc = 0;
571
572     if (!fio->bOpen)
573     {
574         gmx_fatal(FARGS, "File %s closed twice!\n", fio->fn);
575     }
576
577     if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
578     {
579         xdr_destroy(fio->xdr);
580         sfree(fio->xdr);
581     }
582
583     /* Don't close stdin and stdout! */
584     if (!fio->bStdio && fio->fp != NULL)
585     {
586         rc = ffclose(fio->fp); /* fclose returns 0 if happy */
587
588     }
589     fio->bOpen = FALSE;
590
591     return rc;
592 }
593
594 int gmx_fio_close(t_fileio *fio)
595 {
596     int rc = 0;
597
598     /* first lock the big open_files mutex. */
599     /* We don't want two processes operating on the list at the same time */
600     tMPI_Thread_mutex_lock(&open_file_mutex);
601
602     gmx_fio_lock(fio);
603     /* first remove it from the list */
604     gmx_fio_remove(fio);
605     rc = gmx_fio_close_locked(fio);
606     gmx_fio_unlock(fio);
607
608     sfree(fio->fn);
609     sfree(fio);
610
611     tMPI_Thread_mutex_unlock(&open_file_mutex);
612
613     return rc;
614 }
615
616 /* close only fp but keep FIO entry. */
617 int gmx_fio_fp_close(t_fileio *fio)
618 {
619     int rc = 0;
620     gmx_fio_lock(fio);
621     if (!in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR) && !fio->bStdio)
622     {
623         rc      = ffclose(fio->fp); /* fclose returns 0 if happy */
624         fio->fp = NULL;
625     }
626     gmx_fio_unlock(fio);
627
628     return rc;
629 }
630
631 FILE * gmx_fio_fopen(const char *fn, const char *mode)
632 {
633     FILE     *fp, *ret;
634     t_fileio *fio;
635
636     fio = gmx_fio_open(fn, mode);
637     gmx_fio_lock(fio);
638     ret = fio->fp;
639     gmx_fio_unlock(fio);
640
641     return ret;
642 }
643
644 int gmx_fio_fclose(FILE *fp)
645 {
646     t_fileio *cur;
647     t_fileio *found = NULL;
648     int       rc    = -1;
649
650     cur = gmx_fio_get_first();
651     while (cur)
652     {
653         if (cur->fp == fp)
654         {
655             rc = gmx_fio_close_locked(cur);
656             gmx_fio_remove(cur);
657             gmx_fio_stop_getting_next(cur);
658             sfree(cur->fn);
659             sfree(cur);
660             break;
661         }
662         cur = gmx_fio_get_next(cur);
663     }
664
665     return rc;
666 }
667
668 /* internal variant of get_file_md5 that operates on a locked file */
669 static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset,
670                                     unsigned char digest[])
671 {
672     /*1MB: large size important to catch almost identical files */
673 #define CPT_CHK_LEN  1048576
674     md5_state_t   state;
675     unsigned char buf[CPT_CHK_LEN];
676     gmx_off_t     read_len;
677     gmx_off_t     seek_offset;
678     int           ret = -1;
679
680     seek_offset = offset - CPT_CHK_LEN;
681     if (seek_offset < 0)
682     {
683         seek_offset = 0;
684     }
685     read_len = offset - seek_offset;
686
687
688     if (fio->fp && fio->bReadWrite)
689     {
690         ret = gmx_fseek(fio->fp, seek_offset, SEEK_SET);
691         if (ret)
692         {
693             gmx_fseek(fio->fp, 0, SEEK_END);
694         }
695     }
696     if (ret) /*either no fp, not readwrite, or fseek not successful */
697     {
698         return -1;
699     }
700
701     /* the read puts the file position back to offset */
702     if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
703     {
704         /* not fatal: md5sum check to prevent overwriting files
705          * works (less safe) without
706          * */
707         if (ferror(fio->fp))
708         {
709             fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
710                     strerror(errno));
711         }
712         else if (feof(fio->fp))
713         {
714             /*
715              * For long runs that checkpoint frequently but write e.g. logs
716              * infrequently we don't want to issue lots of warnings before we
717              * have written anything to the log.
718              */
719             if (0)
720             {
721                 fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
722             }
723         }
724         else
725         {
726             fprintf(
727                     stderr,
728                     "\nTrying to get md5sum: Unknown reason for short read: %s\n",
729                     fio->fn);
730         }
731
732         gmx_fseek(fio->fp, 0, SEEK_END);
733
734         ret = -1;
735     }
736     gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows
737                                         it gives problems otherwise*/
738
739     if (debug)
740     {
741         fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
742     }
743
744     if (!ret)
745     {
746         md5_init(&state);
747         md5_append(&state, buf, read_len);
748         md5_finish(&state, digest);
749         return read_len;
750     }
751     else
752     {
753         return ret;
754     }
755 }
756
757
758 /*
759  * fio: file to compute md5 for
760  * offset: starting pointer of region to use for md5
761  * digest: return array of md5 sum
762  */
763 int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,
764                          unsigned char digest[])
765 {
766     int ret;
767
768     gmx_fio_lock(fio);
769     ret = gmx_fio_int_get_file_md5(fio, offset, digest);
770     gmx_fio_unlock(fio);
771
772     return ret;
773 }
774
775 /* The fio_mutex should ALWAYS be locked when this function is called */
776 static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
777 {
778     char buf[STRLEN];
779
780     /* Flush the file, so we are sure it is written */
781     if (gmx_fio_int_flush(fio))
782     {
783         char buf[STRLEN];
784         sprintf(
785                 buf,
786                 "Cannot write file '%s'; maybe you are out of disk space?",
787                 fio->fn);
788         gmx_file(buf);
789     }
790
791     /* We cannot count on XDR being able to write 64-bit integers,
792        so separate into high/low 32-bit values.
793        In case the filesystem has 128-bit offsets we only care
794        about the first 64 bits - we'll have to fix
795        this when exabyte-size output files are common...
796      */
797     *offset = gmx_ftell(fio->fp);
798
799     return 0;
800 }
801
802 int gmx_fio_check_file_position(t_fileio gmx_unused *fio)
803 {
804     /* If gmx_off_t is 4 bytes we can not store file offset > 2 GB.
805      * If we do not have ftello, we will play it safe.
806      */
807 #if (SIZEOF_GMX_OFF_T == 4 || !defined HAVE_FSEEKO)
808     gmx_off_t offset;
809
810     gmx_fio_lock(fio);
811     gmx_fio_int_get_file_position(fio, &offset);
812     /* We have a 4 byte offset,
813      * make sure that we will detect out of range for all possible cases.
814      */
815     if (offset < 0 || offset > 2147483647)
816     {
817         fio->bLargerThan_off_t = TRUE;
818     }
819     gmx_fio_unlock(fio);
820 #endif
821
822     return 0;
823 }
824
825 int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
826                                       int                  *p_nfiles)
827 {
828     int                   i, nfiles, rc, nalloc;
829     int                   pos_hi, pos_lo;
830     long                  pos;
831     gmx_file_position_t * outputfiles;
832     char                  buf[STRLEN];
833     t_fileio             *cur;
834
835     nfiles = 0;
836
837     /* pre-allocate 100 files */
838     nalloc = 100;
839     snew(outputfiles, nalloc);
840
841     cur = gmx_fio_get_first();
842     while (cur)
843     {
844         /* Skip the checkpoint files themselves, since they could be open when
845            we call this routine... */
846         /* also skip debug files (shoud be the only iFTP==efNR) */
847         if (cur->bOpen &&
848             !cur->bRead &&
849             !cur->bStdio &&
850             cur->iFTP != efCPT &&
851             cur->iFTP != efNR)
852         {
853             int ret;
854             /* This is an output file currently open for writing, add it */
855             if (nfiles == nalloc)
856             {
857                 nalloc += 100;
858                 srenew(outputfiles, nalloc);
859             }
860
861             strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
862
863             /* Get the file position */
864             if (cur->bLargerThan_off_t)
865             {
866                 /* -1 signals out of range */
867                 outputfiles[nfiles].offset      = -1;
868                 outputfiles[nfiles].chksum_size = -1;
869             }
870             else
871             {
872                 gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
873 #ifndef GMX_FAHCORE
874                 outputfiles[nfiles].chksum_size
875                     = gmx_fio_int_get_file_md5(cur,
876                                                outputfiles[nfiles].offset,
877                                                outputfiles[nfiles].chksum);
878 #endif
879             }
880
881             nfiles++;
882         }
883
884         cur = gmx_fio_get_next(cur);
885     }
886     *p_nfiles      = nfiles;
887     *p_outputfiles = outputfiles;
888
889     return 0;
890 }
891
892
893 void gmx_fio_checktype(t_fileio *fio)
894 {
895     if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
896     {
897         return;
898     }
899     else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
900     {
901         return;
902     }
903     else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
904     {
905         return;
906     }
907 #ifdef HAVE_XMl
908     else if (in_ftpset(fio->iFTP, asize(ftpXML), ftpXML))
909     {
910         return;
911     }
912 #endif
913     else
914     {
915         gmx_fatal(FARGS, "Can not read/write topologies to file type %s",
916                   ftp2ext(fio->iFTP));
917     }
918
919 }
920
921
922 void gmx_fio_setprecision(t_fileio *fio, gmx_bool bDouble)
923 {
924     gmx_fio_lock(fio);
925     fio->bDouble = bDouble;
926     gmx_fio_unlock(fio);
927 }
928
929 gmx_bool gmx_fio_getdebug(t_fileio *fio)
930 {
931     gmx_bool ret;
932
933     gmx_fio_lock(fio);
934     ret = fio->bDebug;
935     gmx_fio_unlock(fio);
936
937     return ret;
938 }
939
940 void gmx_fio_setdebug(t_fileio *fio, gmx_bool bDebug)
941 {
942     gmx_fio_lock(fio);
943     fio->bDebug = bDebug;
944     gmx_fio_unlock(fio);
945 }
946
947 char *gmx_fio_getname(t_fileio *fio)
948 {
949     char *ret;
950     gmx_fio_lock(fio);
951     ret = fio->fn;
952     gmx_fio_unlock(fio);
953
954     return ret;
955 }
956
957 int gmx_fio_getftp(t_fileio* fio)
958 {
959     int ret;
960
961     gmx_fio_lock(fio);
962     ret = fio->iFTP;
963     gmx_fio_unlock(fio);
964
965     return ret;
966 }
967
968 void gmx_fio_rewind(t_fileio* fio)
969 {
970     gmx_fio_lock(fio);
971
972     if (fio->xdr)
973     {
974         xdr_destroy(fio->xdr);
975         frewind(fio->fp);
976         xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
977     }
978     else
979     {
980         frewind(fio->fp);
981     }
982     gmx_fio_unlock(fio);
983 }
984
985
986 int gmx_fio_flush(t_fileio* fio)
987 {
988     int ret;
989
990     gmx_fio_lock(fio);
991     ret = gmx_fio_int_flush(fio);
992     gmx_fio_unlock(fio);
993
994     return ret;
995 }
996
997
998
999 static int gmx_fio_int_fsync(t_fileio *fio)
1000 {
1001     int rc    = 0;
1002     int filen = -1;
1003
1004
1005     if (fio->fp)
1006     {
1007         rc = gmx_fsync(fio->fp);
1008     }
1009     else if (fio->xdr) /* this should normally not happen */
1010     {
1011         rc = gmx_fsync((FILE*) fio->xdr->x_private);
1012         /* ^ is this actually OK? */
1013     }
1014
1015     return rc;
1016 }
1017
1018
1019 int gmx_fio_fsync(t_fileio *fio)
1020 {
1021     int rc;
1022
1023     gmx_fio_lock(fio);
1024     rc = gmx_fio_int_fsync(fio);
1025     gmx_fio_unlock(fio);
1026
1027     return rc;
1028 }
1029
1030
1031
1032 t_fileio *gmx_fio_all_output_fsync(void)
1033 {
1034     t_fileio *ret = NULL;
1035     t_fileio *cur;
1036
1037     cur = gmx_fio_get_first();
1038     while (cur)
1039     {
1040         /* skip debug files (shoud be the only iFTP==efNR) */
1041         if (cur->bOpen &&
1042             !cur->bRead &&
1043             !cur->bStdio &&
1044             cur->iFTP != efNR)
1045         {
1046             /* if any of them fails, return failure code */
1047             int rc = gmx_fio_int_fsync(cur);
1048             if (rc != 0 && !ret)
1049             {
1050                 ret = cur;
1051             }
1052         }
1053         cur = gmx_fio_get_next(cur);
1054     }
1055
1056     /* in addition, we force these to be written out too, if they're being
1057        redirected. We don't check for errors because errors most likely mean
1058        that they're not redirected. */
1059     fflush(stdout);
1060     fflush(stderr);
1061 #if (defined(HAVE_FSYNC))
1062     /* again, fahcore defines HAVE_FSYNC and fsync() */
1063     fsync(STDOUT_FILENO);
1064     fsync(STDERR_FILENO);
1065 #endif
1066
1067     return ret;
1068 }
1069
1070
1071 gmx_off_t gmx_fio_ftell(t_fileio* fio)
1072 {
1073     gmx_off_t ret = 0;
1074
1075     gmx_fio_lock(fio);
1076     if (fio->fp)
1077     {
1078         ret = gmx_ftell(fio->fp);
1079     }
1080     gmx_fio_unlock(fio);
1081     return ret;
1082 }
1083
1084 int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
1085 {
1086     int rc;
1087
1088     gmx_fio_lock(fio);
1089     if (fio->fp)
1090     {
1091         rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
1092     }
1093     else
1094     {
1095         gmx_file(fio->fn);
1096         rc = -1;
1097     }
1098     gmx_fio_unlock(fio);
1099     return rc;
1100 }
1101
1102 FILE *gmx_fio_getfp(t_fileio *fio)
1103 {
1104     FILE *ret = NULL;
1105
1106     gmx_fio_lock(fio);
1107     if (fio->fp)
1108     {
1109         ret = fio->fp;
1110     }
1111     gmx_fio_unlock(fio);
1112     return ret;
1113 }
1114
1115 XDR *gmx_fio_getxdr(t_fileio* fio)
1116 {
1117     XDR *ret = NULL;
1118
1119     gmx_fio_lock(fio);
1120     if (fio->xdr)
1121     {
1122         ret = fio->xdr;
1123     }
1124     gmx_fio_unlock(fio);
1125
1126     return ret;
1127 }
1128
1129 gmx_bool gmx_fio_getread(t_fileio* fio)
1130 {
1131     gmx_bool ret;
1132
1133     gmx_fio_lock(fio);
1134     ret = fio->bRead;
1135     gmx_fio_unlock(fio);
1136
1137     return ret;
1138 }
1139
1140 int xtc_seek_frame(t_fileio *fio, int frame, int natoms)
1141 {
1142     int ret;
1143
1144     gmx_fio_lock(fio);
1145     ret = xdr_xtc_seek_frame(frame, fio->fp, fio->xdr, natoms);
1146     gmx_fio_unlock(fio);
1147
1148     return ret;
1149 }
1150
1151 int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
1152 {
1153     int ret;
1154
1155     gmx_fio_lock(fio);
1156     ret = xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
1157     gmx_fio_unlock(fio);
1158
1159     return ret;
1160 }