@@ -432,13 +432,14 @@ static enum time_type time_type;
432432
433433enum 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
444445static enum sort_type sort_type ;
@@ -853,11 +854,11 @@ ARGMATCH_VERIFY (format_args, format_types);
853854
854855static char const * const sort_args [] =
855856{
856- "none" , "time" , "size" , "extension" , "version" , NULL
857+ "none" , "time" , "size" , "extension" , "version" , "alphanumeric" , NULL
857858};
858859static 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};
862863ARGMATCH_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+
32683319DEFINE_SORT_FUNCTIONS (ctime , cmp_ctime )
32693320DEFINE_SORT_FUNCTIONS (mtime , cmp_mtime )
32703321DEFINE_SORT_FUNCTIONS (atime , cmp_atime )
32713322DEFINE_SORT_FUNCTIONS (size , cmp_size )
32723323DEFINE_SORT_FUNCTIONS (name , cmp_name )
32733324DEFINE_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 );
0 commit comments