Add y flag and environment variable LS_SAMESORT to specify the same

sorting order for time and name with the -t option.  IEEE Std 1003.2
(POSIX.2) mandates that the -t option sort in descending order, and
that if two files have the same timestamp, they should be sorted in
ascending order of their names.  The -r flag reverses both of these
sort orders, so they're never the same.  This creates significant
problems for sequentially named files stored on FAT file systems,
where it can be impossible to list them in the order in which they
were created.

Add , (comma) option to print file sizes grouped and separated by
thousands using the non-monetary separator returned by localeconv(3),
typically a comma or period.

MFC after:  14 days
This commit is contained in:
Greg Lehey 2012-11-08 00:24:26 +00:00
parent f8eb8ef711
commit 9aa68a3ffa
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=242725
6 changed files with 110 additions and 21 deletions

View File

@ -78,7 +78,10 @@ modcmp(const FTSENT *a, const FTSENT *b)
if (b->fts_statp->st_mtim.tv_nsec < if (b->fts_statp->st_mtim.tv_nsec <
a->fts_statp->st_mtim.tv_nsec) a->fts_statp->st_mtim.tv_nsec)
return (-1); return (-1);
return (strcoll(a->fts_name, b->fts_name)); if (f_samesort)
return (strcoll(b->fts_name, a->fts_name));
else
return (strcoll(a->fts_name, b->fts_name));
} }
int int
@ -104,7 +107,10 @@ acccmp(const FTSENT *a, const FTSENT *b)
if (b->fts_statp->st_atim.tv_nsec < if (b->fts_statp->st_atim.tv_nsec <
a->fts_statp->st_atim.tv_nsec) a->fts_statp->st_atim.tv_nsec)
return (-1); return (-1);
return (strcoll(a->fts_name, b->fts_name)); if (f_samesort)
return (strcoll(b->fts_name, a->fts_name));
else
return (strcoll(a->fts_name, b->fts_name));
} }
int int
@ -130,7 +136,10 @@ birthcmp(const FTSENT *a, const FTSENT *b)
if (b->fts_statp->st_birthtim.tv_nsec < if (b->fts_statp->st_birthtim.tv_nsec <
a->fts_statp->st_birthtim.tv_nsec) a->fts_statp->st_birthtim.tv_nsec)
return (-1); return (-1);
return (strcoll(a->fts_name, b->fts_name)); if (f_samesort)
return (strcoll(b->fts_name, a->fts_name));
else
return (strcoll(a->fts_name, b->fts_name));
} }
int int
@ -156,7 +165,10 @@ statcmp(const FTSENT *a, const FTSENT *b)
if (b->fts_statp->st_ctim.tv_nsec < if (b->fts_statp->st_ctim.tv_nsec <
a->fts_statp->st_ctim.tv_nsec) a->fts_statp->st_ctim.tv_nsec)
return (-1); return (-1);
return (strcoll(a->fts_name, b->fts_name)); if (f_samesort)
return (strcoll(b->fts_name, a->fts_name));
else
return (strcoll(a->fts_name, b->fts_name));
} }
int int

View File

