/* 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