Hi Eric,

You said:

> Among other things, I can see the following changes that coreutils
> will need to make to become compliant, or else we need to push back on
> the POSIX folks if we have strong reasons to complain that their
> specification will break things:
>
> POSIX wants 'readlink non-symlink' to output a diagnostic; that is, it
> looks like POSIX wants us to behave like '-v' is enabled by default
> (our current behavior of -q by default will be non-compliant).

I have attached a patch that I think is correct for readlink.

It is a bit more complex than just behaving the same as 'readlink -v'
previously would. This is because the behavior of non-POSIX
options. Here are some commands:

    $ touch file
    $ ln -s symlink file
    
    # Example 1
    $ readlink -v file
    readlink: file: Invalid argument

    # Example 2
    $ readlink -v -f file 
    /home/collin/file

If 'readlink -v' where 'POSIXLY_CORRECT=1 readlink' then example 1 would
conform. I think that example 2 would violate POSIX.

So, when POSIXLY_CORRECT is enabled we can call 'readlink', check for
errors, and then canonicalize if it is successful.

Collin

>From 49d32775db13eb3f272853eb1c7124fff17b93f8 Mon Sep 17 00:00:00 2001
Message-ID: <49d32775db13eb3f272853eb1c7124fff17b93f8.1754194901.git.collin.fu...@gmail.com>
From: Collin Funk <collin.fu...@gmail.com>
Date: Sat, 2 Aug 2025 20:51:30 -0700
Subject: [PATCH] readlink: emit errors when POSIXLY_CORRECT is set

* src/readlink.c (main): Set verbose if the POSIXLY_CORRECT environment
variable is set. Check if the file is a symbolic link if POSIXLY_CORRECT
is set.
* tests/readlink/readlink-posix.sh: New file.
* tests/local.mk (all_tests): Add it.
* NEWS: Mention the change.
* doc/coreutils.texi (readlink invocation): Document the behavior of
POSIXLY_CORRECT.
---
 NEWS                             |  6 ++++
 doc/coreutils.texi               |  5 ++++
 src/readlink.c                   | 24 ++++++++++++++--
 tests/local.mk                   |  1 +
 tests/readlink/readlink-posix.sh | 48 ++++++++++++++++++++++++++++++++
 5 files changed, 81 insertions(+), 3 deletions(-)
 create mode 100755 tests/readlink/readlink-posix.sh

diff --git a/NEWS b/NEWS
index 110e688b1..e79067bb4 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,12 @@ GNU coreutils NEWS                                    -*- outline -*-
   'factor' is now much faster at identifying large prime numbers,
   and significantly faster on composite numbers greater than 2^128.
 
+** New Features
+
+  readlink will print a diagnostic message to standard error and exit
+  with a non-zero status when given a file that is not a symbolic link
+  if the POSIXLY_CORRECT environment variable is defined.
+
 ** Bug fixes
 
   cksum was not compilable by Apple LLVM 10.0.0 x86-64, which
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 40ecf3126..15a231bc3 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -11474,6 +11474,11 @@ @node readlink invocation
 
 @end table
 
+@vindex POSIXLY_CORRECT
+If the @env{POSIXLY_CORRECT} environment variable is set and
+@var{file} is not a symbolic link, @command{readlink} will emit a
+diagnostic message to standard error and exit with a nonzero status.
+
 The @command{readlink} utility first appeared in OpenBSD 2.1.
 
 The @command{realpath} command without options, operates like
diff --git a/src/readlink.c b/src/readlink.c
index 44def1dbb..4586e5820 100644
--- a/src/readlink.c
+++ b/src/readlink.c
@@ -97,6 +97,7 @@ main (int argc, char **argv)
   int status = EXIT_SUCCESS;
   int optc;
   bool use_nuls = false;
+  bool posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
 
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
@@ -152,12 +153,29 @@ main (int argc, char **argv)
       no_newline = false;
     }
 
+  /* POSIX requires a diagnostic message written to standard error and a
+     non-zero exit status when given a file that is not a symbolic link.  */
+  if (posixly_correct)
+    verbose = true;
+
   for (; optind < argc; ++optind)
     {
       char const *fname = argv[optind];
-      char *value = (can_mode != -1
-                     ? canonicalize_filename_mode (fname, can_mode)
-                     : areadlink_with_size (fname, 63));
+      char *value;
+
+      if (! posixly_correct)
+        value = (can_mode != -1
+                 ? canonicalize_filename_mode (fname, can_mode)
+                 : areadlink_with_size (fname, 63));
+      else
+        {
+          /* We have to check if the file is a symbolic link before
+             canonicalizing.  */
+          value = areadlink_with_size (fname, 63);
+          if (value && can_mode != -1)
+            value = canonicalize_filename_mode (value, can_mode);
+        }
+
       if (value)
         {
           fputs (value, stdout);
diff --git a/tests/local.mk b/tests/local.mk
index 6b527f108..7364ec89f 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -365,6 +365,7 @@ all_tests =					\
   tests/printf/printf-quote.sh			\
   tests/pwd/pwd-long.sh				\
   tests/readlink/readlink-fp-loop.sh		\
+  tests/readlink/readlink-posix.sh		\
   tests/readlink/readlink-root.sh		\
   tests/misc/realpath.sh			\
   tests/runcon/runcon-compute.sh		\
diff --git a/tests/readlink/readlink-posix.sh b/tests/readlink/readlink-posix.sh
new file mode 100755
index 000000000..a301545cf
--- /dev/null
+++ b/tests/readlink/readlink-posix.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# Test readlink with POSIXLY_CORRECT defined.
+
+# Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ readlink
+
+touch file || framework_failure_
+ln -s file link1 || framework_failure_
+
+# POSIX requires a diagnostic error and non-zero exit status if the file is not
+# a symbolic link.
+cat <<\EOF > exp || framework_failure_
+readlink: file: Invalid argument
+EOF
+returns_ 1 env POSIXLY_CORRECT=1 readlink file 2>err || fail=1
+compare exp err || fail=1
+returns_ 1 env POSIXLY_CORRECT=1 readlink -f file 2>err || fail=1
+compare exp err || fail=1
+returns_ 1 env POSIXLY_CORRECT=1 readlink -e file 2>err || fail=1
+compare exp err || fail=1
+returns_ 1 env POSIXLY_CORRECT=1 readlink -m file 2>err || fail=1
+
+# Check on a symbolic link.
+cat <<\EOF > exp || framework_failure_
+file
+EOF
+POSIXLY_CORRECT=1 readlink link1 >out || fail=1
+compare exp out || fail=1
+POSIXLY_CORRECT=1 readlink -f link1 || fail=1
+POSIXLY_CORRECT=1 readlink -e link1 || fail=1
+POSIXLY_CORRECT=1 readlink -m link1 || fail=1
+
+Exit $fail
-- 
2.50.1

Reply via email to