1 /*============================================================================
2  * Base file wrapper type and associated functions
3  *============================================================================*/
4 
5 /*
6   This file is part of Code_Saturne, a general-purpose CFD tool.
7 
8   Copyright (C) 1998-2021 EDF S.A.
9 
10   This program is free software; you can redistribute it and/or modify it under
11   the terms of the GNU General Public License as published by the Free Software
12   Foundation; either version 2 of the License, or (at your option) any later
13   version.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18   details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22   Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24 
25 /*----------------------------------------------------------------------------*/
26 
27 #include "cs_config.h"
28 
29 /*
30   Force LARGEFILE_SOURCE if largefiles enabled under 32-bit Linux
31   (otherwise, we may encounter bugs with glibc 2.3 due to fseeko end ftello
32   not being correctly defined). Compiling with -D_GNU_SOURCE instead
33   of -D_POSIX_C_SOURCE=200112L seems to be another way to solve the problem.
34 */
35 
36 #if (SIZEOF_LONG < 8) && (_FILE_OFFSET_BITS == 64)
37 # if defined(__linux__)
38 #  if !defined(_POSIX_SOURCE)
39 #    define _GNU_SOURCE 1
40 #  endif
41 #  if !defined(_GNU_SOURCE) && !defined(_LARGEFILE_SOURCE)
42 #   define _LARGEFILE_SOURCE 1
43 #  endif
44 # endif
45 #endif
46 
47 /*-----------------------------------------------------------------------------*/
48 
49 /*
50  * Standard C library headers
51  */
52 
53 #include <assert.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdarg.h>
57 #include <string.h>
58 
59 #if defined(HAVE_ZLIB)
60 #include <zlib.h>
61 #endif /* defined(_HAVE_ZLIB) */
62 
63 #if defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_STAT_H)
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #endif /* defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_STAT_H) */
67 
68 #if defined(WIN32) || defined(_WIN32)
69 #include <io.h>
70 #endif
71 
72 /*
73  * Optional library and ECS headers
74  */
75 
76 #include "ecs_def.h"
77 #include "ecs_file.h"
78 #include "ecs_mem.h"
79 
80 /*-----------------------------------------------------------------------------*/
81 
82 #ifdef __cplusplus
83 extern "C" {
84 #if 0
85 } /* Fake brace to force Emacs auto-indentation back to column 0 */
86 #endif
87 #endif /* __cplusplus */
88 
89 /*-----------------------------------------------------------------------------
90  * Local type definitions
91  *-----------------------------------------------------------------------------*/
92 
93 #ifndef DOXYGEN_SHOULD_SKIP_THIS
94 
95 /*
96  * ECS file descriptor
97  */
98 
99 struct _ecs_file_t {
100 
101   FILE              *ptr;         /* File descriptor */
102 
103 #if defined(HAVE_ZLIB)
104   gzFile             gzptr;       /* Zlib file descriptor */
105 #endif
106 
107   char              *name;        /* File name */
108   ecs_file_mode_t    mode;        /* File mode */
109   ecs_file_type_t    type;        /* Type (text, binary, Fortan binary) */
110   int                swp_endian;  /* Swap big-endian and little-endian ? */
111 
112 };
113 
114 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
115 
116 /* Associated typedef documentation (for ecs_file.h) */
117 
118 /*!
119  * \typedef ecs_file_t
120  * \brief Pointer to opaque file descriptor
121  */
122 
123 #ifndef DOXYGEN_SHOULD_SKIP_THIS
124 #if defined(HAVE_ZLIB)
125 
126 /* Zlib API may be broken when using large file support, as z_off_t
127    is based on current off_t, and not on a value fixed at compilation time.
128    We redefine prototypes for gzseek() and gztell() ;
129    This is ugly, but should work with an unmodified Zlib (as of Zlib 1.2.1) */
130 
131 #if defined (SIZEOF_Z_OFF_T)
132 #  if (SIZEOF_Z_OFF_T == SIZEOF_LONG)
133 typedef long ecs_z_off_t;
134 #  elif defined (HAVE_LONG_LONG)
135 #    if (SIZEOF_Z_OFF_T == SIZEOF_LONG_LONG)
136 typedef long long ecs_z_off_t;
137 #    else
138 #      error "z_off_t returned by zlibCompileFlags() neither long nor long long"
139 #    endif
140 #  endif
141 #else
142 typedef z_off_t ecs_z_off_t;
143 #endif
144 
145 typedef ecs_z_off_t (ecs_gzseek_t) (gzFile file,
146                                     ecs_z_off_t offset,
147                                     int whence);
148 
149 typedef ecs_z_off_t (ecs_gztell_t) (gzFile file);
150 
151 #endif /* HAVE_ZLIB */
152 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
153 
154 /*-----------------------------------------------------------------------------
155  * Local macro definitions
156  *-----------------------------------------------------------------------------*/
157 
158 /*-----------------------------------------------------------------------------
159  * Local static variable definitions
160  *-----------------------------------------------------------------------------*/
161 
162 /* Message strings that may be used more than once */
163 
164 static const char * _ecs_file_str_b_read_error
165                       = N_("Error reading binary file \"%s\":"
166                            "\n\n  %s");
167 static const char * _ecs_file_str_b_read_closed_error
168                       = N_("Error: reading from closed file \"%s\"");
169 static const char * _ecs_file_str_b_write_error
170                       = N_("Error writing binary file \"%s\":"
171                            "\n\n  %s");
172 static const char * _ecs_file_str_f_read_error
173                       = N_("Error reading Fortran binary file \"%s\":"
174                            "\n\n  %s");
175 static const char * _ecs_file_str_f_write_error
176                       = N_("Error writing Fortran binary file \"%s\":"
177                            "\n\n  %s");
178 static const char * _ecs_file_str_f_rec_too_large
179                       = N_("A record is too large to be represented "
180                            "in this format (i.e. > 2GB).");
181 
182 #if defined(HAVE_ZLIB)
183 
184 /* Zlib API may be broken when using large file support, as z_off_t
185    is based on current off_t, and not on a value fixed at compilation time.
186    Prototypes for gzseek() and gztell() were redefined above, we
187    point to the true functions here.
188    This is ugly, but should work with an unmodified Zlib (as of Zlib 1.2.1) */
189 
190 static ecs_gzseek_t  *_ecs_gzseek = (ecs_gzseek_t *)gzseek;
191 static ecs_gztell_t  *_ecs_gztell = (ecs_gztell_t *)gztell;
192 
193 #endif /* HAVE_ZLIB */
194 
195 /*-----------------------------------------------------------------------------
196  * Local function definitions
197  *-----------------------------------------------------------------------------*/
198 
199 /*
200  * Return an error message associated with an open file.
201  *
202  * parameter:
203  *   f: <-- file descriptor.
204  */
205 
206 static const char *
_ecs_file_error_string(const ecs_file_t * f)207 _ecs_file_error_string(const ecs_file_t  *f)
208 {
209   int err_num;
210 
211   assert(f != NULL) ;
212 
213 #if defined(HAVE_ZLIB)
214 
215   if (f->gzptr != NULL) {
216 
217     const char *err_str;
218 
219     err_str = gzerror(f->gzptr, &err_num);
220 
221     if (err_num != 0)
222       return err_str;
223 
224     else if (gzeof(f->gzptr) != 0)
225       return _("Premature end of file.");
226 
227     else
228       return "\0";
229 
230   }
231 
232 #endif /*defined(HAVE_ZLIB) */
233 
234   if (f->ptr == NULL) return "\0";
235 
236   err_num = ferror(f->ptr);
237 
238   if (err_num != 0)
239     return strerror(err_num);
240 
241   else if (feof(f->ptr) != 0)
242     return _("Premature end of file.");
243 
244   else
245     return "\0";
246 }
247 
248 /*!
249  * \brief Formatted output to a text file (as fprintf()).
250  *
251  * \param [in] f      ecs_file_t descriptor.
252  * \param [in] format format string, as printf() and family.
253  * \param [in] ...    variable arguments based on format string.
254  *
255  * \return number of characters printed, not counting the trailing '\\0'
256  *         used to end output strings
257  */
258 
ecs_file_printf(const ecs_file_t * const f,const char * const format,...)259 int ecs_file_printf(const ecs_file_t  *const f,
260                     const char        *const format,
261                     ...)
262 {
263   va_list       arg_ptr;
264   int           retval = 0;
265 
266   assert(f != NULL) ;
267   assert(f->mode == ECS_FILE_MODE_APPEND || f->mode == ECS_FILE_MODE_WRITE);
268   assert(f->type == ECS_FILE_TYPE_TEXT);
269 
270   if (f->ptr != NULL) {
271 
272     va_start(arg_ptr, format);
273 
274     retval = vfprintf(f->ptr, format, arg_ptr);
275 
276     va_end(arg_ptr);
277 
278     if (retval <= 0) {
279       retval = errno;
280       ecs_error(__FILE__, __LINE__, 0,
281                 _("Error writing to text file \"%s\":\n\n  %s"),
282                 f->name, _ecs_file_error_string(f));
283     }
284 
285   }
286 
287 #if defined(HAVE_ZLIB)
288 
289   else if (f->gzptr != NULL)
290     ecs_error(__FILE__, __LINE__, 0,
291               _("ECS library formatted output to gzipped file "
292                 "not implemented\n\n(file: \"%s\")"), f->name);
293 
294 #endif /* defined(HAVE_ZLIB) */
295 
296   else
297     ecs_error(__FILE__, __LINE__, 0,
298               _("Error writing to closed file \"%s\""), f->name);
299 
300   return retval;
301 }
302 
303 /*
304  * Formatted input from a text file if possible (as fgets()).
305  *
306  * This function is the base for ecs_file_gets() and ecs_file_gets_try();
307  * depending on the allow_eof parameter, failure to read a line due to
308  * an end-of-file condition is considered an error or not.
309  *
310  * parameters:
311  *   s:         --> buffer to which string is to be read.
312  *   size:      <-- maximum number of characters to be read plus one.
313  *   f:         <-- ecs_file_t descriptor.
314  *   line:      <-> file line number if available, or NULL.
315  *   allow_eof: <-- 1 if EOF is allowed, 0 if considered an error.
316  *
317  * returns:
318  *   s on success, NULL on error or when end of file occurs and
319  *   no characters have been read.
320  */
321 
322 static char *
_ecs_file_gets(char * s,const int size,const ecs_file_t * f,int * line,const int allow_eof)323 _ecs_file_gets(char              *s,
324                const int          size,
325                const ecs_file_t  *f,
326                int               *line,
327                const int          allow_eof)
328 {
329   char *retval = NULL;
330 
331   assert(f != NULL);
332 
333   if (f->ptr != NULL)
334     retval = fgets(s, size, f->ptr);
335 
336 #if defined(HAVE_ZLIB)
337 
338   else if (f->gzptr != NULL)
339     retval = gzgets(f->gzptr, s, size);
340 
341 #endif /* defined(HAVE_ZLIB) */
342 
343   else
344     ecs_error(__FILE__, __LINE__, 0,
345               _(_ecs_file_str_b_read_closed_error), f->name);
346 
347   if (retval == NULL && (allow_eof == 0 || ecs_file_eof(f) == 0)) {
348     if (line != NULL)
349       ecs_error(__FILE__, __LINE__, 0,
350                 _("Error reading line %d of text file \"%s\":\n\n  %s"),
351                 *line, f->name, _ecs_file_error_string(f));
352     else
353       ecs_error(__FILE__, __LINE__, 0,
354                 _("Error reading text file \"%s\":\n\n  %s"),
355                 f->name, _ecs_file_error_string(f));
356   }
357 
358   if (retval != NULL) {
359 
360     int i = strlen(s) - 2;
361     if (i > 0) {
362       if (s[i] == '\r' && s[i+1] == '\n') {
363         s[i] = '\n';
364         s[i+1] = '\0';
365       }
366     }
367 
368     if (line != NULL)
369       *line += 1;
370   }
371 
372   return retval;
373 }
374 
375 /*
376  * Reads a "usual" Fortran binary record header or footer.
377  *
378  * Depending on the allow_eof parameter, failure to read a line due to
379  * an end-of-file condition is considered an error or not.
380  *
381  * parameters:
382  *   f:        <-- ecs_file_t descriptor.
383  *   rec_size: <-- size of corresponding record.
384  *
385  * returns:
386  *   0 on success, 1 if the record format or size does not correspond
387  *   to what we expect.
388  */
389 
390 static int
_ecs_file_read_fortran_size(const ecs_file_t * f,const size_t rec_size,const int allow_eof)391 _ecs_file_read_fortran_size(const ecs_file_t  *f,
392                             const size_t       rec_size,
393                             const int          allow_eof)
394 {
395   int32_t  n_bytes_read;
396   size_t   retval = 0;
397 
398   assert(sizeof(int32_t) == 4);
399 
400   if (f->ptr != NULL)
401     retval = fread((void *)(&n_bytes_read), sizeof(int32_t), 1, f->ptr);
402 
403 #if defined(HAVE_ZLIB)
404 
405   else if (f->gzptr != NULL) {
406     retval = (size_t)gzread(f->gzptr, (void *)(&n_bytes_read), sizeof(int32_t));
407     retval = retval / sizeof(int32_t);
408   }
409 
410 #endif /* defined(HAVE_ZLIB) */
411 
412   else
413     ecs_error(__FILE__, __LINE__, 0,
414               _(_ecs_file_str_b_read_closed_error), f->name);
415 
416   if (retval < 1) {
417     if (allow_eof == 0 || ecs_file_eof(f) == 0)
418       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_read_error),
419                 f->name, _ecs_file_error_string(f));
420     return 1;
421   }
422 
423   if (f->swp_endian == 1)
424     ecs_file_swap_endian(&n_bytes_read, &n_bytes_read, sizeof(int32_t), 1);
425 
426   if ((size_t)n_bytes_read != rec_size) {
427     ecs_error(__FILE__, __LINE__, 0,
428               _("Error reading Fortran binary file \"%s\":\n\n"
429                 "  expected record size: %lu\n"
430                 "  read record size:     %lu\n"), f->name,
431               (unsigned long)rec_size, (unsigned long)n_bytes_read);
432     return 1;
433   }
434 
435   return 0;
436 }
437 
438 /*
439  * Read a binary C or Fortran type record.
440  *
441  * This function is the base for ecs_file_read() and ecs_file_read_try();
442  * depending on the allow_eof parameter, failure to read a line due to
443  * an end-of-file condition is considered an error or not.
444  *
445  * A Fortran record compatible with most compilers is structured
446  * as follows:
447  *   - a 4-byte integer indicating the number of bytes in the record.
448  *   - the raw data
449  *   - a 4-byte integer indicating the number of bytes in the record.
450  *
451  * A C record contains only the raw data.
452  *
453  * parameters:
454  *   rec  <-> pointer to location receiving data.
455  *   size <-- size of each item of data in bytes.
456  *   ni   <-- number of items to write.
457  *   f    <-- ecs_file_t descriptor.
458  *
459  * returns:
460  *   the number of items (not bytes) sucessfully read; for a Fortran record,
461  *   if the whole record could not be read, returns 0.
462  */
463 
464 static size_t
_ecs_file_read(void * rec,const size_t size,const size_t ni,const ecs_file_t * f,const int allow_eof)465 _ecs_file_read(void              *rec,
466                const size_t       size,
467                const size_t       ni,
468                const ecs_file_t  *f,
469                const int          allow_eof)
470 {
471   int32_t  n_bytes;
472   size_t   retval;
473   size_t   rec_size;
474 
475   assert(sizeof(int32_t) == 4);
476 
477   /* Check file state */
478 
479   assert(f != NULL);
480   assert(rec != NULL || ni == 0);
481   assert(   f->type == ECS_FILE_TYPE_BINARY
482          || f->type == ECS_FILE_TYPE_FORTRAN_BINARY);
483   assert(f->mode == ECS_FILE_MODE_READ);
484 
485   /* Number of bytes of record to read */
486 
487   rec_size = size * ni;
488 
489   /* In Fortran binary case, read record header */
490 
491   if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY) {
492 
493     /* Check that 4 bytes is enough for record */
494 
495     n_bytes = (int32_t)rec_size;
496 
497     if ((size_t)n_bytes != rec_size) {
498       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_read_error),
499                 f->name, _(_ecs_file_str_f_rec_too_large));
500       return 0;
501     }
502 
503     if (_ecs_file_read_fortran_size(f, rec_size, allow_eof) != 0)
504       return 0;
505 
506   }
507 
508   /* Read the record proper (C or Fortran) */
509 
510   retval = 0;
511 
512   if (f->ptr != NULL)
513 
514     retval = fread((void *)rec, size, ni, f->ptr);
515 
516 #if defined(HAVE_ZLIB)
517 
518   else if (f->gzptr != NULL)
519 
520     retval = ((size_t)gzread(f->gzptr, (void *)(rec), rec_size)) / size;
521 
522 #endif /* defined(HAVE_ZLIB) */
523 
524   else
525     ecs_error(__FILE__, __LINE__, 0,
526               _(_ecs_file_str_b_read_closed_error), f->name);
527 
528   if (retval != (size_t)ni) {
529     if (allow_eof == 0 || ecs_file_eof(f) == 0) {
530       if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY) {
531         ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_read_error),
532                   f->name, _ecs_file_error_string(f));
533         retval = 0;
534       }
535       else
536         ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_b_read_error),
537                   f->name, _ecs_file_error_string(f));
538     }
539     return retval;
540   }
541 
542   if (f->swp_endian == 1 && size > 1)
543     ecs_file_swap_endian(rec, rec, size, ni);
544 
545   /* In Fortran binary case, read record footer */
546 
547   if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY) {
548 
549     if (_ecs_file_read_fortran_size(f, rec_size, allow_eof) != 0)
550       return 0;
551 
552   }
553 
554   return retval;
555 }
556 
557 /*============================================================================
558  * Public function definitions
559  *============================================================================*/
560 
561 /*!
562  * \brief Create a `ecs_file_t' file descriptor and open the associated file.
563  *
564  * By default, data is written or read in native format (as regards
565  * big-endian or little-endian). This behavior may be modified by
566  * ecs_file_set_big_endian() or ecs_file_set_swap_endian().
567  *
568  * \param [in] name file name.
569  * \param [in] mode file acces mode: read, write, or append.
570  * \param [in] type file type: text, binary, or Fortran binary.
571  *
572  * \return pointer to ecs_file_t file descriptor (NULL in case of failure).
573  */
574 
575 ecs_file_t *
ecs_file_open(const char * name,const ecs_file_mode_t mode,const ecs_file_type_t type)576 ecs_file_open(const char             *name,
577               const ecs_file_mode_t   mode,
578               const ecs_file_type_t   type)
579 {
580 
581   ecs_file_t * f;
582 
583   ECS_MALLOC(f, 1, ecs_file_t);
584 
585   f->ptr = NULL;
586 
587 #if defined(HAVE_ZLIB)
588   f->gzptr = NULL;
589 #endif
590 
591   ECS_MALLOC(f->name, strlen(name) + 1, char);
592   strcpy(f->name, name);
593 
594   f->type = type;
595   f->mode = mode;
596 
597   /* Use native endianness by default */
598 
599   f->swp_endian = 0;
600 
601   /* Open file. In case of failure, destroy the allocated structure;
602      this is only useful with a non-default error handler,
603      as the program is terminated by default */
604 
605   if (ecs_file_open_stream(f, mode) != 0)
606     f = ecs_file_free(f);
607 
608   return f;
609 
610 }
611 
612 /*!
613  * \brief Destroy a `ecs_file_t' descriptor and close the associated file.
614  *
615  * The descriptor may only be destroyed if the file was successfully
616  * closed. To force destruction of a ecs_file_t descriptor even
617  * if the associated file was not closed, use (ecs_file_free_force()).
618  *
619  * The associated file is only closed if this was not already the case.
620  *
621  * \param [in] f ecs_file_t descriptor.
622  *
623  * \return pointer to ecs_file_t file descriptor (NULL in case of,
624  *         success, f in case of failure).
625  */
626 
627 ecs_file_t *
ecs_file_free(ecs_file_t * f)628 ecs_file_free(ecs_file_t  *f)
629 {
630   if (f != NULL) {
631 
632     if (ecs_file_close_stream(f) == 0)
633       return ecs_file_free_descriptor(f);
634 
635   }
636 
637   return f;
638 }
639 
640 /*
641  * \brief Destroy a `ecs_file_t' descriptor without closing its associated file.
642  *
643  * \param [in] f ecs_file_t descriptor.
644  *
645  * returns:
646  *   NULL pointer.
647  */
648 
649 ecs_file_t *
ecs_file_free_descriptor(ecs_file_t * f)650 ecs_file_free_descriptor(ecs_file_t  *f)
651 {
652   if (f != NULL) {
653     ECS_FREE(f->name);
654     ECS_FREE(f);
655   }
656   return NULL;
657 }
658 
659 /*!
660  * \brief Open `ecs_file_t' descriptor's associated file.
661  *
662  * If the file is already open, this function does nothing.
663  *
664  * \param [in] f    ecs_file_t descriptor.
665  * \param [in] mode file acces mode: read, write, or append.
666  *
667  * \return 0 in case of success, system error code in case of failure
668  *         (or Zlib error code in case of Zlib memory allocation problem
669  *         for a gzipped file).
670  */
671 
672 int
ecs_file_open_stream(ecs_file_t * f,ecs_file_mode_t mode)673 ecs_file_open_stream(ecs_file_t       *f,
674                      ecs_file_mode_t   mode)
675 {
676   int retval = 0;
677 
678 #if defined(HAVE_ZLIB)
679   int gzipped = 0;
680 #endif
681 
682   assert(f != NULL);
683 
684   if (f->ptr != NULL
685 #if defined(HAVE_ZLIB)
686       || f->gzptr != NULL
687 #endif
688       )
689     return 0;
690 
691   /* The file wrapper exists and the corresponding file is closed */
692 
693   f->mode = mode;
694 
695   switch (f->type) {
696 
697   case ECS_FILE_TYPE_TEXT:
698 
699     if (f->mode == ECS_FILE_MODE_APPEND)
700       f->ptr = fopen(f->name, "a");
701 
702     else if (f->mode == ECS_FILE_MODE_WRITE)
703       f->ptr = fopen(f->name, "w");
704 
705     else if (f->mode == ECS_FILE_MODE_READ) {
706 
707 #if defined(HAVE_ZLIB)
708 
709       if (strlen(f->name) > 3 &&
710           (! strncmp((f->name + strlen(f->name) - 3), ".gz", 3))) {
711 
712         f->gzptr = gzopen(f->name, "r");
713         gzipped = 1;
714         break;
715       }
716 
717 #endif /* defined(HAVE_ZLIB) */
718 
719       f->ptr = fopen(f->name, "r");
720 
721     }
722 
723     break;
724 
725   default:
726 
727     if (f->mode == ECS_FILE_MODE_APPEND)
728       f->ptr = fopen(f->name, "ab");
729 
730     else if (f->mode == ECS_FILE_MODE_WRITE)
731       f->ptr = fopen(f->name, "wb");
732 
733     else if (f->mode == ECS_FILE_MODE_READ) {
734 
735 #if defined(HAVE_ZLIB)
736 
737       if (strlen(f->name) > 3 &&
738           (! strncmp((f->name + strlen(f->name) - 3), ".gz", 3))) {
739 
740         f->gzptr = gzopen(f->name, "rb");
741         gzipped = 1;
742         break;
743       }
744 
745 #endif /* defined(HAVE_ZLIB) */
746 
747       f->ptr = fopen(f->name, "rb");
748 
749     }
750 
751     break;
752 
753   }
754 
755   if (f->ptr == NULL
756 #if defined(HAVE_ZLIB)
757       && f->gzptr == NULL
758 #endif
759       ) {
760 #if defined(HAVE_ZLIB)
761     if (gzipped == 1 && errno == 0) {
762       ecs_error(__FILE__, __LINE__, 0,
763                 _("Error opening file \"%s\":\n\n"
764                   "  %s"), f->name, zError(Z_MEM_ERROR));
765       return Z_MEM_ERROR;
766     }
767 #endif
768     ecs_error(__FILE__, __LINE__, 0,
769               _("Error opening file \"%s\":\n\n"
770                 "  %s"), f->name, strerror(errno));
771     retval = errno;
772   }
773 
774   return retval;
775 }
776 
777 /*!
778  * \brief Close a ecs_file_t file descriptor's associated file.
779  *
780  * If the file is already closed, this function does nothing.
781  *
782  * \param [in] f ecs_file_t descriptor.
783  *
784  * \return 0 in case of success, system error code in case of failure
785  *         (or Zlib error code in case of a Zlib specific error
786  *         for a gzipped file).
787  */
788 
789 int
ecs_file_close_stream(ecs_file_t * f)790 ecs_file_close_stream(ecs_file_t  *f)
791 {
792   int retval = 0;
793 
794   assert(f != NULL);
795 
796   if (f->ptr != NULL) {
797     retval = fclose(f->ptr);
798     if (retval != 0) {
799       ecs_error(__FILE__, __LINE__, 0,
800                 _("Error closing file \"%s\":\n\n"
801                   "  %s"), f->name, strerror(errno));
802       return errno;
803     }
804     f->ptr = NULL ;
805   }
806 
807 #if defined(HAVE_ZLIB)
808 
809   else if (f->gzptr != NULL) {
810     retval = gzclose(f->gzptr);
811     if (retval != 0) {
812       ecs_error(__FILE__, __LINE__, 0,
813                 _("Error closing file \"%s\":\n\n"
814                   "  %s"), f->name, gzerror(f->gzptr, &retval));
815       return retval;
816     }
817     f->gzptr = NULL ;
818   }
819 
820 #endif /* defined(HAVE_ZLIB) */
821 
822   return retval;
823 }
824 
825 /*!
826  * \brief Test the end-of-file indicator for a given file.
827  *
828  * \param [in] f ecs_file_t descriptor.
829  *
830  * \return 0 if the end-of-file has not been reached, or non-zero
831  *         (1 or feof() return value) otherwise.
832  */
833 
834 int
ecs_file_eof(const ecs_file_t * f)835 ecs_file_eof(const ecs_file_t  *f)
836 {
837   int retval = 0;
838 
839   assert(f != NULL);
840 
841   if (f->ptr != NULL)
842     retval = feof(f->ptr);
843 
844 #if defined(HAVE_ZLIB)
845 
846   else if (f->gzptr != NULL)
847     retval = gzeof(f->gzptr);
848 
849 #endif
850 
851   return retval;
852 }
853 
854 /*!
855  * \brief Force write of all user-space buffered data for a given file.
856  *
857  * \param [in] f ecs_file_t descriptor.
858  *
859  * \return 0 upon successful completion, system error code otherwise.
860  */
861 
862 int
ecs_file_flush(ecs_file_t * f)863 ecs_file_flush(ecs_file_t  *f)
864 {
865   int retval = 0;
866 
867   assert(f != NULL);
868 
869   if (f->ptr != NULL) {
870 
871     retval = fflush(f->ptr);
872 
873     if (retval != 0) {
874       retval = errno;
875       ecs_error(__FILE__, __LINE__, 0,
876                 _("Error flushing file \"%s\":\n\n"
877                   "  %s"), f->name, strerror(retval));
878     }
879 
880   }
881 
882 #if defined(HAVE_ZLIB)
883 
884   else if (f->gzptr != NULL) {
885 
886     retval = gzflush(f->gzptr, Z_FULL_FLUSH);
887 
888     if (retval != 0)
889       ecs_error(__FILE__, __LINE__, 0,
890                 _("Error closing file \"%s\":\n\n"
891                   "  %s"), f->name, gzerror(f->gzptr, &retval));
892 
893   }
894 
895 #endif
896 
897   return retval;
898 }
899 
900 /*!
901  * \brief Obtain the current value of a file's position indicator.
902  *
903  * \param [in] f ecs_file_t descriptor.
904  *
905  * \return current value of the file's position indicator, or -1 in case
906  *         of failure.
907  */
908 
909 ecs_file_off_t
ecs_file_tell(ecs_file_t * f)910 ecs_file_tell(ecs_file_t  *f)
911 {
912   ecs_file_off_t offset = 0;
913 
914   assert(f != NULL);
915 
916   if (f->ptr != NULL) {
917 
918 #if (SIZEOF_LONG < 8)
919 
920     /* For 32-bit systems, large file support might be necessary */
921 
922 # if defined(HAVE_FSEEKO) && (_FILE_OFFSET_BITS == 64)
923     offset = (ecs_file_off_t)ftello(f->ptr);
924 # else
925     /*
926       Without ftello, ftell will fail above 2 Gigabytes, in which case
927       offset == -1 and errno == EOVERFLOW, but should work on smaller
928       files. We prefer not to be too strict about fseeko availability, as
929       the only 32-bit case without ftello we have encountered is Cygwin
930       (for which ftello requires additional non-default libraries), which
931       is expected to be used mainly for small cases.
932     */
933     offset = (ecs_file_off_t)ftell(f->ptr);
934 #endif
935 
936 #else /* SIZEOF_LONG >= 8) */
937 
938     /* For 64-bit systems, standard ftell should be enough */
939 
940     offset = (ecs_file_off_t)ftell(f->ptr);
941 
942 #endif
943 
944   }
945 
946 #if defined(HAVE_ZLIB)
947 
948   else if (f->gzptr != NULL)
949     offset = (ecs_file_off_t)_ecs_gztell(f->gzptr);
950 
951 #endif
952 
953   if (offset < 0)
954     ecs_error(__FILE__, __LINE__, 0,
955               _("Error obtaining position in file \"%s\":\n\n  %s"),
956               f->name, _ecs_file_error_string(f));
957 
958   return offset;
959 }
960 
961 /*!
962  * \brief Sets the file position indicator to the beginning of the file.
963  *
964  * A successful call to this function clears the end-of-file indicator for
965  * this file.
966  *
967  * \param [in] f ecs_file_t descriptor.
968  */
969 
970 void
ecs_file_rewind(ecs_file_t * f)971 ecs_file_rewind(ecs_file_t  *f)
972 {
973   assert(f != NULL);
974 
975   if (f->ptr != NULL) {
976 
977     int retval = 0;
978 
979 #if (SIZEOF_LONG < 8)
980 
981     /* For 32-bit systems, large file support might be necessary */
982 
983 # if defined(HAVE_FSEEKO) && (_FILE_OFFSET_BITS == 64)
984     retval = fseeko(f->ptr, 0L, SEEK_SET);
985 # else
986     retval = fseek(f->ptr, 0L, SEEK_SET);
987 # endif
988 
989 #else /* SIZEOF_LONG >= 8) */
990 
991     /* For 64-bit systems, standard fseek should be enough */
992 
993     retval = fseek(f->ptr, 0L, SEEK_SET);
994 
995 #endif
996 
997     if (retval != 0)
998       ecs_error(__FILE__, __LINE__, errno,
999                 _("Error rewinding file \"%s\":\n\n  %s"),
1000                 f->name, _ecs_file_error_string(f));
1001   }
1002 
1003 #if defined(HAVE_ZLIB)
1004 
1005   else if (f->gzptr != NULL)
1006     (void)gzrewind(f->gzptr);
1007 
1008 #endif
1009 }
1010 
1011 /*!
1012  * \brief Sets a file's position indicator.
1013  *
1014  * This function may call the libc's fseek() function, or Zlib's gzseek()
1015  * function. The C 99 standard draft specifies that for a text file, the offset
1016  * argument to fseek() should be zero or a value returned by an earlier
1017  * successful call to ftell() (here ecs_file_ftell()) on a stream (here a
1018  * ecs_file_t structure). Zlib's gzseek() does not support SEEK_END, at least
1019  * as of version 1.2.1.
1020  *
1021  * A successful call to this function clears the end-of-file indicator for
1022  * this file.
1023  *
1024  * \param [in] f      ecs_file_t descriptor.
1025  * \param [in] offset add to position specified to whence to obtain new
1026  *                    position, measured in characters from the beginning of
1027  *                    the file.
1028  * \param [in] whence beginning if ECS_FILE_SEEK_SET, current if
1029  *                    ECS_FILE_SEEK_CUR, or end-of-file if ECS_FILE_SEEK_END.
1030  *
1031  * \return 0 upon success, nonzero otherwise.
1032  */
1033 
1034 int
ecs_file_seek(ecs_file_t * f,const ecs_file_off_t offset,const ecs_file_seek_t whence)1035 ecs_file_seek(ecs_file_t             *f,
1036               const ecs_file_off_t    offset,
1037               const ecs_file_seek_t   whence)
1038 {
1039   int _whence = ECS_FILE_SEEK_SET;
1040   int retval = 0;
1041 
1042   assert(f != NULL);
1043 
1044   switch (whence) {
1045   case ECS_FILE_SEEK_SET:
1046     _whence = SEEK_SET;
1047     break;
1048   case ECS_FILE_SEEK_CUR:
1049     _whence = SEEK_CUR;
1050     break;
1051   case ECS_FILE_SEEK_END:
1052     _whence = SEEK_END;
1053     break;
1054   default:
1055     ecs_error
1056       (__FILE__, __LINE__, 0,
1057        _("Invalid offset argument \"%d\" setting position in file\n"
1058          "\"%s\""),
1059        (int)whence, f->name);
1060   }
1061 
1062   if (f->ptr != NULL) {
1063 
1064 #if (SIZEOF_LONG < 8)
1065 
1066     /* For 32-bit systems, large file support might be necessary */
1067 
1068 # if defined(HAVE_FSEEKO) && (_FILE_OFFSET_BITS == 64)
1069 
1070     retval = fseeko(f->ptr, (off_t)offset, _whence);
1071 
1072     if (retval != 0)
1073       ecs_error(__FILE__, __LINE__, errno,
1074                 _("Error setting position in file \"%s\":\n\n  %s"),
1075                 f->name, _ecs_file_error_string(f));
1076 
1077 # else
1078 
1079     /* Test if offset larger than allowed */
1080 
1081     long _offset = offset;
1082 
1083     if (_offset == offset) {
1084       retval = fseek(f->ptr, (long)offset, _whence);
1085       if (retval != 0)
1086         ecs_error(__FILE__, __LINE__, errno,
1087                   _("Error setting position in file \"%s\":\n\n  %s"),
1088                   f->name, _ecs_file_error_string(f));
1089     }
1090     else {
1091       retval = -1;
1092       ecs_error
1093         (__FILE__, __LINE__, errno,
1094          _("Error setting position in file \"%s\":\n\n  %s"),
1095          f->name,
1096          _("sizeof(off_t) > sizeof(long) but fseeko() not available"));
1097     }
1098 
1099 # endif /* defined(HAVE_FSEEKO) && (_FILE_OFFSET_BITS == 64) */
1100 
1101 #else /* SIZEOF_LONG >= 8) */
1102 
1103     /* For 64-bit systems, standard fseek should be enough */
1104 
1105     retval = fseek(f->ptr, (long)offset, _whence);
1106 
1107     if (retval != 0)
1108       ecs_error(__FILE__, __LINE__, errno,
1109                 _("Error setting position in file \"%s\":\n\n  %s"),
1110                 f->name, _ecs_file_error_string(f));
1111 
1112 #endif /* SIZEOF_LONG */
1113   }
1114 
1115 #if defined(HAVE_ZLIB)
1116 
1117   else if (f->gzptr != NULL) {
1118 
1119     retval = _ecs_gzseek(f->gzptr, (ecs_z_off_t)offset, _whence);
1120 
1121     if (retval != 0)
1122       ecs_error(__FILE__, __LINE__, 0,
1123                 _("Error setting position in file \"%s\":\n\n  %s"),
1124                 f->name, _ecs_file_error_string(f));
1125   }
1126 
1127 #endif
1128 
1129   return retval;
1130 }
1131 
1132 /*!
1133  * \brief Return a file's name.
1134  *
1135  * \param [in] f ecs_file_t descriptor.
1136  *
1137  * \return pointer to file's name.
1138  */
1139 
1140 const char *
ecs_file_get_name(const ecs_file_t * f)1141 ecs_file_get_name(const ecs_file_t  *f)
1142 {
1143   assert(f != NULL);
1144 
1145   return f->name;
1146 }
1147 
1148 /*!
1149  * \brief Return a file's type.
1150  *
1151  * \param [in] f ecs_file_t descriptor.
1152  *
1153  * \return file's type.
1154  */
1155 
1156 ecs_file_type_t
ecs_file_get_type(const ecs_file_t * f)1157 ecs_file_get_type(const ecs_file_t  *f)
1158 {
1159   assert(f != NULL);
1160 
1161   return f->type;
1162 }
1163 
1164 /*!
1165  * \brief Change a file's type.
1166  *
1167  * Using this function assumes one is familiar with a file's coding
1168  * or structure; use with caution.
1169  *
1170  * \param [in, out ] f    ecs_file_t descriptor.
1171  * \param [in]       type text, binary, or Fortran binary type descriptor.
1172  */
1173 
1174 void
ecs_file_set_type(ecs_file_t * f,const ecs_file_type_t type)1175 ecs_file_set_type(ecs_file_t             *f,
1176                   const ecs_file_type_t   type)
1177 {
1178   assert(f != NULL);
1179 
1180   f->type = type;
1181 }
1182 
1183 /*!
1184  * \brief Ensure that data is read or written in big-endian
1185  *        (network standard) format.
1186  *
1187  * By default, data is written or read in native format (as regards
1188  * big-endian or little-endian)..
1189  *
1190  * \param [in, out] f ecs_file_t descriptor.
1191  */
1192 
1193 void
ecs_file_set_big_endian(ecs_file_t * f)1194 ecs_file_set_big_endian(ecs_file_t  *f)
1195 {
1196   unsigned     int_endian;
1197 
1198   /* Check if system is "big-endian" or "little-endian" */
1199 
1200   int_endian = 0;
1201   *((char *)(&int_endian)) = '\1';
1202 
1203   if (int_endian == 1)
1204     f->swp_endian = 1;
1205 
1206 #if defined(DEBUG) && !defined(NDEBUG)
1207 
1208   else {
1209     int_endian = 0;
1210     *((char *) (&int_endian) + sizeof(unsigned) - 1) = '\1';
1211     assert(int_endian == 1);
1212   }
1213 
1214 #endif
1215 
1216 }
1217 
1218 /*!
1219  * \brief Return a file's byte-swapping behavior.
1220  *
1221  * \param [in] f ecs_file_t descriptor.
1222  *
1223  * \return 0 if file's endianness is the same as the system's, 1 otherwise.
1224  */
1225 
1226 int
ecs_file_get_swap_endian(const ecs_file_t * f)1227 ecs_file_get_swap_endian(const ecs_file_t  *f)
1228 {
1229   assert(f != NULL);
1230 
1231   return f->swp_endian;
1232 }
1233 
1234 /*!
1235  * \brief Set a file's byte-swapping behavior.
1236  *
1237  * Using this function assumes one is familiar with a file's coding
1238  * or structure; use with caution.
1239  *
1240  * \param [in] f    ecs_file_t descriptor.
1241  * \param [in] swap 1 if bytes must be swapped, 0 otherwise.
1242  */
1243 
1244 void
ecs_file_set_swap_endian(ecs_file_t * f,const int swap)1245 ecs_file_set_swap_endian(ecs_file_t  *f,
1246                          const int    swap)
1247 {
1248   assert(f != NULL);
1249 
1250   f->swp_endian = swap;
1251 }
1252 
1253 /*!
1254  * \brief Test a file's error or EOF condition.
1255  *
1256  * \param [in]      f    ecs_file_t descriptor.
1257  * \param [in, out] line file line number if available, or NULL.
1258  *
1259  * \return 0 if no error, system error code, or -1 if EOF.
1260  */
1261 
1262 int
ecs_file_read_check_error(const ecs_file_t * f,const int line)1263 ecs_file_read_check_error(const ecs_file_t  *f,
1264                           const int          line)
1265 {
1266   int retval = 0;
1267 
1268   /* Check for possible error */
1269 
1270   if (f->ptr != NULL)
1271     retval = ferror(f->ptr);
1272 
1273 #if defined(HAVE_ZLIB)
1274 
1275   else if (f->gzptr != NULL)
1276     gzerror(f->gzptr, &retval);
1277 
1278 #endif /* defined(HAVE_ZLIB) */
1279 
1280   /* If we have a read error */
1281 
1282   if (retval != 0) {
1283     if (line > 0)
1284       ecs_error(__FILE__, __LINE__, 0,
1285                 _("Error reading line %d of file \"%s\":\n\n"
1286                   "  %s"), line, f->name, _ecs_file_error_string(f));
1287     else
1288       ecs_error(__FILE__, __LINE__, 0,
1289                 _("Error reading file \"%s\":\n\n"
1290                   "  %s"), f->name, _ecs_file_error_string(f));
1291     return retval;
1292   }
1293 
1294   /* Check for EOF condition */
1295 
1296   if (f->ptr != NULL)
1297     retval = feof(f->ptr);
1298 
1299 #if defined(HAVE_ZLIB)
1300 
1301   else if (f->gzptr != NULL)
1302     retval = gzeof(f->gzptr);
1303 
1304 #endif /* defined(HAVE_ZLIB) */
1305 
1306   if (retval != 0) {
1307     if (line > 0)
1308       ecs_error(__FILE__, __LINE__, 0,
1309                 _("Premature end of file \"%s\" at line %d\n\n"),
1310                 f->name, line);
1311     else
1312       ecs_error(__FILE__, __LINE__, 0,
1313                 _("Premature end of file \"%s\"\n\n"), f->name);
1314     retval = -1;
1315   }
1316 
1317   return retval;
1318 }
1319 
1320 /*!
1321  * \brief Formatted input from a text file (as fgets()).
1322  *
1323  * \param [out]     s    buffer to which string is to be read.
1324  * \param [in]      size maximum number of characters to be read plus one.
1325  * \param [in]      f    ecs_file_t descriptor.
1326  * \param [in, out] line file line number if available, or NULL.
1327  *
1328  * \return s on success, NULL on error or when end of file occurs and
1329  *         no characters have been read.
1330  */
1331 
1332 char *
ecs_file_gets(char * s,const int size,const ecs_file_t * f,int * line)1333 ecs_file_gets(char              *s,
1334               const int          size,
1335               const ecs_file_t  *f,
1336               int               *line)
1337 {
1338   return _ecs_file_gets(s, size, f, line, 0);
1339 }
1340 
1341 /*!
1342  * \brief Formatted input from a text file if possible (as fgets()).
1343  *
1344  * This function is similar to ecs_file_gets(), but failure to read
1345  * a line due to an end-of-file condition is not considered an error with
1346  * this variant, which may be used to read text files or sections thereof
1347  * of unknown length.
1348  *
1349  * \param [out]     s    buffer to which string is to be read.
1350  * \param [in]      size maximum number of characters to be read plus one.
1351  * \param [in]      f    ecs_file_t descriptor.
1352  * \param [in, out] line file line number if available, or NULL.
1353  *
1354  * \return s on success, NULL on error or when end of file occurs and
1355  *         no characters have been read.
1356  */
1357 
1358 char *
ecs_file_gets_try(char * s,const int size,const ecs_file_t * f,int * line)1359 ecs_file_gets_try(char              *s,
1360                   const int          size,
1361                   const ecs_file_t  *f,
1362                   int               *line)
1363 {
1364   return _ecs_file_gets(s, size, f, line, 1);
1365 }
1366 
1367 /*!
1368  * \brief Read a binary C or Fortran type record.
1369  *
1370  * A Fortran record compatible with most compilers is structured
1371  * as follows:
1372  *   - a 4-byte integer indicating the number of bytes in the record.
1373  *   - the raw data
1374  *   - a 4-byte integer indicating the number of bytes in the record.
1375  *
1376  * A C record contains only the raw data.
1377  *
1378  * \param [out] rec  pointer to location receiving data.
1379  * \param [in]  size size of each item of data in bytes.
1380  * \param [in]  ni   number of items to read.
1381  * \param [in]  f    ecs_file_t descriptor.
1382  *
1383  * \return the number of items (not bytes) sucessfully read; for a Fortran
1384  *         record, if the whole record could not be read, returns 0.
1385  */
1386 
1387 size_t
ecs_file_read(void * rec,const size_t size,const size_t ni,const ecs_file_t * f)1388 ecs_file_read(void              *rec,
1389               const size_t       size,
1390               const size_t       ni,
1391               const ecs_file_t  *f)
1392 {
1393   return _ecs_file_read(rec, size, ni, f, 0);
1394 }
1395 
1396 /*!
1397  * \brief Read a binary C or Fortran type record.
1398  *
1399  * This function is similar to ecs_file_read(), but failure to read
1400  * a record due to an end-of-file condition is not considered an error with
1401  * this variant, which may be used to read records whose presence in the
1402  * file is unknown.
1403  *
1404  * A Fortran record compatible with most compilers is structured
1405  * as follows:
1406  *   - a 4-byte integer indicating the number of bytes in the record.
1407  *   - the raw data
1408  *   - a 4-byte integer indicating the number of bytes in the record.
1409  *
1410  * A C record contains only the raw data.
1411  *
1412  * \param [out] rec  pointer to location receiving data.
1413  * \param [in]  size size of each item of data in bytes.
1414  * \param [in]  ni   number of items to read.
1415  * \param [in]  f    ecs_file_t descriptor.
1416  *
1417  * \return the number of items (not bytes) sucessfully read; for a Fortran
1418  *         record, if the whole record could not be read, returns 0.
1419  */
1420 
1421 size_t
ecs_file_read_try(void * rec,const size_t size,const size_t ni,const ecs_file_t * f)1422 ecs_file_read_try(void              *rec,
1423                   const size_t       size,
1424                   const size_t       ni,
1425                   const ecs_file_t  *f)
1426 {
1427   return _ecs_file_read(rec, size, ni, f, 1);
1428 }
1429 
1430 /*!
1431  * \brief Write a binary C or Fortran type record.
1432  *
1433  * A Fortran record compatible with most compilers is structured
1434  * as follows:
1435  *   - a 4-byte integer indicating the number of bytes in the record.
1436  *   - the raw data
1437  *   - a 4-byte integer indicating the number of bytes in the record.
1438  *
1439  * A C record contains only the raw data.
1440  *
1441  * \param [in] rec  pointer to location containing data.
1442  * \param [in] size size of each item of data in bytes.
1443  * \param [in] ni   number of items to write.
1444  * \param [in] f    ecs_file_t descriptor.
1445  *
1446  * \return the number of items (not bytes) sucessfully written.
1447  */
1448 
1449 size_t
ecs_file_write(const void * rec,const size_t size,const size_t ni,const ecs_file_t * f)1450 ecs_file_write(const void        *rec,
1451                const size_t       size,
1452                const size_t       ni,
1453                const ecs_file_t  *f)
1454 {
1455   int32_t  n_bytes;
1456   size_t   retval;
1457   size_t   rec_size;
1458 
1459   assert(sizeof(int32_t) == 4);
1460 
1461   /* Check file state */
1462 
1463   assert(f != NULL);
1464   assert(rec != NULL || ni == 0);
1465   assert(   f->type == ECS_FILE_TYPE_BINARY
1466          || f->type == ECS_FILE_TYPE_FORTRAN_BINARY);
1467   assert(f->mode == ECS_FILE_MODE_APPEND || f->mode == ECS_FILE_MODE_WRITE);
1468 
1469   if (f->ptr == NULL)
1470     ecs_error(__FILE__, __LINE__, 0,
1471               _("Error writing to closed file \"%s\""), f->name);
1472 
1473   /* Number of bytes of record to write */
1474 
1475   rec_size = size * ni;
1476 
1477   /* In Fortran binary case, write record header */
1478 
1479   if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY) {
1480 
1481     /* Check that 4 bytes is enough for record */
1482 
1483     n_bytes = (int32_t)rec_size;
1484 
1485     if ((size_t)n_bytes != rec_size) {
1486       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_write_error),
1487                 f->name, _(_ecs_file_str_f_rec_too_large));
1488       return 0;
1489     }
1490 
1491     if (f->swp_endian == 1)
1492       ecs_file_swap_endian((void *)&n_bytes, (void *)&n_bytes,
1493                            sizeof(int32_t), 1) ;
1494 
1495     if (fwrite((void *)(&n_bytes), sizeof(int32_t), 1, f->ptr) != 1) {
1496       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_write_error),
1497                 f->name, _ecs_file_error_string(f));
1498       return 0;
1499     }
1500 
1501   }
1502 
1503   /* Write the record proper (C or Fortran) */
1504 
1505   if (f->swp_endian == 1 && size > 1) {
1506 
1507     void    *buf;
1508 
1509     ECS_MALLOC(buf, rec_size, unsigned char);
1510 
1511     ecs_file_swap_endian(buf, rec, size, ni);
1512 
1513     retval = fwrite((void *)buf, (size_t)size, (size_t)ni, f->ptr);
1514 
1515     ECS_FREE(buf);
1516 
1517   }
1518   else
1519 
1520     retval = fwrite((const void *)rec, (size_t)size, (size_t)ni, f->ptr);
1521 
1522   if (retval != (size_t)ni) {
1523     if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY)
1524       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_write_error),
1525                 f->name, _ecs_file_error_string(f));
1526     else
1527       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_b_write_error),
1528                 f->name, _ecs_file_error_string(f));
1529     return retval;
1530   }
1531 
1532   /* In Fortran binary case, write record footer */
1533 
1534   if (f->type == ECS_FILE_TYPE_FORTRAN_BINARY) {
1535 
1536     if (fwrite((void *)(&n_bytes), sizeof(int32_t), 1, f->ptr) != 1) {
1537       ecs_error(__FILE__, __LINE__, 0, _(_ecs_file_str_f_write_error),
1538                 f->name, _ecs_file_error_string(f));
1539       return 0;
1540     }
1541 
1542   }
1543 
1544   return retval;
1545 }
1546 
1547 /*!
1548  * \brief Convert data from "little-endian" to "big-endian" or the reverse.
1549  *
1550  * The memory areas pointed to by src and dest should overlap either
1551  * exactly or not at all.
1552  *
1553  * \param [out] dest pointer to converted data location.
1554  * \param [in]  src  pointer to source data location.
1555  * \param [in]  size size of each item of data in bytes.
1556  * \param [in]  ni   number of data items.
1557  */
1558 
1559 void
ecs_file_swap_endian(void * dest,const void * src,const size_t size,const size_t ni)1560 ecs_file_swap_endian(void          *dest,
1561                      const void    *src,
1562                      const size_t   size,
1563                      const size_t   ni)
1564 {
1565   size_t   i, ib, shift;
1566   unsigned char  tmpswap;
1567 
1568   unsigned char  *pdest = (unsigned char *)dest;
1569   const unsigned char  *psrc = (const unsigned char *)src;
1570 
1571   for (i = 0 ; i < ni ; i++) {
1572 
1573     shift = i * size;
1574 
1575     for (ib = 0 ; ib < (size / 2) ; ib++) {
1576 
1577       tmpswap = *(psrc + shift + ib);
1578       *(pdest + shift + ib) = *(psrc + shift + (size - 1) - ib);
1579       *(pdest + shift + (size - 1) - ib) = tmpswap;
1580 
1581     }
1582 
1583   }
1584 
1585   if (dest != src && size == 1)
1586     memcpy(dest, src, ni);
1587 }
1588 
1589 /*!
1590  * \brief Create a new directory using default permissions.
1591  *
1592  * This function is similar to the POSIX function mkdir(), except that
1593  * it has no "mode" argument: by default, on a POSIX type system,
1594  * permissions include read, write, and execute access for the user,
1595  * group and others, modified by the users umask value (so with a
1596  * typical configuration, the user will have read, write, and execute
1597  * pemission, the group and others will only have read and execute
1598  * permission, but this behavior may be modified).
1599  *
1600  * Also, contrary to the usual mkdir(), if the directory already
1601  * exists (and is truly a directory), this is considered a success
1602  * and not a failure, and 0 is returned: the aim of this function
1603  * is to make a directory available, so if it already exists,
1604  * this is considered acceptable.
1605  *
1606  * \param [in] pathname name of new directory.
1607  *
1608  * \returns 0 on success, -1 if an error occured (in which case errno
1609  *          contains the appropriate error code). If the underlying
1610  *          system has no mkdir() function or it was not detected
1611  *          upon ECS configuration, 1 is returned.
1612  */
1613 
1614 int
ecs_file_mkdir_default(const char * pathname)1615 ecs_file_mkdir_default(const char  *pathname)
1616 {
1617   static const char  *str_fail = N_("Failure to create "
1618                                     "directory \"%s\":\n\n%s");
1619 
1620 #if defined(HAVE_MKDIR)
1621 
1622 #if defined(WIN32) || defined(_WIN32)
1623 
1624   mkdir(pathname);
1625   return 0;
1626 
1627 #else
1628 
1629   if (mkdir(pathname, S_IRWXU|S_IRWXG|S_IRWXO) != 0) {
1630 
1631     if (errno == EEXIST) {
1632 
1633 #if defined(HAVE_SYS_STAT_H)
1634 
1635       struct stat buf;
1636 
1637       if (stat(pathname, &buf) != 0)
1638         ecs_error(__FILE__, __LINE__, 0, _(str_fail),
1639                   pathname,
1640                   _("  A similarly named file or directory exists "
1641                     "and its status is\n  not available."));
1642       else if (S_ISDIR(buf.st_mode) != 1)
1643         ecs_error(__FILE__, __LINE__, 0, _(str_fail),
1644                   pathname,
1645                   _("  A similarly named file exists and is "
1646                     "not a directory."));
1647       else
1648         return 0;
1649 
1650 #endif
1651 
1652       errno = EEXIST; /* In case modified by stat() */
1653 
1654     }
1655     else {
1656       ecs_error(__FILE__, __LINE__, errno, _(str_fail),
1657                 pathname,
1658                 _("  A similarly named file exists and is "
1659                   "not a directory."));
1660 
1661     }
1662 
1663     return -1;
1664 
1665   } /* End of directory creation failure case */
1666 
1667 #endif
1668 
1669   return 0;
1670 
1671 #else /* #if defined(HAVE_MKDIR) */
1672 
1673   return 1;
1674 
1675 #endif /* #if defined(HAVE_MKDIR) */
1676 
1677 }
1678 
1679 /*!
1680  * \brief Check if a file exists and is a regular file.
1681  *
1682  * \param [in] name file name.
1683  *
1684  * \returns 1 if file exists and is a regular file, 0 otherwise.
1685  */
1686 
1687 int
ecs_file_isreg(const char * name)1688 ecs_file_isreg(const char  *name)
1689 {
1690   int retval = 0;
1691 
1692 #if defined(HAVE_SYS_STAT_H)
1693 
1694   struct stat s;
1695 
1696   if (stat(name, &s) != 0) {
1697     if (errno != ENOENT)
1698       ecs_error(__FILE__, __LINE__, errno,
1699                 _("Error querying information for file:\n%s."),
1700                 name) ;
1701   }
1702   else {
1703     if (S_ISREG(s.st_mode) != 0)
1704       retval = 1;
1705   }
1706 
1707 #else /* defined(HAVE_SYS_STAT_H) */
1708 
1709   /* If Posix-type API is not available, revert to basic method */
1710 
1711   FILE *f;
1712 
1713   if ((f = fopen(fic_name, "r")) != NULL) {
1714     retval = 1;
1715     fclose(f) ;
1716   }
1717 
1718 #endif /* defined(HAVE_SYS_STAT_H) */
1719 
1720   return retval;
1721 }
1722 
1723 
1724 /*!
1725  * \brief Check if a directory exists.
1726  *
1727  * \param [in] name directory name.
1728  *
1729  * \returns 1 if directory exists, 0 otherwise.
1730  */
1731 
1732 int
ecs_file_isdir(const char * name)1733 ecs_file_isdir(const char  *name)
1734 {
1735   int retval = 0;
1736 
1737 #if defined(HAVE_SYS_STAT_H)
1738 
1739   struct stat s;
1740 
1741   if (stat(name, &s) != 0) {
1742     if (errno != ENOENT)
1743       ecs_error(__FILE__, __LINE__, errno,
1744                 _("Error querying information for directory:\n%s."),
1745                 name) ;
1746   }
1747   else {
1748     if (S_ISDIR(s.st_mode) != 0)
1749       retval = 1;
1750   }
1751 
1752 #else /* defined(HAVE_SYS_STAT_H) */
1753 
1754   /* If Posix-type API is not available,
1755      consider that directories are not available either */
1756 
1757   retval = 0;
1758 
1759 #endif /* defined(HAVE_SYS_STAT_H) */
1760 
1761   return retval;
1762 }
1763 
1764 /*!
1765  * \brief Indicate Zlib version available at run time.
1766  *
1767  * It may be useful to compare the Zlib version used at compile
1768  * and run time in case we use dynamic libraries.
1769  *
1770  * \return pointer to string indicating Zlib version in use, or NULL
1771  *         if Zlib support is not available.
1772  */
1773 
1774 const char *
ecs_file_version_zlib(void)1775 ecs_file_version_zlib(void)
1776 {
1777 #if defined(HAVE_ZLIB)
1778   return zlibVersion();
1779 #else
1780   return NULL;
1781 #endif
1782 }
1783 
1784 /*!
1785  * \brief Indicate Zlib version available at compilation time.
1786  *
1787  * It may be useful to compare the Zlib version used at compile
1788  * and link time in case we use dynamic libraries.
1789  *
1790  * \return pointer to string indicating Zlib version at compilation, or NULL
1791  *         if Zlib support is not available.
1792  */
1793 
1794 const char *
ecs_file_version_build_zlib(void)1795 ecs_file_version_build_zlib(void)
1796 {
1797 #if defined(HAVE_ZLIB)
1798 #if defined(ZLIB_VERSION)
1799   return ZLIB_VERSION;
1800 #else
1801   return _("unknown");
1802 #endif
1803 #else
1804   return NULL;
1805 #endif
1806 }
1807 
1808 /*----------------------------------------------------------------------------*/
1809 
1810 #ifdef __cplusplus
1811 }
1812 #endif /* __cplusplus */
1813