From dc7cd08682a7618e1bb2ef9764960e39de14237f Mon Sep 17 00:00:00 2001 From: Carl Edquist Date: Fri, 26 Mar 2021 04:27:54 -0500 Subject: [PATCH] ls: add --sort=width (-W) option to sort by filename width This helps identify the outliers for long filenames, and also produces a more compact display of columns when listing a directory with many entries of various widths. * src/ls.c (sort_type, sort_types, sort_width): New sort_width sort type. (sort_args): Add "width" sort arg. (decode_switches): Parse '-W' option. (cmp_width, fileinfo_width): New sort function and helper for filename width. (quote_name_width): Add function prototype declaration. (usage): Document -W/--sort=width option. * doc/coreutils.texi: Document -W/--sort=width option. * tests/local.mk: Add new test. * tests/ls/sort-width_W-option.sh: Exercise --sort=width and -W options. * NEWS: Mention the new feature. --- NEWS | 2 ++ doc/coreutils.texi | 7 ++++++ src/ls.c | 36 ++++++++++++++++++++++++--- tests/local.mk | 1 + tests/ls/sort-width_W-option.sh | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100755 tests/ls/sort-width_W-option.sh diff --git a/NEWS b/NEWS index 802f4b427..4ba164e85 100644 --- a/NEWS +++ b/NEWS @@ -70,6 +70,8 @@ GNU coreutils NEWS -*- outline -*- ls --classify now supports the "always", "auto", or "never" flags, to support only outputting classifier characters if connected to a tty. + ls now accepts the --sort=width (-W) option, to sort by filename width. + nl --line-increment can now take a negative number to decrement the count. ** Improvements diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 06ecdd74c..0c7bb8d44 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -7939,6 +7939,13 @@ Sort by version name and number, lowest first. It behaves like a default sort, except that each sequence of decimal digits is treated numerically as an index/version number. (@xref{Version sort ordering}.) +@item -W +@itemx --sort=width +@opindex -W +@opindex --sort +@opindex width@r{, sorting option for @command{ls}} +Sort by printed width of filenames. + @item -X @itemx --sort=extension @opindex -X diff --git a/src/ls.c b/src/ls.c index 2d0450e54..12ea550e3 100644 --- a/src/ls.c +++ b/src/ls.c @@ -307,6 +307,10 @@ static void parse_ls_color (void); static void getenv_quoting_style (void); +static size_t quote_name_width (const char *name, + struct quoting_options const *options, + int needs_general_quoting); + /* Initial size of hash table. Most hierarchies are likely to be shallower than this. */ #define INITIAL_TABLE_SIZE 30 @@ -475,6 +479,7 @@ enum sort_type sort_none = -1, /* -U */ sort_name, /* default */ sort_extension, /* -X */ + sort_width, /* -W */ sort_size, /* -S */ sort_version, /* -v */ sort_time, /* -t */ @@ -903,11 +908,11 @@ ARGMATCH_VERIFY (format_args, format_types); static char const *const sort_args[] = { - "none", "time", "size", "extension", "version", NULL + "none", "time", "size", "extension", "version", "width", NULL }; static enum sort_type const sort_types[] = { - sort_none, sort_time, sort_size, sort_extension, sort_version + sort_none, sort_time, sort_size, sort_extension, sort_version, sort_width }; ARGMATCH_VERIFY (sort_args, sort_types); @@ -1958,7 +1963,7 @@ decode_switches (int argc, char **argv) { int oi = -1; int c = getopt_long (argc, argv, - "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1", + "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UWXZ1", long_options, &oi); if (c == -1) break; @@ -2155,6 +2160,11 @@ decode_switches (int argc, char **argv) sort_type_specified = true; break; + case 'W': + sort_type = sort_width; + sort_type_specified = true; + break; + case 'X': sort_type = sort_extension; sort_type_specified = true; @@ -3877,6 +3887,20 @@ cmp_extension (struct fileinfo const *a, struct fileinfo const *b, return diff ? diff : cmp (a->name, b->name); } +static inline size_t +fileinfo_width (struct fileinfo const *f) +{ + return quote_name_width (f->name, filename_quoting_options, f->quoted); +} + +static inline int +cmp_width (struct fileinfo const *a, struct fileinfo const *b, + int (*cmp) (char const *, char const *)) +{ + int diff = fileinfo_width (a) - fileinfo_width (b); + return diff ? diff : cmp (a->name, b->name); +} + DEFINE_SORT_FUNCTIONS (ctime, cmp_ctime) DEFINE_SORT_FUNCTIONS (mtime, cmp_mtime) DEFINE_SORT_FUNCTIONS (atime, cmp_atime) @@ -3884,6 +3908,7 @@ DEFINE_SORT_FUNCTIONS (btime, cmp_btime) DEFINE_SORT_FUNCTIONS (size, cmp_size) DEFINE_SORT_FUNCTIONS (name, cmp_name) DEFINE_SORT_FUNCTIONS (extension, cmp_extension) +DEFINE_SORT_FUNCTIONS (width, cmp_width) /* Compare file versions. Unlike all other compare functions above, cmp_version depends only @@ -3936,6 +3961,7 @@ static qsortFunc const sort_functions[][2][2][2] = { LIST_SORTFUNCTION_VARIANTS (name), LIST_SORTFUNCTION_VARIANTS (extension), + LIST_SORTFUNCTION_VARIANTS (width), LIST_SORTFUNCTION_VARIANTS (size), { @@ -5454,7 +5480,8 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\ -S sort by file size, largest first\n\ --sort=WORD sort by WORD instead of name: none (-U), size (-S)\ ,\n\ - time (-t), version (-v), extension (-X)\n\ + time (-t), version (-v), extension (-X),\n\ + width (-W)\n\ --time=WORD change the default of using modification times;\n\ access time (-u): atime, access, use;\n\ change time (-c): ctime, status;\n\ @@ -5478,6 +5505,7 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\ "), stdout); fputs (_("\ -w, --width=COLS set output width to COLS. 0 means no limit\n\ + -W sort by entry name width\n\ -x list entries by lines instead of by columns\n\ -X sort alphabetically by entry extension\n\ -Z, --context print any security context of each file\n\ diff --git a/tests/local.mk b/tests/local.mk index 27e31ec8e..41981ffd6 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -632,6 +632,7 @@ all_tests = \ tests/ls/symlink-quote.sh \ tests/ls/symlink-slash.sh \ tests/ls/time-style-diag.sh \ + tests/ls/sort-width_W-option.sh \ tests/ls/x-option.sh \ tests/ls/hyperlink.sh \ tests/mkdir/p-1.sh \ diff --git a/tests/ls/sort-width_W-option.sh b/tests/ls/sort-width_W-option.sh new file mode 100755 index 000000000..5eb7afd18 --- /dev/null +++ b/tests/ls/sort-width_W-option.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Exercise the -W/--sort=width option. + +# Copyright (C) 2007-2021 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 . + +. "${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 -W subdir > out1 || fail=1 +ls --sort=width subdir > out2 || fail=1 +cat <<\EOF > exp || framework_failure_ +d +zz +bbb +cccc +aaaaa +EOF + +compare exp out1 || fail=1 +compare exp out2 || fail=1 + +Exit $fail -- 2.17.1