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