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 |