1#include "fitz-imp.h"
2
3#include <string.h>
4
5#define MIN_BOMB (100 << 20)
6
7/*
8 Read from a stream into a given data block.
9
10 stm: The stream to read from.
11
12 data: The data block to read into.
13
14 len: The length of the data block (in bytes).
15
16 Returns the number of bytes read. May throw exceptions.
17*/
18size_t
19fz_read(fz_context *ctx, fz_stream *stm, unsigned char *buf, size_t len)
20{
21 size_t count, n;
22
23 count = 0;
24 do
25 {
26 n = fz_available(ctx, stm, len);
27 if (n > len)
28 n = len;
29 if (n == 0)
30 break;
31
32 memcpy(buf, stm->rp, n);
33 stm->rp += n;
34 buf += n;
35 count += n;
36 len -= n;
37 }
38 while (len > 0);
39
40 return count;
41}
42
43static unsigned char skip_buf[4096];
44
45/*
46 Read from a stream discarding data.
47
48 stm: The stream to read from.
49
50 len: The number of bytes to read.
51
52 Returns the number of bytes read. May throw exceptions.
53*/
54size_t fz_skip(fz_context *ctx, fz_stream *stm, size_t len)
55{
56 size_t count, l, total = 0;
57
58 while (len)
59 {
60 l = len;
61 if (l > sizeof(skip_buf))
62 l = sizeof(skip_buf);
63 count = fz_read(ctx, stm, skip_buf, l);
64 total += count;
65 if (count < l)
66 break;
67 len -= count;
68 }
69 return total;
70}
71
72/*
73 Read all of a stream into a buffer.
74
75 stm: The stream to read from
76
77 initial: Suggested initial size for the buffer.
78
79 Returns a buffer created from reading from the stream. May throw
80 exceptions on failure to allocate.
81*/
82fz_buffer *
83fz_read_all(fz_context *ctx, fz_stream *stm, size_t initial)
84{
85 return fz_read_best(ctx, stm, initial, NULL);
86}
87
88/*
89 Attempt to read a stream into a buffer. If truncated
90 is NULL behaves as fz_read_all, sets a truncated flag in case of
91 error.
92
93 stm: The stream to read from.
94
95 initial: Suggested initial size for the buffer.
96
97 truncated: Flag to store success/failure indication in.
98
99 Returns a buffer created from reading from the stream.
100*/
101fz_buffer *
102fz_read_best(fz_context *ctx, fz_stream *stm, size_t initial, int *truncated)
103{
104 fz_buffer *buf = NULL;
105 int check_bomb = (initial > 0);
106 size_t n;
107
108 fz_var(buf);
109
110 if (truncated)
111 *truncated = 0;
112
113 fz_try(ctx)
114 {
115 if (initial < 1024)
116 initial = 1024;
117
118 buf = fz_new_buffer(ctx, initial+1);
119
120 while (1)
121 {
122 if (buf->len == buf->cap)
123 fz_grow_buffer(ctx, buf);
124
125 if (check_bomb && buf->len >= MIN_BOMB && buf->len / 200 > initial)
126 fz_throw(ctx, FZ_ERROR_GENERIC, "compression bomb detected");
127
128 n = fz_read(ctx, stm, buf->data + buf->len, buf->cap - buf->len);
129 if (n == 0)
130 break;
131
132 buf->len += n;
133 }
134 }
135 fz_catch(ctx)
136 {
137 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
138 {
139 fz_drop_buffer(ctx, buf);
140 fz_rethrow(ctx);
141 }
142 if (truncated)
143 {
144 *truncated = 1;
145 }
146 else
147 {
148 fz_drop_buffer(ctx, buf);
149 fz_rethrow(ctx);
150 }
151 }
152
153 return buf;
154}
155
156/*
157 Read a line from stream into the buffer until either a
158 terminating newline or EOF, which it replaces with a null byte ('\0').
159
160 Returns buf on success, and NULL when end of file occurs while no characters
161 have been read.
162*/
163char *
164fz_read_line(fz_context *ctx, fz_stream *stm, char *mem, size_t n)
165{
166 char *s = mem;
167 int c = EOF;
168 while (n > 1)
169 {
170 c = fz_read_byte(ctx, stm);
171 if (c == EOF)
172 break;
173 if (c == '\r') {
174 c = fz_peek_byte(ctx, stm);
175 if (c == '\n')
176 fz_read_byte(ctx, stm);
177 break;
178 }
179 if (c == '\n')
180 break;
181 *s++ = c;
182 n--;
183 }
184 if (n)
185 *s = '\0';
186 return (s == mem && c == EOF) ? NULL : mem;
187}
188
189/*
190 return the current reading position within a stream
191*/
192int64_t
193fz_tell(fz_context *ctx, fz_stream *stm)
194{
195 return stm->pos - (stm->wp - stm->rp);
196}
197
198/*
199 Seek within a stream.
200
201 stm: The stream to seek within.
202
203 offset: The offset to seek to.
204
205 whence: From where the offset is measured (see fseek).
206*/
207void
208fz_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
209{
210 stm->avail = 0; /* Reset bit reading */
211 if (stm->seek)
212 {
213 if (whence == 1)
214 {
215 offset += fz_tell(ctx, stm);
216 whence = 0;
217 }
218 stm->seek(ctx, stm, offset, whence);
219 stm->eof = 0;
220 }
221 else if (whence != 2)
222 {
223 if (whence == 0)
224 offset -= fz_tell(ctx, stm);
225 if (offset < 0)
226 fz_warn(ctx, "cannot seek backwards");
227 /* dog slow, but rare enough */
228 while (offset-- > 0)
229 {
230 if (fz_read_byte(ctx, stm) == EOF)
231 {
232 fz_warn(ctx, "seek failed");
233 break;
234 }
235 }
236 }
237 else
238 fz_warn(ctx, "cannot seek");
239}
240
241/*
242 Read all the contents of a file into a buffer.
243*/
244fz_buffer *
245fz_read_file(fz_context *ctx, const char *filename)
246{
247 fz_stream *stm;
248 fz_buffer *buf = NULL;
249
250 fz_var(buf);
251
252 stm = fz_open_file(ctx, filename);
253 fz_try(ctx)
254 {
255 buf = fz_read_all(ctx, stm, 0);
256 }
257 fz_always(ctx)
258 {
259 fz_drop_stream(ctx, stm);
260 }
261 fz_catch(ctx)
262 {
263 fz_rethrow(ctx);
264 }
265
266 return buf;
267}
268
269/*
270 fz_read_[u]int(16|24|32|64)(_le)?
271
272 Read a 16/32/64 bit signed/unsigned integer from stream,
273 in big or little-endian byte orders.
274
275 Throws an exception if EOF is encountered.
276*/
277uint16_t fz_read_uint16(fz_context *ctx, fz_stream *stm)
278{
279 uint32_t a = fz_read_byte(ctx, stm);
280 uint32_t b = fz_read_byte(ctx, stm);
281 uint32_t x = (a<<8) | (b);
282 if (a == EOF || b == EOF)
283 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int16");
284 return x;
285}
286
287uint32_t fz_read_uint24(fz_context *ctx, fz_stream *stm)
288{
289 uint32_t a = fz_read_byte(ctx, stm);
290 uint32_t b = fz_read_byte(ctx, stm);
291 uint32_t c = fz_read_byte(ctx, stm);
292 uint32_t x = (a<<16) | (b<<8) | (c);
293 if (a == EOF || b == EOF || c == EOF)
294 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int24");
295 return x;
296}
297
298uint32_t fz_read_uint32(fz_context *ctx, fz_stream *stm)
299{
300 uint32_t a = fz_read_byte(ctx, stm);
301 uint32_t b = fz_read_byte(ctx, stm);
302 uint32_t c = fz_read_byte(ctx, stm);
303 uint32_t d = fz_read_byte(ctx, stm);
304 uint32_t x = (a<<24) | (b<<16) | (c<<8) | (d);
305 if (a == EOF || b == EOF || c == EOF || d == EOF)
306 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int32");
307 return x;
308}
309
310uint64_t fz_read_uint64(fz_context *ctx, fz_stream *stm)
311{
312 uint64_t a = fz_read_byte(ctx, stm);
313 uint64_t b = fz_read_byte(ctx, stm);
314 uint64_t c = fz_read_byte(ctx, stm);
315 uint64_t d = fz_read_byte(ctx, stm);
316 uint64_t e = fz_read_byte(ctx, stm);
317 uint64_t f = fz_read_byte(ctx, stm);
318 uint64_t g = fz_read_byte(ctx, stm);
319 uint64_t h = fz_read_byte(ctx, stm);
320 uint64_t x = (a<<56) | (b<<48) | (c<<40) | (d<<32) | (e<<24) | (f<<16) | (g<<8) | (h);
321 if (a == EOF || b == EOF || c == EOF || d == EOF || e == EOF || f == EOF || g == EOF || h == EOF)
322 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int64");
323 return x;
324}
325
326uint16_t fz_read_uint16_le(fz_context *ctx, fz_stream *stm)
327{
328 uint32_t a = fz_read_byte(ctx, stm);
329 uint32_t b = fz_read_byte(ctx, stm);
330 uint32_t x = (a) | (b<<8);
331 if (a == EOF || b == EOF)
332 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int16");
333 return x;
334}
335
336uint32_t fz_read_uint24_le(fz_context *ctx, fz_stream *stm)
337{
338 uint32_t a = fz_read_byte(ctx, stm);
339 uint32_t b = fz_read_byte(ctx, stm);
340 uint32_t c = fz_read_byte(ctx, stm);
341 uint32_t x = (a) | (b<<8) | (c<<16);
342 if (a == EOF || b == EOF || c == EOF)
343 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int24");
344 return x;
345}
346
347uint32_t fz_read_uint32_le(fz_context *ctx, fz_stream *stm)
348{
349 uint32_t a = fz_read_byte(ctx, stm);
350 uint32_t b = fz_read_byte(ctx, stm);
351 uint32_t c = fz_read_byte(ctx, stm);
352 uint32_t d = fz_read_byte(ctx, stm);
353 uint32_t x = (a) | (b<<8) | (c<<16) | (d<<24);
354 if (a == EOF || b == EOF || c == EOF || d == EOF)
355 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int32");
356 return x;
357}
358
359uint64_t fz_read_uint64_le(fz_context *ctx, fz_stream *stm)
360{
361 uint64_t a = fz_read_byte(ctx, stm);
362 uint64_t b = fz_read_byte(ctx, stm);
363 uint64_t c = fz_read_byte(ctx, stm);
364 uint64_t d = fz_read_byte(ctx, stm);
365 uint64_t e = fz_read_byte(ctx, stm);
366 uint64_t f = fz_read_byte(ctx, stm);
367 uint64_t g = fz_read_byte(ctx, stm);
368 uint64_t h = fz_read_byte(ctx, stm);
369 uint64_t x = (a) | (b<<8) | (c<<16) | (d<<24) | (e<<32) | (f<<40) | (g<<48) | (h<<56);
370 if (a == EOF || b == EOF || c == EOF || d == EOF || e == EOF || f == EOF || g == EOF || h == EOF)
371 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of file in int64");
372 return x;
373}
374
375int16_t fz_read_int16(fz_context *ctx, fz_stream *stm) { return (int16_t)fz_read_uint16(ctx, stm); }
376int32_t fz_read_int32(fz_context *ctx, fz_stream *stm) { return (int32_t)fz_read_uint32(ctx, stm); }
377int64_t fz_read_int64(fz_context *ctx, fz_stream *stm) { return (int64_t)fz_read_uint64(ctx, stm); }
378
379int16_t fz_read_int16_le(fz_context *ctx, fz_stream *stm) { return (int16_t)fz_read_uint16_le(ctx, stm); }
380int32_t fz_read_int32_le(fz_context *ctx, fz_stream *stm) { return (int32_t)fz_read_uint32_le(ctx, stm); }
381int64_t fz_read_int64_le(fz_context *ctx, fz_stream *stm) { return (int64_t)fz_read_uint64_le(ctx, stm); }
382
383/*
384 Read a null terminated string from the stream into
385 a buffer of a given length. The buffer will be null terminated.
386 Throws on failure (including the failure to fit the entire string
387 including the terminator into the buffer).
388*/
389void fz_read_string(fz_context *ctx, fz_stream *stm, char *buffer, int len)
390{
391 int c;
392 do
393 {
394 if (len <= 0)
395 fz_throw(ctx, FZ_ERROR_GENERIC, "Buffer overrun reading null terminated string");
396
397 c = fz_read_byte(ctx, stm);
398 if (c == EOF)
399 fz_throw(ctx, FZ_ERROR_GENERIC, "EOF reading null terminated string");
400 *buffer++ = c;
401 len--;
402 }
403 while (c != 0);
404}
405