1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * Code for processing command-line arguments.
33 *
34 */
35
36 #include <assert.h>
37 #include <ctype.h>
38 #include <stdbool.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #ifndef _WIN32
43 #include <unistd.h>
44 #endif // _WIN32
45
46 #include <vector.h>
47 #include <read.h>
48 #include <args.h>
49 #include <opt.h>
50
51 /**
52 * Adds @a str to the list of expressions to execute later.
53 * @param str The string to add to the list of expressions.
54 */
bc_args_exprs(const char * str)55 static void bc_args_exprs(const char *str) {
56 BC_SIG_ASSERT_LOCKED;
57 if (vm.exprs.v == NULL) bc_vec_init(&vm.exprs, sizeof(uchar), BC_DTOR_NONE);
58 bc_vec_concat(&vm.exprs, str);
59 bc_vec_concat(&vm.exprs, "\n");
60 }
61
62 /**
63 * Adds the contents of @a file to the list of expressions to execute later.
64 * @param file The name of the file whose contents should be added to the list
65 * of expressions to execute.
66 */
bc_args_file(const char * file)67 static void bc_args_file(const char *file) {
68
69 char *buf;
70
71 BC_SIG_ASSERT_LOCKED;
72
73 vm.file = file;
74
75 buf = bc_read_file(file);
76
77 assert(buf != NULL);
78
79 bc_args_exprs(buf);
80 free(buf);
81 }
82
83 #if BC_ENABLED
84
85 /**
86 * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it
87 * throws a fatal error.
88 * @param keyword The keyword to redefine.
89 */
bc_args_redefine(const char * keyword)90 static void bc_args_redefine(const char *keyword) {
91
92 size_t i;
93
94 for (i = 0; i < bc_lex_kws_len; ++i) {
95
96 const BcLexKeyword *kw = bc_lex_kws + i;
97
98 if (!strcmp(keyword, kw->name)) {
99
100 if (BC_LEX_KW_POSIX(kw)) break;
101
102 vm.redefined_kws[i] = true;
103
104 return;
105 }
106 }
107
108 bc_error(BC_ERR_FATAL_ARG, 0, keyword);
109 }
110
111 #endif // BC_ENABLED
112
bc_args(int argc,char * argv[],bool exit_exprs)113 void bc_args(int argc, char *argv[], bool exit_exprs) {
114
115 int c;
116 size_t i;
117 bool do_exit = false, version = false;
118 BcOpt opts;
119
120 BC_SIG_ASSERT_LOCKED;
121
122 bc_opt_init(&opts, argv);
123
124 // This loop should look familiar to anyone who has used getopt() or
125 // getopt_long() in C.
126 while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) {
127
128 switch (c) {
129
130 case 'e':
131 {
132 // Barf if not allowed.
133 if (vm.no_exprs)
134 bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)");
135
136 // Add the expressions and set exit.
137 bc_args_exprs(opts.optarg);
138 vm.exit_exprs = (exit_exprs || vm.exit_exprs);
139
140 break;
141 }
142
143 case 'f':
144 {
145 // Figure out if exiting on expressions is disabled.
146 if (!strcmp(opts.optarg, "-")) vm.no_exprs = true;
147 else {
148
149 // Barf if not allowed.
150 if (vm.no_exprs)
151 bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)");
152
153 // Add the expressions and set exit.
154 bc_args_file(opts.optarg);
155 vm.exit_exprs = (exit_exprs || vm.exit_exprs);
156 }
157
158 break;
159 }
160
161 case 'h':
162 {
163 bc_vm_info(vm.help);
164 do_exit = true;
165 break;
166 }
167
168 case 'i':
169 {
170 vm.flags |= BC_FLAG_I;
171 break;
172 }
173
174 case 'P':
175 {
176 vm.flags &= ~(BC_FLAG_P);
177 break;
178 }
179
180 case 'R':
181 {
182 vm.flags &= ~(BC_FLAG_R);
183 break;
184 }
185
186 #if BC_ENABLED
187 case 'g':
188 {
189 assert(BC_IS_BC);
190 vm.flags |= BC_FLAG_G;
191 break;
192 }
193
194 case 'l':
195 {
196 assert(BC_IS_BC);
197 vm.flags |= BC_FLAG_L;
198 break;
199 }
200
201 case 'q':
202 {
203 assert(BC_IS_BC);
204 // Do nothing.
205 break;
206 }
207
208 case 'r':
209 {
210 bc_args_redefine(opts.optarg);
211 break;
212 }
213
214 case 's':
215 {
216 assert(BC_IS_BC);
217 vm.flags |= BC_FLAG_S;
218 break;
219 }
220
221 case 'w':
222 {
223 assert(BC_IS_BC);
224 vm.flags |= BC_FLAG_W;
225 break;
226 }
227 #endif // BC_ENABLED
228
229 case 'V':
230 case 'v':
231 {
232 do_exit = version = true;
233 break;
234 }
235
236 #if DC_ENABLED
237 case 'x':
238 {
239 assert(BC_IS_DC);
240 vm.flags |= DC_FLAG_X;
241 break;
242 }
243 #endif // DC_ENABLED
244
245 #ifndef NDEBUG
246 // We shouldn't get here because bc_opt_error()/bc_error() should
247 // longjmp() out.
248 case '?':
249 case ':':
250 default:
251 {
252 BC_UNREACHABLE
253 abort();
254 }
255 #endif // NDEBUG
256 }
257 }
258
259 if (version) bc_vm_info(NULL);
260 if (do_exit) {
261 vm.status = (sig_atomic_t) BC_STATUS_QUIT;
262 BC_JMP;
263 }
264
265 // We do not print the banner if expressions are used or dc is used.
266 if (!BC_IS_BC || vm.exprs.len > 1) vm.flags &= ~(BC_FLAG_Q);
267
268 // We need to make sure the files list is initialized. We don't want to
269 // initialize it if there are no files because it's just a waste of memory.
270 if (opts.optind < (size_t) argc && vm.files.v == NULL)
271 bc_vec_init(&vm.files, sizeof(char*), BC_DTOR_NONE);
272
273 // Add all the files to the vector.
274 for (i = opts.optind; i < (size_t) argc; ++i)
275 bc_vec_push(&vm.files, argv + i);
276 }
277