1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2021 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 static int bc_read_open(const char* path, int mode) { 57 58 int fd; 59 60 #ifndef _WIN32 61 fd = open(path, mode); 62 #else // _WIN32 63 fd = -1; 64 open(&fd, path, mode); 65 #endif 66 67 return fd; 68 } 69 70 static bool bc_read_binary(const char *buf, size_t size) { 71 72 size_t i; 73 74 for (i = 0; i < size; ++i) { 75 if (BC_ERR(BC_READ_BIN_CHAR(buf[i]))) return true; 76 } 77 78 return false; 79 } 80 81 bool bc_read_buf(BcVec *vec, char *buf, size_t *buf_len) { 82 83 char *nl; 84 85 if (!*buf_len) return false; 86 87 nl = strchr(buf, '\n'); 88 89 if (nl != NULL) { 90 91 size_t nllen = (size_t) ((nl + 1) - buf); 92 93 nllen = *buf_len >= nllen ? nllen : *buf_len; 94 95 bc_vec_npush(vec, nllen, buf); 96 *buf_len -= nllen; 97 memmove(buf, nl + 1, *buf_len + 1); 98 99 return true; 100 } 101 102 bc_vec_npush(vec, *buf_len, buf); 103 *buf_len = 0; 104 105 return false; 106 } 107 108 BcStatus bc_read_chars(BcVec *vec, const char *prompt) { 109 110 bool done = false; 111 112 assert(vec != NULL && vec->size == sizeof(char)); 113 114 BC_SIG_ASSERT_NOT_LOCKED; 115 116 bc_vec_popAll(vec); 117 118 #if BC_ENABLE_PROMPT 119 if (BC_USE_PROMPT) { 120 bc_file_puts(&vm.fout, bc_flush_none, prompt); 121 bc_file_flush(&vm.fout, bc_flush_none); 122 } 123 #endif // BC_ENABLE_PROMPT 124 125 if (bc_read_buf(vec, vm.buf, &vm.buf_len)) { 126 bc_vec_pushByte(vec, '\0'); 127 return BC_STATUS_SUCCESS; 128 } 129 130 while (!done) { 131 132 ssize_t r; 133 134 BC_SIG_LOCK; 135 136 r = read(STDIN_FILENO, vm.buf + vm.buf_len, 137 BC_VM_STDIN_BUF_SIZE - vm.buf_len); 138 139 if (BC_UNLIKELY(r < 0)) { 140 141 if (errno == EINTR) { 142 143 if (vm.status == (sig_atomic_t) BC_STATUS_QUIT) { 144 BC_SIG_UNLOCK; 145 return BC_STATUS_QUIT; 146 } 147 148 assert(vm.sig); 149 150 vm.status = (sig_atomic_t) BC_STATUS_SUCCESS; 151 #if BC_ENABLE_PROMPT 152 if (BC_USE_PROMPT) 153 bc_file_puts(&vm.fout, bc_flush_none, prompt); 154 #endif // BC_ENABLE_PROMPT 155 bc_file_flush(&vm.fout, bc_flush_none); 156 157 BC_SIG_UNLOCK; 158 159 continue; 160 } 161 162 BC_SIG_UNLOCK; 163 164 bc_vm_fatalError(BC_ERR_FATAL_IO_ERR); 165 } 166 167 BC_SIG_UNLOCK; 168 169 if (r == 0) { 170 bc_vec_pushByte(vec, '\0'); 171 return BC_STATUS_EOF; 172 } 173 174 vm.buf_len += (size_t) r; 175 vm.buf[vm.buf_len] = '\0'; 176 177 done = bc_read_buf(vec, vm.buf, &vm.buf_len); 178 } 179 180 bc_vec_pushByte(vec, '\0'); 181 182 return BC_STATUS_SUCCESS; 183 } 184 185 BcStatus bc_read_line(BcVec *vec, const char *prompt) { 186 187 BcStatus s; 188 189 #if BC_ENABLE_HISTORY 190 if (BC_TTY && !vm.history.badTerm) 191 s = bc_history_line(&vm.history, vec, prompt); 192 else s = bc_read_chars(vec, prompt); 193 #else // BC_ENABLE_HISTORY 194 s = bc_read_chars(vec, prompt); 195 #endif // BC_ENABLE_HISTORY 196 197 if (BC_ERR(bc_read_binary(vec->v, vec->len - 1))) 198 bc_vm_verr(BC_ERR_FATAL_BIN_FILE, bc_program_stdin_name); 199 200 return s; 201 } 202 203 void bc_read_file(const char *path, char **buf) { 204 205 BcErr e = BC_ERR_FATAL_IO_ERR; 206 size_t size, r; 207 struct stat pstat; 208 int fd; 209 210 BC_SIG_ASSERT_LOCKED; 211 212 assert(path != NULL); 213 214 fd = bc_read_open(path, O_RDONLY); 215 216 if (BC_ERR(fd < 0)) bc_vm_verr(BC_ERR_FATAL_FILE_ERR, path); 217 if (BC_ERR(fstat(fd, &pstat) == -1)) goto malloc_err; 218 219 if (BC_ERR(S_ISDIR(pstat.st_mode))) { 220 e = BC_ERR_FATAL_PATH_DIR; 221 goto malloc_err; 222 } 223 224 size = (size_t) pstat.st_size; 225 *buf = bc_vm_malloc(size + 1); 226 227 r = (size_t) read(fd, *buf, size); 228 if (BC_ERR(r != size)) goto read_err; 229 230 (*buf)[size] = '\0'; 231 232 if (BC_ERR(bc_read_binary(*buf, size))) { 233 e = BC_ERR_FATAL_BIN_FILE; 234 goto read_err; 235 } 236 237 close(fd); 238 239 return; 240 241 read_err: 242 free(*buf); 243 malloc_err: 244 close(fd); 245 bc_vm_verr(e, path); 246 } 247