Merge pull request #75 from SeekingMeaning/0BSD
[pforth] / csrc / stdio / pf_fileio_stdio.c
CommitLineData
e0701bfb
HE
1/***************************************************************
2** File access routines based on ANSI C (no Unix stuff).
3**
4** This file is part of pForth
5**
1f99f95d
S
6** Permission to use, copy, modify, and/or distribute this
7** software for any purpose with or without fee is hereby granted.
8**
9** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
15** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
e0701bfb
HE
17**
18****************************************************************/
19
20#include "../pf_all.h"
21
22#ifndef PF_NO_FILEIO
23
24#include <limits.h> /* For LONG_MAX */
25
26typedef int bool_t;
27
28/* Copy SIZE bytes from File FROM to File TO. Return non-FALSE on error. */
29static bool_t CopyFile( FileStream *From, FileStream *To, long Size)
30{
31 bool_t Error = TRUE;
32 size_t Diff = Size;
33 size_t BufSize = 512;
34 char *Buffer = pfAllocMem( BufSize );
35 if( Buffer != 0 )
36 {
37 while( Diff > 0 )
38 {
39 size_t N = MIN( Diff, BufSize );
40 if( fread( Buffer, 1, N, From ) < N ) goto cleanup;
41 if( fwrite( Buffer, 1, N, To ) < N ) goto cleanup;
42 Diff -= N;
43 }
44 Error = FALSE;
45
46 cleanup:
47 pfFreeMem( Buffer );
48 }
49 return Error;
50}
51
52/* Shrink the file FILE to NEWSIZE. Return non-FALSE on error.
53 *
54 * There's no direct way to do this in ANSI C. The closest thing we
55 * have is freopen(3), which truncates a file to zero length if we use
56 * "w+b" as mode argument. So we do this:
57 *
58 * 1. copy original content to temporary file
59 * 2. re-open and truncate FILE
60 * 3. copy the temporary file to FILE
61 *
62 * Unfortunately, "w+b" may not be the same mode as the original mode
63 * of FILE. I don't see a away to avoid this, though.
64 *
65 * We call freopen with NULL as path argument, because we don't know
66 * the actual file-name. It seems that the trick with path=NULL is
67 * not part of C89 but it's in C99.
68 */
69static bool_t TruncateFile( FileStream *File, long Newsize )
70{
71 bool_t Error = TRUE;
72 if( fseek( File, 0, SEEK_SET ) == 0)
73 {
74 FileStream *TmpFile = tmpfile();
75 if( TmpFile != NULL )
76 {
77 if( CopyFile( File, TmpFile, Newsize )) goto cleanup;
78 if( fseek( TmpFile, 0, SEEK_SET ) != 0 ) goto cleanup;
79 if( freopen( NULL, "w+b", File ) == NULL ) goto cleanup;
80 if( CopyFile( TmpFile, File, Newsize )) goto cleanup;
81 Error = FALSE;
82
83 cleanup:
84 fclose( TmpFile );
85 }
86 }
87 return Error;
88}
89
90/* Write DIFF 0 bytes to FILE. Return non-FALSE on error. */
91static bool_t ExtendFile( FileStream *File, size_t Diff )
92{
93 bool_t Error = TRUE;
94 size_t BufSize = 512;
95 char * Buffer = pfAllocMem( BufSize );
96 if( Buffer != 0 )
97 {
98 pfSetMemory( Buffer, 0, BufSize );
99 while( Diff > 0 )
100 {
101 size_t N = MIN( Diff, BufSize );
102 if( fwrite( Buffer, 1, N, File ) < N ) goto cleanup;
103 Diff -= N;
104 }
105 Error = FALSE;
106 cleanup:
107 pfFreeMem( Buffer );
108 }
109 return Error;
110}
111
0b1e2489 112ThrowCode sdResizeFile( FileStream *File, uint64_t Size )
e0701bfb
HE
113{
114 bool_t Error = TRUE;
0b1e2489 115 if( Size <= LONG_MAX )
e0701bfb 116 {
0b1e2489 117 long Newsize = (long) Size;
e0701bfb
HE
118 if( fseek( File, 0, SEEK_END ) == 0 )
119 {
120 long Oldsize = ftell( File );
121 if( Oldsize != -1L )
122 {
123 Error = ( Oldsize <= Newsize
124 ? ExtendFile( File, Newsize - Oldsize )
125 : TruncateFile( File, Newsize ));
126 }
127 }
128 }
129 return Error ? THROW_RESIZE_FILE : 0;
130}
131
132#endif /* !PF_NO_FILEIO */