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