On 23/12/18 19:35, Amin Bandali wrote: > Hello, > > I recently discovered ls’s --group-directories-first option to list > directories before files. It seems that the option treats all symlinks > as files, and thus doesn’t group symlinks to directories with the actual > directories on the top. > > It would be great if ls had an option to group directory symlinks with > the real directories when --group-directories-first is specified.
Good point. It probably should just do that with no other options. The attached implements that. cheers, Pádraig
>From 4ca5419556393cd697f793aec0b44aece60075fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]> Date: Thu, 27 Dec 2018 06:12:05 -0800 Subject: [PATCH] ls: with --group-directories-first, also group symlinked dirs * src/ls.c (is_linked_directory): A new function to also consider symlinked directories. (main): Rename check_symlink_color to check_symlink_mode, and enable that with --group-directories-first. (DIRFIRST_CHECK): Adjust to used is_linked_directory, rather than just is_directory. * tests/ls/group-dirs.sh: A new test. * tests/local.mk: Reference the new test. * NEWS: Mention the change in behavior. --- NEWS | 2 ++ src/ls.c | 26 ++++++++++++++++++-------- tests/local.mk | 1 + tests/ls/group-dirs.sh | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100755 tests/ls/group-dirs.sh diff --git a/NEWS b/NEWS index 6963fe3..4edb0e7 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ GNU coreutils NEWS -*- outline -*- approach is still used in situations where hard links to directories are allowed (e.g., NetBSD when superuser). + ls --group-directories-first will also group symlinks to directories. + 'test -a FILE' is not supported anymore. Long ago, there were concerns about the high probability of humans confusing the -a primary with the -a binary operator, so POSIX changed this to 'test -e FILE'. Scripts using it were diff --git a/src/ls.c b/src/ls.c index bf0c594..d5098d1 100644 --- a/src/ls.c +++ b/src/ls.c @@ -640,9 +640,9 @@ static struct color_ext_type *color_ext_list = NULL; static char *color_buf; /* True means to check for orphaned symbolic link, for displaying - colors. */ + colors, or to group symlink to directories with other dirs. */ -static bool check_symlink_color; +static bool check_symlink_mode; /* True means mention the inode number of each file. -i */ @@ -1483,8 +1483,10 @@ main (int argc, char **argv) if (is_colored (C_ORPHAN) || (is_colored (C_EXEC) && color_symlink_as_referent) || (is_colored (C_MISSING) && format == long_format)) - check_symlink_color = true; + check_symlink_mode = true; } + else if (directories_first) + check_symlink_mode = true; if (dereference == DEREF_UNDEFINED) dereference = ((immediate_dirs @@ -3149,7 +3151,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, || ((print_inode || format_needs_type) && (type == symbolic_link || type == unknown) && (dereference == DEREF_ALWAYS - || color_symlink_as_referent || check_symlink_color)) + || color_symlink_as_referent || check_symlink_mode)) /* Command line dereferences are already taken care of by the above assertion that the inode number is not yet known. */ || (print_inode && inode == NOT_AN_INODE_NUMBER) @@ -3300,7 +3302,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, } if (S_ISLNK (f->stat.st_mode) - && (format == long_format || check_symlink_color)) + && (format == long_format || check_symlink_mode)) { struct stat linkstats; @@ -3315,7 +3317,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, /* Avoid following symbolic links when possible, ie, when they won't be traced and when no indicator is needed. */ if (linkname - && (file_type <= indicator_style || check_symlink_color) + && (file_type <= indicator_style || check_symlink_mode) && stat (linkname, &linkstats) == 0) { f->linkok = true; @@ -3443,6 +3445,14 @@ is_directory (const struct fileinfo *f) return f->filetype == directory || f->filetype == arg_directory; } +/* Return true if F refers to a (symlinked) directory. */ +static bool +is_linked_directory (const struct fileinfo *f) +{ + return f->filetype == directory || f->filetype == arg_directory + || S_ISDIR (f->linkmode); +} + /* Put the name of the file that FILENAME is a symbolic link to into the LINKNAME field of 'f'. COMMAND_LINE_ARG indicates whether FILENAME is a command-line argument. */ @@ -3588,8 +3598,8 @@ typedef int (*qsortFunc)(V a, V b); #define DIRFIRST_CHECK(a, b) \ do \ { \ - bool a_is_dir = is_directory ((struct fileinfo const *) a); \ - bool b_is_dir = is_directory ((struct fileinfo const *) b); \ + bool a_is_dir = is_linked_directory ((struct fileinfo const *) a);\ + bool b_is_dir = is_linked_directory ((struct fileinfo const *) b);\ if (a_is_dir && !b_is_dir) \ return -1; /* a goes before b */ \ if (!a_is_dir && b_is_dir) \ diff --git a/tests/local.mk b/tests/local.mk index d2fdb7e..548fed2 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -595,6 +595,7 @@ all_tests = \ tests/ls/file-type.sh \ tests/ls/follow-slink.sh \ tests/ls/getxattr-speedup.sh \ + tests/ls/group-dirs.sh \ tests/ls/hex-option.sh \ tests/ls/infloop.sh \ tests/ls/inode.sh \ diff --git a/tests/ls/group-dirs.sh b/tests/ls/group-dirs.sh new file mode 100755 index 0000000..4fa3b01 --- /dev/null +++ b/tests/ls/group-dirs.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# test --group-directories-first + +# Copyright (C) 2018 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 + +Exit $fail -- 2.9.3
