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