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