Gwion coverage report


Directory: src/
File: src/arg.c
Date: 2023-01-30 18:32:28
Exec Total Coverage
Lines: 158 243 65.0%
Functions: 15 21 71.4%
Branches: 35 74 47.3%

Line Branch Exec Source
1 #include <errno.h>
2 #include "gwion_util.h"
3 #include "gwion_ast.h"
4 #include "gwion_env.h"
5 #include "soundinfo.h"
6 #include "vm.h"
7 #include "gwion.h"
8 #include "arg.h"
9 #include "pass.h"
10 #include "compile.h"
11 #include "cmdapp.h"
12
13 #define GWIONRC ".gwionrc"
14
15 enum {
16 CONFIG,
17 PLUGIN,
18 LOAD_PLUGIN,
19 MODULE,
20 LOOP,
21 PASS,
22 STDIN,
23 COLOR,
24 // sound options
25 DRIVER,
26 SRATE,
27 NINPUT,
28 NOUTPUT,
29 // pp options
30 DEFINE,
31 UNDEF,
32 INCLUDE,
33 DEBUG,
34 DUMP,
35 CDOC,
36 NOPTIONS
37 };
38
39 /* use before MemPool allocation */
40 707 ANN static inline void config_end(const Vector config) {
41
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 707 times.
707 for (m_uint i = 0; i < vector_size(config); ++i) {
42 const Vector v = (Vector)vector_at(config, i);
43 for (m_uint j = 0; j < vector_size(v); ++j) xfree((m_str)vector_at(v, j));
44 vector_release(v);
45 xfree(v);
46 }
47 707 }
48
49 707 ANN static m_str plug_dir(void) {
50 707 const m_str home = getenv("HOME");
51 707 const size_t sz = strlen(home);
52 707 const m_str pdir = "/.gwplug";
53 707 m_str plug_dir = (m_str)xmalloc(sz + strlen(pdir) + 1);
54 707 strcpy(plug_dir, home);
55 707 strcpy(plug_dir + sz, pdir);
56 707 return plug_dir;
57 }
58
59 enum arg_type {
60 ARG_FILE,
61 ARG_STDIN,
62 ARG_LOAD_PLUGIN,
63 ARG_DEFINE,
64 ARG_UNDEF,
65 ARG_INCLUDE,
66 ARG_DEBUG,
67 ARG_DUMP,
68 ARG_CDOC,
69 };
70
71 707 ANN static void arg_init(CliArg *arg) {
72 707 map_init(&arg->mod);
73 707 vector_init(&arg->add);
74 707 vector_init(&arg->lib);
75 707 vector_init(&arg->config);
76 #ifndef GWION_STANDALONE
77 707 vector_add(&arg->lib, (vtype)plug_dir());
78 #endif
79 707 arg->color = COLOR_AUTO;
80 707 }
81
82 707 ANN void arg_release(CliArg *arg) {
83 707 map_release(&arg->mod);
84 707 vector_release(&arg->add);
85 707 xfree((m_str)vector_front(&arg->lib));
86 707 vector_release(&arg->lib);
87 707 config_end(&arg->config);
88 707 vector_release(&arg->config);
89 707 }
90
91 static inline bool str2bool(const char *str) {
92 if (!str || !strcmp(str, "true")) return true;
93 if (!strcmp(str, "false")) return false;
94 char *rem = NULL;
95 long opt = strtol(str, &rem, 10);
96 if(rem || errno == EINVAL) {
97 gw_err("invalid argument for boolean option, setting to `false`\n");
98 return false;
99 }
100 return !!opt;
101 }
102
103 ANN static inline void get_debug(const Gwion gwion, const char *dbg) {
104 const bool is_dbg = str2bool(dbg);
105 gwion_set_debug(gwion, is_dbg);
106 }
107
108 ANN static inline void get_dump(const Gwion gwion, const char *dbg) {
109 const bool is_dbg = str2bool(dbg);
110 gwion_set_dump(gwion, is_dbg);
111 }
112
113 ANN static inline void get_cdoc(const Gwion gwion, const char *cdoc) {
114 const bool is_cdoc = str2bool(cdoc);
115 gwion_set_cdoc(gwion, is_cdoc);
116 }
117
118 ANN static inline void load_plugin(const Gwion gwion, const char *plug_name) {
119 char c[1024];
120 sprintf(c, "#import %s\n", plug_name);
121 const bool cdoc = gwion->data->cdoc;
122 gwion->data->cdoc = true;
123 compile_string(gwion, "<command-line>", c);
124 gwion->data->cdoc = cdoc;
125 }
126
127 707 ANN void arg_compile(const Gwion gwion, CliArg *arg) {
128 707 const Vector v = &arg->add;
129
2/2
✓ Branch 1 taken 1349 times.
✓ Branch 2 taken 707 times.
2056 for (m_uint i = 0; i < vector_size(v); i++) {
130
4/9
✓ Branch 1 taken 1346 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1349 switch (vector_at(v, i)) {
131 1346 case ARG_FILE:
132 1346 compile_filename(gwion, (m_str)VPTR(v, ++i));
133 1346 break;
134 case ARG_STDIN:
135 compile_file(gwion, "stdin", stdin);
136 break;
137 case ARG_LOAD_PLUGIN:
138 load_plugin(gwion, (m_str)VPTR(v, ++i));
139 break;
140 1 case ARG_DEFINE:
141 1 pparg_add(gwion->ppa, (m_str)VPTR(v, ++i));
142 1 break;
143 1 case ARG_UNDEF:
144 1 pparg_rem(gwion->ppa, (m_str)VPTR(v, ++i));
145 1 break;
146 1 case ARG_INCLUDE:
147 1 pparg_inc(gwion->ppa, (m_str)VPTR(v, ++i));
148 1 break;
149 case ARG_DEBUG:
150 get_debug(gwion, (m_str)VPTR(v, ++i));
151 break;
152 case ARG_DUMP:
153 get_debug(gwion, (m_str)VPTR(v, ++i));
154 break;
155 }
156 }
157 707 }
158
159 2 ANN2(1) static inline void arg_set_pass(const Gwion gwion, const char *str) {
160 2 const Vector v = split_args(gwion->mp, (const m_str)str);
161 2 pass_set(gwion, v);
162
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 for (m_uint i = 0; i < vector_size(v); ++i)
163 2 free_mstr(gwion->mp, (m_str)vector_at(v, i));
164 2 free_vector(gwion->mp, v);
165 2 }
166
167 48 ANN2(1) static void module_arg(const Map map, const char *str) {
168 48 m_str val = strchr(str, '=');
169
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 45 times.
48 if (val) {
170 3 *val = '\0';
171 3 ++val;
172 }
173 48 map_set(map, (vtype)str, (vtype)val);
174 48 }
175
176 707 static void setup_options(cmdapp_t *app, cmdopt_t *opt) {
177 707 cmdapp_set(app, 'C', "config", CMDOPT_TAKESARG, NULL, "parse a config file",
178 &opt[CONFIG]);
179 707 cmdapp_set(app, 'p', "plugdir", CMDOPT_TAKESARG, NULL,
180 "add ARG to the plugin search path", &opt[PLUGIN]);
181 707 cmdapp_set(app, 'm', "module", CMDOPT_TAKESARG, NULL,
182 "activate module (and arguments)", &opt[MODULE]);
183 707 cmdapp_set(app, 'l', "loop", CMDOPT_TAKESARG, NULL, "set loop mode",
184 &opt[LOOP]);
185 707 cmdapp_set(app, 'g', "passes", CMDOPT_TAKESARG, NULL, "set pass order",
186 &opt[PASS]);
187 707 cmdapp_set(app, '\0', "stdin", 0, NULL, "read from stdin", &opt[STDIN]);
188 707 cmdapp_set(app, 'c', "color", CMDOPT_TAKESARG, NULL,
189 "set colored output (never/auto/always, defaults to auto)",
190 &opt[COLOR]);
191 // sound options
192 707 cmdapp_set(app, 'd', "driver", CMDOPT_TAKESARG, NULL,
193 "set driver (and arguments)", &opt[DRIVER]);
194 707 cmdapp_set(app, 's', "samplerate", CMDOPT_TAKESARG, NULL,
195 "set the samplerate", &opt[SRATE]);
196 707 cmdapp_set(app, 'i', "input", CMDOPT_TAKESARG, NULL,
197 "number of input channel", &opt[NINPUT]);
198 707 cmdapp_set(app, 'o', "output", CMDOPT_TAKESARG, NULL,
199 "number of output channel", &opt[NOUTPUT]);
200 707 cmdapp_set(app, 'P', "plugin", CMDOPT_TAKESARG, NULL, "(force-)load a plugin",
201 &opt[LOAD_PLUGIN]);
202 707 cmdapp_set(app, 'D', "define", CMDOPT_TAKESARG, NULL, "define a macro",
203 &opt[DEFINE]);
204 707 cmdapp_set(app, 'U', "undef", CMDOPT_TAKESARG, NULL, "undefine a macro",
205 &opt[UNDEF]);
206 707 cmdapp_set(app, 'I', "include", CMDOPT_TAKESARG, NULL,
207 "add ARG to include path", &opt[INCLUDE]);
208 707 cmdapp_set(app, 'G', "debug", CMDOPT_MAYTAKEARG, NULL, "set/unset debug mode",
209 &opt[DEBUG]);
210 707 cmdapp_set(app, 'B', "dump", CMDOPT_MAYTAKEARG, NULL, "set/unset bytecode dump",
211 &opt[DUMP]);
212 707 cmdapp_set(app, 'H', "cdoc", CMDOPT_MAYTAKEARG, NULL, "set/unset cdoc mode",
213 &opt[CDOC]);
214 707 }
215
216 1349 static inline void add2arg(CliArg *const arg, const char *data,
217 const enum arg_type type) {
218 1349 vector_add(&arg->add, type);
219 1349 vector_add(&arg->add, (vtype)data);
220 1349 }
221
222 ANN static void split_line(const m_str line, const Vector v) {
223 m_str d = strdup(line), c = d;
224 while (d) {
225 const m_str str = strsep(&d, " ");
226 const size_t sz = strlen(str);
227 const m_bool arg = (str[sz - 1] == '\n');
228 vector_add(v, (vtype)strndup(str, arg ? sz - 1 : sz));
229 }
230 xfree(d);
231 xfree(c);
232 }
233
234 707 ANN static Vector get_config(const char *name) {
235 707 char * line = NULL;
236 707 size_t len = 0;
237 707 FILE * f = fopen(name, "r");
238
1/2
✓ Branch 0 taken 707 times.
✗ Branch 1 not taken.
707 CHECK_OO(f);
239 const Vector v = (Vector)xmalloc(sizeof(struct Vector_));
240 vector_init(v);
241 while (getline(&line, &len, f) != -1) {
242 if (line[0] != '#') split_line(line, v);
243 }
244 free(line);
245 fclose(f);
246 return v;
247 }
248
249 struct ArgInternal {
250 const Gwion gwion;
251 CliArg *arg;
252 };
253
254 ANN m_bool _arg_parse(struct ArgInternal *arg);
255
256 707 ANN static void config_parse(struct ArgInternal *arg, const char *name) {
257 707 const Vector v = get_config(name);
258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 707 times.
707 if (v) {
259 struct CArg ca = arg->arg->arg;
260 arg->arg->arg.argc = vector_size(v);
261 arg->arg->arg.argv = (m_str *)(v->ptr + OFFSET);
262 _arg_parse(arg);
263 arg->arg->arg = ca;
264 vector_add(&arg->arg->config, (vtype)v);
265 }
266 707 }
267
268 #define ARG2INT(a) strtol(a, NULL, 10)
269
270 2230 static void myproc(void *data, cmdopt_t *option, const char *arg) {
271 2230 struct ArgInternal *arg_int = data;
272 2230 CliArg * _arg = arg_int->arg;
273
2/2
✓ Branch 0 taken 2053 times.
✓ Branch 1 taken 177 times.
2230 if (arg) {
274
2/2
✓ Branch 0 taken 707 times.
✓ Branch 1 taken 1346 times.
2053 if (!_arg->arg.idx)
275 707 _arg->arg.idx++;
276 else
277 1346 add2arg(_arg, arg, ARG_FILE);
278 } else {
279
12/19
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 69 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
177 switch (option->shorto) {
280 49 case 'p':
281 49 vector_add(&_arg->lib, (vtype)option->value);
282 49 break;
283 48 case 'm':
284 48 module_arg(&_arg->mod, option->value);
285 48 break;
286 case 'C':
287 config_parse(arg_int, option->value);
288 break;
289 2 case 'l':
290 2 _arg->loop = ARG2INT(option->value) > 0 ? true : false;
291 2 break;
292 2 case 'g':
293 2 arg_set_pass(arg_int->gwion, option->value);
294 2 break;
295 case '\0':
296 vector_add(&_arg->add, (vtype)ARG_STDIN);
297 break;
298 case 'P':
299 vector_add(&_arg->add, (vtype)ARG_LOAD_PLUGIN);
300 vector_add(&_arg->add, (vtype)option->value);
301 break;
302 1 case 'c':
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!strcmp(option->value, "never"))
304 _arg->color = COLOR_NEVER;
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if (!strcmp(option->value, "auto"))
306 _arg->color = COLOR_AUTO;
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 else if (!strcmp(option->value, "always"))
308 _arg->color = COLOR_ALWAYS;
309 // ignore error silently
310 1 break;
311 // sound options
312 1 case 's':
313 1 _arg->si->sr = (uint32_t)ARG2INT(option->value);
314 1 break;
315 69 case 'd':
316 69 _arg->si->arg = (m_str)option->value;
317 69 break;
318 1 case 'i':
319 1 _arg->si->in = (uint8_t)ARG2INT(option->value);
320 1 break;
321 1 case 'o':
322 1 _arg->si->out = (uint8_t)ARG2INT(option->value);
323 1 break;
324 // pp options
325 1 case 'D':
326 1 add2arg(_arg, option->value, ARG_DEFINE);
327 1 break;
328 1 case 'U':
329 1 add2arg(_arg, option->value, ARG_UNDEF);
330 1 break;
331 1 case 'I':
332 1 add2arg(_arg, option->value, ARG_INCLUDE);
333 1 break;
334 // debug
335 case 'G':
336 add2arg(_arg, option->value, ARG_DEBUG);
337 break;
338 case 'B':
339 get_dump(arg_int->gwion, option->value);
340 break;
341 case 'H':
342 get_cdoc(arg_int->gwion, option->value);
343 break;
344 }
345 }
346 2230 }
347
348 #ifndef GWION_VERSION
349 #define GWION_VERSION "N.A."
350 #endif
351
352 707 ANN m_bool _arg_parse(struct ArgInternal *arg) {
353 cmdapp_t app;
354 707 const cmdapp_info_t info = {
355 .program = "gwion",
356 .synopses = NULL, // so it's automatic
357 .version = GWION_VERSION,
358 .author = "Jérémie Astor",
359 .year = 2016,
360 .description = "Strongly timed musical programming language.",
361 .help_des_offset = 28,
362 .ver_extra =
363 "License GPLv3+: GNU GPL version 3 or later "
364 "<https://gnu.org/licenses/gpl.html>\n"
365 "This is free software: you are free to change and redistribute it.\n"
366 "There is NO WARRANTY, to the extent permitted by law.\n"};
367 707 struct CArg *ca = &arg->arg->arg;
368 707 cmdapp_init(&app, ca->argc, ca->argv, CMDAPP_MODE_SHORTARG, &info);
369 707 cmdapp_enable_procedure(&app, myproc, arg);
370 cmdopt_t opt[NOPTIONS];
371 707 setup_options(&app, opt);
372
3/4
✓ Branch 1 taken 703 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 703 times.
707 if (cmdapp_run(&app) == EXIT_SUCCESS && cmdapp_should_exit(&app))
373 arg->arg->quit = 1;
374 707 cmdapp_destroy(&app);
375 707 return GW_OK;
376 }
377
378 707 ANN static void config_default(struct ArgInternal *arg) {
379 707 char *home = getenv("HOME");
380 707 char c[strlen(home) + strlen(GWIONRC) + 2];
381 707 sprintf(c, "%s/%s", home, GWIONRC);
382 707 config_parse(arg, c);
383 707 }
384
385 707 ANN m_bool arg_parse(const Gwion gwion, CliArg *a) {
386 707 struct ArgInternal arg = {.gwion = gwion, .arg = a};
387 707 arg_init(a);
388 #ifdef __FUZZING
389 return;
390 #endif
391 #ifndef GWION_STANDALONE
392 707 config_default(&arg);
393 #endif
394 707 return _arg_parse(&arg);
395 }
396