]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: add support for terminal hyperlinks
authorKarel Zak <kzak@redhat.com>
Thu, 28 Nov 2024 11:35:37 +0000 (12:35 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 28 Nov 2024 11:35:37 +0000 (12:35 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
libsmartcols/docs/libsmartcols-sections.txt
libsmartcols/src/cell.c
libsmartcols/src/column.c
libsmartcols/src/libsmartcols.h.in
libsmartcols/src/libsmartcols.sym
libsmartcols/src/print.c
libsmartcols/src/smartcolsP.h

index 5f4c736a74a30bad61c6a09dd40f4d2b6811859b..94bb9ef0584122022ee5eae796bdc8e224c5bb71 100644 (file)
@@ -2,17 +2,20 @@
 <FILE>cell</FILE>
 libscols_cell
 scols_cell_copy_content
+scols_cell_disable_uri
 scols_cell_get_alignment
 scols_cell_get_color
 scols_cell_get_data
 scols_cell_get_datasiz
 scols_cell_get_flags
+scols_cell_get_uri
 scols_cell_get_userdata
 scols_cell_refer_data
 scols_cell_refer_memory
 scols_cell_set_color
 scols_cell_set_data
 scols_cell_set_flags
+scols_cell_set_uri
 scols_cell_set_userdata
 scols_cmpstr_cells
 scols_reset_cell
@@ -30,6 +33,7 @@ scols_column_get_name
 scols_column_get_name_as_shellvar
 scols_column_get_safechars
 scols_column_get_table
+scols_column_get_uri
 scols_column_get_whint
 scols_column_get_width
 scols_column_get_wrap_data
@@ -51,6 +55,7 @@ scols_column_set_json_type
 scols_column_set_name
 scols_column_set_properties
 scols_column_set_safechars
+scols_column_set_uri
 scols_column_set_whint
 scols_column_set_wrapfunc
 scols_copy_column
index df45667ec587d4c51c9d62b1961e6195cd67a6cd..4c279166e8ed04c19b19bfbe98462420f4331f8d 100644 (file)
@@ -47,6 +47,7 @@ int scols_reset_cell(struct libscols_cell *ce)
        /*DBG(CELL, ul_debugobj(ce, "reset"));*/
        free(ce->data);
        free(ce->color);
+       free(ce->uri);
        memset(ce, 0, sizeof(*ce));
        return 0;
 }
@@ -244,6 +245,65 @@ const char *scols_cell_get_color(const struct libscols_cell *ce)
        return ce->color;
 }
 
