18876Srgrimes/* 24Srgrimes * ***************************************************************************** 34Srgrimes * 44Srgrimes * SPDX-License-Identifier: BSD-2-Clause 58876Srgrimes * 64Srgrimes * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 74Srgrimes * 84Srgrimes * Redistribution and use in source and binary forms, with or without 94Srgrimes * modification, are permitted provided that the following conditions are met: 104Srgrimes * 118876Srgrimes * * Redistributions of source code must retain the above copyright notice, this 128876Srgrimes * list of conditions and the following disclaimer. 134Srgrimes * 144Srgrimes * * Redistributions in binary form must reproduce the above copyright notice, 158876Srgrimes * this list of conditions and the following disclaimer in the documentation 164Srgrimes * and/or other materials provided with the distribution. 178876Srgrimes * 184Srgrimes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 194Srgrimes * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 204Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 214Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 228876Srgrimes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 234Srgrimes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 244Srgrimes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 254Srgrimes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 264Srgrimes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 274Srgrimes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 284Srgrimes * POSSIBILITY OF SUCH DAMAGE. 294Srgrimes * 304Srgrimes * ***************************************************************************** 314Srgrimes * 324Srgrimes * Code for processing command-line arguments. 33116176Sobrien * 34116176Sobrien */ 35116176Sobrien 36116176Sobrien#include <assert.h> 372056Swollman#include <ctype.h> 3842654Sjdp#include <stdbool.h> 3986998Sdd#include <stdlib.h> 40131952Smarcel#include <string.h> 4186998Sdd 4286998Sdd#ifndef _WIN32 4317848Spst#include <unistd.h> 4486998Sdd#endif // _WIN32 452056Swollman 4649558Sphk#include <vector.h> 47126399Sphk#include <read.h> 4812734Sbde#include <args.h> 492056Swollman#include <opt.h> 5012473Sbde#include <num.h> 514Srgrimes#include <vm.h> 524Srgrimes 534Srgrimes/** 54118990Smarcel * Adds @a str to the list of expressions to execute later. 5579418Sjulian * @param str The string to add to the list of expressions. 564Srgrimes */ 574Srgrimesstatic void 584Srgrimesbc_args_exprs(const char* str) 594Srgrimes{ 604Srgrimes BC_SIG_ASSERT_LOCKED; 6118296Sbde 624Srgrimes if (vm->exprs.v == NULL) 634Srgrimes { 644Srgrimes bc_vec_init(&vm->exprs, sizeof(uchar), BC_DTOR_NONE); 654Srgrimes } 6678161Speter 6778161Speter bc_vec_concat(&vm->exprs, str); 6878161Speter bc_vec_concat(&vm->exprs, "\n"); 6912515Sphk} 70132002Smarcel 7186998Sdd/** 7285944Speter * Adds the contents of @a file to the list of expressions to execute later. 73132482Smarcel * @param file The name of the file whose contents should be added to the list 74126399Sphk * of expressions to execute. 7517848Spst */ 7618296Sbdestatic void 7718296Sbdebc_args_file(const char* file) 7818296Sbde{ 794Srgrimes char* buf; 804Srgrimes 814Srgrimes BC_SIG_ASSERT_LOCKED; 824Srgrimes 834Srgrimes vm->file = file; 8412515Sphk 854Srgrimes buf = bc_read_file(file); 864Srgrimes 874Srgrimes assert(buf != NULL); 884Srgrimes 894Srgrimes bc_args_exprs(buf); 904Srgrimes free(buf); 914Srgrimes} 924Srgrimes 934Srgrimesstatic BcBigDig 944Srgrimesbc_args_builtin(const char* arg) 954Srgrimes{ 964Srgrimes bool strvalid; 974Srgrimes BcNum n; 984Srgrimes BcBigDig res; 994Srgrimes 1004Srgrimes strvalid = bc_num_strValid(arg); 1014Srgrimes 1024Srgrimes if (BC_ERR(!strvalid)) 1034Srgrimes { 1044Srgrimes bc_verr(BC_ERR_FATAL_ARG, arg); 1054Srgrimes } 1064Srgrimes 10793009Sbde bc_num_init(&n, 0); 10893009Sbde 10992756Salfred bc_num_parse(&n, arg, 10); 11093009Sbde 11193009Sbde res = bc_num_bigdig(&n); 11292756Salfred 11393009Sbde bc_num_free(&n); 11493009Sbde 11512473Sbde return res; 1164Srgrimes} 1174Srgrimes 1184Srgrimes#if BC_ENABLED 11912515Sphk 12078161Speter/** 1214Srgrimes * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it 1224Srgrimes * throws a fatal error. 12318296Sbde * @param keyword The keyword to redefine. 12478161Speter */ 1254Srgrimesstatic void 1264Srgrimesbc_args_redefine(const char* keyword) 1274Srgrimes{ 12818296Sbde size_t i; 1294Srgrimes 1304Srgrimes BC_SIG_ASSERT_LOCKED; 1314Srgrimes 1324Srgrimes for (i = 0; i < bc_lex_kws_len; ++i) 1334Srgrimes { 1344Srgrimes const BcLexKeyword* kw = bc_lex_kws + i; 1354Srgrimes 1364Srgrimes if (!strcmp(keyword, kw->name)) 1374Srgrimes { 1384Srgrimes if (BC_LEX_KW_POSIX(kw)) break; 1394Srgrimes 1404Srgrimes vm->redefined_kws[i] = true; 1414Srgrimes 1424Srgrimes return; 1434Srgrimes } 1444Srgrimes } 1454Srgrimes 1464Srgrimes bc_error(BC_ERR_FATAL_ARG, 0, keyword); 1474Srgrimes} 1484Srgrimes 1494Srgrimes#endif // BC_ENABLED 1504Srgrimes 1514Srgrimesvoid 1524Srgrimesbc_args(int argc, char* argv[], bool exit_exprs, BcBigDig* scale, 1534Srgrimes BcBigDig* ibase, BcBigDig* obase) 1544Srgrimes{ 1554Srgrimes int c; 1564Srgrimes size_t i; 1574Srgrimes bool do_exit = false, version = false; 1584Srgrimes BcOpt opts; 1594Srgrimes#if BC_ENABLE_EXTRA_MATH 1604Srgrimes char* seed = NULL; 16118296Sbde#endif // BC_ENABLE_EXTRA_MATH 16218296Sbde 16378161Speter BC_SIG_ASSERT_LOCKED; 16418296Sbde 16518296Sbde bc_opt_init(&opts, argv); 16618296Sbde 16718296Sbde // This loop should look familiar to anyone who has used getopt() or 16818296Sbde // getopt_long() in C. 16918296Sbde while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) 17018296Sbde { 17118296Sbde switch (c) 17218296Sbde { 17318296Sbde case 'c': 17418296Sbde { 17518296Sbde vm->flags |= BC_FLAG_DIGIT_CLAMP; 17618296Sbde break; 17718296Sbde } 17818296Sbde 17918296Sbde case 'C': 18018296Sbde { 18118296Sbde vm->flags &= ~BC_FLAG_DIGIT_CLAMP; 18218296Sbde break; 18318296Sbde } 18418296Sbde 18518296Sbde case 'e': 18618296Sbde { 18718296Sbde // Barf if not allowed. 18818296Sbde if (vm->no_exprs) 18918296Sbde { 19018296Sbde bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)"); 19118296Sbde } 19218296Sbde 1934Srgrimes // Add the expressions and set exit. 1944Srgrimes bc_args_exprs(opts.optarg); 1954Srgrimes vm->exit_exprs = (exit_exprs || vm->exit_exprs); 1964Srgrimes 1974Srgrimes break; 1984Srgrimes } 1994Srgrimes 2004Srgrimes case 'f': 2014Srgrimes { 20212515Sphk // Figure out if exiting on expressions is disabled. 20378161Speter if (!strcmp(opts.optarg, "-")) vm->no_exprs = true; 2044Srgrimes else 20518296Sbde { 20678161Speter // Barf if not allowed. 2074Srgrimes if (vm->no_exprs) 2084Srgrimes { 20918296Sbde bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)"); 2104Srgrimes } 2114Srgrimes 2124Srgrimes // Add the expressions and set exit. 2134Srgrimes bc_args_file(opts.optarg); 2144Srgrimes vm->exit_exprs = (exit_exprs || vm->exit_exprs); 21518296Sbde } 21618296Sbde 21778161Speter break; 21818296Sbde } 21918296Sbde 22018296Sbde case 'h': 2214Srgrimes { 2224Srgrimes bc_vm_info(vm->help); 22312515Sphk do_exit = true; 22478161Speter break; 2254Srgrimes } 2264Srgrimes 22718296Sbde case 'i': 22878161Speter { 2294Srgrimes vm->flags |= BC_FLAG_I; 2304Srgrimes break; 2314Srgrimes } 2324Srgrimes 2334Srgrimes case 'I': 234798Swollman { 2354Srgrimes *ibase = bc_args_builtin(opts.optarg); 2364Srgrimes break; 2374Srgrimes } 2384Srgrimes 2394Srgrimes case 'z': 2404Srgrimes { 2414Srgrimes vm->flags |= BC_FLAG_Z; 2424Srgrimes break; 2434Srgrimes } 2444Srgrimes 2454Srgrimes case 'L': 2464Srgrimes { 24710348Sbde vm->line_len = 0; 2484Srgrimes break; 2494Srgrimes } 2504Srgrimes 2514Srgrimes case 'O': 2524Srgrimes { 2534Srgrimes *obase = bc_args_builtin(opts.optarg); 2544Srgrimes break; 2554Srgrimes } 2564Srgrimes 2574Srgrimes case 'P': 2584Srgrimes { 2594Srgrimes vm->flags &= ~(BC_FLAG_P); 2604Srgrimes break; 2614Srgrimes } 26218296Sbde 26378161Speter case 'R': 2644Srgrimes { 2654Srgrimes vm->flags &= ~(BC_FLAG_R); 2664Srgrimes break; 2674Srgrimes } 2684Srgrimes 2694Srgrimes case 'S': 2704Srgrimes { 2714Srgrimes *scale = bc_args_builtin(opts.optarg); 2724Srgrimes break; 2734Srgrimes } 2744Srgrimes 27578161Speter#if BC_ENABLE_EXTRA_MATH 2764Srgrimes case 'E': 2774Srgrimes { 2784Srgrimes if (BC_ERR(!bc_num_strValid(opts.optarg))) 2794Srgrimes { 2804Srgrimes bc_verr(BC_ERR_FATAL_ARG, opts.optarg); 2814Srgrimes } 28218296Sbde 28318296Sbde seed = opts.optarg; 284104321Sphk 28578161Speter break; 28678161Speter } 287104321Sphk#endif // BC_ENABLE_EXTRA_MATH 28818296Sbde 2894Srgrimes#if BC_ENABLED 2904Srgrimes case 'g': 29178161Speter { 2924Srgrimes assert(BC_IS_BC); 2934Srgrimes vm->flags |= BC_FLAG_G; 2944Srgrimes break; 2954Srgrimes } 2964Srgrimes 2974Srgrimes case 'l': 2984Srgrimes { 2994Srgrimes assert(BC_IS_BC); 3004Srgrimes vm->flags |= BC_FLAG_L; 3014Srgrimes break; 3024Srgrimes } 3034Srgrimes 3044Srgrimes case 'q': 3054Srgrimes { 3064Srgrimes assert(BC_IS_BC); 3074Srgrimes vm->flags &= ~(BC_FLAG_Q); 3084Srgrimes break; 3094Srgrimes } 3104Srgrimes 3114Srgrimes case 'r': 3124Srgrimes { 3134Srgrimes bc_args_redefine(opts.optarg); 3144Srgrimes break; 3154Srgrimes } 3164Srgrimes 3174Srgrimes case 's': 3184Srgrimes { 3194Srgrimes assert(BC_IS_BC); 3204Srgrimes vm->flags |= BC_FLAG_S; 3214Srgrimes break; 3224Srgrimes } 3234Srgrimes 3244Srgrimes case 'w': 3254Srgrimes { 3264Srgrimes assert(BC_IS_BC); 3274Srgrimes vm->flags |= BC_FLAG_W; 3284Srgrimes break; 3294Srgrimes } 3304Srgrimes#endif // BC_ENABLED 3314Srgrimes 3324Srgrimes case 'V': 3334Srgrimes case 'v': 3344Srgrimes { 3354Srgrimes do_exit = version = true; 3364Srgrimes break; 3374Srgrimes } 3384Srgrimes 3394Srgrimes#if DC_ENABLED 3404Srgrimes case 'x': 3414Srgrimes { 3424Srgrimes assert(BC_IS_DC); 3434Srgrimes vm->flags |= DC_FLAG_X; 3444Srgrimes break; 3454Srgrimes } 3464Srgrimes#endif // DC_ENABLED 3474Srgrimes 3484Srgrimes#if BC_DEBUG 3494Srgrimes // We shouldn't get here because bc_opt_error()/bc_error() should 350118268Sjhb // longjmp() out. 3514Srgrimes case '?': 3524Srgrimes case ':': 3534Srgrimes default: 3544Srgrimes { 3554Srgrimes BC_UNREACHABLE 3564Srgrimes#if !BC_CLANG 3574Srgrimes abort(); 3584Srgrimes#endif // !BC_CLANG 3594Srgrimes } 3604Srgrimes#endif // BC_DEBUG 3614Srgrimes } 3624Srgrimes } 3634Srgrimes 3644Srgrimes if (version) bc_vm_info(NULL); 3654Srgrimes if (do_exit) 3664Srgrimes { 3674Srgrimes vm->status = (sig_atomic_t) BC_STATUS_QUIT; 3684Srgrimes BC_JMP; 3694Srgrimes } 3704Srgrimes 3714Srgrimes // We do not print the banner if expressions are used or dc is used. 3724Srgrimes if (BC_ARGS_SHOULD_BE_QUIET) vm->flags &= ~(BC_FLAG_Q); 3734Srgrimes 3744Srgrimes // We need to make sure the files list is initialized. We don't want to 3754Srgrimes // initialize it if there are no files because it's just a waste of memory. 3764Srgrimes if (opts.optind < (size_t) argc && vm->files.v == NULL) 3772112Swollman { 37812515Sphk bc_vec_init(&vm->files, sizeof(char*), BC_DTOR_NONE); 3791549Srgrimes } 3804Srgrimes 3814Srgrimes // Add all the files to the vector. 3824Srgrimes for (i = opts.optind; i < (size_t) argc; ++i) 38312515Sphk { 3844Srgrimes bc_vec_push(&vm->files, argv + i); 3854Srgrimes } 3864Srgrimes 387131952Smarcel#if BC_ENABLE_EXTRA_MATH 3884Srgrimes if (seed != NULL) 3894Srgrimes { 3904Srgrimes BcNum n; 39112515Sphk 3924Srgrimes bc_num_init(&n, strlen(seed)); 3936920Sjoerg 3944Srgrimes BC_SIG_UNLOCK; 3954Srgrimes 3964Srgrimes bc_num_parse(&n, seed, BC_BASE); 3974Srgrimes 3984Srgrimes bc_program_assignSeed(&vm->prog, &n); 3994Srgrimes 4004Srgrimes BC_SIG_LOCK; 4014Srgrimes 4024Srgrimes bc_num_free(&n); 4034Srgrimes } 4044Srgrimes#endif // BC_ENABLE_EXTRA_MATH 40579573Sbsd} 40679573Sbsd