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 to handle special I/O for bc. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <ctype.h> 38252884aeSStefan Eßer #include <errno.h> 39252884aeSStefan Eßer #include <stdlib.h> 40252884aeSStefan Eßer #include <string.h> 41252884aeSStefan Eßer 42252884aeSStefan Eßer #include <signal.h> 43252884aeSStefan Eßer 44252884aeSStefan Eßer #include <fcntl.h> 45252884aeSStefan Eßer #include <sys/stat.h> 467e5c51e5SStefan Eßer 477e5c51e5SStefan Eßer #ifndef _WIN32 48252884aeSStefan Eßer #include <unistd.h> 497e5c51e5SStefan Eßer #endif // _WIN32 50252884aeSStefan Eßer 51252884aeSStefan Eßer #include <read.h> 52252884aeSStefan Eßer #include <history.h> 53252884aeSStefan Eßer #include <program.h> 54252884aeSStefan Eßer #include <vm.h> 55252884aeSStefan Eßer 5644d4804dSStefan Eßer /** 5778bc019dSStefan Eßer * A portability file open function. This is copied to gen/strgen.c. Make sure 5878bc019dSStefan Eßer * to update that if this changes. 5944d4804dSStefan Eßer * @param path The path to the file to open. 6044d4804dSStefan Eßer * @param mode The mode to open in. 6144d4804dSStefan Eßer */ 6278bc019dSStefan Eßer static int 6378bc019dSStefan Eßer bc_read_open(const char* path, int mode) 6478bc019dSStefan Eßer { 657e5c51e5SStefan Eßer int fd; 667e5c51e5SStefan Eßer 677e5c51e5SStefan Eßer #ifndef _WIN32 687e5c51e5SStefan Eßer fd = open(path, mode); 697e5c51e5SStefan Eßer #else // _WIN32 707e5c51e5SStefan Eßer fd = -1; 717e5c51e5SStefan Eßer open(&fd, path, mode); 727e5c51e5SStefan Eßer #endif 737e5c51e5SStefan Eßer 747e5c51e5SStefan Eßer return fd; 757e5c51e5SStefan Eßer } 767e5c51e5SStefan Eßer 7744d4804dSStefan Eßer /** 7844d4804dSStefan Eßer * Returns true if the buffer data is non-text. 7944d4804dSStefan Eßer * @param buf The buffer to test. 8044d4804dSStefan Eßer * @param size The size of the buffer. 8144d4804dSStefan Eßer */ 8278bc019dSStefan Eßer static bool 8378bc019dSStefan Eßer bc_read_binary(const char* buf, size_t size) 8478bc019dSStefan Eßer { 85252884aeSStefan Eßer size_t i; 86252884aeSStefan Eßer 8778bc019dSStefan Eßer for (i = 0; i < size; ++i) 8878bc019dSStefan Eßer { 89252884aeSStefan Eßer if (BC_ERR(BC_READ_BIN_CHAR(buf[i]))) return true; 90252884aeSStefan Eßer } 91252884aeSStefan Eßer 92252884aeSStefan Eßer return false; 93252884aeSStefan Eßer } 94252884aeSStefan Eßer 9578bc019dSStefan Eßer bool 9678bc019dSStefan Eßer bc_read_buf(BcVec* vec, char* buf, size_t* buf_len) 9778bc019dSStefan Eßer { 98252884aeSStefan Eßer char* nl; 99252884aeSStefan Eßer 10044d4804dSStefan Eßer // If nothing there, return. 1013aa99676SStefan Eßer if (!*buf_len) return false; 102252884aeSStefan Eßer 10344d4804dSStefan Eßer // Find the newline. 1043aa99676SStefan Eßer nl = strchr(buf, '\n'); 105252884aeSStefan Eßer 10644d4804dSStefan Eßer // If a newline exists... 10778bc019dSStefan Eßer if (nl != NULL) 10878bc019dSStefan Eßer { 10944d4804dSStefan Eßer // Get the size of the data up to, and including, the newline. 1103aa99676SStefan Eßer size_t nllen = (size_t) ((nl + 1) - buf); 111252884aeSStefan Eßer 1123aa99676SStefan Eßer nllen = *buf_len >= nllen ? nllen : *buf_len; 113252884aeSStefan Eßer 11444d4804dSStefan Eßer // Move data into the vector, and move the rest of the data in the 11544d4804dSStefan Eßer // buffer up. 1163aa99676SStefan Eßer bc_vec_npush(vec, nllen, buf); 1173aa99676SStefan Eßer *buf_len -= nllen; 11878bc019dSStefan Eßer // NOLINTNEXTLINE 1193aa99676SStefan Eßer memmove(buf, nl + 1, *buf_len + 1); 120252884aeSStefan Eßer 121252884aeSStefan Eßer return true; 122252884aeSStefan Eßer } 123252884aeSStefan Eßer 12444d4804dSStefan Eßer // Just put the data into the vector. 1253aa99676SStefan Eßer bc_vec_npush(vec, *buf_len, buf); 1263aa99676SStefan Eßer *buf_len = 0; 127252884aeSStefan Eßer 128252884aeSStefan Eßer return false; 129252884aeSStefan Eßer } 130252884aeSStefan Eßer 13178bc019dSStefan Eßer BcStatus 13278bc019dSStefan Eßer bc_read_chars(BcVec* vec, const char* prompt) 13378bc019dSStefan Eßer { 134252884aeSStefan Eßer bool done = false; 135252884aeSStefan Eßer 136252884aeSStefan Eßer assert(vec != NULL && vec->size == sizeof(char)); 137252884aeSStefan Eßer 138252884aeSStefan Eßer BC_SIG_ASSERT_NOT_LOCKED; 139252884aeSStefan Eßer 14044d4804dSStefan Eßer // Clear the vector. 14110328f8bSStefan Eßer bc_vec_popAll(vec); 142252884aeSStefan Eßer 14344d4804dSStefan Eßer // Handle the prompt, if desired. 14478bc019dSStefan Eßer if (BC_PROMPT) 14578bc019dSStefan Eßer { 146d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, prompt); 147d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_none); 148252884aeSStefan Eßer } 149252884aeSStefan Eßer 15044d4804dSStefan Eßer // Try reading from the buffer, and if successful, just return. 151d101cdd6SStefan Eßer if (bc_read_buf(vec, vm->buf, &vm->buf_len)) 15278bc019dSStefan Eßer { 153252884aeSStefan Eßer bc_vec_pushByte(vec, '\0'); 154252884aeSStefan Eßer return BC_STATUS_SUCCESS; 155252884aeSStefan Eßer } 156252884aeSStefan Eßer 15744d4804dSStefan Eßer // Loop until we have something. 15878bc019dSStefan Eßer while (!done) 15978bc019dSStefan Eßer { 160252884aeSStefan Eßer ssize_t r; 161252884aeSStefan Eßer 162252884aeSStefan Eßer BC_SIG_LOCK; 163252884aeSStefan Eßer 16444d4804dSStefan Eßer // Read data from stdin. 165d101cdd6SStefan Eßer r = read(STDIN_FILENO, vm->buf + vm->buf_len, 166d101cdd6SStefan Eßer BC_VM_STDIN_BUF_SIZE - vm->buf_len); 167252884aeSStefan Eßer 16844d4804dSStefan Eßer // If there was an error... 16978bc019dSStefan Eßer if (BC_UNLIKELY(r < 0)) 17078bc019dSStefan Eßer { 17144d4804dSStefan Eßer // If interupted... 17278bc019dSStefan Eßer if (errno == EINTR) 17378bc019dSStefan Eßer { 174d101cdd6SStefan Eßer int sig; 175d101cdd6SStefan Eßer 17644d4804dSStefan Eßer // Jump out if we are supposed to quit, which certain signals 17744d4804dSStefan Eßer // will require. 178d101cdd6SStefan Eßer if (vm->status == (sig_atomic_t) BC_STATUS_QUIT) BC_JMP; 179252884aeSStefan Eßer 180d101cdd6SStefan Eßer assert(vm->sig != 0); 181d101cdd6SStefan Eßer 182d101cdd6SStefan Eßer sig = (int) vm->sig; 183252884aeSStefan Eßer 18444d4804dSStefan Eßer // Clear the signal and status. 185d101cdd6SStefan Eßer vm->sig = 0; 186d101cdd6SStefan Eßer vm->status = (sig_atomic_t) BC_STATUS_SUCCESS; 18744d4804dSStefan Eßer 188d101cdd6SStefan Eßer #ifndef _WIN32 189d101cdd6SStefan Eßer // We don't want to print anything on a SIGWINCH. 190d101cdd6SStefan Eßer if (sig != SIGWINCH) 191d101cdd6SStefan Eßer #endif // _WIN32 192d101cdd6SStefan Eßer { 19344d4804dSStefan Eßer // Print the ready message and prompt again. 194d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, 195d101cdd6SStefan Eßer bc_program_ready_msg); 196d101cdd6SStefan Eßer if (BC_PROMPT) 197d101cdd6SStefan Eßer { 198d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, prompt); 199d101cdd6SStefan Eßer } 200d101cdd6SStefan Eßer bc_file_flush(&vm->fout, bc_flush_none); 201d101cdd6SStefan Eßer } 202252884aeSStefan Eßer 203252884aeSStefan Eßer BC_SIG_UNLOCK; 204252884aeSStefan Eßer 205252884aeSStefan Eßer continue; 206252884aeSStefan Eßer } 207252884aeSStefan Eßer 208252884aeSStefan Eßer BC_SIG_UNLOCK; 209252884aeSStefan Eßer 21044d4804dSStefan Eßer // If we get here, it's bad. Barf. 21110328f8bSStefan Eßer bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 212252884aeSStefan Eßer } 213252884aeSStefan Eßer 214252884aeSStefan Eßer BC_SIG_UNLOCK; 215252884aeSStefan Eßer 21644d4804dSStefan Eßer // If we read nothing, make sure to terminate the string and return EOF. 21778bc019dSStefan Eßer if (r == 0) 21878bc019dSStefan Eßer { 219252884aeSStefan Eßer bc_vec_pushByte(vec, '\0'); 220252884aeSStefan Eßer return BC_STATUS_EOF; 221252884aeSStefan Eßer } 222252884aeSStefan Eßer 22310041e99SStefan Eßer BC_SIG_LOCK; 22410041e99SStefan Eßer 22544d4804dSStefan Eßer // Add to the buffer. 226d101cdd6SStefan Eßer vm->buf_len += (size_t) r; 227d101cdd6SStefan Eßer vm->buf[vm->buf_len] = '\0'; 228252884aeSStefan Eßer 22944d4804dSStefan Eßer // Read from the buffer. 230d101cdd6SStefan Eßer done = bc_read_buf(vec, vm->buf, &vm->buf_len); 23110041e99SStefan Eßer 23210041e99SStefan Eßer BC_SIG_UNLOCK; 233252884aeSStefan Eßer } 234252884aeSStefan Eßer 23544d4804dSStefan Eßer // Terminate the string. 236252884aeSStefan Eßer bc_vec_pushByte(vec, '\0'); 237252884aeSStefan Eßer 238252884aeSStefan Eßer return BC_STATUS_SUCCESS; 239252884aeSStefan Eßer } 240252884aeSStefan Eßer 24178bc019dSStefan Eßer BcStatus 24278bc019dSStefan Eßer bc_read_line(BcVec* vec, const char* prompt) 24378bc019dSStefan Eßer { 244252884aeSStefan Eßer BcStatus s; 245252884aeSStefan Eßer 246252884aeSStefan Eßer #if BC_ENABLE_HISTORY 24744d4804dSStefan Eßer // Get a line from either history or manual reading. 248d101cdd6SStefan Eßer if (BC_TTY && !vm->history.badTerm) 24978bc019dSStefan Eßer { 250d101cdd6SStefan Eßer s = bc_history_line(&vm->history, vec, prompt); 25178bc019dSStefan Eßer } 252252884aeSStefan Eßer else s = bc_read_chars(vec, prompt); 253252884aeSStefan Eßer #else // BC_ENABLE_HISTORY 254252884aeSStefan Eßer s = bc_read_chars(vec, prompt); 255252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY 256252884aeSStefan Eßer 257252884aeSStefan Eßer if (BC_ERR(bc_read_binary(vec->v, vec->len - 1))) 25878bc019dSStefan Eßer { 25944d4804dSStefan Eßer bc_verr(BC_ERR_FATAL_BIN_FILE, bc_program_stdin_name); 26078bc019dSStefan Eßer } 261252884aeSStefan Eßer 262252884aeSStefan Eßer return s; 263252884aeSStefan Eßer } 264252884aeSStefan Eßer 26578bc019dSStefan Eßer char* 26678bc019dSStefan Eßer bc_read_file(const char* path) 26778bc019dSStefan Eßer { 26850696a6eSStefan Eßer BcErr e = BC_ERR_FATAL_IO_ERR; 269d43fa8efSStefan Eßer size_t size, to_read; 270252884aeSStefan Eßer struct stat pstat; 271252884aeSStefan Eßer int fd; 27244d4804dSStefan Eßer char* buf; 273d43fa8efSStefan Eßer char* buf2; 274252884aeSStefan Eßer 27578bc019dSStefan Eßer // This has been copied to gen/strgen.c. Make sure to change that if this 27678bc019dSStefan Eßer // changes. 27778bc019dSStefan Eßer 278252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 279252884aeSStefan Eßer 280252884aeSStefan Eßer assert(path != NULL); 281252884aeSStefan Eßer 282103d7cdfSStefan Eßer #if BC_DEBUG 28344d4804dSStefan Eßer // Need this to quiet MSan. 28478bc019dSStefan Eßer // NOLINTNEXTLINE 28544d4804dSStefan Eßer memset(&pstat, 0, sizeof(struct stat)); 286103d7cdfSStefan Eßer #endif // BC_DEBUG 28744d4804dSStefan Eßer 2887e5c51e5SStefan Eßer fd = bc_read_open(path, O_RDONLY); 2897e5c51e5SStefan Eßer 29044d4804dSStefan Eßer // If we can't read a file, we just barf. 29144d4804dSStefan Eßer if (BC_ERR(fd < 0)) bc_verr(BC_ERR_FATAL_FILE_ERR, path); 29244d4804dSStefan Eßer 29344d4804dSStefan Eßer // The reason we call fstat is to eliminate TOCTOU race conditions. This 29444d4804dSStefan Eßer // way, we have an open file, so it's not going anywhere. 295252884aeSStefan Eßer if (BC_ERR(fstat(fd, &pstat) == -1)) goto malloc_err; 296252884aeSStefan Eßer 29744d4804dSStefan Eßer // Make sure it's not a directory. 29878bc019dSStefan Eßer if (BC_ERR(S_ISDIR(pstat.st_mode))) 29978bc019dSStefan Eßer { 30050696a6eSStefan Eßer e = BC_ERR_FATAL_PATH_DIR; 301252884aeSStefan Eßer goto malloc_err; 302252884aeSStefan Eßer } 303252884aeSStefan Eßer 30444d4804dSStefan Eßer // Get the size of the file and allocate that much. 305252884aeSStefan Eßer size = (size_t) pstat.st_size; 30644d4804dSStefan Eßer buf = bc_vm_malloc(size + 1); 307d43fa8efSStefan Eßer buf2 = buf; 308d43fa8efSStefan Eßer to_read = size; 309252884aeSStefan Eßer 31078bc019dSStefan Eßer do 31178bc019dSStefan Eßer { 312d43fa8efSStefan Eßer // Read the file. We just bail if a signal interrupts. This is so that 313d43fa8efSStefan Eßer // users can interrupt the reading of big files if they want. 314d43fa8efSStefan Eßer ssize_t r = read(fd, buf2, to_read); 315d43fa8efSStefan Eßer if (BC_ERR(r < 0)) goto read_err; 316d43fa8efSStefan Eßer to_read -= (size_t) r; 317d43fa8efSStefan Eßer buf2 += (size_t) r; 31878bc019dSStefan Eßer } 31978bc019dSStefan Eßer while (to_read); 320252884aeSStefan Eßer 32144d4804dSStefan Eßer // Got to have a nul byte. 32244d4804dSStefan Eßer buf[size] = '\0'; 323252884aeSStefan Eßer 32478bc019dSStefan Eßer if (BC_ERR(bc_read_binary(buf, size))) 32578bc019dSStefan Eßer { 32650696a6eSStefan Eßer e = BC_ERR_FATAL_BIN_FILE; 327252884aeSStefan Eßer goto read_err; 328252884aeSStefan Eßer } 329252884aeSStefan Eßer 330252884aeSStefan Eßer close(fd); 331252884aeSStefan Eßer 33244d4804dSStefan Eßer return buf; 333252884aeSStefan Eßer 334252884aeSStefan Eßer read_err: 33544d4804dSStefan Eßer free(buf); 336252884aeSStefan Eßer malloc_err: 337252884aeSStefan Eßer close(fd); 33844d4804dSStefan Eßer bc_verr(e, path); 33944d4804dSStefan Eßer return NULL; 340252884aeSStefan Eßer } 341