xref: /freebsd/contrib/bc/src/args.c (revision 10041e99a0c29c9f99c4148fc173bb12dd26aa8d)
1252884aeSStefan Eßer /*
2252884aeSStefan Eßer  * *****************************************************************************
3252884aeSStefan Eßer  *
43aa99676SStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5252884aeSStefan Eßer  *
610328f8bSStefan Eßer  * Copyright (c) 2018-2021 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>
50252884aeSStefan Eßer 
5144d4804dSStefan Eßer /**
5244d4804dSStefan Eßer  * Adds @a str to the list of expressions to execute later.
5344d4804dSStefan Eßer  * @param str  The string to add to the list of expressions.
5444d4804dSStefan Eßer  */
55252884aeSStefan Eßer static void bc_args_exprs(const char *str) {
56252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
5744d4804dSStefan Eßer 	if (vm.exprs.v == NULL) bc_vec_init(&vm.exprs, sizeof(uchar), BC_DTOR_NONE);
58252884aeSStefan Eßer 	bc_vec_concat(&vm.exprs, str);
59252884aeSStefan Eßer 	bc_vec_concat(&vm.exprs, "\n");
60252884aeSStefan Eßer }
61252884aeSStefan Eßer 
6244d4804dSStefan Eßer /**
6344d4804dSStefan Eßer  * Adds the contents of @a file to the list of expressions to execute later.
6444d4804dSStefan Eßer  * @param file  The name of the file whose contents should be added to the list
6544d4804dSStefan Eßer  *              of expressions to execute.
6644d4804dSStefan Eßer  */
67252884aeSStefan Eßer static void bc_args_file(const char *file) {
68252884aeSStefan Eßer 
69252884aeSStefan Eßer 	char *buf;
70252884aeSStefan Eßer 
71252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
72252884aeSStefan Eßer 
73252884aeSStefan Eßer 	vm.file = file;
74252884aeSStefan Eßer 
7544d4804dSStefan Eßer 	buf = bc_read_file(file);
7644d4804dSStefan Eßer 
7744d4804dSStefan Eßer 	assert(buf != NULL);
7844d4804dSStefan Eßer 
79252884aeSStefan Eßer 	bc_args_exprs(buf);
80252884aeSStefan Eßer 	free(buf);
81252884aeSStefan Eßer }
82252884aeSStefan Eßer 
8344d4804dSStefan Eßer #if BC_ENABLED
8444d4804dSStefan Eßer 
8544d4804dSStefan Eßer /**
8644d4804dSStefan Eßer  * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it
8744d4804dSStefan Eßer  * throws a fatal error.
8844d4804dSStefan Eßer  * @param keyword  The keyword to redefine.
8944d4804dSStefan Eßer  */
9044d4804dSStefan Eßer static void bc_args_redefine(const char *keyword) {
9144d4804dSStefan Eßer 
9244d4804dSStefan Eßer 	size_t i;
9344d4804dSStefan Eßer 
94*10041e99SStefan Eßer 	BC_SIG_ASSERT_LOCKED;
95*10041e99SStefan Eßer 
9644d4804dSStefan Eßer 	for (i = 0; i < bc_lex_kws_len; ++i) {
9744d4804dSStefan Eßer 
9844d4804dSStefan Eßer 		const BcLexKeyword *kw = bc_lex_kws + i;
9944d4804dSStefan Eßer 
10044d4804dSStefan Eßer 		if (!strcmp(keyword, kw->name)) {
10144d4804dSStefan Eßer 
10244d4804dSStefan Eßer 			if (BC_LEX_KW_POSIX(kw)) break;
10344d4804dSStefan Eßer 
10444d4804dSStefan Eßer 			vm.redefined_kws[i] = true;
10544d4804dSStefan Eßer 
10644d4804dSStefan Eßer 			return;
10744d4804dSStefan Eßer 		}
10844d4804dSStefan Eßer 	}
10944d4804dSStefan Eßer 
11044d4804dSStefan Eßer 	bc_error(BC_ERR_FATAL_ARG, 0, keyword);
11144d4804dSStefan Eßer }
11244d4804dSStefan Eßer 
11344d4804dSStefan Eßer #endif // BC_ENABLED
11444d4804dSStefan Eßer 
1159a995fe1SStefan Eßer void bc_args(int argc, char *argv[], bool exit_exprs) {
116252884aeSStefan Eßer 
117252884aeSStefan Eßer 	int c;
118252884aeSStefan Eßer 	size_t i;
119252884aeSStefan Eßer 	bool do_exit = false, version = false;
120252884aeSStefan Eßer 	BcOpt opts;
121252884aeSStefan Eßer 
122252884aeSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
123252884aeSStefan Eßer 
124252884aeSStefan Eßer 	bc_opt_init(&opts, argv);
125252884aeSStefan Eßer 
12644d4804dSStefan Eßer 	// This loop should look familiar to anyone who has used getopt() or
12744d4804dSStefan Eßer 	// getopt_long() in C.
128252884aeSStefan Eßer 	while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) {
129252884aeSStefan Eßer 
130252884aeSStefan Eßer 		switch (c) {
131252884aeSStefan Eßer 
132252884aeSStefan Eßer 			case 'e':
133252884aeSStefan Eßer 			{
13444d4804dSStefan Eßer 				// Barf if not allowed.
13544d4804dSStefan Eßer 				if (vm.no_exprs)
13644d4804dSStefan Eßer 					bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)");
13744d4804dSStefan Eßer 
13844d4804dSStefan Eßer 				// Add the expressions and set exit.
139252884aeSStefan Eßer 				bc_args_exprs(opts.optarg);
1409a995fe1SStefan Eßer 				vm.exit_exprs = (exit_exprs || vm.exit_exprs);
14144d4804dSStefan Eßer 
142252884aeSStefan Eßer 				break;
143252884aeSStefan Eßer 			}
144252884aeSStefan Eßer 
145252884aeSStefan Eßer 			case 'f':
146252884aeSStefan Eßer 			{
14744d4804dSStefan Eßer 				// Figure out if exiting on expressions is disabled.
14844d4804dSStefan Eßer 				if (!strcmp(opts.optarg, "-")) vm.no_exprs = true;
1495d934bc0SStefan Eßer 				else {
15044d4804dSStefan Eßer 
15144d4804dSStefan Eßer 					// Barf if not allowed.
15244d4804dSStefan Eßer 					if (vm.no_exprs)
15344d4804dSStefan Eßer 						bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)");
15444d4804dSStefan Eßer 
15544d4804dSStefan Eßer 				// Add the expressions and set exit.
156252884aeSStefan Eßer 					bc_args_file(opts.optarg);
1579a995fe1SStefan Eßer 					vm.exit_exprs = (exit_exprs || vm.exit_exprs);
1585d934bc0SStefan Eßer 				}
15944d4804dSStefan Eßer 
160252884aeSStefan Eßer 				break;
161252884aeSStefan Eßer 			}
162252884aeSStefan Eßer 
163252884aeSStefan Eßer 			case 'h':
164252884aeSStefan Eßer 			{
165252884aeSStefan Eßer 				bc_vm_info(vm.help);
166252884aeSStefan Eßer 				do_exit = true;
167252884aeSStefan Eßer 				break;
168252884aeSStefan Eßer 			}
169252884aeSStefan Eßer 
170252884aeSStefan Eßer 			case 'i':
171252884aeSStefan Eßer 			{
172252884aeSStefan Eßer 				vm.flags |= BC_FLAG_I;
173252884aeSStefan Eßer 				break;
174252884aeSStefan Eßer 			}
175252884aeSStefan Eßer 
176d43fa8efSStefan Eßer 			case 'z':
177d43fa8efSStefan Eßer 			{
178d43fa8efSStefan Eßer 				vm.flags |= BC_FLAG_Z;
179d43fa8efSStefan Eßer 				break;
180d43fa8efSStefan Eßer 			}
181d43fa8efSStefan Eßer 
182d43fa8efSStefan Eßer 			case 'L':
183d43fa8efSStefan Eßer 			{
184d43fa8efSStefan Eßer 				vm.line_len = 0;
185d43fa8efSStefan Eßer 				break;
186d43fa8efSStefan Eßer 			}
187d43fa8efSStefan Eßer 
188252884aeSStefan Eßer 			case 'P':
189252884aeSStefan Eßer 			{
19044d4804dSStefan Eßer 				vm.flags &= ~(BC_FLAG_P);
191252884aeSStefan Eßer 				break;
192252884aeSStefan Eßer 			}
193252884aeSStefan Eßer 
1947e5c51e5SStefan Eßer 			case 'R':
1957e5c51e5SStefan Eßer 			{
19644d4804dSStefan Eßer 				vm.flags &= ~(BC_FLAG_R);
1977e5c51e5SStefan Eßer 				break;
1987e5c51e5SStefan Eßer 			}
1997e5c51e5SStefan Eßer 
200252884aeSStefan Eßer #if BC_ENABLED
201252884aeSStefan Eßer 			case 'g':
202252884aeSStefan Eßer 			{
203252884aeSStefan Eßer 				assert(BC_IS_BC);
204252884aeSStefan Eßer 				vm.flags |= BC_FLAG_G;
205252884aeSStefan Eßer 				break;
206252884aeSStefan Eßer 			}
207252884aeSStefan Eßer 
208252884aeSStefan Eßer 			case 'l':
209252884aeSStefan Eßer 			{
210252884aeSStefan Eßer 				assert(BC_IS_BC);
211252884aeSStefan Eßer 				vm.flags |= BC_FLAG_L;
212252884aeSStefan Eßer 				break;
213252884aeSStefan Eßer 			}
214252884aeSStefan Eßer 
215252884aeSStefan Eßer 			case 'q':
216252884aeSStefan Eßer 			{
217252884aeSStefan Eßer 				assert(BC_IS_BC);
218d43fa8efSStefan Eßer 				vm.flags &= ~(BC_FLAG_Q);
219252884aeSStefan Eßer 				break;
220252884aeSStefan Eßer 			}
221252884aeSStefan Eßer 
22244d4804dSStefan Eßer 			case 'r':
22344d4804dSStefan Eßer 			{
22444d4804dSStefan Eßer 				bc_args_redefine(opts.optarg);
22544d4804dSStefan Eßer 				break;
22644d4804dSStefan Eßer 			}
22744d4804dSStefan Eßer 
228252884aeSStefan Eßer 			case 's':
229252884aeSStefan Eßer 			{
230252884aeSStefan Eßer 				assert(BC_IS_BC);
231252884aeSStefan Eßer 				vm.flags |= BC_FLAG_S;
232252884aeSStefan Eßer 				break;
233252884aeSStefan Eßer 			}
234252884aeSStefan Eßer 
235252884aeSStefan Eßer 			case 'w':
236252884aeSStefan Eßer 			{
237252884aeSStefan Eßer 				assert(BC_IS_BC);
238252884aeSStefan Eßer 				vm.flags |= BC_FLAG_W;
239252884aeSStefan Eßer 				break;
240252884aeSStefan Eßer 			}
241252884aeSStefan Eßer #endif // BC_ENABLED
242252884aeSStefan Eßer 
243252884aeSStefan Eßer 			case 'V':
244252884aeSStefan Eßer 			case 'v':
245252884aeSStefan Eßer 			{
246252884aeSStefan Eßer 				do_exit = version = true;
247252884aeSStefan Eßer 				break;
248252884aeSStefan Eßer 			}
249252884aeSStefan Eßer 
250252884aeSStefan Eßer #if DC_ENABLED
251252884aeSStefan Eßer 			case 'x':
252252884aeSStefan Eßer 			{
2533aa99676SStefan Eßer 				assert(BC_IS_DC);
254252884aeSStefan Eßer 				vm.flags |= DC_FLAG_X;
255252884aeSStefan Eßer 				break;
256252884aeSStefan Eßer 			}
257252884aeSStefan Eßer #endif // DC_ENABLED
258252884aeSStefan Eßer 
259252884aeSStefan Eßer #ifndef NDEBUG
26044d4804dSStefan Eßer 			// We shouldn't get here because bc_opt_error()/bc_error() should
261252884aeSStefan Eßer 			// longjmp() out.
262252884aeSStefan Eßer 			case '?':
263252884aeSStefan Eßer 			case ':':
264252884aeSStefan Eßer 			default:
265252884aeSStefan Eßer 			{
26644d4804dSStefan Eßer 				BC_UNREACHABLE
267252884aeSStefan Eßer 				abort();
268252884aeSStefan Eßer 			}
269252884aeSStefan Eßer #endif // NDEBUG
270252884aeSStefan Eßer 		}
271252884aeSStefan Eßer 	}
272252884aeSStefan Eßer 
273252884aeSStefan Eßer 	if (version) bc_vm_info(NULL);
27444d4804dSStefan Eßer 	if (do_exit) {
27544d4804dSStefan Eßer 		vm.status = (sig_atomic_t) BC_STATUS_QUIT;
27644d4804dSStefan Eßer 		BC_JMP;
27744d4804dSStefan Eßer 	}
278252884aeSStefan Eßer 
27944d4804dSStefan Eßer 	// We do not print the banner if expressions are used or dc is used.
28044d4804dSStefan Eßer 	if (!BC_IS_BC || vm.exprs.len > 1) vm.flags &= ~(BC_FLAG_Q);
28144d4804dSStefan Eßer 
28244d4804dSStefan Eßer 	// We need to make sure the files list is initialized. We don't want to
28344d4804dSStefan Eßer 	// initialize it if there are no files because it's just a waste of memory.
2845d934bc0SStefan Eßer 	if (opts.optind < (size_t) argc && vm.files.v == NULL)
28544d4804dSStefan Eßer 		bc_vec_init(&vm.files, sizeof(char*), BC_DTOR_NONE);
286252884aeSStefan Eßer 
28744d4804dSStefan Eßer 	// Add all the files to the vector.
288252884aeSStefan Eßer 	for (i = opts.optind; i < (size_t) argc; ++i)
289252884aeSStefan Eßer 		bc_vec_push(&vm.files, argv + i);
290252884aeSStefan Eßer }
291