+/**
+ * scols_cell_set_uri:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @uri: URI string
+ *
+ * Set the URI of @ce to @uri.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.41
+ */
+int scols_cell_set_uri(struct libscols_cell *ce, const char *uri)
+{
+       if (!ce)
+               return -EINVAL;
+
+       return strdup_to_struct_member(ce, uri, uri);
+}
+
+/**
+ * scols_cell_get_uri:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * The function returns the URI setting, but it may not necessarily be the final
+ * URI used in the output. This is because the column may define a URI prefix or
+ * the cell content may be used as part of the URI.
+ *
+ * Returns: the current URI of @ce.
+ *
+ * Since: 2.41
+ */
+const char *scols_cell_get_uri(const struct libscols_cell *ce)
+{
+       if (!ce)
+               return NULL;
+
+       return ce->uri;
+}
+
+/**
+ * scols_cell_disable_uri:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @disable: 1 or 0
+ *
+ * Force the library to ignore the cell and column URI setting and print the
+ * content as a regular string.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.41
+ */
+int scols_cell_disable_uri(struct libscols_cell *ce, int disable)
+{
+       if (!ce)
+               return -EINVAL;
+       ce->no_uri = disable ? 1 : 0;
+       return 0;
+}
+
 /**
  * scols_cell_set_flags:
  * @ce: a pointer to a struct libscols_cell instance
@@ -321,6 +381,8 @@ int scols_cell_copy_content(struct libscols_cell *dest,
        rc = scols_cell_refer_memory(dest, data, src->datasiz);
        if (!rc)
                rc = scols_cell_set_color(dest, scols_cell_get_color(src));
+       if (!rc)
+               rc = scols_cell_set_uri(dest, scols_cell_get_uri(src));
        if (!rc)
                dest->userdata = src->userdata;
 
index 27988c2702fad7203da45b0ad1fb9d5dddba9ad9..28def306053c0c1c9b644d2e0c8d641ccbd4ee8b 100644 (file)
@@ -72,6 +72,8 @@ void scols_unref_column(struct libscols_column *cl)
                list_del(&cl->cl_columns);
                scols_reset_cell(&cl->header);
                free(cl->color);
+               free(cl->uri);
+               ul_buffer_free_data(&cl->uri_buf);
                free(cl->safechars);
                free(cl->wrap_data);
                free(cl->shellvar);
@@ -101,6 +103,8 @@ struct libscols_column *scols_copy_column(const struct libscols_column *cl)
 
        if (scols_column_set_color(ret, cl->color))
                goto err;
+       if (scols_column_set_uri(ret, cl->uri))
+               goto err;
        if (scols_cell_copy_content(&ret->header, &cl->header))
                goto err;
 
@@ -431,6 +435,42 @@ const char *scols_column_get_color(const struct libscols_column *cl)
        return cl->color;
 }
 
+/**
+ * scols_column_set_uri:
+ * @cl: a pointer to a struct libscols_column instance
+ * @uri: URI string
+ *
+ * The default URI prefix for cells is used when creating hyperlinks. However,
+ * it can still be disabled for selected cells using scols_cell_disable_uri().
+ * See also scols_cell_set_uri().
+ *
+ * The final cell URI is composed of the column-uri, cell-uri, and cell-data.
+ * The column-uri and/or cell-uri must be set for this feature to be enabled.
+ *
+ * column-uri  cell-uri                  cell-data      final-URI                 link
+ * -----------------------------------------------------------------------------------
+ *             file://host/path/foo.txt  foo            file://host/path/foo.txt  foo
+ * file://host /path/foo.txt             foo            file://host/path/foo.txt  foo
+ * file://host                           /path/foo.txt  file://host/path/foo.txt  /path/foo.txt
+ *
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_uri(struct libscols_column *cl, const char *uri)
+{
+       return strdup_to_struct_member(cl, uri, uri);
+}
+
+/**
+ * scols_column_get_uri:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The current URI setting of the column @cl.
+ */
+const char *scols_column_get_uri(const struct libscols_column *cl)
+{
+       return cl->uri;
+}
 
 /**
  * scols_wrapnl_nextchunk:
index edee65b7e5c99aae63b9cc7f6f36b82b6014c3b0..1acff2f0e9b06d2d02c8c3f8fc4d7e5bfeef859d 100644 (file)
@@ -207,6 +207,10 @@ extern size_t scols_cell_get_datasiz(struct libscols_cell *ce);
 extern int scols_cell_set_color(struct libscols_cell *ce, const char *color);
 extern const char *scols_cell_get_color(const struct libscols_cell *ce);
 
+extern int scols_cell_set_uri(struct libscols_cell *ce, const char *uri);
+extern const char *scols_cell_get_uri(const struct libscols_cell *ce);
+extern int scols_cell_disable_uri(struct libscols_cell *ce, int disable);
+
 extern int scols_cell_set_flags(struct libscols_cell *ce, int flags);
 extern int scols_cell_get_flags(const struct libscols_cell *ce);
 extern int scols_cell_get_alignment(const struct libscols_cell *ce);
@@ -251,6 +255,9 @@ extern int scols_column_set_color(struct libscols_column *cl, const char *color)
 extern const char *scols_column_get_color(const struct libscols_column *cl);
 extern struct libscols_table *scols_column_get_table(const struct libscols_column *cl);
 
+extern int scols_column_set_uri(struct libscols_column *cl, const char *uri);
+extern const char *scols_column_get_uri(const struct libscols_column *cl);
+
 extern int scols_column_set_name(struct libscols_column *cl, const char *name);
 extern const char *scols_column_get_name(struct libscols_column *cl);
 extern const char *scols_column_get_name_as_shellvar(struct libscols_column *cl);
index e74d928627b2d6340421a8f614ca3985ddc2ba71..ce77b0df3c3f042b4463ff6c2e3ca395e2d32b75 100644 (file)
@@ -250,4 +250,9 @@ SMARTCOLS_2.41 {
        scols_line_sprintf;
        scols_line_vprintf_column;
        scols_line_sprintf_column;
+       scols_cell_set_uri;
+       scols_cell_get_uri;
+       scols_cell_disable_uri;
+       scols_column_set_uri;
+       scols_column_get_uri;
 } SMARTCOLS_2.40;
index 89d6aba0923ff457aff2167bbaa4617fb751c603..15303330214a73cdad1e831345f173b006b88592 100644 (file)
@@ -24,6 +24,7 @@
 #include "mbsalign.h"
 #include "carefulputc.h"
 #include "smartcolsP.h"
+#include "ttyutils.h"
 
 /* Fallback for symbols
  *
@@ -307,6 +308,50 @@ static void fputs_color_line_close(struct libscols_table *tb)
        fputs_color_reset(tb);
 }
 
+/* @buf is the cell data generated by __cursor_to_buffer(). We cannot use
+ * scols_cell_get_data() directly because there may be a defined wrap function
+ * and we need the URI for the segment of the data. For example, when the cell
+ * contains multiple filenames.
+ *
+ * The number of URIs can be enormous (due to the number of lines in the table).
+ * Therefore, the goal is to avoid allocation, and a buffer specific to each
+ * column is used and shared for all lines.
+ */
+static const char *mk_cell_uri(struct libscols_column *cl,
+                              struct libscols_cell *ce,
+                              struct ul_buffer *buf)
+{
+       char *path;
+
+       /* URI disabled at all */
+       if (ce->no_uri)
+               return NULL;
+
+       /* No column prefix, return cell URI (or NULL if undefined) */
+       if (!cl->uri)
+               return ce->uri;
+
+       /* Compose URI from column-uri + path. The path is ce->uri or cell data. */
+       path = ce->uri;
+
+       if (!path && buf) {
+               /* The buffer may already contain tree data, so we need to skip it. */
+               path = ul_buffer_get_pointer(buf, SCOLS_BUFPTR_TREEEND);
+               if (!path)
+                       path = ul_buffer_get_string(buf, NULL, NULL);
+       }
+
+       if (!path)
+               return NULL;
+
+       ul_buffer_reset_data(&cl->uri_buf);
+       ul_buffer_append_string(&cl->uri_buf, cl->uri);
+
+       ul_buffer_append_string(&cl->uri_buf, path);
+
+       return ul_buffer_get_string(&cl->uri_buf, NULL, NULL);
+}
+
 /* print padding or ASCII-art instead of data of @cl */
 static void print_empty_cell(struct libscols_table *tb,
                          struct libscols_column *cl,
@@ -414,6 +459,7 @@ static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf)
        struct libscols_column *cl;
        struct libscols_cell *ce;
        char *data;
