xref: /freebsd/contrib/bc/src/args.c (revision 12e0d316644a4f80f5f1f78cf07bd93def43b1ca)
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