Skip to content

Commit b62f2fd

Browse files
committed
feat: add alphanumeric sort
1 parent f70c7b7 commit b62f2fd

File tree

2 files changed

+104
-9
lines changed

2 files changed

+104
-9
lines changed

src/ls.c

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,14 @@ static enum time_type time_type;
432432

433433
enum sort_type
434434
{
435-
sort_none = -1, /* -U */
436-
sort_name, /* default */
437-
sort_extension, /* -X */
438-
sort_size, /* -S */
439-
sort_version, /* -v */
440-
sort_time, /* -t */
441-
sort_numtypes /* the number of elements of this enum */
435+
sort_none = -1, /* -U */
436+
sort_name, /* default */
437+
sort_extension, /* -X */
438+
sort_size, /* -S */
439+
sort_version, /* -v */
440+
sort_time, /* -t */
441+
sort_alphanumeric, /* -Y */
442+
sort_numtypes /* the number of elements of this enum */
442443
};
443444

444445
static enum sort_type sort_type;
@@ -853,11 +854,11 @@ ARGMATCH_VERIFY (format_args, format_types);
853854

854855
static char const *const sort_args[] =
855856
{
856-
"none", "time", "size", "extension", "version", NULL
857+
"none", "time", "size", "extension", "version", "alphanumeric", NULL
857858
};
858859
static enum sort_type const sort_types[] =
859860
{
860-
sort_none, sort_time, sort_size, sort_extension, sort_version
861+
sort_none, sort_time, sort_size, sort_extension, sort_version, sort_alphanumeric
861862
};
862863
ARGMATCH_VERIFY (sort_args, sort_types);
863864

@@ -1838,6 +1839,11 @@ decode_switches (int argc, char **argv)
18381839
sort_type_specified = true;
18391840
break;
18401841

1842+
case 'Y':
1843+
sort_type = sort_alphanumeric;
1844+
sort_type_specified = true;
1845+
break;
1846+
18411847
case '1':
18421848
/* -1 has no effect after -l. */
18431849
if (format != long_format)
@@ -3265,12 +3271,58 @@ cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
32653271
return diff ? diff : cmp (a->name, b->name);
32663272
}
32673273

3274+
/* Compare alphanumerically. That is, strings are sorted by their
3275+
alphanumeric components, with numeric components sorted numerically.
3276+
For example, "foo9" comes before "foo10".
3277+
3278+
Sort code from https://stackoverflow.com/a/1344056/338803 */
3279+
3280+
static inline bool
3281+
isdigit(char c)
3282+
{
3283+
return '0' <= c && c <= '9';
3284+
}
3285+
3286+
static inline int
3287+
cmp_alphanumeric (struct fileinfo const *a, struct fileinfo const *b,
3288+
int (*cmp) (char const *, char const *))
3289+
{
3290+
char *s1 = a->name;
3291+
char *s2 = b->name;
3292+
3293+
for (;;) {
3294+
if (*s2 == '\0')
3295+
return *s1 != '\0';
3296+
else if (*s1 == '\0')
3297+
return 1;
3298+
else if (!(isdigit(*s1) && isdigit(*s2))) {
3299+
if (*s1 != *s2) {
3300+
char str1[2] = {*s1, '\0'};
3301+
char str2[2] = {*s2, '\0'};
3302+
return cmp (str1, str2);
3303+
} else
3304+
(++s1, ++s2);
3305+
} else {
3306+
char *lim1, *lim2;
3307+
unsigned long n1 = strtoul(s1, &lim1, 10);
3308+
unsigned long n2 = strtoul(s2, &lim2, 10);
3309+
if (n1 > n2)
3310+
return 1;
3311+
else if (n1 < n2)
3312+
return -1;
3313+
s1 = lim1;
3314+
s2 = lim2;
3315+
}
3316+
}
3317+
}
3318+
32683319
DEFINE_SORT_FUNCTIONS (ctime, cmp_ctime)
32693320
DEFINE_SORT_FUNCTIONS (mtime, cmp_mtime)
32703321
DEFINE_SORT_FUNCTIONS (atime, cmp_atime)
32713322
DEFINE_SORT_FUNCTIONS (size, cmp_size)
32723323
DEFINE_SORT_FUNCTIONS (name, cmp_name)
32733324
DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
3325+
DEFINE_SORT_FUNCTIONS (alphanumeric, cmp_alphanumeric)
32743326

32753327
/* Compare file versions.
32763328
Unlike all other compare functions above, cmp_version depends only
@@ -4700,6 +4752,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
47004752
-w, --width=COLS assume screen width instead of current value\n\
47014753
-x list entries by lines instead of by columns\n\
47024754
-X sort alphabetically by entry extension\n\
4755+
-Y sort alphanumerically\n\
47034756
-Z, --context print any SELinux security context of each file\n\
47044757
-1 list one file per line\n\
47054758
"), stdout);

tests/ls/alphanumeric-sort

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/sh
2+
# Ensure alphanumeric sort output is correct
3+
4+
# Copyright (C) 2009-2010 Free Software Foundation, Inc.
5+
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
if test "$VERBOSE" = yes; then
20+
set -x
21+
ls --version
22+
fi
23+
24+
. $srcdir/test-lib.sh
25+
26+
touch foo1x.txt || framework_failure
27+
touch foo12x.txt || framework_failure
28+
touch foo2x.txt || framework_failure
29+
touch foo20x.txt || framework_failure
30+
31+
ls -Y -1 > out || fail=1
32+
33+
cat <<\EOL > exp || framework_failure
34+
foo1x.txt
35+
foo2x.txt
36+
foo12x.txt
37+
foo20x.txt
38+
EOL
39+
40+
cmp out exp || fail=1
41+
42+
Exit $fail

0 commit comments

Comments
 (0)