Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 62 additions & 9 deletions src/ls.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,13 +432,14 @@ static enum time_type time_type;

enum sort_type
{
sort_none = -1, /* -U */
sort_name, /* default */
sort_extension, /* -X */
sort_size, /* -S */
sort_version, /* -v */
sort_time, /* -t */
sort_numtypes /* the number of elements of this enum */
sort_none = -1, /* -U */
sort_name, /* default */
sort_extension, /* -X */
sort_size, /* -S */
sort_version, /* -v */
sort_time, /* -t */
sort_alphanumeric, /* -Y */
sort_numtypes /* the number of elements of this enum */
};

static enum sort_type sort_type;
Expand Down Expand Up @@ -853,11 +854,11 @@ ARGMATCH_VERIFY (format_args, format_types);

static char const *const sort_args[] =
{
"none", "time", "size", "extension", "version", NULL
"none", "time", "size", "extension", "version", "alphanumeric", 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_alphanumeric
};
ARGMATCH_VERIFY (sort_args, sort_types);

Expand Down Expand Up @@ -1838,6 +1839,11 @@ decode_switches (int argc, char **argv)
sort_type_specified = true;
break;

case 'Y':
sort_type = sort_alphanumeric;
sort_type_specified = true;
break;

case '1':
/* -1 has no effect after -l. */
if (format != long_format)
Expand Down Expand Up @@ -3265,12 +3271,58 @@ cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
return diff ? diff : cmp (a->name, b->name);
}

/* Compare alphanumerically. That is, strings are sorted by their
alphanumeric components, with numeric components sorted numerically.
For example, "foo9" comes before "foo10".

Sort code from https://stackoverflow.com/a/1344056/338803 */

static inline bool
isdigit(char c)
{
return '0' <= c && c <= '9';
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's probably a better function to use. I was trying to avoid adding any headers.


static inline int
cmp_alphanumeric (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
char *s1 = a->name;
char *s2 = b->name;

for (;;) {
if (*s2 == '\0')
return *s1 != '\0';
else if (*s1 == '\0')
return 1;
else if (!(isdigit(*s1) && isdigit(*s2))) {
if (*s1 != *s2) {
char str1[2] = {*s1, '\0'};
char str2[2] = {*s2, '\0'};
return cmp (str1, str2);
Comment on lines +3300 to +3302
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably isn't quite right either (for unicode).

} else
(++s1, ++s2);
} else {
char *lim1, *lim2;
unsigned long n1 = strtoul(s1, &lim1, 10);
unsigned long n2 = strtoul(s2, &lim2, 10);
if (n1 > n2)
return 1;
else if (n1 < n2)
return -1;
s1 = lim1;
s2 = lim2;
}
}
}

DEFINE_SORT_FUNCTIONS (ctime, cmp_ctime)
DEFINE_SORT_FUNCTIONS (mtime, cmp_mtime)
DEFINE_SORT_FUNCTIONS (atime, cmp_atime)
DEFINE_SORT_FUNCTIONS (size, cmp_size)
DEFINE_SORT_FUNCTIONS (name, cmp_name)
DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
DEFINE_SORT_FUNCTIONS (alphanumeric, cmp_alphanumeric)

/* Compare file versions.
Unlike all other compare functions above, cmp_version depends only
Expand Down Expand Up @@ -4700,6 +4752,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-w, --width=COLS assume screen width instead of current value\n\
-x list entries by lines instead of by columns\n\
-X sort alphabetically by entry extension\n\
-Y sort alphanumerically\n\
-Z, --context print any SELinux security context of each file\n\
-1 list one file per line\n\
"), stdout);
Expand Down
42 changes: 42 additions & 0 deletions tests/ls/alphanumeric-sort
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/sh
# Ensure alphanumeric sort output is correct

# Copyright (C) 2009-2010 Free Software Foundation, Inc.

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

if test "$VERBOSE" = yes; then
set -x
ls --version
fi

. $srcdir/test-lib.sh

touch foo1x.txt || framework_failure
touch foo12x.txt || framework_failure
touch foo2x.txt || framework_failure
touch foo20x.txt || framework_failure

ls -Y -1 > out || fail=1

cat <<\EOL > exp || framework_failure
foo1x.txt
foo2x.txt
foo12x.txt
foo20x.txt
EOL

cmp out exp || fail=1

Exit $fail