/src/mupdf/source/fitz/stream-open.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // Copyright (C) 2004-2024 Artifex Software, Inc.  | 
2  |  | //  | 
3  |  | // This file is part of MuPDF.  | 
4  |  | //  | 
5  |  | // MuPDF is free software: you can redistribute it and/or modify it under the  | 
6  |  | // terms of the GNU Affero General Public License as published by the Free  | 
7  |  | // Software Foundation, either version 3 of the License, or (at your option)  | 
8  |  | // any later version.  | 
9  |  | //  | 
10  |  | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY  | 
11  |  | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS  | 
12  |  | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more  | 
13  |  | // details.  | 
14  |  | //  | 
15  |  | // You should have received a copy of the GNU Affero General Public License  | 
16  |  | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>  | 
17  |  | //  | 
18  |  | // Alternative licensing terms are available from the licensor.  | 
19  |  | // For commercial licensing, see <https://www.artifex.com/> or contact  | 
20  |  | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,  | 
21  |  | // CA 94129, USA, for further information.  | 
22  |  |  | 
23  |  | #define _LARGEFILE_SOURCE  | 
24  |  | #ifndef _FILE_OFFSET_BITS  | 
25  |  | #define _FILE_OFFSET_BITS 64  | 
26  |  | #endif  | 
27  |  |  | 
28  |  | #include "mupdf/fitz.h"  | 
29  |  |  | 
30  |  | #include <string.h>  | 
31  |  | #include <errno.h>  | 
32  |  | #include <stdio.h>  | 
33  |  |  | 
34  |  | #ifdef _WIN32  | 
35  |  | #include <windows.h>  | 
36  |  | #include <wchar.h>  | 
37  |  | #else  | 
38  |  | #include <unistd.h>  | 
39  |  | #endif  | 
40  |  |  | 
41  |  | int  | 
42  |  | fz_file_exists(fz_context *ctx, const char *path)  | 
43  | 0  | { | 
44  | 0  |   FILE *file;  | 
45  |  | #ifdef _WIN32  | 
46  |  |   file = fz_fopen_utf8(path, "rb");  | 
47  |  | #else  | 
48  | 0  |   file = fopen(path, "rb");  | 
49  | 0  | #endif  | 
50  | 0  |   if (file)  | 
51  | 0  |     fclose(file);  | 
52  | 0  |   return !!file;  | 
53  | 0  | }  | 
54  |  |  | 
55  |  | fz_stream *  | 
56  |  | fz_new_stream(fz_context *ctx, void *state, fz_stream_next_fn *next, fz_stream_drop_fn *drop)  | 
57  | 815k  | { | 
58  | 815k  |   fz_stream *stm = NULL;  | 
59  |  |  | 
60  | 1.63M  |   fz_try(ctx)  | 
61  | 1.63M  |   { | 
62  | 815k  |     stm = fz_malloc_struct(ctx, fz_stream);  | 
63  | 815k  |   }  | 
64  | 1.63M  |   fz_catch(ctx)  | 
65  | 0  |   { | 
66  | 0  |     if (drop)  | 
67  | 0  |       drop(ctx, state);  | 
68  | 0  |     fz_rethrow(ctx);  | 
69  | 0  |   }  | 
70  |  |  | 
71  | 815k  |   stm->refs = 1;  | 
72  | 815k  |   stm->error = 0;  | 
73  | 815k  |   stm->eof = 0;  | 
74  | 815k  |   stm->pos = 0;  | 
75  |  |  | 
76  | 815k  |   stm->bits = 0;  | 
77  | 815k  |   stm->avail = 0;  | 
78  |  |  | 
79  | 815k  |   stm->rp = NULL;  | 
80  | 815k  |   stm->wp = NULL;  | 
81  |  |  | 
82  | 815k  |   stm->state = state;  | 
83  | 815k  |   stm->next = next;  | 
84  | 815k  |   stm->drop = drop;  | 
85  | 815k  |   stm->seek = NULL;  | 
86  |  |  | 
87  | 815k  |   return stm;  | 
88  | 815k  | }  | 
89  |  |  | 
90  |  | fz_stream *  | 
91  |  | fz_keep_stream(fz_context *ctx, fz_stream *stm)  | 
92  | 777k  | { | 
93  | 777k  |   return fz_keep_imp(ctx, stm, &stm->refs);  | 
94  | 777k  | }  | 
95  |  |  | 
96  |  | void  | 
97  |  | fz_drop_stream(fz_context *ctx, fz_stream *stm)  | 
98  | 1.93M  | { | 
99  | 1.93M  |   if (fz_drop_imp(ctx, stm, &stm->refs))  | 
100  | 815k  |   { | 
101  | 815k  |     if (stm->drop)  | 
102  | 799k  |       stm->drop(ctx, stm->state);  | 
103  | 815k  |     fz_free(ctx, stm);  | 
104  | 815k  |   }  | 
105  | 1.93M  | }  | 
106  |  |  | 
107  |  | /* File stream */  | 
108  |  |  | 
109  |  | // TODO: WIN32: HANDLE CreateFileW(), etc.  | 
110  |  | // TODO: POSIX: int creat(), read(), write(), lseeko, etc.  | 
111  |  |  | 
112  |  | typedef struct  | 
113  |  | { | 
114  |  |   FILE *file;  | 
115  |  |   char *filename;  | 
116  |  | #ifdef _WIN32  | 
117  |  |   wchar_t *filename_w;  | 
118  |  | #endif  | 
119  |  |   int del_on_drop;  | 
120  |  |   unsigned char buffer[4096];  | 
121  |  | } fz_file_stream;  | 
122  |  |  | 
123  |  | static int next_file(fz_context *ctx, fz_stream *stm, size_t n)  | 
124  | 0  | { | 
125  | 0  |   fz_file_stream *state = stm->state;  | 
126  |  |  | 
127  |  |   /* n is only a hint, that we can safely ignore */  | 
128  | 0  |   n = fread(state->buffer, 1, sizeof(state->buffer), state->file);  | 
129  | 0  |   if (n < sizeof(state->buffer) && ferror(state->file))  | 
130  | 0  |     fz_throw(ctx, FZ_ERROR_SYSTEM, "read error: %s", strerror(errno));  | 
131  | 0  |   stm->rp = state->buffer;  | 
132  | 0  |   stm->wp = state->buffer + n;  | 
133  | 0  |   stm->pos += (int64_t)n;  | 
134  |  | 
  | 
135  | 0  |   if (n == 0)  | 
136  | 0  |     return EOF;  | 
137  | 0  |   return *stm->rp++;  | 
138  | 0  | }  | 
139  |  |  | 
140  |  | static void seek_file(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)  | 
141  | 0  | { | 
142  | 0  |   fz_file_stream *state = stm->state;  | 
143  |  | #ifdef _WIN32  | 
144  |  |   int64_t n = _fseeki64(state->file, offset, whence);  | 
145  |  | #else  | 
146  | 0  |   int64_t n = fseeko(state->file, offset, whence);  | 
147  | 0  | #endif  | 
148  | 0  |   if (n < 0)  | 
149  | 0  |     fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot seek: %s", strerror(errno));  | 
150  |  | #ifdef _WIN32  | 
151  |  |   stm->pos = _ftelli64(state->file);  | 
152  |  | #else  | 
153  | 0  |   stm->pos = ftello(state->file);  | 
154  | 0  | #endif  | 
155  | 0  |   stm->rp = state->buffer;  | 
156  | 0  |   stm->wp = state->buffer;  | 
157  | 0  | }  | 
158  |  |  | 
159  |  | static void drop_file(fz_context *ctx, void *state_)  | 
160  | 0  | { | 
161  | 0  |   fz_file_stream *state = state_;  | 
162  | 0  |   if (state->filename && state->del_on_drop)  | 
163  | 0  |   { | 
164  |  | #ifdef _WIN32  | 
165  |  |     if (state->filename_w)  | 
166  |  |       _wunlink(state->filename_w);  | 
167  |  |     else  | 
168  |  | #endif  | 
169  | 0  |     unlink(state->filename);  | 
170  | 0  |   }  | 
171  |  | #ifdef _WIN32  | 
172  |  |   fz_free(ctx, state->filename_w);  | 
173  |  | #endif  | 
174  | 0  |   fz_free(ctx, state->filename);  | 
175  | 0  |   fz_free(ctx, state);  | 
176  | 0  | }  | 
177  |  |  | 
178  |  | static void close_and_drop_file(fz_context *ctx, void *state_)  | 
179  | 0  | { | 
180  | 0  |   fz_file_stream *state = state_;  | 
181  | 0  |   if (state)  | 
182  | 0  |   { | 
183  | 0  |     int n = fclose(state->file);  | 
184  | 0  |     if (n < 0)  | 
185  | 0  |       fz_warn(ctx, "close error: %s", strerror(errno));  | 
186  | 0  |     drop_file(ctx, state_);  | 
187  | 0  |   }  | 
188  | 0  | }  | 
189  |  |  | 
190  |  | static fz_stream *  | 
191  |  | fz_open_file_ptr(fz_context *ctx, FILE *file, const char *name, int wide, int del_on_drop)  | 
192  | 0  | { | 
193  | 0  |   fz_stream *stm;  | 
194  | 0  |   fz_file_stream *state = NULL;  | 
195  |  | 
  | 
196  | 0  |   fz_var(state);  | 
197  |  | 
  | 
198  | 0  | #ifndef _WIN32  | 
199  | 0  |   assert(!wide);  | 
200  | 0  | #endif  | 
201  | 0  |   fz_try(ctx)  | 
202  | 0  |   { | 
203  | 0  |     state = fz_malloc_struct(ctx, fz_file_stream);  | 
204  | 0  |     state->file = file;  | 
205  |  | #ifdef _WIN32  | 
206  |  |     if (wide)  | 
207  |  |     { | 
208  |  |       size_t z = wcslen((const wchar_t *)name)+1;  | 
209  |  |       state->filename_w = fz_malloc(ctx, z*2);  | 
210  |  |       memcpy(state->filename_w, name, z*2);  | 
211  |  |       state->filename = fz_utf8_from_wchar(ctx, (const wchar_t *)name);  | 
212  |  |     }  | 
213  |  |     else  | 
214  |  | #endif  | 
215  | 0  |     state->filename = fz_strdup(ctx, name);  | 
216  | 0  |     state->del_on_drop = del_on_drop;  | 
217  |  | 
  | 
218  | 0  |     stm = fz_new_stream(ctx, state, next_file, close_and_drop_file);  | 
219  | 0  |     stm->seek = seek_file;  | 
220  | 0  |   }  | 
221  | 0  |   fz_catch(ctx)  | 
222  | 0  |   { | 
223  | 0  |     if (state == NULL && del_on_drop)  | 
224  | 0  |     { | 
225  | 0  |       fclose(file);  | 
226  |  | #ifdef _WIN32  | 
227  |  |       if (wide)  | 
228  |  |         _wunlink((const wchar_t *)name);  | 
229  |  |       else  | 
230  |  | #endif  | 
231  | 0  |         unlink(name);  | 
232  | 0  |     }  | 
233  | 0  |     else  | 
234  | 0  |       close_and_drop_file(ctx, state);  | 
235  | 0  |     fz_rethrow(ctx);  | 
236  | 0  |   }  | 
237  |  |  | 
238  | 0  |   return stm;  | 
239  | 0  | }  | 
240  |  |  | 
241  |  | fz_stream *fz_open_file_ptr_no_close(fz_context *ctx, FILE *file)  | 
242  | 0  | { | 
243  | 0  |   fz_stream *stm;  | 
244  | 0  |   fz_file_stream *state = fz_malloc_struct(ctx, fz_file_stream);  | 
245  | 0  |   state->file = file;  | 
246  |  |  | 
247  |  |   /* We don't own the file ptr. Ensure we don't close it */  | 
248  | 0  |   stm = fz_new_stream(ctx, state, next_file, drop_file);  | 
249  | 0  |   stm->seek = seek_file;  | 
250  |  | 
  | 
251  | 0  |   return stm;  | 
252  | 0  | }  | 
253  |  |  | 
254  |  | fz_stream *  | 
255  |  | fz_open_file(fz_context *ctx, const char *name)  | 
256  | 0  | { | 
257  | 0  |   FILE *file;  | 
258  |  | #ifdef _WIN32  | 
259  |  |   file = fz_fopen_utf8(name, "rb");  | 
260  |  | #else  | 
261  | 0  |   file = fopen(name, "rb");  | 
262  | 0  | #endif  | 
263  | 0  |   if (file == NULL)  | 
264  | 0  |     fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open %s: %s", name, strerror(errno));  | 
265  | 0  |   return fz_open_file_ptr(ctx, file, name, 0, 0);  | 
266  | 0  | }  | 
267  |  |  | 
268  |  | fz_stream *  | 
269  |  | fz_open_file_autodelete(fz_context *ctx, const char *name)  | 
270  | 0  | { | 
271  | 0  |   FILE *file;  | 
272  |  | #ifdef _WIN32  | 
273  |  |   file = fz_fopen_utf8(name, "rb");  | 
274  |  | #else  | 
275  | 0  |   file = fopen(name, "rb");  | 
276  | 0  | #endif  | 
277  | 0  |   if (file == NULL)  | 
278  | 0  |     fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open %s: %s", name, strerror(errno));  | 
279  | 0  |   return fz_open_file_ptr(ctx, file, name, 0, 1);  | 
280  | 0  | }  | 
281  |  |  | 
282  |  | fz_stream *  | 
283  |  | fz_try_open_file(fz_context *ctx, const char *name)  | 
284  | 0  | { | 
285  | 0  |   FILE *file;  | 
286  |  | #ifdef _WIN32  | 
287  |  |   file = fz_fopen_utf8(name, "rb");  | 
288  |  | #else  | 
289  | 0  |   file = fopen(name, "rb");  | 
290  | 0  | #endif  | 
291  | 0  |   if (file == NULL)  | 
292  | 0  |     return NULL;  | 
293  | 0  |   return fz_open_file_ptr(ctx, file, name, 0, 0);  | 
294  | 0  | }  | 
295  |  |  | 
296  |  | #ifdef _WIN32  | 
297  |  | fz_stream *  | 
298  |  | fz_open_file_w(fz_context *ctx, const wchar_t *name)  | 
299  |  | { | 
300  |  |   FILE *file = _wfopen(name, L"rb");  | 
301  |  |   if (file == NULL)  | 
302  |  |     fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open file %ls: %s", name, strerror(errno));  | 
303  |  |  | 
304  |  |   return fz_open_file_ptr(ctx, file, (const char *)name, 1, 0);  | 
305  |  | }  | 
306  |  | #endif  | 
307  |  |  | 
308  |  | const char *  | 
309  |  | fz_stream_filename(fz_context *ctx, fz_stream *stm)  | 
310  | 0  | { | 
311  | 0  |   if (!stm || stm->next != next_file)  | 
312  | 0  |     return NULL;  | 
313  |  |  | 
314  | 0  |   return ((fz_file_stream *)stm->state)->filename;  | 
315  | 0  | }  | 
316  |  |  | 
317  |  | /* Memory stream */  | 
318  |  |  | 
319  |  | static int next_buffer(fz_context *ctx, fz_stream *stm, size_t max)  | 
320  | 83.3k  | { | 
321  | 83.3k  |   return EOF;  | 
322  | 83.3k  | }  | 
323  |  |  | 
324  |  | static void seek_buffer(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)  | 
325  | 1.13M  | { | 
326  | 1.13M  |   int64_t pos = stm->pos - (stm->wp - stm->rp);  | 
327  |  |   /* Convert to absolute pos */  | 
328  | 1.13M  |   if (whence == 1)  | 
329  | 0  |   { | 
330  | 0  |     offset += pos; /* Was relative to current pos */  | 
331  | 0  |   }  | 
332  | 1.13M  |   else if (whence == 2)  | 
333  | 8.43k  |   { | 
334  | 8.43k  |     offset += stm->pos; /* Was relative to end */  | 
335  | 8.43k  |   }  | 
336  |  |  | 
337  | 1.13M  |   if (offset < 0)  | 
338  | 5  |     offset = 0;  | 
339  | 1.13M  |   if (offset > stm->pos)  | 
340  | 14.0k  |     offset = stm->pos;  | 
341  | 1.13M  |   stm->rp += (int)(offset - pos);  | 
342  | 1.13M  | }  | 
343  |  |  | 
344  |  | static void drop_buffer(fz_context *ctx, void *state_)  | 
345  | 59.3k  | { | 
346  | 59.3k  |   fz_buffer *state = (fz_buffer *)state_;  | 
347  | 59.3k  |   fz_drop_buffer(ctx, state);  | 
348  | 59.3k  | }  | 
349  |  |  | 
350  |  | fz_stream *  | 
351  |  | fz_open_buffer(fz_context *ctx, fz_buffer *buf)  | 
352  | 59.3k  | { | 
353  | 59.3k  |   fz_stream *stm;  | 
354  |  |  | 
355  | 59.3k  |   if (buf == NULL)  | 
356  | 0  |     return NULL;  | 
357  |  |  | 
358  | 59.3k  |   fz_keep_buffer(ctx, buf);  | 
359  | 59.3k  |   stm = fz_new_stream(ctx, buf, next_buffer, drop_buffer);  | 
360  | 59.3k  |   stm->seek = seek_buffer;  | 
361  |  |  | 
362  | 59.3k  |   stm->rp = buf->data;  | 
363  | 59.3k  |   stm->wp = buf->data + buf->len;  | 
364  |  |  | 
365  | 59.3k  |   stm->pos = (int64_t)buf->len;  | 
366  |  |  | 
367  | 59.3k  |   return stm;  | 
368  | 59.3k  | }  | 
369  |  |  | 
370  |  | fz_stream *  | 
371  |  | fz_open_memory(fz_context *ctx, const unsigned char *data, size_t len)  | 
372  | 15.6k  | { | 
373  | 15.6k  |   fz_stream *stm;  | 
374  |  |  | 
375  | 15.6k  |   stm = fz_new_stream(ctx, NULL, next_buffer, NULL);  | 
376  | 15.6k  |   stm->seek = seek_buffer;  | 
377  |  |  | 
378  | 15.6k  |   stm->rp = (unsigned char *)data;  | 
379  | 15.6k  |   stm->wp = (unsigned char *)data + len;  | 
380  |  |  | 
381  | 15.6k  |   stm->pos = (int64_t)len;  | 
382  |  |  | 
383  | 15.6k  |   return stm;  | 
384  | 15.6k  | }  |