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