struct file *file __attribute__((__unused__)),
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
const char *partition, *devdrv;
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
struct cdev *cdev = (struct cdev *)file;
const struct cdev_ops *ops = cdev->cdev_ops;
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
struct file *file __attribute__((__unused__)),
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
const char *ename;
struct file *file __attribute__((__unused__)),
struct libscols_line *ln __attribute__((__unused__)),
int column_id,
- size_t column_index __attribute__((__unused__)))
+ size_t column_index __attribute__((__unused__)),
+ const char *uri __attribute__((__unused__)))
{
switch(column_id) {
case COL_NAME:
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
mode_t ftype;
if (file->name
&& scols_line_set_data(ln, column_index, file->name))
err(EXIT_FAILURE, _("failed to add output data"));
+
+ if (uri && (!file->name || *file->name != '/')) {
+ struct libscols_cell *ce = scols_line_get_cell(ln, column_index);
+ if (ce)
+ scols_cell_disable_uri(ce, 1);
+ }
return true;
case COL_STTYPE:
case COL_TYPE:
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
char *name = NULL;
struct file *file __attribute__((__unused__)),
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
switch (column_id) {
case COL_TYPE:
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
char *buf = NULL;
*--dump-counters*::
Dump the definition of counters used in *--summary* output.
+*--hyperlink*[=_mode_]::
+Print paths as terminal hyperlinks. The _mode_ can be set to "always", "never", or "auto". The optional argument _when_ can be set to "auto", "never", or "always". If the _when_ argument is omitted, it will default to "auto". The "auto" setting means that hyperlinks will only be used if the output is on a terminal.
+
*-H*, *--list-columns*::
List available columns that you can specify at *--output* option.
struct filler_data {
struct proc *proc;
struct file *file;
+ const char *uri;
};
struct lsfd_control {
sockets_only : 1, /* display only SOCKETS */
show_xmode : 1; /* XMODE column is enabled. */
+ char *uri;
+
struct libscols_filter *filter; /* filter */
struct libscols_filter **ct_filters; /* counters (NULL terminated array) */
};
}
static struct libscols_column *add_column(struct libscols_table *tb,
- int id, int extra)
+ int id, int extra, char *uri)
{
const struct colinfo *col;
struct libscols_column *cl;
NULL);
scols_column_set_safechars(cl, "\n");
}
+ if (!(extra & SCOLS_FL_HIDDEN) && uri &&
+ (id == COL_NAME || id == COL_KNAME))
+ scols_column_set_uri(cl, uri);
}
return cl;
if (ncolumns >= ARRAY_SIZE(columns))
errx(EXIT_FAILURE, _("too many columns are added via filter expression"));
- cl = add_column(ctl->tb, colid, SCOLS_FL_HIDDEN);
+ cl = add_column(ctl->tb, colid, SCOLS_FL_HIDDEN, ctl->uri);
if (!cl)
err(EXIT_FAILURE, _("failed to allocate output column"));
columns[ncolumns++] = colid;
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
const struct file_class *class = file->class;
while (class) {
if (class->fill_column
&& class->fill_column(proc, file, ln,
- column_id, column_index))
+ column_id, column_index, uri))
break;
class = class->super;
}
{
struct filler_data *fid = (struct filler_data *) userdata;
- fill_column(fid->proc, fid->file, ln, get_column_id(colnum), colnum);
+ fill_column(fid->proc, fid->file, ln, get_column_id(colnum), colnum,
+ fid->uri);
return 0;
}
static void convert_file(struct proc *proc,
struct file *file,
- struct libscols_line *ln)
+ struct libscols_line *ln,
+ const char *uri __attribute__((__unused__)))
{
size_t i;
for (i = 0; i < ncolumns; i++) {
if (scols_line_is_filled(ln, i))
continue;
- fill_column(proc, file, ln, get_column_id(i), i);
+ fill_column(proc, file, ln, get_column_id(i), i, uri);
}
}
int status = 0;
struct filler_data fid = {
.proc = proc,
- .file = file
+ .file = file,
+ .uri = ctl->uri,
};
scols_filter_set_filler_cb(ctl->filter,
}
}
- convert_file(proc, file, ln);
+ convert_file(proc, file, ln, ctl->uri);
if (!ctl->ct_filters)
continue;
fputs(_(" --debug-filter dump the internal data structure of filter and exit\n"), out);
fputs(_(" -C, --counter <name>:<expr> define custom counter for --summary output\n"), out);
fputs(_(" --dump-counters dump counter definitions\n"), out);
+ fputs(_(" --hyperlink[=mode] print paths as terminal hyperlinks (always, never, or auto)\n"), out);
fputs(_(" --summary[=<when>] print summary information (only, append, or never)\n"), out);
fputs(_(" --_drop-privilege (testing purpose) do setuid(1) just after starting\n"), out);
OPT_SUMMARY,
OPT_DUMP_COUNTERS,
OPT_DROP_PRIVILEGE,
+ OPT_HYPERLINK
};
static const struct option longopts[] = {
{ "noheadings", no_argument, NULL, 'n' },
{ "dump-counters",no_argument, NULL, OPT_DUMP_COUNTERS },
{ "list-columns",no_argument, NULL, 'H' },
{ "_drop-privilege",no_argument,NULL,OPT_DROP_PRIVILEGE },
+ { "hyperlink", optional_argument, NULL, OPT_HYPERLINK },
{ NULL, 0, NULL, 0 },
};
if (setuid(1) == -1)
err(EXIT_FAILURE, _("failed to drop privilege"));
break;
+ case OPT_HYPERLINK:
+ if (hyperlinkwanted_or_err(optarg,
+ _("invalid hyperlink argument")))
+ ctl.uri = xgethosturi(NULL);
+ break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
/* create output columns */
for (i = 0; i < ncolumns; i++) {
- struct libscols_column *cl = add_column(ctl.tb, get_column_id(i), 0);
+ struct libscols_column *cl = add_column(ctl.tb, get_column_id(i), 0, ctl.uri);
if (!cl)
err(EXIT_FAILURE, _("failed to allocate output column"));
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index);
+ size_t column_index,
+ const char *uri);
int (*handle_fdinfo)(struct file *file, const char *key, const char* value);
void (*attach_xinfo)(struct file *file);
void (*initialize_content)(struct file *file);
void decode_source(char *buf, size_t bufsize, unsigned int dev_major, unsigned int dev_minor,
enum decode_source_level level);
+
/*
* Name managing
*/
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
struct sock *sock = (struct sock *)file;
struct file *file,
struct libscols_line *ln,
int column_id,
- size_t column_index)
+ size_t column_index,
+ const char *uri __attribute__((__unused__)))
{
char *str = NULL;
struct unkn *unkn = (struct unkn *)file;
--- /dev/null
+# NAME
+00000000 1b 5d 38 3b 3b 66 69 6c 65 3a 2f 2f 2f 65 74 63 |.]8;;file:///etc|
+00000010 2f 70 61 73 73 77 64 1b 5c 2f 65 74 63 2f 70 61 |/passwd.\/etc/pa|
+00000020 73 73 77 64 1b 5d 38 3b 3b 1b 5c 0a |sswd.]8;;.\.|
+0000002c
+# KNAME
+00000000 1b 5d 38 3b 3b 66 69 6c 65 3a 2f 2f 2f 65 74 63 |.]8;;file:///etc|
+00000010 2f 70 61 73 73 77 64 1b 5c 2f 65 74 63 2f 70 61 |/passwd.\/etc/pa|
+00000020 73 73 77 64 1b 5d 38 3b 3b 1b 5c 0a |sswd.]8;;.\.|
+0000002c
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2024 Masatake YAMATO <yamato@redhat.com>
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="--hyperlink option"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+ts_check_test_command "$TS_HELPER_SYSINFO"
+ts_check_test_command "$TS_CMD_HEXDUMP"
+
+ts_check_prog "sed"
+
+HOSTNAME=
+if ! HOSTNAME=$("$TS_HELPER_SYSINFO" hostname); then
+ ts_skip "failed in getting hostname"
+fi
+HOSTNAME_PATTERN=${HOSTNAME//./\\.}
+replace_hostname()
+{
+ # Make the output host independent: replacing file://foo.example.com/
+ # with file:///
+ sed -e 's@file://'"${HOSTNAME_PATTERN}"'/@file:///@'
+}
+
+ts_cd "$TS_OUTDIR"
+PID=
+FD=3
+EXPR='(FD == '"$FD"')'
+
+ts_init_subtest "regular-file"
+{
+ coproc MKFDS { "$TS_HELPER_MKFDS" ro-regular-file $FD; }
+ if read -r -u "${MKFDS[0]}" PID; then
+ for o in NAME KNAME; do
+ echo "# $o"
+ "${TS_CMD_LSFD}" -n --hyperlink=always -o "$o" -p "${PID}" -Q "$EXPR" | replace_hostname | "$TS_CMD_HEXDUMP" -C
+ done
+ echo DONE >&"${MKFDS[1]}"
+ fi
+} > "$TS_OUTPUT" 2>&1
+wait "${MKFDS_PID}"
+ts_finalize_subtest
+
+ts_finalize