aboutsummaryrefslogtreecommitdiff
path: root/src/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/file.c')
-rw-r--r--src/file.c354
1 files changed, 255 insertions, 99 deletions
diff --git a/src/file.c b/src/file.c
index 35a4647d..c038c447 100644
--- a/src/file.c
+++ b/src/file.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2023 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -44,22 +44,26 @@
#include <file.h>
#include <vm.h>
+#if !BC_ENABLE_LINE_LIB
+
/**
* Translates an integer into a string.
* @param val The value to translate.
* @param buf The return parameter.
*/
-static void bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH])
+static void
+bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH])
{
char buf2[BC_FILE_ULL_LENGTH];
size_t i, len;
// We need to make sure the entire thing is zeroed.
+ // NOLINTNEXTLINE
memset(buf2, 0, BC_FILE_ULL_LENGTH);
// The i = 1 is to ensure that there is a null byte at the end.
- for (i = 1; val; ++i) {
-
+ for (i = 1; val; ++i)
+ {
unsigned long long mod = val % 10;
buf2[i] = ((char) mod) + '0';
@@ -69,7 +73,10 @@ static void bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH])
len = i;
// Since buf2 is reversed, reverse it into buf.
- for (i = 0; i < len; ++i) buf[i] = buf2[len - i - 1];
+ for (i = 0; i < len; ++i)
+ {
+ buf[i] = buf2[len - i - 1];
+ }
}
/**
@@ -80,22 +87,27 @@ static void bc_file_ultoa(unsigned long long val, char buf[BC_FILE_ULL_LENGTH])
* @return A status indicating error or success. We could have a fatal I/O
* error or EOF.
*/
-static BcStatus bc_file_output(int fd, const char *buf, size_t n) {
-
+static BcStatus
+bc_file_output(int fd, const char* buf, size_t n)
+{
size_t bytes = 0;
sig_atomic_t lock;
BC_SIG_TRYLOCK(lock);
// While the number of bytes written is less than intended...
- while (bytes < n) {
-
+ while (bytes < n)
+ {
// Write.
ssize_t written = write(fd, buf + bytes, n - bytes);
// Check for error and return, if any.
if (BC_ERR(written == -1))
+ {
+ BC_SIG_TRYUNLOCK(lock);
+
return errno == EPIPE ? BC_STATUS_EOF : BC_STATUS_ERROR_FATAL;
+ }
bytes += (size_t) written;
}
@@ -105,18 +117,31 @@ static BcStatus bc_file_output(int fd, const char *buf, size_t n) {
return BC_STATUS_SUCCESS;
}
-BcStatus bc_file_flushErr(BcFile *restrict f, BcFlushType type)
+#endif // !BC_ENABLE_LINE_LIB
+
+BcStatus
+bc_file_flushErr(BcFile* restrict f, BcFlushType type)
{
BcStatus s;
- // If there is stuff to output...
- if (f->len) {
+ BC_SIG_ASSERT_LOCKED;
+#if BC_ENABLE_LINE_LIB
+
+ // Just flush and propagate the error.
+ if (fflush(f->f) == EOF) s = BC_STATUS_ERROR_FATAL;
+ else s = BC_STATUS_SUCCESS;
+
+#else // BC_ENABLE_LINE_LIB
+
+ // If there is stuff to output...
+ if (f->len)
+ {
#if BC_ENABLE_HISTORY
// If history is enabled...
- if (BC_TTY) {
-
+ if (BC_TTY)
+ {
// If we have been told to save the extras, and there *are*
// extras...
if (f->buf[f->len - 1] != '\n' &&
@@ -126,16 +151,20 @@ BcStatus bc_file_flushErr(BcFile *restrict f, BcFlushType type)
size_t i;
// Look for the last newline.
- for (i = f->len - 2; i < f->len && f->buf[i] != '\n'; --i);
+ for (i = f->len - 2; i < f->len && f->buf[i] != '\n'; --i)
+ {
+ continue;
+ }
i += 1;
// Save the extras.
- bc_vec_string(&vm.history.extras, f->len - i, f->buf + i);
+ bc_vec_string(&vm->history.extras, f->len - i, f->buf + i);
}
// Else clear the extras if told to.
- else if (type >= BC_FLUSH_NO_EXTRAS_CLEAR) {
- bc_vec_popAll(&vm.history.extras);
+ else if (type >= BC_FLUSH_NO_EXTRAS_CLEAR)
+ {
+ bc_vec_popAll(&vm->history.extras);
}
}
#endif // BC_ENABLE_HISTORY
@@ -146,146 +175,253 @@ BcStatus bc_file_flushErr(BcFile *restrict f, BcFlushType type)
}
else s = BC_STATUS_SUCCESS;
+#endif // BC_ENABLE_LINE_LIB
+
return s;
}
-void bc_file_flush(BcFile *restrict f, BcFlushType type) {
+void
+bc_file_flush(BcFile* restrict f, BcFlushType type)
+{
+ BcStatus s;
+ sig_atomic_t lock;
+
+ BC_SIG_TRYLOCK(lock);
- BcStatus s = bc_file_flushErr(f, type);
+ s = bc_file_flushErr(f, type);
// If we have an error...
- if (BC_ERR(s)) {
-
+ if (BC_ERR(s))
+ {
// For EOF, set it and jump.
- if (s == BC_STATUS_EOF) {
- vm.status = (sig_atomic_t) s;
+ if (s == BC_STATUS_EOF)
+ {
+ vm->status = (sig_atomic_t) s;
+ BC_SIG_TRYUNLOCK(lock);
BC_JMP;
}
// Blow up on fatal error. Okay, not blow up, just quit.
else bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
}
+
+ BC_SIG_TRYUNLOCK(lock);
}
-void bc_file_write(BcFile *restrict f, BcFlushType type,
- const char *buf, size_t n)
+#if !BC_ENABLE_LINE_LIB
+
+void
+bc_file_write(BcFile* restrict f, BcFlushType type, const char* buf, size_t n)
{
+ sig_atomic_t lock;
+
+ BC_SIG_TRYLOCK(lock);
+
// If we have enough to flush, do it.
- if (n > f->cap - f->len) {
+ if (n > f->cap - f->len)
+ {
bc_file_flush(f, type);
assert(!f->len);
}
// If the output is large enough to flush by itself, just output it.
// Otherwise, put it into the buffer.
- if (BC_UNLIKELY(n > f->cap - f->len)) bc_file_output(f->fd, buf, n);
- else {
+ if (BC_UNLIKELY(n > f->cap - f->len))
+ {
+ BcStatus s = bc_file_output(f->fd, buf, n);
+
+ if (BC_ERR(s))
+ {
+ // For EOF, set it and jump.
+ if (s == BC_STATUS_EOF)
+ {
+ vm->status = (sig_atomic_t) s;
+ BC_SIG_TRYUNLOCK(lock);
+ BC_JMP;
+ }
+ // Blow up on fatal error. Okay, not blow up, just quit.
+ else bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
+ }
+ else
+ {
+ // NOLINTNEXTLINE
memcpy(f->buf + f->len, buf, n);
f->len += n;
}
+
+ BC_SIG_TRYUNLOCK(lock);
}
-void bc_file_printf(BcFile *restrict f, const char *fmt, ...)
+#endif // BC_ENABLE_LINE_LIB
+
+void
+bc_file_printf(BcFile* restrict f, const char* fmt, ...)
{
va_list args;
+ sig_atomic_t lock;
+
+ BC_SIG_TRYLOCK(lock);
va_start(args, fmt);
bc_file_vprintf(f, fmt, args);
va_end(args);
-}
-void bc_file_vprintf(BcFile *restrict f, const char *fmt, va_list args) {
+ BC_SIG_TRYUNLOCK(lock);
+}
- char *percent;
- const char *ptr = fmt;
- char buf[BC_FILE_ULL_LENGTH];
+void
+bc_file_vprintf(BcFile* restrict f, const char* fmt, va_list args)
+{
+ BC_SIG_ASSERT_LOCKED;
- // This is a poor man's printf(). While I could look up algorithms to make
- // it as fast as possible, and should when I write the standard library for
- // a new language, for bc, outputting is not the bottleneck. So we cheese it
- // for now.
+#if BC_ENABLE_LINE_LIB
- // Find each percent sign.
- while ((percent = strchr(ptr, '%')) != NULL) {
+ {
+ int r;
- char c;
+ // This mess is to silence a warning.
+#if BC_CLANG
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif // BC_CLANG
+ r = vfprintf(f->f, fmt, args);
+#if BC_CLANG
+#pragma clang diagnostic warning "-Wformat-nonliteral"
+#endif // BC_CLANG
- // If the percent sign is not where we are, write what's inbetween to
- // the buffer.
- if (percent != ptr) {
- size_t len = (size_t) (percent - ptr);
- bc_file_write(f, bc_flush_none, ptr, len);
+ // Just print and propagate the error.
+ if (BC_ERR(r < 0))
+ {
+ bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
}
+ }
- c = percent[1];
+#else // BC_ENABLE_LINE_LIB
- // We only parse some format specifiers, the ones bc uses. If you add
- // more, you need to make sure to add them here.
- if (c == 'c') {
+ {
+ char* percent;
+ const char* ptr = fmt;
+ char buf[BC_FILE_ULL_LENGTH];
- uchar uc = (uchar) va_arg(args, int);
+ // This is a poor man's printf(). While I could look up algorithms to
+ // make it as fast as possible, and should when I write the standard
+ // library for a new language, for bc, outputting is not the bottleneck.
+ // So we cheese it for now.
- bc_file_putchar(f, bc_flush_none, uc);
- }
- else if (c == 's') {
+ // Find each percent sign.
+ while ((percent = strchr(ptr, '%')) != NULL)
+ {
+ char c;
- char *s = va_arg(args, char*);
+ // If the percent sign is not where we are, write what's inbetween
+ // to the buffer.
+ if (percent != ptr)
+ {
+ size_t len = (size_t) (percent - ptr);
+ bc_file_write(f, bc_flush_none, ptr, len);
+ }
- bc_file_puts(f, bc_flush_none, s);
- }
-#if BC_DEBUG_CODE
- // We only print signed integers in debug code.
- else if (c == 'd') {
+ c = percent[1];
- int d = va_arg(args, int);
+ // We only parse some format specifiers, the ones bc uses. If you
+ // add more, you need to make sure to add them here.
+ if (c == 'c')
+ {
+ uchar uc = (uchar) va_arg(args, int);
- // Take care of negative. Let's not worry about overflow.
- if (d < 0) {
- bc_file_putchar(f, bc_flush_none, '-');
- d = -d;
+ bc_file_putchar(f, bc_flush_none, uc);
}
+ else if (c == 's')
+ {
+ char* s = va_arg(args, char*);
- // Either print 0 or translate and print.
- if (!d) bc_file_putchar(f, bc_flush_none, '0');
- else {
- bc_file_ultoa((unsigned long long) d, buf);
- bc_file_puts(f, bc_flush_none, buf);
+ bc_file_puts(f, bc_flush_none, s);
}
- }
-#endif // BC_DEBUG_CODE
- else {
-
- unsigned long long ull;
-
- // These are the ones that it expects from here. Fortunately, all of
- // these are unsigned types, so they can use the same code, more or
- // less.
- assert((c == 'l' || c == 'z') && percent[2] == 'u');
-
- if (c == 'z') ull = (unsigned long long) va_arg(args, size_t);
- else ull = (unsigned long long) va_arg(args, unsigned long);
-
- // Either print 0 or translate and print.
- if (!ull) bc_file_putchar(f, bc_flush_none, '0');
- else {
- bc_file_ultoa(ull, buf);
- bc_file_puts(f, bc_flush_none, buf);
+#if BC_DEBUG
+ // We only print signed integers in debug code.
+ else if (c == 'd')
+ {
+ int d = va_arg(args, int);
+
+ // Take care of negative. Let's not worry about overflow.
+ if (d < 0)
+ {
+ bc_file_putchar(f, bc_flush_none, '-');
+ d = -d;
+ }
+
+ // Either print 0 or translate and print.
+ if (!d) bc_file_putchar(f, bc_flush_none, '0');
+ else
+ {
+ bc_file_ultoa((unsigned long long) d, buf);
+ bc_file_puts(f, bc_flush_none, buf);
+ }
}
+#endif // BC_DEBUG
+ else
+ {
+ unsigned long long ull;
+
+ // These are the ones that it expects from here. Fortunately,
+ // all of these are unsigned types, so they can use the same
+ // code, more or less.
+ assert((c == 'l' || c == 'z') && percent[2] == 'u');
+
+ if (c == 'z') ull = (unsigned long long) va_arg(args, size_t);
+ else ull = (unsigned long long) va_arg(args, unsigned long);
+
+ // Either print 0 or translate and print.
+ if (!ull) bc_file_putchar(f, bc_flush_none, '0');
+ else
+ {
+ bc_file_ultoa(ull, buf);
+ bc_file_puts(f, bc_flush_none, buf);
+ }
+ }
+
+ // Increment to the next spot after the specifier.
+ ptr = percent + 2 + (c == 'l' || c == 'z');
}
- // Increment to the next spot after the specifier.
- ptr = percent + 2 + (c == 'l' || c == 'z');
+ // If we get here, there are no more percent signs, so we just output
+ // whatever is left.
+ if (ptr[0]) bc_file_puts(f, bc_flush_none, ptr);
}
- // If we get here, there are no more percent signs, so we just output
- // whatever is left.
- if (ptr[0]) bc_file_puts(f, bc_flush_none, ptr);
+#endif // BC_ENABLE_LINE_LIB
}
-void bc_file_puts(BcFile *restrict f, BcFlushType type, const char *str) {
+void
+bc_file_puts(BcFile* restrict f, BcFlushType type, const char* str)
+{
+#if BC_ENABLE_LINE_LIB
+ // This is used because of flushing issues with using bc_file_write() when
+ // bc is using a line library. It's also using printf() because puts()
+ // writes a newline.
+ bc_file_printf(f, "%s", str);
+#else // BC_ENABLE_LINE_LIB
bc_file_write(f, type, str, strlen(str));
+#endif // BC_ENABLE_LINE_LIB
}
-void bc_file_putchar(BcFile *restrict f, BcFlushType type, uchar c) {
+void
+bc_file_putchar(BcFile* restrict f, BcFlushType type, uchar c)
+{
+ sig_atomic_t lock;
+
+ BC_SIG_TRYLOCK(lock);
+
+#if BC_ENABLE_LINE_LIB
+
+ if (BC_ERR(fputc(c, f->f) == EOF))
+ {
+ // This is here to prevent a stack overflow from unbounded recursion.
+ if (f->f == stderr) exit(BC_STATUS_ERROR_FATAL);
+
+ bc_vm_fatalError(BC_ERR_FATAL_IO_ERR);
+ }
+
+#else // BC_ENABLE_LINE_LIB
if (f->len == f->cap) bc_file_flush(f, type);
@@ -293,10 +429,26 @@ void bc_file_putchar(BcFile *restrict f, BcFlushType type, uchar c) {
f->buf[f->len] = (char) c;
f->len += 1;
+
+#endif // BC_ENABLE_LINE_LIB
+
+ BC_SIG_TRYUNLOCK(lock);
+}
+
+#if BC_ENABLE_LINE_LIB
+
+void
+bc_file_init(BcFile* f, FILE* file)
+{
+ BC_SIG_ASSERT_LOCKED;
+ f->f = file;
}
-void bc_file_init(BcFile *f, int fd, char *buf, size_t cap) {
+#else // BC_ENABLE_LINE_LIB
+void
+bc_file_init(BcFile* f, int fd, char* buf, size_t cap)
+{
BC_SIG_ASSERT_LOCKED;
f->fd = fd;
@@ -305,7 +457,11 @@ void bc_file_init(BcFile *f, int fd, char *buf, size_t cap) {
f->cap = cap;
}
-void bc_file_free(BcFile *f) {
+#endif // BC_ENABLE_LINE_LIB
+
+void
+bc_file_free(BcFile* f)
+{
BC_SIG_ASSERT_LOCKED;
bc_file_flush(f, bc_flush_none);
}