+       const char *uri = NULL;
        size_t i, width = 0, len = 0, bytes = 0;
 
        scols_table_get_cursor(tb, &ln, &cl, &ce);
@@ -424,6 +470,9 @@ static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf)
 
        DBG(COL, ul_debugobj(cl, "printing pending data"));
 
+       if (cl->uri || ce->uri)
+               uri = mk_cell_uri(cl, ce, buf);
+
        if (scols_table_is_noencoding(tb))
                data = ul_buffer_get_data(buf, &bytes, &len);
        else
@@ -444,7 +493,11 @@ static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf)
        }
 
        fputs_color_cell_open(tb, cl, ln, ce);
-       fputs(data, tb->out);
+
+       if (uri)
+               ul_fputs_hyperlink(uri, data, tb->out);
+       else
+               fputs(data, tb->out);
 
        /* minout -- don't fill */
        if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
@@ -537,7 +590,7 @@ static int print_data(struct libscols_table *tb, struct ul_buffer *buf)
        struct libscols_cell *ce;
        size_t len = 0, i, width, bytes;
        char *data = NULL;
-       const char *name = NULL;
+       const char *name = NULL, *uri = NULL;
        int is_last;
 
        assert(tb);
@@ -585,6 +638,9 @@ static int print_data(struct libscols_table *tb, struct ul_buffer *buf)
                break;          /* continue below */
        }
 
+       if (cl->uri || ce->uri)
+               uri = mk_cell_uri(cl, ce, buf);
+
        /* Encode. Note that 'len' and 'width' are number of glyphs not bytes.
         */
        if (scols_table_is_noencoding(tb))
@@ -632,7 +688,20 @@ static int print_data(struct libscols_table *tb, struct ul_buffer *buf)
                                fputs(cellpadding_symbol(tb), tb->out);
                        len = width;
                }
-               fputs(data, tb->out);
+
+               if (uri) {
+                       char *link = data;
+                       size_t skip = ul_buffer_get_pointer_length(buf, SCOLS_BUFPTR_TREEEND);
+
+                       /* Print hyperlink after tree lines */
+                       if (skip) {
+                               link = data + skip;
+                               for (i = 0; i < skip; i++)
+                                       fputc(data[i], tb->out);
+                       }
+                       ul_fputs_hyperlink(uri, link, tb->out);
+               } else
+                       fputs(data, tb->out);
        }
 
        /* minout -- don't fill */
@@ -986,6 +1055,8 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf)
                        continue;
 
                ul_buffer_reset_data(buf);
+               if (cl->uri)
+                       scols_cell_disable_uri(&cl->header, 1);
                scols_table_set_cursor(tb, NULL, cl, &cl->header);
 
                if (cl->is_groups
index 444661ee6a113873a972ce4062c8ed3f69208b5a..68b46886e7989fb6e9e3110bfbb70465567d9aff 100644 (file)
@@ -86,11 +86,13 @@ struct libscols_cell {
        char    *data;
        size_t  datasiz;
        char    *color;
+       char    *uri;
        void    *userdata;
        int     flags;
        size_t  width;
 
-       unsigned int is_filled : 1;
+       unsigned int is_filled : 1,
+                    no_uri : 1;
 };
 
 extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn);
@@ -123,6 +125,8 @@ struct libscols_column {
 
        int     flags;
        char    *color;         /* default column color */
+       char    *uri;           /* default column URI prefix */
+       struct ul_buffer uri_buf; /* temporary buffer to compose URIs */
        char    *safechars;     /* do not encode this bytes */
 
        int (*cmpfunc)(struct libscols_cell *,