1 /* FileInMemoryManager.cpp
2 *
3 * Copyright (C) 2017-2020 David Weenink
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "FileInMemoryManager.h"
20 #include "Collection.h"
21
22 #include "oo_DESTROY.h"
23 #include "FileInMemoryManager_def.h"
24 #include "oo_COPY.h"
25 #include "FileInMemoryManager_def.h"
26 #include "oo_EQUAL.h"
27 #include "FileInMemoryManager_def.h"
28 #include "oo_CAN_WRITE_AS_ENCODING.h"
29 #include "FileInMemoryManager_def.h"
30 #include "oo_WRITE_TEXT.h"
31 #include "FileInMemoryManager_def.h"
32 #include "oo_READ_TEXT.h"
33 #include "FileInMemoryManager_def.h"
34 #include "oo_WRITE_BINARY.h"
35 #include "FileInMemoryManager_def.h"
36 #include "oo_READ_BINARY.h"
37 #include "FileInMemoryManager_def.h"
38 #include "oo_DESCRIPTION.h"
39 #include "FileInMemoryManager_def.h"
40
41 #include <errno.h>
42
43 /*
44 File open and read emulations. The FILE * is internally used as a pointer to the index of the file in the Set.
45 List of open files has to contain per file: index, position, length (bytes), pointer to data
46 */
47
48 Thing_implement (FileInMemoryManager, Daata, 0);
49
v_info()50 void structFileInMemoryManager :: v_info () {
51 FileInMemoryManager_Parent :: v_info ();
52 MelderInfo_writeLine (U"Number of files: ", files -> size);
53 MelderInfo_writeLine (U"Total number of bytes: ", FileInMemorySet_getTotalNumberOfBytes (files.get()));
54 }
55
FileInMemoryManager_hasDirectory(FileInMemoryManager me,conststring32 name)56 bool FileInMemoryManager_hasDirectory (FileInMemoryManager me, conststring32 name) {
57 return FileInMemorySet_hasDirectory (my files.get(), name);
58 }
59
FileInMemoryManager_create(FileInMemorySet files)60 autoFileInMemoryManager FileInMemoryManager_create (FileInMemorySet files) {
61 try {
62 autoFileInMemoryManager me = Thing_new (FileInMemoryManager);
63 my files = Data_copy (files);
64 my openFiles = FileInMemorySet_create ();
65 my openFiles -> _initializeOwnership (false);
66 return me;
67 } catch (MelderError) {
68 Melder_throw (U"");
69 }
70 }
71
72 /*
73 integer SortedSetOfLong_Lookup (SortedSetOfLong me, integer number) {
74 if (my size == 0) return 0; // empty set
75 integer where = number - my at [my size] -> number; // compare with last item
76 if (where > 0) return 0; // not at end
77 if (where == 0) return my size;
78 where = number - my at [1] -> number; // compare with first item
79 if (where < 0) return 0; // not at start
80 if (where == 0) return 1;
81 integer left = 1, right = my size;
82 while (left < right - 1) {
83 integer mid = (left + right) / 2;
84 where = number - my at [mid] -> number;
85 if (where == 0) { // found
86 return mid;
87 } else if (where > 0) {
88 left = mid;
89 } else {
90 right = mid;
91 }
92 }
93 Melder_assert (right == left + 1);
94 if ((number - my at [left] -> number) == 0) {
95 return left;
96 } else if ((number - my at [right] -> number) == 0) {
97 return right;
98 } else {
99 return 0;
100 }
101 }
102 */
103
FileInMemoryManager_createFile(FileInMemoryManager me,MelderFile file)104 autoFileInMemory FileInMemoryManager_createFile (FileInMemoryManager me, MelderFile file) {
105 try {
106 autoFileInMemory thee = FileInMemory_create (file);
107 return thee;
108 } catch (MelderError) {
109 Melder_throw (me, U"Cannot create a FileInMemory object.");
110 }
111 }
112
FileInMemoryManager_extractFiles(FileInMemoryManager me,kMelder_string which,conststring32 criterion)113 autoFileInMemorySet FileInMemoryManager_extractFiles (FileInMemoryManager me, kMelder_string which, conststring32 criterion) {
114 return FileInMemorySet_extractFiles (my files.get(), which, criterion);
115 }
116
_FileInMemoryManager_getIndexInOpenFiles(FileInMemoryManager me,FILE * stream)117 static integer _FileInMemoryManager_getIndexInOpenFiles (FileInMemoryManager me, FILE *stream) {
118 const integer filesIndex = reinterpret_cast<integer> (stream);
119 Melder_require (filesIndex > 0 && filesIndex <= my files -> size,
120 U": Invalid file index: ", filesIndex);
121
122 const FileInMemory fim = static_cast<FileInMemory> (my files -> at [filesIndex]);
123 const integer openFilesIndex = FileInMemorySet_lookUp (my openFiles.get(), fim -> d_path.get());
124 return openFilesIndex;
125 }
126
127 /*
128 From http://www.cplusplus.com/reference/cstdio
129 FILE * fopen ( const char * filename, const char * mode );
130
131 Open file
132 Opens the file whose name is specified in the parameter filename and associates it with a stream that can be identified in future operations by the FILE pointer returned.
133
134 The operations that are allowed on the stream and how these are performed are defined by the mode parameter.
135
136 The returned stream is fully buffered by default if it is known to not refer to an interactive device (see setbuf).
137
138 The returned pointer can be disassociated from the file by calling fclose or freopen. All opened files are automatically closed on normal program termination.
139
140 The running environment supports at least FOPEN_MAX files open simultaneously.
141
142 Parameters
143
144 filename
145 C string containing the name of the file to be opened.
146 Its value shall follow the file name specifications of the running environment and can include a path (if supported by the system).
147 mode
148 C string containing a file access mode. It can be:
149 "r" read: Open file for input operations. The file must exist.
150 "w" write: Create an empty file for output operations. If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.
151 "a" append: Open file for output at the end of a file. Output operations always write data at the end of the file, expanding it. Repositioning operations (fseek, fsetpos, rewind) are ignored. The file is created if it does not exist.
152 "r+" read/update: Open a file for update (both for input and output). The file must exist.
153 "w+" write/update: Create an empty file and open it for update (both for input and output). If a file with the same name already exists its contents are discarded and the file is treated as a new empty file.
154 "a+" append/update: Open a file for update (both for input and output) with all output operations writing data at the end of the file. Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file. The file is created if it does not exist.
155 With the mode specifiers above the file is open as a text file. In order to open a file as a binary file, a "b" character has to be included in the mode string. This additional "b" character can either be appended at the end of the string (thus making the following compound modes: "rb", "wb", "ab", "r+b", "w+b", "a+b") or be inserted between the letter and the "+" sign for the mixed modes ("rb+", "wb+", "ab+").
156
157 The new C standard (C2011, which is not part of C++) adds a new standard subspecifier ("x"), that can be appended to any "w" specifier (to form "wx", "wbx", "w+x" or "w+bx"/"wb+x"). This subspecifier forces the function to fail if the file exists, instead of overwriting it.
158
159 If additional characters follow the sequence, the behavior depends on the library implementation: some implementations may ignore additional characters so that for example an additional "t" (sometimes used to explicitly state a text file) is accepted.
160
161 On some library implementations, opening or creating a text file with update mode may treat the stream instead as a binary file.
162
163
164 Text files are files containing sequences of lines of text. Depending on the environment where the application runs, some special character conversion may occur in input/output operations in text mode to adapt them to a system-specific text file format. Although on some environments no conversions occur and both text files and binary files are treated the same way, using the appropriate mode improves portability.
165
166 For files open for update (those which include a "+" sign), on which both input and output operations are allowed, the stream shall be flushed (fflush) or repositioned (fseek, fsetpos, rewind) before a reading operation that follows a writing operation. The stream shall be repositioned (fseek, fsetpos, rewind) before a writing operation that follows a reading operation (whenever that operation did not reach the end-of-file).
167
168 Return Value
169 If the file is successfully opened, the function returns a pointer to a FILE object that can be used to identify the stream on future operations.
170 Otherwise, a null pointer is returned.
171 On most library implementations, the errno variable is also set to a system-specific error code on failure.
172 */
FileInMemoryManager_fopen(FileInMemoryManager me,const char * filename,const char * mode)173 FILE *FileInMemoryManager_fopen (FileInMemoryManager me, const char *filename, const char *mode) {
174 try {
175 integer index = 0;
176 if (*mode == 'r') { // also covers mode == 'rb'
177 index = FileInMemorySet_lookUp (my files.get(), Melder_peek8to32(filename));
178 if (index > 0) {
179 const FileInMemory fim = (FileInMemory) my files -> at [index];
180 if (fim -> d_position == 0) // not open
181 my openFiles -> addItem_ref (fim);
182 else // reset position
183 fim -> d_position = 0;
184 } else {
185 // file does not exist, set error condition?
186 }
187 } else if (*mode == 'w') {
188
189 }
190 return reinterpret_cast<FILE *> (index);
191 } catch (MelderError) {
192 Melder_throw (U"File ", Melder_peek8to32(filename), U" cannot be opended.");
193 }
194 }
195
196 /*
197 From http://www.cplusplus.com/reference/cstdio 20171028
198 void rewind ( FILE * stream );
199
200 Set position of stream to the beginning
201 Sets the position indicator associated with stream to the beginning of the file.
202
203 The end-of-file and error internal indicators associated to the stream are cleared after a successful call to this function, and all effects from previous calls to ungetc on this stream are dropped.
204
205 On streams open for update (read+write), a call to rewind allows to switch between reading and writing.
206
207 Parameters
208
209 stream
210 Pointer to a FILE object that identifies the stream.
211
212
213 Return Value
214 none
215 */
FileInMemoryManager_rewind(FileInMemoryManager me,FILE * stream)216 void FileInMemoryManager_rewind (FileInMemoryManager me, FILE *stream) {
217 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
218 if (openFilesIndex > 0) {
219 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
220 fim -> d_position = 0;
221 fim -> d_errno = 0;
222 fim -> ungetChar = -1;
223 }
224 }
225
226 /*
227 From http://www.cplusplus.com/reference/cstdio 20171028
228 int fclose ( FILE * stream );
229
230 Close file
231 Closes the file associated with the stream and disassociates it.
232
233 All internal buffers associated with the stream are disassociated from it and flushed: the content of any unwritten output buffer is written and the content of any unread input buffer is discarded.
234
235 Even if the call fails, the stream passed as parameter will no longer be associated with the file nor its buffers.
236
237 Parameters
238
239 stream
240 Pointer to a FILE object that specifies the stream to be closed.
241
242
243 Return Value
244 If the stream is successfully closed, a zero value is returned.
245 On failure, EOF is returned.
246 */
FileInMemoryManager_fclose(FileInMemoryManager me,FILE * stream)247 int FileInMemoryManager_fclose (FileInMemoryManager me, FILE *stream) {
248 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
249 if (openFilesIndex > 0) {
250 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
251 fim -> d_position = 0;
252 fim -> d_errno = 0;
253 fim -> ungetChar = -1;
254 my openFiles -> removeItem (openFilesIndex);
255 }
256 return my errorNumber = 0; // always ok
257 }
258
259 /*
260 From http://www.cplusplus.com/reference/cstdio 20171028
261 int feof ( FILE * stream );
262
263 Check end-of-file indicator
264 Checks whether the end-of-File indicator associated with stream is set, returning a value different from zero if it is.
265
266 This indicator is generally set by a previous operation on the stream that attempted to read at or past the end-of-file.
267
268 Notice that stream's internal position indicator may point to the end-of-file for the next operation, but still, the end-of-file indicator may not be set until an operation attempts to read at that point.
269
270 This indicator is cleared by a call to clearerr, rewind, fseek, fsetpos or freopen. Although if the position indicator is not repositioned by such a call, the next i/o operation is likely to set the indicator again.
271
272 Parameters
273
274 stream
275 Pointer to a FILE object that identifies the stream.
276
277
278 Return Value
279 A non-zero value is returned in the case that the end-of-file indicator associated with the stream is set.
280 Otherwise, zero is returned.
281 */
FileInMemoryManager_feof(FileInMemoryManager me,FILE * stream)282 int FileInMemoryManager_feof (FileInMemoryManager me, FILE *stream) {
283 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
284 int eof = 0;
285 if (openFilesIndex > 0) {
286 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
287 if (fim -> d_position >= fim -> d_numberOfBytes)
288 eof = 1;
289 }
290 return eof;
291 }
292
293 /*
294 From http://www.cplusplus.com/reference/cstdio 20171028
295 int fseek ( FILE * stream, long int offset, int origin );
296
297 Reposition stream position indicator
298 Sets the position indicator associated with the stream to a new position.
299
300 For streams open in binary mode, the new position is defined by adding offset to a reference position specified by origin.
301
302 For streams open in text mode, offset shall either be zero or a value returned by a previous call to ftell, and origin shall necessarily be SEEK_SET.
303
304 If the function is called with other values for these arguments, support depends on the particular system and library implementation (non-portable).
305
306 The end-of-file internal indicator of the stream is cleared after a successful call to this function, and all effects from previous calls to ungetc on this stream are dropped.
307
308 On streams open for update (read+write), a call to fseek allows to switch between reading and writing.
309
310 Parameters
311
312 stream
313 Pointer to a FILE object that identifies the stream.
314 offset
315 Binary files: Number of bytes to offset from origin.
316 Text files: Either zero, or a value returned by ftell.
317 origin
318 Position used as reference for the offset. It is specified by one of the following constants defined in <cstdio> exclusively to be used as arguments for this function:
319 Constant Reference position
320 SEEK_SET Beginning of file
321 SEEK_CUR Current position of the file pointer
322 SEEK_END End of file *
323 * Library implementations are allowed to not meaningfully support SEEK_END (therefore, code using it has no real standard portability).
324
325
326 Return Value
327 If successful, the function returns zero.
328 Otherwise, it returns non-zero value.
329 If a read or write error occurs, the error indicator (ferror) is set.
330 */
FileInMemoryManager_fseek(FileInMemoryManager me,FILE * stream,integer offset,int origin)331 int FileInMemoryManager_fseek (FileInMemoryManager me, FILE *stream, integer offset, int origin) {
332 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
333 int errval = EBADF;
334 if (openFilesIndex > 0) {
335 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
336 integer newPosition = 0;
337 if (origin == SEEK_SET)
338 newPosition = offset;
339 else if (origin == SEEK_CUR)
340 newPosition = fim -> d_position + offset;
341 else if (origin == SEEK_END)
342 newPosition = fim -> d_numberOfBytes + offset;
343 else
344 return my errorNumber = EINVAL;
345
346 if (newPosition < 0) // > numberOfBytes is allowed
347 newPosition = 0;
348
349 fim -> d_position = newPosition;
350 fim -> ungetChar = -1;
351 errval = 0;
352 }
353 return my errorNumber = errval;
354 }
355
356 /*
357 From http://www.cplusplus.com/reference/cstdio 20171028
358 long int ftell ( FILE * stream );
359
360 Get current position in stream
361 Returns the current value of the position indicator of the stream.
362
363 For binary streams, this is the number of bytes from the beginning of the file.
364
365 For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek (if there are characters put back using ungetc still pending of being read, the behavior is undefined).
366
367 Parameters
368
369 stream
370 Pointer to a FILE object that identifies the stream.
371
372
373 Return Value
374 On success, the current value of the position indicator is returned.
375 On failure, -1L is returned, and errno is set to a system-specific positive value.
376 */
FileInMemoryManager_ftell(FileInMemoryManager me,FILE * stream)377 integer FileInMemoryManager_ftell (FileInMemoryManager me, FILE *stream) {
378 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
379 /* int errval = EBADF; */
380 integer currentPosition = -1L;
381 if (openFilesIndex > 0) {
382 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
383 currentPosition = fim -> d_position;
384 }
385 return currentPosition;
386 }
387
388 /*
389 From http://www.cplusplus.com/reference/cstdio 20171028
390 char * fgets ( char * str, int num, FILE * stream );
391
392 Get string from stream
393 Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
394
395 A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
396
397 A terminating null character is automatically appended after the characters copied to str.
398
399 Notice that fgets is quite different from gets: not only fgets accepts a stream argument, but also allows to specify the maximum size of str and includes in the string any ending newline character.
400
401 Parameters
402
403 str
404 Pointer to an array of chars where the string read is copied.
405 num
406 Maximum number of characters to be copied into str (including the terminating null-character).
407 stream
408 Pointer to a FILE object that identifies an input stream.
409 stdin can be used as argument to read from the standard input.
410
411
412 Return Value
413 On success, the function returns str.
414 If the end-of-file is encountered while attempting to read a character, the eof indicator is set (feof). If this happens before any characters could be read, the pointer returned is a null pointer (and the contents of str remain unchanged).
415 If a read error occurs, the error indicator (ferror) is set and a null pointer is also returned (but the contents pointed by str may have changed).
416 */
FileInMemoryManager_fgets(FileInMemoryManager me,char * str,int num,FILE * stream)417 char *FileInMemoryManager_fgets (FileInMemoryManager me, char *str, int num, FILE *stream) {
418 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
419 char *result = nullptr;
420
421 Melder_require (openFilesIndex > 0,
422 U": File should be open.");
423
424 FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
425 integer startPos = fim -> d_position;
426 if (startPos < fim -> d_numberOfBytes) {
427 integer i = 0, endPos = startPos + num;
428 endPos = endPos < fim -> d_numberOfBytes ? endPos : fim -> d_numberOfBytes;
429 const unsigned char * p = fim -> d_data.asArgumentToFunctionThatExpectsZeroBasedArray () + startPos;
430 char *p_str = str;
431 if (fim -> ungetChar > 0) {
432 /*
433 copy the ungetChar and advance one position in stream
434 */
435 *p_str ++ = fim -> ungetChar;
436 p ++;
437 i ++;
438 fim -> ungetChar = -1;
439 }
440 while (i ++ < num && (*p_str ++ = *p) && *p ++ != '\n');
441 str [i] = '\0';
442 fim -> d_position += i;
443 result = str; // everything ok, return the str pointer
444 } else {
445 fim -> d_errno = EOF;
446 }
447 return result;
448 }
449
450 /*
451 From http://www.cplusplus.com/reference/cstdio 20171028
452 int fgetc ( FILE * stream );
453
454 Get character from stream
455 Returns the character currently pointed by the internal file position indicator of the specified stream. The internal file position indicator is then advanced to the next character.
456
457 If the stream is at the end-of-file when called, the function returns EOF and sets the end-of-file indicator for the stream (feof).
458
459 If a read error occurs, the function returns EOF and sets the error indicator for the stream (ferror).
460
461 fgetc and getc are equivalent, except that getc may be implemented as a macro in some libraries.
462
463 Parameters
464
465 stream
466 Pointer to a FILE object that identifies an input stream.
467
468
469 Return Value
470 On success, the character read is returned (promoted to an int value).
471 The return type is int to accommodate for the special value EOF, which indicates failure:
472 If the position indicator was at the end-of-file, the function returns EOF and sets the eof indicator (feof) of stream.
473 If some other reading error happens, the function also returns EOF, but sets its error indicator (ferror) instead.
474 */
FileInMemoryManager_fgetc(FileInMemoryManager me,FILE * stream)475 int FileInMemoryManager_fgetc (FileInMemoryManager me, FILE *stream) {
476 char str [4];
477 (void) FileInMemoryManager_fgets (me, str, 1, stream);
478 return FileInMemoryManager_feof (me, stream) ? EOF : static_cast<int> (*str);
479 }
480
481 /*
482 From http://www.cplusplus.com/reference/cstdio 20171028
483 size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
484
485 Read block of data from stream
486 Reads an array of count elements, each one with a size of size bytes, from the stream and stores them in the block of memory specified by ptr.
487
488 The position indicator of the stream is advanced by the total amount of bytes read.
489
490 The total amount of bytes read if successful is (size*count).
491
492 Parameters
493
494 ptr
495 Pointer to a block of memory with a size of at least (size*count) bytes, converted to a void*.
496 size
497 Size, in bytes, of each element to be read.
498 size_t is an unsigned integral type.
499 count
500 Number of elements, each one with a size of size bytes.
501 size_t is an unsigned integral type.
502 stream
503 Pointer to a FILE object that specifies an input stream.
504
505 Return Value
506 The total number of elements successfully read is returned.
507 If this number differs from the count parameter, either a reading error occurred or the end-of-file was reached while reading. In both cases, the proper indicator is set, which can be checked with ferror and feof, respectively.
508 If either size or count is zero, the function returns zero and both the stream state and the content pointed by ptr remain unchanged.
509 size_t is an unsigned integral type.
510 */
FileInMemoryManager_fread(FileInMemoryManager me,void * ptr,size_t size,size_t count,FILE * stream)511 size_t FileInMemoryManager_fread (FileInMemoryManager me, void *ptr, size_t size, size_t count, FILE *stream) {
512 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
513
514 Melder_require (openFilesIndex > 0 && size > 0 && count > 0,
515 U": File should be open.");
516
517 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
518 size_t result = 0;
519 integer startPos = fim -> d_position;
520 if (startPos < fim -> d_numberOfBytes) {
521 integer i = 0, endPos = startPos + count * size;
522
523 if (endPos > fim -> d_numberOfBytes) {
524 count = (fim -> d_numberOfBytes - startPos) / size;
525 endPos = startPos + count * size;
526 fim -> d_errno = EOF;
527 }
528 const integer numberOfBytes = count * size;
529 const unsigned char * p = fim -> d_data.asArgumentToFunctionThatExpectsZeroBasedArray () + fim -> d_position;
530 char * str = static_cast<char *> (ptr);
531 while (i < numberOfBytes)
532 str [i ++] = *p ++;
533 fim -> d_position = endPos;
534 }
535 result = count;
536 return result;
537 }
538
539 /*
540 From http://www.cplusplus.com/reference/cstdio 20171028
541 int ungetc ( int character, FILE * stream );
542
543 Unget character from stream
544 A character is virtually put back into an input stream, decreasing its internal file position as if a previous getc operation was undone.
545
546 This character may or may not be the one read from the stream in the preceding input operation. In any case, the next character retrieved from stream is the character passed to this function, independently of the original one.
547
548 Notice though, that this only affects further input operations on that stream, and not the content of the physical file associated with it, which is not modified by any calls to this function.
549
550 Some library implementations may support this function to be called multiple times, making the characters available in the reverse order in which they were put back. Although this behavior has no standard portability guarantees, and further calls may simply fail after any number of calls beyond the first.
551
552 If successful, the function clears the end-of-file indicator of stream (if it was currently set), and decrements its internal file position indicator if it operates in binary mode; In text mode, the position indicator has unspecified value until all characters put back with ungetc have been read or discarded.
553
554 A call to fseek, fsetpos or rewind on stream will discard any characters previously put back into it with this function.
555
556 If the argument passed for the character parameter is EOF, the operation fails and the input stream remains unchanged.
557
558 Parameters
559
560 character
561 The int promotion of the character to be put back.
562 The value is internally converted to an unsigned char when put back.
563 stream
564 Pointer to a FILE object that identifies an input stream.
565
566
567 Return Value
568 On success, the character put back is returned.
569 If the operation fails, EOF is returned.
570 */
571
FileInMemoryManager_ungetc(FileInMemoryManager me,int character,FILE * stream)572 int FileInMemoryManager_ungetc (FileInMemoryManager me, int character, FILE * stream) {
573 int result = EOF;
574 if (character != EOF) {
575 const integer openFilesIndex = _FileInMemoryManager_getIndexInOpenFiles (me, stream);
576 if (openFilesIndex > 0) {
577 const FileInMemory fim = static_cast<FileInMemory> (my openFiles -> at [openFilesIndex]);
578 -- (fim -> d_position);
579 result = fim -> ungetChar = character;
580 }
581 }
582 return result;
583 }
584
585
586 /*
587 From http://www.cplusplus.com/reference/cstdio 20171028
588 int fprintf ( FILE * stream, const char * format, ... );
589
590 Write formatted data to stream
591 Writes the C string pointed by format to the stream. If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers.
592
593 After the format parameter, the function expects at least as many additional arguments as specified by format.
594
595 Parameters
596
597 stream
598 Pointer to a FILE object that identifies an output stream.
599 format
600 C string that contains the text to be written to the stream.
601 It can optionally contain embedded format specifiers that are replaced by the values specified in subsequent additional arguments and formatted as requested.
602
603 A format specifier follows this prototype:
604
605 %[flags][width][.precision][length] specifier
606
607 Where the specifier character at the end is the most significant component, since it defines the type and the interpretation of its corresponding argument:
608 specifier Output Example
609 d or i Signed decimal integer 392
610 u Unsigned decimal integer 7235
611 o Unsigned octal 610
612 x Unsigned hexadecimal integer 7fa
613 X Unsigned hexadecimal integer (uppercase) 7FA
614 f Decimal floating point, lowercase 392.65
615 F Decimal floating point, uppercase 392.65
616 e Scientific notation (mantissa/exponent), lowercase 3.9265e+2
617 E Scientific notation (mantissa/exponent), uppercase 3.9265E+2
618 g Use the shortest representation: %e or %f 392.65
619 G Use the shortest representation: %E or %F 392.65
620 a Hexadecimal floating point, lowercase -0xc.90fep-2
621 A Hexadecimal floating point, uppercase -0XC.90FEP-2
622 c Character a
623 s String of characters sample
624 p Pointer address b8000000
625 n Nothing printed.
626 The corresponding argument must be a pointer to a signed int.
627 The number of characters written so far is stored in the pointed location.
628 % A % followed by another % character will write a single % to the stream. %
629
630 The format specifier can also contain sub-specifiers: flags, width, .precision and modifiers (in that order), which are optional and follow these specifications:
631
632 flags description
633 - Left-justify within the given field width; Right justification is the default (see width sub-specifier).
634 + Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers. By default, only negative numbers are preceded with a - sign.
635 (space) If no sign is going to be written, a blank space is inserted before the value.
636 # Used with o, x or X specifiers the value is preceded with 0, 0x or 0X respectively for values different than zero.
637 Used with a, A, e, E, f, F, g or G it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written.
638 0 Left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier).
639
640 width description
641 (number) Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. The value is not truncated even if the result is larger.
642 * The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
643
644 .precision description
645 .number For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.
646 For a, A, e, E, f and F specifiers: this is the number of digits to be printed after the decimal point (by default, this is 6).
647 For g and G specifiers: This is the maximum number of significant digits to be printed.
648 For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.
649 If the period is specified without an explicit value for precision, 0 is assumed.
650 .* The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
651
652 The length sub-specifier modifies the length of the data type. This is a chart showing the types used to interpret the corresponding arguments with and without length specifier (if a different type is used, the proper type promotion or conversion is performed, if allowed):
653 specifiers
654 length d i u o x X f F e E g G a A c s p n
655 (none) int unsigned int double int char* void* int*
656 hh signed char unsigned char signed char*
657 h short int unsigned short int short int*
658 l long int unsigned long int wint_t wchar_t* long int*
659 ll long long int unsigned long long int long long int*
660 j intmax_t uintmax_t intmax_t*
661 z size_t size_t size_t*
662 t ptrdiff_t ptrdiff_t ptrdiff_t*
663 L long double
664 Note that the c specifier takes an int (or wint_t) as argument, but performs the proper conversion to a char value (or a wchar_t) before formatting it for output.
665
666 Note: Yellow rows indicate specifiers and sub-specifiers introduced by C99. See <cinttypes> for the specifiers for extended types.
667 ... (additional arguments)
668 Depending on the format string, the function may expect a sequence of additional arguments, each containing a value to be used to replace a format specifier in the format string (or a pointer to a storage location, for n).
669 There should be at least as many of these arguments as the number of values specified in the format specifiers. Additional arguments are ignored by the function.
670
671
672 Return Value
673 On success, the total number of characters written is returned.
674
675 If a writing error occurs, the error indicator (ferror) is set and a negative number is returned.
676
677 If a multibyte character encoding error occurs while writing wide characters, errno is set to EILSEQ and a negative number is returned.
678 */
FileInMemoryManager_fprintf(FileInMemoryManager me,FILE * stream,const char * format,...)679 int FileInMemoryManager_fprintf (FileInMemoryManager me, FILE * stream, const char *format, ... ) {
680 (void) me;
681 va_list args;
682 if (stream == stderr) {
683 va_start (args, format);
684 int sizeNeeded = vsnprintf (nullptr, 0, format, args); // find size of needed buffer (without final null byte)
685 const size_t bufferSize = sizeNeeded + 1;
686 va_end (args);
687 return bufferSize;
688 }
689 return -1;
690 }
691
test_FileInMemoryManager_io(void)692 void test_FileInMemoryManager_io (void) {
693 const conststring32 path1 = U"~/kanweg1.txt";
694 const conststring32 path2 = U"~/kanweg2.txt";
695 const conststring32 lines1 [3] = { U"abcd\n", U"ef\n", U"ghijk\n" };
696 const conststring32 lines2 [3] = { U"lmno\n", U"pqr\n", U"stuvwxyz\n" };
697 /*
698 Create a test FileInMemorySet with two (text) files in it.
699 */
700 MelderInfo_writeLine (U"test_FileInMemoryManager_io:");
701 MelderInfo_writeLine (U"\tCreating two files: ", path1, U" and ", path2);
702 structMelderFile s_file1 = {} , s_file2 = {};
703 const MelderFile file1 = & s_file1, file2 = & s_file2;
704 Melder_relativePathToFile (path1, file1);
705 Melder_relativePathToFile (path2, file2);
706 autoFileInMemorySet fims = FileInMemorySet_create ();
707
708 FILE *f = fopen (Melder_peek32to8 (file1 -> path), "w");
709 for (integer j = 0; j <= 2; j ++)
710 fputs (Melder_peek32to8 (lines1 [j]), f);
711
712 fclose (f);
713
714 f = fopen (Melder_peek32to8 (file2 -> path), "w");
715 for (integer j = 0; j <= 2; j ++)
716 fputs (Melder_peek32to8 (lines2 [j]), f);
717
718 fclose (f);
719
720 MelderInfo_writeLine (U"\tCreating FileInMemorySet from two files...");
721
722 autoFileInMemory fim1 = FileInMemory_create (file1);
723 fims -> addItem_move (fim1.move());
724 autoFileInMemory fim2 = FileInMemory_create (file2);
725 fims -> addItem_move (fim2.move());
726
727 /*
728 Create the FileInMemoryManager and test
729 */
730
731 autoFileInMemoryManager me = FileInMemoryManager_create (fims.get());
732
733 // fopen test
734 MelderInfo_writeLine (U"\tOpen file ", file1 -> path);
735 FILE * f1 = FileInMemoryManager_fopen (me.get(), Melder_peek32to8 (file1 -> path), "r");
736 const integer openFilesIndex1 = _FileInMemoryManager_getIndexInOpenFiles (me.get(), f1);
737 Melder_assert (openFilesIndex1 == 1);
738 MelderInfo_writeLine (U"\t\t ...opened");
739
740 MelderInfo_writeLine (U"\tOpen file ", file2 -> path);
741 FILE * f2 = FileInMemoryManager_fopen (me.get(), Melder_peek32to8 (file2 -> path), "r");
742 const integer openFilesIndex2 = _FileInMemoryManager_getIndexInOpenFiles (me.get(), f2);
743 Melder_assert (openFilesIndex2 == 2);
744 MelderInfo_writeLine (U"\t\t ...opened");
745
746 FileInMemoryManager_fclose (me.get(), f2);
747 Melder_assert (my openFiles -> size == 1);
748 MelderInfo_writeLine (U"\tClosed file ", file2 -> path);
749
750 // read from open text file
751
752 MelderInfo_writeLine (U"\tRead as text file in memory: ", file1 -> path);
753 char buf0 [200], buf1 [200];
754 const long nbuf = 200;
755
756 FileInMemory fim = (FileInMemory) my files -> at [openFilesIndex1];
757 FILE *file0 = fopen (Melder_peek32to8 (file1 -> path), "r");
758 for (integer i = 0; i <= 2; i ++) {
759 char *p0 = fgets (buf0, nbuf, file0);
760 const integer pos0 = ftell (file0);
761 char *p1 = FileInMemoryManager_fgets (me.get(), buf1, nbuf, f1);
762 const integer pos1 = FileInMemoryManager_ftell (me.get(), f1);
763 Melder_assert (Melder_equ (Melder_peek8to32 (buf0), Melder_peek8to32 (buf1)));
764 Melder_assert (pos0 == pos1);
765 Melder_assert (p0 == buf0 && p1 == buf1);
766 MelderInfo_writeLine (U"\t\tRead 1 line. Positions: ", pos0, U" and ", pos1);
767 }
768
769 MelderInfo_writeLine (U"\t\tRead while at EOF, returns nullptr");
770 char *shouldbenull = FileInMemoryManager_fgets (me.get(), buf1, nbuf, f1);
771 Melder_assert (shouldbenull == nullptr);
772
773 MelderInfo_writeLine (U"\tFinished reading... rewind ");
774
775 // read as binary file
776
777 rewind (file0);
778 FileInMemoryManager_rewind (me.get(), f1);
779
780 MelderInfo_writeLine (U"\tRead as binary file in memory: ", file1 -> path);
781
782 Melder_assert (fim -> d_position == 0);
783 const integer count = 8;
784 size_t nread0 = fread (buf0, 1, count, file0);
785 size_t nread1 = FileInMemoryManager_fread (me.get(), buf1, 1, count, f1);
786 MelderInfo_writeLine (U"\t\tRead ", nread0, U" and ", nread1, U" bytes");
787
788 Melder_assert (nread0 == nread0);
789 Melder_assert (fim -> d_position == count);
790
791 nread0 = fread (buf0, 1, count, file0);
792 nread1 = FileInMemoryManager_fread (me.get(), buf1, 1, count, f1);
793 MelderInfo_writeLine (U"\t\tRead ", nread0, U" and ", nread1, U" bytes");
794 Melder_assert (nread0 == nread1);
795
796 const int eof0 = feof (file0);
797 const int eof1 = FileInMemoryManager_feof (me.get(), f1);
798 MelderInfo_writeLine (U"\tEOF ? ", eof0, U" and ", eof1);
799
800 Melder_assert (eof0 != 0 && eof1 != 0);
801
802 // clean up
803
804 MelderFile_delete (file1);
805 MelderFile_delete (file2);
806
807 MelderInfo_writeLine (U"test_FileInMemoryManager_io: OK");
808 }
809
810 /* End of file FileInMemoryManager.cpp */
811