2 * *****************************************************************************
4 * SPDX-License-Identifier: BSD-2-Clause
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * *****************************************************************************
32 * Code for implementing buffered I/O on my own terms.
48 * Translates an integer into a string.
49 * @param val The value to translate.
50 * @param buf The return parameter.
52 static void bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH])
54 char buf2[BC_FILE_ULL_LENGTH];
57 // We need to make sure the entire thing is zeroed.
58 memset(buf2, 0, BC_FILE_ULL_LENGTH);
60 // The i = 1 is to ensure that there is a null byte at the end.
61 for (i = 1; val; ++i) {
63 unsigned long long mod = val % 10;
65 buf2[i] = ((char) mod) + '0';
71 // Since buf2 is reversed, reverse it into buf.
72 for (i = 0; i < len; ++i) buf[i] = buf2[len - i - 1];
76 * Output to the file directly.
77 * @param fd The file descriptor.
78 * @param buf The buffer of data to output.
79 * @param n The number of bytes to output.
80 * @return A status indicating error or success. We could have a fatal I/O
83 static BcStatus bc_file_output(int fd, const char *buf, size_t n) {
90 // While the number of bytes written is less than intended...
94 ssize_t written = write(fd, buf + bytes, n - bytes);
96 // Check for error and return, if any.
97 if (BC_ERR(written == -1))
98 return errno == EPIPE ? BC_STATUS_EOF : BC_STATUS_ERROR_FATAL;
100 bytes += (size_t) written;
103 BC_SIG_TRYUNLOCK(lock);
105 return BC_STATUS_SUCCESS;
108 BcStatus bc_file_flushErr(BcFile *restrict f, BcFlushType type)
112 // If there is stuff to output...
115 #if BC_ENABLE_HISTORY
117 // If history is enabled...
120 // If we have been told to save the extras, and there *are*
122 if (f->buf[f->len - 1] != '\n' &&
123 (type == BC_FLUSH_SAVE_EXTRAS_CLEAR ||
124 type == BC_FLUSH_SAVE_EXTRAS_NO_CLEAR))
128 // Look for the last newline.
129 for (i = f->len - 2; i < f->len && f->buf[i] != '\n'; --i);
134 bc_vec_string(&vm.history.extras, f->len - i, f->buf + i);
136 // Else clear the extras if told to.
137 else if (type >= BC_FLUSH_NO_EXTRAS_CLEAR) {
138 bc_vec_popAll(&vm.history.extras);
141 #endif // BC_ENABLE_HISTORY
144 s = bc_file_output(f->fd, f->buf, f->len);
147 else s = BC_STATUS_SUCCESS;
152 void bc_file_flush(BcFile *restrict f, BcFlushType type) {
154 BcStatus s = bc_file_flushErr(f, type);
156 // If we have an error...
159 // For EOF, set it and jump.
160 if (s == BC_STATUS_EOF) {
161 vm.status = (sig_atomic_t) s;
164 // Blow up on fatal error. Okay, not blow up, just quit.
165 else bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
169 void bc_file_write(BcFile *restrict f, BcFlushType type,
170 const char *buf, size_t n)
172 // If we have enough to flush, do it.
173 if (n > f->cap - f->len) {
174 bc_file_flush(f, type);
178 // If the output is large enough to flush by itself, just output it.
179 // Otherwise, put it into the buffer.
180 if (BC_UNLIKELY(n > f->cap - f->len)) bc_file_output(f->fd, buf, n);
182 memcpy(f->buf + f->len, buf, n);
187 void bc_file_printf(BcFile *restrict f, const char *fmt, ...)
192 bc_file_vprintf(f, fmt, args);
196 void bc_file_vprintf(BcFile *restrict f, const char *fmt, va_list args) {
199 const char *ptr = fmt;
200 char buf[BC_FILE_ULL_LENGTH];
202 // This is a poor man's printf(). While I could look up algorithms to make
203 // it as fast as possible, and should when I write the standard library for
204 // a new language, for bc, outputting is not the bottleneck. So we cheese it
207 // Find each percent sign.
208 while ((percent = strchr(ptr, '%')) != NULL) {
212 // If the percent sign is not where we are, write what's inbetween to
214 if (percent != ptr) {
215 size_t len = (size_t) (percent - ptr);
216 bc_file_write(f, bc_flush_none, ptr, len);
221 // We only parse some format specifiers, the ones bc uses. If you add
222 // more, you need to make sure to add them here.
225 uchar uc = (uchar) va_arg(args, int);
227 bc_file_putchar(f, bc_flush_none, uc);
231 char *s = va_arg(args, char*);
233 bc_file_puts(f, bc_flush_none, s);
236 // We only print signed integers in debug code.
239 int d = va_arg(args, int);
241 // Take care of negative. Let's not worry about overflow.
243 bc_file_putchar(f, bc_flush_none, '-');
247 // Either print 0 or translate and print.
248 if (!d) bc_file_putchar(f, bc_flush_none, '0');
250 bc_file_ultoa((unsigned long long) d, buf);
251 bc_file_puts(f, bc_flush_none, buf);
254 #endif // BC_DEBUG_CODE
257 unsigned long long ull;
259 // These are the ones that it expects from here. Fortunately, all of
260 // these are unsigned types, so they can use the same code, more or
262 assert((c == 'l' || c == 'z') && percent[2] == 'u');
264 if (c == 'z') ull = (unsigned long long) va_arg(args, size_t);
265 else ull = (unsigned long long) va_arg(args, unsigned long);
267 // Either print 0 or translate and print.
268 if (!ull) bc_file_putchar(f, bc_flush_none, '0');
270 bc_file_ultoa(ull, buf);
271 bc_file_puts(f, bc_flush_none, buf);
275 // Increment to the next spot after the specifier.
276 ptr = percent + 2 + (c == 'l' || c == 'z');
279 // If we get here, there are no more percent signs, so we just output
281 if (ptr[0]) bc_file_puts(f, bc_flush_none, ptr);
284 void bc_file_puts(BcFile *restrict f, BcFlushType type, const char *str) {
285 bc_file_write(f, type, str, strlen(str));
288 void bc_file_putchar(BcFile *restrict f, BcFlushType type, uchar c) {
290 if (f->len == f->cap) bc_file_flush(f, type);
292 assert(f->len < f->cap);
294 f->buf[f->len] = (char) c;
298 void bc_file_init(BcFile *f, int fd, char *buf, size_t cap) {
300 BC_SIG_ASSERT_LOCKED;
308 void bc_file_free(BcFile *f) {
309 BC_SIG_ASSERT_LOCKED;
310 bc_file_flush(f, bc_flush_none);