summaryrefslogtreecommitdiffstats
path: root/liblog_vxlog.c
blob: 4093c59a3638f5a9198787e6c160ebf2044924e0 (plain) (blame)
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifndef TEST

#if defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif

static int
ensure_fit(struct messagebuf *msg, size_t extent)
{
	size_t new_size;
	void *new;
	if (extent >= msg->size - msg->len) {
		new_size = msg->len + extent + 1;
		new = realloc(msg->text, new_size);
		if (!new)
			return -1;
		msg->text = new;
		msg->size = new_size;
	}
	return 0;
}

int
liblog_vxlog(struct liblog_context *ctx, enum liblog_level level, unsigned flags, const char *fmt, va_list args)
{
	/* TODO optionally avoid allocating and deallocating for each call */
	/* TODO respect logmask */

	struct messagebuf msgbuf = MESSAGEBUF_INIT, *msg;
	int corked;
	size_t old_len;

	if (!ctx || (level & (enum liblog_level)~0x001F)) {
	einval:
		errno = EINVAL;
		return -1;
	}
	if (!fmt && !(flags & (XLOG_NOT_INLINE - 1U) & LIBLOG_XLOG_NO_TEXT))
		goto einval;
	if ((flags & (LIBLOG_XLOG_CORK | LIBLOG_XLOG_UNCORK)) == (LIBLOG_XLOG_CORK | LIBLOG_XLOG_UNCORK))
		goto einval;
	if ((flags & (LIBLOG_XLOG_BACKTRACE | LIBLOG_XLOG_NO_BACKTRACE)) == (LIBLOG_XLOG_BACKTRACE | LIBLOG_XLOG_NO_BACKTRACE))
		goto einval;

	msg = ctx->internal_state ? &ctx->internal_state->msg : &msgbuf;
	corked = !!msg->prefix;
	old_len = msg->len;

	/* TODO ctx->internal_state must be allocated */

	if (!msg->prefix) {
		/* TODO implement: prefix */
	}

	if (!(flags & LIBLOG_XLOG_NO_BACKTRACE)) {
		if (flags & LIBLOG_XLOG_BACKTRACE) {
			unsigned levels = (flags / XLOG_NOT_INLINE) + 1U;
			if (liblog_trace__(&msg->text, &msg->len, &msg->size, (size_t)levels, NULL))
				return -1;
		}
	}

	if (!(flags & LIBLOG_XLOG_NO_TEXT)) {
		int len;
		va_list args2;
		va_copy(args2, args);
		len = snprintf(NULL, 0, fmt, args2);
		va_end(args2);
		if (len < 0)
			return -1;
		if (ensure_fit(msg, (size_t)len))
			return -1;
		if (snprintf(&msg->text[msg->len], (size_t)len + 1, fmt, args) != len)
			abort();
		msg->len += (size_t)len;
	}

	if (flags & LIBLOG_XLOG_UNCORK) {
		if (msg == &ctx->internal_state->msg) {
			memcpy(&msgbuf, msg, sizeof(*msg));
			ctx->internal_state->msg = MESSAGEBUF_INIT;
			msg = &msgbuf;
		}
		goto output;
	} else if (flags & LIBLOG_XLOG_CORK) {
		if (msg != &ctx->internal_state->msg)
			memcpy(&ctx->internal_state->msg, msg, sizeof(*msg));
	} else if (corked) {
		/* do nothing, internal state already set */
	} else {
	output:
		if (msg->len && msg->text[msg->len - 1] != '\n') {
			if (ensure_fit(msg, 0))
				goto fail;
			msg->text[msg->len++] = '\n';
		}
		if (liblog_flush__(ctx, msg))
			goto fail;
		free(msg->prefix);
		free(msg->text);
		*msg = MESSAGEBUF_INIT;
	}

	return 0;

fail:
	if (msg != &ctx->internal_state->msg) {
		free(msg->prefix);
		free(msg->text);
	}
	msg->len = old_len;
	return -1;
}

#else

int main(void) {return 0;} /* TODO test */

#endif