Update release notes for v2.0.0
[debian/pforth] / csrc / stdio / pf_fileio_stdio.c
1 /***************************************************************
2 ** File access routines based on ANSI C (no Unix stuff).
3 **
4 ** This file is part of pForth
5 **
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.
17 **
18 ****************************************************************/
19
20 #include "../pf_all.h"
21
22 #ifndef PF_NO_FILEIO
23
24 #include <limits.h>             /* For LONG_MAX */
25
26 typedef int bool_t;
27
28 /* Copy SIZE bytes from File FROM to File TO.  Return non-FALSE on error. */
29 static 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  */
69 static 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. */
91 static 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
112 ThrowCode sdResizeFile( FileStream *File, uint64_t Size )
113 {
114     bool_t Error = TRUE;
115     if( Size <= LONG_MAX )
116     {
117         long Newsize = (long) Size;
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 */