1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2024 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 #include <num.h> 51 #include <vm.h> 52 53 /** 54 * Adds @a str to the list of expressions to execute later. 55 * @param str The string to add to the list of expressions. 56 */ 57 static void 58 bc_args_exprs(const char* str) 59 { 60 BC_SIG_ASSERT_LOCKED; 61 62 if (vm->exprs.v == NULL) 63 { 64 bc_vec_init(&vm->exprs, sizeof(uchar), BC_DTOR_NONE); 65 } 66 67 bc_vec_concat(&vm->exprs, str); 68 bc_vec_concat(&vm->exprs, "\n"); 69 } 70 71 /** 72 * Adds the contents of @a file to the list of expressions to execute later. 73 * @param file The name of the file whose contents should be added to the list 74 * of expressions to execute. 75 */ 76 static void 77 bc_args_file(const char* file) 78 { 79 char* buf; 80 81 BC_SIG_ASSERT_LOCKED; 82 83 vm->file = file; 84 85 buf = bc_read_file(file); 86 87 assert(buf != NULL); 88 89 bc_args_exprs(buf); 90 free(buf); 91 } 92 93 static BcBigDig 94 bc_args_builtin(const char* arg) 95 { 96 bool strvalid; 97 BcNum n; 98 BcBigDig res; 99 100 strvalid = bc_num_strValid(arg); 101 102 if (BC_ERR(!strvalid)) 103 { 104 bc_verr(BC_ERR_FATAL_ARG, arg); 105 } 106 107 bc_num_init(&n, 0); 108 109 bc_num_parse(&n, arg, 10); 110 111 res = bc_num_bigdig(&n); 112 113 bc_num_free(&n); 114 115 return res; 116 } 117 118 #if BC_ENABLED 119 120 /** 121 * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it 122 * throws a fatal error. 123 * @param keyword The keyword to redefine. 124 */ 125 static void 126 bc_args_redefine(const char* keyword) 127 { 128 size_t i; 129 130 BC_SIG_ASSERT_LOCKED; 131 132 for (i = 0; i < bc_lex_kws_len; ++i) 133 { 134 const BcLexKeyword* kw = bc_lex_kws + i; 135 136 if (!strcmp(keyword, kw->name)) 137 { 138 if (BC_LEX_KW_POSIX(kw)) break; 139 140 vm->redefined_kws[i] = true; 141 142 return; 143 } 144 } 145 146 bc_error(BC_ERR_FATAL_ARG, 0, keyword); 147 } 148 149 #endif // BC_ENABLED 150 151 void 152 bc_args(int argc, const char* argv[], bool exit_exprs, BcBigDig* scale, 153 BcBigDig* ibase, BcBigDig* obase) 154 { 155 int c; 156 size_t i; 157 bool do_exit = false, version = false; 158 BcOpt opts; 159 #if BC_ENABLE_EXTRA_MATH 160 const char* seed = NULL; 161 #endif // BC_ENABLE_EXTRA_MATH 162 163 BC_SIG_ASSERT_LOCKED; 164 165 bc_opt_init(&opts, argv); 166 167 // This loop should look familiar to anyone who has used getopt() or 168 // getopt_long() in C. 169 while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) 170 { 171 switch (c) 172 { 173 case 'c': 174 { 175 vm->flags |= BC_FLAG_DIGIT_CLAMP; 176 break; 177 } 178 179 case 'C': 180 { 181 vm->flags &= ~BC_FLAG_DIGIT_CLAMP; 182 break; 183 } 184 185 case 'e': 186 { 187 // Barf if not allowed. 188 if (vm->no_exprs) 189 { 190 bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)"); 191 } 192 193 // Add the expressions and set exit. 194 bc_args_exprs(opts.optarg); 195 vm->exit_exprs = (exit_exprs || vm->exit_exprs); 196 197 break; 198 } 199 200 case 'f': 201 { 202 // Figure out if exiting on expressions is disabled. 203 if (!strcmp(opts.optarg, "-")) vm->no_exprs = true; 204 else 205 { 206 // Barf if not allowed. 207 if (vm->no_exprs) 208 { 209 bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)"); 210 } 211 212 // Add the expressions and set exit. 213 bc_args_file(opts.optarg); 214 vm->exit_exprs = (exit_exprs || vm->exit_exprs); 215 } 216 217 break; 218 } 219 220 case 'h': 221 { 222 bc_vm_info(vm->help); 223 do_exit = true; 224 break; 225 } 226 227 case 'i': 228 { 229 vm->flags |= BC_FLAG_I; 230 break; 231 } 232 233 case 'I': 234 { 235 *ibase = bc_args_builtin(opts.optarg); 236 break; 237 } 238 239 case 'z': 240 { 241 vm->flags |= BC_FLAG_Z; 242 break; 243 } 244 245 case 'L': 246 { 247 vm->line_len = 0; 248 break; 249 } 250 251 case 'O': 252 { 253 *obase = bc_args_builtin(opts.optarg); 254 break; 255 } 256 257 case 'P': 258 { 259 vm->flags &= ~(BC_FLAG_P); 260 break; 261 } 262 263 case 'R': 264 { 265 vm->flags &= ~(BC_FLAG_R); 266 break; 267 } 268 269 case 'S': 270 { 271 *scale = bc_args_builtin(opts.optarg); 272 break; 273 } 274 275 #if BC_ENABLE_EXTRA_MATH 276 case 'E': 277 { 278 if (BC_ERR(!bc_num_strValid(opts.optarg))) 279 { 280 bc_verr(BC_ERR_FATAL_ARG, opts.optarg); 281 } 282 283 seed = opts.optarg; 284 285 break; 286 } 287 #endif // BC_ENABLE_EXTRA_MATH 288 289 #if BC_ENABLED 290 case 'g': 291 { 292 assert(BC_IS_BC); 293 vm->flags |= BC_FLAG_G; 294 break; 295 } 296 297 case 'l': 298 { 299 assert(BC_IS_BC); 300 vm->flags |= BC_FLAG_L; 301 break; 302 } 303 304 case 'q': 305 { 306 assert(BC_IS_BC); 307 vm->flags &= ~(BC_FLAG_Q); 308 break; 309 } 310 311 case 'r': 312 { 313 bc_args_redefine(opts.optarg); 314 break; 315 } 316 317 case 's': 318 { 319 assert(BC_IS_BC); 320 vm->flags |= BC_FLAG_S; 321 break; 322 } 323 324 case 'w': 325 { 326 assert(BC_IS_BC); 327 vm->flags |= BC_FLAG_W; 328 break; 329 } 330 #endif // BC_ENABLED 331 332 case 'V': 333 case 'v': 334 { 335 do_exit = version = true; 336 break; 337 } 338 339 #if DC_ENABLED 340 case 'x': 341 { 342 assert(BC_IS_DC); 343 vm->flags |= DC_FLAG_X; 344 break; 345 } 346 #endif // DC_ENABLED 347 348 #if BC_DEBUG 349 // We shouldn't get here because bc_opt_error()/bc_error() should 350 // longjmp() out. 351 case '?': 352 case ':': 353 default: 354 { 355 BC_UNREACHABLE 356 #if !BC_CLANG 357 abort(); 358 #endif // !BC_CLANG 359 } 360 #endif // BC_DEBUG 361 } 362 } 363 364 if (version) bc_vm_info(NULL); 365 if (do_exit) 366 { 367 vm->status = (sig_atomic_t) BC_STATUS_QUIT; 368 BC_JMP; 369 } 370 371 // We do not print the banner if expressions are used or dc is used. 372 if (BC_ARGS_SHOULD_BE_QUIET) vm->flags &= ~(BC_FLAG_Q); 373 374 // We need to make sure the files list is initialized. We don't want to 375 // initialize it if there are no files because it's just a waste of memory. 376 if (opts.optind < (size_t) argc && vm->files.v == NULL) 377 { 378 bc_vec_init(&vm->files, sizeof(char*), BC_DTOR_NONE); 379 } 380 381 // Add all the files to the vector. 382 for (i = opts.optind; i < (size_t) argc; ++i) 383 { 384 bc_vec_push(&vm->files, argv + i); 385 } 386 387 #if BC_ENABLE_EXTRA_MATH 388 if (seed != NULL) 389 { 390 BcNum n; 391 392 bc_num_init(&n, strlen(seed)); 393 394 BC_SIG_UNLOCK; 395 396 bc_num_parse(&n, seed, BC_BASE); 397 398 bc_program_assignSeed(&vm->prog, &n); 399 400 BC_SIG_LOCK; 401 402 bc_num_free(&n); 403 } 404 #endif // BC_ENABLE_EXTRA_MATH 405 } 406