xref: /freebsd/contrib/bc/src/file.c (revision a970610a3af63b3f4df5b69d91c6b4093a00ed8f)
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