From: Karel Zak Date: Thu, 28 Nov 2024 11:35:37 +0000 (+0100) Subject: libsmartcols: add support for terminal hyperlinks X-Git-Tag: v2.42-start~120^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c267c3ef882843f1465d9809f09914d0ccf09314;p=thirdparty%2Futil-linux.git libsmartcols: add support for terminal hyperlinks Signed-off-by: Karel Zak --- diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index 5f4c736a7..94bb9ef05 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -2,17 +2,20 @@ cell 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 diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c index df45667ec..4c279166e 100644 --- a/libsmartcols/src/cell.c +++ b/libsmartcols/src/cell.c @@ -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; diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c index 27988c270..28def3060 100644 --- a/libsmartcols/src/column.c +++ b/libsmartcols/src/column.c @@ -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: diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index edee65b7e..1acff2f0e 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -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); diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index e74d92862..ce77b0df3 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -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; diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index 89d6aba09..153033302 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -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 diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 444661ee6..68b46886e 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -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 *,