ls: implement --group-directories-first for compatibility with GNU ls

Also implement --group-directories which takes a parameter.
"first" is equivalent to --group-directories-first, "last" gives
reversed sorting.

Changes in sorting between elements of the same type (files,
directories) are not intended.

Differential Revision:	https://reviews.freebsd.org/D48347
This commit is contained in:
Piotr Pawel Stefaniak 2025-01-06 22:26:14 +01:00
parent 82fa7f83b5
commit 8b92977857
3 changed files with 51 additions and 7 deletions

View file

@ -39,6 +39,8 @@
.Nm
.Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuvwxy1\&,
.Op Fl -color Ns = Ns Ar when
.Op Fl -group-directories Ns = Ns Ar order
.Op Fl -group-directories-first
.Op Fl D Ar format
.Op Ar
.Sh DESCRIPTION
@ -303,6 +305,16 @@ options.
Display the long
.Pq Fl l
format output without the file owner's name or number.
.It Fl -group-directories Ns = Ns Ar order
Within results for each operand,
group directories together and print them either
.Cm first
or
.Cm last.
.It Fl -group-directories-first
Equivalent to
.Fl -group-directories Ns = Ns Ar first .
Implemented for compatibility with GNU coreutils.
.It Fl h
When used with the
.Fl l
@ -914,8 +926,13 @@ and
.St -p1003.1-2008 .
The options
.Fl B , D , G , I , T , U , W , Z , b , h , v , w , y
and
,
.Fl ,
.Fl -color
and
.Fl -group-directories Ns =
(including
.Fl -group-directories-first )
are non-standard extensions.
.Pp
The ACL support is compatible with

View file

@ -87,12 +87,24 @@ static void display(const FTSENT *, FTSENT *, int);
static int mastercmp(const FTSENT * const *, const FTSENT * const *);
static void traverse(int, char **, int);
#define COLOR_OPT (CHAR_MAX + 1)
enum {
GRP_NONE = 0,
GRP_DIR_FIRST = -1,
GRP_DIR_LAST = 1
};
enum {
BIN_OPT = CHAR_MAX,
COLOR_OPT,
GROUP_OPT
};
static const struct option long_opts[] =
{
{"color", optional_argument, NULL, COLOR_OPT},
{NULL, no_argument, NULL, 0}
{"color", optional_argument, NULL, COLOR_OPT},
{"group-directories", optional_argument, NULL, GROUP_OPT},
{"group-directories-first", no_argument, NULL, GROUP_OPT},
{NULL, no_argument, NULL, 0}
};
static void (*printfcn)(const DISPLAY *);
@ -105,6 +117,7 @@ int termwidth = 80; /* default terminal width */
int f_accesstime; /* use time of last access */
int f_birthtime; /* use time of birth */
int f_flags; /* show flags associated with a file */
static int f_groupdir = GRP_NONE;/* group directories first/last */
int f_humanval; /* show human-readable file sizes */
int f_inode; /* print inode */
static int f_kblocks; /* print size in kilobytes */
@ -449,6 +462,15 @@ main(int argc, char *argv[])
case 'y':
f_samesort = 1;
break;
case GROUP_OPT:
if (optarg == NULL || strcmp(optarg, "first") == 0)
f_groupdir = GRP_DIR_FIRST;
else if (strcmp(optarg, "last") == 0)
f_groupdir = GRP_DIR_LAST;
else
errx(2, "unsupported --group-directories value '%s' (must be first or last)",
optarg);
break;
case COLOR_OPT:
#ifdef COLORLS
if (optarg == NULL || do_color_always(optarg))
@ -1004,7 +1026,7 @@ label_out:
static int
mastercmp(const FTSENT * const *a, const FTSENT * const *b)
{
int a_info, b_info;
int a_info, b_info, dir;
a_info = (*a)->fts_info;
if (a_info == FTS_ERR)
@ -1023,5 +1045,10 @@ mastercmp(const FTSENT * const *a, const FTSENT * const *b)
if (b_info == FTS_D)
return (-1);
}
if (f_groupdir != GRP_NONE)
if ((dir = (a_info == FTS_D) - (b_info == FTS_D)) != 0)
return (f_groupdir * dir);
return (sortfcn(*a, *b));
}

View file

@ -219,9 +219,9 @@ usage(void)
{
(void)fprintf(stderr,
#ifdef COLORLS
"usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuvwxy1,] [--color=when] [-D format]"
"usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuvwxy1,] [--color=when] [-D format] [--group-directories=]"
#else
"usage: ls [-ABCFHILPRSTUWZabcdfghiklmnopqrstuvwxy1,] [-D format]"
"usage: ls [-ABCFHILPRSTUWZabcdfghiklmnopqrstuvwxy1,] [-D format] [--group-directories=]"
#endif
" [file ...]\n");
exit(1);