Mercurial > hgrepos > Python2 > PyMuPDF
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 : */ |
