On 21/08/17 04:28, Pádraig Brady wrote:
> On 05/05/17 14:05, Pádraig Brady wrote:
>> On 04/05/17 13:14, Egmont Koblinger wrote:
>>> Hi,
>>>
>>> Recently two popular terminal emulators, GNOME Terminal and iTerm2
>>> have implemented a brand new feature: explicit hyperlinks.
>>>
>>> Unlike the existing functionality of most terminal emulators of
>>> automatically detecting URLs that appear on the screen, this time it's
>>> like hyperlinks on web pages: the link target is specified by the OSC
>>> 8 escape sequence and the visible text can be an arbitrary piece of
>>> text.
>>>
>>> As I've played with this feature, I found a really compelling use
>>> case: listing files in a way that all of them are hyperlinks to
>>> "file://...". It makes it as easy and convenient as a Ctrl+click to
>>> open them in their preferred graphical application.
>>>
>>> (For even more fun, there's a pending demo patch to GNOME Terminal to
>>> display a preview of certain local files on mouseover. We're uncertain
>>> yet if we'll finalize and ship it or not.)
>>>
>>> I've created a quick proof of concept patch for a new cmdline option
>>> "ls --hyperlink=always/auto/never", have set it up in my "ls" alias,
>>> and been using that happily for a few weeks now. Please find it at
>>> https://bugzilla.gnome.org/show_bug.cgi?id=779734#c126. Note that it
>>> contains a couple of issues, e.g. I forgot to free some data, and it
>>> does stupid things around symlinks. As said, it's a demo, not a fully
>>> polished patch.
>>>
>>> I'd be curious to hear if you like this idea, and would be happy to
>>> see this option appearing in mainstream coreutils.
>>>
>>> Please see 
>>> https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
>>> for details about the feature.
>>>
>>> Let me know if you have any questions, concerns etc. (cc me, I'm not
>>> subscribed).
>>
>> Interesting.
>> This could apply to any util really that displays file names,
>> though ls would be the most useful.
>> Generally it also seems useful to the case where a file has a non 
>> representable name
>> (well not cleanly at least without $'shell quoting').
> 
> I've attached an implementation for ls --hyperlink.
> 
> Some notes:
> 
> I used canonicalize_filename_mode for consistency with other tools,
> and to relax the canonicalization with CAN_MISSING.
> I did this because access may be possible outside current shell context,
> and also we don't want to diagnose perm issues that would
> either not otherwise need diagnosing, or would be diagnosed
> independently by stat() etc.
> 
> --dired is handled appropriately, since the terminal codes
> are similar to non displayed color codes and can be skipped similarly.
> 
> I used more stringent escaping as per rfc3986
> because I think there are security issues with the more lax
> encoding described at 
> https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
> and I don't see any advantage of using that more lax encoding.
> If terminals provided a copy link functionality, or
> the links otherwise got outside the terminal context
> then there could be problematic handling of non encoded items.
> For example if you didn't encode '?' and the link was passed
> to a browser, then anything after that would be ignored.
> Also '%' needs to be encoded for the same reasons where
> files other than intended could be resolved.
> 
> This should work on windows where c: is separated from the
> hostname appropriately and '\' are converted to '/',
> though I haven't tested there.
> 
> I've tested with valgrind with multiple specified dirs
> and there are no leaks.
> 
> I was tempted to add --hyperlink to realpath, though I'm not sure it's needed.
> 
> I was tempted to add a --hyperlink=raw option to output just the file://...
> portion without the terminal codes, as older terminals highlight/process
> those fine and they might be otherwise usefule.  Though again I wasn't sure.
> 

Version 2 attached which fixes a small mem leak,
ensures that URLs are aligned in padded output,
and disables --dired with --hyperlink since it didn't work in testing
and precluded the URL alignment method used (which excludes quotes from the 
URL).
It might be useful to support //DIRED-OPTIONS// --hyperlink to identify
the URI portion to emacs, but we can do that separately if useful.

cheers,
Pádraig.

From 7cca5fc3279cb5bf54bb37344164078dbee53b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Mon, 21 Aug 2017 03:53:36 -0700
Subject: [PATCH] ls: support --hyperlink to output file:// URIs

Terminals such as iTerm2 and VTE based terminals
(as of version 0.49.1), support hyperlinks when
passed terminals codes as described at:
https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

* src/ls.c (gobble_file): Allocate an absolute file name to output.
(quote_name): Output the absolute name with the appropriate codes.
(file_escape): A new function to encode file names as per rfc8089.
(main): Handle the new option and call the file_escape_init() helper.
Disable --dired when --hyperlink is specified.
(print_dir): Get the absolute file name here too, so that the
directory name can be linkified.
* NEWS: Mention the new feature.
* tests/ls/hyperlink.sh: Add a new test.
* tests/local.mk: Reference the new test.
* doc/coreutils.texi (ls invocation): Describe --hyperlink.
---
 NEWS                  |   3 +
 doc/coreutils.texi    |  26 ++++++-
 src/ls.c              | 198 ++++++++++++++++++++++++++++++++++++++++----------
 tests/local.mk        |   1 +
 tests/ls/hyperlink.sh |  61 ++++++++++++++++
 5 files changed, 248 insertions(+), 41 deletions(-)
 create mode 100755 tests/ls/hyperlink.sh

diff --git a/NEWS b/NEWS
index d37195e..00bfe5a 100644
--- a/NEWS
+++ b/NEWS
@@ -81,6 +81,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   by prefixing the last specified number like --tabs=1,+8 which is
   useful for visualizing diff output for example.
 
+  ls supports a new --hyperlink[=when] option to output file://
+  format links to files, supported by some terminals.
+
   split supports a new --hex-suffixes[=from] option to create files with
   lower case hexadecimal suffixes, similar to the --numeric-suffixes option.
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 8f1cb4c..173f064 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -7857,9 +7857,8 @@ may be omitted, or one of:
 @end itemize
 Specifying @option{--color} and no @var{when} is equivalent to
 @option{--color=always}.
-Piping a colorized listing through a pager like @command{more} or
-@command{less} usually produces unreadable results.  However, using
-@code{more -f} does seem to work.
+If piping a colorized listing through a pager like @command{less},
+use the @option{-R} option to pass the color codes to the terminal.
 
 @vindex LS_COLORS
 @vindex SHELL @r{environment variable, and color}
@@ -7905,6 +7904,27 @@ command line unless the @option{--dereference-command-line} (@option{-H}),
 Append a character to each file name indicating the file type.  This is
 like @option{-F}, except that executables are not marked.
 
+@item --hyperlink [=@var{when}]
+@opindex --hyperlink
+@cindex hyperlink, linking to files
+Output codes recognized by some terminals to link
+to files using the @samp{file://} URI format.
+@var{when} may be omitted, or one of:
+@itemize @bullet
+@item none
+@vindex none @r{hyperlink option}
+- Do not use hyperlinks at all.  This is the default.
+@item auto
+@vindex auto @r{hyperlink option}
+@cindex terminal, using hyperlink iff
+- Only use hyperlinks if standard output is a terminal.
+@item always
+@vindex always @r{hyperlink option}
+- Always use hyperlinks.
+@end itemize
+Specifying @option{--hyperlink} and no @var{when} is equivalent to
+@option{--hyperlink=always}.
+
 @item --indicator-style=@var{word}
 @opindex --indicator-style
 Append a character indicator with style @var{word} to entry names,
diff --git a/src/ls.c b/src/ls.c
index bb97e98..9ed4913 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -110,6 +110,9 @@
 #include "areadlink.h"
 #include "mbsalign.h"
 #include "dircolors.h"
+#include "xgethostname.h"
+#include "c-ctype.h"
+#include "canonicalize.h"
 
 /* Include <sys/capability.h> last to avoid a clash of <sys/types.h>
    include guards with some premature versions of libcap.
@@ -200,6 +203,9 @@ struct fileinfo
     /* For symbolic link, name of the file linked to, otherwise zero.  */
     char *linkname;
 
+    /* For terminal hyperlinks. */
+    char *absolute_name;
+
     struct stat stat;
 
     enum filetype filetype;
@@ -248,7 +254,8 @@ static size_t quote_name (char const *name,
                           struct quoting_options const *options,
                           int needs_general_quoting,
                           const struct bin_str *color,
-                          bool allow_pad, struct obstack *stack);
+                          bool allow_pad, struct obstack *stack,
+                          char const *absolute_name);
 static size_t quote_name_buf (char **inbuf, size_t bufsize, char *name,
                               struct quoting_options const *options,
                               int needs_general_quoting, size_t *width,
@@ -346,6 +353,8 @@ static size_t sorted_file_alloc;
 
 static bool color_symlink_as_referent;
 
+static char const *hostname;
+
 /* mode of appropriate file for colorization */
 #define FILE_OR_LINK_MODE(File) \
     ((color_symlink_as_referent && (File)->linkok) \
@@ -548,17 +557,19 @@ ARGMATCH_VERIFY (indicator_style_args, indicator_style_types);
 
 static bool print_with_color;
 
+static bool print_hyperlink;
+
 /* Whether we used any colors in the output so far.  If so, we will
    need to restore the default color later.  If not, we will need to
    call prep_non_filename_text before using color for the first time. */
 
 static bool used_color = false;
 
-enum color_type
+enum when_type
   {
-    color_never,		/* 0: default or --color=never */
-    color_always,		/* 1: --color=always */
-    color_if_tty		/* 2: --color=tty */
+    when_never,		/* 0: default or --color=never */
+    when_always,	/* 1: --color=always */
+    when_if_tty		/* 2: --color=tty */
   };
 
 enum Dereference_symlink
@@ -814,6 +825,7 @@ enum
   FULL_TIME_OPTION,
   GROUP_DIRECTORIES_FIRST_OPTION,
   HIDE_OPTION,
+  HYPERLINK_OPTION,
   INDICATOR_STYLE_OPTION,
   QUOTING_STYLE_OPTION,
   SHOW_CONTROL_CHARS_OPTION,
@@ -864,6 +876,7 @@ static struct option const long_options[] =
   {"time", required_argument, NULL, TIME_OPTION},
   {"time-style", required_argument, NULL, TIME_STYLE_OPTION},
   {"color", optional_argument, NULL, COLOR_OPTION},
+  {"hyperlink", optional_argument, NULL, HYPERLINK_OPTION},
   {"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
   {"context", no_argument, 0, 'Z'},
   {"author", no_argument, NULL, AUTHOR_OPTION},
@@ -904,20 +917,20 @@ static enum time_type const time_types[] =
 };
 ARGMATCH_VERIFY (time_args, time_types);
 
-static char const *const color_args[] =
+static char const *const when_args[] =
 {
   /* force and none are for compatibility with another color-ls version */
   "always", "yes", "force",
   "never", "no", "none",
   "auto", "tty", "if-tty", NULL
 };
-static enum color_type const color_types[] =
+static enum when_type const when_types[] =
 {
-  color_always, color_always, color_always,
-  color_never, color_never, color_never,
-  color_if_tty, color_if_tty, color_if_tty
+  when_always, when_always, when_always,
+  when_never, when_never, when_never,
+  when_if_tty, when_if_tty, when_if_tty
 };
-ARGMATCH_VERIFY (color_args, color_types);
+ARGMATCH_VERIFY (when_args, when_types);
 
 /* Information about filling a column.  */
 struct column_info
@@ -1066,6 +1079,14 @@ first_percent_b (char const *fmt)
   return NULL;
 }
 
+static char RFC3986[256];
+static void
+file_escape_init (void)
+{
+  for (int i = 0; i < 256; i++)
+    RFC3986[i] |= c_isalnum (i) || i == '~' || i == '-' || i == '.' || i == '_';
+}
+
 /* Read the abbreviated month names from the locale, to align them
    and to determine the max width of the field and to truncate names
    greater than our max allowed.
@@ -1500,6 +1521,17 @@ main (int argc, char **argv)
       obstack_init (&subdired_obstack);
     }
 
+  if (print_hyperlink)
+    {
+      file_escape_init ();
+
+      hostname = xgethostname ();
+      /* The hostname is generally ignored,
+         so ignore failures obtaining it.  */
+      if (! hostname)
+        hostname = "";
+    }
+
   cwd_n_alloc = 100;
   cwd_file = xnmalloc (cwd_n_alloc, sizeof *cwd_file);
   cwd_n_used = 0;
@@ -1783,6 +1815,7 @@ decode_switches (int argc, char **argv)
             format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
           print_block_size = false;	/* disable -s */
           print_with_color = false;	/* disable --color */
+          print_hyperlink = false;	/* disable --hyperlink */
           break;
 
         case FILE_TYPE_INDICATOR_OPTION: /* --file-type */
@@ -1985,14 +2018,14 @@ decode_switches (int argc, char **argv)
           {
             int i;
             if (optarg)
-              i = XARGMATCH ("--color", optarg, color_args, color_types);
+              i = XARGMATCH ("--color", optarg, when_args, when_types);
             else
               /* Using --color with no argument is equivalent to using
                  --color=always.  */
-              i = color_always;
+              i = when_always;
 
-            print_with_color = (i == color_always
-                                || (i == color_if_tty
+            print_with_color = (i == when_always
+                                || (i == when_if_tty
                                     && isatty (STDOUT_FILENO)));
 
             if (print_with_color)
@@ -2005,6 +2038,22 @@ decode_switches (int argc, char **argv)
             break;
           }
 
+        case HYPERLINK_OPTION:
+          {
+            int i;
+            if (optarg)
+              i = XARGMATCH ("--hyperlink", optarg, when_args, when_types);
+            else
+              /* Using --hyperlink with no argument is equivalent to using
+                 --hyperlink=always.  */
+              i = when_always;
+
+            print_hyperlink = (i == when_always
+                               || (i == when_if_tty
+                                   && isatty (STDOUT_FILENO)));
+            break;
+          }
+
         case INDICATOR_STYLE_OPTION:
           indicator_style = XARGMATCH ("--indicator-style", optarg,
                                        indicator_style_args,
@@ -2102,7 +2151,7 @@ decode_switches (int argc, char **argv)
   /* --dired is meaningful only with --format=long (-l).
      Otherwise, ignore it.  FIXME: warn about this?
      Alternatively, make --dired imply --format=long?  */
-  if (dired && format != long_format)
+  if (dired && (format != long_format || print_hyperlink))
     dired = false;
 
   /* If -c or -u is specified and not -l (or any other option that implies -l),
@@ -2715,8 +2764,18 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
       first = false;
       DIRED_INDENT ();
 
+      char *absolute_name = NULL;
+      if (print_hyperlink)
+        {
+          absolute_name = canonicalize_filename_mode (name, CAN_MISSING);
+          if (! absolute_name)
+            file_failure (command_line_arg,
+                          _("error canonicalizing %s"), name);
+        }
       quote_name (realname ? realname : name, dirname_quoting_options, -1,
-                  NULL, true, &subdired_obstack);
+                  NULL, true, &subdired_obstack, absolute_name);
+
+      free (absolute_name);
 
       DIRED_FPUTS_LITERAL (":\n", stdout);
     }
@@ -2909,6 +2968,7 @@ free_ent (struct fileinfo *f)
 {
   free (f->name);
   free (f->linkname);
+  free (f->absolute_name);
   if (f->scontext != UNKNOWN_SECURITY_CONTEXT)
     {
       if (is_smack_enabled ())
@@ -3072,6 +3132,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
     }
 
   if (command_line_arg
+      || print_hyperlink
       || format_needs_stat
       /* When coloring a directory (we may know the type from
          direct.d_type), we have to stat it in order to indicate
@@ -3110,22 +3171,31 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
 
     {
       /* Absolute name of this file.  */
-      char *absolute_name;
+      char *full_name;
       bool do_deref;
       int err;
 
       if (name[0] == '/' || dirname[0] == 0)
-        absolute_name = (char *) name;
+        full_name = (char *) name;
       else
         {
-          absolute_name = alloca (strlen (name) + strlen (dirname) + 2);
-          attach (absolute_name, dirname, name);
+          full_name = alloca (strlen (name) + strlen (dirname) + 2);
+          attach (full_name, dirname, name);
+        }
+
+      if (print_hyperlink)
+        {
+          f->absolute_name = canonicalize_filename_mode (full_name,
+                                                         CAN_MISSING);
+          if (! f->absolute_name)
+            file_failure (command_line_arg,
+                          _("error canonicalizing %s"), full_name);
         }
 
       switch (dereference)
         {
         case DEREF_ALWAYS:
-          err = stat (absolute_name, &f->stat);
+          err = stat (full_name, &f->stat);
           do_deref = true;
           break;
 
@@ -3134,7 +3204,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
           if (command_line_arg)
             {
               bool need_lstat;
-              err = stat (absolute_name, &f->stat);
+              err = stat (full_name, &f->stat);
               do_deref = true;
 
               if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
@@ -3147,14 +3217,14 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
                 break;
 
               /* stat failed because of ENOENT, maybe indicating a dangling
-                 symlink.  Or stat succeeded, ABSOLUTE_NAME does not refer to a
+                 symlink.  Or stat succeeded, FULL_NAME does not refer to a
                  directory, and --dereference-command-line-symlink-to-dir is
                  in effect.  Fall through so that we call lstat instead.  */
             }
           FALLTHROUGH;
 
         default: /* DEREF_NEVER */
-          err = lstat (absolute_name, &f->stat);
+          err = lstat (full_name, &f->stat);
           do_deref = false;
           break;
         }
@@ -3165,7 +3235,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
              an exit status of 2.  For other files, stat failure
              provokes an exit status of 1.  */
           file_failure (command_line_arg,
-                        _("cannot access %s"), absolute_name);
+                        _("cannot access %s"), full_name);
           if (command_line_arg)
             return 0;
 
@@ -3180,13 +3250,13 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
       /* Note has_capability() adds around 30% runtime to 'ls --color'  */
       if ((type == normal || S_ISREG (f->stat.st_mode))
           && print_with_color && is_colored (C_CAP))
-        f->has_capability = has_capability_cache (absolute_name, f);
+        f->has_capability = has_capability_cache (full_name, f);
 
       if (format == long_format || print_scontext)
         {
           bool have_scontext = false;
           bool have_acl = false;
-          int attr_len = getfilecon_cache (absolute_name, f, do_deref);
+          int attr_len = getfilecon_cache (full_name, f, do_deref);
           err = (attr_len < 0);
 
           if (err == 0)
@@ -3210,7 +3280,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
 
           if (err == 0 && format == long_format)
             {
-              int n = file_has_acl_cache (absolute_name, f);
+              int n = file_has_acl_cache (full_name, f);
               err = (n < 0);
               have_acl = (0 < n);
             }
@@ -3223,7 +3293,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
           any_has_acl |= f->acl_type != ACL_T_NONE;
 
           if (err)
-            error (0, errno, "%s", quotef (absolute_name));
+            error (0, errno, "%s", quotef (full_name));
         }
 
       if (S_ISLNK (f->stat.st_mode)
@@ -3231,8 +3301,8 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
         {
           struct stat linkstats;
 
-          get_link_name (absolute_name, f, command_line_arg);
-          char *linkname = make_link_name (absolute_name, f->linkname);
+          get_link_name (full_name, f, command_line_arg);
+          char *linkname = make_link_name (full_name, f->linkname);
 
           /* Use the slower quoting path for this entry, though
              don't update CWD_SOME_QUOTED since alignment not affected.  */
@@ -4373,10 +4443,33 @@ quote_name_width (const char *name, struct quoting_options const *options,
   return width;
 }
 
+/* %XX escape any input out of range as defined in RFC3986,
+   and also if PATH, convert all path separators to '/'.  */
+static char *
+file_escape (const char *str, bool path)
+{
+  char *esc = xnmalloc (3, strlen (str) + 1);
+  char *p = esc;
+  while (*str)
+    {
+      if (path && ISSLASH (*str))
+        {
+          *p++ = '/';
+          str++;
+        }
+      else if (RFC3986[to_uchar (*str)])
+        *p++ = *str++;
+      else
+        p += sprintf (p, "%%%02x", to_uchar (*str++));
+    }
+  *p = '\0';
+  return esc;
+}
+
 static size_t
 quote_name (char const *name, struct quoting_options const *options,
             int needs_general_quoting, const struct bin_str *color,
-            bool allow_pad, struct obstack *stack)
+            bool allow_pad, struct obstack *stack, char const *absolute_name)
 {
   char smallbuf[BUFSIZ];
   char *buf = smallbuf;
@@ -4392,19 +4485,44 @@ quote_name (char const *name, struct quoting_options const *options,
   if (color)
     print_color_indicator (color);
 
+  /* If we're padding, then don't include the outer quotes in
+     the --hyperlink, to improve the alignment of those links.  */
+  bool skip_quotes = false;
+
+  if (absolute_name)
+    {
+      if (align_variable_outer_quotes && cwd_some_quoted && ! pad)
+        {
+          skip_quotes = true;
+          putchar (*buf);
+        }
+      char *h = file_escape (hostname, /* path= */ false);
+      char *n = file_escape (absolute_name, /* path= */ true);
+      printf ("\033]8;;file://%s%s%s\a", h, *n == '/' ? "" : "/", n);
+      free (h);
+      free (n);
+    }
+
   if (stack)
     PUSH_CURRENT_DIRED_POS (stack);
 
-  fwrite (buf, 1, len, stdout);
-
-  if (buf != smallbuf && buf != name)
-    free (buf);
+  fwrite (buf + skip_quotes, 1, len - skip_quotes, stdout);
 
   dired_pos += len;
 
   if (stack)
     PUSH_CURRENT_DIRED_POS (stack);
 
+  if (absolute_name)
+    {
+      fputs ("\033]8;;\a", stdout);
+      if (skip_quotes)
+        putchar (*(buf + len - 1));
+    }
+
+  if (buf != smallbuf && buf != name)
+    free (buf);
+
   return len + pad;
 }
 
@@ -4423,7 +4541,7 @@ print_name_with_quoting (const struct fileinfo *f,
                                && (color || is_colored (C_NORM)));
 
   size_t len = quote_name (name, filename_quoting_options, f->quoted,
-                           color, !symlink_target, stack);
+                           color, !symlink_target, stack, f->absolute_name);
 
   process_signals ();
   if (used_color_this_time)
@@ -5069,6 +5187,10 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
                                (overridden by -a or -A)\n\
 "), stdout);
       fputs (_("\
+      --hyperlink[=WHEN]     hyperlink file names; WHEN can be 'always'\n\
+                               (default if omitted), 'auto', or 'never'\n\
+"), stdout);
+      fputs (_("\
       --indicator-style=WORD  append indicator with style WORD to entry names:\
 \n\
                                none (default), slash (-p),\n\
diff --git a/tests/local.mk b/tests/local.mk
index fd4713d..732ec99 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -607,6 +607,7 @@ all_tests =					\
   tests/ls/symlink-slash.sh			\
   tests/ls/time-style-diag.sh			\
   tests/ls/x-option.sh				\
+  tests/ls/hyperlink.sh				\
   tests/mkdir/p-1.sh				\
   tests/mkdir/p-2.sh				\
   tests/mkdir/p-3.sh				\
diff --git a/tests/ls/hyperlink.sh b/tests/ls/hyperlink.sh
new file mode 100755
index 0000000..bc13311
--- /dev/null
+++ b/tests/ls/hyperlink.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+# Test --hyperlink processing
+
+# Copyright (C) 2017 Free Software Foundation, Inc.
+
+# This program 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 3 of the License, or
+# (at your option) any later version.
+
+# This program 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.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls realpath
+
+hostname=$(hostname) || skip_ 'unable to determine hostname'
+
+# lookup based on first letter
+encode() {
+ printf '%s\n' \
+  'sp%20ace' 'ques%3ftion' 'back%5cslash' 'encoded%253Fquestion' 'testdir' \
+  "$1" |
+ sort -k1,1.1 -s | uniq -w1 -d
+}
+
+ls_encoded() {
+  ef=$(encode "$1")
+  echo "$ef" | grep -q 'dir$' && dir=: || dir=''
+  printf '\033]8;;file://%s%s/%s\a%s\033]8;;\a%s\n' \
+    $hostname $basepath $ef "$1" "$dir"
+}
+
+mkdir testdir || framework_failure_
+basepath=$(realpath -m .) || framework_failure_
+(
+cd testdir
+ls_encoded "testdir" > ../exp.t || framework_failure_
+basepath="$basepath/testdir"
+for f in 'back\slash' 'encoded%3Fquestion' 'ques?tion' 'sp ace'; do
+  touch "$f" || framework_failure_
+  ls_encoded "$f" >> ../exp.t || framework_failure_
+done
+)
+ln -s testdir testdirl || framework_failure_
+(cat exp.t && echo && sed 's/[^\/]testdir/&l/' exp.t) > exp \
+  || framework_failure_
+ls --hyper testdir testdirl >out || fail=1
+compare exp out || fail=1
+
+ln -s '/probably/missing' testlink || framework_failure_
+target=$(realpath -m testlink) || framework_failure_
+ls -l --hyper testlink > out || fail=1
+grep "file://.*$target" out || fail=1
+
+Exit $fail
-- 
2.9.3

Reply via email to