1 /***************************************************************
2 ** File access routines based on ANSI C (no Unix stuff).
4 ** This file is part of pForth
6 ** The pForth software code is dedicated to the public domain,
7 ** and any third party may reproduce, distribute and modify
8 ** the pForth software code or any derivative works thereof
9 ** without any compensation or license. The pForth software
10 ** code is provided on an "as is" basis without any warranty
11 ** of any kind, including, without limitation, the implied
12 ** warranties of merchantability and fitness for a particular
13 ** purpose and their equivalents under the laws of any jurisdiction.
15 ****************************************************************/
17 #include "../pf_all.h"
21 #include <limits.h> /* For LONG_MAX */
25 /* Copy SIZE bytes from File FROM to File TO. Return non-FALSE on error. */
26 static bool_t CopyFile( FileStream *From, FileStream *To, long Size)
31 char *Buffer = pfAllocMem( BufSize );
36 size_t N = MIN( Diff, BufSize );
37 if( fread( Buffer, 1, N, From ) < N ) goto cleanup;
38 if( fwrite( Buffer, 1, N, To ) < N ) goto cleanup;
49 /* Shrink the file FILE to NEWSIZE. Return non-FALSE on error.
51 * There's no direct way to do this in ANSI C. The closest thing we
52 * have is freopen(3), which truncates a file to zero length if we use
53 * "w+b" as mode argument. So we do this:
55 * 1. copy original content to temporary file
56 * 2. re-open and truncate FILE
57 * 3. copy the temporary file to FILE
59 * Unfortunately, "w+b" may not be the same mode as the original mode
60 * of FILE. I don't see a away to avoid this, though.
62 * We call freopen with NULL as path argument, because we don't know
63 * the actual file-name. It seems that the trick with path=NULL is
64 * not part of C89 but it's in C99.
66 static bool_t TruncateFile( FileStream *File, long Newsize )
69 if( fseek( File, 0, SEEK_SET ) == 0)
71 FileStream *TmpFile = tmpfile();
74 if( CopyFile( File, TmpFile, Newsize )) goto cleanup;
75 if( fseek( TmpFile, 0, SEEK_SET ) != 0 ) goto cleanup;
76 if( freopen( NULL, "w+b", File ) == NULL ) goto cleanup;
77 if( CopyFile( TmpFile, File, Newsize )) goto cleanup;
87 /* Write DIFF 0 bytes to FILE. Return non-FALSE on error. */
88 static bool_t ExtendFile( FileStream *File, size_t Diff )
92 char * Buffer = pfAllocMem( BufSize );
95 pfSetMemory( Buffer, 0, BufSize );
98 size_t N = MIN( Diff, BufSize );
99 if( fwrite( Buffer, 1, N, File ) < N ) goto cleanup;
109 ThrowCode sdResizeFile( FileStream *File, uint64_t Size )
112 if( Size <= LONG_MAX )
114 long Newsize = (long) Size;
115 if( fseek( File, 0, SEEK_END ) == 0 )
117 long Oldsize = ftell( File );
120 Error = ( Oldsize <= Newsize
121 ? ExtendFile( File, Newsize - Oldsize )
122 : TruncateFile( File, Newsize ));
126 return Error ? THROW_RESIZE_FILE : 0;
129 #endif /* !PF_NO_FILEIO */