1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * 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. 17 * 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. 29 * 30 * ***************************************************************************** 31 * 32 * Code to handle special I/O for bc. 33 * 34 */ 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include <signal.h> 43 44 #include <fcntl.h> 45 #include <sys/stat.h> 46 47 #ifndef _WIN32 48 #include <unistd.h> 49 #endif // _WIN32 50 51 #include <read.h> 52 #include <history.h> 53 #include <program.h> 54 #include <vm.h> 55 56 /** 57 * A portability file open function. This is copied to gen/strgen.c. Make sure 58 * to update that if this changes. 59 * @param path The path to the file to open. 60 * @param mode The mode to open in. 61 */ 62 static int 63 bc_read_open(const char* path, int mode) 64 { 65 int fd; 66 67 #ifndef _WIN32 68 fd = open(path, mode); 69 #else // _WIN32 70 fd = -1; 71 open(&fd, path, mode); 72 #endif 73 74 return fd; 75 } 76 77 /** 78 * Returns true if the buffer data is non-text. 79 * @param buf The buffer to test. 80 * @param size The size of the buffer. 81 */ 82 static bool 83 bc_read_binary(const char* buf, size_t size) 84 { 85 size_t i; 86 87 for (i = 0; i < size; ++i) 88 { 89 if (BC_ERR(BC_READ_BIN_CHAR(buf[i]))) return true; 90 } 91 92 return false; 93 } 94 95 bool 96 bc_read_buf(BcVec* vec, char* buf, size_t* buf_len) 97 { 98 char* nl; 99 100 // If nothing there, return. 101 if (!*buf_len) return false; 102 103 // Find the newline. 104 nl = strchr(buf, '\n'); 105 106 // If a newline exists... 107 if (nl != NULL) 108 { 109 // Get the size of the data up to, and including, the newline. 110 size_t nllen = (size_t) ((nl + 1) - buf); 111 112 nllen = *buf_len >= nllen ? nllen : *buf_len; 113 114 // Move data into the vector, and move the rest of the data in the 115 // buffer up. 116 bc_vec_npush(vec, nllen, buf); 117 *buf_len -= nllen; 118 // NOLINTNEXTLINE 119 memmove(buf, nl + 1, *buf_len + 1); 120 121 return true; 122 } 123 124 // Just put the data into the vector. 125 bc_vec_npush(vec, *buf_len, buf); 126 *buf_len = 0; 127 128 return false; 129 } 130 131 BcStatus 132 bc_read_chars(BcVec* vec, const char* prompt) 133 { 134 bool done = false; 135 136 assert(vec != NULL && vec->size == sizeof(char)); 137 138 BC_SIG_ASSERT_NOT_LOCKED; 139 140 // Clear the vector. 141 bc_vec_popAll(vec); 142 143 // Handle the prompt, if desired. 144 if (BC_PROMPT) 145 { 146 bc_file_puts(&vm->fout, bc_flush_none, prompt); 147 bc_file_flush(&vm->fout, bc_flush_none); 148 } 149 150 // Try reading from the buffer, and if successful, just return. 151 if (bc_read_buf(vec, vm->buf, &vm->buf_len)) 152 { 153 bc_vec_pushByte(vec, '\0'); 154 return BC_STATUS_SUCCESS; 155 } 156 157 // Loop until we have something. 158 while (!done) 159 { 160 ssize_t r; 161 162 BC_SIG_LOCK; 163 164 // Read data from stdin. 165 r = read(STDIN_FILENO, vm->buf + vm->buf_len, 166 BC_VM_STDIN_BUF_SIZE - vm->buf_len); 167 168 // If there was an error... 169 if (BC_UNLIKELY(r < 0)) 170 { 171 // If interupted... 172 if (errno == EINTR) 173 { 174 int sig; 175 176 // Jump out if we are supposed to quit, which certain signals 177 // will require. 178 if (vm->status == (sig_atomic_t) BC_STATUS_QUIT) BC_JMP; 179 180 assert(vm->sig != 0); 181 182 sig = (int) vm->sig; 183 184 // Clear the signal and status. 185 vm->sig = 0; 186 vm->status = (sig_atomic_t) BC_STATUS_SUCCESS; 187 188 #ifndef _WIN32 189 // We don't want to print anything on a SIGWINCH. 190 if (sig != SIGWINCH) 191 #endif // _WIN32 192 { 193 // Print the ready message and prompt again. 194 bc_file_puts(&vm->fout, bc_flush_none, 195 bc_program_ready_msg); 196 if (BC_PROMPT) 197 { 198 bc_file_puts(&vm->fout, bc_flush_none, prompt); 199 } 200 bc_file_flush(&vm->fout, bc_flush_none); 201 } 202 203 BC_SIG_UNLOCK; 204 205 continue; 206 } 207 208 BC_SIG_UNLOCK; 209 210 // If we get here, it's bad. Barf. 211 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 212 } 213 214 BC_SIG_UNLOCK; 215 216 // If we read nothing, make sure to terminate the string and return EOF. 217 if (r == 0) 218 { 219 bc_vec_pushByte(vec, '\0'); 220 return BC_STATUS_EOF; 221 } 222 223 BC_SIG_LOCK; 224 225 // Add to the buffer. 226 vm->buf_len += (size_t) r; 227 vm->buf[vm->buf_len] = '\0'; 228 229 // Read from the buffer. 230 done = bc_read_buf(vec, vm->buf, &vm->buf_len); 231 232 BC_SIG_UNLOCK; 233 } 234 235 // Terminate the string. 236 bc_vec_pushByte(vec, '\0'); 237 238 return BC_STATUS_SUCCESS; 239 } 240 241 BcStatus 242 bc_read_line(BcVec* vec, const char* prompt) 243 { 244 BcStatus s; 245 246 #if BC_ENABLE_HISTORY 247 // Get a line from either history or manual reading. 248 if (BC_TTY && !vm->history.badTerm) 249 { 250 s = bc_history_line(&vm->history, vec, prompt); 251 } 252 else s = bc_read_chars(vec, prompt); 253 #else // BC_ENABLE_HISTORY 254 s = bc_read_chars(vec, prompt); 255 #endif // BC_ENABLE_HISTORY 256 257 if (BC_ERR(bc_read_binary(vec->v, vec->len - 1))) 258 { 259 bc_verr(BC_ERR_FATAL_BIN_FILE, bc_program_stdin_name); 260 } 261 262 return s; 263 } 264 265 char* 266 bc_read_file(const char* path) 267 { 268 BcErr e = BC_ERR_FATAL_IO_ERR; 269 size_t size, to_read; 270 struct stat pstat; 271 int fd; 272 char* buf; 273 char* buf2; 274 275 // This has been copied to gen/strgen.c. Make sure to change that if this 276 // changes. 277 278 BC_SIG_ASSERT_LOCKED; 279 280 assert(path != NULL); 281 282 #if BC_DEBUG 283 // Need this to quiet MSan. 284 // NOLINTNEXTLINE 285 memset(&pstat, 0, sizeof(struct stat)); 286 #endif // BC_DEBUG 287 288 fd = bc_read_open(path, O_RDONLY); 289 290 // If we can't read a file, we just barf. 291 if (BC_ERR(fd < 0)) bc_verr(BC_ERR_FATAL_FILE_ERR, path); 292 293 // The reason we call fstat is to eliminate TOCTOU race conditions. This 294 // way, we have an open file, so it's not going anywhere. 295 if (BC_ERR(fstat(fd, &pstat) == -1)) goto malloc_err; 296 297 // Make sure it's not a directory. 298 if (BC_ERR(S_ISDIR(pstat.st_mode))) 299 { 300 e = BC_ERR_FATAL_PATH_DIR; 301 goto malloc_err; 302 } 303 304 // Get the size of the file and allocate that much. 305 size = (size_t) pstat.st_size; 306 buf = bc_vm_malloc(size + 1); 307 buf2 = buf; 308 to_read = size; 309 310 do 311 { 312 // Read the file. We just bail if a signal interrupts. This is so that 313 // users can interrupt the reading of big files if they want. 314 ssize_t r = read(fd, buf2, to_read); 315 if (BC_ERR(r < 0)) goto read_err; 316 to_read -= (size_t) r; 317 buf2 += (size_t) r; 318 } 319 while (to_read); 320 321 // Got to have a nul byte. 322 buf[size] = '\0'; 323 324 if (BC_ERR(bc_read_binary(buf, size))) 325 { 326 e = BC_ERR_FATAL_BIN_FILE; 327 goto read_err; 328 } 329 330 close(fd); 331 332 return buf; 333 334 read_err: 335 free(buf); 336 malloc_err: 337 close(fd); 338 bc_verr(e, path); 339 return NULL; 340 } 341