From 4ffba0dd993bc461df18fcf59591fc71ab6e6cc8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Nov 2018 12:06:14 +0100 Subject: [PATCH] format-table: optionally make specific cells clickable links (cherry picked from commit 165ca5663e9859083c70d793a6b4aa4f3b2af24c) Related: #1689832 --- src/basic/format-table.c | 79 +++++++++++++++++++++++++++++++++++----- src/basic/format-table.h | 1 + 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/basic/format-table.c b/src/basic/format-table.c index 3429d9a071..ac5d66eda2 100644 --- a/src/basic/format-table.c +++ b/src/basic/format-table.c @@ -9,6 +9,7 @@ #include "gunicode.h" #include "pager.h" #include "parse-util.h" +#include "pretty-print.h" #include "string-util.h" #include "terminal-util.h" #include "time-util.h" @@ -58,6 +59,7 @@ typedef struct TableData { unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */ const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */ + char *url; /* A URL to use for a clickable hyperlink */ char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */ union { @@ -182,6 +184,8 @@ static TableData *table_data_unref(TableData *d) { return NULL; free(d->formatted); + free(d->url); + return mfree(d); } @@ -392,6 +396,7 @@ int table_dup_cell(Table *t, TableCell *cell) { } static int table_dedup_cell(Table *t, TableCell *cell) { + _cleanup_free_ char *curl = NULL; TableData *nd, *od; size_t i; @@ -410,11 +415,25 @@ static int table_dedup_cell(Table *t, TableCell *cell) { assert(od->n_ref > 1); - nd = table_data_new(od->type, od->data, od->minimum_width, od->maximum_width, od->weight, od->align_percent, od->ellipsize_percent); + if (od->url) { + curl = strdup(od->url); + if (!curl) + return -ENOMEM; + } + + nd = table_data_new( + od->type, + od->data, + od->minimum_width, + od->maximum_width, + od->weight, + od->align_percent, + od->ellipsize_percent); if (!nd) return -ENOMEM; nd->color = od->color; + nd->url = TAKE_PTR(curl); table_data_unref(od); t->data[i] = nd; @@ -542,6 +561,26 @@ int table_set_color(Table *t, TableCell *cell, const char *color) { return 0; } +int table_set_url(Table *t, TableCell *cell, const char *url) { + _cleanup_free_ char *copy = NULL; + int r; + + assert(t); + assert(cell); + + if (url) { + copy = strdup(url); + if (!copy) + return -ENOMEM; + } + + r = table_dedup_cell(t, cell); + if (r < 0) + return r; + + return free_and_replace(table_get_data(t, cell)->url, copy); +} + int table_add_many_internal(Table *t, TableDataType first_type, ...) { TableDataType type; va_list ap; @@ -884,11 +923,13 @@ static int table_data_requested_width(TableData *d, size_t *ret) { return 0; } -static char *align_string_mem(const char *str, size_t new_length, unsigned percent) { - size_t w = 0, space, lspace, old_length; +static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) { + size_t w = 0, space, lspace, old_length, clickable_length; + _cleanup_free_ char *clickable = NULL; const char *p; char *ret; size_t i; + int r; /* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */ @@ -897,6 +938,15 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce old_length = strlen(str); + if (url) { + r = terminal_urlify(url, str, &clickable); + if (r < 0) + return NULL; + + clickable_length = strlen(clickable); + } else + clickable_length = old_length; + /* Determine current width on screen */ p = str; while (p < str + old_length) { @@ -913,23 +963,23 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce /* Already wider than the target, if so, don't do anything */ if (w >= new_length) - return strndup(str, old_length); + return clickable ? TAKE_PTR(clickable) : strdup(str); /* How much spaces shall we add? An how much on the left side? */ space = new_length - w; lspace = space * percent / 100U; - ret = new(char, space + old_length + 1); + ret = new(char, space + clickable_length + 1); if (!ret) return NULL; for (i = 0; i < lspace; i++) ret[i] = ' '; - memcpy(ret + lspace, str, old_length); - for (i = lspace + old_length; i < space + old_length; i++) + memcpy(ret + lspace, clickable ?: str, clickable_length); + for (i = lspace + clickable_length; i < space + clickable_length; i++) ret[i] = ' '; - ret[space + old_length] = 0; + ret[space + clickable_length] = 0; return ret; } @@ -1184,13 +1234,24 @@ int table_print(Table *t, FILE *f) { } else if (l < width[j]) { /* Field is shorter than allocated space. Let's align with spaces */ - buffer = align_string_mem(field, width[j], d->align_percent); + buffer = align_string_mem(field, d->url, width[j], d->align_percent); if (!buffer) return -ENOMEM; field = buffer; } + if (l >= width[j] && d->url) { + _cleanup_free_ char *clickable = NULL; + + r = terminal_urlify(d->url, field, &clickable); + if (r < 0) + return r; + + free_and_replace(buffer, clickable); + field = buffer; + } + if (j > 0) fputc(' ', f); /* column separator */ diff --git a/src/basic/format-table.h b/src/basic/format-table.h index 6dc2d16052..9978a8baf2 100644 --- a/src/basic/format-table.h +++ b/src/basic/format-table.h @@ -42,6 +42,7 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight); int table_set_align_percent(Table *t, TableCell *cell, unsigned percent); int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); int table_set_color(Table *t, TableCell *cell, const char *color); +int table_set_url(Table *t, TableCell *cell, const char *color); int table_add_many_internal(Table *t, TableDataType first_type, ...); #define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX)