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