Coverage Report

Created: 2026-03-31 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/stream-open.c
Line
Count
Source
1
// Copyright (C) 2004-2026 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
17
{
58
17
  fz_stream *stm = NULL;
59
60
34
  fz_try(ctx)
61
34
  {
62
17
    stm = fz_malloc_struct(ctx, fz_stream);
63
17
  }
64
34
  fz_catch(ctx)
65
0
  {
66
0
    if (drop)
67
0
      drop(ctx, state);
68
0
    fz_rethrow(ctx);
69
0
  }
70
71
17
  stm->refs = 1;
72
17
  stm->error = 0;
73
17
  stm->eof = 0;
74
17
  stm->pos = 0;
75
76
17
  stm->bits = 0;
77
17
  stm->avail = 0;
78
79
17
  stm->rp = NULL;
80
17
  stm->wp = NULL;
81
82
17
  stm->state = state;
83
17
  stm->next = next;
84
17
  stm->drop = drop;
85
17
  stm->seek = NULL;
86
87
17
  return stm;
88
17
}
89
90
fz_stream *
91
fz_keep_stream(fz_context *ctx, fz_stream *stm)
92
20
{
93
20
  return fz_keep_imp(ctx, stm, &stm->refs);
94
20
}
95
96
void
97
fz_drop_stream(fz_context *ctx, fz_stream *stm)
98
39
{
99
39
  if (fz_drop_imp(ctx, stm, &stm->refs))
100
17
  {
101
17
    if (stm->drop)
102
5
      stm->drop(ctx, stm->state);
103
17
    fz_free(ctx, stm);
104
17
  }
105
39
}
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
0
  fz_file_stream *state_to_free = NULL;
196
197
0
  fz_var(state);
198
0
  fz_var(state_to_free);
199
200
0
#ifndef _WIN32
201
0
  assert(!wide);
202
0
#endif
203
0
  fz_try(ctx)
204
0
  {
205
0
    state_to_free = state = fz_malloc_struct(ctx, fz_file_stream);
206
0
    state->file = file;
207
#ifdef _WIN32
208
    if (wide)
209
    {
210
      size_t z = wcslen((const wchar_t *)name)+1;
211
      state->filename_w = fz_malloc(ctx, z*2);
212
      memcpy(state->filename_w, name, z*2);
213
      state->filename = fz_utf8_from_wchar(ctx, (const wchar_t *)name);
214
    }
215
    else
216
#endif
217
0
    state->filename = fz_strdup(ctx, name);
218
0
    state->del_on_drop = del_on_drop;
219
220
0
    state_to_free = NULL;
221
0
    stm = fz_new_stream(ctx, state, next_file, close_and_drop_file);
222
0
    stm->seek = seek_file;
223
0
  }
224
0
  fz_catch(ctx)
225
0
  {
226
0
    if (state == NULL && del_on_drop)
227
0
    {
228
0
      fclose(file);
229
#ifdef _WIN32
230
      if (wide)
231
        _wunlink((const wchar_t *)name);
232
      else
233
#endif
234
0
        unlink(name);
235
0
    }
236
0
    if (state_to_free)
237
0
      close_and_drop_file(ctx, state_to_free);
238
0
    fz_rethrow(ctx);
239
0
  }
240
241
0
  return stm;
