1252884aeSStefan Eßer /*
2252884aeSStefan Eßer * *****************************************************************************
3252884aeSStefan Eßer *
43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer *
6a970610aSStefan 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
bc_args_exprs(const char * str)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
bc_args_file(const char * file)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
bc_args_builtin(const char * arg)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
bc_args_redefine(const char * keyword)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
bc_args(int argc,const char * argv[],bool exit_exprs,BcBigDig * scale,BcBigDig * ibase,BcBigDig * obase)152*12e0d316SStefan Eßer bc_args(int argc, const 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
160*12e0d316SStefan Eßer const 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