comparison mupdf-source/thirdparty/zint/backend/filemem.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* filemem.c - write to file/memory abstraction */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2023-2024 Robin Stuart <rstuart114@gmail.com>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. Neither the name of the project nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 SUCH DAMAGE.
30 */
31 /* SPDX-License-Identifier: BSD-3-Clause */
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <stdarg.h>
37 #ifdef _MSC_VER
38 #include <io.h>
39 #include <fcntl.h>
40 #endif
41
42 #include "filemem.h"
43 #include "output.h"
44
45 #define FM_PAGE_SIZE 0x8000 /* 32k */
46
47 #ifndef EOVERFLOW
48 #define EOVERFLOW EINVAL
49 #endif
50
51 #if defined(_MSC_VER) && _MSC_VER < 1800 /* `va_copy()` not before MSVC 2013 (C++ 12.0) */
52 # define va_copy(dest, src) (dest = src)
53 #else
54 # if defined(ZINT_IS_C89) && !defined(va_copy)
55 # ifdef __GNUC__
56 # define va_copy __va_copy /* Available with clang as well */
57 # else
58 # define va_copy(dest, src) (dest = src) /* Will fail if array (need `*dest = *src`) or something else */
59 # endif
60 # endif
61 #endif
62
63 /* Helper to set `err` only if not already set, returning 0 always for convenience */
64 static int fm_seterr(struct filemem *restrict const fmp, const int err) {
65 if (fmp->err == 0) {
66 fmp->err = err;
67 }
68 return 0;
69 }
70
71 /* Helper to set position, syncing end marker */
72 static void fm_setpos(struct filemem *restrict const fmp, const size_t pos) {
73 assert(pos <= fmp->memsize);
74 fmp->mempos = pos;
75 if (fmp->mempos > fmp->memend) {
76 fmp->memend = fmp->mempos;
77 }
78 }
79
80 /* Helper to clear memory buffer and associates */
81 static void fm_clear_mem(struct filemem *restrict const fmp) {
82 if (fmp->mem) {
83 free(fmp->mem);
84 fmp->mem = NULL;
85 }
86 fmp->memsize = fmp->mempos = fmp->memend = 0;
87 #ifdef FM_NO_VSNPRINTF
88 if (fmp->fp_null) {
89 (void) fclose(fmp->fp_null);
90 fmp->fp_null = NULL;
91 }
92 #endif
93 }
94
95 /* `fopen()` if file, setup memory buffer if BARCODE_MEMORY_FILE, returning 1 on success, 0 on failure */
96 INTERNAL int fm_open(struct filemem *restrict const fmp, struct zint_symbol *symbol, const char *mode) {
97 assert(fmp && symbol && mode);
98 fmp->fp = NULL;
99 fmp->mem = NULL;
100 fmp->memsize = fmp->mempos = fmp->memend = 0;
101 fmp->flags = symbol->output_options & (BARCODE_STDOUT | BARCODE_MEMORY_FILE);
102 fmp->err = 0;
103 #ifdef FM_NO_VSNPRINTF
104 fmp->fp_null = NULL;
105 #endif
106
107 if (fmp->flags & BARCODE_MEMORY_FILE) {
108 if (!(fmp->mem = (unsigned char *) malloc(FM_PAGE_SIZE))) {
109 return fm_seterr(fmp, ENOMEM);
110 }
111 fmp->memsize = FM_PAGE_SIZE;
112 if (symbol->memfile) {
113 free(symbol->memfile);
114 symbol->memfile = NULL;
115 }
116 symbol->memfile_size = 0;
117 return 1;
118 }
119 if (fmp->flags & BARCODE_STDOUT) {
120 #ifdef _MSC_VER
121 if (strchr(mode, 'b') != NULL && _setmode(_fileno(stdout), _O_BINARY) == -1) {
122 return fm_seterr(fmp, errno);
123 }
124 #endif
125 fmp->fp = stdout;
126 return 1;
127 }
128 if (!(fmp->fp = out_fopen(symbol->outfile, mode))) {
129 return fm_seterr(fmp, errno);
130 }
131 return 1;
132 }
133
134 /* Expand memory buffer, returning 1 on success, 0 on failure */
135 static int fm_mem_expand(struct filemem *restrict const fmp, const size_t size) {
136 unsigned char *new_mem;
137 size_t new_size;
138
139 assert(fmp);
140 if (!fmp->mem) {
141 return fm_seterr(fmp, EINVAL);
142 }
143 if (size == 0) {
144 return 1;
145 }
146 if (fmp->mempos + size < fmp->memsize) { /* Fits? */
147 if (fmp->mempos + size <= fmp->mempos) { /* Check for overflow */
148 fm_clear_mem(fmp);
149 return fm_seterr(fmp, EOVERFLOW);
150 }
151 return 1;
152 }
153 new_size = fmp->memsize + (size < FM_PAGE_SIZE ? FM_PAGE_SIZE : size);
154 if (new_size <= fmp->memsize) { /* Check for overflow */
155 fm_clear_mem(fmp);
156 return fm_seterr(fmp, EOVERFLOW);
157 }
158 /* Protect against very large files & (Linux) OOM killer - cf `raster_malloc()` in "raster.c" */
159 if (new_size > 0x40000000 /*1GB*/ || !(new_mem = (unsigned char *) realloc(fmp->mem, new_size))) {
160 fm_clear_mem(fmp);
161 return fm_seterr(fmp, new_size > 0x40000000 ? EINVAL : ENOMEM);
162 }
163 fmp->mem = new_mem;
164 fmp->memsize = new_size;
165 return 1;
166 }
167
168 /* `fwrite()` to file or memory, returning 1 on success, 0 on failure */
169 INTERNAL int fm_write(const void *restrict ptr, const size_t size, const size_t nitems,
170 struct filemem *restrict const fmp) {
171 assert(fmp && ptr);
172 if (fmp->err) {
173 return 0;
174 }
175 if (size == 0 || nitems == 0) {
176 return 1;
177 }
178 if (fmp->flags & BARCODE_MEMORY_FILE) {
179 const size_t tot_size = size * nitems;
180 if (tot_size / size != nitems) {
181 return fm_seterr(fmp, EOVERFLOW);
182 }
183 if (!fm_mem_expand(fmp, tot_size)) {
184 return 0;
185 }
186 memcpy(fmp->mem + fmp->mempos, ptr, tot_size);
187 fm_setpos(fmp, fmp->mempos + tot_size);
188 return 1;
189 }
190 if (fwrite(ptr, size, nitems, fmp->fp) != nitems) {
191 return fm_seterr(fmp, errno);
192 }
193 return 1;
194 }
195
196 /* `fputc()` to file or memory, returning 1 on success, 0 on failure */
197 INTERNAL int fm_putc(const int ch, struct filemem *restrict const fmp) {
198 assert(fmp);
199 if (fmp->err) {
200 return 0;
201 }
202 if (fmp->flags & BARCODE_MEMORY_FILE) {
203 if (!fm_mem_expand(fmp, 1)) {
204 return 0;
205 }
206 fmp->mem[fmp->mempos] = (unsigned char) ch;
207 fm_setpos(fmp, fmp->mempos + 1);
208 return 1;
209 }
210 if (fputc(ch, fmp->fp) == EOF) {
211 return fm_seterr(fmp, errno);
212 }
213 return 1;
214 }
215
216 /* `fputs()` to file or memory, returning 1 on success, 0 on failure */
217 INTERNAL int fm_puts(const char *str, struct filemem *restrict const fmp) {
218 assert(fmp);
219 if (fmp->err) {
220 return 0;
221 }
222 if (fmp->flags & BARCODE_MEMORY_FILE) {
223 const size_t len = strlen(str);
224 if (!fm_mem_expand(fmp, len)) {
225 return 0;
226 }
227 memcpy(fmp->mem + fmp->mempos, str, len);
228 fm_setpos(fmp, fmp->mempos + len);
229 return 1;
230 }
231 if (fputs(str, fmp->fp) == EOF) {
232 return fm_seterr(fmp, errno);
233 }
234 return 1;
235 }
236
237 #ifdef FM_NO_VSNPRINTF
238 # ifdef _WIN32
239 # define DEV_NULL "NUL"
240 # else
241 # define DEV_NULL "/dev/null"
242 # endif
243 #endif
244
245 /* Helper to `printf()` into mem buffer */
246 static int fm_vprintf(struct filemem *restrict const fmp, const char *fmt, va_list ap) {
247 va_list cpy;
248 int size, check;
249
250 /* Adapted from https://stackoverflow.com/a/52558247/664741 */
251 #ifdef FM_NO_VSNPRINTF
252 if (!fmp->fp_null && !(fmp->fp_null = fopen(DEV_NULL, "wb"))) {
253 return fm_seterr(fmp, errno);
254 }
255 #endif
256
257 va_copy(cpy, ap);
258 /* The clang-tidy warning is a bug https://github.com/llvm/llvm-project/issues/40656 */
259 #ifdef FM_NO_VSNPRINTF
260 size = vfprintf(fmp->fp_null, fmt, cpy); /* NOLINT(clang-analyzer-valist.Uninitialized) */
261 #else
262 size = vsnprintf(NULL, 0, fmt, cpy); /* NOLINT(clang-analyzer-valist.Uninitialized) */
263 #endif
264 va_end(cpy);
265
266 if (size < 0) {
267 return fm_seterr(fmp, errno);
268 }
269
270 if (!fm_mem_expand(fmp, size + 1)) {
271 return 0;
272 }
273
274 #ifdef FM_NO_VSNPRINTF
275 /* NOLINTNEXTLINE(clang-analyzer-valist.Uninitialized) - see above */
276 check = vsprintf((char *) fmp->mem + fmp->mempos, fmt, ap);
277 #else
278 /* NOLINTNEXTLINE(clang-analyzer-valist.Uninitialized) - see above */
279 check = vsnprintf((char *) fmp->mem + fmp->mempos, size + 1, fmt, ap);
280 #endif
281
282 (void)check;
283 assert(check == size);
284
285 fm_setpos(fmp, fmp->mempos + size);
286
287 return 1;
288 }
289
290 /* `fprintf()` to file or memory, returning 1 on success, 0 on failure */
291 INTERNAL int fm_printf(struct filemem *restrict const fmp, const char *fmt, ...) {
292 va_list ap;
293 int ret;
294
295 assert(fmp && fmt);
296 if (fmp->err) {
297 return 0;
298 }
299 if (fmp->flags & BARCODE_MEMORY_FILE) {
300 va_start(ap, fmt);
301 ret = fm_vprintf(fmp, fmt, ap);
302 va_end(ap);
303 return ret;
304 }
305 va_start(ap, fmt);
306 ret = vfprintf(fmp->fp, fmt, ap) >= 0; /* NOLINT(clang-analyzer-valist.Uninitialized) - see above */
307 va_end(ap);
308 return ret ? 1 : fm_seterr(fmp, errno);
309 }
310
311 /* Output float without trailing zeroes to `fmp` with decimal pts `dp` (precision), returning 1 on success, 0 on
312 failure */
313 INTERNAL int fm_putsf(const char *prefix, const int dp, const float arg, struct filemem *restrict const fmp) {
314 int i, end;
315 char buf[256]; /* Assuming `dp` reasonable */
316 const int len = sprintf(buf, "%.*f", dp, arg);
317
318 assert(fmp);
319 if (fmp->err) {
320 return 0;
321 }
322 if (prefix && *prefix) {
323 if (!fm_puts(prefix, fmp)) {
324 return 0;
325 }
326 }
327
328 /* Adapted from https://stackoverflow.com/a/36202854/664741 */
329 for (i = len - 1, end = len; i >= 0; i--) {
330 if (buf[i] == '0') {
331 if (end == i + 1) {
332 end = i;
333 }
334 } else if (!z_isdigit(buf[i]) && buf[i] != '-') { /* If not digit or minus then decimal point */
335 if (end == i + 1) {
336 end = i;
337 } else {
338 buf[i] = '.'; /* Overwrite any locale-specific setting for decimal point */
339 }
340 buf[end] = '\0';
341 break;
342 }
343 }
344
345 return fm_puts(buf, fmp);
346 }
347
348 /* `fclose()` if file, set `symbol->memfile` & `symbol->memfile_size` if memory, returning 1 on success, 0 on
349 failure */
350 INTERNAL int fm_close(struct filemem *restrict const fmp, struct zint_symbol *symbol) {
351 assert(fmp && symbol);
352 if (fmp->flags & BARCODE_MEMORY_FILE) {
353 if (fmp->err || !fmp->mem) {
354 fm_clear_mem(fmp);
355 return fm_seterr(fmp, EINVAL);
356 }
357 symbol->memfile_size = (int) fmp->mempos;
358 if ((size_t) symbol->memfile_size != fmp->mempos) {
359 fm_clear_mem(fmp);
360 symbol->memfile_size = 0;
361 return fm_seterr(fmp, EINVAL);
362 }
363 symbol->memfile = fmp->mem;
364 fmp->mem = NULL; /* Now belongs to `symbol` */
365 fm_clear_mem(fmp);
366 return 1;
367 }
368 if (fmp->err || !fmp->fp) {
369 if (!(fmp->flags & BARCODE_STDOUT) && fmp->fp) {
370 (void) fclose(fmp->fp);
371 }
372 return fm_seterr(fmp, EINVAL);
373 }
374 if (fmp->flags & BARCODE_STDOUT) {
375 if (fflush(fmp->fp) != 0) {
376 fmp->fp = NULL;
377 return fm_seterr(fmp, errno);
378 }
379 } else {
380 if (fclose(fmp->fp) != 0) {
381 fmp->fp = NULL;
382 return fm_seterr(fmp, errno);
383 }
384 }
385 fmp->fp = NULL;
386 return 1;
387 }
388
389 /* `fseek()` to file/memory offset, returning 1 if successful, 0 on failure */
390 INTERNAL int fm_seek(struct filemem *restrict const fmp, const long offset, const int whence) {
391 assert(fmp);
392 if (fmp->err) {
393 return 0;
394 }
395 if (fmp->flags & BARCODE_MEMORY_FILE) {
396 const size_t start = whence == SEEK_SET ? 0 : whence == SEEK_CUR ? fmp->mempos : fmp->memend;
397 const size_t new_pos = start + offset;
398 if ((offset > 0 && new_pos <= start) || (offset < 0 && new_pos >= start)) { /* Check for over/underflow */
399 return fm_seterr(fmp, EINVAL);
400 }
401 if (!fm_mem_expand(fmp, new_pos)) {
402 return 0;
403 }
404 fm_setpos(fmp, new_pos);
405 return 1;
406 }
407 if (fseek(fmp->fp, offset, whence) != 0) {
408 return fm_seterr(fmp, errno);
409 }
410 return 1;
411 }
412
413 /* `ftell()` returns current file/memory offset if successful, -1 on failure */
414 INTERNAL long fm_tell(struct filemem *restrict const fmp) {
415 long ret;
416 assert(fmp);
417 if (fmp->err) {
418 return -1;
419 }
420 if (fmp->flags & BARCODE_MEMORY_FILE) {
421 if (!fmp->mem) {
422 (void) fm_seterr(fmp, ENOMEM);
423 return -1;
424 }
425 return (long) fmp->mempos;
426 }
427 ret = ftell(fmp->fp);
428 /* On many Linux distros `ftell()` returns LONG_MAX not -1 on error */
429 if (ret < 0 || ret == LONG_MAX) {
430 (void) fm_seterr(fmp, errno);
431 return -1;
432 }
433 return ret;
434 }
435
436 /* Return `err`, which uses `errno` values; if file and `err` not set, test `ferror()` also */
437 INTERNAL int fm_error(struct filemem *restrict const fmp) {
438 assert(fmp);
439 if (fmp->err == 0 && !(fmp->flags & BARCODE_MEMORY_FILE) && ferror(fmp->fp)) {
440 (void) fm_seterr(fmp, EIO);
441 }
442 return fmp->err;
443 }
444
445 /* `fflush()` if file, no-op (apart from error checking) if memory, returning 1 on success, 0 on failure
446 NOTE: don't use, included only for libpng compatibility */
447 INTERNAL int fm_flush(struct filemem *restrict const fmp) {
448 assert(fmp);
449 if (fmp->err) {
450 return 0;
451 }
452 if (fmp->flags & BARCODE_MEMORY_FILE) {
453 if (!fmp->mem) {
454 return fm_seterr(fmp, EINVAL);
455 }
456 return 1;
457 }
458 if (fflush(fmp->fp) == EOF) {
459 return fm_seterr(fmp, errno);
460 }
461 return 1;
462 }
463
464 /* vim: set ts=4 sw=4 et : */