@ -32,7 +32,7 @@
.\" @(#)ls.1 8.7 (Berkeley) 7/29/94 .\" @(#)ls.1 8.7 (Berkeley) 7/29/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd September 28, 2011 .Dd November 8, 2012
.Dt LS 1 .Dt LS 1
.Os .Os
.Sh NAME .Sh NAME
@ -40,7 +40,7 @@
.Nd list directory contents .Nd list directory contents
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuwx1 .Op Fl ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,
.Op Fl D Ar format .Op Fl D Ar format
.Op Ar .Op Ar
.Sh DESCRIPTION .Sh DESCRIPTION
@ -130,6 +130,8 @@ This option is equivalent to defining
.Ev CLICOLOR .Ev CLICOLOR
in the environment. in the environment.
(See below.) (See below.)
This functionality can be compiled out by removing the definition of
.Ev COLORLS .
.It Fl H .It Fl H
Symbolic links on the command line are followed. Symbolic links on the command line are followed.
This option is assumed if This option is assumed if
@ -249,12 +251,35 @@ subsection below, except (if the long format is not also requested)
the directory totals are not output when the output is in a the directory totals are not output when the output is in a
single column, even if multi-column output is requested. single column, even if multi-column output is requested.
.It Fl t .It Fl t
Sort by time modified (most recently modified Sort by descending time modified (most recently modified first). If two files
first) before sorting the operands in lexicographical have the same modification timestamp, sort their names in ascending
order. lexicographical order.
The
.Fl r
option reverses both of these sort orders.
.Pp
Note that these sort orders are contradictory: the time sequence is in
descending order, the lexicographical sort is in ascending order.
This behavior is mandated by
.St -p1003.2 .
This feature can cause problems listing files stored with sequential names on
FAT file systems, such as from digital cameras, where it is possible to have
more than one image with the same timestamp.
In such a case, the photos cannot be listed in the sequence in which
they were taken.
To ensure the same sort order for time and for lexicographical sorting, set the
environment variable
.Ev LS_SAMESORT
or use the
.Fl y
option.
This causes
.Nm
to reverse the lexicographal sort order when sorting files with the
same modification timestamp.
.It Fl u .It Fl u
Use time of last access, Use time of last access,
instead of last modification instead of time of last modification
of the file for sorting of the file for sorting
.Pq Fl t .Pq Fl t
or printing or printing
@ -268,6 +293,15 @@ The same as
.Fl C , .Fl C ,
except that the multi-column output is produced with entries sorted except that the multi-column output is produced with entries sorted
across, rather than down, the columns. across, rather than down, the columns.
.It Fl y
When the
.Fl t
option is set, sort the alphabetical output in the same order as the time output.
This has the same effect as setting
.Ev LS_SAMESORT .
See the description of the
.Fl t
option for more details.
.It Fl 1 .It Fl 1
(The numeric digit (The numeric digit
.Dq one . ) .Dq one . )
@ -275,6 +309,15 @@ Force output to be
one entry per line. one entry per line.
This is the default when This is the default when
output is not to a terminal. output is not to a terminal.
.It Fl ,
(Comma) When the
.Fl l
option is set, print file sizes grouped and separated by thousands using the
non-monetary separator returned by
.Xr localeconv 3 ,
typically a comma or period.
If no locale is set, or the locale does not have a non-monetary separator, this
option has no effect.
.El .El
.Pp .Pp
The The
@ -529,7 +572,7 @@ variable is defined.
.It Ev CLICOLOR_FORCE .It Ev CLICOLOR_FORCE
Color sequences are normally disabled if the output is not directed to Color sequences are normally disabled if the output is not directed to
a terminal. a terminal.
This can be overridden by setting this flag. This can be overridden by setting this variable.
The The
.Ev TERM .Ev TERM
variable still needs to reference a color capable terminal however variable still needs to reference a color capable terminal however
@ -655,6 +698,14 @@ Not all columns have changeable widths.
The fields are, The fields are,
in order: inode, block count, number of links, user name, in order: inode, block count, number of links, user name,
group name, flags, file size, file name. group name, flags, file size, file name.
.It Ev LS_SAMESORT
If this variable is set, the
.Fl t
option sorts the names of files with the same modification timestamp in the same
sense as the time sort.
See the description of the
.Fl t
option for more details.
.It Ev TERM .It Ev TERM
The The
.Ev CLICOLOR .Ev CLICOLOR
@ -678,6 +729,7 @@ specification.
.Xr getfacl 1 , .Xr getfacl 1 ,
.Xr sort 1 , .Xr sort 1 ,
.Xr xterm 1 , .Xr xterm 1 ,
.Xr localeconv 3 ,
.Xr strftime 3 , .Xr strftime 3 ,
.Xr strmode 3 , .Xr strmode 3 ,
.Xr termcap 5 , .Xr termcap 5 ,
@ -716,3 +768,9 @@ option description might be a feature that was
based on the fact that single-column output based on the fact that single-column output
usually goes to something other than a terminal. usually goes to something other than a terminal.
It is debatable whether this is a design bug. It is debatable whether this is a design bug.
.Pp
.St -p1003.2
mandates opposite sort orders for files with the same timestamp when
sorting with the
.Fl t
option.

