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