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