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 BC_SIG_ASSERT_LOCKED;
95
96 for (i = 0; i < bc_lex_kws_len; ++i) {
97
98 const BcLexKeyword *kw = bc_lex_kws + i;
99
100 if (!strcmp(keyword, kw->name)) {
101
102 if (BC_LEX_KW_POSIX(kw)) break;
103
104 vm.redefined_kws[i] = true;
105
106 return;
107 }
108 }
109
110 bc_error(BC_ERR_FATAL_ARG, 0, keyword);
111 }
112
113 #endif // BC_ENABLED
114
bc_args(int argc,char * argv[],bool exit_exprs)115 void bc_args(int argc, char *argv[], bool exit_exprs) {
116
117 int c;
118 size_t i;
119 bool do_exit = false, version = false;
120 BcOpt opts;
121
122 BC_SIG_ASSERT_LOCKED;
123
124 bc_opt_init(&opts, argv);
125
126 // This loop should look familiar to anyone who has used getopt() or
127 // getopt_long() in C.
128 while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1) {
129
130 switch (c) {
131
132 case 'e':
133 {
134 // Barf if not allowed.
135 if (vm.no_exprs)
136 bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)");
137
138 // Add the expressions and set exit.
139 bc_args_exprs(opts.optarg);
140 vm.exit_exprs = (exit_exprs || vm.exit_exprs);
141
142 break;
143 }
144
145 case 'f':
146 {
147 // Figure out if exiting on expressions is disabled.
148 if (!strcmp(opts.optarg, "-")) vm.no_exprs = true;
149 else {
150
151 // Barf if not allowed.
152 if (vm.no_exprs)
153 bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)");
154
155 // Add the expressions and set exit.
156 bc_args_file(opts.optarg);
157 vm.exit_exprs = (exit_exprs || vm.exit_exprs);
158 }
159
160 break;
161 }
162
163 case 'h':
164 {
165 bc_vm_info(vm.help);
166 do_exit = true;
167 break;
168 }
169
170 case 'i':
171 {
172 vm.flags |= BC_FLAG_I;
173 break;
174 }
175
176 case 'z':
177 {
178 vm.flags |= BC_FLAG_Z;
179 break;
180 }
181
182 case 'L':
183 {
184 vm.line_len = 0;
185 break;
186 }
187
188 case 'P':
189 {
190 vm.flags &= ~(BC_FLAG_P);
191 break;
192 }
193
194 case 'R':
195 {
196 vm.flags &= ~(BC_FLAG_R);
197 break;
198 }
199
200 #if BC_ENABLED
201 case 'g':
202 {
203 assert(BC_IS_BC);
204 vm.flags |= BC_FLAG_G;
205 break;
206 }
207
208 case 'l':
209 {
210 assert(BC_IS_BC);
211 vm.flags |= BC_FLAG_L;
212 break;
213 }
214
215 case 'q':
216 {
217 assert(BC_IS_BC);
218 vm.flags &= ~(BC_FLAG_Q);
219 break;
220 }
221
222 case 'r':
223 {
224 bc_args_redefine(opts.optarg);
225 break;
226 }
227
228 case 's':
229 {
230 assert(BC_IS_BC);
231 vm.flags |= BC_FLAG_S;
232 break;
233 }
234
235 case 'w':
236 {
237 assert(BC_IS_BC);
238 vm.flags |= BC_FLAG_W;
239 break;
240 }
241 #endif // BC_ENABLED
242
243 case 'V':
244 case 'v':
245 {
246 do_exit = version = true;
247 break;
248 }
249
250 #if DC_ENABLED
251 case 'x':
252 {
253 assert(BC_IS_DC);
254 vm.flags |= DC_FLAG_X;
255 break;
256 }
257 #endif // DC_ENABLED
258
259 #ifndef NDEBUG
260 // We shouldn't get here because bc_opt_error()/bc_error() should
261 // longjmp() out.
262 case '?':
263 case ':':
264 default:
265 {
266 BC_UNREACHABLE
267 abort();
268 }
269 #endif // NDEBUG
270 }
271 }
272
273 if (version) bc_vm_info(NULL);
274 if (do_exit) {
275 vm.status = (sig_atomic_t) BC_STATUS_QUIT;
276 BC_JMP;
277 }
278
279 // We do not print the banner if expressions are used or dc is used.
280 if (!BC_IS_BC || vm.exprs.len > 1) vm.flags &= ~(BC_FLAG_Q);
281
282 // We need to make sure the files list is initialized. We don't want to
283 // initialize it if there are no files because it's just a waste of memory.
284 if (opts.optind < (size_t) argc && vm.files.v == NULL)
285 bc_vec_init(&vm.files, sizeof(char*), BC_DTOR_NONE);
286
287 // Add all the files to the vector.
288 for (i = opts.optind; i < (size_t) argc; ++i)
289 bc_vec_push(&vm.files, argv + i);
290 }
291