1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 6*a970610aSStefan Eßer * Copyright (c) 2018-2024 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Code for processing command-line arguments. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <ctype.h> 38252884aeSStefan Eßer #include <stdbool.h> 39252884aeSStefan Eßer #include <stdlib.h> 40252884aeSStefan Eßer #include <string.h> 41252884aeSStefan Eßer 427e5c51e5SStefan Eßer #ifndef _WIN32 43252884aeSStefan Eßer #include <unistd.h> 447e5c51e5SStefan Eßer #endif // _WIN32 45252884aeSStefan Eßer 46252884aeSStefan Eßer #include <vector.h> 47252884aeSStefan Eßer #include <read.h> 48252884aeSStefan Eßer #include <args.h> 49252884aeSStefan Eßer #include <opt.h> 5078bc019dSStefan Eßer #include <num.h> 51d101cdd6SStefan Eßer #include <vm.h> 52252884aeSStefan Eßer 5344d4804dSStefan Eßer /** 5444d4804dSStefan Eßer * Adds @a str to the list of expressions to execute later. 5544d4804dSStefan Eßer * @param str The string to add to the list of expressions. 5644d4804dSStefan Eßer */ 5778bc019dSStefan Eßer static void 5878bc019dSStefan Eßer bc_args_exprs(const char* str) 5978bc019dSStefan Eßer { 60252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 61d101cdd6SStefan Eßer 62d101cdd6SStefan Eßer if (vm->exprs.v == NULL) 63d101cdd6SStefan Eßer { 64d101cdd6SStefan Eßer bc_vec_init(&vm->exprs, sizeof(uchar), BC_DTOR_NONE); 65d101cdd6SStefan Eßer } 66d101cdd6SStefan Eßer 67d101cdd6SStefan Eßer bc_vec_concat(&vm->exprs, str); 68d101cdd6SStefan Eßer bc_vec_concat(&vm->exprs, "\n"); 69252884aeSStefan Eßer } 70252884aeSStefan Eßer 7144d4804dSStefan Eßer /** 7244d4804dSStefan Eßer * Adds the contents of @a file to the list of expressions to execute later. 7344d4804dSStefan Eßer * @param file The name of the file whose contents should be added to the list 7444d4804dSStefan Eßer * of expressions to execute. 7544d4804dSStefan Eßer */ 7678bc019dSStefan Eßer static void 7778bc019dSStefan Eßer bc_args_file(const char* file) 7878bc019dSStefan Eßer { 79252884aeSStefan Eßer char* buf; 80252884aeSStefan Eßer 81252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 82252884aeSStefan Eßer 83d101cdd6SStefan Eßer vm->file = file; 84252884aeSStefan Eßer 8544d4804dSStefan Eßer buf = bc_read_file(file); 8644d4804dSStefan Eßer 8744d4804dSStefan Eßer assert(buf != NULL); 8844d4804dSStefan Eßer 89252884aeSStefan Eßer bc_args_exprs(buf); 90252884aeSStefan Eßer free(buf); 91252884aeSStefan Eßer } 92252884aeSStefan Eßer 9378bc019dSStefan Eßer static BcBigDig 9478bc019dSStefan Eßer bc_args_builtin(const char* arg) 9578bc019dSStefan Eßer { 9678bc019dSStefan Eßer bool strvalid; 9778bc019dSStefan Eßer BcNum n; 9878bc019dSStefan Eßer BcBigDig res; 9978bc019dSStefan Eßer 10078bc019dSStefan Eßer strvalid = bc_num_strValid(arg); 10178bc019dSStefan Eßer 10278bc019dSStefan Eßer if (BC_ERR(!strvalid)) 10378bc019dSStefan Eßer { 10478bc019dSStefan Eßer bc_verr(BC_ERR_FATAL_ARG, arg); 10578bc019dSStefan Eßer } 10678bc019dSStefan Eßer 10778bc019dSStefan Eßer bc_num_init(&n, 0); 10878bc019dSStefan Eßer 10978bc019dSStefan Eßer bc_num_parse(&n, arg, 10); 11078bc019dSStefan Eßer 11178bc019dSStefan Eßer res = bc_num_bigdig(&n); 11278bc019dSStefan Eßer 11378bc019dSStefan Eßer bc_num_free(&n); 11478bc019dSStefan Eßer 11578bc019dSStefan Eßer return res; 11678bc019dSStefan Eßer } 11778bc019dSStefan Eßer 11844d4804dSStefan Eßer #if BC_ENABLED 11944d4804dSStefan Eßer 12044d4804dSStefan Eßer /** 12144d4804dSStefan Eßer * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it 12244d4804dSStefan Eßer * throws a fatal error. 12344d4804dSStefan Eßer * @param keyword The keyword to redefine. 12444d4804dSStefan Eßer */ 12578bc019dSStefan Eßer static void 12678bc019dSStefan Eßer bc_args_redefine(const char* keyword) 12778bc019dSStefan Eßer { 12844d4804dSStefan Eßer size_t i; 12944d4804dSStefan Eßer 13010041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 13110041e99SStefan Eßer 13278bc019dSStefan Eßer for (i = 0; i < bc_lex_kws_len; ++i) 13378bc019dSStefan Eßer { 13444d4804dSStefan Eßer const BcLexKeyword* kw = bc_lex_kws + i; 13544d4804dSStefan Eßer 13678bc019dSStefan Eßer if (!strcmp(keyword, kw->name)) 13778bc019dSStefan Eßer { 13844d4804dSStefan Eßer if (BC_LEX_KW_POSIX(kw)) break; 13944d4804dSStefan Eßer 140d101cdd6SStefan Eßer vm->redefined_kws[i] = true; 14144d4804dSStefan Eßer 14244d4804dSStefan Eßer return; 14344d4804dSStefan Eßer } 14444d4804dSStefan Eßer } 14544d4804dSStefan Eßer 14644d4804dSStefan Eßer bc_error(BC_ERR_FATAL_ARG, 0, keyword); 14744d4804dSStefan Eßer } 14844d4804dSStefan Eßer 14944d4804dSStefan Eßer #endif // BC_ENABLED 15044d4804dSStefan Eßer 15178bc019dSStefan Eßer void 152d101cdd6SStefan Eßer bc_args(int argc, char* argv[], bool exit_exprs, BcBigDig* scale, 153d101cdd6SStefan Eßer BcBigDig* ibase, BcBigDig* obase) 15478bc019dSStefan Eßer { 155252884aeSStefan Eßer int c; 156252884aeSStefan Eßer size_t i; 157252884aeSStefan Eßer bool do_exit = false, version = false; 158252884aeSStefan Eßer BcOpt opts; 15978bc019dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 16078bc019dSStefan Eßer char* seed = NULL; 16178bc019dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 162252884aeSStefan Eßer 163252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 164252884aeSStefan Eßer 165252884aeSStefan Eßer bc_opt_init(&opts, argv); 166252884aeSStefan Eßer 16744d4804dSStefan Eßer // This loop should look familiar to anyone who has used getopt() or 16844d4804dSStefan Eßer // getopt_long() in C. 16978bc019dSStefan Eßer while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) 17078bc019dSStefan Eßer { 17178bc019dSStefan Eßer switch (c) 17278bc019dSStefan Eßer { 173d101cdd6SStefan Eßer case 'c': 174d101cdd6SStefan Eßer { 175d101cdd6SStefan Eßer vm->flags |= BC_FLAG_DIGIT_CLAMP; 176d101cdd6SStefan Eßer break; 177d101cdd6SStefan Eßer } 178d101cdd6SStefan Eßer 179d101cdd6SStefan Eßer case 'C': 180d101cdd6SStefan Eßer { 181d101cdd6SStefan Eßer vm->flags &= ~BC_FLAG_DIGIT_CLAMP; 182d101cdd6SStefan Eßer break; 183d101cdd6SStefan Eßer } 184d101cdd6SStefan Eßer 185252884aeSStefan Eßer case 'e': 186252884aeSStefan Eßer { 18744d4804dSStefan Eßer // Barf if not allowed. 188d101cdd6SStefan Eßer if (vm->no_exprs) 18978bc019dSStefan Eßer { 19044d4804dSStefan Eßer bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)"); 19178bc019dSStefan Eßer } 19244d4804dSStefan Eßer 19344d4804dSStefan Eßer // Add the expressions and set exit. 194252884aeSStefan Eßer bc_args_exprs(opts.optarg); 195d101cdd6SStefan Eßer vm->exit_exprs = (exit_exprs || vm->exit_exprs); 19644d4804dSStefan Eßer 197252884aeSStefan Eßer break; 198252884aeSStefan Eßer } 199252884aeSStefan Eßer 200252884aeSStefan Eßer case 'f': 201252884aeSStefan Eßer { 20244d4804dSStefan Eßer // Figure out if exiting on expressions is disabled. 203d101cdd6SStefan Eßer if (!strcmp(opts.optarg, "-")) vm->no_exprs = true; 20478bc019dSStefan Eßer else 20578bc019dSStefan Eßer { 20644d4804dSStefan Eßer // Barf if not allowed. 207d101cdd6SStefan Eßer if (vm->no_exprs) 20878bc019dSStefan Eßer { 20944d4804dSStefan Eßer bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)"); 21078bc019dSStefan Eßer } 21144d4804dSStefan Eßer 21244d4804dSStefan Eßer // Add the expressions and set exit. 213252884aeSStefan Eßer bc_args_file(opts.optarg); 214d101cdd6SStefan Eßer vm->exit_exprs = (exit_exprs || vm->exit_exprs); 2155d934bc0SStefan Eßer } 21644d4804dSStefan Eßer 217252884aeSStefan Eßer break; 218252884aeSStefan Eßer } 219252884aeSStefan Eßer 220252884aeSStefan Eßer case 'h': 221252884aeSStefan Eßer { 222d101cdd6SStefan Eßer bc_vm_info(vm->help); 223252884aeSStefan Eßer do_exit = true; 224252884aeSStefan Eßer break; 225252884aeSStefan Eßer } 226252884aeSStefan Eßer 227252884aeSStefan Eßer case 'i': 228252884aeSStefan Eßer { 229d101cdd6SStefan Eßer vm->flags |= BC_FLAG_I; 230252884aeSStefan Eßer break; 231252884aeSStefan Eßer } 232252884aeSStefan Eßer 23378bc019dSStefan Eßer case 'I': 23478bc019dSStefan Eßer { 235d101cdd6SStefan Eßer *ibase = bc_args_builtin(opts.optarg); 23678bc019dSStefan Eßer break; 23778bc019dSStefan Eßer } 23878bc019dSStefan Eßer 239d43fa8efSStefan Eßer case 'z': 240d43fa8efSStefan Eßer { 241d101cdd6SStefan Eßer vm->flags |= BC_FLAG_Z; 242d43fa8efSStefan Eßer break; 243d43fa8efSStefan Eßer } 244d43fa8efSStefan Eßer 245d43fa8efSStefan Eßer case 'L': 246d43fa8efSStefan Eßer { 247d101cdd6SStefan Eßer vm->line_len = 0; 248d43fa8efSStefan Eßer break; 249d43fa8efSStefan Eßer } 250d43fa8efSStefan Eßer 25178bc019dSStefan Eßer case 'O': 25278bc019dSStefan Eßer { 253d101cdd6SStefan Eßer *obase = bc_args_builtin(opts.optarg); 25478bc019dSStefan Eßer break; 25578bc019dSStefan Eßer } 25678bc019dSStefan Eßer 257252884aeSStefan Eßer case 'P': 258252884aeSStefan Eßer { 259d101cdd6SStefan Eßer vm->flags &= ~(BC_FLAG_P); 260252884aeSStefan Eßer break; 261252884aeSStefan Eßer } 262252884aeSStefan Eßer 2637e5c51e5SStefan Eßer case 'R': 2647e5c51e5SStefan Eßer { 265d101cdd6SStefan Eßer vm->flags &= ~(BC_FLAG_R); 2667e5c51e5SStefan Eßer break; 2677e5c51e5SStefan Eßer } 2687e5c51e5SStefan Eßer 26978bc019dSStefan Eßer case 'S': 27078bc019dSStefan Eßer { 271d101cdd6SStefan Eßer *scale = bc_args_builtin(opts.optarg); 27278bc019dSStefan Eßer break; 27378bc019dSStefan Eßer } 27478bc019dSStefan Eßer 27578bc019dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 27678bc019dSStefan Eßer case 'E': 27778bc019dSStefan Eßer { 27878bc019dSStefan Eßer if (BC_ERR(!bc_num_strValid(opts.optarg))) 27978bc019dSStefan Eßer { 28078bc019dSStefan Eßer bc_verr(BC_ERR_FATAL_ARG, opts.optarg); 28178bc019dSStefan Eßer } 28278bc019dSStefan Eßer 28378bc019dSStefan Eßer seed = opts.optarg; 28478bc019dSStefan Eßer 28578bc019dSStefan Eßer break; 28678bc019dSStefan Eßer } 28778bc019dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 28878bc019dSStefan Eßer 289252884aeSStefan Eßer #if BC_ENABLED 290252884aeSStefan Eßer case 'g': 291252884aeSStefan Eßer { 292252884aeSStefan Eßer assert(BC_IS_BC); 293d101cdd6SStefan Eßer vm->flags |= BC_FLAG_G; 294252884aeSStefan Eßer break; 295252884aeSStefan Eßer } 296252884aeSStefan Eßer 297252884aeSStefan Eßer case 'l': 298252884aeSStefan Eßer { 299252884aeSStefan Eßer assert(BC_IS_BC); 300d101cdd6SStefan Eßer vm->flags |= BC_FLAG_L; 301252884aeSStefan Eßer break; 302252884aeSStefan Eßer } 303252884aeSStefan Eßer 304252884aeSStefan Eßer case 'q': 305252884aeSStefan Eßer { 306252884aeSStefan Eßer assert(BC_IS_BC); 307d101cdd6SStefan Eßer vm->flags &= ~(BC_FLAG_Q); 308252884aeSStefan Eßer break; 309252884aeSStefan Eßer } 310252884aeSStefan Eßer 31144d4804dSStefan Eßer case 'r': 31244d4804dSStefan Eßer { 31344d4804dSStefan Eßer bc_args_redefine(opts.optarg); 31444d4804dSStefan Eßer break; 31544d4804dSStefan Eßer } 31644d4804dSStefan Eßer 317252884aeSStefan Eßer case 's': 318252884aeSStefan Eßer { 319252884aeSStefan Eßer assert(BC_IS_BC); 320d101cdd6SStefan Eßer vm->flags |= BC_FLAG_S; 321252884aeSStefan Eßer break; 322252884aeSStefan Eßer } 323252884aeSStefan Eßer 324252884aeSStefan Eßer case 'w': 325252884aeSStefan Eßer { 326252884aeSStefan Eßer assert(BC_IS_BC); 327d101cdd6SStefan Eßer vm->flags |= BC_FLAG_W; 328252884aeSStefan Eßer break; 329252884aeSStefan Eßer } 330252884aeSStefan Eßer #endif // BC_ENABLED 331252884aeSStefan Eßer 332252884aeSStefan Eßer case 'V': 333252884aeSStefan Eßer case 'v': 334252884aeSStefan Eßer { 335252884aeSStefan Eßer do_exit = version = true; 336252884aeSStefan Eßer break; 337252884aeSStefan Eßer } 338252884aeSStefan Eßer 339252884aeSStefan Eßer #if DC_ENABLED 340252884aeSStefan Eßer case 'x': 341252884aeSStefan Eßer { 3423aa99676SStefan Eßer assert(BC_IS_DC); 343d101cdd6SStefan Eßer vm->flags |= DC_FLAG_X; 344252884aeSStefan Eßer break; 345252884aeSStefan Eßer } 346252884aeSStefan Eßer #endif // DC_ENABLED 347252884aeSStefan Eßer 348103d7cdfSStefan Eßer #if BC_DEBUG 34944d4804dSStefan Eßer // We shouldn't get here because bc_opt_error()/bc_error() should 350252884aeSStefan Eßer // longjmp() out. 351252884aeSStefan Eßer case '?': 352252884aeSStefan Eßer case ':': 353252884aeSStefan Eßer default: 354252884aeSStefan Eßer { 35544d4804dSStefan Eßer BC_UNREACHABLE 356d101cdd6SStefan Eßer #if !BC_CLANG 357252884aeSStefan Eßer abort(); 358d101cdd6SStefan Eßer #endif // !BC_CLANG 359252884aeSStefan Eßer } 360103d7cdfSStefan Eßer #endif // BC_DEBUG 361252884aeSStefan Eßer } 362252884aeSStefan Eßer } 363252884aeSStefan Eßer 364252884aeSStefan Eßer if (version) bc_vm_info(NULL); 36578bc019dSStefan Eßer if (do_exit) 36678bc019dSStefan Eßer { 367d101cdd6SStefan Eßer vm->status = (sig_atomic_t) BC_STATUS_QUIT; 36844d4804dSStefan Eßer BC_JMP; 36944d4804dSStefan Eßer } 370252884aeSStefan Eßer 37144d4804dSStefan Eßer // We do not print the banner if expressions are used or dc is used. 372d101cdd6SStefan Eßer if (BC_ARGS_SHOULD_BE_QUIET) vm->flags &= ~(BC_FLAG_Q); 37344d4804dSStefan Eßer 37444d4804dSStefan Eßer // We need to make sure the files list is initialized. We don't want to 37544d4804dSStefan Eßer // initialize it if there are no files because it's just a waste of memory. 376d101cdd6SStefan Eßer if (opts.optind < (size_t) argc && vm->files.v == NULL) 37778bc019dSStefan Eßer { 378d101cdd6SStefan Eßer bc_vec_init(&vm->files, sizeof(char*), BC_DTOR_NONE); 37978bc019dSStefan Eßer } 380252884aeSStefan Eßer 38144d4804dSStefan Eßer // Add all the files to the vector. 382252884aeSStefan Eßer for (i = opts.optind; i < (size_t) argc; ++i) 38378bc019dSStefan Eßer { 384d101cdd6SStefan Eßer bc_vec_push(&vm->files, argv + i); 385252884aeSStefan Eßer } 38678bc019dSStefan Eßer 38778bc019dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 38878bc019dSStefan Eßer if (seed != NULL) 38978bc019dSStefan Eßer { 39078bc019dSStefan Eßer BcNum n; 39178bc019dSStefan Eßer 39278bc019dSStefan Eßer bc_num_init(&n, strlen(seed)); 39378bc019dSStefan Eßer 39478bc019dSStefan Eßer BC_SIG_UNLOCK; 39578bc019dSStefan Eßer 39678bc019dSStefan Eßer bc_num_parse(&n, seed, BC_BASE); 39778bc019dSStefan Eßer 398d101cdd6SStefan Eßer bc_program_assignSeed(&vm->prog, &n); 39978bc019dSStefan Eßer 40078bc019dSStefan Eßer BC_SIG_LOCK; 40178bc019dSStefan Eßer 40278bc019dSStefan Eßer bc_num_free(&n); 40378bc019dSStefan Eßer } 40478bc019dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 40578bc019dSStefan Eßer } 406