1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* fileio.c */ |
4 | /* */ |
5 | /* File I/O for the ld65 linker */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2008 Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <string.h> |
37 | #include <errno.h> |
38 | |
39 | /* common */ |
40 | #include "xmalloc.h" |
41 | |
42 | /* ld65 */ |
43 | #include "error.h" |
44 | #include "fileio.h" |
45 | #include "spool.h" |
46 | |
47 | |
48 | |
49 | /*****************************************************************************/ |
50 | /* Code */ |
51 | /*****************************************************************************/ |
52 | |
53 | |
54 | |
55 | void FileSetPos (FILE* F, unsigned long Pos) |
56 | /* Seek to the given absolute position, fail on errors */ |
57 | { |
58 | if (fseek (F, Pos, SEEK_SET) != 0) { |
59 | Error ("Cannot seek: %s" , strerror (errno)); |
60 | } |
61 | } |
62 | |
63 | |
64 | |
65 | unsigned long FileGetPos (FILE* F) |
66 | /* Return the current file position, fail on errors */ |
67 | { |
68 | long Pos = ftell (F); |
69 | if (Pos < 0) { |
70 | Error ("Error in ftell: %s" , strerror (errno)); |
71 | } |
72 | return Pos; |
73 | } |
74 | |
75 | |
76 | |
77 | void Write8 (FILE* F, unsigned Val) |
78 | /* Write an 8 bit value to the file */ |
79 | { |
80 | if (putc (Val, F) == EOF) { |
81 | Error ("Write error (disk full?)" ); |
82 | } |
83 | } |
84 | |
85 | |
86 | |
87 | void Write16 (FILE* F, unsigned Val) |
88 | /* Write a 16 bit value to the file */ |
89 | { |
90 | Write8 (F, (unsigned char) Val); |
91 | Write8 (F, (unsigned char) (Val >> 8)); |
92 | } |
93 | |
94 | |
95 | |
96 | void Write24 (FILE* F, unsigned long Val) |
97 | /* Write a 24 bit value to the file */ |
98 | { |
99 | Write8 (F, (unsigned char) Val); |
100 | Write8 (F, (unsigned char) (Val >> 8)); |
101 | Write8 (F, (unsigned char) (Val >> 16)); |
102 | } |
103 | |
104 | |
105 | |
106 | void Write32 (FILE* F, unsigned long Val) |
107 | /* Write a 32 bit value to the file */ |
108 | { |
109 | Write8 (F, (unsigned char) Val); |
110 | Write8 (F, (unsigned char) (Val >> 8)); |
111 | Write8 (F, (unsigned char) (Val >> 16)); |
112 | Write8 (F, (unsigned char) (Val >> 24)); |
113 | } |
114 | |
115 | |
116 | |
117 | void WriteVal (FILE* F, unsigned long Val, unsigned Size) |
118 | /* Write a value of the given size to the output file */ |
119 | { |
120 | switch (Size) { |
121 | |
122 | case 1: |
123 | Write8 (F, Val); |
124 | break; |
125 | |
126 | case 2: |
127 | Write16 (F, Val); |
128 | break; |
129 | |
130 | case 3: |
131 | Write24 (F, Val); |
132 | break; |
133 | |
134 | case 4: |
135 | Write32 (F, Val); |
136 | break; |
137 | |
138 | default: |
139 | Internal ("WriteVal: Invalid size: %u" , Size); |
140 | |
141 | } |
142 | } |
143 | |
144 | |
145 | |
146 | void WriteVar (FILE* F, unsigned long V) |
147 | /* Write a variable sized value to the file in special encoding */ |
148 | { |
149 | /* We will write the value to the file in 7 bit chunks. If the 8th bit |
150 | ** is clear, we're done, if it is set, another chunk follows. This will |
151 | ** allow us to encode smaller values with less bytes, at the expense of |
152 | ** needing 5 bytes if a 32 bit value is written to file. |
153 | */ |
154 | do { |
155 | unsigned char C = (V & 0x7F); |
156 | V >>= 7; |
157 | if (V) { |
158 | C |= 0x80; |
159 | } |
160 | Write8 (F, C); |
161 | } while (V != 0); |
162 | } |
163 | |
164 | |
165 | |
166 | void WriteStr (FILE* F, const char* S) |
167 | /* Write a string to the file */ |
168 | { |
169 | unsigned Len = strlen (S); |
170 | WriteVar (F, Len); |
171 | WriteData (F, S, Len); |
172 | } |
173 | |
174 | |
175 | |
176 | void WriteData (FILE* F, const void* Data, unsigned Size) |
177 | /* Write data to the file */ |
178 | { |
179 | if (fwrite (Data, 1, Size, F) != Size) { |
180 | Error ("Write error (disk full?)" ); |
181 | } |
182 | } |
183 | |
184 | |
185 | |
186 | void WriteMult (FILE* F, unsigned char Val, unsigned long Count) |
187 | /* Write one byte several times to the file */ |
188 | { |
189 | while (Count--) { |
190 | Write8 (F, Val); |
191 | } |
192 | } |
193 | |
194 | |
195 | |
196 | unsigned Read8 (FILE* F) |
197 | /* Read an 8 bit value from the file */ |
198 | { |
199 | int C = getc (F); |
200 | if (C == EOF) { |
201 | long Pos = ftell (F); |
202 | Error ("Read error at position %ld (file corrupt?)" , Pos); |
203 | } |
204 | return C; |
205 | } |
206 | |
207 | |
208 | |
209 | unsigned Read16 (FILE* F) |
210 | /* Read a 16 bit value from the file */ |
211 | { |
212 | unsigned Lo = Read8 (F); |
213 | unsigned Hi = Read8 (F); |
214 | return (Hi << 8) | Lo; |
215 | } |
216 | |
217 | |
218 | |
219 | unsigned long Read24 (FILE* F) |
220 | /* Read a 24 bit value from the file */ |
221 | { |
222 | unsigned long Lo = Read16 (F); |
223 | unsigned long Hi = Read8 (F); |
224 | return (Hi << 16) | Lo; |
225 | } |
226 | |
227 | |
228 | |
229 | unsigned long Read32 (FILE* F) |
230 | /* Read a 32 bit value from the file */ |
231 | { |
232 | unsigned long Lo = Read16 (F); |
233 | unsigned long Hi = Read16 (F); |
234 | return (Hi << 16) | Lo; |
235 | } |
236 | |
237 | |
238 | |
239 | long Read32Signed (FILE* F) |
240 | /* Read a 32 bit value from the file. Sign extend the value. */ |
241 | { |
242 | /* Read a 32 bit value */ |
243 | unsigned long V = Read32 (F); |
244 | |
245 | /* Sign extend the value */ |
246 | if (V & 0x80000000UL) { |
247 | /* Signed value */ |
248 | V |= ~0xFFFFFFFFUL; |
249 | } |
250 | |
251 | /* Return it as a long */ |
252 | return (long) V; |
253 | } |
254 | |
255 | |
256 | |
257 | unsigned long ReadVar (FILE* F) |
258 | /* Read a variable size value from the file */ |
259 | { |
260 | /* The value was written to the file in 7 bit chunks LSB first. If there |
261 | ** are more bytes, bit 8 is set, otherwise it is clear. |
262 | */ |
263 | unsigned char C; |
264 | unsigned long V = 0; |
265 | unsigned Shift = 0; |
266 | do { |
267 | /* Read one byte */ |
268 | C = Read8 (F); |
269 | /* Encode it into the target value */ |
270 | V |= ((unsigned long)(C & 0x7F)) << Shift; |
271 | /* Next value */ |
272 | Shift += 7; |
273 | } while (C & 0x80); |
274 | |
275 | /* Return the value read */ |
276 | return V; |
277 | } |
278 | |
279 | |
280 | |
281 | unsigned ReadStr (FILE* F) |
282 | /* Read a string from the file, place it into the global string pool, and |
283 | ** return its string id. |
284 | */ |
285 | { |
286 | unsigned Id; |
287 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
288 | |
289 | /* Read the length */ |
290 | unsigned Len = ReadVar (F); |
291 | |
292 | /* Expand the string buffer memory */ |
293 | SB_Realloc (&Buf, Len); |
294 | |
295 | /* Read the string */ |
296 | ReadData (F, SB_GetBuf (&Buf), Len); |
297 | Buf.Len = Len; |
298 | |
299 | /* Insert it into the string pool and remember the id */ |
300 | Id = GetStrBufId (&Buf); |
301 | |
302 | /* Free the memory buffer */ |
303 | SB_Done (&Buf); |
304 | |
305 | /* Return the string id */ |
306 | return Id; |
307 | } |
308 | |
309 | |
310 | |
311 | FilePos* ReadFilePos (FILE* F, FilePos* Pos) |
312 | /* Read a file position from the file */ |
313 | { |
314 | /* Read the data fields */ |
315 | Pos->Line = ReadVar (F); |
316 | Pos->Col = ReadVar (F); |
317 | Pos->Name = ReadVar (F); |
318 | return Pos; |
319 | } |
320 | |
321 | |
322 | |
323 | void* ReadData (FILE* F, void* Data, unsigned Size) |
324 | /* Read data from the file */ |
325 | { |
326 | /* Explicitly allow reading zero bytes */ |
327 | if (Size > 0) { |
328 | if (fread (Data, 1, Size, F) != Size) { |
329 | long Pos = ftell (F); |
330 | Error ("Read error at position %ld (file corrupt?)" , Pos); |
331 | } |
332 | } |
333 | return Data; |
334 | } |
335 | |