242
0
}
243
244
fz_stream *fz_open_file_ptr_no_close(fz_context *ctx, FILE *file)
245
0
{
246
0
  fz_stream *stm;
247
0
  fz_file_stream *state = fz_malloc_struct(ctx, fz_file_stream);
248
0
  state->file = file;
249
250
  /* We don't own the file ptr. Ensure we don't close it */
251
0
  stm = fz_new_stream(ctx, state, next_file, drop_file);
252
0
  stm->seek = seek_file;
253
254
0
  return stm;
255
0
}
256
257
fz_stream *
258
fz_open_file(fz_context *ctx, const char *name)
259
0
{
260
0
  FILE *file;
261
#ifdef _WIN32
262
  file = fz_fopen_utf8(name, "rb");
263
#else
264
0
  file = fopen(name, "rb");
265
0
#endif
266
0
  if (file == NULL)
267
0
    fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open %s: %s", name, strerror(errno));
268
0
  return fz_open_file_ptr(ctx, file, name, 0, 0);
269
0
}
270
271
fz_stream *
272
fz_open_file_autodelete(fz_context *ctx, const char *name)
273
0
{
274
0
  FILE *file;
275
#ifdef _WIN32
276
  file = fz_fopen_utf8(name, "rb");
277
#else
278
0
  file = fopen(name, "rb");
279
0
#endif
280
0
  if (file == NULL)
281
0
    fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open %s: %s", name, strerror(errno));
282
0
  return fz_open_file_ptr(ctx, file, name, 0, 1);
283
0
}
284
285
fz_stream *
286
fz_try_open_file(fz_context *ctx, const char *name)
287
0
{
288
0
  FILE *file;
289
#ifdef _WIN32
290
  file = fz_fopen_utf8(name, "rb");
291
#else
292
0
  file = fopen(name, "rb");
293
0
#endif
294
0
  if (file == NULL)
295
0
    return NULL;
296
0
  return fz_open_file_ptr(ctx, file, name, 0, 0);
297
0
}
298
299
#ifdef _WIN32
300
fz_stream *
301
fz_open_file_w(fz_context *ctx, const wchar_t *name)
302
{
303
  FILE *file = _wfopen(name, L"rb");
304
  if (file == NULL)
305
    fz_throw(ctx, FZ_ERROR_SYSTEM, "cannot open file %ls: %s", name, strerror(errno));
306
307
  return fz_open_file_ptr(ctx, file, (const char *)name, 1, 0);
308
}
309
#endif
310
311
const char *
312
fz_stream_filename(fz_context *ctx, fz_stream *stm)
313
0
{
314
0
  if (!stm || stm->next != next_file)
315
0
    return NULL;
316
317
0
  return ((fz_file_stream *)stm->state)->filename;
318
0
}
319
320
/* Memory stream */
321
322
static int next_buffer(fz_context *ctx, fz_stream *stm, size_t max)
323
24
{
324
24
  return EOF;
325
24
}
326
327
static void seek_buffer(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
328
14.7k
{
329
14.7k
  int64_t pos = stm->pos - (stm->wp - stm->rp);
330
  /* Convert to absolute pos */
331
14.7k
  if (whence == 1)
332
0
  {
333
0
    offset += pos; /* Was relative to current pos */
334
0
  }
335
14.7k
  else if (whence == 2)
336
8
  {
337
8
    offset += stm->pos; /* Was relative to end */
338
8
  }
339
340
14.7k
  if (offset < 0)
341
0
    offset = 0;
342
14.7k
  if (offset > stm->pos)
343
6
    offset = stm->pos;
344
14.7k
  stm->rp += (int)(offset - pos);
345
14.7k
}
346
347
static void drop_buffer(fz_context *ctx, void *state_)
348
0
{
349
0
  fz_buffer *state = (fz_buffer *)state_;
350
0
  fz_drop_buffer(ctx, state);
351
0
}
352
353
fz_stream *
354
fz_open_buffer(fz_context *ctx, fz_buffer *buf)
355
0
{
356
0
  fz_stream *stm;
357
358
0
  if (buf == NULL)
359
0
    return NULL;
360
361
0
  fz_keep_buffer(ctx, buf);
362
0
  stm = fz_new_stream(ctx, buf, next_buffer, drop_buffer);
363
0
  stm->seek = seek_buffer;
364
365
0
  stm->rp = buf->data;
366
0
  stm->wp = buf->data + buf->len;
367
368
0
  stm->pos = (int64_t)buf->len;
369
370
0
  return stm;
371
0
}
372
373
fz_stream *
374
fz_open_memory(fz_context *ctx, const unsigned char *data, size_t len)
375
12
{
376
12
  fz_stream *stm;
377
378
12
  stm = fz_new_stream(ctx, NULL, next_buffer, NULL);
379
12
  stm->seek = seek_buffer;
380
381
12
  stm->rp = (unsigned char *)data;
382
12
  stm->wp = (unsigned char *)data + len;
383
384
12
  stm->pos = (int64_t)len;
385
386
12
  return stm;
387
12
}