View File

@ -109,10 +109,11 @@ int termwidth = 80; /* default terminal width */
int f_humanval; /* show human-readable file sizes */ int f_humanval; /* show human-readable file sizes */
int f_inode; /* print inode */ int f_inode; /* print inode */
static int f_kblocks; /* print size in kilobytes */ static int f_kblocks; /* print size in kilobytes */
int f_label; /* show MAC label */
static int f_listdir; /* list actual directory, not contents */ static int f_listdir; /* list actual directory, not contents */
static int f_listdot; /* list files beginning with . */ static int f_listdot; /* list files beginning with . */
static int f_noautodot; /* do not automatically enable -A for root */
int f_longform; /* long listing format */ int f_longform; /* long listing format */
static int f_noautodot; /* do not automatically enable -A for root */
static int f_nofollow; /* don't follow symbolic link arguments */ static int f_nofollow; /* don't follow symbolic link arguments */
int f_nonprint; /* show unprintables as ? */ int f_nonprint; /* show unprintables as ? */
static int f_nosort; /* don't sort output */ static int f_nosort; /* don't sort output */
@ -122,19 +123,21 @@ static int f_numericonly; /* don't convert uid/gid to name */
int f_octal_escape; /* like f_octal but use C escapes if possible */ int f_octal_escape; /* like f_octal but use C escapes if possible */
static int f_recursive; /* ls subdirectories also */ static int f_recursive; /* ls subdirectories also */
static int f_reversesort; /* reverse whatever sort is used */ static int f_reversesort; /* reverse whatever sort is used */
int f_sectime; /* print the real time for all files */ int f_samesort; /* sort time and name in same direction */
int f_sectime; /* print full time information */
static int f_singlecol; /* use single column output */ static int f_singlecol; /* use single column output */
int f_size; /* list size in short listing */ int f_size; /* list size in short listing */
static int f_sizesort;
int f_slash; /* similar to f_type, but only for dirs */ int f_slash; /* similar to f_type, but only for dirs */
int f_sortacross; /* sort across rows, not down columns */ int f_sortacross; /* sort across rows, not down columns */
int f_statustime; /* use time of last mode change */ int f_statustime; /* use time of last mode change */
static int f_stream; /* stream the output, separate with commas */ static int f_stream; /* stream the output, separate with commas */
static int f_timesort; /* sort by time vice name */ int f_thousands; /* show file sizes with thousands separators */
char *f_timeformat; /* user-specified time format */ char *f_timeformat; /* user-specified time format */
static int f_sizesort; static int f_timesort; /* sort by time vice name */
int f_type; /* add type character for non-regular files */ int f_type; /* add type character for non-regular files */
static int f_whiteout; /* show whiteout entries */ static int f_whiteout; /* show whiteout entries */
int f_label; /* show MAC label */
#ifdef COLORLS #ifdef COLORLS
int f_color; /* add type in color for non-regular files */ int f_color; /* add type in color for non-regular files */
@ -180,8 +183,10 @@ main(int argc, char *argv[])
} }
fts_options = FTS_PHYSICAL; fts_options = FTS_PHYSICAL;
if (getenv("LS_SAMESORT"))
f_samesort = 1;
while ((ch = getopt(argc, argv, while ((ch = getopt(argc, argv,
"1ABCD:FGHILPRSTUWZabcdfghiklmnopqrstuwx")) != -1) { "1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,")) != -1) {
switch (ch) { switch (ch) {
/* /*
* The -1, -C, -x and -l options all override each other so * The -1, -C, -x and -l options all override each other so
@ -237,6 +242,9 @@ main(int argc, char *argv[])
f_sizesort = 0; f_sizesort = 0;
break; break;
/* Other flags. Please keep alphabetic. */ /* Other flags. Please keep alphabetic. */
case ',':
f_thousands = 1;
break;
case 'B': case 'B':
f_nonprint = 0; f_nonprint = 0;
f_octal = 1; f_octal = 1;
@ -338,6 +346,9 @@ main(int argc, char *argv[])
f_octal = 0; f_octal = 0;
f_octal_escape = 0; f_octal_escape = 0;
break; break;
case 'y':
f_samesort = 1;
break;
default: default:
case '?': case '?':
usage(); usage();
@ -850,6 +861,8 @@ display(const FTSENT *p, FTSENT *list, int options)
d.s_size = sizelen; d.s_size = sizelen;
d.s_user = maxuser; d.s_user = maxuser;
} }
if (f_thousands) /* make space for commas */
d.s_size += (d.s_size - 1) / 3;
printfcn(&d); printfcn(&d);
output = 1; output = 1;

View File

@ -49,11 +49,13 @@ extern int f_longform; /* long listing format */
extern int f_octal; /* print unprintables in octal */ extern int f_octal; /* print unprintables in octal */
extern int f_octal_escape; /* like f_octal but use C escapes if possible */ extern int f_octal_escape; /* like f_octal but use C escapes if possible */
extern int f_nonprint; /* show unprintables as ? */ extern int f_nonprint; /* show unprintables as ? */
extern int f_samesort; /* sort time and name in same direction */
extern int f_sectime; /* print the real time for all files */ extern int f_sectime; /* print the real time for all files */
extern int f_size; /* list size in short listing */ extern int f_size; /* list size in short listing */
extern int f_slash; /* append a '/' if the file is a directory */ extern int f_slash; /* append a '/' if the file is a directory */
extern int f_sortacross; /* sort across rows, not down columns */ extern int f_sortacross; /* sort across rows, not down columns */
extern int f_statustime; /* use time of last mode change */ extern int f_statustime; /* use time of last mode change */
extern int f_thousands; /* show file sizes with thousands separators */
extern char *f_timeformat; /* user-specified time format */ extern char *f_timeformat; /* user-specified time format */
extern int f_notabs; /* don't use tab-separated multi-col output */ extern int f_notabs; /* don't use tab-separated multi-col output */
extern int f_type; /* add type character for non-regular files */ extern int f_type; /* add type character for non-regular files */

View File

@ -606,7 +606,11 @@ printsize(size_t width, off_t bytes)
humanize_number(buf, sizeof(buf), (int64_t)bytes, "", humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
(void)printf("%*s ", (u_int)width, buf); (void)printf("%*s ", (u_int)width, buf);
} else } else if (f_thousands) { /* with commas */
/* This format assignment needed to work round gcc bug. */
const char *format = "%*j'd ";
(void)printf(format, (u_int)width, bytes);
} else
(void)printf("%*jd ", (u_int)width, bytes); (void)printf("%*jd ", (u_int)width, bytes);
} }

View File

@ -175,7 +175,7 @@ prn_octal(const char *s)
size_t clen; size_t clen;
unsigned char ch; unsigned char ch;
int goodchar, i, len, prtlen; int goodchar, i, len, prtlen;
memset(&mbs, 0, sizeof(mbs)); memset(&mbs, 0, sizeof(mbs));
len = 0; len = 0;
while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
@ -222,9 +222,9 @@ usage(void)
{ {
(void)fprintf(stderr, (void)fprintf(stderr,
#ifdef COLORLS #ifdef COLORLS
"usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuwx1] [-D format]" "usage: ls [-ABCFGHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [-D format]"
#else #else
"usage: ls [-ABCFHILPRSTUWZabcdfghiklmnopqrstuwx1] [-D format]" "usage: ls [-ABCFHILPRSTUWZabcdfghiklmnopqrstuwxy1,] [-D format]"
#endif #endif
" [file ...]\n"); " [file ...]\n");
exit(1); exit(1);