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 | ** | |
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. | |
14 | ** | |
15 | ****************************************************************/ | |
16 | ||
17 | #include "../pf_all.h" | |
18 | ||
19 | #ifndef PF_NO_FILEIO | |
20 | ||
21 | #include <limits.h> /* For LONG_MAX */ | |
22 | ||
23 | typedef int bool_t; | |
24 | ||
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) | |
27 | { | |
28 | bool_t Error = TRUE; | |
29 | size_t Diff = Size; | |
30 | size_t BufSize = 512; | |
31 | char *Buffer = pfAllocMem( BufSize ); | |
32 | if( Buffer != 0 ) | |
33 | { | |
34 | while( Diff > 0 ) | |
35 | { | |
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; | |
39 | Diff -= N; | |
40 | } | |
41 | Error = FALSE; | |
42 | ||
43 | cleanup: | |
44 | pfFreeMem( Buffer ); | |
45 | } | |
46 | return Error; | |
47 | } | |
48 | ||
49 | /* Shrink the file FILE to NEWSIZE. Return non-FALSE on error. | |
50 | * | |
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: | |
54 | * | |
55 | * 1. copy original content to temporary file | |
56 | * 2. re-open and truncate FILE | |
57 | * 3. copy the temporary file to FILE | |
58 | * | |
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. | |
61 | * | |
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. | |
65 | */ | |
66 | static bool_t TruncateFile( FileStream *File, long Newsize ) | |
67 | { | |
68 | bool_t Error = TRUE; | |
69 | if( fseek( File, 0, SEEK_SET ) == 0) | |
70 | { | |
71 | FileStream *TmpFile = tmpfile(); | |
72 | if( TmpFile != NULL ) | |
73 | { | |
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; | |
78 | Error = FALSE; | |
79 | ||
80 | cleanup: | |
81 | fclose( TmpFile ); | |
82 | } | |
83 | } | |
84 | return Error; | |
85 | } | |
86 | ||
87 | /* Write DIFF 0 bytes to FILE. Return non-FALSE on error. */ | |
88 | static bool_t ExtendFile( FileStream *File, size_t Diff ) | |
89 | { | |
90 | bool_t Error = TRUE; | |
91 | size_t BufSize = 512; | |
92 | char * Buffer = pfAllocMem( BufSize ); | |
93 | if( Buffer != 0 ) | |
94 | { | |
95 | pfSetMemory( Buffer, 0, BufSize ); | |
96 | while( Diff > 0 ) | |
97 | { | |
98 | size_t N = MIN( Diff, BufSize ); | |
99 | if( fwrite( Buffer, 1, N, File ) < N ) goto cleanup; | |
100 | Diff -= N; | |
101 | } | |
102 | Error = FALSE; | |
103 | cleanup: | |
104 | pfFreeMem( Buffer ); | |
105 | } | |
106 | return Error; | |
107 | } | |
108 | ||
0b1e2489 | 109 | ThrowCode sdResizeFile( FileStream *File, uint64_t Size ) |
e0701bfb HE |
110 | { |
111 | bool_t Error = TRUE; | |
0b1e2489 | 112 | if( Size <= LONG_MAX ) |
e0701bfb | 113 | { |
0b1e2489 | 114 | long Newsize = (long) Size; |
e0701bfb HE |
115 | if( fseek( File, 0, SEEK_END ) == 0 ) |
116 | { | |
117 | long Oldsize = ftell( File ); | |
118 | if( Oldsize != -1L ) | |
119 | { | |
120 | Error = ( Oldsize <= Newsize | |
121 | ? ExtendFile( File, Newsize - Oldsize ) | |
122 | : TruncateFile( File, Newsize )); | |
123 | } | |
124 | } | |
125 | } | |
126 | return Error ? THROW_RESIZE_FILE : 0; | |
127 | } | |
128 | ||
129 | #endif /* !PF_NO_FILEIO */ |