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