Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
0b1e2489 | 112 | ThrowCode 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 */ |