summaryrefslogtreecommitdiffstats
path: root/tests/ls
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 16:58:41 +0000
commite1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe (patch)
treef5cc731bedcac0fb7fe14d952e4581e749f8bb87 /tests/ls
parentInitial commit. (diff)
downloadcoreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.tar.xz
coreutils-e1908ae95dd4c9d19ee4dfabfc8bf8a7f85943fe.zip
Adding upstream version 9.4.upstream/9.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/ls')
-rwxr-xr-xtests/ls/a-option.sh27
-rwxr-xr-xtests/ls/abmon-align.sh53
-rwxr-xr-xtests/ls/birthtime.sh27
-rwxr-xr-xtests/ls/block-size.sh186
-rwxr-xr-xtests/ls/capability.sh63
-rwxr-xr-xtests/ls/classify.sh63
-rwxr-xr-xtests/ls/color-clear-to-eol.sh41
-rwxr-xr-xtests/ls/color-dtype-dir.sh64
-rwxr-xr-xtests/ls/color-ext.sh85
-rwxr-xr-xtests/ls/color-norm.sh84
-rwxr-xr-xtests/ls/color-term.sh48
-rwxr-xr-xtests/ls/dangle.sh64
-rwxr-xr-xtests/ls/dired.sh35
-rwxr-xr-xtests/ls/file-type.sh65
-rwxr-xr-xtests/ls/follow-slink.sh63
-rwxr-xr-xtests/ls/getxattr-speedup.sh66
-rwxr-xr-xtests/ls/group-dirs.sh43
-rwxr-xr-xtests/ls/hex-option.sh24
-rwxr-xr-xtests/ls/hyperlink.sh63
-rwxr-xr-xtests/ls/infloop.sh41
-rwxr-xr-xtests/ls/inode.sh65
-rwxr-xr-xtests/ls/ls-misc.pl375
-rwxr-xr-xtests/ls/ls-time.sh145
-rwxr-xr-xtests/ls/m-option.sh40
-rwxr-xr-xtests/ls/multihardlink.sh80
-rwxr-xr-xtests/ls/nameless-uid.sh40
-rwxr-xr-xtests/ls/no-arg.sh56
-rwxr-xr-xtests/ls/no-cap.sh33
-rwxr-xr-xtests/ls/quote-align.sh62
-rwxr-xr-xtests/ls/readdir-mountpoint-inode.sh73
-rwxr-xr-xtests/ls/recursive.sh62
-rwxr-xr-xtests/ls/removed-directory.sh38
-rwxr-xr-xtests/ls/root-rel-symlink-color.sh51
-rwxr-xr-xtests/ls/rt-1.sh46
-rwxr-xr-xtests/ls/selinux-segfault.sh33
-rwxr-xr-xtests/ls/slink-acl.sh33
-rwxr-xr-xtests/ls/sort-width-option.sh41
-rwxr-xr-xtests/ls/stat-dtype.sh60
-rwxr-xr-xtests/ls/stat-failed.sh57
-rwxr-xr-xtests/ls/stat-free-color.sh89
-rwxr-xr-xtests/ls/stat-free-symlinks.sh76
-rwxr-xr-xtests/ls/stat-vs-dirent.sh60
-rwxr-xr-xtests/ls/symlink-loop.sh35
-rwxr-xr-xtests/ls/symlink-quote.sh30
-rwxr-xr-xtests/ls/symlink-slash.sh30
-rwxr-xr-xtests/ls/time-style-diag.sh38
-rwxr-xr-xtests/ls/w-option.sh48
-rwxr-xr-xtests/ls/x-option.sh37
-rwxr-xr-xtests/ls/zero-option.sh41
49 files changed, 3079 insertions, 0 deletions
diff --git a/tests/ls/a-option.sh b/tests/ls/a-option.sh
new file mode 100755
index 0000000..c154666
--- /dev/null
+++ b/tests/ls/a-option.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# exercise the -a option
+
+# Copyright 2018-2023 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_ ls
+
+mkdir d || framework_failure_
+
+ls -aA d >out || framework_failure_
+compare /dev/null out || fail=1
+
+Exit $fail
diff --git a/tests/ls/abmon-align.sh b/tests/ls/abmon-align.sh
new file mode 100755
index 0000000..73308dc
--- /dev/null
+++ b/tests/ls/abmon-align.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+# Ensure ls output is aligned when using abbreviated months from the locale
+
+# Copyright (C) 2009-2023 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_ ls
+
+mid_month="$(date +%Y-%m-15)" || framework_failure_
+for mon in $(seq -w 12); do
+ touch -d"$mid_month +$mon month" $mon.ts || framework_failure_
+done
+
+
+# Note some of the following locales may be missing but if so
+# we should fail back to the C locale which should be aligned
+
+for format in "%b" "[%b" "%b]" "[%b]"; do
+ for LOC in C gv_GB ga_IE fi_FI.utf8 zh_CN ar_SY $LOCALE_FR_UTF8; do
+ # The sed usage here is slightly different from the original,
+ # removing the \(.*\), to avoid triggering misbehavior in at least
+ # GNU sed 4.2 (possibly miscompiled) on Mac OS X (Darwin 9.8.0).
+ months="$(
+ LC_ALL=$LOC TIME_STYLE=+"$format" ls -lgG *.ts |
+ LC_ALL=C sed 's/.\{15\}//;s/ ..\.ts$//;s/ /./g')"
+ n_widths=$(echo "$months" |
+ while read mon; do echo "$mon" | LC_ALL=$LOC wc -L; done |
+ uniq | wc -l
+ )
+ n_dupes=$(echo "$months" | sort | uniq -d | wc -l)
+ test "$n_widths" = "1" || { fail=1; break 2; }
+ test "$n_dupes" = "0" || { fail=1; break 2; }
+ done
+done
+if test "$fail" = "1"; then
+ echo "misalignment or ambiguous output in $LOC locale:"
+ LC_ALL=$LOC TIME_STYLE=+%b ls -lgG *.ts
+fi
+
+Exit $fail
diff --git a/tests/ls/birthtime.sh b/tests/ls/birthtime.sh
new file mode 100755
index 0000000..7f6347f
--- /dev/null
+++ b/tests/ls/birthtime.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# ensure that ls attempts birthtime access
+
+# Copyright (C) 2020-2023 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_ ls
+
+# ls should not fail, even if birth time not available
+touch a || framework_failure_
+ls --time=birth -l a || fail=1
+ls --time=creation -t a || fail=1
+
+Exit $fail
diff --git a/tests/ls/block-size.sh b/tests/ls/block-size.sh
new file mode 100755
index 0000000..3dfd5fe
--- /dev/null
+++ b/tests/ls/block-size.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+# Exercise ls --block-size and related options.
+
+# Copyright (C) 2011-2023 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_ ls
+
+TZ=UTC0
+export TZ
+
+mkdir sub
+cd sub
+
+for size in 1024 4096 262144; do
+ echo foo | dd conv=sync bs=$size >file$size || fail=1
+done
+touch -d '2001-01-01 00:00' file* || fail=1
+
+size_etc='s/[^ ]* *[^ ]* *//'
+
+ls -og * | sed "$size_etc" >../out || fail=1
+POSIXLY_CORRECT=1 ls -og * | sed "$size_etc" >>../out || fail=1
+POSIXLY_CORRECT=1 ls -k -og * | sed "$size_etc" >>../out || fail=1
+
+for var in BLOCKSIZE BLOCK_SIZE LS_BLOCK_SIZE; do
+ for blocksize in 1 512 1K 1KiB; do
+ (eval $var=$blocksize && export $var &&
+ echo "x x # $var=$blocksize" &&
+ ls -og * &&
+ ls -og -k * &&
+ ls -og -k --block-size=$blocksize *
+ ) | sed "$size_etc" >>../out || fail=1
+ done
+done
+
+cd ..
+
+cat >exp <<'EOF'
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+# BLOCKSIZE=1
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+# BLOCKSIZE=512
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+# BLOCKSIZE=1K
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+# BLOCKSIZE=1KiB
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+# BLOCK_SIZE=1
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+# BLOCK_SIZE=512
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+# BLOCK_SIZE=1K
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+# BLOCK_SIZE=1KiB
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+# LS_BLOCK_SIZE=1
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+1024 Jan 1 2001 file1024
+262144 Jan 1 2001 file262144
+4096 Jan 1 2001 file4096
+# LS_BLOCK_SIZE=512
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+2 Jan 1 2001 file1024
+512 Jan 1 2001 file262144
+8 Jan 1 2001 file4096
+# LS_BLOCK_SIZE=1K
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+# LS_BLOCK_SIZE=1KiB
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+1 Jan 1 2001 file1024
+256 Jan 1 2001 file262144
+4 Jan 1 2001 file4096
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/capability.sh b/tests/ls/capability.sh
new file mode 100755
index 0000000..0ccceda
--- /dev/null
+++ b/tests/ls/capability.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Ensure "ls --color" properly colors name of file with capability.
+
+# Copyright (C) 2008-2023 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_ ls printf
+require_root_
+
+grep '^#define HAVE_CAP 1' $CONFIG_HEADER > /dev/null \
+ || skip_ "configured without libcap support"
+
+(setcap --help) 2>&1 |grep 'usage: setcap' > /dev/null \
+ || skip_ "setcap utility not found"
+
+# Don't let a different umask perturb the results.
+umask 22
+
+# We create 2 files of the same name as
+# before coreutils 8.1 only the name rather than
+# the full path was used to read the capabilities
+# thus giving false positives and negatives.
+mkdir test test/dir
+cd test
+touch cap_pos dir/cap_pos dir/cap_neg
+for file in cap_pos dir/cap_neg; do
+ setcap 'cap_net_bind_service=ep' $file ||
+ skip_ "setcap doesn't work"
+done
+
+code='30;41'
+# Note we explicitly disable "executable" coloring
+# so that capability coloring is not dependent on it,
+# as was the case before coreutils 8.1
+for ex in '' ex=:; do
+ LS_COLORS="di=:${ex}ca=$code" \
+ ls --color=always cap_pos dir > out || fail=1
+
+ env printf "\
+\e[0m\e[${code}mcap_pos\e[0m
+
+dir:
+\e[${code}mcap_neg\e[0m
+cap_pos
+" > out_ok || framework_failure_
+
+ compare out out_ok || fail=1
+done
+
+Exit $fail
diff --git a/tests/ls/classify.sh b/tests/ls/classify.sh
new file mode 100755
index 0000000..8645ab5
--- /dev/null
+++ b/tests/ls/classify.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Test --classify processing
+
+# Copyright (C) 2020-2023 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_ ls
+
+mkdir testdir || framework_failure_
+cd testdir || framework_failure_
+mkdir dir || framework_failure_
+touch regular executable || framework_failure_
+chmod a+x executable || framework_failure_
+ln -s regular slink-reg || framework_failure_
+ln -s dir slink-dir || framework_failure_
+ln -s nowhere slink-dangle || framework_failure_
+mknod block b 20 20 2> /dev/null && block="block
+"
+mknod char c 10 10 2> /dev/null && char="char
+"
+mkfifo_or_skip_ fifo
+cd ..
+
+cat <<EOF > exp
+$block${char}dir/
+executable*
+fifo|
+regular
+slink-dangle@
+slink-dir@
+slink-reg@
+EOF
+sed 's/[*/@|]//' exp > exp2 || framework_failure_
+
+ls --classify testdir > out || fail=1
+ls --classify=always testdir > out2 || fail=1
+ls --classify=auto testdir > out3 || fail=1
+ls --classify=never testdir > out4 || fail=1
+
+compare exp out || fail=1
+
+compare exp out2 || fail=1
+
+compare exp2 out3 || fail=1
+
+compare exp2 out4 || fail=1
+
+returns_ 1 ls --classify=invalid || fail=1
+
+Exit $fail
diff --git a/tests/ls/color-clear-to-eol.sh b/tests/ls/color-clear-to-eol.sh
new file mode 100755
index 0000000..dc4bcd2
--- /dev/null
+++ b/tests/ls/color-clear-to-eol.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# ensure that ls --color works well when a colored name is wrapped
+
+# Copyright (C) 2009-2023 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_ ls
+
+long_name=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.foo
+touch $long_name || framework_failure_
+
+e='\33'
+color_code='0;31;42'
+c_pre="$e[0m$e[${color_code}m"
+c_post="$e[0m$e[K\n"
+printf "$c_pre$long_name$c_post\n" > exp || framework_failure_
+
+env TERM=xterm COLUMNS=80 LS_COLORS="*.foo=$color_code" TIME_STYLE=+T \
+ ls -og --color=always $long_name > out || fail=1
+
+# Append a newline, to accommodate less-capable versions of sed.
+echo >> out || framework_failure_
+
+sed 's/.*T //' out > k && mv k out
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/color-dtype-dir.sh b/tests/ls/color-dtype-dir.sh
new file mode 100755
index 0000000..d51cbaf
--- /dev/null
+++ b/tests/ls/color-dtype-dir.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+# Ensure "ls --color" properly colors other-writable and sticky directories.
+# Before coreutils-6.2, this test would fail, coloring all three
+# directories the same as the first one -- but only on a file system
+# with dirent.d_type support.
+
+# Copyright (C) 2006-2023 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_ ls
+
+# Don't let a different umask perturb the results.
+umask 22
+
+mkdir d other-writable sticky || framework_failure_
+chmod o+w other-writable || framework_failure_
+chmod o+t sticky || framework_failure_
+
+
+TERM=xterm ls --color=always > out || fail=1
+cat -A out > o1 || framework_failure_
+mv o1 out || framework_failure_
+
+cat <<\EOF > exp || framework_failure_
+^[[0m^[[01;34md^[[0m$
+^[[34;42mother-writable^[[0m$
+out$
+^[[37;44msticky^[[0m$
+EOF
+
+compare exp out || fail=1
+
+rm exp || framework_failure_
+
+# Turn off colors for other-writable dirs and ensure
+# we fall back to the color for standard directories.
+
+LS_COLORS="ow=:" ls --color=always > out || fail=1
+cat -A out > o1 || fail=1
+mv o1 out || fail=1
+
+cat <<\EOF > exp || framework_failure_
+^[[0m^[[01;34md^[[0m$
+^[[01;34mother-writable^[[0m$
+out$
+^[[37;44msticky^[[0m$
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/color-ext.sh b/tests/ls/color-ext.sh
new file mode 100755
index 0000000..dbc0d0a
--- /dev/null
+++ b/tests/ls/color-ext.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+# Ensure "ls --color" properly colors file name extensions.
+
+# Copyright (C) 2018-2023 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_ ls
+working_umask_or_skip_
+
+touch img1.jpg IMG2.JPG img3.JpG file1.z file2.Z || framework_failure_
+code_jpg='01;35'
+code_JPG='01;35;46'
+code_z='01;31'
+c0=$(printf '\033[0m')
+c_jpg=$(printf '\033[%sm' $code_jpg)
+c_JPG=$(printf '\033[%sm' $code_JPG)
+c_z=$(printf '\033[%sm' $code_z)
+
+# Case insensitive extensions
+LS_COLORS="*.jpg=$code_jpg:*.Z=$code_z" ls -U1 --color=always \
+ img1.jpg IMG2.JPG file1.z file2.Z > out || fail=1
+printf "$c0\
+${c_jpg}img1.jpg$c0
+${c_jpg}IMG2.JPG$c0
+${c_z}file1.z$c0
+${c_z}file2.Z$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# Case sensitive extensions
+LS_COLORS="*.jpg=$code_jpg:*.JPG=$code_JPG" ls -U1 --color=always \
+ img1.jpg IMG2.JPG img3.JpG > out || fail=1
+printf "$c0\
+${c_jpg}img1.jpg$c0
+${c_JPG}IMG2.JPG$c0
+img3.JpG
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# Case insensitive extensions (due to same sequences)
+LS_COLORS="*.jpg=$code_jpg:*.JPG=$code_jpg" ls -U1 --color=always \
+ img1.jpg IMG2.JPG img3.JpG > out || fail=1
+printf "$c0\
+${c_jpg}img1.jpg$c0
+${c_jpg}IMG2.JPG$c0
+${c_jpg}img3.JpG$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# Case insensitive extensions (due to same sequences (after ignored sequences))
+# Note later entries in LS_COLORS take precedence.
+LS_COLORS="*.jpg=$code_jpg:*.jpg=$code_JPG:*.JPG=$code_JPG" \
+ ls -U1 --color=always img1.jpg IMG2.JPG img3.JpG > out || fail=1
+printf "$c0\
+${c_JPG}img1.jpg$c0
+${c_JPG}IMG2.JPG$c0
+${c_JPG}img3.JpG$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# Case sensitive extensions (due to diff sequences (after ignored sequences))
+# Note later entries in LS_COLORS take precedence.
+LS_COLORS="*.jpg=$code_JPG:*.jpg=$code_jpg:*.JPG=$code_JPG" \
+ ls -U1 --color=always img1.jpg IMG2.JPG img3.JpG > out || fail=1
+printf "$c0\
+${c_jpg}img1.jpg$c0
+${c_JPG}IMG2.JPG$c0
+img3.JpG
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+Exit $fail
diff --git a/tests/ls/color-norm.sh b/tests/ls/color-norm.sh
new file mode 100755
index 0000000..09b52f3
--- /dev/null
+++ b/tests/ls/color-norm.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+# Ensure "ls --color" properly colors "normal" text and files.
+# I.e., that it uses NORMAL to style non file name output and
+# file names with no associated color (unless FILE is also set).
+
+# Copyright (C) 2010-2023 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_ ls
+
+# Don't let a different umask perturb the results.
+umask 22
+
+# Output time as something constant
+export TIME_STYLE="+norm"
+
+# helper to strip ls columns up to "norm" time
+qls() { sed 's/-r.*norm/norm/'; }
+
+touch exe || framework_failure_
+chmod u+x exe || framework_failure_
+touch nocolor || framework_failure_
+
+TCOLORS="no=7:ex=01;32"
+
+# Uncolored file names inherit NORMAL attributes.
+LS_COLORS=$TCOLORS ls -gGU --color exe nocolor | qls >> out || fail=1
+LS_COLORS=$TCOLORS ls -xU --color exe nocolor >> out || fail=1
+LS_COLORS=$TCOLORS ls -gGU --color nocolor exe | qls >> out || fail=1
+LS_COLORS=$TCOLORS ls -xU --color nocolor exe >> out || fail=1
+
+# NORMAL does not override FILE though
+LS_COLORS=$TCOLORS:fi=1 ls -gGU --color nocolor exe | qls >> out || fail=1
+
+# Support uncolored ordinary files that do _not_ inherit from NORMAL.
+# Note there is a redundant RESET output before a non colored
+# file in this case which may be removed in future.
+LS_COLORS=$TCOLORS:fi= ls -gGU --color nocolor exe | qls >> out || fail=1
+LS_COLORS=$TCOLORS:fi=0 ls -gGU --color nocolor exe | qls >> out || fail=1
+
+# A caveat worth noting is that commas (-m), indicator chars (-F)
+# and the "total" line, do not currently use NORMAL attributes
+LS_COLORS=$TCOLORS ls -mFU --color nocolor exe >> out || fail=1
+
+# Ensure no coloring is done unless enabled
+LS_COLORS=$TCOLORS ls -gGU nocolor exe | qls >> out || fail=1
+
+cat -A out > out.display || framework_failure_
+mv out.display out || framework_failure_
+
+cat <<\EOF > exp || framework_failure_
+^[[0m^[[7mnorm ^[[m^[[01;32mexe^[[0m$
+^[[7mnorm nocolor^[[0m$
+^[[0m^[[7m^[[m^[[01;32mexe^[[0m ^[[7mnocolor^[[0m$
+^[[0m^[[7mnorm nocolor^[[0m$
+^[[7mnorm ^[[m^[[01;32mexe^[[0m$
+^[[0m^[[7mnocolor^[[0m ^[[7m^[[m^[[01;32mexe^[[0m$
+^[[0m^[[7mnorm ^[[m^[[1mnocolor^[[0m$
+^[[7mnorm ^[[m^[[01;32mexe^[[0m$
+^[[0m^[[7mnorm ^[[m^[[mnocolor^[[0m$
+^[[7mnorm ^[[m^[[01;32mexe^[[0m$
+^[[0m^[[7mnorm ^[[m^[[0mnocolor^[[0m$
+^[[7mnorm ^[[m^[[01;32mexe^[[0m$
+^[[0m^[[7mnocolor^[[0m, ^[[7m^[[m^[[01;32mexe^[[0m*$
+norm nocolor$
+norm exe$
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/color-term.sh b/tests/ls/color-term.sh
new file mode 100755
index 0000000..3e5aca0
--- /dev/null
+++ b/tests/ls/color-term.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# Ensure "ls --color" doesn't output colors for TERM=dumb
+
+# Copyright (C) 2014-2023 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_ ls
+
+# Output time as something constant
+export TIME_STYLE="+norm"
+
+touch exe || framework_failure_
+chmod u+x exe || framework_failure_
+
+# output colors
+LS_COLORS='' COLORTERM='nonempty' TERM='' ls --color=always exe >> out || fail=1
+LS_COLORS='' COLORTERM='' TERM='xterm' ls --color=always exe >> out || fail=1
+
+# Don't output colors
+LS_COLORS='' COLORTERM='' TERM='dumb' ls --color=always exe >> out || fail=1
+LS_COLORS='' COLORTERM='' TERM='' ls --color=always exe >> out || fail=1
+
+cat -A out > out.display || framework_failure_
+mv out.display out || framework_failure_
+
+cat <<\EOF > exp || framework_failure_
+^[[0m^[[01;32mexe^[[0m$
+^[[0m^[[01;32mexe^[[0m$
+exe$
+exe$
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/dangle.sh b/tests/ls/dangle.sh
new file mode 100755
index 0000000..0e59e86
--- /dev/null
+++ b/tests/ls/dangle.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+# Make sure ls properly handles dangling symlinks vs. ls's -L, -H, options.
+
+# Copyright (C) 2003-2023 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_ ls
+
+LS_MINOR_PROBLEM=1
+LS_FAILURE=2
+
+ln -s no-such-file dangle || framework_failure_
+mkdir -p dir/sub || framework_failure_
+ln -s dir slink-to-dir || framework_failure_
+mkdir d || framework_failure_
+ln -s no-such d/dangle || framework_failure_
+printf '? dangle\n' > subdir_Li_exp || framework_failure_
+printf 'total 0\n? dangle\n' > subdir_Ls_exp || framework_failure_
+
+# This must exit nonzero.
+returns_ $LS_FAILURE ls -L dangle > /dev/null 2>&1 || fail=1
+# So must this.
+returns_ $LS_FAILURE ls -H dangle > /dev/null 2>&1 || fail=1
+
+# This must exit successfully.
+ls dangle >> out || fail=1
+
+ls slink-to-dir >> out 2>&1 || fail=1
+ls -H slink-to-dir >> out 2>&1 || fail=1
+ls -L slink-to-dir >> out 2>&1 || fail=1
+
+cat <<\EOF > exp
+dangle
+sub
+sub
+sub
+EOF
+
+compare exp out || fail=1
+
+# Ensure that ls -Li prints "?" as the inode of a dangling symlink.
+rm -f out
+returns_ $LS_MINOR_PROBLEM ls -Li d > out 2>/dev/null || fail=1
+compare subdir_Li_exp out || fail=1
+
+# Ensure that ls -Ls prints "?" as the allocation of a dangling symlink.
+rm -f out
+returns_ $LS_MINOR_PROBLEM ls -Ls d > out 2>/dev/null || fail=1
+compare subdir_Ls_exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/dired.sh b/tests/ls/dired.sh
new file mode 100755
index 0000000..150fff2
--- /dev/null
+++ b/tests/ls/dired.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# make sure --dired option works
+
+# Copyright (C) 2001-2023 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_ ls
+
+mkdir dir || framework_failure_
+
+
+LC_MESSAGES=C ls -lR --dired dir > out || fail=1
+cat <<EOF > exp
+ dir:
+ total 0
+//SUBDIRED// 2 5
+//DIRED-OPTIONS// --quoting-style=literal
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/file-type.sh b/tests/ls/file-type.sh
new file mode 100755
index 0000000..1df2074
--- /dev/null
+++ b/tests/ls/file-type.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# contrast ls -F, ls -p, and ls --indicator-style=file-type
+
+# Copyright (C) 2002-2023 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_ ls
+
+mkdir sub
+cd sub
+mkdir dir
+touch regular executable
+chmod a+x executable
+ln -s regular slink-reg
+ln -s dir slink-dir
+ln -s nowhere slink-dangle
+mknod block b 20 20 2> /dev/null && block="block
+"
+mknod char c 10 10 2> /dev/null && char="char
+"
+mkfifo_or_skip_ fifo
+cd ..
+
+
+
+ls -F sub > out || fail=1
+cat <<EOF > exp
+$block${char}dir/
+executable*
+fifo|
+regular
+slink-dangle@
+slink-dir@
+slink-reg@
+EOF
+
+sed 's/\*//' exp > exp2
+ls --indicator-style=file-type sub > out2 || fail=1
+
+sed 's/[@|]$//' exp2 > exp3
+ls -p sub > out3 || fail=1
+
+compare exp out || fail=1
+
+compare exp2 out2 || fail=1
+
+compare exp3 out3 || fail=1
+
+ls --color=auto -F sub > out || fail=1
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/follow-slink.sh b/tests/ls/follow-slink.sh
new file mode 100755
index 0000000..38ee0af
--- /dev/null
+++ b/tests/ls/follow-slink.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# make sure ls -L always follows symlinks
+
+# Copyright (C) 2000-2023 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_ ls
+
+LS_FAILURE=2
+
+# Isolate output files from directory being listed
+mkdir dir dir/sub dir1 || framework_failure_
+cd dir || framework_failure_
+ln -s link link || framework_failure_
+ln -s ../../dir1 sub/link-to-dir || framework_failure_
+
+# Make sure the symlink was created.
+# 'ln -s link link' succeeds, but creates no file on
+# systems running some DJGPP-2.03 libc.
+ls -F link > /dev/null || framework_failure_
+
+
+# When explicitly listing a broken link, the command must fail.
+returns_ $LS_FAILURE ls -L link 2> /dev/null || fail=1
+
+# When encountering a broken link implicitly, Solaris 9 and OpenBSD 3.4
+# list the link, provided no further information about the link needed
+# to be printed. Since POSIX does not specify one way or the other, we
+# opt for compatibility (this was broken in 5.3.0 through 5.94).
+LC_ALL=C ls -L > ../out-L || fail=1
+LC_ALL=C ls -FLR sub > ../out-FLR-sub || fail=1
+
+cd .. || fail=1
+
+cat <<\EOF > exp-L
+link
+sub
+EOF
+
+cat <<\EOF > exp-FLR-sub
+sub:
+link-to-dir/
+
+sub/link-to-dir:
+EOF
+
+compare exp-L out-L || fail=1
+compare exp-FLR-sub out-FLR-sub || fail=1
+
+Exit $fail
diff --git a/tests/ls/getxattr-speedup.sh b/tests/ls/getxattr-speedup.sh
new file mode 100755
index 0000000..5665e39
--- /dev/null
+++ b/tests/ls/getxattr-speedup.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+# Show that we've eliminated most of ls' failing getxattr syscalls,
+# regardless of how many files are in a directory we list.
+# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
+# Similarly, on a system that lacks getxattr altogether, skipping it is fine.
+
+# Copyright (C) 2012-2023 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_ ls
+require_gcc_shared_
+
+# Replace each getxattr and lgetxattr call with a call to these stubs.
+# Count those and write the total number of calls to the file "x"
+# via a global destructor.
+cat > k.c <<'EOF' || framework_failure_
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+static unsigned long int n_calls;
+
+static void __attribute__ ((destructor))
+print_call_count (void)
+{
+ FILE *fp = fopen ("x", "w"); if (!fp) return;
+ fprintf (fp, "%lu\n", n_calls); fclose (fp);
+}
+
+static ssize_t incr () { ++n_calls; errno = ENOTSUP; return -1; }
+ssize_t getxattr (const char *path, const char *name, void *value, size_t size)
+{ return incr (); }
+ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size)
+{ return incr (); }
+EOF
+
+# Then compile/link it:
+gcc_shared_ k.c k.so \
+ || framework_failure_ 'failed to build shared library'
+
+# Create a few files:
+seq 20 | xargs touch || framework_failure_
+
+# Finally, run the test:
+LD_PRELOAD=$LD_PRELOAD:./k.so ls --color=always -l . || fail=1
+
+test -f x || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
+
+# Ensure that there were no more than 3 *getxattr calls.
+n_calls=$(cat x)
+test "$n_calls" -le 3 || fail=1
+
+Exit $fail
diff --git a/tests/ls/group-dirs.sh b/tests/ls/group-dirs.sh
new file mode 100755
index 0000000..c13a94e
--- /dev/null
+++ b/tests/ls/group-dirs.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# test --group-directories-first
+
+# Copyright (C) 2018-2023 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_ ls
+
+# Isolate output files from directory being listed
+mkdir dir dir/b || framework_failure_
+touch dir/a || framework_failure_
+ln -s b dir/bl || framework_failure_
+
+ls --group dir > out || fail=1
+cat <<\EOF > exp
+b
+bl
+a
+EOF
+compare exp out || fail=1
+
+ls --group -d dir/* > out || fail=1
+cat <<\EOF > exp
+dir/b
+dir/bl
+dir/a
+EOF
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/hex-option.sh b/tests/ls/hex-option.sh
new file mode 100755
index 0000000..2a09309
--- /dev/null
+++ b/tests/ls/hex-option.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# accept hex/oct numbers to -w and -T
+
+# Copyright (C) 2014-2023 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_ ls
+
+ls -x -T0x10 -w010 || fail=1
+
+Exit $fail
diff --git a/tests/ls/hyperlink.sh b/tests/ls/hyperlink.sh
new file mode 100755
index 0000000..970e062
--- /dev/null
+++ b/tests/ls/hyperlink.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+# Test --hyperlink processing
+
+# Copyright (C) 2017-2023 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_ ls
+
+# 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 'dir$' >/dev/null && dir=: || dir=''
+ printf '\033]8;;file:///%s\a%s\033]8;;\a%s\n' \
+ "$ef" "$1" "$dir"
+}
+
+# These could be encoded, so remove from consideration
+strip_host_and_path() {
+ sed 's|file://.*/|file:///|'
+}
+
+mkdir testdir || framework_failure_
+(
+cd testdir
+ls_encoded "testdir" > ../exp.t || framework_failure_
+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 && printf '\n' && sed 's/[^\/]testdir/&l/' exp.t) > exp \
+ || framework_failure_
+ls --hyper testdir testdirl >out.t || fail=1
+strip_host_and_path <out.t >out || framework_failure_
+compare exp out || fail=1
+
+ln -s '/probably_missing' testlink || framework_failure_
+ls -l --hyper testlink > out.t || fail=1
+strip_host_and_path <out.t >out || framework_failure_
+grep 'file:///probably_missing' out || fail=1
+
+Exit $fail
diff --git a/tests/ls/infloop.sh b/tests/ls/infloop.sh
new file mode 100755
index 0000000..d59ba81
--- /dev/null
+++ b/tests/ls/infloop.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# show that the following no longer makes ls infloop
+# mkdir loop; cd loop; ln -s ../loop sub; ls -RL
+# Also ensure ls exits with status = 2 in that case.
+# Copyright (C) 2001-2023 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_ ls
+
+mkdir loop || framework_failure_
+ln -s ../loop loop/sub || framework_failure_
+
+cat <<\EOF > exp-out || framework_failure_
+loop:
+sub
+EOF
+
+cat <<\EOF > exp-err || framework_failure_
+ls: loop/sub: not listing already-listed directory
+EOF
+
+# Ensure that ls exits with status 2 upon detecting a cycle
+returns_ 2 timeout 10 ls -RL loop >out 2>err || fail=1
+
+compare exp-err err || fail=1
+compare exp-out out || fail=1
+
+Exit $fail
diff --git a/tests/ls/inode.sh b/tests/ls/inode.sh
new file mode 100755
index 0000000..5060a96
--- /dev/null
+++ b/tests/ls/inode.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Make sure that ls -i works properly on symlinks.
+
+# Copyright (C) 2003-2023 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_ ls
+
+touch f || framework_failure_
+ln -s f slink || framework_failure_
+
+
+# When listed explicitly:
+
+set x $(ls -Ci f slink); shift
+test $# = 4 || fail=1
+# The inode numbers should differ.
+test "$1" != "$3" || fail=1
+
+set x $(ls -CLi f slink); shift
+test $# = 4 || fail=1
+# With -L, they must be the same.
+test "$1" = "$3" || fail=1
+
+set x $(ls -CHi f slink); shift
+test $# = 4 || fail=1
+# With -H, they must be the same, too, from the command line.
+# Note that POSIX says -H must make ls dereference only
+# symlinks (specified on the command line) to directories,
+# but the historical BSD meaning of -H is to dereference
+# any symlink given on the command line. For compatibility GNU ls
+# implements the BSD semantics.
+test "$1" = "$3" || fail=1
+
+# When listed from a directory:
+
+set x $(ls -Ci); shift
+test $# = 4 || fail=1
+# The inode numbers should differ.
+test "$1" != "$3" || fail=1
+
+set x $(ls -CLi); shift
+test $# = 4 || fail=1
+# With -L, they must be the same.
+test "$1" = "$3" || fail=1
+
+set x $(ls -CHi); shift
+test $# = 4 || fail=1
+# With -H, they must be different from inside a directory.
+test "$1" != "$3" || fail=1
+
+Exit $fail
diff --git a/tests/ls/ls-misc.pl b/tests/ls/ls-misc.pl
new file mode 100755
index 0000000..f3f09c4
--- /dev/null
+++ b/tests/ls/ls-misc.pl
@@ -0,0 +1,375 @@
+#!/usr/bin/perl
+
+# Copyright (C) 1998-2023 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/>.
+
+use strict;
+
+(my $ME = $0) =~ s|.*/||;
+my $prog = 'ls';
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+my $saved_ls_colors;
+
+sub push_ls_colors($)
+{
+ $saved_ls_colors = $ENV{LS_COLORS} || '';
+ $ENV{LS_COLORS} = $_[0];
+}
+
+sub restore_ls_colors()
+{
+ $ENV{LS_COLORS} = $saved_ls_colors;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new string.
+sub shell_quote($)
+{
+ my ($s) = @_;
+ if ($s =~ m![^\w+/.,-]!)
+ {
+ # Convert each single quote to '\''
+ $s =~ s/\'/\'\\\'\'/g;
+ # Then single quote the string.
+ $s = "'$s'";
+ }
+ return $s;
+}
+
+# Set up files used by the setuid-etc tests; skip this entire test if
+# that cannot be done.
+sub setuid_setup()
+{
+ my $test = 'env test';
+ system (qq(touch setuid && chmod u+s setuid && $test -u setuid &&
+ touch setgid && chmod g+s setgid && $test -g setgid &&
+ mkdir sticky && chmod +t sticky && $test -k sticky &&
+ mkdir owt && chmod +t,o+w owt && $test -k owt &&
+ mkdir owr && chmod o+w owr)) == 0
+ or CuSkip::skip "$ME: cannot create setuid/setgid/sticky files,"
+ . "so can't run this test\n";
+}
+
+sub mk_file(@)
+{
+ foreach my $f (@_)
+ {
+ open (F, '>', $f) && close F
+ or die "creating $f: $!\n";
+ }
+}
+
+sub mkdir_d {mkdir 'd',0755 or die "d: $!\n"}
+sub rmdir_d {rmdir 'd' or die "d: $!\n"}
+my $mkdir = {PRE => sub {mkdir_d}};
+my $rmdir = {POST => sub {rmdir_d}};
+my $mkdir_reg = {PRE => sub {mkdir_d; mk_file 'd/f' }};
+my $rmdir_reg = {POST => sub {unlink 'd/f' or die "d/f: $!\n";
+ rmdir 'd' or die "d: $!\n"}};
+
+my $mkdir2 = {PRE => sub {mkdir 'd',0755 or die "d: $!\n";
+ mkdir 'd/e',0755 or die "d/e: $!\n" }};
+my $rmdir2 = {POST => sub {rmdir 'd/e' or die "d/e: $!\n";
+ rmdir 'd' or die "d: $!\n" }};
+
+my $target = {PRE => sub {
+ mkdir 'd',0755 or die "d: $!\n";
+ symlink '.', 'd/X' or die "d/X: $!\n";
+ push_ls_colors('ln=target')
+ }};
+my $target2 = {POST => sub {unlink 'd/X' or die "d/X: $!\n";
+ rmdir 'd' or die "d: $!\n";
+ restore_ls_colors
+ }};
+my $slink_d = {PRE => sub {symlink '/', 'd' or die "d: $!\n";
+ push_ls_colors('ln=01;36:di=01;34:or=40;31;01')
+ }};
+my $unlink_d = {POST => sub {unlink 'd' or die "d: $!\n"; restore_ls_colors}};
+
+my $mkdir_d_slink = {PRE => sub {mkdir 'd',0755 or die "d: $!\n";
+ symlink '/', 'd/s' or die "d/s: $!\n" }};
+my $rmdir_d_slink = {POST => sub {unlink 'd/s' or die "d/s: $!\n";
+ rmdir 'd' or die "d: $!\n" }};
+
+sub make_j_d ()
+{
+ mkdir 'j', 0700 or die "creating j: $!\n";
+ mk_file 'j/d';
+ chmod 0555, 'j/d' or die "making j/d executable: $!\n";
+}
+
+my @v_files = (
+ '.0', '.9',
+ '.A', '.Z', '.a', '.z', '.zz~', '.zz', '.zz.~1~', '.zz.0',
+ '0', '9',
+ 'A', 'Z', 'a', 'z', 'zz~', 'zz', 'zz.~1~', 'zz.0');
+my $exe_in_subdir = {PRE => sub { make_j_d (); push_ls_colors('ex=01;32') }};
+my $remove_j = {POST => sub {unlink 'j/d' or die "j/d: $!\n";
+ rmdir 'j' or die "j: $!\n";
+ restore_ls_colors }};
+
+my $e = "\e[0m";
+my $q_bell = {IN => {"q\a" => ''}};
+my @Tests =
+ (
+ # test-name options input expected-output
+ #
+ # quoting tests............................................
+ ['q-', $q_bell, {OUT => "q\a\n"}, {EXIT => 0}],
+ ['q-N', '-N', $q_bell, {OUT => "q\a\n"}, {ERR => ''}],
+ ['q-q', '-q', $q_bell, {OUT => "q?\n"}],
+ ['q-Q', '-Q', $q_bell, {OUT => "\"q\\a\"\n"}],
+
+ ['q-qs-lit', '--quoting=literal', $q_bell, {OUT => "q\a\n"}],
+ ['q-qs-sh', '--quoting=shell', $q_bell, {OUT => "q\a\n"}],
+ ['q-qs-sh-a', '--quoting=shell-always',$q_bell, {OUT => "'q\a'\n"}],
+ ['q-qs-sh-e', '--quoting=shell-escape',$q_bell, {OUT => "'q'\$'\\a'\n"}],
+ ['q-qs-c', '--quoting=c', $q_bell, {OUT => "\"q\\a\"\n"}],
+ ['q-qs-esc', '--quoting=escape', $q_bell, {OUT => "q\\a\n"}],
+ ['q-qs-loc', '--quoting=locale', $q_bell, {OUT => "'q\\a'\n"}],
+ ['q-qs-cloc', '--quoting=clocale', $q_bell, {OUT => "\"q\\a\"\n"}],
+
+ ['q-qs-lit-q', '--quoting=literal -q', $q_bell, {OUT => "q?\n"}],
+ ['q-qs-sh-q', '--quoting=shell -q', $q_bell, {OUT => "q?\n"}],
+ ['q-qs-sh-a-q', '--quoting=shell-al -q', $q_bell, {OUT => "'q?'\n"}],
+ ['q-qs-sh-e-q', '--quoting=shell-escape -q',
+ $q_bell, {OUT => "'q'\$'\\a'\n"}],
+ ['q-qs-c-q', '--quoting=c -q', $q_bell, {OUT => "\"q\\a\"\n"}],
+ ['q-qs-esc-q', '--quoting=escape -q', $q_bell, {OUT => "q\\a\n"}],
+ ['q-qs-loc-q', '--quoting=locale -q', $q_bell, {OUT => "'q\\a'\n"}],
+ ['q-qs-cloc-q', '--quoting=clocale -q', $q_bell, {OUT => "\"q\\a\"\n"}],
+
+ ['q-qs-c-1', '--quoting=c',
+ {IN => {"t\004" => ''}}, {OUT => "\"t\\004\"\n"}],
+
+ ['emptydir', 'd', {OUT => ''}, $mkdir, $rmdir],
+ ['emptydir-x2', 'd d', {OUT => "d:\n\nd:\n"}, $mkdir, $rmdir],
+ ['emptydir-R', '-R d', {OUT => "d:\n"}, $mkdir, $rmdir],
+
+ # test 'ls -R .' ............................................
+ ['R-dot', '--ignore="[a-ce-zA-Z]*" -R .', {OUT => ".:\nd\n\n\./d:\n"},
+ $mkdir, $rmdir],
+
+ ['slink-dir-F', '-F d', {OUT => "d@\n"}, $slink_d, $unlink_d],
+ ['slink-dir-dF', '-dF d', {OUT => "d@\n"}, $slink_d, $unlink_d],
+ ['slinkdir-dFH', '-dFH d', {OUT => "d/\n"}, $slink_d, $unlink_d],
+ ['slinkdir-dFL', '-dFL d', {OUT => "d/\n"}, $slink_d, $unlink_d],
+
+ # Test for a bug that was fixed in coreutils-4.5.4.
+ ['sl-F-color', '-F --color=always d',
+ {OUT => "$e\e[01;36md$e\@\n"},
+ $slink_d, $unlink_d],
+ ['sl-dF-color', '-dF --color=always d',
+ {OUT => "$e\e[01;36md$e\@\n"},
+ $slink_d, $unlink_d],
+
+ # A listing with no output should have no color sequences at all.
+ ['no-c-empty', '--color=always d', {OUT => ""}, $mkdir, $rmdir],
+ # A listing with only regular files should have no color sequences at all.
+ ['no-c-reg', '--color=always d', {OUT => "f\n"}, $mkdir_reg, $rmdir_reg],
+
+ # Test for a bug fixed after coreutils-6.9.
+ ['sl-target', '--color=always d',
+ {OUT => "$e\e[01;34mX$e\n"}, $target, $target2],
+
+ # Test for another bug fixed after coreutils-6.9.
+ # This one bites only for a system/file system with d_type support.
+ ['sl-dangle', '--color=always d',
+ {OUT => "$e\e[40;31;01mX$e\n"},
+ {PRE => sub {
+ mkdir 'd',0755 or die "d: $!\n";
+ symlink 'non-existent', 'd/X' or die "d/X: $!\n";
+ push_ls_colors('or=40;31;01')
+ }},
+ {POST => sub {unlink 'd/X' or die "d/X: $!\n";
+ rmdir 'd' or die "d: $!\n";
+ restore_ls_colors; }},
+ ],
+
+ # Test for a bug fixed after coreutils-8.2.
+ ['sl-dangle2', '-o --time-style=+:TIME: --color=always l',
+ {OUT_SUBST => 's/.*:TIME: //'},
+ {OUT => "l -> nowhere\n"},
+ {PRE => sub {symlink 'nowhere', 'l' or die "l: $!\n";
+ push_ls_colors('ln=target')
+ }},
+ {POST => sub {unlink 'l' or die "l: $!\n";
+ restore_ls_colors; }},
+ ],
+ ['sl-dangle3', '-o --time-style=+:TIME: --color=always l',
+ {OUT_SUBST => 's/.*:TIME: //'},
+ {OUT => "$e\e[40ml$e -> \e[34mnowhere$e\n"},
+ {PRE => sub {symlink 'nowhere', 'l' or die "l: $!\n";
+ push_ls_colors('ln=target:or=40:mi=34:')
+ }},
+ {POST => sub {unlink 'l' or die "l: $!\n";
+ restore_ls_colors; }},
+ ],
+ ['sl-dangle4', '-o --time-style=+:TIME: --color=always l',
+ {OUT_SUBST => 's/.*:TIME: //'},
+ {OUT => "$e\e[36ml$e -> \e[35mnowhere$e\n"},
+ {PRE => sub {symlink 'nowhere', 'l' or die "l: $!\n";
+ push_ls_colors('ln=34:mi=35:or=36:')
+ }},
+ {POST => sub {unlink 'l' or die "l: $!\n";
+ restore_ls_colors; }},
+ ],
+ ['sl-dangle5', '-o --time-style=+:TIME: --color=always l',
+ {OUT_SUBST => 's/.*:TIME: //'},
+ {OUT => "$e\e[34ml$e -> \e[35mnowhere$e\n"},
+ {PRE => sub {symlink 'nowhere', 'l' or die "l: $!\n";
+ push_ls_colors('ln=34:mi=35:')
+ }},
+ {POST => sub {unlink 'l' or die "l: $!\n";
+ restore_ls_colors; }},
+ ],
+
+ # Test for a bug fixed after coreutils-8.13
+ # where 'argetm' was erroneously printed for dangling links
+ # when ln=target was used in LS_COLORS
+ ['sl-dangle6', '-L --color=always d',
+ {OUT => "s\n"},
+ {PRE => sub {mkdir 'd',0755 or die "d: $!\n";
+ symlink 'dangle', 'd/s' or die "d/s: $!\n";
+ push_ls_colors('ln=target')
+ }},
+ {POST => sub {unlink 'd/s' or die "d/s: $!\n";
+ rmdir 'd' or die "d: $!\n";
+ restore_ls_colors; }},
+ {ERR => "ls: cannot access 'd/s': No such file or directory\n"},
+ {EXIT => 1}
+ ],
+ # Related to the above fix, is this case where
+ # the code simulates "linkok". In this case "linkmode"
+ # should always be zero, and hence not trigger any
+ # issues with type being set to C_LINK
+ ['sl-dangle7', '--color=always d',
+ {OUT => "$e\e[ms$e\n"},
+ {PRE => sub {mkdir 'd',0755 or die "d: $!\n";
+ symlink 'dangle', 'd/s' or die "d/s: $!\n";
+ push_ls_colors('ln=target:or=:ex=:')
+ }},
+ {POST => sub {unlink 'd/s' or die "d/s: $!\n";
+ rmdir 'd' or die "d: $!\n";
+ restore_ls_colors; }},
+ ],
+ # Another case with simulated "linkok", that does
+ # actually use the value of 'ln' from $LS_COLORS.
+ # This path is not taken though when 'ln=target'.
+ ['sl-dangle8', '--color=always s',
+ {OUT => "$e\e[1;36ms$e\n"},
+ {PRE => sub {symlink 'dangle', 's' or die "s: $!\n";
+ push_ls_colors('ln=1;36:or=:')
+ }},
+ {POST => sub {unlink 's' or die "s: $!\n";
+ restore_ls_colors; }},
+ ],
+ # The patch associated with sl-dangle[678] introduced a regression
+ # that was fixed after coreutils-8.19. This edge case triggers when
+ # listing a dir containing dangling symlinks, but with orphans uncolored.
+ # I.e., the same as the previous test, but listing the directory
+ # rather than the symlink directly.
+ ['sl-dangle9', '--color=always d',
+ {OUT => "$e\e[1;36ms$e\n"},
+ {PRE => sub {mkdir 'd',0755 or die "d: $!\n";
+ symlink 'dangle', 'd/s' or die "d/s: $!\n";
+ push_ls_colors('ln=1;36:or=:')
+ }},
+ {POST => sub {unlink 'd/s' or die "d/s: $!\n";
+ rmdir 'd' or die "d: $!\n";
+ restore_ls_colors; }},
+ ],
+
+ # Test for a bug that was introduced in coreutils-4.5.4; fixed in 4.5.5.
+ # To demonstrate it, the file in question (with executable bit set)
+ # must not be a command line argument.
+ ['color-exe1', '--color=always j',
+ {OUT => "$e\e[01;32md$e\n"},
+ $exe_in_subdir, $remove_j],
+
+ # From Stéphane Chazelas.
+ ['no-a-isdir-b', 'no-dir d',
+ {OUT => "d:\n"},
+ {ERR => "ls: cannot access 'no-dir': No such file or directory\n"},
+ $mkdir, $rmdir, {EXIT => 2}],
+
+ ['recursive-2', '-R d', {OUT => "d:\ne\n\nd/e:\n"}, $mkdir2, $rmdir2],
+
+ ['setuid-etc', '-1 -d --color=always owr owt setgid setuid sticky',
+ {OUT =>
+ "$e\e[34;42mowr$e\n"
+ . "\e[30;42mowt$e\n"
+ . "\e[30;43msetgid$e\n"
+ . "\e[37;41msetuid$e\n"
+ . "\e[37;44msticky$e\n"
+ },
+
+ {PRE => sub {
+ push_ls_colors('ow=34;42:tw=30;42:sg=30;43:su=37;41:st=37;44'); }},
+ {POST => sub {
+ unlink qw(setuid setgid);
+ foreach my $dir (qw(owr owt sticky)) {rmdir $dir}
+ restore_ls_colors; }},
+ ],
+
+ # For 5.97 and earlier, --file-type acted like --indicator-style=slash.
+ ['file-type', '--file-type d', {OUT => "s@\n"},
+ $mkdir_d_slink, $rmdir_d_slink],
+
+ # 7.1 had a regression in how -v -a ordered some files
+ ['version-sort', '-v -A ' . join (' ', @v_files),
+ {OUT => join ("\n", @v_files) . "\n"},
+ {PRE => sub { mk_file @v_files }},
+ {POST => sub { unlink @v_files }},
+ ],
+
+ # Test for the ls -1U bug fixed in coreutils-7.5.
+ # It is triggered only with -1U and with two or more arguments,
+ # at least one of which is a nonempty directory.
+ ['multi-arg-U1', '-U1 d no-such',
+ {OUT => "d:\nf\n"},
+ {ERR_SUBST=>"s/ch':.*/ch':/"},
+ {ERR => "$prog: cannot access 'no-such':\n"},
+ $mkdir_reg,
+ $rmdir_reg,
+ {EXIT => 2},
+ ],
+ );
+
+umask 022;
+
+# Start with an unset LS_COLORS environment variable.
+delete $ENV{LS_COLORS};
+
+my $save_temps = $ENV{SAVE_TEMPS};
+my $verbose = $ENV{VERBOSE};
+
+setuid_setup;
+my $fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose);
+$fail
+ and exit 1;
+
+# Be careful to use the just-build dircolors.
+my $env = qx/dircolors -b/;
+$env =~ s/^LS_COLORS=\'//;
+$env =~ s/\';.*//sm;
+$ENV{LS_COLORS} = $env;
+
+setuid_setup;
+$fail = run_tests ($ME, $prog, \@Tests, $save_temps, $verbose);
+exit $fail;
diff --git a/tests/ls/ls-time.sh b/tests/ls/ls-time.sh
new file mode 100755
index 0000000..5585d62
--- /dev/null
+++ b/tests/ls/ls-time.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+# Test some of ls's sorting options.
+
+# Copyright (C) 1998-2023 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_ ls
+
+# Avoid any possible glitches due to daylight-saving changes near the
+# timestamps used during the test.
+TZ=UTC0
+export TZ
+
+t1='1998-01-15 21:00'
+t2='1998-01-15 22:00'
+t3='1998-01-15 23:00'
+
+u1='1998-01-14 11:00'
+u2='1998-01-14 12:00'
+u3='1998-01-14 13:00'
+
+touch -m -d "$t3" a || framework_failure_
+touch -m -d "$t2" b || framework_failure_
+touch -m -d "$t1" c || framework_failure_
+
+touch -a -d "$u3" c || framework_failure_
+touch -a -d "$u2" b || framework_failure_
+# Make sure A has ctime at least 1 second more recent than C's.
+sleep 2
+touch -a -d "$u1" a || framework_failure_
+# Updating the atime is usually enough to update the ctime, but on
+# Solaris 10's tmpfs, ctime is not updated, so force an update here:
+{ ln a a-ctime && rm a-ctime; } || framework_failure_
+
+
+# A has ctime more recent than C.
+set $(ls -c a c)
+test "$*" = 'a c' || fail=1
+
+# Sleep so long in an attempt to avoid spurious failures
+# due to NFS caching and/or clock skew.
+sleep 2
+
+# Create a link, updating c's ctime.
+ln c d || framework_failure_
+
+# Before we go any further, verify that touch's -m option works.
+set -- $(ls --full -l --time=mtime a)
+case "$*" in
+ *" $t3:00.000000000 +0000 a") ;;
+ *)
+ # This might be what's making HPUX 11 systems fail this test.
+ cat >&2 << EOF
+A basic test of touch -m has just failed, so the subsequent
+tests in this file will not be run.
+
+In the output below, the date of last modification for 'a' should
+have been $t3.
+EOF
+ ls --full -l a
+ skip_ "touch -m -d '$t3' didn't work"
+ ;;
+esac
+
+# Ensure that touch's -a option works.
+set -- $(ls --full -lu a)
+case "$*" in
+ *" $u1:00.000000000 +0000 a") ;;
+ *)
+ # This might be what's making HPUX 11 systems fail this test.
+ cat >&2 << EOF
+A fundamental touch -a test has just failed, so the subsequent
+tests in this file will not be run.
+
+In the output below, the date of last access for 'a' should
+have been $u1.
+EOF
+ ls --full -lu a
+ Exit 77
+ ;;
+esac
+
+set $(ls -ut a b c)
+test "$*" = 'c b a' && : || fail=1
+test $fail = 1 && ls -l --full-time --time=access a b c
+
+set $(ls -t a b c)
+test "$*" = 'a b c' && : || fail=1
+test $fail = 1 && ls -l --full-time a b c
+
+# Now, C should have ctime more recent than A.
+set $(ls -ct a c)
+if test "$*" = 'c a'; then
+ : ok
+else
+ # In spite of documentation, (e.g., stat(2)), neither link nor chmod
+ # update a file's st_ctime on SunOS4.1.4.
+ cat >&2 << \EOF
+failed ls ctime test -- this failure is expected at least for SunOS4.1.4
+and for tmpfs file systems on Solaris 5.5.1.
+It is also expected to fail on a btrfs file system until
+https://bugzilla.redhat.com/591068 is addressed.
+
+In the output below, 'c' should have had a ctime more recent than
+that of 'a', but does not.
+EOF
+ #'
+ ls -ctl --full-time a c
+ fail=1
+fi
+
+# This check is ineffective if:
+# en_US locale is not on the system.
+# The system en_US message catalog has a specific TIME_FMT translation,
+# which was inadvertently the case between coreutils 8.1 and 8.5 inclusive.
+
+if gettext --version >/dev/null 2>&1; then
+
+ default_tf1='%b %e %Y'
+ en_tf1=$(LC_ALL=en_US gettext coreutils "$default_tf1")
+
+ if test "$default_tf1" = "$en_tf1"; then
+ LC_ALL=en_US ls -l c >en_output
+ ls -l --time-style=long-iso c >liso_output
+ if compare en_output liso_output; then
+ fail=1
+ echo "Long ISO TIME_FMT being used for en_US locale." >&2
+ fi
+ fi
+fi
+
+Exit $fail
diff --git a/tests/ls/m-option.sh b/tests/ls/m-option.sh
new file mode 100755
index 0000000..3b8cdcd
--- /dev/null
+++ b/tests/ls/m-option.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# exercise the -m option
+
+# Copyright (C) 2003-2023 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_ ls
+
+seq 2000 > b || framework_failure_
+touch a || framework_failure_
+
+
+# Before coreutils-5.1.1, the following would output a space after the comma.
+ls -w2 -m a b > out || fail=1
+
+# Before coreutils-5.1.1, the following would produce leading white space.
+# All of the sed business is because the sizes are not portable.
+ls -sm a b | sed 's/^[0-9]/0/;s/, [0-9][0-9]* b/, 12 b/' >> out || fail=1
+cat <<\EOF > exp || framework_failure_
+a,
+b
+0 a, 12 b
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/multihardlink.sh b/tests/ls/multihardlink.sh
new file mode 100755
index 0000000..0d35981
--- /dev/null
+++ b/tests/ls/multihardlink.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+# Ensure "ls --color" properly colors names of hard linked files.
+
+# Copyright (C) 2008-2023 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_ ls
+working_umask_or_skip_
+
+touch file file1 || framework_failure_
+ln file1 file2 || skip_ "can't create hard link"
+code_mh='44;37'
+code_ex='01;32'
+code_png='01;35'
+c0=$(printf '\033[0m')
+c_mh=$(printf '\033[%sm' $code_mh)
+c_ex=$(printf '\033[%sm' $code_ex)
+c_png=$(printf '\033[%sm' $code_png)
+
+# regular file - not hard linked
+LS_COLORS="mh=$code_mh" ls -U1 --color=always file > out || fail=1
+printf "file\n" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# hard links
+LS_COLORS="mh=$code_mh" ls -U1 --color=always file1 file2 > out || fail=1
+printf "$c0${c_mh}file1$c0
+${c_mh}file2$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# hard links and png (hard link coloring takes precedence)
+mv file2 file2.png || framework_failure_
+LS_COLORS="mh=$code_mh:*.png=$code_png" ls -U1 --color=always file1 file2.png \
+ > out || fail=1
+printf "$c0${c_mh}file1$c0
+${c_mh}file2.png$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# hard links and exe (exe coloring takes precedence)
+chmod a+x file2.png || framework_failure_
+LS_COLORS="mh=$code_mh:*.png=$code_png:ex=$code_ex" \
+ ls -U1 --color=always file1 file2.png > out || fail=1
+chmod a-x file2.png || framework_failure_
+printf "$c0${c_ex}file1$c0
+${c_ex}file2.png$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# hard links and png (hard link coloring disabled => png coloring enabled)
+LS_COLORS="mh=00:*.png=$code_png" ls -U1 --color=always file1 file2.png > out \
+ || fail=1
+printf "file1
+$c0${c_png}file2.png$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+# hard links and png (hard link coloring not enabled explicitly => png coloring)
+LS_COLORS="*.png=$code_png" ls -U1 --color=always file1 file2.png > out \
+ || fail=1
+printf "file1
+$c0${c_png}file2.png$c0
+" > out_ok || framework_failure_
+compare out out_ok || fail=1
+
+Exit $fail
diff --git a/tests/ls/nameless-uid.sh b/tests/ls/nameless-uid.sh
new file mode 100755
index 0000000..94cdf40
--- /dev/null
+++ b/tests/ls/nameless-uid.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Ensure that ls -l works on files with nameless uid and/or gid
+
+# Copyright (C) 2006-2023 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_ ls
+
+require_root_
+require_perl_
+
+nameless_uid=$($PERL -e '
+ foreach my $i (1000..16*1024) { getpwuid $i or (print "$i\n"), exit }
+')
+
+if test x$nameless_uid = x; then
+ skip_ "couldn't find a nameless UID"
+fi
+
+touch f || framework_failure_
+chown $nameless_uid f || framework_failure_
+
+
+set -- $(ls -o f) || fail=1
+test $3 = $nameless_uid || fail=1
+
+Exit $fail
diff --git a/tests/ls/no-arg.sh b/tests/ls/no-arg.sh
new file mode 100755
index 0000000..bb72d83
--- /dev/null
+++ b/tests/ls/no-arg.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+# make sure ls and 'ls -R' do the right thing when invoked with no arguments.
+
+# Copyright (C) 2001-2023 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_ ls
+
+mkdir -p dir/subdir || framework_failure_
+touch dir/subdir/file2 || framework_failure_
+ln -s f symlink || framework_failure_
+
+cat > exp <<\EOF || framework_failure_
+dir
+exp
+out
+symlink
+EOF
+
+
+ls -1 > out || fail=1
+
+compare exp out || fail=1
+
+cat > exp <<\EOF
+.:
+dir
+exp
+out
+symlink
+
+./dir:
+subdir
+
+./dir/subdir:
+file2
+EOF
+
+ls -R1 > out || fail=1
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/no-cap.sh b/tests/ls/no-cap.sh
new file mode 100755
index 0000000..bee7bee
--- /dev/null
+++ b/tests/ls/no-cap.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# ensure that an empty "ca=" attribute disables ls's capability-checking
+
+# Copyright (C) 2008-2023 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_ ls
+require_strace_ capget
+
+LS_COLORS=ca=1; export LS_COLORS
+strace -e capget ls --color=always > /dev/null 2> out || fail=1
+$EGREP 'capget\(' out || skip_ "your ls doesn't call capget"
+
+rm -f out
+
+LS_COLORS=ca=:; export LS_COLORS
+strace -e capget ls --color=always > /dev/null 2> out || fail=1
+$EGREP 'capget\(' out && fail=1
+
+Exit $fail
diff --git a/tests/ls/quote-align.sh b/tests/ls/quote-align.sh
new file mode 100755
index 0000000..54a6fd0
--- /dev/null
+++ b/tests/ls/quote-align.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# test quote alignment combinations
+
+# Copyright (C) 2016-2023 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_ ls
+
+dirname='dir:name'
+mkdir "$dirname" || framework_failure_
+touch "$dirname/a b" "$dirname/c.foo" || framework_failure_
+
+e=$(printf '\033')
+color_code='0;31;42'
+c_pre="$e[0m$e[${color_code}m"
+c_post="$e[0m"
+
+cat <<EOF >exp || framework_failure_
+'$dirname':
+'a b' ${c_pre}c.foo${c_post}
+'$dirname':
+'a b' ${c_pre}c.foo${c_post}
+'$dirname':
+'a b'
+ ${c_pre}c.foo${c_post}
+'$dirname':
+'a b'
+${c_pre}c.foo${c_post}
+'$dirname':
+'a b', ${c_pre}c.foo${c_post}
+'$dirname':
+'a b' ${c_pre}c.foo${c_post}
+
+EOF
+
+for opt in '-w0 -x' '-x' '-og' '-1' '-m' '-C'; do
+ env TERM=xterm LS_COLORS="*.foo=$color_code" TIME_STYLE=+T \
+ ls $opt -R --quoting=shell-escape --color=always "$dirname" >> out || fail=1
+done
+
+# Append a newline, to accommodate less-capable versions of sed.
+echo >> out || framework_failure_
+
+# Strip possible varying portion of long format
+sed -e 's/.*T //' -e '/^total/d' out > k && mv k out
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/readdir-mountpoint-inode.sh b/tests/ls/readdir-mountpoint-inode.sh
new file mode 100755
index 0000000..18c5d53
--- /dev/null
+++ b/tests/ls/readdir-mountpoint-inode.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# ensure that ls -i works also for mount points
+
+# Copyright (C) 2009-2023 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_ ls
+
+# We use --local here so as to not activate
+# potentially very many remote mounts.
+df --local --out=target | sed -n '/^\/./p' > mount_points
+test -s mount_points ||
+ skip_ "this test requires a non-root mount point"
+
+# Given e.g., /dev/shm, produce the list of GNU ls options that
+# let us list just that entry using readdir data from its parent:
+# ls -i -I '[^s]*' -I 's[^h]*' -I 'sh[^m]*' -I 'shm?*' -I '.?*' \
+# -I '?' -I '??' /dev
+
+ls_ignore_options()
+{
+ name=$1
+ opts="-I '.?*' -I '$name?*'"
+ while :; do
+ glob=$(echo "$name"|sed 's/\(.*\)\(.\)$/\1[^\2]*/')
+ opts="$opts -I '$glob'"
+ name=$(echo "$name"|sed 's/.$//')
+ test -z "$name" && break
+ glob=$(echo "$name"|sed 's/./?/g')
+ opts="$opts -I '$glob'"
+ done
+ echo "$opts"
+}
+
+inode_via_readdir()
+{
+ mount_point=$1
+ base=$(basename "$mount_point")
+ case "$base" in
+ .*) skip_ 'mount point component starts with "."' ;;
+ *[*?]*) skip_ 'mount point component contains "?" or "*"' ;;
+ esac
+ opts=$(ls_ignore_options "$base")
+ parent_dir=$(dirname "$mount_point")
+ ls_out=$(eval "ls -i $opts '$parent_dir'")
+ test $? -eq 0 || \
+ skip_ "'$parent_dir' is not readable for current user"
+ echo $ls_out | sed 's/ .*//'
+}
+
+while read dir; do
+ readdir_inode=$(inode_via_readdir "$dir")
+ test $? = 77 && continue
+ stat_inode=$(timeout 1 stat --format=%i "$dir")
+ # If stat fails or says the inode is 0, skip $dir.
+ case $stat_inode in 0|'') continue;; esac
+ test "$readdir_inode" = "$stat_inode" || fail=1
+done < mount_points
+
+Exit $fail
diff --git a/tests/ls/recursive.sh b/tests/ls/recursive.sh
new file mode 100755
index 0000000..16554a8
--- /dev/null
+++ b/tests/ls/recursive.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# 4.1.1 and 4.1.2 had a bug whereby some recursive listings
+# didn't include a blank line between per-directory groups of files.
+
+# Copyright (C) 2001-2023 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_ ls
+
+mkdir x y a b c a/1 a/2 a/3 || framework_failure_
+touch f a/1/I a/1/II || framework_failure_
+
+
+# This first example is from Andreas Schwab's bug report.
+ls -R1 a b c > out || fail=1
+cat <<EOF > exp
+a:
+1
+2
+3
+
+a/1:
+I
+II
+
+a/2:
+
+a/3:
+
+b:
+
+c:
+EOF
+
+compare exp out || fail=1
+
+rm -rf out exp
+ls -R1 x y f > out || fail=1
+cat <<EOF > exp
+f
+
+x:
+
+y:
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/removed-directory.sh b/tests/ls/removed-directory.sh
new file mode 100755
index 0000000..692d05d
--- /dev/null
+++ b/tests/ls/removed-directory.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# If ls is asked to list a removed directory (e.g., the parent process's
+# current working directory has been removed by another process), it
+# should not emit an error message merely because the directory is removed.
+
+# Copyright (C) 2020-2023 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_ ls
+
+cwd=$(pwd)
+mkdir d || framework_failure_
+cd d || framework_failure_
+rmdir ../d || skip_ "can't remove working directory on this platform"
+
+# On NFS, 'ls' would run into the error "Stale file handle".
+test -d . || skip_ "can't examine removed working directory on this platform"
+
+ls >"$cwd"/out 2>"$cwd"/err || fail=1
+cd "$cwd" || framework_failure_
+
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+
+Exit $fail
diff --git a/tests/ls/root-rel-symlink-color.sh b/tests/ls/root-rel-symlink-color.sh
new file mode 100755
index 0000000..d8663e4
--- /dev/null
+++ b/tests/ls/root-rel-symlink-color.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Exercise the 8.17 ls bug with coloring relative-named symlinks in "/".
+
+# Copyright (C) 2012-2023 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_ ls test
+
+symlink_to_rel=
+for i in /*; do
+ # Skip non-symlinks:
+ env test -h "$i" || continue
+
+ # Skip dangling symlinks:
+ env test -e "$i" || continue
+
+ # Skip any symlink-to-absolute-name:
+ case $(readlink "$i") in /*) continue ;; esac
+
+ symlink_to_rel=$i
+ break
+done
+
+test -z "$symlink_to_rel" \
+ && skip_ no relative symlink in /
+
+e='\33'
+color_code='01;36'
+c_pre="$e[0m$e[${color_code}m"
+c_post="$e[0m"
+printf "$c_pre$symlink_to_rel$c_post\n" > exp || framework_failure_
+
+env TERM=xterm LS_COLORS="ln=$color_code:or=1;31;42" \
+ ls -d --color=always "$symlink_to_rel" > out || fail=1
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/rt-1.sh b/tests/ls/rt-1.sh
new file mode 100755
index 0000000..4a8b948
--- /dev/null
+++ b/tests/ls/rt-1.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+# Make sure name is used as secondary key when sorting on mtime or ctime.
+
+# Copyright (C) 1998-2023 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_ ls touch
+
+date=1998-01-15
+
+touch -d "$date" c || framework_failure_
+touch -d "$date" a || framework_failure_
+touch -d "$date" b || framework_failure_
+
+
+ls -1t a b c > out || fail=1
+cat <<EOF > exp
+a
+b
+c
+EOF
+compare exp out || fail=1
+
+rm -rf out exp
+ls -1rt a b c > out || fail=1
+cat <<EOF > exp
+c
+b
+a
+EOF
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/selinux-segfault.sh b/tests/ls/selinux-segfault.sh
new file mode 100755
index 0000000..4e0d551
--- /dev/null
+++ b/tests/ls/selinux-segfault.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Ensure we don't segfault in selinux handling
+
+# Copyright (C) 2008-2023 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_ ls
+
+# ls -l /proc/sys would segfault when built against libselinux1 2.0.15-2+b1
+f=/proc/sys
+test -r $f || f=.
+ls -l $f > out || fail=1
+
+# ls <= 8.32 would segfault when printing
+# the security context of broken symlink targets
+mkdir sedir || framework_failure_
+ln -sf missing sedir/broken || framework_failure_
+returns_ 1 ls -L -R -Z -m sedir > out || fail=1
+
+Exit $fail
diff --git a/tests/ls/slink-acl.sh b/tests/ls/slink-acl.sh
new file mode 100755
index 0000000..ab9fb8f
--- /dev/null
+++ b/tests/ls/slink-acl.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# verify that ls -lL works when applied to a symlink to an ACL'd file
+
+# Copyright (C) 2011-2023 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_ ls
+
+require_setfacl_
+
+touch k || framework_failure_
+setfacl -m user::r-- k || framework_failure_
+ln -s k s || framework_failure_
+
+set _ $(ls -Log s); shift; link=$1
+set _ $(ls -og k); shift; reg=$1
+
+test "$link" = "$reg" || fail=1
+
+Exit $fail
diff --git a/tests/ls/sort-width-option.sh b/tests/ls/sort-width-option.sh
new file mode 100755
index 0000000..bffd2e9
--- /dev/null
+++ b/tests/ls/sort-width-option.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Exercise the --sort=width option.
+
+# Copyright (C) 2021-2023 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_ ls
+
+mkdir subdir || framework_failure_
+touch subdir/aaaaa || framework_failure_
+touch subdir/bbb || framework_failure_
+touch subdir/cccc || framework_failure_
+touch subdir/d || framework_failure_
+touch subdir/zz || framework_failure_
+
+
+ls --sort=width subdir > out || fail=1
+cat <<\EOF > exp || framework_failure_
+d
+zz
+bbb
+cccc
+aaaaa
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/stat-dtype.sh b/tests/ls/stat-dtype.sh
new file mode 100755
index 0000000..794fcef
--- /dev/null
+++ b/tests/ls/stat-dtype.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Ensure that ls --file-type does not call stat unnecessarily.
+# Also check for the dtype-related (and fs-type dependent) bug
+# in coreutils-6.0 that made ls -CF columns misaligned.
+
+# Copyright (C) 2006-2023 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/>.
+
+# The trick is to create an un-stat'able symlink and to see if ls
+# can report its type nonetheless, using dirent.d_type.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls
+
+# Skip this test unless "." is on a file system with useful d_type info.
+# FIXME: This uses "ls -p" to decide whether to test "ls" with other options,
+# but if ls's d_type code is buggy then "ls -p" might be buggy too.
+mkdir -p c/d || framework_failure_
+chmod a-x c || framework_failure_
+if test "X$(ls -p c 2>&1)" != Xd/; then
+ skip_ "'.' is not on a suitable file system for this test"
+fi
+
+mkdir d || framework_failure_
+ln -s / d/s || framework_failure_
+chmod 600 d || framework_failure_
+
+mkdir -p e/a2345 e/b || framework_failure_
+chmod 600 e || framework_failure_
+
+
+ls --file-type d > out || fail=1
+cat <<\EOF > exp || framework_failure_
+s@
+EOF
+
+compare exp out || fail=1
+
+# Check for the ls -CF misaligned-columns bug:
+ls -CF e > out || fail=1
+
+# coreutils-6.0 would print two spaces after the first slash,
+# rather than the appropriate TAB.
+printf 'a2345/\tb/\n' > exp || framework_failure_
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/stat-failed.sh b/tests/ls/stat-failed.sh
new file mode 100755
index 0000000..642ac4a
--- /dev/null
+++ b/tests/ls/stat-failed.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# Verify that ls works properly when it fails to stat a file that is
+# not mentioned on the command line.
+
+# Copyright (C) 2006-2023 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_ ls
+skip_if_root_
+
+LS_MINOR_PROBLEM=1
+
+mkdir d || framework_failure_
+ln -s / d/s || framework_failure_
+chmod 600 d || framework_failure_
+
+
+returns_ 1 ls -Log d > out || fail=1
+
+# Linux 2.6.32 client with Isilon OneFS always returns d_type==DT_DIR ('d')
+# Newer Linux 3.10.0 returns the more correct DT_UNKNOWN ('?')
+grep '^[l?]?' out || skip_ 'unrecognized d_type returned'
+
+cat <<\EOF > exp || framework_failure_
+total 0
+?????????? ? ? ? s
+EOF
+
+sed 's/^l/?/' out | compare exp - || fail=1
+
+# Ensure that the offsets in --dired output are accurate.
+rm -f out exp
+returns_ $LS_MINOR_PROBLEM ls --dired -l d > out || fail=1
+
+cat <<\EOF > exp || framework_failure_
+ total 0
+ ?????????? ? ? ? ? ? s
+//DIRED// 44 45
+//DIRED-OPTIONS// --quoting-style=literal
+EOF
+
+sed 's/^ l/ ?/' out | compare exp - || fail=1
+
+Exit $fail
diff --git a/tests/ls/stat-free-color.sh b/tests/ls/stat-free-color.sh
new file mode 100755
index 0000000..b02c06b
--- /dev/null
+++ b/tests/ls/stat-free-color.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+# Show that --color need not use stat, as long as we have d_type support.
+
+# Copyright (C) 2011-2023 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_ ls
+require_strace_ stat
+require_dirent_d_type_
+
+stats='stat'
+# List of other _file name_ stat functions to increase coverage.
+other_stats='statx lstat stat64 lstat64 newfstatat fstatat64'
+for stat in $other_stats; do
+ strace -qe "$stat" true > /dev/null 2>&1 &&
+ stats="$stats,$stat"
+done
+
+# Disable enough features via LS_COLORS so that ls --color
+# can do its job without calling stat (other than the obligatory
+# one-call-per-command-line argument).
+cat <<EOF > color-without-stat || framework_failure_
+RESET 0
+DIR 01;34
+LINK 01;36
+FIFO 40;33
+SOCK 01;35
+DOOR 01;35
+BLK 40;33;01
+CHR 40;33;01
+ORPHAN 00
+SETUID 00
+SETGID 00
+CAPABILITY 00
+STICKY_OTHER_WRITABLE 00
+OTHER_WRITABLE 00
+STICKY 00
+EXEC 00
+MULTIHARDLINK 00
+EOF
+eval $(dircolors -b color-without-stat)
+
+# The system may perform additional stat-like calls before main.
+# Furthermore, underlying library functions may also implicitly
+# add an extra stat call, e.g. opendir since glibc-2.21-360-g46f894d.
+# Finally, ls(1) makes a stat call for stdout, but only in the case
+# when there is something to output.
+# To get the comparison right, first get a baseline count for running
+# 'ls -a' with one empty directory argument. Then, compare that with
+# the invocation under test.
+mkdir d || framework_failure_
+
+count_stats() { grep -vE '\+\+\+|ENOSYS|NOTSUP' "$1" | wc -l; }
+strace -q -o log1 -e $stats ls -a --color=always d || fail=1
+n_stat1=$(count_stats log1) || framework_failure_
+
+test $n_stat1 = 0 \
+ && skip_ 'No stat calls recognized on this platform'
+
+# Populate the test directory.
+mkdir d/subdir \
+ && touch d/regf \
+ && ln d/regf d/hlink \
+ && ln -s regf d/slink \
+ && ln -s nowhere d/dangle \
+ || framework_failure_
+
+# Invocation under test.
+strace -q -o log2 -e $stats ls --color=always d || fail=1
+n_stat2=$(count_stats log2) || framework_failure_
+
+# Expect the same number of stat calls.
+test $n_stat1 = $n_stat2 \
+ || { fail=1; head -n30 log*; }
+
+Exit $fail
diff --git a/tests/ls/stat-free-symlinks.sh b/tests/ls/stat-free-symlinks.sh
new file mode 100755
index 0000000..27f3414
--- /dev/null
+++ b/tests/ls/stat-free-symlinks.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+# ensure that ls does not stat a symlink in an unusual case
+
+# Copyright (C) 2007-2023 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_ ls
+require_strace_ stat
+
+stats='stat'
+# List of other _file name_ stat functions to increase coverage.
+other_stats='statx lstat stat64 lstat64 newfstatat fstatat64'
+for stat in $other_stats; do
+ strace -qe "$stat" true > /dev/null 2>&1 &&
+ stats="$stats,$stat"
+done
+
+# The system may perform additional stat-like calls before main.
+# Furthermore, underlying library functions may also implicitly
+# add an extra stat call, e.g. opendir since glibc-2.21-360-g46f894d.
+# To avoid counting those, first get a baseline count for running
+# ls with one empty directory argument. Then, compare that with the
+# invocation under test.
+count_stats() { grep -vE '\+\+\+|ENOSYS|NOTSUP' "$1" | wc -l; }
+mkdir d || framework_failure_
+strace -q -o log1 -e $stats ls -F --color=always d || fail=1
+n_stat1=$(count_stats log1) || framework_failure_
+
+test $n_stat1 = 0 \
+ && skip_ 'No stat calls recognized on this platform'
+
+
+touch x || framework_failure_
+chmod a+x x || framework_failure_
+ln -s x link-to-x || framework_failure_
+
+
+# ls from coreutils 6.9 would unnecessarily stat a symlink in an unusual case:
+# When not coloring orphan and missing entries, and without ln=target,
+# ensure that ls -F (or -d, or -l: i.e., when not dereferencing)
+# does not stat a symlink to directory, and does still color that
+# symlink and an executable file properly.
+
+LS_COLORS='or=0:mi=0:ex=01;32:ln=01;35' \
+ strace -qe $stats -o log2 ls -F --color=always x link-to-x > out.tmp || fail=1
+n_stat2=$(count_stats log2) || framework_failure_
+
+# Expect one more stat call,
+# which failed with coreutils 6.9 and earlier, which had 2.
+test $n_stat1 = $(($n_stat2 - 1)) \
+ || { fail=1; head -n30 log*; }
+
+# Check that output is colored, as requested, too.
+{
+ printf '\033[0m\033[01;35mlink-to-x\033[0m@\n'
+ printf '\033[01;32mx\033[0m*\n'
+} > exp || fail=1
+# Elide info messages strace can send to stdout of the form:
+# [ Process PID=1234 runs in 32 bit mode. ]
+sed '/Process PID=/d' out.tmp > out
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/stat-vs-dirent.sh b/tests/ls/stat-vs-dirent.sh
new file mode 100755
index 0000000..b29889c
--- /dev/null
+++ b/tests/ls/stat-vs-dirent.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Ensure that d_ino (from ls -di) and st_ino (from stat --format=%i) match.
+
+# Copyright (C) 2006-2023 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_ ls
+
+
+root_dev_ino=$(stat --format=%d-%i /)
+t=$(pwd)
+while :; do
+ if ls -i1 "$t" > tmp; then
+ # Extract the inode number from the first line of output from ls -i1.
+ # This value comes from dirent.d_ino, on systems with d_ino support.
+ d_ino=$(sed -n '1s/^ *\([0-9][0-9]*\) .*/\1/p;q' tmp)
+
+ # Extract the name of the corresponding directory entry.
+ file=$(sed -n '1s/^ *[0-9][0-9]* //p;q' tmp)
+
+ # Get its inode number (stat.st_ino) via stat(1)'s call to lstat.
+ st_ino=$(stat --format=%i "$t/$file") ||
+ skip_ "error stating: $t/$file" # removed or newlines in name etc.
+
+ # Make sure that they are the same.
+ # We know from experience that there may be mismatches on some
+ # buggy file systems, at mount points.
+ # Note that when a directory contains only entries whose names
+ # start with ".", d_ino and file will both be empty. In that case,
+ # skip the test.
+ if test -n "$d_ino" && test "$d_ino" != "$st_ino"; then
+ echo "$0: test failed: $t/$file: d_ino($d_ino) != st_ino($st_ino)
+ This may indicate a flaw in your kernel or file system implementation.
+ The flaw isn't serious for coreutils, but it might break other tools,
+ so you should report it to your operating system vendor." 1>&2
+
+ fail=1
+ break
+ fi
+ fi
+
+ t=$(cd "$t/.."; pwd)
+ dev_ino=$(stat --format=%d-%i "$t")
+ test $dev_ino = $root_dev_ino && break
+done
+
+Exit $fail
diff --git a/tests/ls/symlink-loop.sh b/tests/ls/symlink-loop.sh
new file mode 100755
index 0000000..0e0b0bf
--- /dev/null
+++ b/tests/ls/symlink-loop.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Exercise ls symlink ELOOP handling
+
+# Copyright (C) 2023 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_ ls
+
+ln -s loop loop || framework_failure_
+cat loop >/dev/null 2>&1 && skip_ 'symlink loops not detected'
+
+# With coreutils <= 9.3 we would dereference symlinks on the command line
+# and thus fail to display a symlink that could not be traversed.
+ls loop || fail=1
+ls -l loop || fail=1
+ls -l --color=always loop || fail=1
+
+# Ensure these still fail
+returns_ 2 ls -H loop || fail=1
+returns_ 2 ls -L loop || fail=1
+
+Exit $fail
diff --git a/tests/ls/symlink-quote.sh b/tests/ls/symlink-quote.sh
new file mode 100755
index 0000000..ba45de5
--- /dev/null
+++ b/tests/ls/symlink-quote.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Ensure symlinks are quoted appropriately
+
+# Copyright (C) 2017-2023 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_ ls
+
+ln -s 'needs quoting' symlink || framework_failure_
+
+ls -l --quoting-style='shell-escape' symlink >out || fail=1
+
+# Coreutils v8.26 and 8.27 failed to quote the target name
+grep "symlink -> 'needs quoting'\$" out >/dev/null ||
+ { cat out; fail=1; }
+
+Exit $fail
diff --git a/tests/ls/symlink-slash.sh b/tests/ls/symlink-slash.sh
new file mode 100755
index 0000000..f62e44d
--- /dev/null
+++ b/tests/ls/symlink-slash.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Do dereference a symlink arg if its name is written with a trailing slash.
+
+# Copyright (C) 1999-2023 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_ ls
+
+mkdir dir || framework_failure_
+ln -s dir symlink || framework_failure_
+
+set $(ls -l symlink/)
+
+# Prior to fileutils-4.0k, the following would have output '... symlink -> dir'.
+test "$*" = 'total 0' && : || fail=1
+
+Exit $fail
diff --git a/tests/ls/time-style-diag.sh b/tests/ls/time-style-diag.sh
new file mode 100755
index 0000000..af53ba3
--- /dev/null
+++ b/tests/ls/time-style-diag.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Ensure that an invalid --time-style=ARG is diagnosed the way we want.
+
+# Copyright (C) 2011-2023 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_ ls
+
+returns_ 2 ls -l --time-style=XX > out 2> err || fail=1
+
+cat <<\EOF > exp || framework_failure_
+ls: invalid argument 'XX' for 'time style'
+Valid arguments are:
+ - [posix-]full-iso
+ - [posix-]long-iso
+ - [posix-]iso
+ - [posix-]locale
+ - +FORMAT (e.g., +%H:%M) for a 'date'-style format
+Try 'ls --help' for more information.
+EOF
+
+compare exp err || fail=1
+compare /dev/null out || fail=1
+
+Exit $fail
diff --git a/tests/ls/w-option.sh b/tests/ls/w-option.sh
new file mode 100755
index 0000000..555e98c
--- /dev/null
+++ b/tests/ls/w-option.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# exercise the -w option
+
+# Copyright (C) 2015-2023 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_ ls
+getlimits_
+
+touch a b || framework_failure_
+chmod a+x a || framework_failure_
+
+# Negative values disallowed
+returns_ 2 ls -w-1 || fail=1
+
+# Verify octal parsing (especially since 0 is allowed)
+returns_ 2 ls -w08 || fail=1
+
+# Overflowed values are capped at SIZE_MAX
+ls -w$SIZE_OFLOW || fail=1
+
+# After coreutils 8.24 -w0 means no limit
+# and delimiting with spaces
+ls -w0 -x -T1 a b > out || fail=1
+printf '%s\n' 'a b' > exp || framework_failure_
+compare exp out || fail=1
+
+# Ensure that 0 line length doesn't cause division by zero
+TERM=xterm ls -w0 -x --color=always || fail=1
+
+# coreutils <= 8.24 could display 1 column too few
+ls -w4 -x -T0 a b > out || fail=1
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/x-option.sh b/tests/ls/x-option.sh
new file mode 100755
index 0000000..30c2e7c
--- /dev/null
+++ b/tests/ls/x-option.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Exercise the -x option.
+
+# Copyright (C) 2007-2023 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_ ls
+
+mkdir subdir || framework_failure_
+touch subdir/b || framework_failure_
+touch subdir/a || framework_failure_
+
+
+# Coreutils 6.8 and 6.9 would output this in the wrong order.
+ls -x subdir > out || fail=1
+ls -rx subdir >> out || fail=1
+cat <<\EOF > exp || framework_failure_
+a b
+b a
+EOF
+
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/ls/zero-option.sh b/tests/ls/zero-option.sh
new file mode 100755
index 0000000..d3c9f0d
--- /dev/null
+++ b/tests/ls/zero-option.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Verify behavior of ls --zero.
+
+# Copyright 2021-2023 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_ ls
+
+mkdir dir && touch dir/a dir/b dir/cc || framework_failure_
+
+allowed_options='-l' # explicit -l with --zero is allowed
+LC_ALL=C ls $allowed_options --zero dir >out || fail=1
+grep '^total' out || fail=1 # Ensure -l honored
+
+disallowed_options='-l --dired' # dired only enabled with -l
+returns_ 2 ls $disallowed_options --zero dir || fail=1
+
+disabled_options='--color=always -x -m -C -Q -q'
+LC_ALL=C ls $disabled_options --zero dir >out || fail=1
+tr '\n' '\0' <<EOF >exp
+a
+b
+cc
+EOF
+
+compare exp out || fail=1
+
+Exit $fail