GCC Code Coverage Report


Directory: ./
File: common/src/config.c
Date: 2026-11-08 20:01:15
Exec Total Coverage
Lines: 658 775 84.9%
Functions: 32 32 100.0%
Branches: 365 547 66.7%

Line Branch Exec Source
1 /*
2 * Common code for hanlding command line arguments and config files.
3 * Copyright © 2005-2026, albinoloverats ~ Software Development
4 * email: encrypt@albinoloverats.net
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <errno.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <getopt.h>
27
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdbool.h>
31
32 #include <gcrypt.h>
33
34 #ifndef _WIN32
35 #include <sys/utsname.h>
36 #include <sys/ioctl.h>
37 #ifdef __sun
38 #include <sys/tty.h>
39 #endif
40 #endif
41
42 #include "common.h"
43 #include "non-gnu.h"
44 #include "error.h"
45 #include "mem.h"
46 #include "ccrypt.h"
47 #include "version.h"
48 #include "cli.h"
49 #include "config.h"
50 #include "pair.h"
51 #include "list.h"
52
53 #ifdef _WIN32
54 #include <Shlobj.h>
55 extern char *program_invocation_short_name;
56 #endif
57
58
59 static void show_version(list_t args, list_t largs, list_t notes, list_t extra) __attribute__((noreturn));
60 static void show_help(list_t args, list_t largs, list_t notes, list_t extra) __attribute__((noreturn));
61 static void show_licence(list_t args, list_t largs, list_t notes, list_t extra) __attribute__((noreturn));
62
63 inline static bool is_argument(char, const char *, const char *);
64 inline static void format_section(char *);
65 inline static void print_usage(list_t, list_t);
66
67 static int64_t parse_number_size_suffix(const char *s);
68
69 static bool parse_boolean(const char *, const char *, bool *);
70 static bool parse_integer(const char *, const char *, int64_t *);
71 //static bool parse_decimal(const char *, const char *, __float128 *);
72 static bool parse_decimal(const char *, const char *, long double *);
73 static char *parse_string (const char *, const char *, char *);
74
75 static bool parse_pair_boolean(const char *c, const char *l, pair_boolean_s *);
76 static bool parse_pair_integer(const char *c, const char *l, pair_integer_s *);
77 static bool parse_pair_decimal(const char *c, const char *l, pair_decimal_s *);
78 static bool parse_pair_string (const char *c, const char *l, pair_string_s *);
79
80 static pair_u *parse_pair(const char *c, const char *l);
81
82 static char *parse_tail(const char *, const char *);
83
84 static void parse_list_boolean(const char *, list_t list);
85 static void parse_list_integer(const char *, list_t list);
86 static void parse_list_decimal(const char *, list_t list);
87
88 static void parse_list(config_arg_e, const char *, list_t);
89
90 static bool init = false;
91 static config_about_s about = { 0x0 };
92
93
94 7 extern void config_init(config_about_s a)
95 {
96 7 init = true;
97 7 memcpy(&about, &a, sizeof about);
98 7 return;
99 }
100
101 200 extern int config_named_compare(const void *a, const void *b)
102 {
103 200 const config_named_s *x = a;
104 200 const config_named_s *y = b;
105 200 return x->short_option - y->short_option;
106 }
107
108 1 extern int config_unnamed_compare(const void *a, const void *b)
109 {
110 1 const config_unnamed_s *x = a;
111 1 const config_unnamed_s *y = b;
112 1 return strcmp(x->description, y->description);
113 }
114
115 31 extern void config_named_free(void *f)
116 {
117 31 config_named_s *x = (config_named_s *)f;
118
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 17 times.
31 if (x->response.type & CONFIG_ARG_LIST)
119 14 list_deinit(x->response.value.list, free);
120
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17 else if (x->response.type & CONFIG_ARG_STRING)
121 4 free(x->response.value.string);
122 31 free(x);
123 31 return;
124 }
125
126 1 extern void config_unnamed_free(void *f)
127 {
128 1 config_unnamed_s *x = (config_unnamed_s *)f;
129
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (x->response.type & CONFIG_ARG_STRING)
130 1 free(x->response.value.string);
131 1 free(x);
132 1 return;
133 }
134
135 7 extern int config_parse_aux(int argc, char **argv, list_t args, list_t extra, list_t notes, bool warn)
136 {
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!init)
138 {
139 ✗ cli_eprintf(_("Call config_init() first\n"));
140 ✗ return -1;
141 }
142
143 7 list_t largs = list_init((void *)strcmp, true, false);
144
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 7 times.
31 for (int i = 1; i < argc; i++) // from 1, skip invokation name
145 {
146 24 char *x = argv[i];
147
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 1 times.
24 if (x[0] == '-' && x[1] == '-')
148 19 {
149 // long option, check for '='
150 19 char *o = strchr(x, '=');
151
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
19 if (o)
152 {
153 14 list_append(largs, m_strndup(x, o - x));
154 14 list_append(largs, m_strdup(o + 1));
155 }
156 else
157 5 list_append(largs, m_strdup(argv[i]));
158 }
159
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
5 else if (x[0] == '-' && strlen(x) > 2)
160 {
161 // short option, check for anything after '-x...'
162 1 list_append(largs, m_strndup(x, 2));
163 1 list_append(largs, m_strdup(x + 2));
164 }
165 else
166 4 list_append(largs, m_strdup(argv[i]));
167 }
168 /* handle help et al first */
169
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (list_contains(largs, "-h") || list_contains(largs, "--help"))
170 1 show_help(args, largs, notes, extra);
171
3/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
6 if (list_contains(largs, "-v") || list_contains(largs, "--version"))
172 1 show_version(args, largs, notes, extra);
173
3/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (list_contains(largs, "-l") || list_contains(largs, "--licence"))
174 1 show_licence(args, largs, notes, extra);
175
176 /*
177 * check for options in rc file
178 */
179
3/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
4 if (args && about.config != NULL)
180 {
181 2 char *rc = NULL;
182 #ifndef __DEBUG__
183 #ifndef _WIN32
184 ✗ if (about.config[0] == '/' || (about.config[0] == '.' && about.config[1] == '/'))
185 ✗ rc = m_strdup(about.config);
186 else
187 ✗ rc = m_strdupf("%s/%s", getenv("HOME") ? : ".", about.config);
188 #else
189 rc = m_calloc(MAX_PATH, sizeof( char ));
190 SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, rc);
191 strcat(rc, "\\");
192 strcat(rc, about.config);
193 #endif
194 #else
195 2 rc = m_strdup(about.config);
196 #endif
197 2 FILE *f = fopen(rc, "rb");
198
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (f)
199 {
200 2 char *line = NULL;
201 2 size_t len = 0;
202
203
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 2 times.
21 while (getline(&line, &len, f) >= 0)
204 {
205
3/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 18 times.
19 if (len == 0 || line[0] == '#')
206 1 goto end_line;
207
208 // TODO handle unknown config file settings
209 18 iter_t iter = list_iterator(args);
210
2/2
✓ Branch 1 taken 194 times.
✓ Branch 2 taken 18 times.
212 while (list_has_next(iter))
211 {
212 194 config_named_s *arg = (config_named_s *)list_get_next(iter);
213
5/6
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 173 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 3 times.
194 if (arg->long_option && !strncmp(arg->long_option, line, strlen(arg->long_option)) && isspace((unsigned char)line[strlen(arg->long_option)]))
214
13/14
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
18 switch (arg->response.type)
215 {
216 /*
217 * optional values
218 */
219 2 case CONFIG_ARG_OPT_BOOLEAN:
220 (void)0; // for Slackware's older GCC
221 __attribute__((fallthrough)); /* allow fall-through */
222 case CONFIG_ARG_REQ_BOOLEAN:
223 2 arg->seen = parse_boolean(arg->long_option, line, &arg->response.value.boolean);
224 2 break;
225
226 1 case CONFIG_ARG_OPT_INTEGER:
227 (void)0; // for Slackware's older GCC
228 __attribute__((fallthrough)); /* allow fall-through */
229 case CONFIG_ARG_REQ_INTEGER:
230 1 arg->seen = parse_integer(arg->long_option, line, &arg->response.value.integer);
231 1 break;
232
233 1 case CONFIG_ARG_OPT_DECIMAL:
234 (void)0; // for Slackware's older GCC
235 __attribute__((fallthrough)); /* allow fall-through */
236 case CONFIG_ARG_REQ_DECIMAL:
237 1 arg->seen = parse_decimal(arg->long_option, line, &arg->response.value.decimal);
238 1 break;
239
240 1 case CONFIG_ARG_OPT_STRING:
241 (void)0; // for Slackware's older GCC
242 __attribute__((fallthrough)); /* allow fall-through */
243 case CONFIG_ARG_REQ_STRING:
244 1 arg->seen = true;
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (arg->response.value.string)
246 ✗ free(arg->response.value.string);
247 1 arg->response.value.string = parse_string(arg->long_option, line, arg->response.value.string);
248 1 break;
249
250 /*
251 * pairs of values
252 */
253 1 case CONFIG_ARG_PAIR_BOOLEAN:
254 1 arg->seen = parse_pair_boolean(arg->long_option, line, &arg->response.value.pair.boolean);
255 1 break;
256
257 1 case CONFIG_ARG_PAIR_INTEGER:
258 1 arg->seen = parse_pair_integer(arg->long_option, line, &arg->response.value.pair.integer);
259 1 break;
260
261 1 case CONFIG_ARG_PAIR_DECIMAL:
262 1 arg->seen = parse_pair_decimal(arg->long_option, line, &arg->response.value.pair.decimal);
263 1 break;
264
265 1 case CONFIG_ARG_PAIR_STRING:
266 1 arg->seen = parse_pair_string(arg->long_option, line, &arg->response.value.pair.string);
267 1 break;
268
269 /*
270 * lists of values
271 */
272 2 case CONFIG_ARG_LIST_BOOLEAN:
273
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!arg->seen && arg->response.value.list)
274 {
275 ✗ free(arg->response.value.list);
276 ✗ arg->response.value.list = NULL;
277 }
278 2 arg->seen = true;
279
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!arg->response.value.list)
280 2 arg->response.value.list = list_default();
281
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(line, ','))
282 1 parse_list(CONFIG_ARG_BOOLEAN, line + strlen(arg->long_option) + 1, arg->response.value.list);
283 else
284 1 parse_list_boolean(line + strlen(arg->long_option) + 1, arg->response.value.list);
285 2 break;
286
287 2 case CONFIG_ARG_LIST_INTEGER:
288
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!arg->seen && arg->response.value.list)
289 {
290 ✗ free(arg->response.value.list);
291 ✗ arg->response.value.list = NULL;
292 }
293 2 arg->seen = true;
294
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!arg->response.value.list)
295 2 arg->response.value.list = list_default();
296
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(line, ','))
297 1 parse_list(CONFIG_ARG_INTEGER, line + strlen(arg->long_option) + 1, arg->response.value.list);
298 else
299 1 parse_list_integer(line + strlen(arg->long_option) + 1, arg->response.value.list);
300 2 break;
301
302 2 case CONFIG_ARG_LIST_DECIMAL:
303
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!arg->seen && arg->response.value.list)
304 {
305 ✗ free(arg->response.value.list);
306 ✗ arg->response.value.list = NULL;
307 }
308 2 arg->seen = true;
309
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!arg->response.value.list)
310 2 arg->response.value.list = list_default();
311
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(line, ','))
312 1 parse_list(CONFIG_ARG_DECIMAL, line + strlen(arg->long_option) + 1, arg->response.value.list);
313 else
314 1 parse_list_decimal(line + strlen(arg->long_option) + 1, arg->response.value.list);
315 2 break;
316
317 2 case CONFIG_ARG_LIST_STRING:
318
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (!arg->seen && arg->response.value.list)
319 {
320 ✗ free(arg->response.value.list);
321 ✗ arg->response.value.list = NULL;
322 }
323 2 arg->seen = true;
324
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!arg->response.value.list)
325 2 arg->response.value.list = list_default();
326
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(line, ','))
327 1 parse_list(CONFIG_ARG_STRING, line + strlen(arg->long_option) + 1, arg->response.value.list);
328 else
329 1 list_append(arg->response.value.list, parse_string(arg->long_option, line, NULL));
330 2 break;
331
332 /*
333 * lists of pairs
334 */
335 1 case CONFIG_ARG_LIST_PAIR_STRING:
336 {
337
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!arg->seen && arg->response.value.list)
338 {
339 ✗ free(arg->response.value.list);
340 ✗ arg->response.value.list = NULL;
341 }
342 1 arg->seen = true;
343
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!arg->response.value.list)
344 1 arg->response.value.list = list_default();
345 // would this not be a map?
346 1 pair_string_s *pair = m_malloc(sizeof( pair_string_s ));
347
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (parse_pair_string(arg->long_option, line, pair))
348
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!list_append(arg->response.value.list, pair))
349 ✗ free(pair);
350 }
351 1 break;
352
353 ✗ default:
354 ✗ break;
355 }
356 }
357 18 free(iter);
358 19 end_line:
359 19 free(line);
360 19 line = NULL;
361 19 len = 0;
362 }
363 2 fclose(f);
364 2 free(line);
365 }
366 2 free(rc);
367 }
368
369
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 4 times.
25 for (size_t i = 0, j = 0; i < list_size(largs); i++)
370 {
371 21 bool unknown = true;
372 21 const char *curr = list_get(largs, i);
373 21 const char *next = list_get(largs, i + 1);
374
375 21 iter_t iter = list_iterator(args);
376
2/2
✓ Branch 1 taken 153 times.
✓ Branch 2 taken 4 times.
157 while (list_has_next(iter))
377 {
378 153 config_named_s *arg = (config_named_s *)list_get_next(iter);
379
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 136 times.
153 if (is_argument(arg->short_option, arg->long_option, curr))
380 {
381 17 unknown = false;
382
5/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
17 if (next && (arg->response.type & CONFIG_ARG_REQUIRED || next[0] != '-'))
383 15 i++;
384 else
385 2 next = NULL;
386
11/12
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
17 switch (arg->response.type)
387 {
388 2 case CONFIG_ARG_OPT_BOOLEAN:
389 (void)0; // for Slackware's older GCC
390 __attribute__((fallthrough)); /* allow fall-through; argument was seen */
391 case CONFIG_ARG_REQ_BOOLEAN:
392 2 arg->seen = true;
393
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (next)
394 1 parse_boolean(NULL, next, &arg->response.value.boolean);
395 else
396 1 arg->response.value.boolean = !arg->response.value.boolean;
397 2 break;
398
399 2 case CONFIG_ARG_OPT_INTEGER:
400 2 arg->seen = true;
401
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!next)
402 1 break;
403 __attribute__((fallthrough)); /* allow fall-through; argument was seen and has a value */
404 case CONFIG_ARG_REQ_INTEGER:
405 2 arg->seen = parse_integer(NULL, next, &arg->response.value.integer);
406 2 break;
407
408 1 case CONFIG_ARG_OPT_DECIMAL:
409 1 arg->seen = true;
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!next)
411 ✗ break;
412 __attribute__((fallthrough)); /* allow fall-through; argument was seen and has a value */
413 case CONFIG_ARG_REQ_DECIMAL:
414 2 arg->seen = parse_decimal(NULL, next, &arg->response.value.decimal);
415 2 break;
416
417 1 case CONFIG_ARG_OPT_STRING:
418 1 arg->seen = true;
419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!next)
420 ✗ break;
421 __attribute__((fallthrough)); /* allow fall-through; argument was seen and has a value */
422 case CONFIG_ARG_REQ_STRING:
423 2 arg->seen = true;
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (arg->response.value.string)
425 ✗ free(arg->response.value.string);
426 2 arg->response.value.string = m_strdup(next);
427 2 break;
428
429 2 case CONFIG_ARG_LIST_BOOLEAN:
430
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (!arg->seen && arg->response.value.list)
431 {
432 ✗ free(arg->response.value.list);
433 ✗ arg->response.value.list = NULL;
434 }
435 2 arg->seen = true;
436
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!arg->response.value.list)
437 1 arg->response.value.list = list_default();
438
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(next, ','))
439 1 parse_list(CONFIG_ARG_BOOLEAN, next, arg->response.value.list);
440 else
441 1 parse_list_boolean(next, arg->response.value.list);
442 2 break;
443
444 2 case CONFIG_ARG_LIST_INTEGER:
445
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (!arg->seen && arg->response.value.list)
446 {
447 ✗ free(arg->response.value.list);
448 ✗ arg->response.value.list = NULL;
449 }
450 2 arg->seen = true;
451
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!arg->response.value.list)
452 1 arg->response.value.list = list_default();
453
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(next, ','))
454 1 parse_list(CONFIG_ARG_INTEGER, next, arg->response.value.list);
455 else
456 1 parse_list_integer(next, arg->response.value.list);
457 2 break;
458
459 2 case CONFIG_ARG_LIST_DECIMAL:
460
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (!arg->seen && arg->response.value.list)
461 {
462 ✗ free(arg->response.value.list);
463 ✗ arg->response.value.list = NULL;
464 }
465 2 arg->seen = true;
466
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!arg->response.value.list)
467 1 arg->response.value.list = list_default();
468
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(next, ','))
469 1 parse_list(CONFIG_ARG_DECIMAL, next, arg->response.value.list);
470 else
471 1 parse_list_decimal(next, arg->response.value.list);
472 2 break;
473
474 2 case CONFIG_ARG_LIST_STRING:
475
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (!arg->seen && arg->response.value.list)
476 {
477 ✗ free(arg->response.value.list);
478 ✗ arg->response.value.list = NULL;
479 }
480 2 arg->seen = true;
481
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!arg->response.value.list)
482 1 arg->response.value.list = list_default();
483
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (strchr(next, ','))
484 {
485 1 char *s = m_strdup(next);
486 1 char *u = m_strdup(strtok(s, ","));
487
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!list_append(arg->response.value.list, u))
488 ✗ free(u);
489 1 char *t = NULL;
490
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 while ((t = strtok(NULL, ",")))
491 {
492 1 char *v = m_strdup(t);
493
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!list_append(arg->response.value.list, v))
494 ✗ free(v);
495 }
496 1 free(s);
497 }
498 else
499 {
500 1 char *x = m_strdup(next);
501
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!list_append(arg->response.value.list, x))
502 ✗ free(x);
503 }
504 2 break;
505
506 /* TODO extend handling of pairs and list of pairs... */
507
508 ✗ default:
509 ✗ arg->response.value.boolean = !arg->response.value.boolean;
510 ✗ break;
511 }
512 17 break;
513 }
514 }
515 21 free(iter);
516
517
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17 times.
21 if (unknown)
518 {
519
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (extra)
520 {
521
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (j >= list_size(extra))
522 {
523 ✗ config_unnamed_s *new = m_calloc(1, sizeof( config_unnamed_s ));
524 ✗ new->response.type = CONFIG_ARG_STRING;
525 ✗ list_append(extra, new);
526 }
527 4 config_unnamed_s *x = (config_unnamed_s *)list_get(extra, j);
528
4/5
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
4 switch (x->response.type)
529 {
530 1 case CONFIG_ARG_BOOLEAN:
531 1 x->seen = parse_boolean(NULL, curr, &x->response.value.boolean);
532 1 break;
533 1 case CONFIG_ARG_INTEGER:
534 1 x->seen = parse_integer(NULL, curr, &x->response.value.integer);
535 1 break;
536 1 case CONFIG_ARG_DECIMAL:
537 1 x->seen = parse_decimal(NULL, curr, &x->response.value.decimal);
538 1 break;
539 1 case CONFIG_ARG_STRING:
540 1 x->seen = true;
541 1 x->response.value.string = m_strdup(curr);
542 1 break;
543 ✗ default:
544 ✗ break;
545 }
546 4 j++;
547 }
548 ✗ else if (warn)
549 ✗ config_show_usage(args, extra);
550 }
551 }
552 4 list_deinit(largs, free);
553
554 4 int r = 0;
555 4 iter_t iter = list_iterator(args);
556
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 3 times.
34 while (list_has_next(iter))
557 {
558 31 config_named_s *arg = (config_named_s *)list_get_next(iter);
559
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 1 times.
31 if (arg->seen)
560 30 r++;
561
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 else if (arg->required && !arg->seen && warn)
562 {
563 1 cli_eprintf("Missing required argument \"%s\"\n", arg->description);
564 1 config_show_usage(args, extra);
565 }
566
567 }
568 3 free(iter);
569
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (extra)
570 {
571 1 iter = list_iterator(extra);
572
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 while (list_has_next(iter))
573 {
574 4 config_unnamed_s *arg = (config_unnamed_s *)list_get_next(iter);
575
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (arg->seen)
576 4 r++;
577 }
578 1 free(iter);
579 }
580
581 3 return r;
582 }
583
584 153 inline static bool is_argument(char s, const char *l, const char *a)
585 {
586
5/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 150 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
153 if (strlen(a) == 2 && a[0] == '-' && a[1] == s)
587 1 return true;
588
5/6
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 102 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
152 if (strlen(a) > 2 && a[0] == '-' && a[1] == '-')
589 102 return !strcmp(l, a + 2);
590 50 return false;
591 }
592
593 5 inline static void format_section(char *s)
594 {
595 5 cli_eprintf(ANSI_COLOUR_CYAN "%s" ANSI_COLOUR_RESET ":\n", s);
596 5 return;
597 }
598
599 1 static void show_version(list_t args, list_t largs, list_t notes, list_t extra)
600 {
601 1 version_print(about.name, about.version, about.url);
602
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while (version_is_checking)
603 ✗ sleep(1);
604 1 list_deinit(args);
605 1 list_deinit(largs, free);
606 1 list_deinit(notes);
607 1 list_deinit(extra);
608 1 exit(EXIT_SUCCESS);
609 }
610
611 2 inline static void print_usage(list_t args, list_t extra)
612 {
613 #ifndef _WIN32
614 struct winsize ws;
615 2 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws);
616 2 int max_width = ws.ws_col - strlen(about.name) - 2;
617 #else
618 CONSOLE_SCREEN_BUFFER_INFO csbi;
619 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
620 int max_width = (csbi.srWindow.Right - csbi.srWindow.Left + 1) - strlen(about.name) - 2;
621 #endif
622
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
2 if (max_width <= 0 || !isatty(STDERR_FILENO))
623 2 max_width = CLI_MAX_WIDTH - strlen(about.name); // needed for MSYS2
624 2 format_section(_("Usage"));
625 2 int j = cli_eprintf(" " ANSI_COLOUR_GREEN "%s", about.name) - strlen(ANSI_COLOUR_GREEN) - 2;
626
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (extra)
627 {
628 1 iter_t iter = list_iterator(extra);
629
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 while (list_has_next(iter))
630 {
631 1 const config_unnamed_s *x = (config_unnamed_s *)list_get_next(iter);
632
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (x->required)
633 1 j += cli_eprintf(ANSI_COLOUR_RED " <%s>" ANSI_COLOUR_RESET, x->description);
634 else
635 ✗ j+= cli_eprintf(ANSI_COLOUR_YELLOW " [%s]" ANSI_COLOUR_RESET, x->description);
636 }
637 1 free(iter);
638
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (isatty(STDERR_FILENO))
639 ✗ j -= (strlen(ANSI_COLOUR_RESET) + strlen(ANSI_COLOUR_WHITE));
640 }
641
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (args)
642 {
643 2 iter_t iter = list_iterator(args);
644
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2 times.
9 while (list_has_next(iter))
645 {
646 7 const config_named_s *arg = list_get_next(iter);
647
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if (!arg->hidden)
648 {
649
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if ((int)(j + 4 + (arg->option_type ? strlen(arg->option_type) : 0)) > max_width)
650 ✗ j = cli_eprintf("\n%*s ", (int)strlen(about.name), " ");
651
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (arg->required)
652 1 j += cli_eprintf(ANSI_COLOUR_RED " <-%c", arg->short_option);
653 else
654 5 j += cli_eprintf(ANSI_COLOUR_YELLOW " [-%c", arg->short_option);
655
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (arg->option_type)
656 1 j += cli_eprintf(" %s", arg->option_type);
657
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 j += cli_eprintf("%c" ANSI_COLOUR_RESET, arg->required ? '>' : ']');
658
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (isatty(STDERR_FILENO))
659 ✗ j -= (strlen(ANSI_COLOUR_RESET) + strlen(ANSI_COLOUR_WHITE));
660 }
661 }
662 2 free(iter);
663 }
664 2 cli_eprintf(ANSI_COLOUR_RESET "\n");
665 2 return;
666 }
667
668 1 extern void config_show_usage(list_t args, list_t extra)
669 {
670 1 print_usage(args, extra);
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while (version_is_checking)
672 ✗ sleep(1);
673 1 exit(EXIT_SUCCESS);
674 }
675
676 8 static void print_option(int indent, char sopt, char *lopt, char *type, char *def, bool req, char *desc)
677 {
678 8 size_t z = indent - 8;
679
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (lopt)
680 8 z -= strlen(lopt);
681 else
682 ✗ z += 4;
683 8 cli_eprintf(" " ANSI_COLOUR_WHITE "-%c" ANSI_COLOUR_RESET, sopt);
684
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (lopt)
685 8 cli_eprintf(", " ANSI_COLOUR_WHITE "--%s" ANSI_COLOUR_RESET, lopt);
686
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (type)
687 {
688
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (req)
689 {
690 ✗ if (lopt)
691 ✗ cli_eprintf(ANSI_COLOUR_WHITE "=" ANSI_COLOUR_RED "<%s>" ANSI_COLOUR_RESET, type);
692 else
693 ✗ cli_eprintf(" " ANSI_COLOUR_RED "<%s>" ANSI_COLOUR_RESET, type);
694 }
695 else
696 {
697
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (lopt)
698 1 cli_eprintf(ANSI_COLOUR_YELLOW "[=%s]" ANSI_COLOUR_RESET, type);
699 else
700 ✗ cli_eprintf(ANSI_COLOUR_YELLOW " [%s]" ANSI_COLOUR_RESET, type);
701 }
702 1 z -= 3 + strlen(type);
703 }
704 8 cli_eprintf("%*s", (int)z, " ");
705
706 #ifndef _WIN32
707 struct winsize ws;
708 8 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws);
709 8 int max_width = ws.ws_col - 2;
710 #else
711 CONSOLE_SCREEN_BUFFER_INFO csbi;
712 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
713 int max_width = (csbi.srWindow.Right - csbi.srWindow.Left + 1) - 2;
714 #endif
715
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
8 if (max_width <= 0 || !isatty(STDERR_FILENO))
716 8 max_width = CLI_MAX_WIDTH; // needed for MSYS2, but also sensible default if output is being redirected
717 8 int width = max_width - indent;
718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 for (; isspace(*desc); desc++)
719 ;
720 8 int l = strlen(desc);
721
722 #ifdef _WIN32
723 char *tmp = m_calloc(l + 1, 1);
724 for (int i = 0, j = 0; i < l; i++, j++)
725 if (!strncmp(desc + i, "‘", strlen("‘")) || !strncmp(desc + i, "’", strlen("‘")))
726 {
727 tmp[j] = '\'';
728 i += strlen("‘") - 1;
729 }
730 else
731 tmp[j] = desc[i];
732 desc = tmp;
733 #endif
734 8 char *x_desc = NULL;
735
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 if (def)
736 5 x_desc = m_strdupf("%s" ANSI_COLOUR_WHITE " (default:" ANSI_COLOUR_GREEN " %s" ANSI_COLOUR_WHITE ")", desc, def);
737 else
738 3 x_desc = desc;
739 8 l = strlen(x_desc);
740
741 8 cli_eprintf(ANSI_COLOUR_BLUE);
742
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if (l < width)
743 4 cli_eprintf("%s", x_desc);
744
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 else if (width <= indent)
745 ✗ cli_eprintf("\n %s\n", x_desc);
746 else
747 {
748 4 int s = 0;
749 do
750 {
751 9 bool too_long = false;
752 9 int e = s + width;
753
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 if (e > l)
754 4 e = l;
755 else
756
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 for (too_long = true; e > s; e--)
757
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 24 times.
29 if (isspace(x_desc[e]))
758 {
759 5 too_long = false;
760 5 break;
761 }
762
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (too_long)
763 {
764 ✗ for (int e2 = s; e2 < s + max_width; e2++)
765 ✗ if (isspace(x_desc[e2]) || x_desc[e2] == 0x00)
766 ✗ e = e2;
767 ✗ cli_eprintf("\n ");
768 }
769
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 else if (s)
770 5 cli_eprintf("\n%*s", indent, " ");
771 9 cli_eprintf("%.*s", e - s, x_desc + s);
772 9 s = e + 1;
773 }
774
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 while (s < l);
775 }
776 #ifdef _WIN32
777 free(tmp);
778 #endif
779
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 if (def)
780 5 free(x_desc);
781 8 cli_eprintf(ANSI_COLOUR_RESET "\n");
782 8 return;
783 }
784
785 1 static void print_notes(const char *line)
786 {
787 #ifndef _WIN32
788 1 cli_eprintf(" • ");
789 struct winsize ws;
790 1 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws);
791 1 int max_width = ws.ws_col - 5;
792 #else
793 cli_eprintf(" * ");
794 CONSOLE_SCREEN_BUFFER_INFO csbi;
795 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
796 int max_width = (csbi.srWindow.Right - csbi.srWindow.Left + 1) - 5;
797 #endif
798
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1 if (max_width <= 0 || !isatty(STDERR_FILENO))
799 1 max_width = 72; // needed for MSYS2, but also sensible default if output is being redirected
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 for (; isspace(*line); line++)
801 ;
802 1 int l = strlen(line);
803
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (l < max_width)
804 ✗ cli_eprintf("%s", line);
805 else
806 {
807 1 int s = 0;
808 do
809 {
810 2 bool too_long = false;
811 2 int e = s + max_width;
812
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (e > l)
813 1 e = l;
814 else
815
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 for (too_long = true; e > s; e--)
816
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (isspace(line[e]))
817 {
818 1 too_long = false;
819 1 break;
820 }
821
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (too_long)
822 {
823 ✗ for (e = s; ; e++)
824 ✗ if (isspace(line[e]) || line[e] == 0x00)
825 break;
826 ✗ cli_eprintf("\n ");
827 }
828
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 else if (s)
829 1 cli_eprintf("\n%*s", 4, " ");
830 2 cli_eprintf("%.*s", e - s, line + s);
831 2 s = e + 1;
832 }
833
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 while (s < l);
834 }
835 1 cli_eprintf(ANSI_COLOUR_RESET "\n");
836 1 return;
837 }
838
839 5 static char *parse_default(config_arg_e type, config_arg_u value)
840 {
841 5 char *d = NULL;
842
4/5
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
5 switch (type)
843 {
844 1 case CONFIG_ARG_OPT_BOOLEAN:
845 (void)0; // for Slackware's older GCC
846 __attribute__((fallthrough)); /* allow fall-through */
847 case CONFIG_ARG_REQ_BOOLEAN:
848
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 d = m_strdup(value.boolean ? "true" : "false");
849 1 break;
850 1 case CONFIG_ARG_OPT_INTEGER:
851 (void)0; // for Slackware's older GCC
852 __attribute__((fallthrough)); /* allow fall-through */
853 case CONFIG_ARG_REQ_INTEGER:
854 1 d = m_strdupf("%'" PRIi64, (int64_t)value.integer);
855 1 break;
856 1 case CONFIG_ARG_OPT_DECIMAL:
857 (void)0; // for Slackware's older GCC
858 __attribute__((fallthrough)); /* allow fall-through */
859 case CONFIG_ARG_REQ_DECIMAL:
860 //{
861 // char buf[0xFF] = { 0x00 };
862 // strfromf128(buf, sizeof buf, "%'.9f", (__float128)value.decimal);
863 // d = m_strdupf("%s", buf);
864 //}
865 1 d = m_strdupf("%'Lf", (long double)value.decimal);
866 1 break;
867 2 case CONFIG_ARG_OPT_STRING:
868 (void)0; // for Slackware's older GCC
869 __attribute__((fallthrough)); /* allow fall-through */
870 case CONFIG_ARG_REQ_STRING:
871
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 d = m_strdup(value.string ? : "(null)");
872 2 break;
873 ✗ default: // all other defaults to be displayed should be string
874 ✗ if (value.string)
875 ✗ d = m_strdup((char *)value.string);
876 ✗ break;
877 }
878 5 return d;
879 }
880
881 1 static void show_help(list_t args, list_t largs, list_t notes, list_t extra)
882 {
883 1 version_print(about.name, about.version, about.url);
884 1 cli_eprintf("\n");
885 1 print_usage(args, extra);
886
887 1 int indent = 10;
888 1 bool has_advanced = false;
889
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (args)
890 {
891 1 iter_t iter = list_iterator(args);
892
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
7 while (list_has_next(iter))
893 {
894 6 const config_named_s *arg = list_get_next(iter);
895
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 int w = 10 + (arg->long_option ? strlen(arg->long_option) : 0);
896
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (arg->option_type)
897 1 w += 3 + strlen(arg->option_type);
898 6 indent = indent > w ? indent : w;
899
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
6 if (arg->advanced && !arg->hidden)
900 1 has_advanced = true;
901 }
902 1 free(iter);
903 }
904 else
905 ✗ indent += 7;
906
907 1 cli_eprintf("\n");
908 1 format_section(_("Options"));
909 1 print_option(indent, 'h', "help", NULL, NULL, false, "Display this message");
910 1 print_option(indent, 'l', "licence", NULL, NULL, false, "Display GNU GPL v3 licence header");
911 1 print_option(indent, 'v', "version", NULL, NULL, false, "Display application version");
912
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (args)
913 {
914 1 iter_t iter = list_iterator(args);
915
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
7 while (list_has_next(iter))
916 {
917 6 const config_named_s *arg = list_get_next(iter);
918
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
6 if (!arg->hidden && !arg->advanced)
919 {
920 4 char *def = parse_default(arg->response.type, arg->response.value);
921 4 print_option(indent, arg->short_option, arg->long_option, arg->option_type ? : NULL, def, arg->response.type & CONFIG_ARG_REQUIRED, arg->description);
922
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (def)
923 4 free(def);
924 }
925 }
926 1 free(iter);
927 }
928
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (has_advanced)
929 {
930 1 cli_eprintf("\n");
931 1 format_section(_("Advanced Options"));
932 1 iter_t iter = list_iterator(args);
933
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
7 while (list_has_next(iter))
934 {
935 6 const config_named_s *arg = list_get_next(iter);
936
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
6 if (!arg->hidden && arg->advanced)
937 {
938 1 char *def = parse_default(arg->response.type, arg->response.value);
939 1 print_option(indent, arg->short_option, arg->long_option, arg->option_type ? : NULL, def, arg->response.type & CONFIG_ARG_REQUIRED, arg->description);
940
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (def)
941 1 free(def);
942 }
943 }
944 1 free(iter);
945 }
946
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (notes)
947 {
948 1 cli_eprintf("\n");
949 1 format_section(_("Notes"));
950 1 iter_t iter = list_iterator(notes);
951
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 while (list_has_next(iter))
952 1 print_notes(list_get_next(iter));
953 1 free(iter);
954 }
955
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while (version_is_checking)
956 ✗ sleep(1);
957 1 list_deinit(args);
958 1 list_deinit(largs, free);
959 1 list_deinit(notes);
960 1 list_deinit(extra);
961 1 exit(EXIT_SUCCESS);
962 }
963
964 1 static void show_licence(list_t args, list_t largs, list_t notes, list_t extra)
965 {
966 1 cli_eprintf(_(TEXT_LICENCE));
967
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while (version_is_checking)
968 ✗ sleep(1);
969 1 list_deinit(args);
970 1 list_deinit(largs, free);
971 1 list_deinit(notes);
972 1 list_deinit(extra);
973 1 exit(EXIT_SUCCESS);
974 }
975
976 2 extern void update_config(const char * const restrict o, const char * const restrict v)
977 {
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!init)
979 {
980 ✗ cli_eprintf(_("Call config_init() first\n"));
981 ✗ return;
982 }
983 2 char *rc = NULL;
984 #ifndef _WIN32
985
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 rc = m_strdupf("%s/%s", getenv("HOME") ? : ".", about.config);
986 #else
987 rc = m_calloc(MAX_PATH, sizeof( char ));
988 SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, rc);
989 strcat(rc, "\\");
990 strcat(rc, about.config);
991 #endif
992 2 FILE *f = fopen(rc, "rb+");
993
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!f) /* file doesn’t exist, so create it */
994 ✗ f = fopen(rc, "wb+");
995
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (f)
996 {
997 2 FILE *t = tmpfile();
998 2 char *line = NULL;
999 2 size_t len = 0;
1000 2 bool found = false;
1001
1002
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (int i = 0; i < 2; i++)
1003 {
1004 /*
1005 * first iteration: read rc file and change the value second
1006 * iteration: read from tmpfile back into rc file
1007 */
1008
2/3
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 4 times.
28 while (getline(&line, &len, f) >= 0)
1009 {
1010
5/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
24 if (!i && (!strncmp(o, line, strlen(o)) && isspace((unsigned char)line[strlen(o)])))
1011 {
1012 1 free(line);
1013 1 line = m_strdupf("%s %s\n", o, v);
1014 1 found = true;
1015 }
1016 24 cli_fprintf(t, "%s", line);
1017 24 free(line);
1018 24 line = NULL;
1019 24 len = 0;
1020 }
1021 4 fseek(f, 0, SEEK_SET);
1022 4 fseek(t, 0, SEEK_SET);
1023
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (!i)
1024 2 ftruncate(fileno(f), 0);
1025 4 FILE *z = f;
1026 4 f = t;
1027 4 t = z;
1028 }
1029
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!found)
1030 {
1031 1 fseek(f, 0, SEEK_END);
1032 1 cli_fprintf(f, "\n%s %s\n", o, v);
1033 }
1034 2 fclose(f);
1035 2 free(line);
1036 2 fclose(t);
1037 }
1038 2 free(rc);
1039 2 return;
1040 }
1041
1042 12 static bool parse_boolean(const char *c, const char *l, bool *v)
1043 {
1044 12 bool r = false;
1045 12 char *n = parse_tail(c, l);
1046
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (n)
1047 {
1048
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 if (!strcasecmp(CONF_TRUE, n)
1049
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 || !strcasecmp(CONF_ON, n)
1050
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 || !strcasecmp(CONF_ENABLED, n)
1051
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 || !strcasecmp(CONF_YES, n)
1052
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 || !strcmp(CONF_ONE, n))
1053 {
1054 7 *v = true;
1055 7 r = true;
1056 }
1057
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 else if (!strcasecmp(CONF_FALSE, n) ||
1058
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 !strcasecmp(CONF_OFF, n) ||
1059
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 !strcasecmp(CONF_DISABLED, n) ||
1060
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 !strcasecmp(CONF_NO, n) ||
1061 ✗ !strcmp(CONF_ZERO, n))
1062 {
1063 5 *v = false;
1064 5 r = true;
1065 }
1066 12 free(n);
1067 }
1068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!r)
1069 ✗ cli_eprintf("invalid boolean value [%s]\n", l);
1070 12 return r;
1071 }
1072
1073 4 static int64_t parse_number_size_suffix(const char *s)
1074 {
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (s[0] == ' ')
1076 ✗ return 1;
1077 4 int64_t r = 1;
1078
1/13
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
4 switch (s[0])
1079 {
1080 ✗ case 'E':
1081 ✗ r *= KILOBYTE;
1082 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1083 ✗ case 'P':
1084 ✗ r *= KILOBYTE;
1085 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1086 ✗ case 'T':
1087 ✗ r *= KILOBYTE;
1088 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1089 ✗ case 'G':
1090 ✗ r *= KILOBYTE;
1091 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1092 ✗ case 'M':
1093 ✗ r *= KILOBYTE;
1094 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1095 4 case 'K':
1096 4 r *= KILOBYTE;
1097 4 break;
1098 ✗ case 'e':
1099 ✗ r *= THOUSAND;
1100 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1101 ✗ case 'p':
1102 ✗ r *= THOUSAND;
1103 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1104 ✗ case 't':
1105 ✗ r *= THOUSAND;
1106 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1107 ✗ case 'g':
1108 ✗ r *= THOUSAND;
1109 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1110 ✗ case 'm':
1111 ✗ r *= THOUSAND;
1112 __attribute__((fallthrough)); /* allow fall-through; keep multiplying to get the correct size increase */
1113 ✗ case 'k':
1114 ✗ r *= THOUSAND;
1115 ✗ break;
1116 ✗ default:
1117 ✗ cli_eprintf("invalid size suffix [%c]\n", s[0]);
1118 ✗ break;
1119 }
1120 4 return r;
1121 }
1122
1123 13 static bool parse_integer(const char *c, const char *l, int64_t *v)
1124 {
1125 13 bool r = false;
1126 13 char *n = parse_tail(c, l);
1127
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (n)
1128 {
1129 13 char *e = NULL;
1130 13 *v = strtoull(n, &e, 0);
1131
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (e != n)
1132 {
1133
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
13 int64_t m = strlen(e) >= 1 ? parse_number_size_suffix(e) : 1;
1134 13 *v *= m;
1135 13 r = true;
1136 }
1137 13 free(n);
1138 }
1139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (!r)
1140 ✗ cli_eprintf("invalid integer value [%s]\n", l);
1141 13 return r;
1142 }
1143
1144 //static bool parse_decimal(const char *c, const char *l, __float128 *v)
1145 12 static bool parse_decimal(const char *c, const char *l, long double *v)
1146 {
1147 12 bool r = false;
1148 12 char *n = parse_tail(c, l);
1149
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (n)
1150 {
1151 12 char *e = NULL;
1152 12 *v = strtof128(n, &e);
1153
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (e != n)
1154 {
1155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 int64_t m = strlen(e) >= 1 ? parse_number_size_suffix(e) : 1;
1156 12 *v *= m;
1157 12 r = true;
1158 }
1159 12 free(n);
1160 }
1161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (!r)
1162 ✗ cli_eprintf("invalid decimal value [%s]\n", l);
1163 12 return r;
1164 }
1165
1166 2 static char *parse_string(const char *c, const char *l, char *v)
1167 {
1168 2 char *r = parse_tail(c, l);
1169
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 return r ? : v;
1170 }
1171
1172 39 static char *parse_tail(const char *c, const char *l)
1173 {
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (!l)
1175 ✗ return NULL;
1176
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 33 times.
39 char *x = m_strdup(l + (c ? strlen(c) : 0));
1177 39 size_t i = 0;
1178
3/4
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 39 times.
45 for (i = 0; i < strlen(x) && isspace((unsigned char)x[i]); i++)
1179 ;
1180 39 char *y = m_strdup(x + i);
1181 39 free(x);
1182
4/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 29 times.
51 for (i = strlen(y) - 1; i > 0 && isspace((unsigned char)y[i]); i--)
1183 ;//y[i] = '\0';
1184 39 char *tail = m_strndup(y, i + 1);
1185 39 free(y);
1186 39 return tail;
1187 }
1188
1189 1 static bool parse_pair_boolean(const char *c, const char *l, pair_boolean_s *pair)
1190 {
1191 1 pair_u *p = parse_pair(c, l);
1192 1 bool p1 = parse_boolean(NULL, p->string.s1, &pair->b1);
1193 1 bool p2 = parse_boolean(NULL, p->string.s2, &pair->b2);
1194 1 free(p->string.s1);
1195 1 free(p->string.s2);
1196 1 free(p);
1197
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return p1 && p2;
1198 }
1199
1200 1 static bool parse_pair_integer(const char *c, const char *l, pair_integer_s *pair)
1201 {
1202 1 pair_u *p = parse_pair(c, l);
1203 1 bool p1 = parse_integer(NULL, p->string.s1, &pair->i1);
1204 1 bool p2 = parse_integer(NULL, p->string.s2, &pair->i2);
1205 1 free(p->string.s1);
1206 1 free(p->string.s2);
1207 1 free(p);
1208
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return p1 && p2;
1209 }
1210
1211 1 static bool parse_pair_decimal(const char *c, const char *l, pair_decimal_s *pair)
1212 {
1213 1 pair_u *p = parse_pair(c, l);
1214 1 bool p1 = parse_decimal(NULL, p->string.s1, &pair->d1);
1215 1 bool p2 = parse_decimal(NULL, p->string.s2, &pair->d2);
1216 1 free(p->string.s1);
1217 1 free(p->string.s2);
1218 1 free(p);
1219
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return p1 && p2;
1220 }
1221
1222 2 static bool parse_pair_string(const char *c, const char *l, pair_string_s *pair)
1223 {
1224 2 pair_u *p = parse_pair(c, l);
1225 2 pair->s1 = p->string.s1;
1226 2 pair->s2 = p->string.s2;
1227 2 free(p);
1228
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 return pair->s1 && pair->s2;
1229 }
1230
1231 5 static pair_u *parse_pair(const char *c, const char *l)
1232 {
1233 5 pair_u *pair = m_calloc(1, sizeof (pair_u));
1234 /* get everything after the parameter name */
1235 5 char *x = m_strdup(l + strlen(c));
1236 5 size_t i = 0, j = 0;
1237 /* skip past all whitespace */
1238
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
10 for (i = 0; i < strlen(x) && isspace((unsigned char)x[i]); i++)
1239 ;
1240 5 char *y = x + i;
1241 /* find the end of the first value */
1242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (y[0] == '"')
1243 ✗ for (i = 1, j = 1; i < strlen(y) && y[i] != '"'; i++)
1244 ;
1245 else
1246
3/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 5 times.
16 for (; i < strlen(y) && !isspace(y[i]); i++)
1247 ;
1248 5 pair->string.s1 = m_strndup(y + j, i - j);
1249 /* skip past all whitespace */
1250
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
10 for (; i < strlen(y) && isspace((unsigned char)y[i]); i++)
1251 ;
1252 5 char *z = y + i + j;
1253 /* remove any trailing whitespace */
1254
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 4 times.
10 for (i = strlen(z) - 1; i > 0 && isspace((unsigned char)z[i]); i--)
1255 ;//y[i] = '\0';
1256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (z[j] == '"')
1257 ✗ j++;
1258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 pair->string.s2 = m_strndup(z + j, i + (j ? -j : 1));
1259 /* all done */
1260 5 free(x);
1261 5 return pair;
1262 }
1263
1264 6 static void parse_list_boolean(const char *text, list_t list)
1265 {
1266 6 bool *r = m_malloc(sizeof( bool ));
1267
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (parse_boolean(NULL, text, r))
1268 {
1269
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!list_append(list, r))
1270 ✗ free(r);
1271 }
1272 else
1273 ✗ free(r);
1274 6 return;
1275 }
1276
1277 7 static void parse_list_integer(const char *text, list_t list)
1278 {
1279 7 int64_t *r = m_malloc(sizeof( int64_t ));
1280
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 if (parse_integer(NULL, text, r))
1281 {
1282
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (!list_append(list, r))
1283 ✗ free(r);
1284 }
1285 else
1286 ✗ free(r);
1287 7 return;
1288 }
1289
1290 6 static void parse_list_decimal(const char *text, list_t list)
1291 {
1292 //__float128 *r = m_malloc(sizeof( __float128 ));
1293 6 long double *r = m_malloc(sizeof( long double ));
1294
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (parse_decimal(NULL, text, r))
1295 {
1296
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (!list_append(list, r))
1297 ✗ free(r);
1298 }
1299 else
1300 ✗ free(r);
1301 6 return;
1302 }
1303
1304 7 static void parse_list(config_arg_e type, const char *text, list_t list)
1305 {
1306
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 while (*text && isspace(*text))
1307 ✗ text++;
1308 7 char *s = m_strdup(text);
1309 7 char *t = s;
1310 7 char *u = s;
1311
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 7 times.
22 while ((t = strtok(u, ",")))
1312 {
1313
4/5
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
15 switch (type)
1314 {
1315 4 case CONFIG_ARG_BOOLEAN:
1316 4 parse_list_boolean(t, list);
1317 4 break;
1318
1319 5 case CONFIG_ARG_INTEGER:
1320 5 parse_list_integer(t, list);
1321 5 break;
1322
1323 4 case CONFIG_ARG_DECIMAL:
1324 4 parse_list_decimal(t, list);
1325 4 break;
1326
1327 2 case CONFIG_ARG_STRING:
1328 {
1329 2 char *v = m_strdup(t);
1330
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!list_append(list, v))
1331 ✗ free(v);
1332 }
1333 2 break;
1334
1335 ✗ default:
1336 ✗ break;
1337 }
1338 15 u = NULL;
1339 }
1340 7 free(s);
1341 7 return;
1342 }
1343