view src/doveadm/doveadm-print.c @ 19609:e7e593e68fce

doveadm: Changed most print formatters to write to ostream. This allows all the formatters to be used when sending replies to the upcoming doveadm HTTP server responses. The "table" formatter wasn't modified, because it writes to both stdout and stderr.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Fri, 22 Jan 2016 19:06:59 +0200
parents 0f22db71df7a
children 4f23573700c9
line wrap: on
line source

/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "istream.h"
#include "ostream.h"
#include "doveadm-print-private.h"

struct doveadm_print_header_context {
	const char *key;
	char *sticky_value;
	bool sticky;
};

struct doveadm_print_context {
	pool_t pool;
	ARRAY(struct doveadm_print_header_context) headers;
	const struct doveadm_print_vfuncs *v;

	unsigned int header_idx;
	bool print_stream_open;
};

bool doveadm_print_hide_titles = FALSE;
struct ostream *doveadm_print_ostream = NULL;

static struct doveadm_print_context *ctx;

bool doveadm_print_is_initialized(void)
{
	return ctx != NULL;
}

void doveadm_print_header(const char *key, const char *title,
			  enum doveadm_print_header_flags flags)
{
	struct doveadm_print_header hdr;
	struct doveadm_print_header_context *hdr_ctx;

	if (title == NULL)
		flags |= DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE;

	memset(&hdr, 0, sizeof(hdr));
	hdr.key = key;
	hdr.title = title;
	hdr.flags = flags;
	ctx->v->header(&hdr);

	hdr_ctx = array_append_space(&ctx->headers);
	hdr_ctx->key = p_strdup(ctx->pool, key);
	hdr_ctx->sticky = (flags & DOVEADM_PRINT_HEADER_FLAG_STICKY) != 0;
}

void doveadm_print_header_simple(const char *key_title)
{
	doveadm_print_header(key_title, key_title, 0);
}

static void doveadm_print_sticky_headers(void)
{
	const struct doveadm_print_header_context *headers;
	unsigned int count;

	headers = array_get(&ctx->headers, &count);
	i_assert(count > 0);
	for (;;) {
		if (ctx->header_idx == count)
			ctx->header_idx = 0;
		else if (headers[ctx->header_idx].sticky) {
			ctx->v->print(headers[ctx->header_idx].sticky_value);
			ctx->header_idx++;
		} else {
			break;
		}
	}
}

void doveadm_print(const char *value)
{
	i_assert(!ctx->print_stream_open);

	doveadm_print_sticky_headers();
	ctx->v->print(value);
	ctx->header_idx++;
}

void doveadm_print_num(uintmax_t value)
{
	T_BEGIN {
		doveadm_print(dec2str(value));
	} T_END;
}

void doveadm_print_stream(const void *value, size_t size)
{
	if (!ctx->print_stream_open) {
		doveadm_print_sticky_headers();
		ctx->print_stream_open = TRUE;
	}
	ctx->v->print_stream(value, size);
	if (size == 0) {
		ctx->header_idx++;
		ctx->print_stream_open = FALSE;
	}
}

int doveadm_print_istream(struct istream *input)
{
	const unsigned char *data;
	size_t size;
	ssize_t ret;

	while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
		doveadm_print_stream(data, size);
		i_stream_skip(input, size);
	}
	i_assert(ret == -1);
	doveadm_print_stream("", 0);
	if (input->stream_errno != 0) {
		i_error("read(%s) failed: %s", i_stream_get_name(input),
			i_stream_get_error(input));
		return -1;
	}
	return 0;
}

void doveadm_print_sticky(const char *key, const char *value)
{
	struct doveadm_print_header_context *hdr;

	if (ctx == NULL) {
		/* command doesn't really print anything */
		return;
	}

	array_foreach_modifiable(&ctx->headers, hdr) {
		if (strcmp(hdr->key, key) == 0) {
			i_free(hdr->sticky_value);
			hdr->sticky_value = i_strdup(value);
			return;
		}
	}
	i_unreached();
}

void doveadm_print_flush(void)
{
	if (ctx != NULL && ctx->v->flush != NULL)
		ctx->v->flush();
	o_stream_uncork(doveadm_print_ostream);
	o_stream_cork(doveadm_print_ostream);
}

void doveadm_print_unstick_headers(void)
{
	struct doveadm_print_header_context *hdr;

	if (ctx != NULL) {
		array_foreach_modifiable(&ctx->headers, hdr)
			hdr->sticky = FALSE;
	}
}

void doveadm_print_init(const char *name)
{
	pool_t pool;
	unsigned int i;

	if (ctx != NULL) {
		/* already forced the type */
		return;
	}

	pool = pool_alloconly_create("doveadm print", 1024);
	ctx = p_new(pool, struct doveadm_print_context, 1);
	ctx->pool = pool;
	p_array_init(&ctx->headers, pool, 16);

	for (i = 0; doveadm_print_vfuncs_all[i] != NULL; i++) {
		if (strcmp(doveadm_print_vfuncs_all[i]->name, name) == 0) {
			ctx->v = doveadm_print_vfuncs_all[i];
			break;
		}
	}
	if (ctx->v == NULL)
		i_fatal("Unknown print formatter: %s", name);
	if (ctx->v->init != NULL)
		ctx->v->init();
}

void doveadm_print_deinit(void)
{
	struct doveadm_print_header_context *hdr;

	if (ctx == NULL)
		return;

	if (ctx->v->flush != NULL && doveadm_print_ostream != NULL) {
		ctx->v->flush();
		o_stream_uncork(doveadm_print_ostream);
	}
	if (ctx->v->deinit != NULL)
		ctx->v->deinit();
	array_foreach_modifiable(&ctx->headers, hdr)
		i_free(hdr->sticky_value);
	pool_unref(&ctx->pool);
	ctx = NULL;
}