Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <errno.h> | ||
2 | #include <stdarg.h> | ||
3 | #include <stdio.h> | ||
4 | #include <string.h> | ||
5 | #include "error.h" | ||
6 | #include "command/run.h" | ||
7 | #include "config.h" | ||
8 | #include "util/log.h" | ||
9 | #include "util/xstdio.h" | ||
10 | |||
11 | typedef struct { | ||
12 | char buf[512]; | ||
13 | unsigned int nr_errors; | ||
14 | bool is_error; | ||
15 | bool print_to_stderr; | ||
16 | } ErrorBuffer; | ||
17 | |||
18 | // This is not accessed from signal handlers or multiple threads and | ||
19 | // is considered an acceptable use of non-const globals: | ||
20 | // NOLINTNEXTLINE(*-avoid-non-const-global-variables) | ||
21 | static ErrorBuffer err; | ||
22 | |||
23 | 95 | void clear_error(void) | |
24 | { | ||
25 | 95 | err.buf[0] = '\0'; | |
26 | 95 | } | |
27 | |||
28 | 114 | bool error_msg(const char *format, ...) | |
29 | { | ||
30 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 13 times.
|
114 | const char *cmd = current_command ? current_command->name : NULL; |
31 | 114 | const char *file = current_config.file; | |
32 | 114 | const unsigned int line = current_config.line; | |
33 | 114 | const size_t size = sizeof(err.buf); | |
34 | 114 | int pos = 0; | |
35 | |||
36 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
|
114 | if (file && cmd) { |
37 | ✗ | pos = snprintf(err.buf, size, "%s:%u: %s: ", file, line, cmd); | |
38 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 113 times.
|
114 | } else if (file) { |
39 | 1 | pos = snprintf(err.buf, size, "%s:%u: ", file, line); | |
40 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 12 times.
|
113 | } else if (cmd) { |
41 | 101 | pos = snprintf(err.buf, size, "%s: ", cmd); | |
42 | } | ||
43 | |||
44 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
|
102 | if (unlikely(pos < 0)) { |
45 | // Note: POSIX snprintf(3) *does* set errno on failure (unlike ISO C) | ||
46 | ✗ | LOG_ERRNO("snprintf"); | |
47 | ✗ | pos = 0; | |
48 | } | ||
49 | |||
50 |
1/2✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
|
114 | if (likely(pos < (size - 3))) { |
51 | 114 | va_list ap; | |
52 | 114 | va_start(ap, format); | |
53 | 114 | vsnprintf(err.buf + pos, size - pos, format, ap); | |
54 | 114 | va_end(ap); | |
55 | } else { | ||
56 | ✗ | LOG_WARNING("no buffer space left for error message"); | |
57 | } | ||
58 | |||
59 | 114 | err.is_error = true; | |
60 | 114 | err.nr_errors++; | |
61 | |||
62 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 110 times.
|
114 | if (err.print_to_stderr) { |
63 | 4 | xfputs(err.buf, stderr); | |
64 | 4 | xfputc('\n', stderr); | |
65 | } | ||
66 | |||
67 | 114 | LOG_INFO("%s", err.buf); | |
68 | |||
69 | // Always return false, to allow tail-calling as `return error_msg(...);` | ||
70 | // from command handlers, instead of `error_msg(...); return false;` | ||
71 | 114 | return false; | |
72 | } | ||
73 | |||
74 | 1 | bool error_msg_errno(const char *prefix) | |
75 | { | ||
76 | 1 | return error_msg("%s: %s", prefix, strerror(errno)); | |
77 | } | ||
78 | |||
79 | 11 | bool info_msg(const char *format, ...) | |
80 | { | ||
81 | 11 | va_list ap; | |
82 | 11 | va_start(ap, format); | |
83 | 11 | vsnprintf(err.buf, sizeof(err.buf), format, ap); | |
84 | 11 | va_end(ap); | |
85 | 11 | err.is_error = false; | |
86 | 11 | return true; // To allow tail-calling from command handlers | |
87 | } | ||
88 | |||
89 | 95 | const char *get_msg(bool *is_error) | |
90 | { | ||
91 | 95 | *is_error = err.is_error; | |
92 | 95 | return err.buf; | |
93 | } | ||
94 | |||
95 | 362 | unsigned int get_nr_errors(void) | |
96 | { | ||
97 | 362 | return err.nr_errors; | |
98 | } | ||
99 | |||
100 | 24 | void errors_to_stderr(bool enable) | |
101 | { | ||
102 | 24 | err.print_to_stderr = enable; | |
103 | 24 | } | |
104 |