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, ¤t_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, ¤t_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