summaryrefslogtreecommitdiff
path: root/examples/dte/error.c
blob: 87831c9bc30bced54d9ea3c9180c626e22bedffa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "error.h"
#include "command/run.h"
#include "config.h"
#include "util/log.h"
#include "util/xstdio.h"

static char error_buf[512];
static unsigned int nr_errors;
static bool msg_is_error;
static bool print_errors_to_stderr;

void clear_error(void)
{
    error_buf[0] = '\0';
}

bool error_msg(const char *format, ...)
{
    const char *cmd = current_command ? current_command->name : NULL;
    const char *file = current_config.file;
    const unsigned int line = current_config.line;
    const size_t size = sizeof(error_buf);
    int pos = 0;

    if (file && cmd) {
        pos = snprintf(error_buf, size, "%s:%u: %s: ", file, line, cmd);
    } else if (file) {
        pos = snprintf(error_buf, size, "%s:%u: ", file, line);
    } else if (cmd) {
        pos = snprintf(error_buf, size, "%s: ", cmd);
    }

    if (unlikely(pos < 0)) {
        // Note: POSIX snprintf(3) *does* set errno on failure (unlike ISO C)
        LOG_ERRNO("snprintf");
        pos = 0;
    }

    if (likely(pos < (size - 3))) {
        va_list ap;
        va_start(ap, format);
        vsnprintf(error_buf + pos, size - pos, format, ap);
        va_end(ap);
    } else {
        LOG_WARNING("no buffer space left for error message");
    }

    msg_is_error = true;
    nr_errors++;

    if (print_errors_to_stderr) {
        xfputs(error_buf, stderr);
        xfputc('\n', stderr);
    }

    LOG_INFO("%s", error_buf);

    // Always return false, to allow tail-calling as `return error_msg(...);`
    // from command handlers, instead of `error_msg(...); return false;`
    return false;
}

bool error_msg_errno(const char *prefix)
{
    return error_msg("%s: %s", prefix, strerror(errno));
}

void info_msg(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    vsnprintf(error_buf, sizeof(error_buf), format, ap);
    va_end(ap);
    msg_is_error = false;
}

const char *get_msg(bool *is_error)
{
    *is_error = msg_is_error;
    return error_buf;
}

unsigned int get_nr_errors(void)
{
    return nr_errors;
}

void set_print_errors_to_stderr(bool enable)
{
    print_errors_to_stderr = enable;
}