]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/src/args.c
Merge llvm-project release/14.x llvmorg-14.0.0-rc1-74-g4dc3cb8e3255
[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
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  */
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  */
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  */
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
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 }