1#define _LARGEFILE_SOURCE
2#ifndef _FILE_OFFSET_BITS
3#define _FILE_OFFSET_BITS 64
4#endif
5
6#include "mupdf/fitz.h"
7#include "fitz-imp.h"
8
9#include <string.h>
10#include <errno.h>
11#include <stdio.h>
12
13/*
14 Return true if the named file exists and is readable.
15*/
16int
17fz_file_exists(fz_context *ctx, const char *path)
18{
19 FILE *file;
20#ifdef _WIN32
21 file = fz_fopen_utf8(path, "rb");
22#else
23 file = fopen(path, "rb");
24#endif
25 if (file)
26 fclose(file);
27 return !!file;
28}
29
30/*
31 Create a new stream object with the given
32 internal state and function pointers.
33
34 state: Internal state (opaque to everything but implementation).
35
36 next: Should provide the next set of bytes (up to max) of stream
37 data. Return the number of bytes read, or EOF when there is no
38 more data.
39
40 drop: Should clean up and free the internal state. May not
41 throw exceptions.
42*/
43fz_stream *
44fz_new_stream(fz_context *ctx, void *state, fz_stream_next_fn *next, fz_stream_drop_fn *drop)
45{
46 fz_stream *stm = NULL;
47
48 fz_try(ctx)
49 {
50 stm = fz_malloc_struct(ctx, fz_stream);
51 }
52 fz_catch(ctx)
53 {
54 if (drop)
55 drop(ctx, state);
56 fz_rethrow(ctx);
57 }
58
59 stm->refs = 1;
60 stm->error = 0;
61 stm->eof = 0;
62 stm->pos = 0;
63
64 stm->bits = 0;
65 stm->avail = 0;
66
67 stm->rp = NULL;
68 stm->wp = NULL;
69
70 stm->state = state;
71 stm->next = next;
72 stm->drop = drop;
73 stm->seek = NULL;
74
75 return stm;
76}
77
78fz_stream *
79fz_keep_stream(fz_context *ctx, fz_stream *stm)
80{
81 return fz_keep_imp(ctx, stm, &stm->refs);
82}
83
84void
85fz_drop_stream(fz_context *ctx, fz_stream *stm)
86{
87 if (fz_drop_imp(ctx, stm, &stm->refs))
88 {
89 if (stm->drop)
90 stm->drop(ctx, stm->state);
91 fz_free(ctx, stm);
92 }
93}
94
95/* File stream */
96
97// TODO: WIN32: HANDLE CreateFileW(), etc.
98// TODO: POSIX: int creat(), read(), write(), lseeko, etc.
99
100typedef struct fz_file_stream_s
101{
102 FILE *file;
103 unsigned char buffer[4096];
104} fz_file_stream;
105
106static int next_file(fz_context *ctx, fz_stream *stm, size_t n)
107{
108 fz_file_stream *state = stm->state;
109
110 /* n is only a hint, that we can safely ignore */
111 n = fread(state->buffer, 1, sizeof(state->buffer), state->file);
112 if (n < sizeof(state->buffer) && ferror(state->file))
113 fz_throw(ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno));
114 stm->rp = state->buffer;
115 stm->wp = state->buffer + n;
116 stm->pos += (int64_t)n;
117
118 if (n == 0)
119 return EOF;
120 return *stm->rp++;
121}
122
123static void seek_file(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
124{
125 fz_file_stream *state = stm->state;
126#ifdef _WIN32
127 int64_t n = _fseeki64(state->file, offset, whence);
128#else
129 int64_t n = fseeko(state->file, offset, whence);
130#endif
131 if (n < 0)
132 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno));
133#ifdef _WIN32
134 stm->pos = _ftelli64(state->file);
135#else
136 stm->pos = ftello(state->file);
137#endif
138 stm->rp = state->buffer;
139 stm->wp = state->buffer;
140}
141
142static void drop_file(fz_context *ctx, void *state_)
143{
144 fz_file_stream *state = state_;
145 int n = fclose(state->file);
146 if (n < 0)
147 fz_warn(ctx, "close error: %s", strerror(errno));
148 fz_free(ctx, state);
149}
150
151static fz_stream *
152fz_open_file_ptr(fz_context *ctx, FILE *file)
153{
154 fz_stream *stm;
155 fz_file_stream *state = fz_malloc_struct(ctx, fz_file_stream);
156 state->file = file;
157
158 stm = fz_new_stream(ctx, state, next_file, drop_file);
159 stm->seek = seek_file;
160
161 return stm;
162}
163
164fz_stream *fz_open_file_ptr_no_close(fz_context *ctx, FILE *file)
165{
166 fz_stream *stm = fz_open_file_ptr(ctx, file);
167 /* We don't own the file ptr. Ensure we don't close it */
168 stm->drop = fz_free;
169 return stm;
170}
171
172/*
173 Open the named file and wrap it in a stream.
174
175 filename: Path to a file. On non-Windows machines the filename should
176 be exactly as it would be passed to fopen(2). On Windows machines, the
177 path should be UTF-8 encoded so that non-ASCII characters can be
178 represented. Other platforms do the encoding as standard anyway (and
179 in most cases, particularly for MacOS and Linux, the encoding they
180 use is UTF-8 anyway).
181*/
182fz_stream *
183fz_open_file(fz_context *ctx, const char *name)
184{
185 FILE *file;
186#ifdef _WIN32
187 file = fz_fopen_utf8(name, "rb");
188#else
189 file = fopen(name, "rb");
190#endif
191 if (file == NULL)
192 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s: %s", name, strerror(errno));
193 return fz_open_file_ptr(ctx, file);
194}
195
196#ifdef _WIN32
197/*
198 Open the named file and wrap it in a stream.
199
200 This function is only available when compiling for Win32.
201
202 filename: Wide character path to the file as it would be given
203 to _wfopen().
204*/
205fz_stream *
206fz_open_file_w(fz_context *ctx, const wchar_t *name)
207{
208 FILE *file = _wfopen(name, L"rb");
209 if (file == NULL)
210 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file %ls: %s", name, strerror(errno));
211 return fz_open_file_ptr(ctx, file);
212}
213#endif
214
215/* Memory stream */
216
217static int next_buffer(fz_context *ctx, fz_stream *stm, size_t max)
218{
219 return EOF;
220}
221
222static void seek_buffer(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
223{
224 int64_t pos = stm->pos - (stm->wp - stm->rp);
225 /* Convert to absolute pos */
226 if (whence == 1)
227 {
228 offset += pos; /* Was relative to current pos */
229 }
230 else if (whence == 2)
231 {
232 offset += stm->pos; /* Was relative to end */
233 }
234
235 if (offset < 0)
236 offset = 0;
237 if (offset > stm->pos)
238 offset = stm->pos;
239 stm->rp += (int)(offset - pos);
240}
241
242static void drop_buffer(fz_context *ctx, void *state_)
243{
244 fz_buffer *state = (fz_buffer *)state_;
245 fz_drop_buffer(ctx, state);
246}
247
248/*
249 Open a buffer as a stream.
250
251 buf: The buffer to open. Ownership of the buffer is NOT passed in
252 (this function takes its own reference).
253
254 Returns pointer to newly created stream. May throw exceptions on
255 failure to allocate.
256*/
257fz_stream *
258fz_open_buffer(fz_context *ctx, fz_buffer *buf)
259{
260 fz_stream *stm;
261
262 fz_keep_buffer(ctx, buf);
263 stm = fz_new_stream(ctx, buf, next_buffer, drop_buffer);
264 stm->seek = seek_buffer;
265
266 stm->rp = buf->data;
267 stm->wp = buf->data + buf->len;
268
269 stm->pos = (int64_t)buf->len;
270
271 return stm;
272}
273
274/*
275 Open a block of memory as a stream.
276
277 data: Pointer to start of data block. Ownership of the data block is
278 NOT passed in.
279
280 len: Number of bytes in data block.
281
282 Returns pointer to newly created stream. May throw exceptions on
283 failure to allocate.
284*/
285fz_stream *
286fz_open_memory(fz_context *ctx, const unsigned char *data, size_t len)
287{
288 fz_stream *stm;
289
290 stm = fz_new_stream(ctx, NULL, next_buffer, NULL);
291 stm->seek = seek_buffer;
292
293 stm->rp = (unsigned char *)data;
294 stm->wp = (unsigned char *)data + len;
295
296 stm->pos = (int64_t)len;
297
298 return stm;
299}
300