1 /*
2 * *****************************************************************************
3 *
4 * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
5 *
6 * All rights reserved.
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 <ctype.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include <getopt.h>
43
44 #include <status.h>
45 #include <vector.h>
46 #include <read.h>
47 #include <vm.h>
48 #include <args.h>
49
50 static const struct option bc_args_lopt[] = {
51
52 { "expression", required_argument, NULL, 'e' },
53 { "file", required_argument, NULL, 'f' },
54 { "help", no_argument, NULL, 'h' },
55 { "interactive", no_argument, NULL, 'i' },
56 { "no-prompt", no_argument, NULL, 'P' },
57 #if BC_ENABLED
58 { "global-stacks", no_argument, NULL, 'g' },
59 { "mathlib", no_argument, NULL, 'l' },
60 { "quiet", no_argument, NULL, 'q' },
61 { "standard", no_argument, NULL, 's' },
62 { "warn", no_argument, NULL, 'w' },
63 #endif // BC_ENABLED
64 { "version", no_argument, NULL, 'v' },
65 #if DC_ENABLED
66 { "extended-register", no_argument, NULL, 'x' },
67 #endif // DC_ENABLED
68 { 0, 0, 0, 0 },
69
70 };
71
72 #if !BC_ENABLED
73 static const char* const bc_args_opt = "e:f:hiPvVx";
74 #elif !DC_ENABLED
75 static const char* const bc_args_opt = "e:f:ghilPqsvVw";
76 #else // BC_ENABLED && DC_ENABLED
77 static const char* const bc_args_opt = "e:f:ghilPqsvVwx";
78 #endif // BC_ENABLED && DC_ENABLED
79
bc_args_exprs(BcVec * exprs,const char * str)80 static void bc_args_exprs(BcVec *exprs, const char *str) {
81 bc_vec_concat(exprs, str);
82 bc_vec_concat(exprs, "\n");
83 }
84
bc_args_file(BcVec * exprs,const char * file)85 static BcStatus bc_args_file(BcVec *exprs, const char *file) {
86
87 BcStatus s;
88 char *buf;
89
90 vm->file = file;
91
92 s = bc_read_file(file, &buf);
93 if (BC_ERR(s)) return s;
94
95 bc_args_exprs(exprs, buf);
96 free(buf);
97
98 return s;
99 }
100
bc_args(int argc,char * argv[])101 BcStatus bc_args(int argc, char *argv[]) {
102
103 BcStatus s = BC_STATUS_SUCCESS;
104 int c, i, err = 0;
105 bool do_exit = false, version = false;
106
107 i = optind = 0;
108
109 while ((c = getopt_long(argc, argv, bc_args_opt, bc_args_lopt, &i)) != -1) {
110
111 switch (c) {
112
113 case 0:
114 {
115 // This is the case when a long option is found.
116 break;
117 }
118
119 case 'e':
120 {
121 bc_args_exprs(&vm->exprs, optarg);
122 break;
123 }
124
125 case 'f':
126 {
127 s = bc_args_file(&vm->exprs, optarg);
128 if (BC_ERR(s)) return s;
129 break;
130 }
131
132 case 'h':
133 {
134 bc_vm_info(vm->help);
135 do_exit = true;
136 break;
137 }
138
139 case 'i':
140 {
141 vm->flags |= BC_FLAG_I;
142 break;
143 }
144
145 case 'P':
146 {
147 vm->flags |= BC_FLAG_P;
148 break;
149 }
150
151 #if BC_ENABLED
152 case 'g':
153 {
154 if (BC_ERR(!BC_IS_BC)) err = c;
155 vm->flags |= BC_FLAG_G;
156 break;
157 }
158
159 case 'l':
160 {
161 if (BC_ERR(!BC_IS_BC)) err = c;
162 vm->flags |= BC_FLAG_L;
163 break;
164 }
165
166 case 'q':
167 {
168 if (BC_ERR(!BC_IS_BC)) err = c;
169 vm->flags |= BC_FLAG_Q;
170 break;
171 }
172
173 case 's':
174 {
175 if (BC_ERR(!BC_IS_BC)) err = c;
176 vm->flags |= BC_FLAG_S;
177 break;
178 }
179
180 case 'w':
181 {
182 if (BC_ERR(!BC_IS_BC)) err = c;
183 vm->flags |= BC_FLAG_W;
184 break;
185 }
186 #endif // BC_ENABLED
187
188 case 'V':
189 case 'v':
190 {
191 do_exit = version = true;
192 break;
193 }
194
195 #if DC_ENABLED
196 case 'x':
197 {
198 if (BC_ERR(BC_IS_BC)) err = c;
199 vm->flags |= DC_FLAG_X;
200 break;
201 }
202 #endif // DC_ENABLED
203
204 // Getopt printed an error message, but we should exit.
205 case '?':
206 default:
207 {
208 return BC_STATUS_ERROR_FATAL;
209 }
210 }
211
212 if (BC_ERR(err)) {
213
214 for (i = 0; bc_args_lopt[i].name != NULL; ++i) {
215 if (bc_args_lopt[i].val == err) break;
216 }
217
218 return bc_vm_verr(BC_ERROR_FATAL_OPTION, err, bc_args_lopt[i].name);
219 }
220 }
221
222 if (version) bc_vm_info(NULL);
223 if (do_exit) exit((int) s);
224 if (vm->exprs.len > 1 || !BC_IS_BC) vm->flags |= BC_FLAG_Q;
225 if (argv[optind] != NULL && !strcmp(argv[optind], "--")) ++optind;
226
227 for (i = optind; i < argc; ++i) bc_vec_push(&vm->files, argv + i);
228
229 return s;
230 }
231