1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 6*a970610aSStefan Eßer * Copyright (c) 2018-2024 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Code for implementing buffered I/O on my own terms. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <errno.h> 38252884aeSStefan Eßer #include <string.h> 397e5c51e5SStefan Eßer 407e5c51e5SStefan Eßer #ifndef _WIN32 41252884aeSStefan Eßer #include <unistd.h> 427e5c51e5SStefan Eßer #endif // _WIN32 43252884aeSStefan Eßer 44252884aeSStefan Eßer #include <file.h> 45252884aeSStefan Eßer #include <vm.h> 46252884aeSStefan Eßer 4778bc019dSStefan Eßer #if !BC_ENABLE_LINE_LIB 4878bc019dSStefan Eßer 4944d4804dSStefan Eßer /** 5044d4804dSStefan Eßer * Translates an integer into a string. 5144d4804dSStefan Eßer * @param val The value to translate. 5244d4804dSStefan Eßer * @param buf The return parameter. 5344d4804dSStefan Eßer */ 5478bc019dSStefan Eßer static void 5578bc019dSStefan Eßer bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH]) 5650696a6eSStefan Eßer { 57252884aeSStefan Eßer char buf2[BC_FILE_ULL_LENGTH]; 58252884aeSStefan Eßer size_t i, len; 59252884aeSStefan Eßer 6044d4804dSStefan Eßer // We need to make sure the entire thing is zeroed. 6178bc019dSStefan Eßer // NOLINTNEXTLINE 62252884aeSStefan Eßer memset(buf2, 0, BC_FILE_ULL_LENGTH); 63252884aeSStefan Eßer 64252884aeSStefan Eßer // The i = 1 is to ensure that there is a null byte at the end. 6578bc019dSStefan Eßer for (i = 1; val; ++i) 6678bc019dSStefan Eßer { 67252884aeSStefan Eßer unsigned long long mod = val % 10; 6844d4804dSStefan Eßer 69252884aeSStefan Eßer buf2[i] = ((char) mod) + '0'; 70252884aeSStefan Eßer val /= 10; 71252884aeSStefan Eßer } 72252884aeSStefan Eßer 73252884aeSStefan Eßer len = i; 74252884aeSStefan Eßer 7544d4804dSStefan Eßer // Since buf2 is reversed, reverse it into buf. 7678bc019dSStefan Eßer for (i = 0; i < len; ++i) 7778bc019dSStefan Eßer { 7878bc019dSStefan Eßer buf[i] = buf2[len - i - 1]; 7978bc019dSStefan Eßer } 80252884aeSStefan Eßer } 81252884aeSStefan Eßer 8244d4804dSStefan Eßer /** 8344d4804dSStefan Eßer * Output to the file directly. 8444d4804dSStefan Eßer * @param fd The file descriptor. 8544d4804dSStefan Eßer * @param buf The buffer of data to output. 8644d4804dSStefan Eßer * @param n The number of bytes to output. 8744d4804dSStefan Eßer * @return A status indicating error or success. We could have a fatal I/O 8844d4804dSStefan Eßer * error or EOF. 8944d4804dSStefan Eßer */ 9078bc019dSStefan Eßer static BcStatus 9178bc019dSStefan Eßer bc_file_output(int fd, const char* buf, size_t n) 9278bc019dSStefan Eßer { 93252884aeSStefan Eßer size_t bytes = 0; 94252884aeSStefan Eßer sig_atomic_t lock; 95252884aeSStefan Eßer 96252884aeSStefan Eßer BC_SIG_TRYLOCK(lock); 97252884aeSStefan Eßer 9844d4804dSStefan Eßer // While the number of bytes written is less than intended... 9978bc019dSStefan Eßer while (bytes < n) 10078bc019dSStefan Eßer { 10144d4804dSStefan Eßer // Write. 102252884aeSStefan Eßer ssize_t written = write(fd, buf + bytes, n - bytes); 103252884aeSStefan Eßer 10444d4804dSStefan Eßer // Check for error and return, if any. 10578bc019dSStefan Eßer if (BC_ERR(written == -1)) 10678bc019dSStefan Eßer { 10710041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 10810041e99SStefan Eßer 109252884aeSStefan Eßer return errno == EPIPE ? BC_STATUS_EOF : BC_STATUS_ERROR_FATAL; 11010041e99SStefan Eßer } 111252884aeSStefan Eßer 112252884aeSStefan Eßer bytes += (size_t) written; 113252884aeSStefan Eßer } 114252884aeSStefan Eßer 115252884aeSStefan Eßer BC_SIG_TRYUNLOCK(lock); 116252884aeSStefan Eßer 117252884aeSStefan Eßer return BC_STATUS_SUCCESS; 118252884aeSStefan Eßer } 119252884aeSStefan Eßer 12078bc019dSStefan Eßer #endif // !BC_ENABLE_LINE_LIB 12178bc019dSStefan Eßer 12278bc019dSStefan Eßer BcStatus 12378bc019dSStefan Eßer bc_file_flushErr(BcFile* restrict f, BcFlushType type) 1247e5c51e5SStefan Eßer { 125252884aeSStefan Eßer BcStatus s; 126252884aeSStefan Eßer 12710041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 12810041e99SStefan Eßer 12978bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB 1307e5c51e5SStefan Eßer 13178bc019dSStefan Eßer // Just flush and propagate the error. 13278bc019dSStefan Eßer if (fflush(f->f) == EOF) s = BC_STATUS_ERROR_FATAL; 13378bc019dSStefan Eßer else s = BC_STATUS_SUCCESS; 13478bc019dSStefan Eßer 13578bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB 13678bc019dSStefan Eßer 13778bc019dSStefan Eßer // If there is stuff to output... 13878bc019dSStefan Eßer if (f->len) 13978bc019dSStefan Eßer { 1407e5c51e5SStefan Eßer #if BC_ENABLE_HISTORY 14144d4804dSStefan Eßer 14244d4804dSStefan Eßer // If history is enabled... 14378bc019dSStefan Eßer if (BC_TTY) 14478bc019dSStefan Eßer { 14544d4804dSStefan Eßer // If we have been told to save the extras, and there *are* 14644d4804dSStefan Eßer // extras... 1477e5c51e5SStefan Eßer if (f->buf[f->len - 1] != '\n' && 1487e5c51e5SStefan Eßer (type == BC_FLUSH_SAVE_EXTRAS_CLEAR || 1497e5c51e5SStefan Eßer type == BC_FLUSH_SAVE_EXTRAS_NO_CLEAR)) 1507e5c51e5SStefan Eßer { 1517e5c51e5SStefan Eßer size_t i; 1527e5c51e5SStefan Eßer 15344d4804dSStefan Eßer // Look for the last newline. 15478bc019dSStefan Eßer for (i = f->len - 2; i < f->len && f->buf[i] != '\n'; --i) 15578bc019dSStefan Eßer { 15678bc019dSStefan Eßer continue; 15778bc019dSStefan Eßer } 1587e5c51e5SStefan Eßer 1597e5c51e5SStefan Eßer i += 1; 1607e5c51e5SStefan Eßer 16144d4804dSStefan Eßer // Save the extras. 162d101cdd6SStefan Eßer bc_vec_string(&vm->history.extras, f->len - i, f->buf + i); 1637e5c51e5SStefan Eßer } 16444d4804dSStefan Eßer // Else clear the extras if told to. 16578bc019dSStefan Eßer else if (type >= BC_FLUSH_NO_EXTRAS_CLEAR) 16678bc019dSStefan Eßer { 167d101cdd6SStefan Eßer bc_vec_popAll(&vm->history.extras); 1687e5c51e5SStefan Eßer } 1697e5c51e5SStefan Eßer } 1707e5c51e5SStefan Eßer #endif // BC_ENABLE_HISTORY 1717e5c51e5SStefan Eßer 17244d4804dSStefan Eßer // Actually output. 173252884aeSStefan Eßer s = bc_file_output(f->fd, f->buf, f->len); 174252884aeSStefan Eßer f->len = 0; 175252884aeSStefan Eßer } 176252884aeSStefan Eßer else s = BC_STATUS_SUCCESS; 177252884aeSStefan Eßer 17878bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 17978bc019dSStefan Eßer 180252884aeSStefan Eßer return s; 181252884aeSStefan Eßer } 182252884aeSStefan Eßer 18378bc019dSStefan Eßer void 18478bc019dSStefan Eßer bc_file_flush(BcFile* restrict f, BcFlushType type) 18578bc019dSStefan Eßer { 18610041e99SStefan Eßer BcStatus s; 18710041e99SStefan Eßer sig_atomic_t lock; 18810041e99SStefan Eßer 18910041e99SStefan Eßer BC_SIG_TRYLOCK(lock); 19010041e99SStefan Eßer 19110041e99SStefan Eßer s = bc_file_flushErr(f, type); 192252884aeSStefan Eßer 19344d4804dSStefan Eßer // If we have an error... 19478bc019dSStefan Eßer if (BC_ERR(s)) 19578bc019dSStefan Eßer { 19644d4804dSStefan Eßer // For EOF, set it and jump. 19778bc019dSStefan Eßer if (s == BC_STATUS_EOF) 19878bc019dSStefan Eßer { 199d101cdd6SStefan Eßer vm->status = (sig_atomic_t) s; 20010041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 20144d4804dSStefan Eßer BC_JMP; 202252884aeSStefan Eßer } 203*a970610aSStefan Eßer // Make sure to handle non-fatal I/O properly. 204*a970610aSStefan Eßer else if (!f->errors_fatal) 205*a970610aSStefan Eßer { 206*a970610aSStefan Eßer bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 207*a970610aSStefan Eßer } 20844d4804dSStefan Eßer // Blow up on fatal error. Okay, not blow up, just quit. 209*a970610aSStefan Eßer else exit(BC_STATUS_ERROR_FATAL); 210252884aeSStefan Eßer } 21110041e99SStefan Eßer 21210041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 213252884aeSStefan Eßer } 214252884aeSStefan Eßer 21578bc019dSStefan Eßer #if !BC_ENABLE_LINE_LIB 21678bc019dSStefan Eßer 21778bc019dSStefan Eßer void 21878bc019dSStefan Eßer bc_file_write(BcFile* restrict f, BcFlushType type, const char* buf, size_t n) 2197e5c51e5SStefan Eßer { 22010041e99SStefan Eßer sig_atomic_t lock; 22110041e99SStefan Eßer 22210041e99SStefan Eßer BC_SIG_TRYLOCK(lock); 22310041e99SStefan Eßer 22444d4804dSStefan Eßer // If we have enough to flush, do it. 22578bc019dSStefan Eßer if (n > f->cap - f->len) 22678bc019dSStefan Eßer { 2277e5c51e5SStefan Eßer bc_file_flush(f, type); 228252884aeSStefan Eßer assert(!f->len); 229252884aeSStefan Eßer } 230252884aeSStefan Eßer 23144d4804dSStefan Eßer // If the output is large enough to flush by itself, just output it. 23244d4804dSStefan Eßer // Otherwise, put it into the buffer. 23378bc019dSStefan Eßer if (BC_UNLIKELY(n > f->cap - f->len)) 23478bc019dSStefan Eßer { 23578bc019dSStefan Eßer BcStatus s = bc_file_output(f->fd, buf, n); 23678bc019dSStefan Eßer 23778bc019dSStefan Eßer if (BC_ERR(s)) 23878bc019dSStefan Eßer { 23978bc019dSStefan Eßer // For EOF, set it and jump. 24078bc019dSStefan Eßer if (s == BC_STATUS_EOF) 24178bc019dSStefan Eßer { 242d101cdd6SStefan Eßer vm->status = (sig_atomic_t) s; 24378bc019dSStefan Eßer BC_SIG_TRYUNLOCK(lock); 24478bc019dSStefan Eßer BC_JMP; 24578bc019dSStefan Eßer } 246*a970610aSStefan Eßer // Make sure to handle non-fatal I/O properly. 247*a970610aSStefan Eßer else if (!f->errors_fatal) 248*a970610aSStefan Eßer { 249*a970610aSStefan Eßer bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 250*a970610aSStefan Eßer } 25178bc019dSStefan Eßer // Blow up on fatal error. Okay, not blow up, just quit. 252*a970610aSStefan Eßer else exit(BC_STATUS_ERROR_FATAL); 25378bc019dSStefan Eßer } 25478bc019dSStefan Eßer } 25578bc019dSStefan Eßer else 25678bc019dSStefan Eßer { 25778bc019dSStefan Eßer // NOLINTNEXTLINE 258252884aeSStefan Eßer memcpy(f->buf + f->len, buf, n); 259252884aeSStefan Eßer f->len += n; 260252884aeSStefan Eßer } 26110041e99SStefan Eßer 26210041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 263252884aeSStefan Eßer } 264252884aeSStefan Eßer 26578bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 26678bc019dSStefan Eßer 26778bc019dSStefan Eßer void 26878bc019dSStefan Eßer bc_file_printf(BcFile* restrict f, const char* fmt, ...) 2697e5c51e5SStefan Eßer { 270252884aeSStefan Eßer va_list args; 27110041e99SStefan Eßer sig_atomic_t lock; 27210041e99SStefan Eßer 27310041e99SStefan Eßer BC_SIG_TRYLOCK(lock); 274252884aeSStefan Eßer 275252884aeSStefan Eßer va_start(args, fmt); 276252884aeSStefan Eßer bc_file_vprintf(f, fmt, args); 277252884aeSStefan Eßer va_end(args); 27810041e99SStefan Eßer 27910041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 280252884aeSStefan Eßer } 281252884aeSStefan Eßer 28278bc019dSStefan Eßer void 28378bc019dSStefan Eßer bc_file_vprintf(BcFile* restrict f, const char* fmt, va_list args) 28478bc019dSStefan Eßer { 28578bc019dSStefan Eßer BC_SIG_ASSERT_LOCKED; 28678bc019dSStefan Eßer 28778bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB 28878bc019dSStefan Eßer 289d101cdd6SStefan Eßer { 290d101cdd6SStefan Eßer int r; 291d101cdd6SStefan Eßer 292d101cdd6SStefan Eßer // This mess is to silence a warning. 293d101cdd6SStefan Eßer #if BC_CLANG 294d101cdd6SStefan Eßer #pragma clang diagnostic ignored "-Wformat-nonliteral" 295d101cdd6SStefan Eßer #endif // BC_CLANG 296d101cdd6SStefan Eßer r = vfprintf(f->f, fmt, args); 297d101cdd6SStefan Eßer #if BC_CLANG 298d101cdd6SStefan Eßer #pragma clang diagnostic warning "-Wformat-nonliteral" 299d101cdd6SStefan Eßer #endif // BC_CLANG 300d101cdd6SStefan Eßer 30178bc019dSStefan Eßer // Just print and propagate the error. 302d101cdd6SStefan Eßer if (BC_ERR(r < 0)) 30378bc019dSStefan Eßer { 304*a970610aSStefan Eßer // Make sure to handle non-fatal I/O properly. 305*a970610aSStefan Eßer if (!f->errors_fatal) 306*a970610aSStefan Eßer { 30778bc019dSStefan Eßer bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 30878bc019dSStefan Eßer } 309*a970610aSStefan Eßer else 310*a970610aSStefan Eßer { 311*a970610aSStefan Eßer exit(BC_STATUS_ERROR_FATAL); 312*a970610aSStefan Eßer } 313*a970610aSStefan Eßer } 314d101cdd6SStefan Eßer } 31578bc019dSStefan Eßer 31678bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB 317252884aeSStefan Eßer 318d101cdd6SStefan Eßer { 319252884aeSStefan Eßer char* percent; 320252884aeSStefan Eßer const char* ptr = fmt; 321252884aeSStefan Eßer char buf[BC_FILE_ULL_LENGTH]; 322252884aeSStefan Eßer 323d101cdd6SStefan Eßer // This is a poor man's printf(). While I could look up algorithms to 324d101cdd6SStefan Eßer // make it as fast as possible, and should when I write the standard 325d101cdd6SStefan Eßer // library for a new language, for bc, outputting is not the bottleneck. 326d101cdd6SStefan Eßer // So we cheese it for now. 32744d4804dSStefan Eßer 32844d4804dSStefan Eßer // Find each percent sign. 32978bc019dSStefan Eßer while ((percent = strchr(ptr, '%')) != NULL) 33078bc019dSStefan Eßer { 331252884aeSStefan Eßer char c; 332252884aeSStefan Eßer 333d101cdd6SStefan Eßer // If the percent sign is not where we are, write what's inbetween 334d101cdd6SStefan Eßer // to the buffer. 33578bc019dSStefan Eßer if (percent != ptr) 33678bc019dSStefan Eßer { 337252884aeSStefan Eßer size_t len = (size_t) (percent - ptr); 3387e5c51e5SStefan Eßer bc_file_write(f, bc_flush_none, ptr, len); 339252884aeSStefan Eßer } 340252884aeSStefan Eßer 341252884aeSStefan Eßer c = percent[1]; 342252884aeSStefan Eßer 343d101cdd6SStefan Eßer // We only parse some format specifiers, the ones bc uses. If you 344d101cdd6SStefan Eßer // add more, you need to make sure to add them here. 34578bc019dSStefan Eßer if (c == 'c') 34678bc019dSStefan Eßer { 347252884aeSStefan Eßer uchar uc = (uchar) va_arg(args, int); 348252884aeSStefan Eßer 3497e5c51e5SStefan Eßer bc_file_putchar(f, bc_flush_none, uc); 350252884aeSStefan Eßer } 35178bc019dSStefan Eßer else if (c == 's') 35278bc019dSStefan Eßer { 353252884aeSStefan Eßer char* s = va_arg(args, char*); 354252884aeSStefan Eßer 3557e5c51e5SStefan Eßer bc_file_puts(f, bc_flush_none, s); 356252884aeSStefan Eßer } 357103d7cdfSStefan Eßer #if BC_DEBUG 35844d4804dSStefan Eßer // We only print signed integers in debug code. 35978bc019dSStefan Eßer else if (c == 'd') 36078bc019dSStefan Eßer { 361252884aeSStefan Eßer int d = va_arg(args, int); 362252884aeSStefan Eßer 36344d4804dSStefan Eßer // Take care of negative. Let's not worry about overflow. 36478bc019dSStefan Eßer if (d < 0) 36578bc019dSStefan Eßer { 3667e5c51e5SStefan Eßer bc_file_putchar(f, bc_flush_none, '-'); 367252884aeSStefan Eßer d = -d; 368252884aeSStefan Eßer } 369252884aeSStefan Eßer 37044d4804dSStefan Eßer // Either print 0 or translate and print. 3717e5c51e5SStefan Eßer if (!d) bc_file_putchar(f, bc_flush_none, '0'); 37278bc019dSStefan Eßer else 37378bc019dSStefan Eßer { 374252884aeSStefan Eßer bc_file_ultoa((unsigned long long) d, buf); 3757e5c51e5SStefan Eßer bc_file_puts(f, bc_flush_none, buf); 376252884aeSStefan Eßer } 377252884aeSStefan Eßer } 378103d7cdfSStefan Eßer #endif // BC_DEBUG 37978bc019dSStefan Eßer else 38078bc019dSStefan Eßer { 381252884aeSStefan Eßer unsigned long long ull; 382252884aeSStefan Eßer 383d101cdd6SStefan Eßer // These are the ones that it expects from here. Fortunately, 384d101cdd6SStefan Eßer // all of these are unsigned types, so they can use the same 385d101cdd6SStefan Eßer // code, more or less. 386252884aeSStefan Eßer assert((c == 'l' || c == 'z') && percent[2] == 'u'); 387252884aeSStefan Eßer 388252884aeSStefan Eßer if (c == 'z') ull = (unsigned long long) va_arg(args, size_t); 389252884aeSStefan Eßer else ull = (unsigned long long) va_arg(args, unsigned long); 390252884aeSStefan Eßer 39144d4804dSStefan Eßer // Either print 0 or translate and print. 3927e5c51e5SStefan Eßer if (!ull) bc_file_putchar(f, bc_flush_none, '0'); 39378bc019dSStefan Eßer else 39478bc019dSStefan Eßer { 395252884aeSStefan Eßer bc_file_ultoa(ull, buf); 3967e5c51e5SStefan Eßer bc_file_puts(f, bc_flush_none, buf); 397252884aeSStefan Eßer } 398252884aeSStefan Eßer } 399252884aeSStefan Eßer 40044d4804dSStefan Eßer // Increment to the next spot after the specifier. 401252884aeSStefan Eßer ptr = percent + 2 + (c == 'l' || c == 'z'); 402252884aeSStefan Eßer } 403252884aeSStefan Eßer 40444d4804dSStefan Eßer // If we get here, there are no more percent signs, so we just output 40544d4804dSStefan Eßer // whatever is left. 4067e5c51e5SStefan Eßer if (ptr[0]) bc_file_puts(f, bc_flush_none, ptr); 407d101cdd6SStefan Eßer } 40878bc019dSStefan Eßer 40978bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 410252884aeSStefan Eßer } 411252884aeSStefan Eßer 41278bc019dSStefan Eßer void 41378bc019dSStefan Eßer bc_file_puts(BcFile* restrict f, BcFlushType type, const char* str) 41478bc019dSStefan Eßer { 41578bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB 41678bc019dSStefan Eßer // This is used because of flushing issues with using bc_file_write() when 41778bc019dSStefan Eßer // bc is using a line library. It's also using printf() because puts() 41878bc019dSStefan Eßer // writes a newline. 41978bc019dSStefan Eßer bc_file_printf(f, "%s", str); 42078bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB 4217e5c51e5SStefan Eßer bc_file_write(f, type, str, strlen(str)); 42278bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 423252884aeSStefan Eßer } 424252884aeSStefan Eßer 42578bc019dSStefan Eßer void 42678bc019dSStefan Eßer bc_file_putchar(BcFile* restrict f, BcFlushType type, uchar c) 42778bc019dSStefan Eßer { 42810041e99SStefan Eßer sig_atomic_t lock; 42910041e99SStefan Eßer 43010041e99SStefan Eßer BC_SIG_TRYLOCK(lock); 43110041e99SStefan Eßer 43278bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB 43378bc019dSStefan Eßer 43478bc019dSStefan Eßer if (BC_ERR(fputc(c, f->f) == EOF)) 43578bc019dSStefan Eßer { 43678bc019dSStefan Eßer // This is here to prevent a stack overflow from unbounded recursion. 43778bc019dSStefan Eßer if (f->f == stderr) exit(BC_STATUS_ERROR_FATAL); 43878bc019dSStefan Eßer 439*a970610aSStefan Eßer bc_err(BC_ERR_FATAL_IO_ERR); 44078bc019dSStefan Eßer } 44178bc019dSStefan Eßer 44278bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB 44378bc019dSStefan Eßer 4447e5c51e5SStefan Eßer if (f->len == f->cap) bc_file_flush(f, type); 44544d4804dSStefan Eßer 446252884aeSStefan Eßer assert(f->len < f->cap); 44744d4804dSStefan Eßer 448252884aeSStefan Eßer f->buf[f->len] = (char) c; 449252884aeSStefan Eßer f->len += 1; 45010041e99SStefan Eßer 45178bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 45278bc019dSStefan Eßer 45310041e99SStefan Eßer BC_SIG_TRYUNLOCK(lock); 454252884aeSStefan Eßer } 455252884aeSStefan Eßer 45678bc019dSStefan Eßer #if BC_ENABLE_LINE_LIB 45744d4804dSStefan Eßer 45878bc019dSStefan Eßer void 459*a970610aSStefan Eßer bc_file_init(BcFile* f, FILE* file, bool errors_fatal) 46078bc019dSStefan Eßer { 46178bc019dSStefan Eßer BC_SIG_ASSERT_LOCKED; 46278bc019dSStefan Eßer f->f = file; 463*a970610aSStefan Eßer f->errors_fatal = errors_fatal; 46478bc019dSStefan Eßer } 46578bc019dSStefan Eßer 46678bc019dSStefan Eßer #else // BC_ENABLE_LINE_LIB 46778bc019dSStefan Eßer 46878bc019dSStefan Eßer void 469*a970610aSStefan Eßer bc_file_init(BcFile* f, int fd, char* buf, size_t cap, bool errors_fatal) 47078bc019dSStefan Eßer { 471252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 47244d4804dSStefan Eßer 473252884aeSStefan Eßer f->fd = fd; 474252884aeSStefan Eßer f->buf = buf; 475252884aeSStefan Eßer f->len = 0; 476252884aeSStefan Eßer f->cap = cap; 477*a970610aSStefan Eßer f->errors_fatal = errors_fatal; 478252884aeSStefan Eßer } 479252884aeSStefan Eßer 48078bc019dSStefan Eßer #endif // BC_ENABLE_LINE_LIB 48178bc019dSStefan Eßer 48278bc019dSStefan Eßer void 48378bc019dSStefan Eßer bc_file_free(BcFile* f) 48478bc019dSStefan Eßer { 485252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 4867e5c51e5SStefan Eßer bc_file_flush(f, bc_flush_none); 487252884aeSStefan Eßer } 488