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 for processing command-line arguments. 33 * 34 */ 35 36 #include <assert.h> 37 #include <ctype.h> 38 #include <stdbool.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #ifndef _WIN32 43 #include <unistd.h> 44 #endif // _WIN32 45 46 #include <vector.h> 47 #include <read.h> 48 #include <args.h> 49 #include <opt.h> 50 51 /** 52 * Adds @a str to the list of expressions to execute later. 53 * @param str The string to add to the list of expressions. 54 */ 55 static void bc_args_exprs(const char *str) { 56 BC_SIG_ASSERT_LOCKED; 57 if (vm.exprs.v == NULL) bc_vec_init(&vm.exprs, sizeof(uchar), BC_DTOR_NONE); 58 bc_vec_concat(&vm.exprs, str); 59 bc_vec_concat(&vm.exprs, "\n"); 60 } 61 62 /** 63 * Adds the contents of @a file to the list of expressions to execute later. 64 * @param file The name of the file whose contents should be added to the list 65 * of expressions to execute. 66 */ 67 static void bc_args_file(const char *file) { 68 69 char *buf; 70 71 BC_SIG_ASSERT_LOCKED; 72 73 vm.file = file; 74 75 buf = bc_read_file(file); 76 77 assert(buf != NULL); 78 79 bc_args_exprs(buf); 80 free(buf); 81 } 82 83 #if BC_ENABLED 84 85 /** 86 * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it 87 * throws a fatal error. 88 * @param keyword The keyword to redefine. 89 */ 90 static void bc_args_redefine(const char *keyword) { 91 92 size_t i; 93 94 BC_SIG_ASSERT_LOCKED; 95 96 for (i = 0; i < bc_lex_kws_len; ++i) { 97 98 const BcLexKeyword *kw = bc_lex_kws + i; 99 100 if (!strcmp(keyword, kw->name)) { 101 102 if (BC_LEX_KW_POSIX(kw)) break; 103 104 vm.redefined_kws[i] = true; 105 106 return; 107 } 108 } 109 110 bc_error(BC_ERR_FATAL_ARG, 0, keyword); 111 } 112 113 #endif // BC_ENABLED 114 115 void bc_args(int argc, char *argv[], bool exit_exprs) { 116 117 int c; 118 size_t i; 119 bool do_exit = false, version = false; 120 BcOpt opts; 121 122 BC_SIG_ASSERT_LOCKED; 123 124 bc_opt_init(&opts, argv); 125 126 // This loop should look familiar to anyone who has used getopt() or 127 // getopt_long() in C. 128 while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) { 129 130 switch (c) { 131 132 case 'e': 133 { 134 // Barf if not allowed. 135 if (vm.no_exprs) 136 bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)"); 137 138 // Add the expressions and set exit. 139 bc_args_exprs(opts.optarg); 140 vm.exit_exprs = (exit_exprs || vm.exit_exprs); 141 142 break; 143 } 144 145 case 'f': 146 { 147 // Figure out if exiting on expressions is disabled. 148 if (!strcmp(opts.optarg, "-")) vm.no_exprs = true; 149 else { 150 151 // Barf if not allowed. 152 if (vm.no_exprs) 153 bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)"); 154 155 // Add the expressions and set exit. 156 bc_args_file(opts.optarg); 157 vm.exit_exprs = (exit_exprs || vm.exit_exprs); 158 } 159 160 break; 161 } 162 163 case 'h': 164 { 165 bc_vm_info(vm.help); 166 do_exit = true; 167 break; 168 } 169 170 case 'i': 171 { 172 vm.flags |= BC_FLAG_I; 173 break; 174 } 175 176 case 'z': 177 { 178 vm.flags |= BC_FLAG_Z; 179 break; 180 } 181 182 case 'L': 183 { 184 vm.line_len = 0; 185 break; 186 } 187 188 case 'P': 189 { 190 vm.flags &= ~(BC_FLAG_P); 191 break; 192 } 193 194 case 'R': 195 { 196 vm.flags &= ~(BC_FLAG_R); 197 break; 198 } 199 200 #if BC_ENABLED 201 case 'g': 202 { 203 assert(BC_IS_BC); 204 vm.flags |= BC_FLAG_G; 205 break; 206 } 207 208 case 'l': 209 { 210 assert(BC_IS_BC); 211 vm.flags |= BC_FLAG_L; 212 break; 213 } 214 215 case 'q': 216 { 217 assert(BC_IS_BC); 218 vm.flags &= ~(BC_FLAG_Q); 219 break; 220 } 221 222 case 'r': 223 { 224 bc_args_redefine(opts.optarg); 225 break; 226 } 227 228 case 's': 229 { 230 assert(BC_IS_BC); 231 vm.flags |= BC_FLAG_S; 232 break; 233 } 234 235 case 'w': 236 { 237 assert(BC_IS_BC); 238 vm.flags |= BC_FLAG_W; 239 break; 240 } 241 #endif // BC_ENABLED 242 243 case 'V': 244 case 'v': 245 { 246 do_exit = version = true; 247 break; 248 } 249 250 #if DC_ENABLED 251 case 'x': 252 { 253 assert(BC_IS_DC); 254 vm.flags |= DC_FLAG_X; 255 break; 256 } 257 #endif // DC_ENABLED 258 259 #ifndef NDEBUG 260 // We shouldn't get here because bc_opt_error()/bc_error() should 261 // longjmp() out. 262 case '?': 263 case ':': 264 default: 265 { 266 BC_UNREACHABLE 267 abort(); 268 } 269 #endif // NDEBUG 270 } 271 } 272 273 if (version) bc_vm_info(NULL); 274 if (do_exit) { 275 vm.status = (sig_atomic_t) BC_STATUS_QUIT; 276 BC_JMP; 277 } 278 279 // We do not print the banner if expressions are used or dc is used. 280 if (!BC_IS_BC || vm.exprs.len > 1) vm.flags &= ~(BC_FLAG_Q); 281 282 // We need to make sure the files list is initialized. We don't want to 283 // initialize it if there are no files because it's just a waste of memory. 284 if (opts.optind < (size_t) argc && vm.files.v == NULL) 285 bc_vec_init(&vm.files, sizeof(char*), BC_DTOR_NONE); 286 287 // Add all the files to the vector. 288 for (i = opts.optind; i < (size_t) argc; ++i) 289 bc_vec_push(&vm.files, argv + i); 290 } 291