]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/src/args.c
zfs: merge openzfs/zfs@2e6b3c4d9
[FreeBSD/FreeBSD.git] / contrib / bc / src / args.c
1 /*
2  * *****************************************************************************
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2018-2023 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 #include <num.h>
51 #include <vm.h>
52
53 /**
54  * Adds @a str to the list of expressions to execute later.
55  * @param str  The string to add to the list of expressions.
56  */
57 static void
58 bc_args_exprs(const char* str)
59 {
60         BC_SIG_ASSERT_LOCKED;
61
62         if (vm->exprs.v == NULL)
63         {
64                 bc_vec_init(&vm->exprs, sizeof(uchar), BC_DTOR_NONE);
65         }
66
67         bc_vec_concat(&vm->exprs, str);
68         bc_vec_concat(&vm->exprs, "\n");
69 }
70
71 /**
72  * Adds the contents of @a file to the list of expressions to execute later.
73  * @param file  The name of the file whose contents should be added to the list
74  *              of expressions to execute.
75  */
76 static void
77 bc_args_file(const char* file)
78 {
79         char* buf;
80
81         BC_SIG_ASSERT_LOCKED;
82
83         vm->file = file;
84
85         buf = bc_read_file(file);
86
87         assert(buf != NULL);
88
89         bc_args_exprs(buf);
90         free(buf);
91 }
92
93 static BcBigDig
94 bc_args_builtin(const char* arg)
95 {
96         bool strvalid;
97         BcNum n;
98         BcBigDig res;
99
100         strvalid = bc_num_strValid(arg);
101
102         if (BC_ERR(!strvalid))
103         {
104                 bc_verr(BC_ERR_FATAL_ARG, arg);
105         }
106
107         bc_num_init(&n, 0);
108
109         bc_num_parse(&n, arg, 10);
110
111         res = bc_num_bigdig(&n);
112
113         bc_num_free(&n);
114
115         return res;
116 }
117
118 #if BC_ENABLED
119
120 /**
121  * Redefines a keyword, if it exists and is not a POSIX keyword. Otherwise, it
122  * throws a fatal error.
123  * @param keyword  The keyword to redefine.
124  */
125 static void
126 bc_args_redefine(const char* keyword)
127 {
128         size_t i;
129
130         BC_SIG_ASSERT_LOCKED;
131
132         for (i = 0; i < bc_lex_kws_len; ++i)
133         {
134                 const BcLexKeyword* kw = bc_lex_kws + i;
135
136                 if (!strcmp(keyword, kw->name))
137                 {
138                         if (BC_LEX_KW_POSIX(kw)) break;
139
140                         vm->redefined_kws[i] = true;
141
142                         return;
143                 }
144         }
145
146         bc_error(BC_ERR_FATAL_ARG, 0, keyword);
147 }
148
149 #endif // BC_ENABLED
150
151 void
152 bc_args(int argc, char* argv[], bool exit_exprs, BcBigDig* scale,
153         BcBigDig* ibase, BcBigDig* obase)
154 {
155         int c;
156         size_t i;
157         bool do_exit = false, version = false;
158         BcOpt opts;
159 #if BC_ENABLE_EXTRA_MATH
160         char* seed = NULL;
161 #endif // BC_ENABLE_EXTRA_MATH
162
163         BC_SIG_ASSERT_LOCKED;
164
165         bc_opt_init(&opts, argv);
166
167         // This loop should look familiar to anyone who has used getopt() or
168         // getopt_long() in C.
169         while ((c = bc_opt_parse(&opts, bc_args_lopt)) != -1)
170         {
171                 switch (c)
172                 {
173                         case 'c':
174                         {
175                                 vm->flags |= BC_FLAG_DIGIT_CLAMP;
176                                 break;
177                         }
178
179                         case 'C':
180                         {
181                                 vm->flags &= ~BC_FLAG_DIGIT_CLAMP;
182                                 break;
183                         }
184
185                         case 'e':
186                         {
187                                 // Barf if not allowed.
188                                 if (vm->no_exprs)
189                                 {
190                                         bc_verr(BC_ERR_FATAL_OPTION, "-e (--expression)");
191                                 }
192
193                                 // Add the expressions and set exit.
194                                 bc_args_exprs(opts.optarg);
195                                 vm->exit_exprs = (exit_exprs || vm->exit_exprs);
196
197                                 break;
198                         }
199
200                         case 'f':
201                         {
202                                 // Figure out if exiting on expressions is disabled.
203                                 if (!strcmp(opts.optarg, "-")) vm->no_exprs = true;
204                                 else
205                                 {
206                                         // Barf if not allowed.
207                                         if (vm->no_exprs)
208                                         {
209                                                 bc_verr(BC_ERR_FATAL_OPTION, "-f (--file)");
210                                         }
211
212                                         // Add the expressions and set exit.
213                                         bc_args_file(opts.optarg);
214                                         vm->exit_exprs = (exit_exprs || vm->exit_exprs);
215                                 }
216
217                                 break;
218                         }
219
220                         case 'h':
221                         {
222                                 bc_vm_info(vm->help);
223                                 do_exit = true;
224                                 break;
225                         }
226
227                         case 'i':
228                         {
229                                 vm->flags |= BC_FLAG_I;
230                                 break;
231                         }
232
233                         case 'I':
234                         {
235                                 *ibase = bc_args_builtin(opts.optarg);
236                                 break;
237                         }
238
239                         case 'z':
240                         {
241                                 vm->flags |= BC_FLAG_Z;
242                                 break;
243                         }
244
245                         case 'L':
246                         {
247                                 vm->line_len = 0;
248                                 break;
249                         }
250
251                         case 'O':
252                         {
253                                 *obase = bc_args_builtin(opts.optarg);
254                                 break;
255                         }
256
257                         case 'P':
258                         {
259                                 vm->flags &= ~(BC_FLAG_P);
260                                 break;
261                         }
262
263                         case 'R':
264                         {
265                                 vm->flags &= ~(BC_FLAG_R);
266                                 break;
267                         }
268
269                         case 'S':
270                         {
271                                 *scale = bc_args_builtin(opts.optarg);
272                                 break;
273                         }
274
275 #if BC_ENABLE_EXTRA_MATH
276                         case 'E':
277                         {
278                                 if (BC_ERR(!bc_num_strValid(opts.optarg)))
279                                 {
280                                         bc_verr(BC_ERR_FATAL_ARG, opts.optarg);
281                                 }
282
283                                 seed = opts.optarg;
284
285                                 break;
286                         }
287 #endif // BC_ENABLE_EXTRA_MATH
288
289 #if BC_ENABLED
290                         case 'g':
291                         {
292                                 assert(BC_IS_BC);
293                                 vm->flags |= BC_FLAG_G;
294                                 break;
295                         }
296
297                         case 'l':
298                         {
299                                 assert(BC_IS_BC);
300                                 vm->flags |= BC_FLAG_L;
301                                 break;
302                         }
303
304                         case 'q':
305                         {
306                                 assert(BC_IS_BC);
307                                 vm->flags &= ~(BC_FLAG_Q);
308                                 break;
309                         }
310
311                         case 'r':
312                         {
313                                 bc_args_redefine(opts.optarg);
314                                 break;
315                         }
316
317                         case 's':
318                         {
319                                 assert(BC_IS_BC);
320                                 vm->flags |= BC_FLAG_S;
321                                 break;
322                         }
323
324                         case 'w':
325                         {
326                                 assert(BC_IS_BC);
327                                 vm->flags |= BC_FLAG_W;
328                                 break;
329                         }
330 #endif // BC_ENABLED
331
332                         case 'V':
333                         case 'v':
334                         {
335                                 do_exit = version = true;
336                                 break;
337                         }
338
339 #if DC_ENABLED
340                         case 'x':
341                         {
342                                 assert(BC_IS_DC);
343                                 vm->flags |= DC_FLAG_X;
344                                 break;
345                         }
346 #endif // DC_ENABLED
347
348 #if BC_DEBUG
349                         // We shouldn't get here because bc_opt_error()/bc_error() should
350                         // longjmp() out.
351                         case '?':
352                         case ':':
353                         default:
354                         {
355                                 BC_UNREACHABLE
356 #if !BC_CLANG
357                                 abort();
358 #endif // !BC_CLANG
359                         }
360 #endif // BC_DEBUG
361                 }
362         }
363
364         if (version) bc_vm_info(NULL);
365         if (do_exit)
366         {
367                 vm->status = (sig_atomic_t) BC_STATUS_QUIT;
368                 BC_JMP;
369         }
370
371         // We do not print the banner if expressions are used or dc is used.
372         if (BC_ARGS_SHOULD_BE_QUIET) vm->flags &= ~(BC_FLAG_Q);
373
374         // We need to make sure the files list is initialized. We don't want to
375         // initialize it if there are no files because it's just a waste of memory.
376         if (opts.optind < (size_t) argc && vm->files.v == NULL)
377         {
378                 bc_vec_init(&vm->files, sizeof(char*), BC_DTOR_NONE);
379         }
380
381         // Add all the files to the vector.
382         for (i = opts.optind; i < (size_t) argc; ++i)
383         {
384                 bc_vec_push(&vm->files, argv + i);
385         }
386
387 #if BC_ENABLE_EXTRA_MATH
388         if (seed != NULL)
389         {
390                 BcNum n;
391
392                 bc_num_init(&n, strlen(seed));
393
394                 BC_SIG_UNLOCK;
395
396                 bc_num_parse(&n, seed, BC_BASE);
397
398                 bc_program_assignSeed(&vm->prog, &n);
399
400                 BC_SIG_LOCK;
401
402                 bc_num_free(&n);
403         }
404 #endif // BC_ENABLE_EXTRA_MATH
405 }