summaryrefslogtreecommitdiffstats
path: root/src/doveadm/doveadm-print-table.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/doveadm/doveadm-print-table.c')
-rw-r--r--src/doveadm/doveadm-print-table.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/doveadm/doveadm-print-table.c b/src/doveadm/doveadm-print-table.c
new file mode 100644
index 0000000..25a4441
--- /dev/null
+++ b/src/doveadm/doveadm-print-table.c
@@ -0,0 +1,268 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "unichar.h"
+#include "doveadm-print-private.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#define DEFAULT_COLUMNS 80
+#define MIN_COLUMNS 30
+#define MAX_BUFFER_LINES 100
+
+struct doveadm_print_table_header {
+ const char *key;
+ const char *title;
+ enum doveadm_print_header_flags flags;
+ size_t min_length, max_length, length;
+};
+
+struct doveadm_print_table_context {
+ pool_t pool;
+ ARRAY(struct doveadm_print_table_header) headers;
+ ARRAY_TYPE(const_string) buffered_values;
+ string_t *stream;
+ unsigned int hdr_idx;
+ unsigned int columns;
+
+ bool lengths_set:1;
+};
+
+static struct doveadm_print_table_context *ctx;
+
+static void
+doveadm_print_table_header(const struct doveadm_print_header *hdr)
+{
+ struct doveadm_print_table_header *thdr;
+
+ thdr = array_append_space(&ctx->headers);
+ thdr->key = p_strdup(ctx->pool, hdr->key);
+ thdr->title = p_strdup(ctx->pool, hdr->title);
+ thdr->length = thdr->max_length = thdr->min_length = strlen(hdr->title);
+ thdr->flags = hdr->flags;
+}
+
+static void doveadm_calc_header_length(void)
+{
+ struct doveadm_print_table_header *headers;
+ const char *value, *const *values;
+ unsigned int i, line, hdr_count, value_count, line_count;
+ size_t len, max_length, orig_length, diff;
+
+ ctx->lengths_set = TRUE;
+
+ headers = array_get_modifiable(&ctx->headers, &hdr_count);
+ values = array_get(&ctx->buffered_values, &value_count);
+ i_assert((value_count % hdr_count) == 0);
+ line_count = value_count / hdr_count;
+
+ /* find min and max lengths of fields */
+ for (line = 0; line < line_count; line++) {
+ for (i = 0; i < hdr_count; i++) {
+ value = values[line*hdr_count + i];
+ len = value == NULL ? 0 : uni_utf8_strlen(value);
+ if (headers[i].min_length > len)
+ headers[i].min_length = len;
+ if (headers[i].max_length < len) {
+ headers[i].max_length = len;
+ headers[i].length = len;
+ }
+ }
+ }
+
+ /* +1 for space between fields */
+ max_length = 0;
+ for (i = 0; i < hdr_count; i++)
+ max_length += headers[i].max_length + 1;
+ max_length--;
+
+ while (max_length > ctx->columns) {
+ /* shrink something so we'll fit */
+ orig_length = max_length;
+ for (i = hdr_count - 1;; i--) {
+ diff = headers[i].length - headers[i].min_length;
+ if (max_length - diff <= ctx->columns) {
+ /* we can finish with this */
+ diff = max_length - ctx->columns;
+ headers[i].length -= diff;
+ max_length -= diff;
+ break;
+ }
+ if (diff > 0) {
+ /* take a bit off from it */
+ headers[i].length -= diff == 1 ? 1 : diff/2;
+ }
+
+ if (i == 0)
+ break;
+ }
+ if (max_length == orig_length) {
+ /* can't shrink it any more */
+ break;
+ }
+ }
+ if (max_length < ctx->columns) {
+ for (i = 0; i < hdr_count; i++) {
+ if ((headers[i].flags & DOVEADM_PRINT_HEADER_FLAG_EXPAND) != 0) {
+ i++;
+ break;
+ }
+ }
+ headers[i-1].length += (ctx->columns - max_length) / 2;
+ }
+}
+
+static size_t utf8_correction(const char *str)
+{
+ size_t i, len = 0;
+
+ for (i = 0; str[i] != '\0'; i++) {
+ if ((str[i] & 0xc0) == 0x80)
+ len++;
+ }
+ return len;
+}
+
+static void doveadm_print_next(const char *value)
+{
+ const struct doveadm_print_table_header *hdr;
+ int value_padded_len;
+
+ hdr = array_idx(&ctx->headers, ctx->hdr_idx);
+
+ value_padded_len = hdr->length + utf8_correction(value);
+ if ((hdr->flags & DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY) == 0)
+ printf("%-*s", value_padded_len, value);
+ else
+ printf("%*s", value_padded_len, value);
+
+ if (++ctx->hdr_idx == array_count(&ctx->headers)) {
+ ctx->hdr_idx = 0;
+ printf("\n");
+ } else {
+ printf(" ");
+ }
+}
+
+static void doveadm_print_headers(void)
+{
+ const struct doveadm_print_table_header *headers;
+ unsigned int i, count;
+
+ if (doveadm_print_hide_titles)
+ return;
+
+ headers = array_get(&ctx->headers, &count);
+ /* if all headers are hidden, don't print any of them */
+ for (i = 0; i < count; i++) {
+ if ((headers[i].flags & DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE) == 0)
+ break;
+ }
+ if (i == count)
+ return;
+ for (i = 0; i < count; i++) {
+ if (i > 0) printf(" ");
+
+ if ((headers[i].flags &
+ DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY) == 0) {
+ printf("%-*s", (int)headers[i].length,
+ headers[i].title);
+ } else {
+ printf("%*s", (int)headers[i].length,
+ headers[i].title);
+ }
+ }
+ printf("\n");
+}
+
+static void doveadm_buffer_flush(void)
+{
+ const char *value;
+
+ doveadm_calc_header_length();
+ doveadm_print_headers();
+
+ array_foreach_elem(&ctx->buffered_values, value)
+ doveadm_print_next(value);
+ array_clear(&ctx->buffered_values);
+}
+
+static void doveadm_print_table_print(const char *value)
+{
+ unsigned int line_count;
+
+ if (!ctx->lengths_set) {
+ line_count = array_count(&ctx->buffered_values) /
+ array_count(&ctx->headers);
+ if (line_count < MAX_BUFFER_LINES) {
+ value = p_strdup(ctx->pool, value);
+ array_push_back(&ctx->buffered_values, &value);
+ return;
+ }
+ doveadm_buffer_flush();
+ }
+ doveadm_print_next(value);
+}
+
+static void
+doveadm_print_table_print_stream(const unsigned char *value, size_t size)
+{
+ if (memchr(value, '\n', size) != NULL)
+ i_fatal("table formatter doesn't support multi-line values");
+
+ if (size != 0)
+ str_append_data(ctx->stream, value, size);
+ else {
+ doveadm_print_table_print(str_c(ctx->stream));
+ str_truncate(ctx->stream, 0);
+ }
+}
+
+static void doveadm_print_table_flush(void)
+{
+ if (!ctx->lengths_set && array_count(&ctx->headers) > 0)
+ doveadm_buffer_flush();
+}
+
+static void doveadm_print_table_init(void)
+{
+ pool_t pool;
+ struct winsize ws;
+
+ pool = pool_alloconly_create("doveadm print table", 2048);
+ ctx = p_new(pool, struct doveadm_print_table_context, 1);
+ ctx->pool = pool;
+ ctx->stream = str_new(default_pool, 128);
+ p_array_init(&ctx->headers, pool, 16);
+ i_array_init(&ctx->buffered_values, 64);
+ ctx->columns = DEFAULT_COLUMNS;
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
+ ctx->columns = ws.ws_col < MIN_COLUMNS ?
+ MIN_COLUMNS : ws.ws_col;
+ }
+}
+
+static void doveadm_print_table_deinit(void)
+{
+ str_free(&ctx->stream);
+ array_free(&ctx->buffered_values);
+ pool_unref(&ctx->pool);
+ ctx = NULL;
+}
+
+struct doveadm_print_vfuncs doveadm_print_table_vfuncs = {
+ "table",
+
+ doveadm_print_table_init,
+ doveadm_print_table_deinit,
+ doveadm_print_table_header,
+ doveadm_print_table_print,
+ doveadm_print_table_print_stream,
+ doveadm_print_table_flush
+};