1 /********************************************************************/
2 /*                                                                  */
3 /*  fil_rtl.c     Primitive actions for the C library file type.    */
4 /*  Copyright (C) 1989 - 2019  Thomas Mertes                        */
5 /*                                                                  */
6 /*  This file is part of the Seed7 Runtime Library.                 */
7 /*                                                                  */
8 /*  The Seed7 Runtime Library is free software; you can             */
9 /*  redistribute it and/or modify it under the terms of the GNU     */
10 /*  Lesser General Public License as published by the Free Software */
11 /*  Foundation; either version 2.1 of the License, or (at your      */
12 /*  option) any later version.                                      */
13 /*                                                                  */
14 /*  The Seed7 Runtime Library is distributed in the hope that it    */
15 /*  will be useful, but WITHOUT ANY WARRANTY; without even the      */
16 /*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /*  PURPOSE.  See the GNU Lesser General Public License for more    */
18 /*  details.                                                        */
19 /*                                                                  */
20 /*  You should have received a copy of the GNU Lesser General       */
21 /*  Public License along with this program; if not, write to the    */
22 /*  Free Software Foundation, Inc., 51 Franklin Street,             */
23 /*  Fifth Floor, Boston, MA  02110-1301, USA.                       */
24 /*                                                                  */
25 /*  Module: Seed7 Runtime Library                                   */
26 /*  File: seed7/src/fil_rtl.c                                       */
27 /*  Changes: 1992 - 1994, 2009, 2013 - 2018  Thomas Mertes          */
28 /*  Content: Primitive actions for the C library file type.         */
29 /*                                                                  */
30 /********************************************************************/
31 
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34 
35 #include "version.h"
36 
37 #include "stdlib.h"
38 #include "stdio.h"
39 #include "string.h"
40 #include "sys/types.h"
41 #include "sys/stat.h"
42 #include "signal.h"
43 #include "setjmp.h"
44 #if UNISTD_H_PRESENT
45 #include "unistd.h"
46 #endif
47 #if defined ISATTY_INCLUDE_IO_H || defined FTRUNCATE_INCLUDE_IO_H || defined LSEEK_INCLUDE_IO_H
48 #include "io.h"
49 #endif
50 #include "errno.h"
51 
52 #include "common.h"
53 #include "data_rtl.h"
54 #include "os_decls.h"
55 #include "heaputl.h"
56 #include "striutl.h"
57 #include "sigutl.h"
58 #include "ut8_rtl.h"
59 #include "cmd_rtl.h"
60 #include "stat_drv.h"
61 #include "big_drv.h"
62 #include "fil_drv.h"
63 #include "rtl_err.h"
64 
65 #undef EXTERN
66 #define EXTERN
67 #define DO_INIT
68 #include "fil_rtl.h"
69 
70 
71 #ifdef C_PLUS_PLUS
72 #define C "C"
73 #else
74 #define C
75 #endif
76 
77 #ifdef DEFINE_FSEEKI64_PROTOTYPE
78 extern C int __cdecl _fseeki64 (FILE *, __int64, int);
79 #endif
80 
81 #ifdef DEFINE_FTELLI64_PROTOTYPE
82 extern C __int64 __cdecl _ftelli64 (FILE *);
83 #endif
84 
85 #ifdef DEFINE_WPOPEN
86 DEFINE_WPOPEN
87 #endif
88 
89 #ifdef DEFINE_CHSIZE_S_PROTOTYPE
90 extern int _chsize_s (int fd, int64Type size);
91 #endif
92 
93 #ifdef DEFINE_CHSIZE_PROTOTYPE
94 extern int _chsize (int fd, long size);
95 #endif
96 
97 #define MAX_MODE_LEN               5
98 #define BUFFER_SIZE             4096
99 #define GETS_DEFAULT_SIZE    1048576
100 #define READ_STRI_INIT_SIZE      256
101 #define READ_STRI_SIZE_DELTA    2048
102 
103 
104 
105 /**
106  *  Translate a Seed7 file open mode to a C file open mode.
107  *  The following modes are accepted
108  *   Seed7 mode | C mode | Comment
109  *   "r"        | "rb"   | Open file for reading.
110  *   "w"        | "rw"   | Truncate to zero length or create file for writing.
111  *   "a"        | "ra"   | Append; open or create file for writing at end-of-file.
112  *   "r+"       | "rb+"  | Open file for update (reading and writing).
113  *   "w+"       | "rw+"  | Truncate to zero length or create file for update.
114  *   "a+"       | "ra+"  | Append; open or create file for update, writing at end-of-file.
115  *   "rt"       | "r"    | Open file for reading.
116  *   "wt"       | "w"    | Truncate to zero length or create file for writing.
117  *   "at"       | "a"    | Append; open or create file for writing at end-of-file.
118  *   "rt+"      | "r+"   | Open file for update (reading and writing).
119  *   "wt+"      | "w+"   | Truncate to zero length or create file for update.
120  *   "at+"      | "q+"   | Append; open or create file for update, writing at end-of-file.
121  *  Other Seed7 modes correspond to the C mode "".
122  *  The Seed7 modes with t are text modes and the modes
123  *  without t are binary modes.
124  *  If there is a mode character to set the O_CLOEXEC flag (FOPEN_SUPPORTS_CLOEXEC_MODE),
125  *  it is appended to os_mode.
126  */
get_mode(os_charType os_mode[MAX_MODE_LEN],const const_striType file_mode)127 static void get_mode (os_charType os_mode[MAX_MODE_LEN], const const_striType file_mode)
128 
129   {
130     int mode_pos = 0;
131 
132   /* get_mode */
133     logFunction(printf("get_mode(*, \"%s\")\n",
134                        striAsUnquotedCStri(file_mode)););
135     if (file_mode->size >= 1 &&
136         (file_mode->mem[0] == 'r' ||
137          file_mode->mem[0] == 'w' ||
138          file_mode->mem[0] == 'a')) {
139       if (file_mode->size == 1) {
140         /* Binary mode
141            r ... Open file for reading.
142            w ... Truncate to zero length or create file for writing.
143            a ... Append; open or create file for writing at end-of-file.
144         */
145         os_mode[mode_pos++] = (os_charType) file_mode->mem[0];
146         os_mode[mode_pos++] = 'b';
147       } else if (file_mode->size == 2) {
148         if (file_mode->mem[1] == '+') {
149           /* Binary mode
150              r+ ... Open file for update (reading and writing).
151              w+ ... Truncate to zero length or create file for update.
152              a+ ... Append; open or create file for update, writing at end-of-file.
153           */
154           os_mode[mode_pos++] = (os_charType) file_mode->mem[0];
155           os_mode[mode_pos++] = 'b';
156           os_mode[mode_pos++] = '+';
157          } else if (file_mode->mem[1] == 't') {
158           /* Text mode
159              rt ... Open file for reading.
160              wt ... Truncate to zero length or create file for writing.
161              at ... Append; open or create file for writing at end-of-file.
162           */
163           os_mode[mode_pos++] = (os_charType) file_mode->mem[0];
164         } /* if */
165       } else if (file_mode->size == 3) {
166         if (file_mode->mem[1] == 't' &&
167             file_mode->mem[2] == '+') {
168           /* Text mode
169              rt+ ... Open file for update (reading and writing).
170              wt+ ... Truncate to zero length or create file for update.
171              at+ ... Append; open or create file for update, writing at end-of-file.
172           */
173           os_mode[mode_pos++] = (os_charType) file_mode->mem[0];
174           os_mode[mode_pos++] = '+';
175         } /* if */
176       } /* if */
177 #if FOPEN_SUPPORTS_CLOEXEC_MODE
178       if (mode_pos != 0) {
179         os_mode[mode_pos++] = 'e';
180       } /* if */
181 #endif
182     } /* if */
183     os_mode[mode_pos++] = '\0';
184     logFunction(printf("get_mode(\"" FMT_S_OS "\", \"%s\") -->\n",
185                        os_mode, striAsUnquotedCStri(file_mode)););
186   } /* get_mode */
187 
188 
189 
190 /**
191  *  Determine the length of a file by using a seek function.
192  *  The file length is measured in bytes.
193  *  The file position is moved to the end of the file and the
194  *  end position is used as file length. Afterwards the file
195  *  position is moved back to the previous position.
196  *  This function returns an os_off_t result. The size of
197  *  os_off_t might be different from the size of intType.
198  *  @return the length of the file, or
199  *          -1 if the length could not be obtained.
200  */
seekFileLength(cFileType aFile)201 static os_off_t seekFileLength (cFileType aFile)
202 
203   {
204     os_off_t file_length;
205 
206   /* seekFileLength */
207     logFunction(printf("seekFileLength(%d)\n", safe_fileno(aFile)););
208 #if FTELL_SUCCEEDS_FOR_PIPE
209     {
210       int file_no;
211       os_fstat_struct stat_buf;
212 
213       file_no = fileno(aFile);
214       if (file_no == -1 || os_fstat(file_no, &stat_buf) != 0 ||
215           !S_ISREG(stat_buf.st_mode)) {
216         logFunction(printf("seekFileLength --> -1 (not regular file)\n"););
217         return (os_off_t) -1;
218       } /* if */
219     }
220 #endif
221 #if FSEEK_SUCCEEDS_FOR_STDIN
222     {
223       int file_no;
224 
225       file_no = fileno(aFile);
226       if (file_no == -1 || isatty(file_no)) {
227         logFunction(printf("seekFileLength --> -1 (isatty)\n"););
228         return (os_off_t) -1;
229       } /* if */
230     }
231 #endif
232 #if defined(os_fseek) && defined(os_ftell)
233     {
234       os_off_t current_file_position;
235 
236       current_file_position = os_ftell(aFile);
237       if (current_file_position == (os_off_t) -1) {
238         file_length = -1;
239       } else if (os_fseek(aFile, (os_off_t) 0, SEEK_END) != 0) {
240         file_length = -1;
241       } else {
242         file_length = os_ftell(aFile);
243         if (os_fseek(aFile, current_file_position, SEEK_SET) != 0) {
244           file_length = -1;
245         } /* if */
246       } /* if */
247     }
248 #elif defined os_fsetpos && defined os_fgetpos
249     {
250       fpos_t current_file_pos;
251       fpos_t file_pos;
252 
253       if (os_fgetpos(aFile, &current_file_pos) != 0) {
254         file_length = -1;
255       } else if (fseek(aFile, 0, SEEK_END) != 0) {
256         file_length = -1;
257       } else {
258         if (os_fgetpos(aFile, &file_pos) != 0) {
259           file_length = -1;
260         } else {
261           file_length = file_pos;
262         } /* if */
263         if (os_fsetpos(aFile, &current_file_pos) != 0) {
264           file_length = -1;
265         } /* if */
266       } /* if */
267     }
268 #else
269     {
270       int file_no;
271       os_off_t current_file_position;
272 
273       file_no = fileno(aFile);
274       if (file_no == -1) {
275         file_length = -1;
276       } else {
277         fflush(aFile);
278         current_file_position = os_lseek(file_no, (os_off_t) 0, SEEK_CUR);
279         if (current_file_position == (os_off_t) -1) {
280           file_length = -1;
281         } else {
282           file_length = os_lseek(file_no, (os_off_t) 0, SEEK_END);
283           if (file_length != (os_off_t) -1) {
284             if (os_lseek(file_no, current_file_position, SEEK_SET) == (os_off_t) -1) {
285               file_length = -1;
286             } /* if */
287           } /* if */
288         } /* if */
289       } /* if */
290     }
291 #endif
292     logFunction(printf("seekFileLength --> " FMT_D_OFF "\n", file_length););
293     return file_length;
294   } /* seekFileLength */
295 
296 
297 
298 /**
299  *  Determine the current file position.
300  *  The file position is measured in bytes.
301  *  This function uses 0 as the position of the first byte in the file.
302  *  The function returns an os_off_t result. The size of
303  *  os_off_t might be different from the size of intType.
304  *  @return the current file position, or
305  *          -1 if the file position could not be obtained.
306  */
offsetTell(cFileType aFile)307 static os_off_t offsetTell (cFileType aFile)
308 
309   {
310     os_off_t current_file_position;
311 
312   /* offsetTell */
313     logFunction(printf("offsetTell(%d)\n", safe_fileno(aFile)););
314 #if FTELL_SUCCEEDS_FOR_PIPE
315     {
316       int file_no;
317       os_fstat_struct stat_buf;
318 
319       file_no = fileno(aFile);
320       if (file_no == -1 || os_fstat(file_no, &stat_buf) != 0 ||
321           !S_ISREG(stat_buf.st_mode)) {
322         logFunction(printf("offsetTell --> -1 (not regular file)\n"););
323         return (os_off_t) -1;
324       } /* if */
325     }
326 #endif
327 #if FSEEK_SUCCEEDS_FOR_STDIN
328     {
329       int file_no;
330 
331       file_no = fileno(aFile);
332       if (file_no == -1 || isatty(file_no)) {
333         logFunction(printf("offsetTell --> -1 (isatty)\n"););
334         return (os_off_t) -1;
335       } /* if */
336     }
337 #endif
338 #ifdef os_ftell
339     current_file_position = os_ftell(aFile);
340     logErrorIfTrue(current_file_position == -1,
341                    printf("offsetTell: os_ftell(fd of file = %d) failed:\n"
342                           "errno=%d\nerror: %s\n",
343                           safe_fileno(aFile), errno, strerror(errno)););
344 #elif defined os_fgetpos
345     {
346       fpos_t file_pos;
347 
348       if (os_fgetpos(aFile, &file_pos) != 0) {
349         current_file_position = -1;
350       } else {
351         current_file_position = file_pos;
352       } /* if */
353     }
354 #else
355     {
356       int file_no;
357 
358       file_no = fileno(aFile);
359       if (file_no == -1) {
360         current_file_position = -1;
361       } else {
362         fflush(aFile);
363         current_file_position = os_lseek(file_no, (os_off_t) 0, SEEK_CUR);
364       } /* if */
365     }
366 #endif
367     logFunction(printf("offsetTell --> " FMT_D_OFF "\n", current_file_position););
368     return current_file_position;
369   } /* offsetTell */
370 
371 
372 
373 /**
374  *  Set the current file position.
375  *  The file position is measured in bytes.
376  *  This function uses 0 as the position of the first byte in the file.
377  *  The parameter 'anOffset' uses the type os_off_t. The size of
378  *  os_off_t might be different from the size of intType.
379  *  @return 0 upon successful completion, or
380  *          -1 if the file position could not be set.
381  */
offsetSeek(cFileType aFile,const os_off_t anOffset,const int origin)382 int offsetSeek (cFileType aFile, const os_off_t anOffset, const int origin)
383 
384   {
385     int result;
386 
387   /* offsetSeek */
388     logFunction(printf("offsetSeek(%d, " FMT_D_OFF ", %d)\n",
389                        safe_fileno(aFile), anOffset, origin););
390 #ifdef OS_FSEEK_OFFSET_BITS
391 #if OS_FSEEK_OFFSET_BITS == 32
392     if (anOffset > (os_off_t) INT32TYPE_MAX ||
393         anOffset < (os_off_t) INT32TYPE_MIN) {
394       logFunction(printf("offsetSeek --> -1\n"););
395       return -1;
396     } /* if */
397 #endif
398 #endif
399 #if FTELL_SUCCEEDS_FOR_PIPE
400     {
401       int file_no;
402       os_fstat_struct stat_buf;
403 
404       file_no = fileno(aFile);
405       if (file_no == -1 || os_fstat(file_no, &stat_buf) != 0 ||
406           !S_ISREG(stat_buf.st_mode)) {
407         logFunction(printf("offsetSeek --> -1 (not regular file)\n"););
408         return -1;
409       } /* if */
410     }
411 #endif
412 #if FSEEK_SUCCEEDS_FOR_STDIN
413     {
414       int file_no;
415 
416       file_no = fileno(aFile);
417       if (file_no == -1 || isatty(file_no)) {
418         logFunction(printf("offsetSeek --> -1 (isatty)\n"););
419         return -1;
420       } /* if */
421     }
422 #endif
423 #ifdef os_fseek
424     result = os_fseek(aFile, anOffset, origin);
425     logErrorIfTrue(result != 0,
426                    printf("offsetSeek: os_fseek(fd of file = %d, "
427                           FMT_D_OFF ", %d) failed:\n"
428                           "errno=%d\nerror: %s\n",
429                           safe_fileno(aFile), anOffset, origin,
430                           errno, strerror(errno)););
431 #elif defined os_fsetpos && defined os_fgetpos
432     {
433       fpos_t file_pos;
434 
435       switch (origin) {
436         case SEEK_SET:
437           file_pos = anOffset;
438           break;
439         case SEEK_CUR:
440           if (os_fgetpos(aFile, &file_pos) != 0) {
441             logFunction(printf("offsetSeek --> -1\n"););
442             return -1;
443           } else {
444             file_pos += anOffset;
445           } /* if */
446           break;
447         case SEEK_END:
448           if (fseek(aFile, 0, SEEK_END) != 0) {
449             logFunction(printf("offsetSeek --> -1\n"););
450             return -1;
451           } else if (os_fgetpos(aFile, &file_pos) != 0) {
452             logFunction(printf("offsetSeek --> -1\n"););
453             return -1;
454           } else {
455             file_pos += anOffset;
456           } /* if */
457           break;
458       } /* switch */
459       if (os_fsetpos(aFile, &file_pos) != 0) {
460         result = -1;
461       } else {
462         result = 0;
463       } /* if */
464     }
465 #else
466     {
467       int file_no;
468 
469       file_no = fileno(aFile);
470       if (file_no == -1) {
471         result = -1;
472       } else {
473         fflush(aFile);
474         if (os_lseek(file_no, anOffset, origin) == (os_off_t) -1) {
475           result = -1;
476         } else {
477           result = 0;
478         } /* if */
479       } /* if */
480     }
481 #endif
482     logFunction(printf("offsetSeek --> %d\n", result););
483     return result;
484   } /* offsetSeek */
485 
486 
487 
488 /**
489  *  Compute how many bytes can be read from the current position.
490  *  Returns 0 if the current position or the file size cannot be
491  *  determined or if the current position is beyond the filesize.
492  *  Returns MAX_MEMSIZETYPE if the result does not fit into
493  *  memSizeType.
494  */
remainingBytesInFile(cFileType aFile)495 memSizeType remainingBytesInFile (cFileType aFile)
496 
497   {
498     int file_no;
499     os_fstat_struct stat_buf;
500     os_off_t file_length;
501     os_off_t current_file_position;
502     memSizeType remainingBytes;
503 
504   /* remainingBytesInFile */
505     current_file_position = offsetTell(aFile);
506     if (current_file_position == (os_off_t) -1) {
507       remainingBytes = 0;
508     } else {
509       file_no = fileno(aFile);
510       if (file_no != -1 && os_fstat(file_no, &stat_buf) == 0 &&
511           S_ISREG(stat_buf.st_mode)) {
512         /* Using stat_buf.st_size, which is filled by os_fstat() */
513         /* is okay, because we do read from a file.              */
514         file_length = stat_buf.st_size;
515       } else {
516         file_length = seekFileLength(aFile);
517       } /* if */
518       if (file_length == (os_off_t) -1) {
519         remainingBytes = 0;
520       } else if (file_length < current_file_position) {
521         remainingBytes = 0;
522       } else if ((unsigned_os_off_t) (file_length - current_file_position) >= MAX_MEMSIZETYPE) {
523         remainingBytes = MAX_MEMSIZETYPE;
524       } else {
525         remainingBytes = (memSizeType) (file_length - current_file_position);
526       } /* if */
527     } /* if */
528     return remainingBytes;
529   } /* remainingBytesInFile */
530 
531 
532 
getFileLengthUsingSeek(cFileType aFile)533 intType getFileLengthUsingSeek (cFileType aFile)
534 
535   {
536     os_off_t file_length;
537     intType length;
538 
539   /* getFileLengthUsingSeek */
540     file_length = seekFileLength(aFile);
541     if (unlikely(file_length < (os_off_t) 0)) {
542       logError(printf("getFileLengthUsingSeek: seekFileLength(%d) failed:\n"
543                       "errno=%d\nerror: %s\n",
544                       safe_fileno(aFile), errno, strerror(errno)););
545       raise_error(FILE_ERROR);
546       length = 0;
547     } else if (unlikely(file_length > INTTYPE_MAX)) {
548       logError(printf("getFileLengthUsingSeek(%d): "
549                       "File length does not fit into an integer: " FMT_D_OFF ".\n",
550                       safe_fileno(aFile), file_length););
551       raise_error(RANGE_ERROR);
552       length = 0;
553     } else {
554       length = (intType) file_length;
555     } /* if */
556     return length;
557   } /* getFileLengthUsingSeek */
558 
559 
560 
getBigFileLengthUsingSeek(cFileType aFile)561 bigIntType getBigFileLengthUsingSeek (cFileType aFile)
562 
563   {
564     os_off_t file_length;
565     bigIntType length;
566 
567   /* getBigFileLengthUsingSeek */
568     file_length = seekFileLength(aFile);
569     if (unlikely(file_length < (os_off_t) 0)) {
570       logError(printf("getBigFileLengthUsingSeek: seekFileLength(%d) failed:\n"
571                       "errno=%d\nerror: %s\n",
572                       safe_fileno(aFile), errno, strerror(errno)););
573       raise_error(FILE_ERROR);
574       length = NULL;
575     } else {
576 #if OS_OFF_T_SIZE == 32
577       length = bigFromUInt32((uint32Type) file_length);
578 #elif OS_OFF_T_SIZE == 64
579       length = bigFromUInt64((uint64Type) file_length);
580 #else
581 #error "sizeof(os_off_t) is neither 4 nor 8."
582 #endif
583     } /* if */
584     return length;
585   } /* getBigFileLengthUsingSeek */
586 
587 
588 
589 /**
590  *  Read a string, if we do not know how many bytes are available.
591  *  This function reads data into a list of buffers. This is done
592  *  until enough characters are read or EOF has been reached.
593  *  Afterwards the string is allocated, the data is copied from the
594  *  buffers and the list of buffers is freed.
595  */
read_and_alloc_stri(cFileType inFile,memSizeType chars_missing,errInfoType * err_info)596 static striType read_and_alloc_stri (cFileType inFile, memSizeType chars_missing,
597     errInfoType *err_info)
598 
599   {
600     struct bufferStruct buffer;
601     bufferList currBuffer = &buffer;
602     bufferList oldBuffer;
603     memSizeType bytes_in_buffer = LIST_BUFFER_SIZE;
604     memSizeType result_pos;
605     memSizeType result_size = 0;
606     striType result;
607 
608   /* read_and_alloc_stri */
609     logFunction(printf("read_and_alloc_stri(%d, " FMT_U_MEM ", *)\n",
610                        safe_fileno(inFile), chars_missing););
611     buffer.next = NULL;
612     while (chars_missing - result_size >= LIST_BUFFER_SIZE &&
613            bytes_in_buffer == LIST_BUFFER_SIZE) {
614       bytes_in_buffer = (memSizeType) fread(currBuffer->buffer, 1, LIST_BUFFER_SIZE, inFile);
615       /* printf("read_and_alloc_stri: bytes_in_buffer=" FMT_U_MEM "\n", bytes_in_buffer); */
616       if (unlikely(bytes_in_buffer == 0 && result_size == 0 && ferror(inFile))) {
617         logError(printf("read_and_alloc_stri: "
618                         "fread(*, 1, " FMT_U_MEM ", %d) failed:\n"
619                         "errno=%d\nerror: %s\n",
620                         (memSizeType) LIST_BUFFER_SIZE, safe_fileno(inFile),
621                         errno, strerror(errno)););
622         *err_info = FILE_ERROR;
623         result = NULL;
624       } else {
625         result_size += bytes_in_buffer;
626         if (chars_missing > result_size && bytes_in_buffer == LIST_BUFFER_SIZE) {
627           currBuffer->next = (bufferList) malloc(sizeof(struct bufferStruct));
628           if (unlikely(currBuffer->next == NULL)) {
629             logError(printf("read_and_alloc_stri(%d, " FMT_U_MEM ", *): "
630                             "malloc(" FMT_U_MEM ") failed.\n",
631                             safe_fileno(inFile), chars_missing, sizeof(struct bufferStruct)););
632             *err_info = MEMORY_ERROR;
633             result = NULL;
634             /* Leave the while loop by setting bytes_in_buffer to zero. */
635             bytes_in_buffer = 0;
636           } else {
637             currBuffer = currBuffer->next;
638             currBuffer->next = NULL;
639           } /* if */
640         } /* if */
641       } /* if */
642     } /* while */
643     if (chars_missing > result_size &&
644         bytes_in_buffer == LIST_BUFFER_SIZE) {
645       bytes_in_buffer = (memSizeType) fread(currBuffer->buffer, 1,
646                                             chars_missing - result_size, inFile);
647       /* printf("read_and_alloc_stri: bytes_in_buffer=" FMT_U_MEM "\n", bytes_in_buffer); */
648       if (unlikely(bytes_in_buffer == 0 && result_size == 0 && ferror(inFile))) {
649         logError(printf("read_and_alloc_stri: "
650                         "fread(*, 1, " FMT_U_MEM ", %d) failed:\n"
651                         "errno=%d\nerror: %s\n",
652                         chars_missing - result_size, safe_fileno(inFile),
653                         errno, strerror(errno)););
654         *err_info = FILE_ERROR;
655         result = NULL;
656       } else {
657         result_size += bytes_in_buffer;
658       } /* if */
659     } /* if */
660     if (likely(*err_info == OKAY_NO_ERROR)) {
661       if (unlikely(!ALLOC_STRI_SIZE_OK(result, result_size))) {
662         logError(printf("read_and_alloc_stri(%d, " FMT_U_MEM ", *): "
663                         "ALLOC_STRI_SIZE_OK(*, " FMT_U_MEM ") failed.\n",
664                         safe_fileno(inFile), chars_missing, result_size););
665         *err_info = MEMORY_ERROR;
666       } else {
667         result->size = result_size;
668         currBuffer = &buffer;
669         result_pos = 0;
670         while (result_size - result_pos >= LIST_BUFFER_SIZE) {
671           memcpy_to_strelem(&result->mem[result_pos], currBuffer->buffer, LIST_BUFFER_SIZE);
672           currBuffer = currBuffer->next;
673           result_pos += LIST_BUFFER_SIZE;
674         } /* while */
675         memcpy_to_strelem(&result->mem[result_pos], currBuffer->buffer,
676                           result_size - result_pos);
677       } /* if */
678     } /* if */
679     currBuffer = buffer.next;
680     while (currBuffer != NULL) {
681       oldBuffer = currBuffer;
682       currBuffer = currBuffer->next;
683       free(oldBuffer);
684     } /* while */
685     logFunction(printf("read_and_alloc_stri(%d, " FMT_U_MEM ", %d) -->\n",
686                        safe_fileno(inFile), chars_missing, *err_info););
687     return result;
688   } /* read_and_alloc_stri */
689 
690 
691 
692 #if !HAS_SIGACTION && !HAS_SIGNAL
693 /**
694  *  Read a character from 'inFile' and check if ctrl-c is pressed.
695  *  @param inFile File from which the character is read.
696  *  @param sigintReceived Flag indicating if ctrl-c has been pressed.
697  *  @return the character read.
698  */
readCharChkCtrlC(cFileType inFile,boolType * sigintReceived)699 int readCharChkCtrlC (cFileType inFile, boolType *sigintReceived)
700 
701   {
702     int ch;
703 
704   /* readCharChkCtrlC */
705     logFunction(printf("readCharChkCtrlC(%d, %d)\n",
706                        safe_fileno(inFile), *sigintReceived););
707     ch = getc(inFile);
708     logFunction(printf("readCharChkCtrlC(%d, %d) --> %d\n",
709                        safe_fileno(inFile), *sigintReceived, ch););
710     return ch;
711   } /* readCharChkCtrlC */
712 #endif
713 
714 
715 
716 /**
717  *  Read a character from 'inFile' and handle when ctrl-c is pressed.
718  *  If the user decides that the program should resume a prompt to
719  *  re-enter the input is written. This is the central function to read from
720  *  a terminal. It is used by doGetcFromTerminal(), doGetsFromTerminal(),
721  *  doLineReadFromTerminal() and doWordReadFromTerminal().
722  *  @param inFile File from which the character is read.
723  *  @param sigintReceived Flag indicating that ctrl-c has been pressed and
724  *         no resume is done.
725  *  @return the character read.
726  */
readCharFromTerminal(cFileType inFile,boolType * sigintReceived)727 static int readCharFromTerminal (cFileType inFile, boolType *sigintReceived)
728 
729   {
730     boolType resume;
731     int ch;
732 
733   /* readCharFromTerminal */
734     do {
735       resume = FALSE;
736       ch = readCharChkCtrlC(inFile, sigintReceived);
737       if (unlikely(*sigintReceived)) {
738         resume = callSignalHandler(SIGINT);
739         if (resume) {
740           printf("re-enter input> ");
741           fflush(stdout);
742         } /* if */
743       } /* if */
744     } while (resume);
745     return ch;
746   } /* readCharFromTerminal */
747 
748 
749 
doGetcFromTerminal(cFileType inFile)750 static charType doGetcFromTerminal (cFileType inFile)
751 
752   {
753     boolType sigintReceived = FALSE;
754     charType ch;
755 
756   /* doGetcFromTerminal */
757     ch = (charType) readCharFromTerminal(inFile, &sigintReceived);
758     if (unlikely(sigintReceived)) {
759       ch = (charType) 3;
760     } /* if */
761     return ch;
762   } /* doGetcFromTerminal */
763 
764 
765 
doGetsFromTerminal(fileType inFile,intType length)766 static striType doGetsFromTerminal (fileType inFile, intType length)
767 
768   {
769     cFileType cInFile;
770     boolType sigintReceived = FALSE;
771     int ch;
772     striType result;
773 
774   /* doGetsFromTerminal */
775     cInFile = inFile->cFile;
776     if (unlikely(length <= 0)) {
777       if (unlikely(length != 0)) {
778         logError(printf("doGetsFromTerminal(%d, " FMT_D "): Negative length.\n",
779                         safe_fileno(cInFile), length););
780         raise_error(RANGE_ERROR);
781         result = NULL;
782       } else {
783         if (unlikely(!ALLOC_STRI_SIZE_OK(result, 0))) {
784           raise_error(MEMORY_ERROR);
785         } else {
786           result->size = 0;
787         } /* if */
788       } /* if */
789     } else {
790       ch = readCharFromTerminal(cInFile, &sigintReceived);
791       if (unlikely(sigintReceived)) {
792         if (unlikely(!ALLOC_STRI_SIZE_OK(result, 0))) {
793           raise_error(MEMORY_ERROR);
794         } else {
795           result->size = 0;
796         } /* if */
797       } else {
798         if (ch != EOF) {
799           ungetc(ch, cInFile);
800         } /* if */
801         result = filGets(inFile, length);
802       } /* if */
803     } /* if */
804     return result;
805   } /* doGetsFromTerminal */
806 
807 
808 
doLineReadFromTerminal(fileType inFile,charType * terminationChar)809 static striType doLineReadFromTerminal (fileType inFile, charType *terminationChar)
810 
811   {
812     cFileType cInFile;
813     boolType sigintReceived = FALSE;
814     int ch;
815     striType result;
816 
817   /* doLineReadFromTerminal */
818     logFunction(printf("doLineReadFromTerminal(%s%d, '\\" FMT_U32 ";')\n",
819                        inFile == NULL ? "NULL " : "",
820                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
821                        *terminationChar););
822     cInFile = inFile->cFile;
823     ch = readCharFromTerminal(cInFile, &sigintReceived);
824     if (unlikely(sigintReceived)) {
825       if (unlikely(!ALLOC_STRI_SIZE_OK(result, 0))) {
826         raise_error(MEMORY_ERROR);
827       } else {
828         result->size = 0;
829       } /* if */
830     } else {
831       if (ch != EOF) {
832         ungetc(ch, cInFile);
833       } /* if */
834       result = filLineRead(inFile, terminationChar);
835     } /* if */
836     return result;
837   } /* doLineReadFromTerminal */
838 
839 
840 
doWordReadFromTerminal(fileType inFile,charType * terminationChar)841 static striType doWordReadFromTerminal (fileType inFile, charType *terminationChar)
842 
843   {
844     cFileType cInFile;
845     boolType sigintReceived = FALSE;
846     int ch;
847     striType result;
848 
849   /* doWordReadFromTerminal */
850     logFunction(printf("doWordReadFromTerminal(%s%d, '\\" FMT_U32 ";')\n",
851                        inFile == NULL ? "NULL " : "",
852                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
853                        *terminationChar););
854     cInFile = inFile->cFile;
855     ch = readCharFromTerminal(cInFile, &sigintReceived);
856     if (unlikely(sigintReceived)) {
857       if (unlikely(!ALLOC_STRI_SIZE_OK(result, 0))) {
858         raise_error(MEMORY_ERROR);
859       } else {
860         result->size = 0;
861       } /* if */
862     } else {
863       if (ch != EOF) {
864         ungetc(ch, cInFile);
865       } /* if */
866       result = filWordRead(inFile, terminationChar);
867     } /* if */
868     return result;
869   } /* doWordReadFromTerminal */
870 
871 
872 
873 /**
874  *  Determine the size of a file and return it as bigInteger.
875  *  The file length is measured in bytes.
876  *  @return the size of the given file.
877  *  @exception FILE_ERROR A system function returns an error or the
878  *             file length reported by the system is negative.
879  *  @exception MEMORY_ERROR Not enough memory to represent the result.
880  */
filBigLng(fileType aFile)881 bigIntType filBigLng (fileType aFile)
882 
883   {
884     cFileType cFile;
885     bigIntType length;
886 
887   /* filBigLng */
888     logFunction(printf("filBigLng(%s%d)\n",
889                        aFile == NULL ? "NULL " : "",
890                        aFile != NULL ? safe_fileno(aFile->cFile) : 0););
891     cFile = aFile->cFile;
892     if (unlikely(cFile == NULL)) {
893       logError(printf("filBigLng: Attempt to get the length of a closed file.\n"););
894       raise_error(FILE_ERROR);
895       length = NULL;
896     } else {
897       /* os_fstat() is not used, because when writing to a */
898       /* file the stat data is only updated after a flush. */
899       length = getBigFileLengthUsingSeek(cFile);
900     } /* if */
901     logFunction(printf("filBigLng --> %s\n", bigHexCStri(length)););
902     return length;
903   } /* filBigLng */
904 
905 
906 
907 /**
908  *  Set the current file position.
909  *  The file position is measured in bytes from the start of the file.
910  *  The first byte in the file has the position 1.
911  *  @exception RANGE_ERROR The file position is negative or zero or
912  *             the file position is not representable in the system
913  *             file position type.
914  *  @exception FILE_ERROR A system function returns an error.
915  */
filBigSeek(fileType aFile,const const_bigIntType position)916 void filBigSeek (fileType aFile, const const_bigIntType position)
917 
918   {
919     cFileType cFile;
920     os_off_t file_position;
921 
922   /* filBigSeek */
923     logFunction(printf("filBigSeek(%s%d, %s)\n",
924                        aFile == NULL ? "NULL " : "",
925                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
926                        bigHexCStri(position)););
927     cFile = aFile->cFile;
928     if (unlikely(cFile == NULL)) {
929       logError(printf("filBigSeek: Attempt to set the current position of a closed file.\n"););
930       raise_error(FILE_ERROR);
931     } else {
932 #if OS_OFF_T_SIZE == 32
933       file_position = (os_off_t) bigToInt32(position, NULL);
934 #elif OS_OFF_T_SIZE == 64
935       file_position = (os_off_t) bigToInt64(position, NULL);
936 #else
937 #error "sizeof(os_off_t) is neither 4 nor 8."
938 #endif
939       if (unlikely(file_position <= 0)) {
940         logError(printf("filBigSeek(%d, " FMT_D_OFF "): Position <= 0%s.\n",
941                         safe_fileno(cFile), file_position, file_position == 0 ?
942                             " or conversion from bigInteger failed" : ""););
943         raise_error(RANGE_ERROR);
944       } else if (unlikely(offsetSeek(cFile, file_position - 1, SEEK_SET) != 0)) {
945         logError(printf("filBigSeek(%d, %s): "
946                         "offsetSeek(%d, " FMT_D64 ", SEEK_SET) failed.\n",
947                         safe_fileno(cFile), bigHexCStri(position),
948                         safe_fileno(cFile), (int64Type) (file_position - 1)););
949         raise_error(FILE_ERROR);
950       } /* if */
951     } /* if */
952   } /* filBigSeek */
953 
954 
955 
956 /**
957  *  Obtain the current file position.
958  *  The file position is measured in bytes from the start of the file.
959  *  The first byte in the file has the position 1.
960  *  @return the current file position.
961  *  @exception FILE_ERROR A system function returns an error or the
962  *             file position reported by the system is negative.
963  *  @exception MEMORY_ERROR Not enough memory to represent the result.
964  */
filBigTell(fileType aFile)965 bigIntType filBigTell (fileType aFile)
966 
967   {
968     cFileType cFile;
969     os_off_t current_file_position;
970     bigIntType position;
971 
972   /* filBigTell */
973     logFunction(printf("filBigTell(%s%d)\n",
974                        aFile == NULL ? "NULL " : "",
975                        aFile != NULL ? safe_fileno(aFile->cFile) : 0););
976     cFile = aFile->cFile;
977     if (unlikely(cFile == NULL)) {
978       logError(printf("filBigTell: Attempt to get the current position of a closed file.\n"););
979       raise_error(FILE_ERROR);
980       position = NULL;
981     } else {
982       current_file_position = offsetTell(cFile);
983       if (unlikely(current_file_position < (os_off_t) 0)) {
984         logError(printf("filBigTell(%d): offsetTell(%d) "
985                         "returns negative offset: " FMT_D64 ".\n",
986                         safe_fileno(cFile), safe_fileno(cFile),
987                         (int64Type) current_file_position););
988         raise_error(FILE_ERROR);
989         position = NULL;
990       } else {
991 #if OS_OFF_T_SIZE == 32
992         position = bigFromUInt32((uint32Type) current_file_position + 1);
993 #elif OS_OFF_T_SIZE == 64
994         position = bigFromUInt64((uint64Type) current_file_position + 1);
995 #else
996 #error "sizeof(os_off_t) is neither 4 nor 8."
997 #endif
998       } /* if */
999     } /* if */
1000     logFunction(printf("filBigTell --> %s\n", bigHexCStri(position)););
1001     return position;
1002   } /* filBigTell */
1003 
1004 
1005 
1006 /**
1007  *  Close a clib_file.
1008  *  @exception FILE_ERROR A system function returns an error.
1009  */
filClose(fileType aFile)1010 void filClose (fileType aFile)
1011 
1012   { /* filClose */
1013     logFunction(printf("filClose(" FMT_U_MEM " %s%d (usage=" FMT_U "))\n",
1014                        (memSizeType) aFile,
1015                        aFile == NULL ? "NULL " : "",
1016                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
1017                        aFile != NULL ? aFile->usage_count : (uintType) 0););
1018     if (unlikely(aFile->cFile == NULL)) {
1019       logError(printf("filClose: fclose(NULL)\n"););
1020       raise_error(FILE_ERROR);
1021     } else if (unlikely(fclose(aFile->cFile) != 0)) {
1022       logError(printf("filClose: fclose(%d) failed:\n"
1023                       "errno=%d\nerror: %s\n",
1024                       safe_fileno(aFile->cFile), errno, strerror(errno)););
1025       /* After the call to fclose(), any use of */
1026       /* cFile results in undefined behavior.   */
1027       aFile->cFile = NULL;
1028       raise_error(FILE_ERROR);
1029     } else {
1030       aFile->cFile = NULL;
1031     } /* if */
1032     logFunction(printf("filClose(" FMT_U_MEM " %s%d (usage=" FMT_U ")) -->\n",
1033                        (memSizeType) aFile,
1034                        aFile == NULL ? "NULL " : "",
1035                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
1036                        aFile != NULL ? aFile->usage_count : (uintType) 0););
1037   } /* filClose */
1038 
1039 
1040 
1041 /**
1042  *  Assign source to *dest.
1043  *  A copy function assumes that *dest contains a legal value.
1044  */
filCpy(fileType * const dest,const fileType source)1045 void filCpy (fileType *const dest, const fileType source)
1046 
1047   { /* filCpy */
1048     logFunction(printf("filCpy(" FMT_U_MEM " %s%d (usage=" FMT_U "), "
1049                        FMT_U_MEM " %s%d (usage=" FMT_U "))\n",
1050                        (memSizeType) *dest,
1051                        *dest == NULL ? "NULL " : "",
1052                        *dest != NULL ? safe_fileno((*dest)->cFile) : 0,
1053                        *dest != NULL ? (*dest)->usage_count : (uintType) 0,
1054                        (memSizeType) source,
1055                        source == NULL ? "NULL " : "",
1056                        source != NULL ? safe_fileno(source->cFile) : 0,
1057                        source != NULL ? source->usage_count : (uintType) 0););
1058     if (source != NULL && source->usage_count != 0) {
1059       source->usage_count++;
1060     } /* if */
1061     if (*dest != NULL && (*dest)->usage_count != 0) {
1062       (*dest)->usage_count--;
1063       if ((*dest)->usage_count == 0) {
1064         filFree(*dest);
1065       } /* if */
1066     } /* if */
1067     *dest = source;
1068     logFunction(printf("filCpy(" FMT_U_MEM " %s%d (usage=" FMT_U "), "
1069                        FMT_U_MEM " %s%d (usage=" FMT_U ")) -->\n",
1070                        (memSizeType) *dest,
1071                        *dest == NULL ? "NULL " : "",
1072                        *dest != NULL ? safe_fileno((*dest)->cFile) : 0,
1073                        *dest != NULL ? (*dest)->usage_count : (uintType) 0,
1074                        (memSizeType) source,
1075                        source == NULL ? "NULL " : "",
1076                        source != NULL ? safe_fileno(source->cFile) : 0,
1077                        source != NULL ? source->usage_count : (uintType) 0););
1078   } /* filCpy */
1079 
1080 
1081 
1082 /**
1083  *  Reinterpret the generic parameters as fileType and call filCpy.
1084  *  Function pointers in C programs generated by the Seed7 compiler
1085  *  may point to this function. This assures correct behaviour even
1086  *  if sizeof(genericType) != sizeof(fileType).
1087  */
filCpyGeneric(genericType * const dest,const genericType source)1088 void filCpyGeneric (genericType *const dest, const genericType source)
1089 
1090   { /* filCpyGeneric */
1091     filCpy(&((rtlObjectType *) dest)->value.fileValue,
1092            ((const_rtlObjectType *) &source)->value.fileValue);
1093   } /* filCpyGeneric */
1094 
1095 
1096 
1097 /**
1098  *  Return a copy of source, that can be assigned to a new destination.
1099  *  It is assumed that the destination of the assignment is undefined.
1100  *  Create functions can be used to initialize Seed7 constants.
1101  *  @return a copy of source.
1102  */
filCreate(const fileType source)1103 fileType filCreate (const fileType source)
1104 
1105   { /* filCreate */
1106     logFunction(printf("filCreate(" FMT_U_MEM " %s%d (usage=" FMT_U ")\n",
1107                        (memSizeType) source,
1108                        source == NULL ? "NULL " : "",
1109                        source != NULL ? safe_fileno(source->cFile) : 0,
1110                        source != NULL ? source->usage_count : (uintType) 0););
1111     if (source != NULL && source->usage_count != 0) {
1112       source->usage_count++;
1113     } /* if */
1114     logFunction(printf("filCreate --> " FMT_U_MEM " %s%d (usage=" FMT_U ")\n",
1115                        (memSizeType) source,
1116                        source == NULL ? "NULL " : "",
1117                        source != NULL ? safe_fileno(source->cFile) : 0,
1118                        source != NULL ? source->usage_count : (uintType) 0););
1119     return source;
1120   } /* filCreate */
1121 
1122 
1123 
1124 /**
1125  *  Generic Create function to be used via function pointers.
1126  *  Function pointers in C programs generated by the Seed7 compiler
1127  *  may point to this function. This assures correct behaviour even
1128  *  if sizeof(genericType) != sizeof(fileType).
1129  */
filCreateGeneric(const genericType from_value)1130 genericType filCreateGeneric (const genericType from_value)
1131 
1132   {
1133     rtlObjectType result;
1134 
1135   /* filCreateGeneric */
1136     INIT_GENERIC_PTR(result.value.genericValue);
1137     result.value.fileValue =
1138         filCreate(((const_rtlObjectType *) &from_value)->value.fileValue);
1139     return result.value.genericValue;
1140   } /* filCreateGeneric */
1141 
1142 
1143 
1144 /**
1145  *  Maintain a usage count and free an unused file 'oldFile'.
1146  *  After a file is freed 'oldFile' refers to not existing memory.
1147  *  The memory where 'oldFile' is stored can be freed after filDestr.
1148  */
filDestr(const fileType oldFile)1149 void filDestr (const fileType oldFile)
1150 
1151   { /* filDestr */
1152     logFunction(printf("filDestr(" FMT_U_MEM " %s%d (usage=" FMT_U "))\n",
1153                        (memSizeType) oldFile,
1154                        oldFile == NULL ? "NULL " : "",
1155                        oldFile != NULL ? safe_fileno(oldFile->cFile) : 0,
1156                        oldFile != NULL ? oldFile->usage_count : (uintType) 0););
1157     if (oldFile != NULL && oldFile->usage_count != 0) {
1158       oldFile->usage_count--;
1159       if (oldFile->usage_count == 0) {
1160         filFree(oldFile);
1161       } /* if */
1162     } /* if */
1163   } /* filDestr */
1164 
1165 
1166 
1167 /**
1168  *  Generic Destr function to be used via function pointers.
1169  *  Function pointers in C programs generated by the Seed7 compiler
1170  *  may point to this function. This assures correct behaviour even
1171  *  if sizeof(genericType) != sizeof(fileType).
1172  */
filDestrGeneric(const genericType old_value)1173 void filDestrGeneric (const genericType old_value)
1174 
1175   { /* filDestrGeneric */
1176     filDestr(((const_rtlObjectType *) &old_value)->value.fileValue);
1177   } /* filDestrGeneric */
1178 
1179 
1180 
filEof(fileType inFile)1181 boolType filEof (fileType inFile)
1182 
1183   {
1184     cFileType cInFile;
1185     boolType eofIndicator;
1186 
1187   /* filEof */
1188     logFunction(printf("filEof(%s%d)\n",
1189                        inFile == NULL ? "NULL " : "",
1190                        inFile != NULL ? safe_fileno(inFile->cFile) : 0););
1191     cInFile = inFile->cFile;
1192     if (unlikely(cInFile == NULL)) {
1193       logError(printf("filEof: Attempt to test a closed file.\n"););
1194       raise_error(FILE_ERROR);
1195       eofIndicator = FALSE;
1196     } else {
1197       eofIndicator = feof(cInFile) != 0;
1198     } /* if */
1199     logFunction(printf("filEof(%d) --> %d\n",
1200                        safe_fileno(cInFile), eofIndicator););
1201     return eofIndicator;
1202   } /* filEof */
1203 
1204 
1205 
1206 #ifdef OUT_OF_ORDER
filFileType(fileType aFile)1207 intType filFileType (fileType aFile)
1208 
1209   {
1210     cFileType cFile;
1211     int file_no;
1212     os_fstat_struct stat_buf;
1213     intType result;
1214 
1215   /* filFileType */
1216     cFile = aFile->cFile;
1217     file_no = fileno(cFile);
1218     if (unlikely(file_no == -1 ||
1219                  os_fstat(file_no, &stat_buf) != 0)) {
1220       result = 0;
1221       raise_error(FILE_ERROR);
1222     } else {
1223       if (S_ISREG(stat_buf.st_mode)) {
1224         result = 2;
1225       } else if (S_ISDIR(stat_buf.st_mode)) {
1226         result = 3;
1227       } else if (S_ISCHR(stat_buf.st_mode)) {
1228         result = 4;
1229       } else if (S_ISBLK(stat_buf.st_mode)) {
1230         result = 5;
1231       } else if (S_ISFIFO(stat_buf.st_mode)) {
1232         result = 6;
1233       } else if (S_ISLNK(stat_buf.st_mode)) {
1234         result = 7;
1235       } else if (S_ISSOCK(stat_buf.st_mode)) {
1236         result = 8;
1237       } else {
1238         result = 1;
1239       } /* if */
1240     } /* if */
1241     return result;
1242   } /* filFileType */
1243 #endif
1244 
1245 
1246 
filFlush(fileType outFile)1247 void filFlush (fileType outFile)
1248 
1249   {
1250     cFileType cOutFile;
1251 
1252   /* filFlush */
1253     logFunction(printf("filFlush(%s%d)\n",
1254                        outFile == NULL ? "NULL " : "",
1255                        outFile != NULL ? safe_fileno(outFile->cFile) : 0););
1256     cOutFile = outFile->cFile;
1257     if (unlikely(cOutFile == NULL)) {
1258       logError(printf("filFlush: Attempt to flush a closed file.\n"););
1259       raise_error(FILE_ERROR);
1260     } else {
1261       fflush(cOutFile);
1262     } /* if */
1263   } /* filFlush */
1264 
1265 
1266 
filFree(fileType oldFile)1267 void filFree (fileType oldFile)
1268 
1269   { /* filFree */
1270     logFunction(printf("filFree(" FMT_U_MEM " %s%d (usage=" FMT_U "))\n",
1271                        (memSizeType) oldFile,
1272                        oldFile == NULL ? "NULL " : "",
1273                        oldFile != NULL ? safe_fileno(oldFile->cFile) : 0,
1274                        oldFile != NULL ? oldFile->usage_count : (uintType) 0););
1275     if (oldFile->cFile != NULL) {
1276       fclose(oldFile->cFile);
1277     } /* if */
1278     FREE_RECORD(oldFile, fileRecord, count.files);
1279   } /* filFree */
1280 
1281 
1282 
1283 /**
1284  *  Read a character from a clib_file.
1285  *  @return the character read, or EOF at the end of the file.
1286  */
filGetcChkCtrlC(fileType inFile)1287 charType filGetcChkCtrlC (fileType inFile)
1288 
1289   {
1290     cFileType cInFile;
1291     int file_no;
1292     charType result;
1293 
1294   /* filGetcChkCtrlC */
1295     cInFile = inFile->cFile;
1296     if (unlikely(cInFile == NULL)) {
1297       logError(printf("filGetcChkCtrlC: Attempt to read from closed file.\n"););
1298       raise_error(FILE_ERROR);
1299       result = 0;
1300     } else {
1301       file_no = fileno(cInFile);
1302       if (file_no != -1 && isatty(file_no)) {
1303         result = doGetcFromTerminal(cInFile);
1304       } else {
1305         result = (charType) getc(cInFile);
1306       } /* if */
1307     } /* if */
1308     return result;
1309   } /* filGetcChkCtrlC */
1310 
1311 
1312 
1313 /**
1314  *  Read a string with 'length' characters from 'inFile'.
1315  *  In order to work reasonable good for the common case (reading
1316  *  just some characters), memory for 'length' characters is requested
1317  *  with malloc(). After the data is read the result string is
1318  *  shrunk to the actual size (with realloc()). If 'length' is
1319  *  larger than GETS_DEFAULT_SIZE or the memory cannot be requested
1320  *  a different strategy is used. In this case the function tries to
1321  *  find out the number of available characters (this is possible
1322  *  for a regular file but not for a pipe). If this fails a third
1323  *  strategy is used. In this case data is read into a list of buffers.
1324  *  After the data has been read memory is requested, the data is
1325  *  copied from the buffers and the list of buffers is freed.
1326  *  @return the string read.
1327  *  @exception RANGE_ERROR The length is negative.
1328  *  @exception MEMORY_ERROR Not enough memory to represent the result.
1329  *  @exception FILE_ERROR A system function returns an error.
1330  */
filGets(fileType inFile,intType length)1331 striType filGets (fileType inFile, intType length)
1332 
1333   {
1334     cFileType cInFile;
1335     memSizeType chars_requested;
1336     memSizeType bytes_there;
1337     memSizeType allocated_size;
1338     memSizeType num_of_chars_read;
1339     errInfoType err_info = OKAY_NO_ERROR;
1340     striType resized_result;
1341     striType result;
1342 
1343   /* filGets */
1344     logFunction(printf("filGets(%s%d, " FMT_D ")\n",
1345                        inFile == NULL ? "NULL " : "",
1346                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
1347                        length););
1348     cInFile = inFile->cFile;
1349     if (unlikely(cInFile == NULL)) {
1350       logError(printf("filGets: Attempt to read from closed file.\n"););
1351       raise_error(FILE_ERROR);
1352       result = NULL;
1353     } else if (unlikely(length <= 0)) {
1354       if (unlikely(length != 0)) {
1355         logError(printf("filGets(%d, " FMT_D "): Negative length.\n",
1356                         safe_fileno(cInFile), length););
1357         raise_error(RANGE_ERROR);
1358         result = NULL;
1359       } else {
1360         if (unlikely(!ALLOC_STRI_SIZE_OK(result, 0))) {
1361           raise_error(MEMORY_ERROR);
1362         } else {
1363           result->size = 0;
1364         } /* if */
1365       } /* if */
1366     } else {
1367       if ((uintType) length > MAX_MEMSIZETYPE) {
1368         chars_requested = MAX_MEMSIZETYPE;
1369       } else {
1370         chars_requested = (memSizeType) length;
1371       } /* if */
1372       if (chars_requested > GETS_DEFAULT_SIZE) {
1373         /* Avoid requesting too much */
1374         result = NULL;
1375       } else {
1376         allocated_size = chars_requested;
1377         (void) ALLOC_STRI_SIZE_OK(result, allocated_size);
1378       } /* if */
1379       if (result == NULL) {
1380         bytes_there = remainingBytesInFile(cInFile);
1381         /* printf("bytes_there=" FMT_U_MEM "\n", bytes_there); */
1382         if (bytes_there != 0) {
1383           /* Now we know that bytes_there bytes are available in cInFile */
1384           if (chars_requested <= bytes_there) {
1385             allocated_size = chars_requested;
1386           } else {
1387             allocated_size = bytes_there;
1388           } /* if */
1389           /* printf("allocated_size=" FMT_U_MEM "\n", allocated_size); */
1390           if (unlikely(!ALLOC_STRI_CHECK_SIZE(result, allocated_size))) {
1391             /* printf("MAX_STRI_LEN= " FMT_U_MEM
1392                 ", SIZ_STRI(MAX_STRI_LEN)=" FMT_U_MEM "\n",
1393                 MAX_STRI_LEN, SIZ_STRI(MAX_STRI_LEN)); */
1394             raise_error(MEMORY_ERROR);
1395             return NULL;
1396           } /* if */
1397         } /* if */
1398       } /* if */
1399       if (result != NULL) {
1400         /* We have allocated a buffer for the requested number of chars
1401            or for the number of bytes which are available in the file */
1402         result->size = allocated_size;
1403         /* printf("read_size=" FMT_U_MEM "\n", allocated_size); */
1404         num_of_chars_read = (memSizeType) fread(result->mem, 1,
1405             (size_t) allocated_size, cInFile);
1406         /* printf("num_of_chars_read=" FMT_U_MEM "\n", num_of_chars_read); */
1407         if (unlikely(num_of_chars_read == 0 && ferror(cInFile))) {
1408           logError(printf("filGets: fread(*, 1, " FMT_U_MEM ", %d) failed:\n"
1409                           "errno=%d\nerror: %s\n",
1410                           allocated_size, safe_fileno(cInFile),
1411                           errno, strerror(errno)););
1412           err_info = FILE_ERROR;
1413         } else {
1414           memcpy_to_strelem(result->mem, (ustriType) result->mem,
1415                             num_of_chars_read);
1416           if (num_of_chars_read < result->size) {
1417             REALLOC_STRI_SIZE_SMALLER(resized_result, result, result->size,
1418                                       num_of_chars_read);
1419             if (unlikely(resized_result == NULL)) {
1420               err_info = MEMORY_ERROR;
1421             } else {
1422               result = resized_result;
1423               COUNT3_STRI(result->size, num_of_chars_read);
1424               result->size = num_of_chars_read;
1425             } /* if */
1426           } /* if */
1427         } /* if */
1428       } else {
1429         /* Read a string, if we do not know how many bytes are available. */
1430         result = read_and_alloc_stri(cInFile, chars_requested, &err_info);
1431       } /* if */
1432       if (unlikely(err_info != OKAY_NO_ERROR)) {
1433         if (result != NULL) {
1434           FREE_STRI(result, result->size);
1435         } /* if */
1436         raise_error(err_info);
1437         result = NULL;
1438       } /* if */
1439     } /* if */
1440     logFunction(printf("filGets(%d, " FMT_D ") --> \"%s\"\n",
1441                        safe_fileno(cInFile), length,
1442                        striAsUnquotedCStri(result)););
1443     return result;
1444   } /* filGets */
1445 
1446 
1447 
filGetsChkCtrlC(fileType inFile,intType length)1448 striType filGetsChkCtrlC (fileType inFile, intType length)
1449 
1450   {
1451     cFileType cInFile;
1452     int file_no;
1453     striType result;
1454 
1455   /* filGetsChkCtrlC */
1456     logFunction(printf("filGetsChkCtrlC(%s%d, " FMT_D ")\n",
1457                        inFile == NULL ? "NULL " : "",
1458                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
1459                        length););
1460     cInFile = inFile->cFile;
1461     if (unlikely(cInFile == NULL)) {
1462       logError(printf("filGetsChkCtrlC: Attempt to read from closed file.\n"););
1463       raise_error(FILE_ERROR);
1464       result = NULL;
1465     } else {
1466       file_no = fileno(cInFile);
1467       if (file_no != -1 && isatty(file_no)) {
1468         result = doGetsFromTerminal(inFile, length);
1469       } else {
1470         result = filGets(inFile, length);
1471       } /* if */
1472     } /* if */
1473     logFunction(printf("filGetsChkCtrlC(%d, " FMT_D ") --> \"%s\"\n",
1474                        safe_fileno(cInFile), length, striAsUnquotedCStri(result)););
1475     return result;
1476   } /* filGetsChkCtrlC */
1477 
1478 
1479 
1480 /**
1481  *  Determine if at least one character can be read successfully.
1482  *  This function allows a file to be handled like an iterator.
1483  *  @return FALSE if 'getc' would return EOF, TRUE otherwise.
1484  */
filHasNext(fileType inFile)1485 boolType filHasNext (fileType inFile)
1486 
1487   {
1488     cFileType cInFile;
1489     int next_char;
1490     boolType hasNext;
1491 
1492   /* filHasNext */
1493     logFunction(printf("filHasNext(%s%d)\n",
1494                        inFile == NULL ? "NULL " : "",
1495                        inFile != NULL ? safe_fileno(inFile->cFile) : 0););
1496     cInFile = inFile->cFile;
1497     if (unlikely(cInFile == NULL)) {
1498       logError(printf("filHasNext: Attempt to test a closed file.\n"););
1499       raise_error(FILE_ERROR);
1500       hasNext = FALSE;
1501     } else if (feof(cInFile)) {
1502       hasNext = FALSE;
1503     } else {
1504       next_char = getc(cInFile);
1505       if (next_char != EOF) {
1506         if (unlikely(ungetc(next_char, cInFile) != next_char)) {
1507           logError(printf("filHasNext: ungetc('\\" FMT_U32 ";', %d) "
1508                           "does not return '\\" FMT_U32 ";'.\n",
1509                           next_char, safe_fileno(cInFile), next_char););
1510           raise_error(FILE_ERROR);
1511           hasNext = FALSE;
1512         } else {
1513           hasNext = TRUE;
1514         } /* if */
1515       } else {
1516         clearerr(cInFile);
1517         hasNext = FALSE;
1518       } /* if */
1519     } /* if */
1520     logFunction(printf("filHasNext(%d) --> %d\n",
1521                        safe_fileno(cInFile), hasNext););
1522     return hasNext;
1523   } /* filHasNext */
1524 
1525 
1526 
filHasNextChkCtrlC(fileType inFile)1527 boolType filHasNextChkCtrlC (fileType inFile)
1528 
1529   {
1530     cFileType cInFile;
1531     int file_no;
1532     int next_char;
1533     boolType hasNext;
1534 
1535   /* filHasNextChkCtrlC */
1536     logFunction(printf("filHasNextChkCtrlC(%s%d)\n",
1537                        inFile == NULL ? "NULL " : "",
1538                        inFile != NULL ? safe_fileno(inFile->cFile) : 0););
1539     cInFile = inFile->cFile;
1540     if (unlikely(cInFile == NULL)) {
1541       logError(printf("filHasNextChkCtrlC: Attempt to test a closed file.\n"););
1542       raise_error(FILE_ERROR);
1543       hasNext = FALSE;
1544     } else if (feof(cInFile)) {
1545       hasNext = FALSE;
1546     } else {
1547       file_no = fileno(cInFile);
1548       if (file_no != -1 && isatty(file_no)) {
1549         next_char = (int) (scharType) doGetcFromTerminal(cInFile);
1550       } else {
1551         next_char = getc(cInFile);
1552       } /* if */
1553       if (next_char != EOF) {
1554         if (unlikely(ungetc(next_char, cInFile) != next_char)) {
1555           logError(printf("filHasNextChkCtrlC: "
1556                           "ungetc('\\" FMT_U32 ";', %d) "
1557                           "does not return '\\" FMT_U32 ";'.\n",
1558                           next_char, safe_fileno(cInFile), next_char););
1559           raise_error(FILE_ERROR);
1560           hasNext = FALSE;
1561         } else {
1562           hasNext = TRUE;
1563         } /* if */
1564       } else {
1565         clearerr(cInFile);
1566         hasNext = FALSE;
1567       } /* if */
1568     } /* if */
1569     logFunction(printf("filHasNextChkCtrlC(%d) --> %d\n",
1570                        safe_fileno(cInFile), hasNext););
1571     return hasNext;
1572   } /* filHasNextChkCtrlC */
1573 
1574 
1575 
1576 /**
1577  *  Read a line from a clib_file.
1578  *  The function accepts lines ending with "\n", "\r\n" or EOF.
1579  *  The line ending characters are not copied into the string.
1580  *  That means that the "\r" of a "\r\n" sequence is silently removed.
1581  *  When the function is left terminationChar contains '\n' or EOF.
1582  *  @return the line read.
1583  *  @exception MEMORY_ERROR Not enough memory to represent the result.
1584  *  @exception FILE_ERROR A system function returns an error.
1585  */
filLineRead(fileType inFile,charType * terminationChar)1586 striType filLineRead (fileType inFile, charType *terminationChar)
1587 
1588   {
1589     cFileType cInFile;
1590     register int ch;
1591     register memSizeType position;
1592     strElemType *memory;
1593     memSizeType memlength;
1594     memSizeType newmemlength;
1595     striType resized_result;
1596     striType result;
1597 
1598   /* filLineRead */
1599     logFunction(printf("filLineRead(%s%d, '\\" FMT_U32 ";')\n",
1600                        inFile == NULL ? "NULL " : "",
1601                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
1602                        *terminationChar););
1603     cInFile = inFile->cFile;
1604     if (unlikely(cInFile == NULL)) {
1605       logError(printf("filLineRead: Attempt to read from closed file.\n"););
1606       raise_error(FILE_ERROR);
1607       result = NULL;
1608     } else {
1609       memlength = READ_STRI_INIT_SIZE;
1610       if (unlikely(!ALLOC_STRI_SIZE_OK(result, memlength))) {
1611         raise_error(MEMORY_ERROR);
1612       } else {
1613         memory = result->mem;
1614         position = 0;
1615         flockfile(cInFile);
1616         while ((ch = getc_unlocked(cInFile)) != '\n' && ch != EOF) {
1617           if (position >= memlength) {
1618             newmemlength = memlength + READ_STRI_SIZE_DELTA;
1619             REALLOC_STRI_CHECK_SIZE(resized_result, result, memlength, newmemlength);
1620             if (unlikely(resized_result == NULL)) {
1621               FREE_STRI(result, memlength);
1622               funlockfile(cInFile);
1623               raise_error(MEMORY_ERROR);
1624               return NULL;
1625             } /* if */
1626             result = resized_result;
1627             COUNT3_STRI(memlength, newmemlength);
1628             memory = result->mem;
1629             memlength = newmemlength;
1630           } /* if */
1631           memory[position++] = (strElemType) ch;
1632         } /* while */
1633         funlockfile(cInFile);
1634         if (ch == '\n' && position != 0 && memory[position - 1] == '\r') {
1635           position--;
1636         } /* if */
1637         if (unlikely(ch == EOF && position == 0 && ferror(cInFile))) {
1638           FREE_STRI(result, memlength);
1639           logError(printf("filLineRead(%d, '\\" FMT_U32 ";'): "
1640                           "getc_unlocked(%d) failed:\n"
1641                           "errno=%d\nerror: %s\n",
1642                           safe_fileno(cInFile), *terminationChar,
1643                           safe_fileno(cInFile), errno, strerror(errno)););
1644           raise_error(FILE_ERROR);
1645           result = NULL;
1646         } else {
1647           REALLOC_STRI_SIZE_SMALLER(resized_result, result, memlength, position);
1648           if (unlikely(resized_result == NULL)) {
1649             FREE_STRI(result, memlength);
1650             raise_error(MEMORY_ERROR);
1651             result = NULL;
1652           } else {
1653             result = resized_result;
1654             COUNT3_STRI(memlength, position);
1655             result->size = position;
1656             *terminationChar = (charType) ch;
1657           } /* if */
1658         } /* if */
1659       } /* if */
1660     } /* if */
1661     logFunction(printf("filLineRead(%d, '\\" FMT_U32 ";') --> \"%s\"\n",
1662                        safe_fileno(cInFile), *terminationChar,
1663                        striAsUnquotedCStri(result)););
1664     return result;
1665   } /* filLineRead */
1666 
1667 
1668 
filLineReadChkCtrlC(fileType inFile,charType * terminationChar)1669 striType filLineReadChkCtrlC (fileType inFile, charType *terminationChar)
1670 
1671   {
1672     cFileType cInFile;
1673     int file_no;
1674     striType result;
1675 
1676   /* filLineReadChkCtrlC */
1677     logFunction(printf("filLineReadChkCtrlC(%s%d, '\\" FMT_U32 ";')\n",
1678                        inFile == NULL ? "NULL " : "",
1679                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
1680                        *terminationChar););
1681     cInFile = inFile->cFile;
1682     if (unlikely(cInFile == NULL)) {
1683       logError(printf("filLineReadChkCtrlC: Attempt to read from closed file.\n"););
1684       raise_error(FILE_ERROR);
1685       result = NULL;
1686     } else {
1687       file_no = fileno(cInFile);
1688       if (file_no != -1 && isatty(file_no)) {
1689         result = doLineReadFromTerminal(inFile, terminationChar);
1690       } else {
1691         result = filLineRead(inFile, terminationChar);
1692       } /* if */
1693     } /* if */
1694     logFunction(printf("filLineReadChkCtrlC(%d, '\\" FMT_U32 ";') --> \"%s\"\n",
1695                        safe_fileno(cInFile), *terminationChar,
1696                        striAsUnquotedCStri(result)););
1697     return result;
1698   } /* filLineReadChkCtrlC */
1699 
1700 
1701 
filLit(fileType aFile)1702 striType filLit (fileType aFile)
1703 
1704   {
1705     cFileType cFile;
1706     const_cstriType file_name;
1707     striType result;
1708 
1709   /* filLit */
1710     cFile = aFile->cFile;
1711     if (cFile == NULL) {
1712       file_name = "NULL";
1713     } else if (cFile == stdin) {
1714       file_name = "stdin";
1715     } else if (cFile == stdout) {
1716       file_name = "stdout";
1717     } else if (cFile == stderr) {
1718       file_name = "stderr";
1719     } else {
1720       file_name = "file";
1721     } /* if */
1722     result = cstri_to_stri(file_name);
1723     if (unlikely(result == NULL)) {
1724       raise_error(MEMORY_ERROR);
1725     } /* if */
1726     return result;
1727   } /* filLit */
1728 
1729 
1730 
1731 /**
1732  *  Obtain the length of a file.
1733  *  The file length is measured in bytes.
1734  *  @return the size of the given file.
1735  *  @exception RANGE_ERROR The file length does not fit into
1736  *             an integer value.
1737  *  @exception FILE_ERROR A system function returns an error or the
1738  *             file length reported by the system is negative.
1739  */
filLng(fileType aFile)1740 intType filLng (fileType aFile)
1741 
1742   {
1743     cFileType cFile;
1744     intType length;
1745 
1746   /* filLng */
1747     logFunction(printf("filLng(%s%d)\n",
1748                        aFile == NULL ? "NULL " : "",
1749                        aFile != NULL ? safe_fileno(aFile->cFile) : 0););
1750     cFile = aFile->cFile;
1751     if (unlikely(cFile == NULL)) {
1752       logError(printf("filLng: Attempt to get the length of a closed file.\n"););
1753       raise_error(FILE_ERROR);
1754       length = 0;
1755     } else {
1756       /* os_fstat() is not used, because when writing to a */
1757       /* file the stat data is only updated after a flush. */
1758       length = getFileLengthUsingSeek(cFile);
1759     } /* if */
1760     logFunction(printf("filLng --> " FMT_D "\n", length););
1761     return length;
1762   } /* filLng */
1763 
1764 
1765 
1766 /**
1767  *  Opens a file with the specified 'path' and 'mode'.
1768  *  There are text modes and binary modes:
1769  *  - Binary modes:
1770  *   - "r"   Open file for reading.
1771  *   - "w"   Truncate to zero length or create file for writing.
1772  *   - "a"   Append; open or create file for writing at end-of-file.
1773  *   - "r+"  Open file for update (reading and writing).
1774  *   - "w+"  Truncate to zero length or create file for update.
1775  *   - "a+"  Append; open or create file for update, writing at end-of-file.
1776  *  - Text modes:
1777  *   - "rt"  Open file for reading.
1778  *   - "wt"  Truncate to zero length or create file for writing.
1779  *   - "at"  Append; open or create file for writing at end-of-file.
1780  *   - "rt+" Open file for update (reading and writing).
1781  *   - "wt+" Truncate to zero length or create file for update.
1782  *   - "at+" Append; open or create file for update, writing at end-of-file.
1783  *
1784  *  Note that this modes differ from the ones used by the C function
1785  *  fopen(). Unicode characters in 'path' are converted to the
1786  *  representation used by the fopen() function of the operating
1787  *  system.
1788  *  @param path Path of the file to be opened. The path must
1789  *         use the standard path representation.
1790  *  @param mode Mode of the file to be opened.
1791  *  @param err_info Unchanged if the function succeeds, or
1792  *                  MEMORY_ERROR if there is not enough memory to convert
1793  *                        the path to the system path type, or
1794  *                  RANGE_ERROR if the 'mode' is not one of the allowed
1795  *                        values or 'path' does not use the standard path
1796  *                        representation or 'path' cannot be converted
1797  *                        to the system path type.
1798  *  @return the file opened, or NULL if it could not be opened or
1799  *          if 'path' refers to a directory.
1800  */
cFileOpen(const const_striType path,const const_striType mode,errInfoType * err_info)1801 static cFileType cFileOpen (const const_striType path, const const_striType mode,
1802     errInfoType *err_info)
1803 
1804   {
1805     os_striType os_path;
1806     os_charType os_mode[MAX_MODE_LEN];
1807     int path_info = PATH_IS_NORMAL;
1808 #if FOPEN_OPENS_DIRECTORIES
1809     int file_no;
1810     os_fstat_struct stat_buf;
1811 #endif
1812     cFileType result;
1813 
1814   /* cFileOpen */
1815     logFunction(printf("cFileOpen(\"%s\", ", striAsUnquotedCStri(path));
1816                 printf("\"%s\", *)\n", striAsUnquotedCStri(mode)););
1817     get_mode(os_mode, mode);
1818     if (unlikely(os_mode[0] == '\0')) {
1819       logError(printf("cFileOpen: Illegal mode: \"%s\".\n",
1820                       striAsUnquotedCStri(mode)););
1821       *err_info = RANGE_ERROR;
1822       result = NULL;
1823     } else {
1824       os_path = cp_to_os_path(path, &path_info, err_info);
1825       /* printf("os_path \"%ls\" %d %d\n", os_path, path_info, *err_info); */
1826       if (unlikely(os_path == NULL)) {
1827 #if MAP_ABSOLUTE_PATH_TO_DRIVE_LETTERS
1828         if (unlikely(path_info == PATH_IS_NORMAL))
1829 #endif
1830         {
1831           logError(printf("cFileOpen: cp_to_os_path(\"%s\", *, *) failed:\n"
1832                           "path_info=%d, err_info=%d\n",
1833                           striAsUnquotedCStri(path), path_info, *err_info););
1834         }
1835         result = NULL;
1836       } else {
1837         logMessage(printf("os_fopen(\"" FMT_S_OS "\", \"" FMT_S_OS "\")\n",
1838                           os_path, os_mode););
1839         result = os_fopen(os_path, os_mode);
1840         if (unlikely(result == NULL)) {
1841           logError(printf("cFileOpen: "
1842                           "fopen(\"" FMT_S_OS "\", \"" FMT_S_OS "\") failed:\n"
1843                           "errno=%d\nerror: %s\n",
1844                           os_path, os_mode, errno, strerror(errno)););
1845         } else {
1846 #if !FOPEN_SUPPORTS_CLOEXEC_MODE && HAS_FCNTL_SETFD_CLOEXEC
1847           file_no = fileno(result);
1848           if (file_no != -1) {
1849             fcntl(file_no, F_SETFD, fcntl(file_no, F_GETFD) | FD_CLOEXEC);
1850           } /* if */
1851 #endif
1852 #if FOPEN_OPENS_DIRECTORIES
1853           file_no = fileno(result);
1854           if (file_no != -1 && os_fstat(file_no, &stat_buf) == 0 &&
1855               S_ISDIR(stat_buf.st_mode)) {
1856             /* An attempt to open a directory with cFileOpen()   */
1857             /* returns NULL even if fopen() succeeds. On many    */
1858             /* modern operating systems functions like fgetc()   */
1859             /* and readf() fail to read from a directory anyway. */
1860             /* So it is better to fail early, when the file is   */
1861             /* opened, instead of later at an unexpected place.  */
1862             /* Even if reading a directory as file succeeds      */
1863             /* there is another issue: Reading a directory as    */
1864             /* file is not portable, since it delivers an        */
1865             /* operating system specific representation of the   */
1866             /* directory. So reading a directory as file should  */
1867             /* be avoided altogether. The functions dirOpen(),   */
1868             /* dirRead() and dirClose() provide a portable way   */
1869             /* to open, read and close a directory.              */
1870             fclose(result);
1871             logError(printf("cFileOpen: "
1872                             "fopen(\"" FMT_S_OS "\", \"" FMT_S_OS "\") "
1873                             "opened a directory. Close it and return NULL.\n",
1874                             os_path, os_mode););
1875             result = NULL;
1876           } /* if */
1877 #endif
1878         } /* if */
1879         os_stri_free(os_path);
1880       } /* if */
1881     } /* if */
1882     logFunction(printf("cFileOpen(\"%s\", ", striAsUnquotedCStri(path));
1883                 printf("\"%s\", %d) --> %d\n",
1884                        striAsUnquotedCStri(mode),
1885                        *err_info, safe_fileno(result)););
1886     return result;
1887   } /* cFileOpen */
1888 
1889 
1890 
1891 /**
1892  *  Opens a file with the specified 'path' and 'mode'.
1893  *  There are text modes and binary modes:
1894  *  - Binary modes:
1895  *   - "r"   Open file for reading.
1896  *   - "w"   Truncate to zero length or create file for writing.
1897  *   - "a"   Append; open or create file for writing at end-of-file.
1898  *   - "r+"  Open file for update (reading and writing).
1899  *   - "w+"  Truncate to zero length or create file for update.
1900  *   - "a+"  Append; open or create file for update, writing at end-of-file.
1901  *  - Text modes:
1902  *   - "rt"  Open file for reading.
1903  *   - "wt"  Truncate to zero length or create file for writing.
1904  *   - "at"  Append; open or create file for writing at end-of-file.
1905  *   - "rt+" Open file for update (reading and writing).
1906  *   - "wt+" Truncate to zero length or create file for update.
1907  *   - "at+" Append; open or create file for update, writing at end-of-file.
1908  *
1909  *  Note that this modes differ from the ones used by the C function
1910  *  fopen(). Unicode characters in 'path' are converted to the
1911  *  representation used by the fopen() function of the operating
1912  *  system.
1913  *  @param path Path of the file to be opened. The path must
1914  *         use the standard path representation.
1915  *  @param mode Mode of the file to be opened.
1916  *  @return the file opened, or CLIB_NULL_FILE if it could not be opened or
1917  *          if 'path' refers to a directory.
1918  *  @exception MEMORY_ERROR Not enough memory to convert the path
1919  *             to the system path type.
1920  *  @exception RANGE_ERROR The 'mode' is not one of the allowed
1921  *             values or 'path' does not use the standard path
1922  *             representation or 'path' cannot be converted
1923  *             to the system path type.
1924  */
filOpen(const const_striType path,const const_striType mode)1925 fileType filOpen (const const_striType path, const const_striType mode)
1926 
1927   {
1928     cFileType cFile;
1929     errInfoType err_info = OKAY_NO_ERROR;
1930     fileType fileOpened;
1931 
1932   /* filOpen */
1933     logFunction(printf("filOpen(\"%s\", ", striAsUnquotedCStri(path));
1934                 printf("\"%s\")\n", striAsUnquotedCStri(mode)););
1935     cFile = cFileOpen(path, mode, &err_info);
1936     if (unlikely(cFile == NULL)) {
1937       if (unlikely(err_info != OKAY_NO_ERROR)) {
1938         raise_error(err_info);
1939         fileOpened = NULL;
1940       } else {
1941         fileOpened = &nullFileRecord;
1942       } /* if */
1943     } else {
1944       if (unlikely(!ALLOC_RECORD(fileOpened, fileRecord, count.files))) {
1945         fclose(cFile);
1946         raise_error(MEMORY_ERROR);
1947       } else {
1948         initFileType(fileOpened, 1);
1949         fileOpened->cFile = cFile;
1950       } /* if */
1951     } /* if */
1952     logFunction(printf("filOpen(\"%s\", ", striAsUnquotedCStri(path));
1953                 printf("\"%s\") --> " FMT_U_MEM " %s%d (usage=" FMT_U ")\n",
1954                        striAsUnquotedCStri(mode),
1955                        (memSizeType) fileOpened,
1956                        fileOpened == NULL ? "NULL " : "",
1957                        fileOpened != NULL ? safe_fileno(fileOpened->cFile) : 0,
1958                        fileOpened != NULL ? fileOpened->usage_count : (uintType) 0););
1959     return fileOpened;
1960   } /* filOpen */
1961 
1962 
1963 
1964 /**
1965  *  Open the null device of the operation system for reading and writing.
1966  *  @return the null device opened, or CLIB_NULL_FILE if it could not be opened.
1967  */
filOpenNullDevice(void)1968 fileType filOpenNullDevice (void)
1969 
1970   {
1971     cFileType cFile;
1972     fileType fileOpened;
1973 
1974   /* filOpenNullDevice */
1975     logFunction(printf("filOpenNullDevice()\n"););
1976     cFile = fopen(NULL_DEVICE, "r+");
1977     if (unlikely(cFile == NULL)) {
1978       fileOpened = &nullFileRecord;
1979     } else {
1980       if (unlikely(!ALLOC_RECORD(fileOpened, fileRecord, count.files))) {
1981         fclose(cFile);
1982         raise_error(MEMORY_ERROR);
1983       } else {
1984         initFileType(fileOpened, 1);
1985         fileOpened->cFile = cFile;
1986       } /* if */
1987     } /* if */
1988     logFunction(printf("filOpenNullDevice() --> " FMT_U_MEM " %s%d (usage=" FMT_U ")\n",
1989                        (memSizeType) fileOpened,
1990                        fileOpened == NULL ? "NULL " : "",
1991                        fileOpened != NULL ? safe_fileno(fileOpened->cFile) : 0,
1992                        fileOpened != NULL ? fileOpened->usage_count : (uintType) 0););
1993     return fileOpened;
1994   } /* filOpenNullDevice */
1995 
1996 
1997 
1998 /**
1999  *  Wait for the process associated with 'aPipe' to terminate.
2000  *  @param aPipe Pipe file to be closed (created by 'filPopen').
2001  *  @exception FILE_ERROR A system function returned an error.
2002  */
filPclose(fileType aPipe)2003 void filPclose (fileType aPipe)
2004 
2005   { /* filPclose */
2006     logFunction(printf("filPclose(%s%d)\n",
2007                        aPipe == NULL ? "NULL " : "",
2008                        aPipe != NULL ? safe_fileno(aPipe->cFile) : 0););
2009 #if HAS_POPEN
2010     if (unlikely(aPipe->cFile == NULL)) {
2011       logError(printf("filPclose: fclose(NULL)\n"););
2012       raise_error(FILE_ERROR);
2013     } else if (unlikely(os_pclose(aPipe->cFile) == -1)) {
2014       logError(printf("filPclose: pclose(%d) failed:\n"
2015                       "errno=%d\nerror: %s\n",
2016                       safe_fileno(aPipe->cFile), errno, strerror(errno)););
2017       raise_error(FILE_ERROR);
2018     } else {
2019       aPipe->cFile = NULL;
2020     } /* if */
2021 #endif
2022     logFunction(printf("filPclose -->\n"););
2023   } /* filPclose */
2024 
2025 
2026 
2027 /**
2028  *  Open a pipe to a shell 'command', with 'parameters'.
2029  *  The pipe can be used to read, respectively write data
2030  *  with Latin-1 or UTF-8 encoding. Parameters which contain
2031  *  a space must be enclosed in double quotes. The commands
2032  *  supported and the format of the 'parameters' are not
2033  *  covered by the description of the 'filPopen' function.
2034  *  Due to the usage of the operating system shell and external
2035  *  programs, it is hard to write portable programs, which use
2036  *  the 'filPopen' function.
2037  *  @param command Name of the command to be executed. A path must
2038  *         use the standard path representation.
2039  *  @param parameters Space separated list of parameters for
2040  *         the 'command', or "" if there are no parameters.
2041  *  @param mode A pipe can be opened with the binary modes
2042  *         "r" (read) and "w" (write) or with the text modes
2043  *         "rt" (read) and "wt" (write).
2044  *  @return the pipe file opened, or NULL if it could not be opened.
2045  *  @exception RANGE_ERROR 'command' is not representable as
2046  *             operating system path, or 'mode' is illegal.
2047  */
filPopen(const const_striType command,const const_striType parameters,const const_striType mode)2048 fileType filPopen (const const_striType command,
2049     const const_striType parameters, const const_striType mode)
2050 
2051   {
2052 #if HAS_POPEN
2053     os_striType os_command;
2054     os_charType os_mode[MAX_MODE_LEN];
2055     int mode_pos = 0;
2056     errInfoType err_info = OKAY_NO_ERROR;
2057     cFileType cFile;
2058 #endif
2059     fileType fileOpened;
2060 
2061   /* filPopen */
2062     logFunction(printf("filPopen(\"%s\", ", striAsUnquotedCStri(command));
2063                 printf("\"%s\", ", striAsUnquotedCStri(parameters));
2064                 printf("\"%s\")\n", striAsUnquotedCStri(mode)););
2065 #if HAS_POPEN
2066     os_command = cp_to_command(command, parameters, &err_info);
2067     if (unlikely(os_command == NULL)) {
2068       logError(printf("filPopen: cp_to_command(\"%s\", ",
2069                       striAsUnquotedCStri(command));
2070                printf("\"%s\", *) failed:\n"
2071                       "err_info=%d\n",
2072                       striAsUnquotedCStri(parameters), err_info););
2073       raise_error(err_info);
2074       fileOpened = NULL;
2075     } else {
2076       if (mode->size == 1 &&
2077           (mode->mem[0] == 'r' ||
2078            mode->mem[0] == 'w')) {
2079         os_mode[mode_pos++] = (os_charType) mode->mem[0];
2080 #if POPEN_SUPPORTS_BINARY_MODE
2081         os_mode[mode_pos++] = 'b';
2082 #endif
2083       } else if (mode->size == 2 &&
2084           (mode->mem[0] == 'r' ||
2085            mode->mem[0] == 'w') &&
2086            mode->mem[1] == 't') {
2087         os_mode[mode_pos++] = (os_charType) mode->mem[0];
2088 #if POPEN_SUPPORTS_TEXT_MODE
2089         os_mode[mode_pos++] = 't';
2090 #endif
2091       } /* if */
2092       if (unlikely(mode_pos == 0)) {
2093         logError(printf("filPopen: Illegal mode: \"%s\".\n",
2094                         striAsUnquotedCStri(mode)););
2095         raise_error(RANGE_ERROR);
2096         fileOpened = NULL;
2097       } else {
2098 #if POPEN_SUPPORTS_CLOEXEC_MODE
2099         os_mode[mode_pos++] = 'e';
2100 #endif
2101         os_mode[mode_pos++] = '\0';
2102 #if defined USE_EXTENDED_LENGTH_PATH && USE_EXTENDED_LENGTH_PATH
2103         adjustCwdForShell(&err_info);
2104 #endif
2105         logMessage(printf("os_popen(\"" FMT_S_OS "\", \"" FMT_S_OS "\")\n",
2106                           os_command, os_mode););
2107         cFile = os_popen(os_command, os_mode);
2108         if (unlikely(cFile == NULL)) {
2109           logError(printf("filPopen: os_popen(\"" FMT_S_OS "\","
2110                           " \"" FMT_S_OS "\") failed:\n"
2111                           "errno=%d\nerror: %s\n",
2112                           os_command, os_mode,
2113                           errno, strerror(errno)););
2114           fileOpened = &nullFileRecord;
2115         } else {
2116           FREE_OS_STRI(os_command);
2117           if (unlikely(!ALLOC_RECORD(fileOpened, fileRecord, count.files))) {
2118             os_pclose(cFile);
2119             raise_error(MEMORY_ERROR);
2120             fileOpened = NULL;
2121           } else {
2122             initFileType(fileOpened, 1);
2123             fileOpened->cFile = cFile;
2124           } /* if */
2125         } /* if */
2126       } /* if */
2127     } /* if */
2128 #else
2129     fileOpened = &nullFileRecord;
2130 #endif
2131     logFunction(printf("filPopen(\"%s\", ", striAsUnquotedCStri(command));
2132                 printf("\"%s\", ", striAsUnquotedCStri(parameters));
2133                 printf("\"%s\") --> " FMT_U_MEM " %s%d (usage=" FMT_U ")\n",
2134                        striAsUnquotedCStri(mode),
2135                        (memSizeType) fileOpened,
2136                        fileOpened == NULL ? "NULL " : "",
2137                        fileOpened != NULL ? safe_fileno(fileOpened->cFile) : 0,
2138                        fileOpened != NULL ? fileOpened->usage_count : (uintType) 0););
2139     return fileOpened;
2140   } /* filPopen */
2141 
2142 
2143 
filPrint(const const_striType stri)2144 void filPrint (const const_striType stri)
2145 
2146   { /* filPrint */
2147     ut8Write(&stdoutFileRecord, stri);
2148     putchar('\n');
2149     fflush(stdout);
2150   } /* filPrint */
2151 
2152 
2153 
2154 /**
2155  *  Set the current file position.
2156  *  The file position is measured in bytes from the start of the file.
2157  *  The first byte in the file has the position 1.
2158  *  @exception RANGE_ERROR The file position is negative or zero or
2159  *             the file position is not representable in the system
2160  *             file position type.
2161  *  @exception FILE_ERROR A system function returns an error.
2162  */
filSeek(fileType aFile,intType position)2163 void filSeek (fileType aFile, intType position)
2164 
2165   {
2166     cFileType cFile;
2167 
2168   /* filSeek */
2169     logFunction(printf("filSeek(%s%d, " FMT_D ")\n",
2170                        aFile == NULL ? "NULL " : "",
2171                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
2172                        position););
2173     cFile = aFile->cFile;
2174     if (unlikely(cFile == NULL)) {
2175       logError(printf("filSeek: Attempt to set the current position of a closed file.\n"););
2176       raise_error(FILE_ERROR);
2177     } else if (unlikely(position <= 0)) {
2178       logError(printf("filSeek(%d, " FMT_D "): Position <= 0.\n",
2179                       safe_fileno(cFile), position););
2180       raise_error(RANGE_ERROR);
2181 #if OS_OFF_T_SIZE < INTTYPE_SIZE
2182 #if OS_OFF_T_SIZE == 32
2183     } else if (unlikely(position > INT32TYPE_MAX)) {
2184       logError(printf("filSeek(%d, " FMT_D "): "
2185                       "Position not representable in the system file position type.\n",
2186                       safe_fileno(cFile), position););
2187       raise_error(RANGE_ERROR);
2188 #elif OS_OFF_T_SIZE == 64
2189     } else if (unlikely(position > INT64TYPE_MAX)) {
2190       logError(printf("filSeek(%d, " FMT_D "): "
2191                       "Position not representable in the system file position type.\n",
2192                       safe_fileno(cFile), position););
2193       raise_error(RANGE_ERROR);
2194 #else
2195 #error "sizeof(os_off_t) is neither 4 nor 8."
2196 #endif
2197 #endif
2198     } else if (unlikely(offsetSeek(cFile, (os_off_t) (position - 1), SEEK_SET) != 0)) {
2199       logError(printf("filSeek(%d, " FMT_D "): "
2200                       "offsetSeek(%d, " FMT_D ", SEEK_SET) failed:\n"
2201                       "errno=%d\nerror: %s\n",
2202                       safe_fileno(cFile), position,
2203                       safe_fileno(cFile), position - 1,
2204                       errno, strerror(errno)););
2205       raise_error(FILE_ERROR);
2206     } /* if */
2207   } /* filSeek */
2208 
2209 
2210 
2211 /**
2212  *  Determine if the file 'aFile' is seekable.
2213  *  If a file is seekable the functions filSeek() and filTell()
2214  *  can be used to set and and obtain the current file position.
2215  *  @return TRUE, if 'aFile' is seekable, FALSE otherwise.
2216  */
filSeekable(fileType aFile)2217 boolType filSeekable (fileType aFile)
2218 
2219   {
2220     cFileType cFile;
2221     int file_no;
2222     os_fstat_struct stat_buf;
2223     boolType seekable;
2224 
2225   /* filSeekable */
2226     logFunction(printf("filSeekable(%s%d)\n",
2227                        aFile == NULL ? "NULL " : "",
2228                        aFile != NULL ? safe_fileno(aFile->cFile) : 0););
2229     cFile = aFile->cFile;
2230     if (unlikely(cFile == NULL)) {
2231       logError(printf("filSeekable: Attempt to get propery of a closed file.\n"););
2232       raise_error(FILE_ERROR);
2233       seekable = FALSE;
2234     } else {
2235       file_no = fileno(cFile);
2236       if (file_no != -1) {
2237         if (os_fstat(file_no, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)) {
2238           seekable = TRUE;
2239         } else if (os_lseek(file_no, (off_t) 0, SEEK_CUR) != (off_t) -1) {
2240           seekable = TRUE;
2241         } else {
2242           seekable = FALSE;
2243         } /* if */
2244       } else {
2245         seekable = FALSE;
2246       } /* if */
2247     } /* if */
2248     return seekable;
2249   } /* filSeekable */
2250 
2251 
2252 
filSetbuf(fileType aFile,intType mode,intType size)2253 void filSetbuf (fileType aFile, intType mode, intType size)
2254 
2255   {
2256     cFileType cFile;
2257 
2258   /* filSetbuf */
2259     logFunction(printf("filSetbuf(%s%d, " FMT_D ", " FMT_D ")\n",
2260                        aFile == NULL ? "NULL " : "",
2261                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
2262                        mode, size););
2263     cFile = aFile->cFile;
2264     if (unlikely(mode < 0 || mode > 2 || size < 0 || (uintType) size > MAX_MEMSIZETYPE)) {
2265       logError(printf("filSetbuf(%d, " FMT_D ", " FMT_D "): "
2266                       "Mode or size not in allowed range.\n",
2267                       safe_fileno(cFile), mode, size););
2268       raise_error(RANGE_ERROR);
2269     } else if (unlikely(setvbuf(cFile, NULL, (int) mode, (memSizeType) size) != 0)) {
2270       logError(printf("filSetbuf: "
2271                       "setvbuf(%d, NULL, %d, " FMT_U_MEM ") failed.:\n"
2272                       "errno=%d\nerror: %s\n",
2273                       safe_fileno(cFile), (int) mode, (memSizeType) size,
2274                       errno, strerror(errno)););
2275       raise_error(FILE_ERROR);
2276     } /* if */
2277   } /* filSetbuf */
2278 
2279 
2280 
2281 /**
2282  *  Obtain the current file position.
2283  *  The file position is measured in bytes from the start of the file.
2284  *  The first byte in the file has the position 1.
2285  *  @return the current file position.
2286  *  @exception RANGE_ERROR The file position does not fit into
2287  *             an integer value.
2288  *  @exception FILE_ERROR A system function returns an error or the
2289  *             file position reported by the system is negative.
2290  */
filTell(fileType aFile)2291 intType filTell (fileType aFile)
2292 
2293   {
2294     cFileType cFile;
2295     os_off_t current_file_position;
2296     intType position;
2297 
2298   /* filTell */
2299     logFunction(printf("filTell(%s%d)\n",
2300                        aFile == NULL ? "NULL " : "",
2301                        aFile != NULL ? safe_fileno(aFile->cFile) : 0););
2302     cFile = aFile->cFile;
2303     if (unlikely(cFile == NULL)) {
2304       logError(printf("filTell: Attempt to get the current position of a closed file.\n"););
2305       raise_error(FILE_ERROR);
2306       position = 0;
2307     } else {
2308       current_file_position = offsetTell(cFile);
2309       if (unlikely(current_file_position < (os_off_t) 0)) {
2310         logError(printf("filTell(%d): "
2311                         "offsetTell(%d) returns negative offset: " FMT_D64 ".\n",
2312                         safe_fileno(cFile), safe_fileno(cFile),
2313                         (int64Type) current_file_position););
2314         raise_error(FILE_ERROR);
2315         position = 0;
2316       } else if (unlikely(current_file_position >= INTTYPE_MAX)) {
2317         logError(printf("filTell(%d): "
2318                         "File position does not fit into an integer: " FMT_D_OFF ".\n",
2319                         safe_fileno(cFile), current_file_position););
2320         raise_error(RANGE_ERROR);
2321         position = 0;
2322       } else {
2323         position = (intType) (current_file_position + 1);
2324       } /* if */
2325     } /* if */
2326     logFunction(printf("filTell --> " FMT_D "\n", position););
2327     return position;
2328   } /* filTell */
2329 
2330 
2331 
2332 /**
2333  *  Truncate 'aFile' to the given 'length'.
2334  *  If the file previously was larger than 'length', the extra data is lost.
2335  *  If the file previously was shorter, it is extended, and the extended
2336  *  part is filled with null bytes ('\0;').
2337  *  @param aFile File to be truncated.
2338  *  @param length Requested length of 'aFile' in bytes.
2339  *  @exception RANGE_ERROR The requested length is negative or
2340  *             the length is not representable in the type
2341  *             used by the system function.
2342  *  @exception FILE_ERROR A system function returns an error.
2343  */
filTruncate(fileType aFile,intType length)2344 void filTruncate (fileType aFile, intType length)
2345 
2346   {
2347     cFileType cFile;
2348     int file_no;
2349     fpos_t pos;
2350 
2351   /* filTruncate */
2352     logFunction(printf("filTruncate(%s%d, " FMT_D ")\n",
2353                        aFile == NULL ? "NULL " : "",
2354                        aFile != NULL ? safe_fileno(aFile->cFile) : 0,
2355                        length););
2356     cFile = aFile->cFile;
2357     if (unlikely(cFile == NULL)) {
2358       logError(printf("filTruncate: Attempt to truncate a closed file.\n"););
2359       raise_error(FILE_ERROR);
2360     } else if (unlikely(length < 0)) {
2361       logError(printf("filTruncate(%d, " FMT_D "): Length < 0.\n",
2362                       safe_fileno(cFile), length););
2363       raise_error(RANGE_ERROR);
2364 #if FTRUNCATE_SIZE < INTTYPE_SIZE
2365 #if FTRUNCATE_SIZE == 32
2366     } else if (unlikely(length > INT32TYPE_MAX)) {
2367       logError(printf("filTruncate(%d, " FMT_D "): "
2368                       "Length not representable in the system file length type.\n",
2369                       safe_fileno(cFile), length););
2370       raise_error(RANGE_ERROR);
2371 #elif FTRUNCATE_SIZE == 64
2372     } else if (unlikely(length > INT64TYPE_MAX)) {
2373       logError(printf("filTruncate(%d, " FMT_D "): "
2374                       "Length not representable in the system file length type.\n",
2375                       safe_fileno(cFile), length););
2376       raise_error(RANGE_ERROR);
2377 #else
2378 #error "FTRUNCATE_SIZE is neither 32 nor 64."
2379 #endif
2380 #endif
2381     } else {
2382       file_no = fileno(cFile);
2383       if (unlikely(file_no == -1)) {
2384         logError(printf("filTruncate(%d): fileno(%d) failed:\n"
2385                         "errno=%d\nerror: %s\n",
2386                         safe_fileno(cFile), safe_fileno(cFile),
2387                         errno, strerror(errno)););
2388         raise_error(FILE_ERROR);
2389       } else {
2390         /* Use fgetpos() and fsetpos() to ensure, that the internal buffer */
2391         /* of cFile is synchronized with the underlying file descriptor file. */
2392         /* This way nothing written is lost. */
2393         if (unlikely(fgetpos(cFile, &pos) != 0 || fsetpos(cFile, &pos) != 0)) {
2394           logError(printf("filTruncate(%d): fgetpos/fsetpos(%d) failed:\n"
2395                           "errno=%d\nerror: %s\n",
2396                           file_no, file_no, errno, strerror(errno)););
2397           raise_error(FILE_ERROR);
2398         } else if (unlikely(os_ftruncate(file_no, length) != 0)) {
2399           logError(printf("filTruncate(%d, " FMT_D "): "
2400                           "ftruncate(%d, " FMT_D ") failed:\n"
2401                           "errno=%d\nerror: %s\n",
2402                           file_no, length, file_no, length,
2403                           errno, strerror(errno)););
2404           raise_error(FILE_ERROR);
2405         } /* if */
2406       } /* if */
2407     } /* if */
2408   } /* filTruncate */
2409 
2410 
2411 
2412 /**
2413  *  Read a word from a clib_file.
2414  *  Before reading the word it skips spaces and tabs. The function
2415  *  accepts words ending with " ", "\t", "\n", "\r\n" or EOF.
2416  *  The word ending characters are not copied into the string.
2417  *  That means that the "\r" of a "\r\n" sequence is silently removed.
2418  *  When the function is left terminationChar contains ' ', '\t', '\n' or
2419  *  EOF.
2420  *  @return the word read.
2421  *  @exception MEMORY_ERROR Not enough memory to represent the result.
2422  *  @exception FILE_ERROR A system function returns an error.
2423  */
filWordRead(fileType inFile,charType * terminationChar)2424 striType filWordRead (fileType inFile, charType *terminationChar)
2425 
2426   {
2427     cFileType cInFile;
2428     register int ch;
2429     register memSizeType position;
2430     strElemType *memory;
2431     memSizeType memlength;
2432     memSizeType newmemlength;
2433     striType resized_result;
2434     striType result;
2435 
2436   /* filWordRead */
2437     logFunction(printf("filWordRead(%s%d, '\\" FMT_U32 ";')\n",
2438                        inFile == NULL ? "NULL " : "",
2439                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
2440                        *terminationChar););
2441     cInFile = inFile->cFile;
2442     if (unlikely(cInFile == NULL)) {
2443       logError(printf("filWordRead: Attempt to read from closed file.\n"););
2444       raise_error(FILE_ERROR);
2445       result = NULL;
2446     } else {
2447       memlength = READ_STRI_INIT_SIZE;
2448       if (unlikely(!ALLOC_STRI_SIZE_OK(result, memlength))) {
2449         raise_error(MEMORY_ERROR);
2450       } else {
2451         memory = result->mem;
2452         position = 0;
2453         flockfile(cInFile);
2454         do {
2455           ch = getc_unlocked(cInFile);
2456         } while (ch == ' ' || ch == '\t');
2457         while (ch != ' ' && ch != '\t' &&
2458             ch != '\n' && ch != EOF) {
2459           if (position >= memlength) {
2460             newmemlength = memlength + READ_STRI_SIZE_DELTA;
2461             REALLOC_STRI_CHECK_SIZE(resized_result, result, memlength, newmemlength);
2462             if (unlikely(resized_result == NULL)) {
2463               FREE_STRI(result, memlength);
2464               funlockfile(cInFile);
2465               raise_error(MEMORY_ERROR);
2466               return NULL;
2467             } /* if */
2468             result = resized_result;
2469             COUNT3_STRI(memlength, newmemlength);
2470             memory = result->mem;
2471             memlength = newmemlength;
2472           } /* if */
2473           memory[position++] = (strElemType) ch;
2474           ch = getc_unlocked(cInFile);
2475         } /* while */
2476         funlockfile(cInFile);
2477         if (ch == '\n' && position != 0 && memory[position - 1] == '\r') {
2478           position--;
2479         } /* if */
2480         if (unlikely(ch == EOF && position == 0 && ferror(cInFile))) {
2481           FREE_STRI(result, memlength);
2482           logError(printf("filWordRead(%d, '\\" FMT_U32 ";'): "
2483                           "getc_unlocked(%d) failed:\n"
2484                           "errno=%d\nerror: %s\n",
2485                           safe_fileno(cInFile), *terminationChar,
2486                           safe_fileno(cInFile), errno, strerror(errno)););
2487           raise_error(FILE_ERROR);
2488           result = NULL;
2489         } else {
2490           REALLOC_STRI_SIZE_SMALLER(resized_result, result, memlength, position);
2491           if (unlikely(resized_result == NULL)) {
2492             FREE_STRI(result, memlength);
2493             raise_error(MEMORY_ERROR);
2494             result = NULL;
2495           } else {
2496             result = resized_result;
2497             COUNT3_STRI(memlength, position);
2498             result->size = position;
2499             *terminationChar = (charType) ch;
2500           } /* if */
2501         } /* if */
2502       } /* if */
2503     } /* if */
2504     logFunction(printf("filWordRead(%d, '\\" FMT_U32 ";') --> \"%s\"\n",
2505                        safe_fileno(cInFile), *terminationChar,
2506                        striAsUnquotedCStri(result)););
2507     return result;
2508   } /* filWordRead */
2509 
2510 
2511 
filWordReadChkCtrlC(fileType inFile,charType * terminationChar)2512 striType filWordReadChkCtrlC (fileType inFile, charType *terminationChar)
2513 
2514   {
2515     cFileType cInFile;
2516     int file_no;
2517     striType result;
2518 
2519   /* filWordReadChkCtrlC */
2520     logFunction(printf("filWordReadChkCtrlC(%s%d, '\\" FMT_U32 ";')\n",
2521                        inFile == NULL ? "NULL " : "",
2522                        inFile != NULL ? safe_fileno(inFile->cFile) : 0,
2523                        *terminationChar););
2524     cInFile = inFile->cFile;
2525     if (unlikely(cInFile == NULL)) {
2526       logError(printf("filWordReadChkCtrlC: Attempt to read from closed file.\n"););
2527       raise_error(FILE_ERROR);
2528       result = NULL;
2529     } else {
2530       file_no = fileno(cInFile);
2531       if (file_no != -1 && isatty(file_no)) {
2532         result = doWordReadFromTerminal(inFile, terminationChar);
2533       } else {
2534         result = filWordRead(inFile, terminationChar);
2535       } /* if */
2536     } /* if */
2537     logFunction(printf("filWordReadChkCtrlC(%d, '\\" FMT_U32 ";') --> \"%s\"\n",
2538                        safe_fileno(cInFile), *terminationChar,
2539                        striAsUnquotedCStri(result)););
2540     return result;
2541   } /* filWordReadChkCtrlC */
2542 
2543 
2544 
2545 /**
2546  *  Write a string to a clib_file.
2547  *  @exception FILE_ERROR A system function returns an error.
2548  *  @exception RANGE_ERROR The string contains a character that does
2549  *             not fit into a byte.
2550  */
filWrite(fileType outFile,const const_striType stri)2551 void filWrite (fileType outFile, const const_striType stri)
2552 
2553   {
2554     cFileType cOutFile;
2555     const strElemType *striElems;
2556     memSizeType len;
2557     ucharType buffer[BUFFER_SIZE];
2558 
2559   /* filWrite */
2560     logFunction(printf("filWrite(%s%d, \"%s\")\n",
2561                        outFile == NULL ? "NULL " : "",
2562                        outFile != NULL ? safe_fileno(outFile->cFile) : 0,
2563                        striAsUnquotedCStri(stri)););
2564     cOutFile = outFile->cFile;
2565     if (unlikely(cOutFile == NULL)) {
2566       logError(printf("filWrite: Attempt to write to closed file.\n"););
2567       raise_error(FILE_ERROR);
2568       return;
2569     } /* if */
2570 #if FWRITE_WRONG_FOR_READ_ONLY_FILES
2571     if (unlikely(stri->size > 0 && (cOutFile->flags & _F_WRIT) == 0)) {
2572       logError(printf("filWrite: Attempt to write to read only file: %d.\n",
2573                       safe_fileno(cOutFile)););
2574       raise_error(FILE_ERROR);
2575       return;
2576     } /* if */
2577 #endif
2578     striElems = stri->mem;
2579     for (len = stri->size; len >= BUFFER_SIZE; len -= BUFFER_SIZE) {
2580       if (unlikely(memcpy_from_strelem(buffer, striElems, BUFFER_SIZE))) {
2581         logError(printf("filWrite(%d, \"%s\"): "
2582                         "At least one character does not fit into a byte.\n",
2583                         safe_fileno(cOutFile), striAsUnquotedCStri(stri)););
2584         raise_error(RANGE_ERROR);
2585         return;
2586       } /* if */
2587       striElems = &striElems[BUFFER_SIZE];
2588       if (unlikely(BUFFER_SIZE != fwrite(buffer, 1, BUFFER_SIZE, cOutFile))) {
2589         logError(printf("filWrite: fwrite(*, 1, " FMT_U_MEM ", %d) failed:\n"
2590                         "errno=%d\nerror: %s\n",
2591                         (memSizeType) BUFFER_SIZE, safe_fileno(cOutFile),
2592                         errno, strerror(errno)););
2593         raise_error(FILE_ERROR);
2594         return;
2595       } /* if */
2596     } /* for */
2597     if (len > 0) {
2598       if (unlikely(memcpy_from_strelem(buffer, striElems, len))) {
2599         logError(printf("filWrite(%d, \"%s\"): "
2600                         "At least one character does not fit into a byte.\n",
2601                         safe_fileno(cOutFile), striAsUnquotedCStri(stri)););
2602         raise_error(RANGE_ERROR);
2603         return;
2604       } /* if */
2605       if (unlikely(len != fwrite(buffer, 1, len, cOutFile))) {
2606         logError(printf("filWrite: fwrite(*, 1, " FMT_U_MEM ", %d) failed:\n"
2607                         "errno=%d\nerror: %s\n",
2608                         len, safe_fileno(cOutFile), errno, strerror(errno)););
2609         raise_error(FILE_ERROR);
2610         return;
2611       } /* if */
2612     } /* if */
2613     logFunction(printf("filWrite -->\n"););
2614   } /* filWrite */
2615