summaryrefslogtreecommitdiff
path: root/src/ls.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ls.c')
-rw-r--r--src/ls.c4054
1 files changed, 2304 insertions, 1750 deletions
diff --git a/src/ls.c b/src/ls.c
index 3d48900..d976036 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -1,10 +1,10 @@
-/* `dir', `vdir' and `ls' directory listing programs for GNU.
- Copyright (C) 85, 88, 90, 91, 1995-2007 Free Software Foundation, Inc.
+/* 'dir', 'vdir' and 'ls' directory listing programs for GNU.
+ Copyright (C) 1985-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ 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 2, or (at your option)
- any later version.
+ 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
@@ -12,23 +12,22 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* If ls_mode is LS_MULTI_COL,
the multi-column format is the default regardless
of the type of output device.
- This is for the `dir' program.
+ This is for the 'dir' program.
If ls_mode is LS_LONG_FORMAT,
the long format is the default regardless of the
type of output device.
- This is for the `vdir' program.
+ This is for the 'vdir' program.
If ls_mode is LS_LS,
the output format depends on whether the output
device is a terminal.
- This is for the `ls' program. */
+ This is for the 'ls' program. */
/* Written by Richard Stallman and David MacKenzie. */
@@ -39,15 +38,11 @@
#include <config.h>
#include <sys/types.h>
-#if HAVE_TERMIOS_H
-# include <termios.h>
-#endif
+#include <termios.h>
#if HAVE_STROPTS_H
# include <stropts.h>
#endif
-#if HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
+#include <sys/ioctl.h>
#ifdef WINSIZE_IN_PTEM
# include <sys/stream.h>
@@ -57,10 +52,15 @@
#include <stdio.h>
#include <assert.h>
#include <setjmp.h>
-#include <grp.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>
+#include <selinux/selinux.h>
+#include <wchar.h>
+
+#if HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+#endif
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
present. */
@@ -72,6 +72,11 @@
# define siginterrupt(sig, flag) /* empty */
# endif
#endif
+
+/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt, so don't
+ restart syscalls after a signal handler fires. This may cause
+ colors to get messed up on the screen if 'ls' is interrupted, but
+ that's the best we can do on such a platform. */
#ifndef SA_RESTART
# define SA_RESTART 0
#endif
@@ -82,34 +87,43 @@
#include "acl.h"
#include "argmatch.h"
#include "dev-ino.h"
-#include "dirfd.h"
#include "error.h"
#include "filenamecat.h"
#include "hard-locale.h"
#include "hash.h"
#include "human.h"
#include "filemode.h"
-#include "inttostr.h"
+#include "filevercmp.h"
+#include "idcache.h"
#include "ls.h"
-#include "lstat.h"
#include "mbswidth.h"
#include "mpsort.h"
#include "obstack.h"
#include "quote.h"
-#include "quotearg.h"
-#include "same.h"
+#include "smack.h"
+#include "stat-size.h"
#include "stat-time.h"
#include "strftime.h"
-#include "strverscmp.h"
-#include "wcwidth.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-#include "xreadlink.h"
+#include "areadlink.h"
+#include "mbsalign.h"
+#include "dircolors.h"
+
+/* Include <sys/capability.h> last to avoid a clash of <sys/types.h>
+ include guards with some premature versions of libcap.
+ For more details, see <http://bugzilla.redhat.com/483548>. */
+#ifdef HAVE_CAP
+# include <sys/capability.h>
+#endif
#define PROGRAM_NAME (ls_mode == LS_LS ? "ls" \
- : (ls_mode == LS_MULTI_COL \
- ? "dir" : "vdir"))
+ : (ls_mode == LS_MULTI_COL \
+ ? "dir" : "vdir"))
-#define AUTHORS "Richard Stallman", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("David MacKenzie")
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
@@ -118,6 +132,26 @@
Subtracting doesn't always work, due to overflow. */
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))
+/* Unix-based readdir implementations have historically returned a dirent.d_ino
+ value that is sometimes not equal to the stat-obtained st_ino value for
+ that same entry. This error occurs for a readdir entry that refers
+ to a mount point. readdir's error is to return the inode number of
+ the underlying directory -- one that typically cannot be stat'ed, as
+ long as a file system is mounted on that directory. RELIABLE_D_INO
+ encapsulates whether we can use the more efficient approach of relying
+ on readdir-supplied d_ino values, or whether we must incur the cost of
+ calling stat or lstat to obtain each guaranteed-valid inode number. */
+
+#ifndef READDIR_LIES_ABOUT_MOUNTPOINT_D_INO
+# define READDIR_LIES_ABOUT_MOUNTPOINT_D_INO 1
+#endif
+
+#if READDIR_LIES_ABOUT_MOUNTPOINT_D_INO
+# define RELIABLE_D_INO(dp) NOT_AN_INODE_NUMBER
+#else
+# define RELIABLE_D_INO(dp) D_INO (dp)
+#endif
+
#if ! HAVE_STRUCT_STAT_ST_AUTHOR
# define st_author st_uid
#endif
@@ -150,6 +184,12 @@ verify (sizeof filetype_letter - 1 == arg_directory + 1);
C_LINK, C_SOCK, C_FILE, C_DIR \
}
+enum acl_type
+ {
+ ACL_T_NONE,
+ ACL_T_LSM_CONTEXT_ONLY,
+ ACL_T_YES
+ };
struct fileinfo
{
@@ -167,23 +207,22 @@ struct fileinfo
zero. */
mode_t linkmode;
+ /* security context. */
+ char *scontext;
+
bool stat_ok;
/* For symbolic link and color printing, true if linked-to file
exists, otherwise false. */
bool linkok;
-#if USE_ACL
- /* For long listings, true if the file has an access control list. */
- bool have_acl;
-#endif
- };
+ /* For long listings, true if the file has an access control list,
+ or a security context. */
+ enum acl_type acl_type;
-#if USE_ACL
-# define FILE_HAS_ACL(F) ((F)->have_acl)
-#else
-# define FILE_HAS_ACL(F) 0
-#endif
+ /* For color listings, true if a regular file has capability info. */
+ bool has_capability;
+ };
#define LEN_STR_PAIR(s) sizeof (s) - 1, s
@@ -197,65 +236,61 @@ struct bin_str
const char *string; /* Pointer to the same */
};
-char *getgroup ();
-char *getuser ();
-
#if ! HAVE_TCGETPGRP
# define tcgetpgrp(Fd) 0
#endif
static size_t quote_name (FILE *out, const char *name,
- struct quoting_options const *options,
- size_t *width);
+ struct quoting_options const *options,
+ size_t *width);
static char *make_link_name (char const *name, char const *linkname);
static int decode_switches (int argc, char **argv);
static bool file_ignored (char const *name);
static uintmax_t gobble_file (char const *name, enum filetype type,
- ino_t inode, bool command_line_arg,
- char const *dirname);
-static void print_color_indicator (const char *name, mode_t mode, int linkok,
- bool stat_ok, enum filetype type);
+ ino_t inode, bool command_line_arg,
+ char const *dirname);
+static bool print_color_indicator (const struct fileinfo *f,
+ bool symlink_target);
static void put_indicator (const struct bin_str *ind);
static void add_ignore_pattern (const char *pattern);
static void attach (char *dest, const char *dirname, const char *name);
static void clear_files (void);
static void extract_dirs_from_files (char const *dirname,
- bool command_line_arg);
+ bool command_line_arg);
static void get_link_name (char const *filename, struct fileinfo *f,
- bool command_line_arg);
+ bool command_line_arg);
static void indent (size_t from, size_t to);
static size_t calculate_columns (bool by_columns);
static void print_current_files (void);
static void print_dir (char const *name, char const *realname,
- bool command_line_arg);
-static void print_file_name_and_frills (const struct fileinfo *f);
+ bool command_line_arg);
+static size_t print_file_name_and_frills (const struct fileinfo *f,
+ size_t start_col);
static void print_horizontal (void);
static int format_user_width (uid_t u);
static int format_group_width (gid_t g);
static void print_long_format (const struct fileinfo *f);
static void print_many_per_line (void);
-static void print_name_with_quoting (const char *p, mode_t mode,
- int linkok, bool stat_ok,
- enum filetype type,
- struct obstack *stack);
+static size_t print_name_with_quoting (const struct fileinfo *f,
+ bool symlink_target,
+ struct obstack *stack,
+ size_t start_col);
static void prep_non_filename_text (void);
-static void print_type_indicator (bool stat_ok, mode_t mode,
- enum filetype type);
-static void print_with_commas (void);
+static bool print_type_indicator (bool stat_ok, mode_t mode,
+ enum filetype type);
+static void print_with_separator (char sep);
static void queue_directory (char const *name, char const *realname,
- bool command_line_arg);
+ bool command_line_arg);
static void sort_files (void);
static void parse_ls_color (void);
-void usage (int status);
-/* The name this program was run with. */
-char *program_name;
+static void getenv_quoting_style (void);
/* Initial size of hash table.
Most hierarchies are likely to be shallower than this. */
#define INITIAL_TABLE_SIZE 30
-/* The set of `active' directories, from the current command-line argument
+/* The set of 'active' directories, from the current command-line argument
to the level in the hierarchy at which files are being listed.
A directory is represented by its device and inode numbers (struct dev_ino).
A directory is added to this set when ls begins listing it or its
@@ -268,17 +303,17 @@ static Hash_table *active_dir_set;
/* The table of files in the current directory:
- `cwd_file' points to a vector of `struct fileinfo', one per file.
- `cwd_n_alloc' is the number of elements space has been allocated for.
- `cwd_n_used' is the number actually in use. */
+ 'cwd_file' points to a vector of 'struct fileinfo', one per file.
+ 'cwd_n_alloc' is the number of elements space has been allocated for.
+ 'cwd_n_used' is the number actually in use. */
/* Address of block containing the files that are described. */
static struct fileinfo *cwd_file;
-/* Length of block that `cwd_file' points to, measured in files. */
+/* Length of block that 'cwd_file' points to, measured in files. */
static size_t cwd_n_alloc;
-/* Index of first unused slot in `cwd_file'. */
+/* Index of first unused slot in 'cwd_file'. */
static size_t cwd_n_used;
/* Vector of pointers to files, in proper sorted order, and the number
@@ -287,15 +322,15 @@ static void **sorted_file;
static size_t sorted_file_alloc;
/* When true, in a color listing, color each symlink name according to the
- type of file it points to. Otherwise, color them according to the `ln'
+ type of file it points to. Otherwise, color them according to the 'ln'
directive in LS_COLORS. Dangling (orphan) symlinks are treated specially,
- regardless. This is set when `ln=target' appears in LS_COLORS. */
+ regardless. This is set when 'ln=target' appears in LS_COLORS. */
static bool color_symlink_as_referent;
/* mode of appropriate file for colorization */
#define FILE_OR_LINK_MODE(File) \
- ((color_symlink_as_referent & (File)->linkok) \
+ ((color_symlink_as_referent && (File)->linkok) \
? (File)->linkmode : (File)->stat.st_mode)
@@ -305,7 +340,7 @@ struct pending
{
char *name;
/* If the directory is actually the file pointed to by a symbolic link we
- were told to list, `realname' will contain the name of the symbolic
+ were told to list, 'realname' will contain the name of the symbolic
link, otherwise zero. */
char *realname;
bool command_line_arg;
@@ -317,17 +352,15 @@ static struct pending *pending_dirs;
/* Current time in seconds and nanoseconds since 1970, updated as
needed when deciding whether a file is recent. */
-static time_t current_time = TYPE_MINIMUM (time_t);
-static int current_time_ns = -1;
+static struct timespec current_time;
+
+static bool print_scontext;
+static char UNKNOWN_SECURITY_CONTEXT[] = "?";
/* Whether any of the files has an ACL. This affects the width of the
mode column. */
-#if USE_ACL
static bool any_has_acl;
-#else
-enum { any_has_acl = false };
-#endif
/* The number of columns to use for columns containing inode numbers,
block sizes, link counts, owners, groups, authors, major device
@@ -336,6 +369,7 @@ enum { any_has_acl = false };
static int inode_number_width;
static int block_size_width;
static int nlink_width;
+static int scontext_width;
static int owner_width;
static int group_width;
static int author_width;
@@ -365,9 +399,9 @@ enum format
static enum format format;
-/* `full-iso' uses full ISO-style dates and times. `long-iso' uses longer
- ISO-style time stamps, though shorter than `full-iso'. `iso' uses shorter
- ISO-style time stamps. `locale' uses locale-dependent time stamps. */
+/* 'full-iso' uses full ISO-style dates and times. 'long-iso' uses longer
+ ISO-style time stamps, though shorter than 'full-iso'. 'iso' uses shorter
+ ISO-style time stamps. 'locale' uses locale-dependent time stamps. */
enum time_style
{
full_iso_time_style, /* --time-style=full-iso */
@@ -447,13 +481,14 @@ static bool numeric_ids;
static bool print_block_size;
-/* Human-readable options for output. */
+/* Human-readable options for output, when printing block counts. */
static int human_output_opts;
-/* The units to use when printing sizes other than file sizes. */
+/* The units to use when printing block counts. */
static uintmax_t output_block_size;
/* Likewise, but for file sizes. */
+static int file_human_output_opts;
static uintmax_t file_output_block_size = 1;
/* Follow the output with a special string. Using this format,
@@ -461,10 +496,10 @@ static uintmax_t file_output_block_size = 1;
strange characters in file names. */
static bool dired;
-/* `none' means don't mention the type of files.
- `slash' means mention directories only, with a '/'.
- `file_type' means mention file types.
- `classify' means mention file types and mark executables.
+/* 'none' means don't mention the type of files.
+ 'slash' means mention directories only, with a '/'.
+ 'file_type' means mention file types.
+ 'classify' means mention file types and mark executables.
Controlled by -F, -p, and --indicator-style. */
@@ -495,6 +530,12 @@ ARGMATCH_VERIFY (indicator_style_args, indicator_style_types);
static bool print_with_color;
+/* Whether we used any colors in the output so far. If so, we will
+ need to restore the default color later. If not, we will need to
+ call prep_non_filename_text before using color for the first time. */
+
+static bool used_color = false;
+
enum color_type
{
color_never, /* 0: default or --color=never */
@@ -513,16 +554,18 @@ enum Dereference_symlink
enum indicator_no
{
- C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
+ C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK,
+ C_FIFO, C_SOCK,
C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,
- C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE
+ C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK,
+ C_CLR_TO_EOL
};
static const char *const indicator_name[]=
{
- "lc", "rc", "ec", "no", "fi", "di", "ln", "pi", "so",
+ "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so",
"bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st",
- "ow", "tw", NULL
+ "ow", "tw", "ca", "mh", "cl", NULL
};
struct color_ext_type
@@ -536,9 +579,10 @@ static struct bin_str color_indicator[] =
{
{ LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
{ LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
- { 0, NULL }, /* ec: End color (replaces lc+no+rc) */
- { LEN_STR_PAIR ("0") }, /* no: Normal */
- { LEN_STR_PAIR ("0") }, /* fi: File: default */
+ { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
+ { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
+ { 0, NULL }, /* no: Normal */
+ { 0, NULL }, /* fi: File: default */
{ LEN_STR_PAIR ("01;34") }, /* di: Directory: bright blue */
{ LEN_STR_PAIR ("01;36") }, /* ln: Symlink: bright cyan */
{ LEN_STR_PAIR ("33") }, /* pi: Pipe: yellow/brown */
@@ -554,6 +598,9 @@ static struct bin_str color_indicator[] =
{ LEN_STR_PAIR ("37;44") }, /* st: sticky: black on blue */
{ LEN_STR_PAIR ("34;42") }, /* ow: other-writable: blue on green */
{ LEN_STR_PAIR ("30;42") }, /* tw: ow w/ sticky: black on green */
+ { LEN_STR_PAIR ("30;41") }, /* ca: black on red */
+ { 0, NULL }, /* mh: disabled by default */
+ { LEN_STR_PAIR ("\033[K") }, /* cl: clear to end of line */
};
/* FIXME: comment */
@@ -594,11 +641,11 @@ static bool directories_first;
static enum
{
- /* Ignore files whose names start with `.', and files specified by
+ /* Ignore files whose names start with '.', and files specified by
--hide and --ignore. */
IGNORE_DEFAULT,
- /* Ignore `.', `..', and files specified by --ignore. */
+ /* Ignore '.', '..', and files specified by --ignore. */
IGNORE_DOT_AND_DOTDOT,
/* Ignore only files specified by --ignore. */
@@ -608,7 +655,7 @@ static enum
/* A linked list of shell-style globbing patterns. If a non-argument
file name matches any of these patterns, it is ignored.
Controlled by -I. Multiple -I options accumulate.
- The -B option adds `*~' and `.*~' to this list. */
+ The -B option adds '*~' and '.*~' to this list. */
struct ignore_pattern
{
@@ -622,12 +669,12 @@ static struct ignore_pattern *ignore_patterns;
variable itself to be ignored. */
static struct ignore_pattern *hide_patterns;
-/* True means output nongraphic chars in file names as `?'.
+/* True means output nongraphic chars in file names as '?'.
(-q, --hide-control-chars)
qmark_funny_chars and the quoting style (-Q, --quoting-style=WORD) are
independent. The algorithm is: first, obey the quoting style to get a
string representing the file name; then, if qmark_funny_chars is set,
- replace all nonprintable chars in that string with `?'. It's necessary
+ replace all nonprintable chars in that string with '?'. It's necessary
to replace nonprintable chars even in quoted strings, because we don't
want to mess up the terminal if control chars get sent to it, and some
quoting methods pass through control chars as-is. */
@@ -651,12 +698,16 @@ static bool print_dir_name;
static size_t line_length;
+/* The local time zone rules, as per the TZ environment variable. */
+
+static timezone_t localtz;
+
/* If true, the file listing format requires that stat be called on
each file. */
static bool format_needs_stat;
-/* Similar to `format_needs_stat', but set if only the file type is
+/* Similar to 'format_needs_stat', but set if only the file type is
needed. */
static bool format_needs_type;
@@ -681,6 +732,11 @@ static char const *long_time_format[2] =
screen columns small, because many people work in windows with
only 80 columns. But make this as wide as the other string
below, for recent files. */
+ /* TRANSLATORS: ls output needs to be aligned for ease of reading,
+ so be wary of using variable width fields from the locale.
+ Note %b is handled specially by ls and aligned correctly.
+ Note also that specifying a width as in %5b is erroneous as strftime
+ will count bytes rather than characters in multibyte locales. */
N_("%b %e %Y"),
/* strftime format for recent files (younger than 6 months), in -l
output. This should contain the month, day and time (at
@@ -689,6 +745,11 @@ static char const *long_time_format[2] =
screen columns small, because many people work in windows with
only 80 columns. But make this as wide as the other string
above, for non-recent files. */
+ /* TRANSLATORS: ls output needs to be aligned for ease of reading,
+ so be wary of using variable width fields from the locale.
+ Note %b is handled specially by ls and aligned correctly.
+ Note also that specifying a width as in %5b is erroneous as strftime
+ will count bytes rather than characters in multibyte locales. */
N_("%b %e %H:%M")
};
@@ -711,11 +772,14 @@ static int exit_status;
/* Exit statuses. */
enum
{
- /* "ls" had a minor problem (e.g., it could not stat a directory
- entry). */
+ /* "ls" had a minor problem. E.g., while processing a directory,
+ ls obtained the name of an entry via readdir, yet was later
+ unable to stat that name. This happens when listing a directory
+ in which entries are actively being removed or renamed. */
LS_MINOR_PROBLEM = 1,
- /* "ls" had more serious trouble. */
+ /* "ls" had more serious trouble (e.g., memory exhausted, invalid
+ option or failure to stat a command line argument. */
LS_FAILURE = 2
};
@@ -733,10 +797,6 @@ enum
GROUP_DIRECTORIES_FIRST_OPTION,
HIDE_OPTION,
INDICATOR_STYLE_OPTION,
-
- /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
- KILOBYTES_LONG_OPTION,
-
QUOTING_STYLE_OPTION,
SHOW_CONTROL_CHARS_OPTION,
SI_OPTION,
@@ -756,7 +816,7 @@ static struct option const long_options[] =
GROUP_DIRECTORIES_FIRST_OPTION},
{"human-readable", no_argument, NULL, 'h'},
{"inode", no_argument, NULL, 'i'},
- {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION},
+ {"kibibytes", no_argument, NULL, 'k'},
{"numeric-uid-gid", no_argument, NULL, 'n'},
{"no-group", no_argument, NULL, 'G'},
{"hide-control-chars", no_argument, NULL, 'q'},
@@ -787,6 +847,7 @@ static struct option const long_options[] =
{"time-style", required_argument, NULL, TIME_STYLE_OPTION},
{"color", optional_argument, NULL, COLOR_OPTION},
{"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
+ {"context", no_argument, 0, 'Z'},
{"author", no_argument, NULL, AUTHOR_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -879,16 +940,16 @@ static size_t dired_pos;
#define DIRED_INDENT() \
do \
{ \
- if (dired) \
- DIRED_FPUTS_LITERAL (" ", stdout); \
+ if (dired) \
+ DIRED_FPUTS_LITERAL (" ", stdout); \
} \
while (0)
-/* With --dired, store pairs of beginning and ending indices of filenames. */
+/* With --dired, store pairs of beginning and ending indices of file names. */
static struct obstack dired_obstack;
/* With --dired, store pairs of beginning and ending indices of any
- directory names that appear as headers (just before `total' line)
+ directory names that appear as headers (just before 'total' line)
for lists of directory entries. Such directory names are seen when
listing hierarchies using -R and when a directory is listed with at
least one other command line argument. */
@@ -899,7 +960,7 @@ static struct obstack subdired_obstack;
do \
{ \
if (dired) \
- obstack_grow (obs, &dired_pos, sizeof (dired_pos)); \
+ obstack_grow (obs, &dired_pos, sizeof (dired_pos)); \
} \
while (0)
@@ -909,27 +970,36 @@ static struct obstack subdired_obstack;
static struct obstack dev_ino_obstack;
/* Push a pair onto the device/inode stack. */
-#define DEV_INO_PUSH(Dev, Ino) \
- do \
- { \
- struct dev_ino *di; \
- obstack_blank (&dev_ino_obstack, sizeof (struct dev_ino)); \
- di = -1 + (struct dev_ino *) obstack_next_free (&dev_ino_obstack); \
- di->st_dev = (Dev); \
- di->st_ino = (Ino); \
- } \
- while (0)
+static void
+dev_ino_push (dev_t dev, ino_t ino)
+{
+ void *vdi;
+ struct dev_ino *di;
+ int dev_ino_size = sizeof *di;
+ obstack_blank (&dev_ino_obstack, dev_ino_size);
+ vdi = obstack_next_free (&dev_ino_obstack);
+ di = vdi;
+ di--;
+ di->st_dev = dev;
+ di->st_ino = ino;
+}
/* Pop a dev/ino struct off the global dev_ino_obstack
and return that struct. */
static struct dev_ino
dev_ino_pop (void)
{
- assert (sizeof (struct dev_ino) <= obstack_object_size (&dev_ino_obstack));
- obstack_blank (&dev_ino_obstack, -(int) (sizeof (struct dev_ino)));
- return *(struct dev_ino *) obstack_next_free (&dev_ino_obstack);
+ void *vdi;
+ struct dev_ino *di;
+ int dev_ino_size = sizeof *di;
+ assert (dev_ino_size <= obstack_object_size (&dev_ino_obstack));
+ obstack_blank_fast (&dev_ino_obstack, -dev_ino_size);
+ vdi = obstack_next_free (&dev_ino_obstack);
+ di = vdi;
+ return *di;
}
+/* Note the use commented out below:
#define ASSERT_MATCHING_DEV_INO(Name, Di) \
do \
{ \
@@ -940,7 +1010,7 @@ dev_ino_pop (void)
assert (sb.st_ino == Di.st_ino); \
} \
while (0)
-
+*/
/* Write to standard output PREFIX, followed by the quoting style and
a space-separated list of the integers stored in OS all on one line. */
@@ -959,11 +1029,61 @@ dired_dump_obstack (const char *prefix, struct obstack *os)
pos = (size_t *) obstack_finish (os);
fputs (prefix, stdout);
for (i = 0; i < n_pos; i++)
- printf (" %lu", (unsigned long int) pos[i]);
+ printf (" %lu", (unsigned long int) pos[i]);
putchar ('\n');
}
}
+/* Read the abbreviated month names from the locale, to align them
+ and to determine the max width of the field and to truncate names
+ greater than our max allowed.
+ Note even though this handles multibyte locales correctly
+ it's not restricted to them as single byte locales can have
+ variable width abbreviated months and also precomputing/caching
+ the names was seen to increase the performance of ls significantly. */
+
+/* max number of display cells to use */
+enum { MAX_MON_WIDTH = 5 };
+/* In the unlikely event that the abmon[] storage is not big enough
+ an error message will be displayed, and we revert to using
+ unmodified abbreviated month names from the locale database. */
+static char abmon[12][MAX_MON_WIDTH * 2 * MB_LEN_MAX + 1];
+/* minimum width needed to align %b, 0 => don't use precomputed values. */
+static size_t required_mon_width;
+
+static size_t
+abmon_init (void)
+{
+#ifdef HAVE_NL_LANGINFO
+ required_mon_width = MAX_MON_WIDTH;
+ size_t curr_max_width;
+ do
+ {
+ curr_max_width = required_mon_width;
+ required_mon_width = 0;
+ for (int i = 0; i < 12; i++)
+ {
+ size_t width = curr_max_width;
+
+ size_t req = mbsalign (nl_langinfo (ABMON_1 + i),
+ abmon[i], sizeof (abmon[i]),
+ &width, MBS_ALIGN_LEFT, 0);
+
+ if (req == (size_t) -1 || req >= sizeof (abmon[i]))
+ {
+ required_mon_width = 0; /* ignore precomputed strings. */
+ return required_mon_width;
+ }
+
+ required_mon_width = MAX (required_mon_width, width);
+ }
+ }
+ while (curr_max_width > required_mon_width);
+#endif
+
+ return required_mon_width;
+}
+
static size_t
dev_ino_hash (void const *x, size_t table_size)
{
@@ -1034,8 +1154,8 @@ is_colored (enum indicator_no type)
size_t len = color_indicator[type].len;
char const *s = color_indicator[type].string;
return ! (len == 0
- || (len == 1 && strncmp (s, "0", 1) == 0)
- || (len == 2 && strncmp (s, "00", 2) == 0));
+ || (len == 1 && STRNCMP_LIT (s, "0") == 0)
+ || (len == 2 && STRNCMP_LIT (s, "00") == 0));
}
static void
@@ -1045,6 +1165,17 @@ restore_default_color (void)
put_indicator (&color_indicator[C_RIGHT]);
}
+static void
+set_normal_color (void)
+{
+ if (print_with_color && is_colored (C_NORM))
+ {
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (&color_indicator[C_NORM]);
+ put_indicator (&color_indicator[C_RIGHT]);
+ }
+}
+
/* An ordinary signal was received; arrange for the program to exit. */
static void
@@ -1076,39 +1207,40 @@ stophandler (int sig)
static void
process_signals (void)
{
- while (interrupt_signal | stop_signal_count)
+ while (interrupt_signal || stop_signal_count)
{
int sig;
int stops;
sigset_t oldset;
- restore_default_color ();
+ if (used_color)
+ restore_default_color ();
fflush (stdout);
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
/* Reload interrupt_signal and stop_signal_count, in case a new
- signal was handled before sigprocmask took effect. */
+ signal was handled before sigprocmask took effect. */
sig = interrupt_signal;
stops = stop_signal_count;
/* SIGTSTP is special, since the application can receive that signal
- more than once. In this case, don't set the signal handler to the
- default. Instead, just raise the uncatchable SIGSTOP. */
+ more than once. In this case, don't set the signal handler to the
+ default. Instead, just raise the uncatchable SIGSTOP. */
if (stops)
- {
- stop_signal_count = stops - 1;
- sig = SIGSTOP;
- }
+ {
+ stop_signal_count = stops - 1;
+ sig = SIGSTOP;
+ }
else
- signal (sig, SIG_DFL);
+ signal (sig, SIG_DFL);
/* Exit or suspend the program. */
raise (sig);
sigprocmask (SIG_SETMASK, &oldset, NULL);
/* If execution reaches here, then the program has been
- continued (after being suspended). */
+ continued (after being suspended). */
}
}
@@ -1143,14 +1275,14 @@ main (int argc, char **argv)
SIGXFSZ,
#endif
};
- enum { nsigs = sizeof sig / sizeof sig[0] };
+ enum { nsigs = ARRAY_CARDINALITY (sig) };
#if ! SA_NOCLDSTOP
bool caught_sig[nsigs];
#endif
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1158,13 +1290,16 @@ main (int argc, char **argv)
initialize_exit_failure (LS_FAILURE);
atexit (close_stdout);
-#define N_ENTRIES(Array) (sizeof Array / sizeof *(Array))
- assert (N_ENTRIES (color_indicator) + 1 == N_ENTRIES (indicator_name));
+ assert (ARRAY_CARDINALITY (color_indicator) + 1
+ == ARRAY_CARDINALITY (indicator_name));
exit_status = EXIT_SUCCESS;
print_dir_name = true;
pending_dirs = NULL;
+ current_time.tv_sec = TYPE_MINIMUM (time_t);
+ current_time.tv_nsec = -1;
+
i = decode_switches (argc, argv);
if (print_with_color)
@@ -1176,82 +1311,83 @@ main (int argc, char **argv)
{
/* Avoid following symbolic links when possible. */
if (is_colored (C_ORPHAN)
- || is_colored (C_EXEC)
- || (is_colored (C_MISSING) && format == long_format))
- check_symlink_color = true;
+ || (is_colored (C_EXEC) && color_symlink_as_referent)
+ || (is_colored (C_MISSING) && format == long_format))
+ check_symlink_color = true;
/* If the standard output is a controlling terminal, watch out
for signals, so that the colors can be restored to the
default state if "ls" is suspended or interrupted. */
if (0 <= tcgetpgrp (STDOUT_FILENO))
- {
- int j;
+ {
+ int j;
#if SA_NOCLDSTOP
- struct sigaction act;
-
- sigemptyset (&caught_signals);
- for (j = 0; j < nsigs; j++)
- {
- sigaction (sig[j], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[j]);
- }
-
- act.sa_mask = caught_signals;
- act.sa_flags = SA_RESTART;
-
- for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- {
- act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
- sigaction (sig[j], &act, NULL);
- }
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (j = 0; j < nsigs; j++)
+ {
+ sigaction (sig[j], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[j]);
+ }
+
+ act.sa_mask = caught_signals;
+ act.sa_flags = SA_RESTART;
+
+ for (j = 0; j < nsigs; j++)
+ if (sigismember (&caught_signals, sig[j]))
+ {
+ act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
+ sigaction (sig[j], &act, NULL);
+ }
#else
- for (j = 0; j < nsigs; j++)
- {
- caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
- if (caught_sig[j])
- {
- signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
- siginterrupt (sig[j], 0);
- }
- }
+ for (j = 0; j < nsigs; j++)
+ {
+ caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
+ if (caught_sig[j])
+ {
+ signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
+ siginterrupt (sig[j], 0);
+ }
+ }
#endif
- }
-
- prep_non_filename_text ();
+ }
}
if (dereference == DEREF_UNDEFINED)
dereference = ((immediate_dirs
- || indicator_style == classify
- || format == long_format)
- ? DEREF_NEVER
- : DEREF_COMMAND_LINE_SYMLINK_TO_DIR);
+ || indicator_style == classify
+ || format == long_format)
+ ? DEREF_NEVER
+ : DEREF_COMMAND_LINE_SYMLINK_TO_DIR);
/* When using -R, initialize a data structure we'll use to
detect any directory cycles. */
if (recursive)
{
active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, NULL,
- dev_ino_hash,
- dev_ino_compare,
- dev_ino_free);
+ dev_ino_hash,
+ dev_ino_compare,
+ dev_ino_free);
if (active_dir_set == NULL)
- xalloc_die ();
+ xalloc_die ();
obstack_init (&dev_ino_obstack);
}
+ localtz = tzalloc (getenv ("TZ"));
+
format_needs_stat = sort_type == sort_time || sort_type == sort_size
|| format == long_format
+ || print_scontext
|| print_block_size;
format_needs_type = (! format_needs_stat
- && (recursive
- || print_with_color
- || indicator_style != none
- || directories_first));
+ && (recursive
+ || print_with_color
+ || indicator_style != none
+ || directories_first));
if (dired)
{
@@ -1270,9 +1406,9 @@ main (int argc, char **argv)
if (n_files <= 0)
{
if (immediate_dirs)
- gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, "");
+ gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, "");
else
- queue_directory (".", NULL, true);
+ queue_directory (".", NULL, true);
}
else
do
@@ -1283,11 +1419,11 @@ main (int argc, char **argv)
{
sort_files ();
if (!immediate_dirs)
- extract_dirs_from_files (NULL, true);
- /* `cwd_n_used' might be zero now. */
+ extract_dirs_from_files (NULL, true);
+ /* 'cwd_n_used' might be zero now. */
}
- /* In the following if/else blocks, it is sufficient to test `pending_dirs'
+ /* In the following if/else blocks, it is sufficient to test 'pending_dirs'
(and not pending_dirs->name) because there may be no markers in the queue
at this point. A marker may be enqueued when extract_dirs_from_files is
called with a non-empty string or via print_dir. */
@@ -1295,7 +1431,7 @@ main (int argc, char **argv)
{
print_current_files ();
if (pending_dirs)
- DIRED_PUTCHAR ('\n');
+ DIRED_PUTCHAR ('\n');
}
else if (n_files <= 1 && pending_dirs && pending_dirs->next == 0)
print_dir_name = false;
@@ -1306,25 +1442,25 @@ main (int argc, char **argv)
pending_dirs = pending_dirs->next;
if (LOOP_DETECT)
- {
- if (thispend->name == NULL)
- {
- /* thispend->name == NULL means this is a marker entry
- indicating we've finished processing the directory.
- Use its dev/ino numbers to remove the corresponding
- entry from the active_dir_set hash table. */
- struct dev_ino di = dev_ino_pop ();
- struct dev_ino *found = hash_delete (active_dir_set, &di);
- /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
- assert (found);
- dev_ino_free (found);
- free_pending_ent (thispend);
- continue;
- }
- }
+ {
+ if (thispend->name == NULL)
+ {
+ /* thispend->name == NULL means this is a marker entry
+ indicating we've finished processing the directory.
+ Use its dev/ino numbers to remove the corresponding
+ entry from the active_dir_set hash table. */
+ struct dev_ino di = dev_ino_pop ();
+ struct dev_ino *found = hash_delete (active_dir_set, &di);
+ /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
+ assert (found);
+ dev_ino_free (found);
+ free_pending_ent (thispend);
+ continue;
+ }
+ }
print_dir (thispend->name, thispend->realname,
- thispend->command_line_arg);
+ thispend->command_line_arg);
free_pending_ent (thispend);
print_dir_name = true;
@@ -1334,29 +1470,38 @@ main (int argc, char **argv)
{
int j;
- restore_default_color ();
+ if (used_color)
+ {
+ /* Skip the restore when it would be a no-op, i.e.,
+ when left is "\033[" and right is "m". */
+ if (!(color_indicator[C_LEFT].len == 2
+ && memcmp (color_indicator[C_LEFT].string, "\033[", 2) == 0
+ && color_indicator[C_RIGHT].len == 1
+ && color_indicator[C_RIGHT].string[0] == 'm'))
+ restore_default_color ();
+ }
fflush (stdout);
/* Restore the default signal handling. */
#if SA_NOCLDSTOP
for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- signal (sig[j], SIG_DFL);
+ if (sigismember (&caught_signals, sig[j]))
+ signal (sig[j], SIG_DFL);
#else
for (j = 0; j < nsigs; j++)
- if (caught_sig[j])
- signal (sig[j], SIG_DFL);
+ if (caught_sig[j])
+ signal (sig[j], SIG_DFL);
#endif
/* Act on any signals that arrived before the default was restored.
- This can process signals out of order, but there doesn't seem to
- be an easy way to do them in order, and the order isn't that
- important anyway. */
+ This can process signals out of order, but there doesn't seem to
+ be an easy way to do them in order, and the order isn't that
+ important anyway. */
for (j = stop_signal_count; j; j--)
- raise (SIGSTOP);
+ raise (SIGSTOP);
j = interrupt_signal;
if (j)
- raise (j);
+ raise (j);
}
if (dired)
@@ -1365,7 +1510,7 @@ main (int argc, char **argv)
dired_dump_obstack ("//DIRED//", &dired_obstack);
dired_dump_obstack ("//SUBDIRED//", &subdired_obstack);
printf ("//DIRED-OPTIONS// --quoting-style=%s\n",
- quoting_style_args[get_quoting_style (filename_quoting_options)]);
+ quoting_style_args[get_quoting_style (filename_quoting_options)]);
}
if (LOOP_DETECT)
@@ -1374,7 +1519,32 @@ main (int argc, char **argv)
hash_free (active_dir_set);
}
- exit (exit_status);
+ return exit_status;
+}
+
+/* Set the line length to the value given by SPEC. Return true if
+ successful. 0 means no limit on line length. */
+
+static bool
+set_line_length (char const *spec)
+{
+ uintmax_t val;
+
+ /* Treat too-large values as if they were SIZE_MAX, which is
+ effectively infinity. */
+ switch (xstrtoumax (spec, NULL, 0, &val, ""))
+ {
+ case LONGINT_OK:
+ line_length = MIN (val, SIZE_MAX);
+ return true;
+
+ case LONGINT_OVERFLOW:
+ line_length = SIZE_MAX;
+ return true;
+
+ default:
+ return false;
+ }
}
/* Set all the option flags according to the switches specified.
@@ -1383,11 +1553,10 @@ main (int argc, char **argv)
static int
decode_switches (int argc, char **argv)
{
- int c;
char *time_style_option = NULL;
- /* Record whether there is an option specifying sort type. */
bool sort_type_specified = false;
+ bool kibibytes_specified = false;
qmark_funny_chars = false;
@@ -1396,30 +1565,31 @@ decode_switches (int argc, char **argv)
switch (ls_mode)
{
case LS_MULTI_COL:
- /* This is for the `dir' program. */
+ /* This is for the 'dir' program. */
format = many_per_line;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LONG_FORMAT:
- /* This is for the `vdir' program. */
+ /* This is for the 'vdir' program. */
format = long_format;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LS:
- /* This is for the `ls' program. */
+ /* This is for the 'ls' program. */
if (isatty (STDOUT_FILENO))
- {
- format = many_per_line;
- /* See description of qmark_funny_chars, above. */
- qmark_funny_chars = true;
- }
+ {
+ format = many_per_line;
+ set_quoting_style (NULL, shell_escape_quoting_style);
+ /* See description of qmark_funny_chars, above. */
+ qmark_funny_chars = true;
+ }
else
- {
- format = one_per_line;
- qmark_funny_chars = false;
- }
+ {
+ format = one_per_line;
+ qmark_funny_chars = false;
+ }
break;
default:
@@ -1439,48 +1609,17 @@ decode_switches (int argc, char **argv)
ignore_mode = IGNORE_DEFAULT;
ignore_patterns = NULL;
hide_patterns = NULL;
+ print_scontext = false;
- /* FIXME: put this in a function. */
- {
- char const *q_style = getenv ("QUOTING_STYLE");
- if (q_style)
- {
- int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
- if (0 <= i)
- set_quoting_style (NULL, quoting_style_vals[i]);
- else
- error (0, 0,
- _("ignoring invalid value of environment variable QUOTING_STYLE: %s"),
- quotearg (q_style));
- }
- }
-
- {
- char const *ls_block_size = getenv ("LS_BLOCK_SIZE");
- human_output_opts = human_options (ls_block_size, false,
- &output_block_size);
- if (ls_block_size || getenv ("BLOCK_SIZE"))
- file_output_block_size = output_block_size;
- }
+ getenv_quoting_style ();
line_length = 80;
{
char const *p = getenv ("COLUMNS");
- if (p && *p)
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
- && 0 < tmp_ulong && tmp_ulong <= SIZE_MAX)
- {
- line_length = tmp_ulong;
- }
- else
- {
- error (0, 0,
- _("ignoring invalid width in environment variable COLUMNS: %s"),
- quotearg (p));
- }
- }
+ if (p && *p && ! set_line_length (p))
+ error (0, 0,
+ _("ignoring invalid width in environment variable COLUMNS: %s"),
+ quote (p));
}
#ifdef TIOCGWINSZ
@@ -1488,7 +1627,7 @@ decode_switches (int argc, char **argv)
struct winsize ws;
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1
- && 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
+ && 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
line_length = ws.ws_col;
}
#endif
@@ -1498,331 +1637,353 @@ decode_switches (int argc, char **argv)
tabsize = 8;
if (p)
{
- unsigned long int tmp_ulong;
- if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
- && tmp_ulong <= SIZE_MAX)
- {
- tabsize = tmp_ulong;
- }
- else
- {
- error (0, 0,
- _("ignoring invalid tab size in environment variable TABSIZE: %s"),
- quotearg (p));
- }
+ unsigned long int tmp_ulong;
+ if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
+ && tmp_ulong <= SIZE_MAX)
+ {
+ tabsize = tmp_ulong;
+ }
+ else
+ {
+ error (0, 0,
+ _("ignoring invalid tab size in environment variable TABSIZE: %s"),
+ quote (p));
+ }
}
}
- while ((c = getopt_long (argc, argv,
- "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1",
- long_options, NULL)) != -1)
+ while (true)
{
+ int oi = -1;
+ int c = getopt_long (argc, argv,
+ "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1",
+ long_options, &oi);
+ if (c == -1)
+ break;
+
switch (c)
- {
- case 'a':
- ignore_mode = IGNORE_MINIMAL;
- break;
-
- case 'b':
- set_quoting_style (NULL, escape_quoting_style);
- break;
-
- case 'c':
- time_type = time_ctime;
- break;
-
- case 'd':
- immediate_dirs = true;
- break;
-
- case 'f':
- /* Same as enabling -a -U and disabling -l -s. */
- ignore_mode = IGNORE_MINIMAL;
- sort_type = sort_none;
- sort_type_specified = true;
- /* disable -l */
- if (format == long_format)
- format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
- print_block_size = false; /* disable -s */
- print_with_color = false; /* disable --color */
- break;
-
- case FILE_TYPE_INDICATOR_OPTION: /* --file-type */
- indicator_style = file_type;
- break;
-
- case 'g':
- format = long_format;
- print_owner = false;
- break;
-
- case 'h':
- human_output_opts = human_autoscale | human_SI | human_base_1024;
- file_output_block_size = output_block_size = 1;
- break;
-
- case 'i':
- print_inode = true;
- break;
-
- case KILOBYTES_LONG_OPTION:
- error (0, 0,
- _("the --kilobytes option is deprecated; use -k instead"));
- /* fall through */
- case 'k':
- human_output_opts = 0;
- file_output_block_size = output_block_size = 1024;
- break;
-
- case 'l':
- format = long_format;
- break;
-
- case 'm':
- format = with_commas;
- break;
-
- case 'n':
- numeric_ids = true;
- format = long_format;
- break;
-
- case 'o': /* Just like -l, but don't display group info. */
- format = long_format;
- print_group = false;
- break;
-
- case 'p':
- indicator_style = slash;
- break;
-
- case 'q':
- qmark_funny_chars = true;
- break;
-
- case 'r':
- sort_reverse = true;
- break;
-
- case 's':
- print_block_size = true;
- break;
-
- case 't':
- sort_type = sort_time;
- sort_type_specified = true;
- break;
-
- case 'u':
- time_type = time_atime;
- break;
-
- case 'v':
- sort_type = sort_version;
- sort_type_specified = true;
- break;
-
- case 'w':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= SIZE_MAX))
- error (LS_FAILURE, 0, _("invalid line width: %s"),
- quotearg (optarg));
- line_length = tmp_ulong;
- break;
- }
-
- case 'x':
- format = horizontal;
- break;
-
- case 'A':
- if (ignore_mode == IGNORE_DEFAULT)
- ignore_mode = IGNORE_DOT_AND_DOTDOT;
- break;
-
- case 'B':
- add_ignore_pattern ("*~");
- add_ignore_pattern (".*~");
- break;
-
- case 'C':
- format = many_per_line;
- break;
-
- case 'D':
- dired = true;
- break;
-
- case 'F':
- indicator_style = classify;
- break;
-
- case 'G': /* inhibit display of group info */
- print_group = false;
- break;
-
- case 'H':
- dereference = DEREF_COMMAND_LINE_ARGUMENTS;
- break;
-
- case DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION:
- dereference = DEREF_COMMAND_LINE_SYMLINK_TO_DIR;
- break;
-
- case 'I':
- add_ignore_pattern (optarg);
- break;
-
- case 'L':
- dereference = DEREF_ALWAYS;
- break;
-
- case 'N':
- set_quoting_style (NULL, literal_quoting_style);
- break;
-
- case 'Q':
- set_quoting_style (NULL, c_quoting_style);
- break;
-
- case 'R':
- recursive = true;
- break;
-
- case 'S':
- sort_type = sort_size;
- sort_type_specified = true;
- break;
-
- case 'T':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || SIZE_MAX < tmp_ulong)
- error (LS_FAILURE, 0, _("invalid tab size: %s"),
- quotearg (optarg));
- tabsize = tmp_ulong;
- break;
- }
-
- case 'U':
- sort_type = sort_none;
- sort_type_specified = true;
- break;
-
- case 'X':
- sort_type = sort_extension;
- sort_type_specified = true;
- break;
-
- case '1':
- /* -1 has no effect after -l. */
- if (format != long_format)
- format = one_per_line;
- break;
+ {
+ case 'a':
+ ignore_mode = IGNORE_MINIMAL;
+ break;
+
+ case 'b':
+ set_quoting_style (NULL, escape_quoting_style);
+ break;
+
+ case 'c':
+ time_type = time_ctime;
+ break;
+
+ case 'd':
+ immediate_dirs = true;
+ break;
+
+ case 'f':
+ /* Same as enabling -a -U and disabling -l -s. */
+ ignore_mode = IGNORE_MINIMAL;
+ sort_type = sort_none;
+ sort_type_specified = true;
+ /* disable -l */
+ if (format == long_format)
+ format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
+ print_block_size = false; /* disable -s */
+ print_with_color = false; /* disable --color */
+ break;
+
+ case FILE_TYPE_INDICATOR_OPTION: /* --file-type */
+ indicator_style = file_type;
+ break;
+
+ case 'g':
+ format = long_format;
+ print_owner = false;
+ break;
+
+ case 'h':
+ file_human_output_opts = human_output_opts =
+ human_autoscale | human_SI | human_base_1024;
+ file_output_block_size = output_block_size = 1;
+ break;
+
+ case 'i':
+ print_inode = true;
+ break;
+
+ case 'k':
+ kibibytes_specified = true;
+ break;
+
+ case 'l':
+ format = long_format;
+ break;
+
+ case 'm':
+ format = with_commas;
+ break;
+
+ case 'n':
+ numeric_ids = true;
+ format = long_format;
+ break;
+
+ case 'o': /* Just like -l, but don't display group info. */
+ format = long_format;
+ print_group = false;
+ break;
+
+ case 'p':
+ indicator_style = slash;
+ break;
+
+ case 'q':
+ qmark_funny_chars = true;
+ break;
+
+ case 'r':
+ sort_reverse = true;
+ break;
+
+ case 's':
+ print_block_size = true;
+ break;
+
+ case 't':
+ sort_type = sort_time;
+ sort_type_specified = true;
+ break;
+
+ case 'u':
+ time_type = time_atime;
+ break;
+
+ case 'v':
+ sort_type = sort_version;
+ sort_type_specified = true;
+ break;
+
+ case 'w':
+ if (! set_line_length (optarg))
+ error (LS_FAILURE, 0, "%s: %s", _("invalid line width"),
+ quote (optarg));
+ break;
+
+ case 'x':
+ format = horizontal;
+ break;
+
+ case 'A':
+ if (ignore_mode == IGNORE_DEFAULT)
+ ignore_mode = IGNORE_DOT_AND_DOTDOT;
+ break;
+
+ case 'B':
+ add_ignore_pattern ("*~");
+ add_ignore_pattern (".*~");
+ break;
+
+ case 'C':
+ format = many_per_line;
+ break;
+
+ case 'D':
+ dired = true;
+ break;
+
+ case 'F':
+ indicator_style = classify;
+ break;
+
+ case 'G': /* inhibit display of group info */
+ print_group = false;
+ break;
+
+ case 'H':
+ dereference = DEREF_COMMAND_LINE_ARGUMENTS;
+ break;
+
+ case DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION:
+ dereference = DEREF_COMMAND_LINE_SYMLINK_TO_DIR;
+ break;
+
+ case 'I':
+ add_ignore_pattern (optarg);
+ break;
+
+ case 'L':
+ dereference = DEREF_ALWAYS;
+ break;
+
+ case 'N':
+ set_quoting_style (NULL, literal_quoting_style);
+ break;
+
+ case 'Q':
+ set_quoting_style (NULL, c_quoting_style);
+ break;
+
+ case 'R':
+ recursive = true;
+ break;
+
+ case 'S':
+ sort_type = sort_size;
+ sort_type_specified = true;
+ break;
+
+ case 'T':
+ tabsize = xnumtoumax (optarg, 0, 0, SIZE_MAX, "",
+ _("invalid tab size"), LS_FAILURE);
+ break;
+
+ case 'U':
+ sort_type = sort_none;
+ sort_type_specified = true;
+ break;
+
+ case 'X':
+ sort_type = sort_extension;
+ sort_type_specified = true;
+ break;
+
+ case '1':
+ /* -1 has no effect after -l. */
+ if (format != long_format)
+ format = one_per_line;
+ break;
case AUTHOR_OPTION:
print_author = true;
break;
- case HIDE_OPTION:
- {
- struct ignore_pattern *hide = xmalloc (sizeof *hide);
- hide->pattern = optarg;
- hide->next = hide_patterns;
- hide_patterns = hide;
- }
- break;
-
- case SORT_OPTION:
- sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
- sort_type_specified = true;
- break;
-
- case GROUP_DIRECTORIES_FIRST_OPTION:
- directories_first = true;
- break;
-
- case TIME_OPTION:
- time_type = XARGMATCH ("--time", optarg, time_args, time_types);
- break;
-
- case FORMAT_OPTION:
- format = XARGMATCH ("--format", optarg, format_args, format_types);
- break;
-
- case FULL_TIME_OPTION:
- format = long_format;
- time_style_option = "full-iso";
- break;
-
- case COLOR_OPTION:
- {
- int i;
- if (optarg)
- i = XARGMATCH ("--color", optarg, color_args, color_types);
- else
- /* Using --color with no argument is equivalent to using
- --color=always. */
- i = color_always;
-
- print_with_color = (i == color_always
- || (i == color_if_tty
- && isatty (STDOUT_FILENO)));
-
- if (print_with_color)
- {
- /* Don't use TAB characters in output. Some terminal
- emulators can't handle the combination of tabs and
- color codes on the same line. */
- tabsize = 0;
- }
- break;
- }
-
- case INDICATOR_STYLE_OPTION:
- indicator_style = XARGMATCH ("--indicator-style", optarg,
- indicator_style_args,
- indicator_style_types);
- break;
-
- case QUOTING_STYLE_OPTION:
- set_quoting_style (NULL,
- XARGMATCH ("--quoting-style", optarg,
- quoting_style_args,
- quoting_style_vals));
- break;
-
- case TIME_STYLE_OPTION:
- time_style_option = optarg;
- break;
-
- case SHOW_CONTROL_CHARS_OPTION:
- qmark_funny_chars = false;
- break;
-
- case BLOCK_SIZE_OPTION:
- human_output_opts = human_options (optarg, true, &output_block_size);
- file_output_block_size = output_block_size;
- break;
-
- case SI_OPTION:
- human_output_opts = human_autoscale | human_SI;
- file_output_block_size = output_block_size = 1;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (LS_FAILURE);
- }
+ case HIDE_OPTION:
+ {
+ struct ignore_pattern *hide = xmalloc (sizeof *hide);
+ hide->pattern = optarg;
+ hide->next = hide_patterns;
+ hide_patterns = hide;
+ }
+ break;
+
+ case SORT_OPTION:
+ sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
+ sort_type_specified = true;
+ break;
+
+ case GROUP_DIRECTORIES_FIRST_OPTION:
+ directories_first = true;
+ break;
+
+ case TIME_OPTION:
+ time_type = XARGMATCH ("--time", optarg, time_args, time_types);
+ break;
+
+ case FORMAT_OPTION:
+ format = XARGMATCH ("--format", optarg, format_args, format_types);
+ break;
+
+ case FULL_TIME_OPTION:
+ format = long_format;
+ time_style_option = bad_cast ("full-iso");
+ break;
+
+ case COLOR_OPTION:
+ {
+ int i;
+ if (optarg)
+ i = XARGMATCH ("--color", optarg, color_args, color_types);
+ else
+ /* Using --color with no argument is equivalent to using
+ --color=always. */
+ i = color_always;
+
+ print_with_color = (i == color_always
+ || (i == color_if_tty
+ && isatty (STDOUT_FILENO)));
+
+ if (print_with_color)
+ {
+ /* Don't use TAB characters in output. Some terminal
+ emulators can't handle the combination of tabs and
+ color codes on the same line. */
+ tabsize = 0;
+ }
+ break;
+ }
+
+ case INDICATOR_STYLE_OPTION:
+ indicator_style = XARGMATCH ("--indicator-style", optarg,
+ indicator_style_args,
+ indicator_style_types);
+ break;
+
+ case QUOTING_STYLE_OPTION:
+ set_quoting_style (NULL,
+ XARGMATCH ("--quoting-style", optarg,
+ quoting_style_args,
+ quoting_style_vals));
+ break;
+
+ case TIME_STYLE_OPTION:
+ time_style_option = optarg;
+ break;
+
+ case SHOW_CONTROL_CHARS_OPTION:
+ qmark_funny_chars = false;
+ break;
+
+ case BLOCK_SIZE_OPTION:
+ {
+ enum strtol_error e = human_options (optarg, &human_output_opts,
+ &output_block_size);
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, 0, long_options, optarg);
+ file_human_output_opts = human_output_opts;
+ file_output_block_size = output_block_size;
+ }
+ break;
+
+ case SI_OPTION:
+ file_human_output_opts = human_output_opts =
+ human_autoscale | human_SI;
+ file_output_block_size = output_block_size = 1;
+ break;
+
+ case 'Z':
+ print_scontext = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (LS_FAILURE);
+ }
}
- max_idx = MAX (1, line_length / MIN_COLUMN_WIDTH);
+ if (! output_block_size)
+ {
+ char const *ls_block_size = getenv ("LS_BLOCK_SIZE");
+ human_options (ls_block_size,
+ &human_output_opts, &output_block_size);
+ if (ls_block_size || getenv ("BLOCK_SIZE"))
+ {
+ file_human_output_opts = human_output_opts;
+ file_output_block_size = output_block_size;
+ }
+ if (kibibytes_specified)
+ {
+ human_output_opts = 0;
+ output_block_size = 1024;
+ }
+ }
+
+ /* Determine the max possible number of display columns. */
+ max_idx = line_length / MIN_COLUMN_WIDTH;
+ /* Account for first display column not having a separator,
+ or line_lengths shorter than MIN_COLUMN_WIDTH. */
+ max_idx += line_length % MIN_COLUMN_WIDTH != 0;
filename_quoting_options = clone_quoting_options (NULL);
if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
@@ -1830,8 +1991,8 @@ decode_switches (int argc, char **argv)
if (file_type <= indicator_style)
{
char const *p;
- for (p = "*=>@|" + indicator_style - file_type; *p; p++)
- set_char_quoting (filename_quoting_options, *p, 1);
+ for (p = &"*=>@|"[indicator_style - file_type]; *p; p++)
+ set_char_quoting (filename_quoting_options, *p, 1);
}
dirname_quoting_options = clone_quoting_options (NULL);
@@ -1846,7 +2007,7 @@ decode_switches (int argc, char **argv)
/* If -c or -u is specified and not -l (or any other option that implies -l),
and no sort-type was specified, then sort by the ctime (-c) or atime (-u).
The behavior of ls when using either -c or -u but with neither -l nor -t
- appears to be unspecified by POSIX. So, with GNU ls, `-u' alone means
+ appears to be unspecified by POSIX. So, with GNU ls, '-u' alone means
sort by atime (this is the one that's not specified by the POSIX spec),
-lu means show atime and sort by name, -lut means show atime and sort
by atime. */
@@ -1863,68 +2024,89 @@ decode_switches (int argc, char **argv)
static char const posix_prefix[] = "posix-";
if (! style)
- if (! (style = getenv ("TIME_STYLE")))
- style = "locale";
+ if (! (style = getenv ("TIME_STYLE")))
+ style = bad_cast ("locale");
- while (strncmp (style, posix_prefix, sizeof posix_prefix - 1) == 0)
- {
- if (! hard_locale (LC_TIME))
- return optind;
- style += sizeof posix_prefix - 1;
- }
+ while (STREQ_LEN (style, posix_prefix, sizeof posix_prefix - 1))
+ {
+ if (! hard_locale (LC_TIME))
+ return optind;
+ style += sizeof posix_prefix - 1;
+ }
if (*style == '+')
- {
- char *p0 = style + 1;
- char *p1 = strchr (p0, '\n');
- if (! p1)
- p1 = p0;
- else
- {
- if (strchr (p1 + 1, '\n'))
- error (LS_FAILURE, 0, _("invalid time style format %s"),
- quote (p0));
- *p1++ = '\0';
- }
- long_time_format[0] = p0;
- long_time_format[1] = p1;
- }
+ {
+ char *p0 = style + 1;
+ char *p1 = strchr (p0, '\n');
+ if (! p1)
+ p1 = p0;
+ else
+ {
+ if (strchr (p1 + 1, '\n'))
+ error (LS_FAILURE, 0, _("invalid time style format %s"),
+ quote (p0));
+ *p1++ = '\0';
+ }
+ long_time_format[0] = p0;
+ long_time_format[1] = p1;
+ }
else
- switch (XARGMATCH ("time style", style,
- time_style_args,
- time_style_types))
- {
- case full_iso_time_style:
- long_time_format[0] = long_time_format[1] =
- "%Y-%m-%d %H:%M:%S.%N %z";
- break;
-
- case long_iso_time_style:
- case_long_iso_time_style:
- long_time_format[0] = long_time_format[1] = "%Y-%m-%d %H:%M";
- break;
-
- case iso_time_style:
- long_time_format[0] = "%Y-%m-%d ";
- long_time_format[1] = "%m-%d %H:%M";
- break;
-
- case locale_time_style:
- if (hard_locale (LC_TIME))
- {
- /* Ensure that the locale has translations for both
- formats. If not, fall back on long-iso format. */
- int i;
- for (i = 0; i < 2; i++)
- {
- char const *locale_format =
- dcgettext (NULL, long_time_format[i], LC_TIME);
- if (locale_format == long_time_format[i])
- goto case_long_iso_time_style;
- long_time_format[i] = locale_format;
- }
- }
- }
+ {
+ ptrdiff_t res = argmatch (style, time_style_args,
+ (char const *) time_style_types,
+ sizeof (*time_style_types));
+ if (res < 0)
+ {
+ /* This whole block used to be a simple use of XARGMATCH.
+ but that didn't print the "posix-"-prefixed variants or
+ the "+"-prefixed format string option upon failure. */
+ argmatch_invalid ("time style", style, res);
+
+ /* The following is a manual expansion of argmatch_valid,
+ but with the added "+ ..." description and the [posix-]
+ prefixes prepended. Note that this simplification works
+ only because all four existing time_style_types values
+ are distinct. */
+ fputs (_("Valid arguments are:\n"), stderr);
+ char const *const *p = time_style_args;
+ while (*p)
+ fprintf (stderr, " - [posix-]%s\n", *p++);
+ fputs (_(" - +FORMAT (e.g., +%H:%M) for a 'date'-style"
+ " format\n"), stderr);
+ usage (LS_FAILURE);
+ }
+ switch (res)
+ {
+ case full_iso_time_style:
+ long_time_format[0] = long_time_format[1] =
+ "%Y-%m-%d %H:%M:%S.%N %z";
+ break;
+
+ case long_iso_time_style:
+ long_time_format[0] = long_time_format[1] = "%Y-%m-%d %H:%M";
+ break;
+
+ case iso_time_style:
+ long_time_format[0] = "%Y-%m-%d ";
+ long_time_format[1] = "%m-%d %H:%M";
+ break;
+
+ case locale_time_style:
+ if (hard_locale (LC_TIME))
+ {
+ int i;
+ for (i = 0; i < 2; i++)
+ long_time_format[i] =
+ dcgettext (NULL, long_time_format[i], LC_TIME);
+ }
+ }
+ }
+
+ /* Note we leave %5b etc. alone so user widths/flags are honored. */
+ if (strstr (long_time_format[0], "%b")
+ || strstr (long_time_format[1], "%b"))
+ if (!abmon_init ())
+ error (0, 0, _("error initializing month strings"));
}
return optind;
@@ -1945,7 +2127,7 @@ decode_switches (int argc, char **argv)
static bool
get_funky_string (char **dest, const char **src, bool equals_end,
- size_t *output_count)
+ size_t *output_count)
{
char num; /* For numerical codes */
size_t count; /* Something to count with */
@@ -1965,170 +2147,170 @@ get_funky_string (char **dest, const char **src, bool equals_end,
while (state < ST_END)
{
switch (state)
- {
- case ST_GND: /* Ground state (no escapes) */
- switch (*p)
- {
- case ':':
- case '\0':
- state = ST_END; /* End of string */
- break;
- case '\\':
- state = ST_BACKSLASH; /* Backslash scape sequence */
- ++p;
- break;
- case '^':
- state = ST_CARET; /* Caret escape */
- ++p;
- break;
- case '=':
- if (equals_end)
- {
- state = ST_END; /* End */
- break;
- }
- /* else fall through */
- default:
- *(q++) = *(p++);
- ++count;
- break;
- }
- break;
-
- case ST_BACKSLASH: /* Backslash escaped character */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state = ST_OCTAL; /* Octal sequence */
- num = *p - '0';
- break;
- case 'x':
- case 'X':
- state = ST_HEX; /* Hex sequence */
- num = 0;
- break;
- case 'a': /* Bell */
- num = '\a';
- break;
- case 'b': /* Backspace */
- num = '\b';
- break;
- case 'e': /* Escape */
- num = 27;
- break;
- case 'f': /* Form feed */
- num = '\f';
- break;
- case 'n': /* Newline */
- num = '\n';
- break;
- case 'r': /* Carriage return */
- num = '\r';
- break;
- case 't': /* Tab */
- num = '\t';
- break;
- case 'v': /* Vtab */
- num = '\v';
- break;
- case '?': /* Delete */
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash escape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = '\a';
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
num = 127;
- break;
- case '_': /* Space */
- num = ' ';
- break;
- case '\0': /* End of string */
- state = ST_ERROR; /* Error! */
- break;
- default: /* Escaped character like \ ^ : = */
- num = *p;
- break;
- }
- if (state == ST_BACKSLASH)
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- ++p;
- break;
-
- case ST_OCTAL: /* Octal sequence */
- if (*p < '0' || *p > '7')
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- else
- num = (num << 3) + (*(p++) - '0');
- break;
-
- case ST_HEX: /* Hex sequence */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- num = (num << 4) + (*(p++) - '0');
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- num = (num << 4) + (*(p++) - 'a') + 10;
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- num = (num << 4) + (*(p++) - 'A') + 10;
- break;
- default:
- *(q++) = num;
- ++count;
- state = ST_GND;
- break;
- }
- break;
-
- case ST_CARET: /* Caret escape */
- state = ST_GND; /* Should be the next state... */
- if (*p >= '@' && *p <= '~')
- {
- *(q++) = *(p++) & 037;
- ++count;
- }
- else if (*p == '?')
- {
- *(q++) = 127;
- ++count;
- }
- else
- state = ST_ERROR;
- break;
-
- default:
- abort ();
- }
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = (num << 4) + (*(p++) - '0');
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ num = (num << 4) + (*(p++) - 'a') + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ num = (num << 4) + (*(p++) - 'A') + 10;
+ break;
+ default:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if (*p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ abort ();
+ }
}
*dest = q;
@@ -2138,18 +2320,60 @@ get_funky_string (char **dest, const char **src, bool equals_end,
return state != ST_ERROR;
}
+enum parse_state
+ {
+ PS_START = 1,
+ PS_2,
+ PS_3,
+ PS_4,
+ PS_DONE,
+ PS_FAIL
+ };
+
+
+/* Check if the content of TERM is a valid name in dircolors. */
+
+static bool
+known_term_type (void)
+{
+ char const *term = getenv ("TERM");
+ if (! term || ! *term)
+ return false;
+
+ char const *line = G_line;
+ while (line - G_line < sizeof (G_line))
+ {
+ if (STRNCMP_LIT (line, "TERM ") == 0)
+ {
+ if (fnmatch (line + 5, term, 0) == 0)
+ return true;
+ }
+ line += strlen (line) + 1;
+ }
+
+ return false;
+}
+
static void
parse_ls_color (void)
{
const char *p; /* Pointer to character being parsed */
char *buf; /* color_buf buffer pointer */
- int state; /* State of parser */
int ind_no; /* Indicator number */
char label[3]; /* Indicator label */
struct color_ext_type *ext; /* Extension we are working on */
if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
- return;
+ {
+ /* LS_COLORS takes precedence, but if that's not set then
+ honor the COLORTERM and TERM env variables so that
+ we only go with the internal ANSI color codes if the
+ former is non empty or the latter is set to a known value. */
+ char const *colorterm = getenv ("COLORTERM");
+ if (! (colorterm && *colorterm) && ! known_term_type ())
+ print_with_color = false;
+ return;
+ }
ext = NULL;
strcpy (label, "??");
@@ -2160,111 +2384,137 @@ parse_ls_color (void)
advance. */
buf = color_buf = xstrdup (p);
- state = 1;
- while (state > 0)
+ enum parse_state state = PS_START;
+ while (true)
{
switch (state)
- {
- case 1: /* First label character */
- switch (*p)
- {
- case ':':
- ++p;
- break;
-
- case '*':
- /* Allocate new extension block and add to head of
- linked list (this way a later definition will
- override an earlier one, which can be useful for
- having terminal-specific defs override global). */
-
- ext = xmalloc (sizeof *ext);
- ext->next = color_ext_list;
- color_ext_list = ext;
-
- ++p;
- ext->ext.string = buf;
-
- state = (get_funky_string (&buf, &p, true, &ext->ext.len)
- ? 4 : -1);
- break;
-
- case '\0':
- state = 0; /* Done! */
- break;
-
- default: /* Assume it is file type label */
- label[0] = *(p++);
- state = 2;
- break;
- }
- break;
-
- case 2: /* Second label character */
- if (*p)
- {
- label[1] = *(p++);
- state = 3;
- }
- else
- state = -1; /* Error */
- break;
-
- case 3: /* Equal sign after indicator label */
- state = -1; /* Assume failure... */
- if (*(p++) == '=')/* It *should* be... */
- {
- for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
- {
- if (STREQ (label, indicator_name[ind_no]))
- {
- color_indicator[ind_no].string = buf;
- state = (get_funky_string (&buf, &p, false,
- &color_indicator[ind_no].len)
- ? 1 : -1);
- break;
- }
- }
- if (state == -1)
- error (0, 0, _("unrecognized prefix: %s"), quotearg (label));
- }
- break;
-
- case 4: /* Equal sign after *.ext */
- if (*(p++) == '=')
- {
- ext->seq.string = buf;
- state = (get_funky_string (&buf, &p, false, &ext->seq.len)
- ? 1 : -1);
- }
- else
- state = -1;
- break;
- }
+ {
+ case PS_START: /* First label character */
+ switch (*p)
+ {
+ case ':':
+ ++p;
+ break;
+
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+ ext = xmalloc (sizeof *ext);
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
+ ? PS_4 : PS_FAIL);
+ break;
+
+ case '\0':
+ state = PS_DONE; /* Done! */
+ goto done;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = PS_2;
+ break;
+ }
+ break;
+
+ case PS_2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = PS_3;
+ }
+ else
+ state = PS_FAIL; /* Error */
+ break;
+
+ case PS_3: /* Equal sign after indicator label */
+ state = PS_FAIL; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
+ {
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+ state = (get_funky_string (&buf, &p, false,
+ &color_indicator[ind_no].len)
+ ? PS_START : PS_FAIL);
+ break;
+ }
+ }
+ if (state == PS_FAIL)
+ error (0, 0, _("unrecognized prefix: %s"), quote (label));
+ }
+ break;
+
+ case PS_4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
+ ? PS_START : PS_FAIL);
+ }
+ else
+ state = PS_FAIL;
+ break;
+
+ case PS_FAIL:
+ goto done;
+
+ default:
+ abort ();
+ }
}
+ done:
- if (state < 0)
+ if (state == PS_FAIL)
{
struct color_ext_type *e;
struct color_ext_type *e2;
error (0, 0,
- _("unparsable value for LS_COLORS environment variable"));
+ _("unparsable value for LS_COLORS environment variable"));
free (color_buf);
for (e = color_ext_list; e != NULL; /* empty */)
- {
- e2 = e;
- e = e->next;
- free (e2);
- }
+ {
+ e2 = e;
+ e = e->next;
+ free (e2);
+ }
print_with_color = false;
}
if (color_indicator[C_LINK].len == 6
- && !strncmp (color_indicator[C_LINK].string, "target", 6))
+ && !STRNCMP_LIT (color_indicator[C_LINK].string, "target"))
color_symlink_as_referent = true;
}
+/* Set the quoting style default if the environment variable
+ QUOTING_STYLE is set. */
+
+static void
+getenv_quoting_style (void)
+{
+ char const *q_style = getenv ("QUOTING_STYLE");
+ if (q_style)
+ {
+ int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
+ if (0 <= i)
+ set_quoting_style (NULL, quoting_style_vals[i]);
+ else
+ error (0, 0,
+ _("ignoring invalid value of environment variable QUOTING_STYLE: %s"),
+ quote (q_style));
+ }
+}
+
/* Set the exit status to report a failure. If SERIOUS, it is a
serious failure; otherwise, it is merely a minor problem. */
@@ -2284,7 +2534,7 @@ set_exit_status (bool serious)
static void
file_failure (bool serious, char const *message, char const *file)
{
- error (0, errno, message, quotearg_colon (file));
+ error (0, errno, message, quoteaf (file));
set_exit_status (serious);
}
@@ -2337,29 +2587,43 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
/* If dirfd failed, endure the overhead of using stat. */
if ((0 <= fd
- ? fstat (fd, &dir_stat)
- : stat (name, &dir_stat)) < 0)
- {
- file_failure (command_line_arg,
- _("cannot determine device and inode of %s"), name);
- closedir (dirp);
- return;
- }
+ ? fstat (fd, &dir_stat)
+ : stat (name, &dir_stat)) < 0)
+ {
+ file_failure (command_line_arg,
+ _("cannot determine device and inode of %s"), name);
+ closedir (dirp);
+ return;
+ }
/* If we've already visited this dev/inode pair, warn that
- we've found a loop, and do not process this directory. */
+ we've found a loop, and do not process this directory. */
if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
- {
- error (0, 0, _("%s: not listing already-listed directory"),
- quotearg_colon (name));
- closedir (dirp);
- return;
- }
-
- DEV_INO_PUSH (dir_stat.st_dev, dir_stat.st_ino);
+ {
+ error (0, 0, _("%s: not listing already-listed directory"),
+ quotef (name));
+ closedir (dirp);
+ set_exit_status (true);
+ return;
+ }
+
+ dev_ino_push (dir_stat.st_dev, dir_stat.st_ino);
}
- /* Read the directory entries, and insert the subfiles into the `cwd_file'
+ if (recursive || print_dir_name)
+ {
+ if (!first)
+ DIRED_PUTCHAR ('\n');
+ first = false;
+ DIRED_INDENT ();
+ PUSH_CURRENT_DIRED_POS (&subdired_obstack);
+ dired_pos += quote_name (stdout, realname ? realname : name,
+ dirname_quoting_options, NULL);
+ PUSH_CURRENT_DIRED_POS (&subdired_obstack);
+ DIRED_FPUTS_LITERAL (":\n", stdout);
+ }
+
+ /* Read the directory entries, and insert the subfiles into the 'cwd_file'
table. */
clear_files ();
@@ -2367,42 +2631,63 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
while (1)
{
/* Set errno to zero so we can distinguish between a readdir failure
- and when readdir simply finds that there are no more entries. */
+ and when readdir simply finds that there are no more entries. */
errno = 0;
next = readdir (dirp);
if (next)
- {
- if (! file_ignored (next->d_name))
- {
- enum filetype type = unknown;
+ {
+ if (! file_ignored (next->d_name))
+ {
+ enum filetype type = unknown;
#if HAVE_STRUCT_DIRENT_D_TYPE
- switch (next->d_type)
- {
- case DT_BLK: type = blockdev; break;
- case DT_CHR: type = chardev; break;
- case DT_DIR: type = directory; break;
- case DT_FIFO: type = fifo; break;
- case DT_LNK: type = symbolic_link; break;
- case DT_REG: type = normal; break;
- case DT_SOCK: type = sock; break;
+ switch (next->d_type)
+ {
+ case DT_BLK: type = blockdev; break;
+ case DT_CHR: type = chardev; break;
+ case DT_DIR: type = directory; break;
+ case DT_FIFO: type = fifo; break;
+ case DT_LNK: type = symbolic_link; break;
+ case DT_REG: type = normal; break;
+ case DT_SOCK: type = sock; break;
# ifdef DT_WHT
- case DT_WHT: type = whiteout; break;
+ case DT_WHT: type = whiteout; break;
# endif
- }
+ }
#endif
- total_blocks += gobble_file (next->d_name, type, D_INO (next),
- false, name);
- }
- }
+ total_blocks += gobble_file (next->d_name, type,
+ RELIABLE_D_INO (next),
+ false, name);
+
+ /* In this narrow case, print out each name right away, so
+ ls uses constant memory while processing the entries of
+ this directory. Useful when there are many (millions)
+ of entries in a directory. */
+ if (format == one_per_line && sort_type == sort_none
+ && !print_block_size && !recursive)
+ {
+ /* We must call sort_files in spite of
+ "sort_type == sort_none" for its initialization
+ of the sorted_file vector. */
+ sort_files ();
+ print_current_files ();
+ clear_files ();
+ }
+ }
+ }
else if (errno != 0)
- {
- file_failure (command_line_arg, _("reading directory %s"), name);
- if (errno != EOVERFLOW)
- break;
- }
+ {
+ file_failure (command_line_arg, _("reading directory %s"), name);
+ if (errno != EOVERFLOW)
+ break;
+ }
else
- break;
+ break;
+
+ /* When processing a very large directory, and since we've inhibited
+ interrupts, this loop would take so long that ls would be annoyingly
+ uninterruptible. This ensures that it handles signals promptly. */
+ process_signals ();
}
if (closedir (dirp) != 0)
@@ -2418,20 +2703,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
contents listed rather than being mentioned here as files. */
if (recursive)
- extract_dirs_from_files (name, command_line_arg);
-
- if (recursive | print_dir_name)
- {
- if (!first)
- DIRED_PUTCHAR ('\n');
- first = false;
- DIRED_INDENT ();
- PUSH_CURRENT_DIRED_POS (&subdired_obstack);
- dired_pos += quote_name (stdout, realname ? realname : name,
- dirname_quoting_options, NULL);
- PUSH_CURRENT_DIRED_POS (&subdired_obstack);
- DIRED_FPUTS_LITERAL (":\n", stdout);
- }
+ extract_dirs_from_files (name, false);
if (format == long_format || print_block_size)
{
@@ -2443,7 +2715,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR (' ');
p = human_readable (total_blocks, buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size);
+ ST_NBLOCKSIZE, output_block_size);
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR ('\n');
}
@@ -2452,7 +2724,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
print_current_files ();
}
-/* Add `pattern' to the list of patterns for which files that match are
+/* Add 'pattern' to the list of patterns for which files that match are
not listed. */
static void
@@ -2485,11 +2757,11 @@ static bool
file_ignored (char const *name)
{
return ((ignore_mode != IGNORE_MINIMAL
- && name[0] == '.'
- && (ignore_mode == IGNORE_DEFAULT || ! name[1 + (name[1] == '.')]))
- || (ignore_mode == IGNORE_DEFAULT
- && patterns_match (hide_patterns, name))
- || patterns_match (ignore_patterns, name));
+ && name[0] == '.'
+ && (ignore_mode == IGNORE_DEFAULT || ! name[1 + (name[1] == '.')]))
+ || (ignore_mode == IGNORE_DEFAULT
+ && patterns_match (hide_patterns, name))
+ || patterns_match (ignore_patterns, name));
}
/* POSIX requires that a file size be printed without a sign, even
@@ -2502,10 +2774,55 @@ unsigned_file_size (off_t size)
return size + (size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);
}
-/* Enter and remove entries in the table `cwd_file'. */
+#ifdef HAVE_CAP
+/* Return true if NAME has a capability (see linux/capability.h) */
+static bool
+has_capability (char const *name)
+{
+ char *result;
+ bool has_cap;
-/* Empty the table of files. */
+ cap_t cap_d = cap_get_file (name);
+ if (cap_d == NULL)
+ return false;
+
+ result = cap_to_text (cap_d, NULL);
+ cap_free (cap_d);
+ if (!result)
+ return false;
+ /* check if human-readable capability string is empty */
+ has_cap = !!*result;
+
+ cap_free (result);
+ return has_cap;
+}
+#else
+static bool
+has_capability (char const *name _GL_UNUSED)
+{
+ errno = ENOTSUP;
+ return false;
+}
+#endif
+
+/* Enter and remove entries in the table 'cwd_file'. */
+
+static void
+free_ent (struct fileinfo *f)
+{
+ free (f->name);
+ free (f->linkname);
+ if (f->scontext != UNKNOWN_SECURITY_CONTEXT)
+ {
+ if (is_smack_enabled ())
+ free (f->scontext);
+ else
+ freecon (f->scontext);
+ }
+}
+
+/* Empty the table of files. */
static void
clear_files (void)
{
@@ -2514,32 +2831,114 @@ clear_files (void)
for (i = 0; i < cwd_n_used; i++)
{
struct fileinfo *f = sorted_file[i];
- free (f->name);
- free (f->linkname);
+ free_ent (f);
}
cwd_n_used = 0;
-#if USE_ACL
any_has_acl = false;
-#endif
inode_number_width = 0;
block_size_width = 0;
nlink_width = 0;
owner_width = 0;
group_width = 0;
author_width = 0;
+ scontext_width = 0;
major_device_number_width = 0;
minor_device_number_width = 0;
file_size_width = 0;
}
+/* Return true if ERR implies lack-of-support failure by a
+ getxattr-calling function like getfilecon or file_has_acl. */
+static bool
+errno_unsupported (int err)
+{
+ return (err == EINVAL || err == ENOSYS || is_ENOTSUP (err));
+}
+
+/* Cache *getfilecon failure, when it's trivial to do so.
+ Like getfilecon/lgetfilecon, but when F's st_dev says it's doesn't
+ support getting the security context, fail with ENOTSUP immediately. */
+static int
+getfilecon_cache (char const *file, struct fileinfo *f, bool deref)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that [l]getfilecon fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+ int r = 0;
+#ifdef HAVE_SMACK
+ if (is_smack_enabled ())
+ r = smack_new_label_from_path (file, "security.SMACK64", deref,
+ &f->scontext);
+ else
+#endif
+ r = (deref
+ ? getfilecon (file, &f->scontext)
+ : lgetfilecon (file, &f->scontext));
+ if (r < 0 && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return r;
+}
+
+/* Cache file_has_acl failure, when it's trivial to do.
+ Like file_has_acl, but when F's st_dev says it's on a file
+ system lacking ACL support, return 0 with ENOTSUP immediately. */
+static int
+file_has_acl_cache (char const *file, struct fileinfo *f)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that file_has_acl fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return 0;
+ }
+
+ /* Zero errno so that we can distinguish between two 0-returning cases:
+ "has-ACL-support, but only a default ACL" and "no ACL support". */
+ errno = 0;
+ int n = file_has_acl (file, &f->stat);
+ if (n <= 0 && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return n;
+}
+
+/* Cache has_capability failure, when it's trivial to do.
+ Like has_capability, but when F's st_dev says it's on a file
+ system lacking capability support, return 0 with ENOTSUP immediately. */
+static bool
+has_capability_cache (char const *file, struct fileinfo *f)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that has_capability fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return 0;
+ }
+
+ bool b = has_capability (file);
+ if ( !b && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return b;
+}
+
/* Add a file to the current table of files.
Verify that the file exists, and print an error message if it does not.
Return the number of blocks that the file occupies. */
-
static uintmax_t
gobble_file (char const *name, enum filetype type, ino_t inode,
- bool command_line_arg, char const *dirname)
+ bool command_line_arg, char const *dirname)
{
uintmax_t blocks = 0;
struct fileinfo *f;
@@ -2562,212 +2961,273 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
if (command_line_arg
|| format_needs_stat
/* When coloring a directory (we may know the type from
- direct.d_type), we have to stat it in order to indicate
- sticky and/or other-writable attributes. */
- || (type == directory && print_with_color)
+ direct.d_type), we have to stat it in order to indicate
+ sticky and/or other-writable attributes. */
+ || (type == directory && print_with_color
+ && (is_colored (C_OTHER_WRITABLE)
+ || is_colored (C_STICKY)
+ || is_colored (C_STICKY_OTHER_WRITABLE)))
/* When dereferencing symlinks, the inode and type must come from
- stat, but readdir provides the inode and type of lstat. */
+ stat, but readdir provides the inode and type of lstat. */
|| ((print_inode || format_needs_type)
- && (type == symbolic_link || type == unknown)
- && (dereference == DEREF_ALWAYS
- || (command_line_arg && dereference != DEREF_NEVER)))
+ && (type == symbolic_link || type == unknown)
+ && (dereference == DEREF_ALWAYS
+ || color_symlink_as_referent || check_symlink_color))
/* Command line dereferences are already taken care of by the above
- assertion that the inode number is not yet known. */
+ assertion that the inode number is not yet known. */
|| (print_inode && inode == NOT_AN_INODE_NUMBER)
|| (format_needs_type
- && (type == unknown || command_line_arg
- /* --indicator-style=classify (aka -F)
- requires that we stat each regular file
- to see if it's executable. */
- || (type == normal && (indicator_style == classify
- /* This is so that --color ends up
- highlighting files with the executable
- bit set even when options like -F are
- not specified. */
- || (print_with_color
- && is_colored (C_EXEC))
- )))))
+ && (type == unknown || command_line_arg
+ /* --indicator-style=classify (aka -F)
+ requires that we stat each regular file
+ to see if it's executable. */
+ || (type == normal && (indicator_style == classify
+ /* This is so that --color ends up
+ highlighting files with these mode
+ bits set even when options like -F are
+ not specified. Note we do a redundant
+ stat in the very unlikely case where
+ C_CAP is set but not the others. */
+ || (print_with_color
+ && (is_colored (C_EXEC)
+ || is_colored (C_SETUID)
+ || is_colored (C_SETGID)
+ || is_colored (C_CAP)))
+ )))))
{
/* Absolute name of this file. */
char *absolute_name;
-
+ bool do_deref;
int err;
if (name[0] == '/' || dirname[0] == 0)
- absolute_name = (char *) name;
+ absolute_name = (char *) name;
else
- {
- absolute_name = alloca (strlen (name) + strlen (dirname) + 2);
- attach (absolute_name, dirname, name);
- }
+ {
+ absolute_name = alloca (strlen (name) + strlen (dirname) + 2);
+ attach (absolute_name, dirname, name);
+ }
switch (dereference)
- {
- case DEREF_ALWAYS:
- err = stat (absolute_name, &f->stat);
- break;
-
- case DEREF_COMMAND_LINE_ARGUMENTS:
- case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
- if (command_line_arg)
- {
- bool need_lstat;
- err = stat (absolute_name, &f->stat);
-
- if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
- break;
-
- need_lstat = (err < 0
- ? errno == ENOENT
- : ! S_ISDIR (f->stat.st_mode));
- if (!need_lstat)
- break;
-
- /* stat failed because of ENOENT, maybe indicating a dangling
- symlink. Or stat succeeded, ABSOLUTE_NAME does not refer to a
- directory, and --dereference-command-line-symlink-to-dir is
- in effect. Fall through so that we call lstat instead. */
- }
-
- default: /* DEREF_NEVER */
- err = lstat (absolute_name, &f->stat);
- break;
- }
+ {
+ case DEREF_ALWAYS:
+ err = stat (absolute_name, &f->stat);
+ do_deref = true;
+ break;
+
+ case DEREF_COMMAND_LINE_ARGUMENTS:
+ case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
+ if (command_line_arg)
+ {
+ bool need_lstat;
+ err = stat (absolute_name, &f->stat);
+ do_deref = true;
+
+ if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
+ break;
+
+ need_lstat = (err < 0
+ ? errno == ENOENT
+ : ! S_ISDIR (f->stat.st_mode));
+ if (!need_lstat)
+ break;
+
+ /* stat failed because of ENOENT, maybe indicating a dangling
+ symlink. Or stat succeeded, ABSOLUTE_NAME does not refer to a
+ directory, and --dereference-command-line-symlink-to-dir is
+ in effect. Fall through so that we call lstat instead. */
+ }
+
+ default: /* DEREF_NEVER */
+ err = lstat (absolute_name, &f->stat);
+ do_deref = false;
+ break;
+ }
if (err != 0)
- {
- /* Failure to stat a command line argument leads to
- an exit status of 2. For other files, stat failure
- provokes an exit status of 1. */
- file_failure (command_line_arg,
- _("cannot access %s"), absolute_name);
- if (command_line_arg)
- return 0;
+ {
+ /* Failure to stat a command line argument leads to
+ an exit status of 2. For other files, stat failure
+ provokes an exit status of 1. */
+ file_failure (command_line_arg,
+ _("cannot access %s"), absolute_name);
+ if (command_line_arg)
+ return 0;
- f->name = xstrdup (name);
- cwd_n_used++;
+ f->name = xstrdup (name);
+ cwd_n_used++;
- return 0;
- }
+ return 0;
+ }
f->stat_ok = true;
-#if USE_ACL
- if (format == long_format)
- {
- int n = file_has_acl (absolute_name, &f->stat);
- f->have_acl = (0 < n);
- any_has_acl |= f->have_acl;
- if (n < 0)
- error (0, errno, "%s", quotearg_colon (absolute_name));
- }
-#endif
+ /* Note has_capability() adds around 30% runtime to 'ls --color' */
+ if ((type == normal || S_ISREG (f->stat.st_mode))
+ && print_with_color && is_colored (C_CAP))
+ f->has_capability = has_capability_cache (absolute_name, f);
+
+ if (format == long_format || print_scontext)
+ {
+ bool have_scontext = false;
+ bool have_acl = false;
+ int attr_len = getfilecon_cache (absolute_name, f, do_deref);
+ err = (attr_len < 0);
+
+ if (err == 0)
+ {
+ if (is_smack_enabled ())
+ have_scontext = ! STREQ ("_", f->scontext);
+ else
+ have_scontext = ! STREQ ("unlabeled", f->scontext);
+ }
+ else
+ {
+ f->scontext = UNKNOWN_SECURITY_CONTEXT;
+
+ /* When requesting security context information, don't make
+ ls fail just because the file (even a command line argument)
+ isn't on the right type of file system. I.e., a getfilecon
+ failure isn't in the same class as a stat failure. */
+ if (is_ENOTSUP (errno) || errno == ENODATA)
+ err = 0;
+ }
+
+ if (err == 0 && format == long_format)
+ {
+ int n = file_has_acl_cache (absolute_name, f);
+ err = (n < 0);
+ have_acl = (0 < n);
+ }
+
+ f->acl_type = (!have_scontext && !have_acl
+ ? ACL_T_NONE
+ : (have_scontext && !have_acl
+ ? ACL_T_LSM_CONTEXT_ONLY
+ : ACL_T_YES));
+ any_has_acl |= f->acl_type != ACL_T_NONE;
+
+ if (err)
+ error (0, errno, "%s", quotef (absolute_name));
+ }
if (S_ISLNK (f->stat.st_mode)
- && (format == long_format || check_symlink_color))
- {
- char *linkname;
- struct stat linkstats;
-
- get_link_name (absolute_name, f, command_line_arg);
- linkname = make_link_name (absolute_name, f->linkname);
-
- /* Avoid following symbolic links when possible, ie, when
- they won't be traced and when no indicator is needed. */
- if (linkname
- && (file_type <= indicator_style || check_symlink_color)
- && stat (linkname, &linkstats) == 0)
- {
- f->linkok = true;
-
- /* Symbolic links to directories that are mentioned on the
- command line are automatically traced if not being
- listed as files. */
- if (!command_line_arg || format == long_format
- || !S_ISDIR (linkstats.st_mode))
- {
- /* Get the linked-to file's mode for the filetype indicator
- in long listings. */
- f->linkmode = linkstats.st_mode;
- }
- }
- free (linkname);
- }
+ && (format == long_format || check_symlink_color))
+ {
+ struct stat linkstats;
+
+ get_link_name (absolute_name, f, command_line_arg);
+ char *linkname = make_link_name (absolute_name, f->linkname);
+
+ /* Avoid following symbolic links when possible, ie, when
+ they won't be traced and when no indicator is needed. */
+ if (linkname
+ && (file_type <= indicator_style || check_symlink_color)
+ && stat (linkname, &linkstats) == 0)
+ {
+ f->linkok = true;
+
+ /* Symbolic links to directories that are mentioned on the
+ command line are automatically traced if not being
+ listed as files. */
+ if (!command_line_arg || format == long_format
+ || !S_ISDIR (linkstats.st_mode))
+ {
+ /* Get the linked-to file's mode for the filetype indicator
+ in long listings. */
+ f->linkmode = linkstats.st_mode;
+ }
+ }
+ free (linkname);
+ }
if (S_ISLNK (f->stat.st_mode))
- f->filetype = symbolic_link;
+ f->filetype = symbolic_link;
else if (S_ISDIR (f->stat.st_mode))
- {
- if (command_line_arg & !immediate_dirs)
- f->filetype = arg_directory;
- else
- f->filetype = directory;
- }
+ {
+ if (command_line_arg && !immediate_dirs)
+ f->filetype = arg_directory;
+ else
+ f->filetype = directory;
+ }
else
- f->filetype = normal;
+ f->filetype = normal;
blocks = ST_NBLOCKS (f->stat);
- {
- char buf[LONGEST_HUMAN_READABLE + 1];
- int len = mbswidth (human_readable (blocks, buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size),
- 0);
- if (block_size_width < len)
- block_size_width = len;
- }
-
- if (print_owner)
- {
- int len = format_user_width (f->stat.st_uid);
- if (owner_width < len)
- owner_width = len;
- }
+ if (format == long_format || print_block_size)
+ {
+ char buf[LONGEST_HUMAN_READABLE + 1];
+ int len = mbswidth (human_readable (blocks, buf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size),
+ 0);
+ if (block_size_width < len)
+ block_size_width = len;
+ }
- if (print_group)
- {
- int len = format_group_width (f->stat.st_gid);
- if (group_width < len)
- group_width = len;
- }
-
- if (print_author)
- {
- int len = format_user_width (f->stat.st_author);
- if (author_width < len)
- author_width = len;
- }
-
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- int len = strlen (umaxtostr (f->stat.st_nlink, buf));
- if (nlink_width < len)
- nlink_width = len;
- }
+ if (format == long_format)
+ {
+ if (print_owner)
+ {
+ int len = format_user_width (f->stat.st_uid);
+ if (owner_width < len)
+ owner_width = len;
+ }
+
+ if (print_group)
+ {
+ int len = format_group_width (f->stat.st_gid);
+ if (group_width < len)
+ group_width = len;
+ }
+
+ if (print_author)
+ {
+ int len = format_user_width (f->stat.st_author);
+ if (author_width < len)
+ author_width = len;
+ }
+ }
+
+ if (print_scontext)
+ {
+ int len = strlen (f->scontext);
+ if (scontext_width < len)
+ scontext_width = len;
+ }
- if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- int len = strlen (umaxtostr (major (f->stat.st_rdev), buf));
- if (major_device_number_width < len)
- major_device_number_width = len;
- len = strlen (umaxtostr (minor (f->stat.st_rdev), buf));
- if (minor_device_number_width < len)
- minor_device_number_width = len;
- len = major_device_number_width + 2 + minor_device_number_width;
- if (file_size_width < len)
- file_size_width = len;
- }
- else
- {
- char buf[LONGEST_HUMAN_READABLE + 1];
- uintmax_t size = unsigned_file_size (f->stat.st_size);
- int len = mbswidth (human_readable (size, buf, human_output_opts,
- 1, file_output_block_size),
- 0);
- if (file_size_width < len)
- file_size_width = len;
- }
+ if (format == long_format)
+ {
+ char b[INT_BUFSIZE_BOUND (uintmax_t)];
+ int b_len = strlen (umaxtostr (f->stat.st_nlink, b));
+ if (nlink_width < b_len)
+ nlink_width = b_len;
+
+ if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ int len = strlen (umaxtostr (major (f->stat.st_rdev), buf));
+ if (major_device_number_width < len)
+ major_device_number_width = len;
+ len = strlen (umaxtostr (minor (f->stat.st_rdev), buf));
+ if (minor_device_number_width < len)
+ minor_device_number_width = len;
+ len = major_device_number_width + 2 + minor_device_number_width;
+ if (file_size_width < len)
+ file_size_width = len;
+ }
+ else
+ {
+ char buf[LONGEST_HUMAN_READABLE + 1];
+ uintmax_t size = unsigned_file_size (f->stat.st_size);
+ int len = mbswidth (human_readable (size, buf,
+ file_human_output_opts,
+ 1, file_output_block_size),
+ 0);
+ if (file_size_width < len)
+ file_size_width = len;
+ }
+ }
}
if (print_inode)
@@ -2775,7 +3235,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
int len = strlen (umaxtostr (f->stat.st_ino, buf));
if (inode_number_width < len)
- inode_number_width = len;
+ inode_number_width = len;
}
f->name = xstrdup (name);
@@ -2792,50 +3252,52 @@ is_directory (const struct fileinfo *f)
}
/* Put the name of the file that FILENAME is a symbolic link to
- into the LINKNAME field of `f'. COMMAND_LINE_ARG indicates whether
+ into the LINKNAME field of 'f'. COMMAND_LINE_ARG indicates whether
FILENAME is a command-line argument. */
static void
get_link_name (char const *filename, struct fileinfo *f, bool command_line_arg)
{
- f->linkname = xreadlink_with_size (filename, f->stat.st_size);
+ f->linkname = areadlink_with_size (filename, f->stat.st_size);
if (f->linkname == NULL)
file_failure (command_line_arg, _("cannot read symbolic link %s"),
- filename);
+ filename);
}
-/* If `linkname' is a relative name and `name' contains one or more
- leading directories, return `linkname' with those directories
- prepended; otherwise, return a copy of `linkname'.
- If `linkname' is zero, return zero. */
+/* If LINKNAME is a relative name and NAME contains one or more
+ leading directories, return LINKNAME with those directories
+ prepended; otherwise, return a copy of LINKNAME.
+ If LINKNAME is NULL, return NULL. */
static char *
make_link_name (char const *name, char const *linkname)
{
- char *linkbuf;
- size_t bufsiz;
-
if (!linkname)
return NULL;
- if (*linkname == '/')
+ if (IS_ABSOLUTE_FILE_NAME (linkname))
return xstrdup (linkname);
/* The link is to a relative name. Prepend any leading directory
- in `name' to the link name. */
- linkbuf = strrchr (name, '/');
- if (linkbuf == 0)
+ in 'name' to the link name. */
+ size_t prefix_len = dir_len (name);
+ if (prefix_len == 0)
return xstrdup (linkname);
- bufsiz = linkbuf - name + 1;
- linkbuf = xmalloc (bufsiz + strlen (linkname) + 1);
- strncpy (linkbuf, name, bufsiz);
- strcpy (linkbuf + bufsiz, linkname);
- return linkbuf;
+ char *p = xmalloc (prefix_len + 1 + strlen (linkname) + 1);
+
+ /* PREFIX_LEN usually specifies a string not ending in slash.
+ In that case, extend it by one, since the next byte *is* a slash.
+ Otherwise, the prefix is "/", so leave the length unchanged. */
+ if ( ! ISSLASH (name[prefix_len - 1]))
+ ++prefix_len;
+
+ stpcpy (stpncpy (p, name, prefix_len), linkname);
+ return p;
}
-/* Return true if the last component of NAME is `.' or `..'
- This is so we don't try to recurse on `././././. ...' */
+/* Return true if the last component of NAME is '.' or '..'
+ This is so we don't try to recurse on '././././. ...' */
static bool
basename_is_dot_or_dotdot (const char *name)
@@ -2862,8 +3324,8 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg)
if (dirname && LOOP_DETECT)
{
/* Insert a marker entry first. When we dequeue this marker entry,
- we'll know that DIRNAME has been processed and may be removed
- from the set of active directories. */
+ we'll know that DIRNAME has been processed and may be removed
+ from the set of active directories. */
queue_directory (NULL, dirname, false);
}
@@ -2874,20 +3336,20 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg)
struct fileinfo *f = sorted_file[i];
if (is_directory (f)
- && (! ignore_dot_and_dot_dot
- || ! basename_is_dot_or_dotdot (f->name)))
- {
- if (!dirname || f->name[0] == '/')
- queue_directory (f->name, f->linkname, command_line_arg);
- else
- {
- char *name = file_name_concat (dirname, f->name, NULL);
- queue_directory (name, f->linkname, command_line_arg);
- free (name);
- }
- if (f->filetype == arg_directory)
- free (f->name);
- }
+ && (! ignore_dot_and_dot_dot
+ || ! basename_is_dot_or_dotdot (f->name)))
+ {
+ if (!dirname || f->name[0] == '/')
+ queue_directory (f->name, f->linkname, command_line_arg);
+ else
+ {
+ char *name = file_name_concat (dirname, f->name, NULL);
+ queue_directory (name, f->linkname, command_line_arg);
+ free (name);
+ }
+ if (f->filetype == arg_directory)
+ free_ent (f);
+ }
}
/* Now delete the directories from the table, compacting all the remaining
@@ -2916,7 +3378,7 @@ xstrcoll (char const *a, char const *b)
if (errno)
{
error (0, errno, _("cannot compare file names %s and %s"),
- quote_n (0, a), quote_n (1, b));
+ quote_n (0, a), quote_n (1, b));
set_exit_status (false);
longjmp (failed_strcoll, 1);
}
@@ -2937,9 +3399,9 @@ typedef int (*qsortFunc)(V a, V b);
bool a_is_dir = is_directory ((struct fileinfo const *) a); \
bool b_is_dir = is_directory ((struct fileinfo const *) b); \
if (a_is_dir && !b_is_dir) \
- return -1; /* a goes before b */ \
+ return -1; /* a goes before b */ \
if (!a_is_dir && b_is_dir) \
- return 1; /* b goes before a */ \
+ return 1; /* b goes before a */ \
} \
while (0)
@@ -2954,19 +3416,19 @@ typedef int (*qsortFunc)(V a, V b);
{ return key_cmp_func (a, b, xstrcoll); } \
static int strcmp_##key_name (V a, V b) \
{ return key_cmp_func (a, b, strcmp); } \
- \
+ \
/* reverse, non-dirfirst versions */ \
static int rev_xstrcoll_##key_name (V a, V b) \
{ return key_cmp_func (b, a, xstrcoll); } \
static int rev_strcmp_##key_name (V a, V b) \
{ return key_cmp_func (b, a, strcmp); } \
- \
+ \
/* direct, dirfirst versions */ \
static int xstrcoll_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (a, b, xstrcoll); } \
static int strcmp_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (a, b, strcmp); } \
- \
+ \
/* reverse, dirfirst versions */ \
static int rev_xstrcoll_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (b, a, xstrcoll); } \
@@ -2975,34 +3437,34 @@ typedef int (*qsortFunc)(V a, V b);
static inline int
cmp_ctime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_ctime (&b->stat),
- get_stat_ctime (&a->stat));
+ get_stat_ctime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_mtime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_mtime (&b->stat),
- get_stat_mtime (&a->stat));
+ get_stat_mtime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_atime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_atime (&b->stat),
- get_stat_atime (&a->stat));
+ get_stat_atime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_size (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = longdiff (b->stat.st_size, a->stat.st_size);
return diff ? diff : cmp (a->name, b->name);
@@ -3010,17 +3472,17 @@ cmp_size (struct fileinfo const *a, struct fileinfo const *b,
static inline int
cmp_name (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
return cmp (a->name, b->name);
}
-/* Compare file extensions. Files with no extension are `smallest'.
- If extensions are the same, compare by filenames instead. */
+/* Compare file extensions. Files with no extension are 'smallest'.
+ If extensions are the same, compare by file names instead. */
static inline int
cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
char const *base1 = strrchr (a->name, '.');
char const *base2 = strrchr (b->name, '.');
@@ -3037,17 +3499,17 @@ DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
/* Compare file versions.
Unlike all other compare functions above, cmp_version depends only
- on strverscmp, which does not fail (even for locale reasons), and does not
- need a secondary sort key.
+ on filevercmp, which does not fail (even for locale reasons), and does not
+ need a secondary sort key. See lib/filevercmp.h for function description.
+
All the other sort options, in fact, need xstrcoll and strcmp variants,
because they all use a string comparison (either as the primary or secondary
sort key), and xstrcoll has the ability to do a longjmp if strcoll fails for
- locale reasons. Last, strverscmp is ALWAYS available in coreutils,
- thanks to the gnulib library. */
+ locale reasons. Lastly, filevercmp is ALWAYS available with gnulib. */
static inline int
cmp_version (struct fileinfo const *a, struct fileinfo const *b)
{
- return strverscmp (a->name, b->name);
+ return filevercmp (a->name, b->name);
}
static int xstrcoll_version (V a, V b)
@@ -3060,13 +3522,13 @@ static int rev_xstrcoll_df_version (V a, V b)
{ DIRFIRST_CHECK (a, b); return cmp_version (b, a); }
-/* We have 2^3 different variants for each sortkey function
+/* We have 2^3 different variants for each sort-key function
(for 3 independent sort modes).
The function pointers stored in this array must be dereferenced as:
sort_variants[sort_key][use_strcmp][reverse][dirs_first]
- Note that the order in which sortkeys are listed in the function pointer
+ Note that the order in which sort keys are listed in the function pointer
array below is defined by the order of the elements in the time_type and
sort_type enums! */
@@ -3082,7 +3544,7 @@ static int rev_xstrcoll_df_version (V a, V b)
} \
}
-static qsortFunc sort_functions[][2][2][2] =
+static qsortFunc const sort_functions[][2][2][2] =
{
LIST_SORTFUNCTION_VARIANTS (name),
LIST_SORTFUNCTION_VARIANTS (extension),
@@ -3110,16 +3572,16 @@ static qsortFunc sort_functions[][2][2][2] =
LIST_SORTFUNCTION_VARIANTS (atime)
};
-/* The number of sortkeys is calculated as
- the number of elements in the sort_type enum (i.e. sort_numtypes) +
- the number of elements in the time_type enum (i.e. time_numtypes) - 1
+/* The number of sort keys is calculated as the sum of
+ the number of elements in the sort_type enum (i.e., sort_numtypes)
+ the number of elements in the time_type enum (i.e., time_numtypes) - 1
This is because when sort_type==sort_time, we have up to
- time_numtypes possible sortkeys.
+ time_numtypes possible sort keys.
This line verifies at compile-time that the array of sort functions has been
- initialized for all possible sortkeys. */
+ initialized for all possible sort keys. */
verify (ARRAY_CARDINALITY (sort_functions)
- == sort_numtypes + time_numtypes - 1 );
+ == sort_numtypes + time_numtypes - 1 );
/* Set up SORTED_FILE to point to the in-use entries in CWD_FILE, in order. */
@@ -3166,9 +3628,9 @@ sort_files (void)
/* When sort_type == sort_time, use time_type as subindex. */
mpsort ((void const **) sorted_file, cwd_n_used,
- sort_functions[sort_type + (sort_type == sort_time ? time_type : 0)]
- [use_strcmp][sort_reverse]
- [directories_first]);
+ sort_functions[sort_type + (sort_type == sort_time ? time_type : 0)]
+ [use_strcmp][sort_reverse]
+ [directories_first]);
}
/* List all the files now in the table. */
@@ -3182,34 +3644,71 @@ print_current_files (void)
{
case one_per_line:
for (i = 0; i < cwd_n_used; i++)
- {
- print_file_name_and_frills (sorted_file[i]);
- putchar ('\n');
- }
+ {
+ print_file_name_and_frills (sorted_file[i], 0);
+ putchar ('\n');
+ }
break;
case many_per_line:
- print_many_per_line ();
+ if (! line_length)
+ print_with_separator (' ');
+ else
+ print_many_per_line ();
break;
case horizontal:
- print_horizontal ();
+ if (! line_length)
+ print_with_separator (' ');
+ else
+ print_horizontal ();
break;
case with_commas:
- print_with_commas ();
+ print_with_separator (',');
break;
case long_format:
for (i = 0; i < cwd_n_used; i++)
- {
- print_long_format (sorted_file[i]);
- DIRED_PUTCHAR ('\n');
- }
+ {
+ set_normal_color ();
+ print_long_format (sorted_file[i]);
+ DIRED_PUTCHAR ('\n');
+ }
break;
}
}
+/* Replace the first %b with precomputed aligned month names.
+ Note on glibc-2.7 at least, this speeds up the whole 'ls -lU'
+ process by around 17%, compared to letting strftime() handle the %b. */
+
+static size_t
+align_nstrftime (char *buf, size_t size, char const *fmt, struct tm const *tm,
+ timezone_t tz, int ns)
+{
+ const char *nfmt = fmt;
+ /* In the unlikely event that rpl_fmt below is not large enough,
+ the replacement is not done. A malloc here slows ls down by 2% */
+ char rpl_fmt[sizeof (abmon[0]) + 100];
+ const char *pb;
+ if (required_mon_width && (pb = strstr (fmt, "%b"))
+ && 0 <= tm->tm_mon && tm->tm_mon <= 11)
+ {
+ if (strlen (fmt) < (sizeof (rpl_fmt) - sizeof (abmon[0]) + 2))
+ {
+ char *pfmt = rpl_fmt;
+ nfmt = rpl_fmt;
+
+ pfmt = mempcpy (pfmt, fmt, pb - fmt);
+ pfmt = stpcpy (pfmt, abmon[tm->tm_mon]);
+ strcpy (pfmt, pb + 2);
+ }
+ }
+ size_t ret = nstrftime (buf, size, nfmt, tm, tz, ns);
+ return ret;
+}
+
/* Return the expected number of columns in a long-format time stamp,
or zero if it cannot be calculated. */
@@ -3225,63 +3724,28 @@ long_time_expected_width (void)
char buf[TIME_STAMP_LEN_MAXIMUM + 1];
/* In case you're wondering if localtime can fail with an input time_t
- value of 0, let's just say it's very unlikely, but not inconceivable.
- The TZ environment variable would have to specify a time zone that
- is 2**31-1900 years or more ahead of UTC. This could happen only on
- a 64-bit system that blindly accepts e.g., TZ=UTC+20000000000000.
- However, this is not possible with Solaris 10 or glibc-2.3.5, since
- their implementations limit the offset to 167:59 and 24:00, resp. */
+ value of 0, let's just say it's very unlikely, but not inconceivable.
+ The TZ environment variable would have to specify a time zone that
+ is 2**31-1900 years or more ahead of UTC. This could happen only on
+ a 64-bit system that blindly accepts e.g., TZ=UTC+20000000000000.
+ However, this is not possible with Solaris 10 or glibc-2.3.5, since
+ their implementations limit the offset to 167:59 and 24:00, resp. */
if (tm)
- {
- size_t len =
- nstrftime (buf, sizeof buf, long_time_format[0], tm, 0, 0);
- if (len != 0)
- width = mbsnwidth (buf, len, 0);
- }
+ {
+ size_t len =
+ align_nstrftime (buf, sizeof buf, long_time_format[0], tm,
+ localtz, 0);
+ if (len != 0)
+ width = mbsnwidth (buf, len, 0);
+ }
if (width < 0)
- width = 0;
+ width = 0;
}
return width;
}
-/* Get the current time. */
-
-static void
-get_current_time (void)
-{
-#if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
- {
- struct timespec timespec;
- if (clock_gettime (CLOCK_REALTIME, &timespec) == 0)
- {
- current_time = timespec.tv_sec;
- current_time_ns = timespec.tv_nsec;
- return;
- }
- }
-#endif
-
- /* The clock does not have nanosecond resolution, so get the maximum
- possible value for the current time that is consistent with the
- reported clock. That way, files are not considered to be in the
- future merely because their time stamps have higher resolution
- than the clock resolution. */
-
-#if HAVE_GETTIMEOFDAY
- {
- struct timeval timeval;
- gettimeofday (&timeval, NULL);
- current_time = timeval.tv_sec;
- current_time_ns = timeval.tv_usec * 1000 + 999;
- }
-#else
- current_time = time (NULL);
- current_time_ns = 999999999;
-#endif
-}
-
/* Print the user or group name NAME, with numeric id ID, using a
print width of WIDTH columns. */
@@ -3298,7 +3762,7 @@ format_user_or_group (char const *name, unsigned long int id, int width)
len = strlen (name) + pad;
do
- putchar (' ');
+ putchar (' ');
while (pad--);
}
else
@@ -3317,7 +3781,7 @@ static void
format_user (uid_t u, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? NULL : getuser (u)), u, width);
+ (numeric_ids ? NULL : getuser (u)), u, width);
}
/* Likewise, for groups. */
@@ -3326,7 +3790,7 @@ static void
format_group (gid_t g, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? NULL : getgroup (g)), g, width);
+ (numeric_ids ? NULL : getgroup (g)), g, width);
}
/* Return the number of columns that format_user_or_group will print. */
@@ -3341,7 +3805,7 @@ format_user_or_group_width (char const *name, unsigned long int id)
}
else
{
- char buf[INT_BUFSIZE_BOUND (unsigned long int)];
+ char buf[INT_BUFSIZE_BOUND (id)];
sprintf (buf, "%lu", id);
return strlen (buf);
}
@@ -3363,9 +3827,19 @@ format_group_width (gid_t g)
return format_user_or_group_width (numeric_ids ? NULL : getgroup (g), g);
}
+/* Return a pointer to a formatted version of F->stat.st_ino,
+ possibly using buffer, BUF, of length BUFLEN, which must be at least
+ INT_BUFSIZE_BOUND (uintmax_t) bytes. */
+static char *
+format_inode (char *buf, size_t buflen, const struct fileinfo *f)
+{
+ assert (INT_BUFSIZE_BOUND (uintmax_t) <= buflen);
+ return (f->stat_ok && f->stat.st_ino != NOT_AN_INODE_NUMBER
+ ? umaxtostr (f->stat.st_ino, buf)
+ : (char *) "?");
+}
/* Print information about F in long format. */
-
static void
print_long_format (const struct fileinfo *f)
{
@@ -3381,13 +3855,11 @@ print_long_format (const struct fileinfo *f)
];
size_t s;
char *p;
- time_t when;
- int when_ns;
struct timespec when_timespec;
struct tm *when_local;
/* Compute the mode string, except remove the trailing space if no
- files in this directory have ACLs. */
+ file in this directory has an ACL or security context. */
if (f->stat_ok)
filemodestring (&f->stat, modebuf);
else
@@ -3398,7 +3870,9 @@ print_long_format (const struct fileinfo *f)
}
if (! any_has_acl)
modebuf[10] = '\0';
- else if (FILE_HAS_ACL (f))
+ else if (f->acl_type == ACL_T_LSM_CONTEXT_ONLY)
+ modebuf[10] = '.';
+ else if (f->acl_type == ACL_T_YES)
modebuf[10] = '+';
switch (time_type)
@@ -3416,20 +3890,15 @@ print_long_format (const struct fileinfo *f)
abort ();
}
- when = when_timespec.tv_sec;
- when_ns = when_timespec.tv_nsec;
-
p = buf;
if (print_inode)
{
char hbuf[INT_BUFSIZE_BOUND (uintmax_t)];
sprintf (p, "%*s ", inode_number_width,
- (f->stat.st_ino == NOT_AN_INODE_NUMBER
- ? "?"
- : umaxtostr (f->stat.st_ino, hbuf)));
+ format_inode (hbuf, sizeof hbuf, f));
/* Increment by strlen (p) here, rather than by inode_number_width + 1.
- The latter is wrong when inode_number_width is zero. */
+ The latter is wrong when inode_number_width is zero. */
p += strlen (p);
}
@@ -3437,15 +3906,15 @@ print_long_format (const struct fileinfo *f)
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
char const *blocks =
- (! f->stat_ok
- ? "?"
- : human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size));
+ (! f->stat_ok
+ ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size));
int pad;
for (pad = block_size_width - mbswidth (blocks, 0); 0 < pad; pad--)
- *p++ = ' ';
+ *p++ = ' ';
while ((*p++ = *blocks++))
- continue;
+ continue;
p[-1] = ' ';
}
@@ -3454,7 +3923,7 @@ print_long_format (const struct fileinfo *f)
{
char hbuf[INT_BUFSIZE_BOUND (uintmax_t)];
sprintf (p, "%s %*s ", modebuf, nlink_width,
- ! f->stat_ok ? "?" : umaxtostr (f->stat.st_nlink, hbuf));
+ ! f->stat_ok ? "?" : umaxtostr (f->stat.st_nlink, hbuf));
}
/* Increment by strlen (p) here, rather than by, e.g.,
sizeof modebuf - 2 + any_has_acl + 1 + nlink_width + 1.
@@ -3463,18 +3932,21 @@ print_long_format (const struct fileinfo *f)
DIRED_INDENT ();
- if (print_owner | print_group | print_author)
+ if (print_owner || print_group || print_author || print_scontext)
{
DIRED_FPUTS (buf, stdout, p - buf);
if (print_owner)
- format_user (f->stat.st_uid, owner_width, f->stat_ok);
+ format_user (f->stat.st_uid, owner_width, f->stat_ok);
if (print_group)
- format_group (f->stat.st_gid, group_width, f->stat_ok);
+ format_group (f->stat.st_gid, group_width, f->stat_ok);
if (print_author)
- format_user (f->stat.st_author, author_width, f->stat_ok);
+ format_user (f->stat.st_author, author_width, f->stat_ok);
+
+ if (print_scontext)
+ format_user_or_group (f->scontext, 0, scontext_width);
p = buf;
}
@@ -3485,28 +3957,29 @@ print_long_format (const struct fileinfo *f)
char majorbuf[INT_BUFSIZE_BOUND (uintmax_t)];
char minorbuf[INT_BUFSIZE_BOUND (uintmax_t)];
int blanks_width = (file_size_width
- - (major_device_number_width + 2
- + minor_device_number_width));
+ - (major_device_number_width + 2
+ + minor_device_number_width));
sprintf (p, "%*s, %*s ",
- major_device_number_width + MAX (0, blanks_width),
- umaxtostr (major (f->stat.st_rdev), majorbuf),
- minor_device_number_width,
- umaxtostr (minor (f->stat.st_rdev), minorbuf));
+ major_device_number_width + MAX (0, blanks_width),
+ umaxtostr (major (f->stat.st_rdev), majorbuf),
+ minor_device_number_width,
+ umaxtostr (minor (f->stat.st_rdev), minorbuf));
p += file_size_width + 1;
}
else
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
char const *size =
- (! f->stat_ok
- ? "?"
- : human_readable (unsigned_file_size (f->stat.st_size),
- hbuf, human_output_opts, 1, file_output_block_size));
+ (! f->stat_ok
+ ? "?"
+ : human_readable (unsigned_file_size (f->stat.st_size),
+ hbuf, file_human_output_opts, 1,
+ file_output_block_size));
int pad;
for (pad = file_size_width - mbswidth (size, 0); 0 < pad; pad--)
- *p++ = ' ';
+ *p++ = ' ';
while ((*p++ = *size++))
- continue;
+ continue;
p[-1] = ' ';
}
@@ -3516,35 +3989,37 @@ print_long_format (const struct fileinfo *f)
if (f->stat_ok && when_local)
{
- time_t six_months_ago;
+ struct timespec six_months_ago;
bool recent;
char const *fmt;
/* If the file appears to be in the future, update the current
- time, in case the file happens to have been modified since
- the last time we checked the clock. */
- if (current_time < when
- || (current_time == when && current_time_ns < when_ns))
- {
- /* Note that get_current_time calls gettimeofday which, on some non-
- compliant systems, clobbers the buffer used for localtime's result.
- But it's ok here, because we use a gettimeofday wrapper that
- saves and restores the buffer around the gettimeofday call. */
- get_current_time ();
- }
-
- /* Consider a time to be recent if it is within the past six
- months. A Gregorian year has 365.2425 * 24 * 60 * 60 ==
- 31556952 seconds on the average. Write this value as an
- integer constant to avoid floating point hassles. */
- six_months_ago = current_time - 31556952 / 2;
- recent = (six_months_ago <= when
- && (when < current_time
- || (when == current_time && when_ns <= current_time_ns)));
+ time, in case the file happens to have been modified since
+ the last time we checked the clock. */
+ if (timespec_cmp (current_time, when_timespec) < 0)
+ {
+ /* Note that gettime may call gettimeofday which, on some non-
+ compliant systems, clobbers the buffer used for localtime's result.
+ But it's ok here, because we use a gettimeofday wrapper that
+ saves and restores the buffer around the gettimeofday call. */
+ gettime (&current_time);
+ }
+
+ /* Consider a time to be recent if it is within the past six months.
+ A Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds
+ on the average. Write this value as an integer constant to
+ avoid floating point hassles. */
+ six_months_ago.tv_sec = current_time.tv_sec - 31556952 / 2;
+ six_months_ago.tv_nsec = current_time.tv_nsec;
+
+ recent = (timespec_cmp (six_months_ago, when_timespec) < 0
+ && (timespec_cmp (when_timespec, current_time) < 0));
fmt = long_time_format[recent];
- s = nstrftime (p, TIME_STAMP_LEN_MAXIMUM + 1, fmt,
- when_local, 0, when_ns);
+ /* We assume here that all time zones are offset from UTC by a
+ whole number of seconds. */
+ s = align_nstrftime (p, TIME_STAMP_LEN_MAXIMUM + 1, fmt,
+ when_local, localtz, when_timespec.tv_nsec);
}
if (s || !*p)
@@ -3558,31 +4033,28 @@ print_long_format (const struct fileinfo *f)
else
{
/* The time cannot be converted using the desired format, so
- print it as a huge integer number of seconds. */
+ print it as a huge integer number of seconds. */
char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
sprintf (p, "%*s ", long_time_expected_width (),
- (! f->stat_ok
- ? "?"
- : (TYPE_SIGNED (time_t)
- ? imaxtostr (when, hbuf)
- : umaxtostr (when, hbuf))));
+ (! f->stat_ok
+ ? "?"
+ : timetostr (when_timespec.tv_sec, hbuf)));
+ /* FIXME: (maybe) We discarded when_timespec.tv_nsec. */
p += strlen (p);
}
DIRED_FPUTS (buf, stdout, p - buf);
- print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
- f->stat_ok, f->filetype, &dired_obstack);
+ size_t w = print_name_with_quoting (f, false, &dired_obstack, p - buf);
if (f->filetype == symbolic_link)
{
if (f->linkname)
- {
- DIRED_FPUTS_LITERAL (" -> ", stdout);
- print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1,
- f->stat_ok, f->filetype, NULL);
- if (indicator_style != none)
- print_type_indicator (true, f->linkmode, unknown);
- }
+ {
+ DIRED_FPUTS_LITERAL (" -> ", stdout);
+ print_name_with_quoting (f, true, NULL, (p - buf) + w + 4);
+ if (indicator_style != none)
+ print_type_indicator (true, f->linkmode, unknown);
+ }
}
else if (indicator_style != none)
print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
@@ -3596,12 +4068,12 @@ print_long_format (const struct fileinfo *f)
static size_t
quote_name (FILE *out, const char *name, struct quoting_options const *options,
- size_t *width)
+ size_t *width)
{
char smallbuf[BUFSIZ];
size_t len = quotearg_buffer (smallbuf, sizeof smallbuf, name, -1, options);
char *buf;
- size_t displayed_width IF_LINT (= 0);
+ size_t displayed_width IF_LINT ( = 0);
if (len < sizeof smallbuf)
buf = smallbuf;
@@ -3611,144 +4083,144 @@ quote_name (FILE *out, const char *name, struct quoting_options const *options,
quotearg_buffer (buf, len + 1, name, -1, options);
}
- if (qmark_funny_chars)
+ enum quoting_style qs = get_quoting_style (options);
+
+ if (qmark_funny_chars
+ && (qs == shell_quoting_style || qs == shell_always_quoting_style
+ || qs == literal_quoting_style))
{
-#if HAVE_MBRTOWC
if (MB_CUR_MAX > 1)
- {
- char const *p = buf;
- char const *plimit = buf + len;
- char *q = buf;
- displayed_width = 0;
-
- while (p < plimit)
- switch (*p)
- {
- case ' ': case '!': case '"': case '#': case '%':
- case '&': case '\'': case '(': case ')': case '*':
- case '+': case ',': case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case ':': case ';': case '<': case '=': case '>':
- case '?':
- case 'A': case 'B': case 'C': case 'D': case 'E':
- case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
- case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y':
- case 'Z':
- case '[': case '\\': case ']': case '^': case '_':
- case 'a': case 'b': case 'c': case 'd': case 'e':
- case 'f': case 'g': case 'h': case 'i': case 'j':
- case 'k': case 'l': case 'm': case 'n': case 'o':
- case 'p': case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x': case 'y':
- case 'z': case '{': case '|': case '}': case '~':
- /* These characters are printable ASCII characters. */
- *q++ = *p++;
- displayed_width += 1;
- break;
- default:
- /* If we have a multibyte sequence, copy it until we
- reach its end, replacing each non-printable multibyte
- character with a single question mark. */
- {
- mbstate_t mbstate = { 0, };
- do
- {
- wchar_t wc;
- size_t bytes;
- int w;
-
- bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
-
- if (bytes == (size_t) -1)
- {
- /* An invalid multibyte sequence was
- encountered. Skip one input byte, and
- put a question mark. */
- p++;
- *q++ = '?';
- displayed_width += 1;
- break;
- }
-
- if (bytes == (size_t) -2)
- {
- /* An incomplete multibyte character
- at the end. Replace it entirely with
- a question mark. */
- p = plimit;
- *q++ = '?';
- displayed_width += 1;
- break;
- }
-
- if (bytes == 0)
- /* A null wide character was encountered. */
- bytes = 1;
-
- w = wcwidth (wc);
- if (w >= 0)
- {
- /* A printable multibyte character.
- Keep it. */
- for (; bytes > 0; --bytes)
- *q++ = *p++;
- displayed_width += w;
- }
- else
- {
- /* An unprintable multibyte character.
- Replace it entirely with a question
- mark. */
- p += bytes;
- *q++ = '?';
- displayed_width += 1;
- }
- }
- while (! mbsinit (&mbstate));
- }
- break;
- }
-
- /* The buffer may have shrunk. */
- len = q - buf;
- }
+ {
+ char const *p = buf;
+ char const *plimit = buf + len;
+ char *q = buf;
+ displayed_width = 0;
+
+ while (p < plimit)
+ switch (*p)
+ {
+ case ' ': case '!': case '"': case '#': case '%':
+ case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>':
+ case '?':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '[': case '\\': case ']': case '^': case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z': case '{': case '|': case '}': case '~':
+ /* These characters are printable ASCII characters. */
+ *q++ = *p++;
+ displayed_width += 1;
+ break;
+ default:
+ /* If we have a multibyte sequence, copy it until we
+ reach its end, replacing each non-printable multibyte
+ character with a single question mark. */
+ {
+ mbstate_t mbstate = { 0, };
+ do
+ {
+ wchar_t wc;
+ size_t bytes;
+ int w;
+
+ bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
+
+ if (bytes == (size_t) -1)
+ {
+ /* An invalid multibyte sequence was
+ encountered. Skip one input byte, and
+ put a question mark. */
+ p++;
+ *q++ = '?';
+ displayed_width += 1;
+ break;
+ }
+
+ if (bytes == (size_t) -2)
+ {
+ /* An incomplete multibyte character
+ at the end. Replace it entirely with
+ a question mark. */
+ p = plimit;
+ *q++ = '?';
+ displayed_width += 1;
+ break;
+ }
+
+ if (bytes == 0)
+ /* A null wide character was encountered. */
+ bytes = 1;
+
+ w = wcwidth (wc);
+ if (w >= 0)
+ {
+ /* A printable multibyte character.
+ Keep it. */
+ for (; bytes > 0; --bytes)
+ *q++ = *p++;
+ displayed_width += w;
+ }
+ else
+ {
+ /* An unprintable multibyte character.
+ Replace it entirely with a question
+ mark. */
+ p += bytes;
+ *q++ = '?';
+ displayed_width += 1;
+ }
+ }
+ while (! mbsinit (&mbstate));
+ }
+ break;
+ }
+
+ /* The buffer may have shrunk. */
+ len = q - buf;
+ }
else
-#endif
- {
- char *p = buf;
- char const *plimit = buf + len;
-
- while (p < plimit)
- {
- if (! isprint (to_uchar (*p)))
- *p = '?';
- p++;
- }
- displayed_width = len;
- }
+ {
+ char *p = buf;
+ char const *plimit = buf + len;
+
+ while (p < plimit)
+ {
+ if (! isprint (to_uchar (*p)))
+ *p = '?';
+ p++;
+ }
+ displayed_width = len;
+ }
}
else if (width != NULL)
{
-#if HAVE_MBRTOWC
if (MB_CUR_MAX > 1)
- displayed_width = mbsnwidth (buf, len, 0);
+ displayed_width = mbsnwidth (buf, len, 0);
else
-#endif
- {
- char const *p = buf;
- char const *plimit = buf + len;
-
- displayed_width = 0;
- while (p < plimit)
- {
- if (isprint (to_uchar (*p)))
- displayed_width++;
- p++;
- }
- }
+ {
+ char const *p = buf;
+ char const *plimit = buf + len;
+
+ displayed_width = 0;
+ while (p < plimit)
+ {
+ if (isprint (to_uchar (*p)))
+ displayed_width++;
+ p++;
+ }
+ }
}
if (out != NULL)
@@ -3758,27 +4230,38 @@ quote_name (FILE *out, const char *name, struct quoting_options const *options,
return len;
}
-static void
-print_name_with_quoting (const char *p, mode_t mode, int linkok,
- bool stat_ok, enum filetype type,
- struct obstack *stack)
+static size_t
+print_name_with_quoting (const struct fileinfo *f,
+ bool symlink_target,
+ struct obstack *stack,
+ size_t start_col)
{
- if (print_with_color)
- print_color_indicator (p, mode, linkok, stat_ok, type);
+ const char* name = symlink_target ? f->linkname : f->name;
+
+ bool used_color_this_time
+ = (print_with_color
+ && (print_color_indicator (f, symlink_target)
+ || is_colored (C_NORM)));
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
- dired_pos += quote_name (stdout, p, filename_quoting_options, NULL);
+ size_t width = quote_name (stdout, name, filename_quoting_options, NULL);
+ dired_pos += width;
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
- if (print_with_color)
+ process_signals ();
+ if (used_color_this_time)
{
- process_signals ();
prep_non_filename_text ();
+ if (line_length
+ && (start_col / line_length != (start_col + width - 1) / line_length))
+ put_indicator (&color_indicator[C_CLR_TO_EOL]);
}
+
+ return width;
}
static void
@@ -3789,34 +4272,41 @@ prep_non_filename_text (void)
else
{
put_indicator (&color_indicator[C_LEFT]);
- put_indicator (&color_indicator[C_NORM]);
+ put_indicator (&color_indicator[C_RESET]);
put_indicator (&color_indicator[C_RIGHT]);
}
}
-/* Print the file name of `f' with appropriate quoting.
+/* Print the file name of 'f' with appropriate quoting.
Also print file size, inode number, and filetype indicator character,
as requested by switches. */
-static void
-print_file_name_and_frills (const struct fileinfo *f)
+static size_t
+print_file_name_and_frills (const struct fileinfo *f, size_t start_col)
{
char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
+ set_normal_color ();
+
if (print_inode)
printf ("%*s ", format == with_commas ? 0 : inode_number_width,
- umaxtostr (f->stat.st_ino, buf));
+ format_inode (buf, sizeof buf, f));
if (print_block_size)
printf ("%*s ", format == with_commas ? 0 : block_size_width,
- human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size));
+ ! f->stat_ok ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size));
+
+ if (print_scontext)
+ printf ("%*s ", format == with_commas ? 0 : scontext_width, f->scontext);
- print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
- f->stat_ok, f->filetype, NULL);
+ size_t width = print_name_with_quoting (f, false, NULL, start_col);
if (indicator_style != none)
- print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
+ width += print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
+
+ return width;
}
/* Given these arguments describing a file, return the single-byte
@@ -3829,96 +4319,118 @@ get_type_indicator (bool stat_ok, mode_t mode, enum filetype type)
if (stat_ok ? S_ISREG (mode) : type == normal)
{
if (stat_ok && indicator_style == classify && (mode & S_IXUGO))
- c = '*';
+ c = '*';
else
- c = 0;
+ c = 0;
}
else
{
if (stat_ok ? S_ISDIR (mode) : type == directory || type == arg_directory)
- c = '/';
+ c = '/';
else if (indicator_style == slash)
- c = 0;
+ c = 0;
else if (stat_ok ? S_ISLNK (mode) : type == symbolic_link)
- c = '@';
+ c = '@';
else if (stat_ok ? S_ISFIFO (mode) : type == fifo)
- c = '|';
+ c = '|';
else if (stat_ok ? S_ISSOCK (mode) : type == sock)
- c = '=';
+ c = '=';
else if (stat_ok && S_ISDOOR (mode))
- c = '>';
+ c = '>';
else
- c = 0;
+ c = 0;
}
return c;
}
-static void
+static bool
print_type_indicator (bool stat_ok, mode_t mode, enum filetype type)
{
char c = get_type_indicator (stat_ok, mode, type);
if (c)
DIRED_PUTCHAR (c);
+ return !!c;
}
-static void
-print_color_indicator (const char *name, mode_t mode, int linkok,
- bool stat_ok, enum filetype filetype)
+/* Returns whether any color sequence was printed. */
+static bool
+print_color_indicator (const struct fileinfo *f, bool symlink_target)
{
- int type;
+ enum indicator_no type;
struct color_ext_type *ext; /* Color extension */
size_t len; /* Length of name */
+ const char* name;
+ mode_t mode;
+ int linkok;
+ if (symlink_target)
+ {
+ name = f->linkname;
+ mode = f->linkmode;
+ linkok = f->linkok ? 0 : -1;
+ }
+ else
+ {
+ name = f->name;
+ mode = FILE_OR_LINK_MODE (f);
+ linkok = f->linkok;
+ }
+
/* Is this a nonexistent file? If so, linkok == -1. */
- if (linkok == -1 && color_indicator[C_MISSING].string != NULL)
+ if (linkok == -1 && is_colored (C_MISSING))
type = C_MISSING;
- else if (! stat_ok)
+ else if (!f->stat_ok)
{
static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS;
- type = filetype_indicator[filetype];
+ type = filetype_indicator[f->filetype];
}
else
{
if (S_ISREG (mode))
- {
- type = C_FILE;
- if ((mode & S_ISUID) != 0)
- type = C_SETUID;
- else if ((mode & S_ISGID) != 0)
- type = C_SETGID;
- else if ((mode & S_IXUGO) != 0)
- type = C_EXEC;
- }
+ {
+ type = C_FILE;
+
+ if ((mode & S_ISUID) != 0 && is_colored (C_SETUID))
+ type = C_SETUID;
+ else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID))
+ type = C_SETGID;
+ else if (is_colored (C_CAP) && f->has_capability)
+ type = C_CAP;
+ else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC))
+ type = C_EXEC;
+ else if ((1 < f->stat.st_nlink) && is_colored (C_MULTIHARDLINK))
+ type = C_MULTIHARDLINK;
+ }
else if (S_ISDIR (mode))
- {
- if ((mode & S_ISVTX) && (mode & S_IWOTH))
- type = C_STICKY_OTHER_WRITABLE;
- else if ((mode & S_IWOTH) != 0)
- type = C_OTHER_WRITABLE;
- else if ((mode & S_ISVTX) != 0)
- type = C_STICKY;
- else
- type = C_DIR;
- }
+ {
+ type = C_DIR;
+
+ if ((mode & S_ISVTX) && (mode & S_IWOTH)
+ && is_colored (C_STICKY_OTHER_WRITABLE))
+ type = C_STICKY_OTHER_WRITABLE;
+ else if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE))
+ type = C_OTHER_WRITABLE;
+ else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY))
+ type = C_STICKY;
+ }
else if (S_ISLNK (mode))
- type = ((!linkok && color_indicator[C_ORPHAN].string)
- ? C_ORPHAN : C_LINK);
+ type = C_LINK;
else if (S_ISFIFO (mode))
- type = C_FIFO;
+ type = C_FIFO;
else if (S_ISSOCK (mode))
- type = C_SOCK;
+ type = C_SOCK;
else if (S_ISBLK (mode))
- type = C_BLK;
+ type = C_BLK;
else if (S_ISCHR (mode))
- type = C_CHR;
+ type = C_CHR;
else if (S_ISDOOR (mode))
- type = C_DOOR;
+ type = C_DOOR;
else
- {
- /* Classify a file of some other type as C_ORPHAN. */
- type = C_ORPHAN;
- }
+ {
+ /* Classify a file of some other type as C_ORPHAN. */
+ type = C_ORPHAN;
+ }
}
/* Check the file's suffix only if still classified as C_FILE. */
@@ -3930,30 +4442,50 @@ print_color_indicator (const char *name, mode_t mode, int linkok,
len = strlen (name);
name += len; /* Pointer to final \0. */
for (ext = color_ext_list; ext != NULL; ext = ext->next)
- {
- if (ext->ext.len <= len
- && strncmp (name - ext->ext.len, ext->ext.string,
- ext->ext.len) == 0)
- break;
- }
+ {
+ if (ext->ext.len <= len
+ && STREQ_LEN (name - ext->ext.len, ext->ext.string,
+ ext->ext.len))
+ break;
+ }
}
- put_indicator (&color_indicator[C_LEFT]);
- put_indicator (ext ? &(ext->seq) : &color_indicator[type]);
- put_indicator (&color_indicator[C_RIGHT]);
+ /* Adjust the color for orphaned symlinks. */
+ if (type == C_LINK && !linkok)
+ {
+ if (color_symlink_as_referent || is_colored (C_ORPHAN))
+ type = C_ORPHAN;
+ }
+
+ {
+ const struct bin_str *const s
+ = ext ? &(ext->seq) : &color_indicator[type];
+ if (s->string != NULL)
+ {
+ /* Need to reset so not dealing with attribute combinations */
+ if (is_colored (C_NORM))
+ restore_default_color ();
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (s);
+ put_indicator (&color_indicator[C_RIGHT]);
+ return true;
+ }
+ else
+ return false;
+ }
}
/* Output a color indicator (which may contain nulls). */
static void
put_indicator (const struct bin_str *ind)
{
- size_t i;
- const char *p;
-
- p = ind->string;
+ if (! used_color)
+ {
+ used_color = true;
+ prep_non_filename_text ();
+ }
- for (i = ind->len; i != 0; --i)
- putchar (*(p++));
+ fwrite (ind->string, ind->len, 1, stdout);
}
static size_t
@@ -3965,15 +4497,19 @@ length_of_file_name_and_frills (const struct fileinfo *f)
if (print_inode)
len += 1 + (format == with_commas
- ? strlen (umaxtostr (f->stat.st_ino, buf))
- : inode_number_width);
+ ? strlen (umaxtostr (f->stat.st_ino, buf))
+ : inode_number_width);
if (print_block_size)
len += 1 + (format == with_commas
- ? strlen (human_readable (ST_NBLOCKS (f->stat), buf,
- human_output_opts, ST_NBLOCKSIZE,
- output_block_size))
- : block_size_width);
+ ? strlen (! f->stat_ok ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), buf,
+ human_output_opts, ST_NBLOCKSIZE,
+ output_block_size))
+ : block_size_width);
+
+ if (print_scontext)
+ len += 1 + (format == with_commas ? strlen (f->scontext) : scontext_width);
quote_name (NULL, f->name, filename_quoting_options, &name_width);
len += name_width;
@@ -4006,19 +4542,19 @@ print_many_per_line (void)
/* Print the next row. */
while (1)
- {
- struct fileinfo const *f = sorted_file[filesno];
- size_t name_length = length_of_file_name_and_frills (f);
- size_t max_name_length = line_fmt->col_arr[col++];
- print_file_name_and_frills (f);
-
- filesno += rows;
- if (filesno >= cwd_n_used)
- break;
-
- indent (pos + name_length, pos + max_name_length);
- pos += max_name_length;
- }
+ {
+ struct fileinfo const *f = sorted_file[filesno];
+ size_t name_length = length_of_file_name_and_frills (f);
+ size_t max_name_length = line_fmt->col_arr[col++];
+ print_file_name_and_frills (f, pos);
+
+ filesno += rows;
+ if (filesno >= cwd_n_used)
+ break;
+
+ indent (pos + name_length, pos + max_name_length);
+ pos += max_name_length;
+ }
putchar ('\n');
}
}
@@ -4030,31 +4566,31 @@ print_horizontal (void)
size_t pos = 0;
size_t cols = calculate_columns (false);
struct column_info const *line_fmt = &column_info[cols - 1];
- size_t name_length = length_of_file_name_and_frills (cwd_file);
+ struct fileinfo const *f = sorted_file[0];
+ size_t name_length = length_of_file_name_and_frills (f);
size_t max_name_length = line_fmt->col_arr[0];
/* Print first entry. */
- print_file_name_and_frills (cwd_file);
+ print_file_name_and_frills (f, 0);
/* Now the rest. */
for (filesno = 1; filesno < cwd_n_used; ++filesno)
{
- struct fileinfo const *f;
size_t col = filesno % cols;
if (col == 0)
- {
- putchar ('\n');
- pos = 0;
- }
+ {
+ putchar ('\n');
+ pos = 0;
+ }
else
- {
- indent (pos + name_length, pos + max_name_length);
- pos += max_name_length;
- }
+ {
+ indent (pos + name_length, pos + max_name_length);
+ pos += max_name_length;
+ }
f = sorted_file[filesno];
- print_file_name_and_frills (f);
+ print_file_name_and_frills (f, pos);
name_length = length_of_file_name_and_frills (f);
max_name_length = line_fmt->col_arr[col];
@@ -4062,8 +4598,10 @@ print_horizontal (void)
putchar ('\n');
}
+/* Output name + SEP + ' '. */
+
static void
-print_with_commas (void)
+print_with_separator (char sep)
{
size_t filesno;
size_t pos = 0;
@@ -4071,28 +4609,30 @@ print_with_commas (void)
for (filesno = 0; filesno < cwd_n_used; filesno++)
{
struct fileinfo const *f = sorted_file[filesno];
- size_t len = length_of_file_name_and_frills (f);
+ size_t len = line_length ? length_of_file_name_and_frills (f) : 0;
if (filesno != 0)
- {
- char separator;
-
- if (pos + len + 2 < line_length)
- {
- pos += 2;
- separator = ' ';
- }
- else
- {
- pos = 0;
- separator = '\n';
- }
-
- putchar (',');
- putchar (separator);
- }
-
- print_file_name_and_frills (f);
+ {
+ char separator;
+
+ if (! line_length
+ || ((pos + len + 2 < line_length)
+ && (pos <= SIZE_MAX - len - 2)))
+ {
+ pos += 2;
+ separator = ' ';
+ }
+ else
+ {
+ pos = 0;
+ separator = '\n';
+ }
+
+ putchar (sep);
+ putchar (separator);
+ }
+
+ print_file_name_and_frills (f, pos);
pos += len;
}
putchar ('\n');
@@ -4107,19 +4647,19 @@ indent (size_t from, size_t to)
while (from < to)
{
if (tabsize != 0 && to / tabsize > (from + 1) / tabsize)
- {
- putchar ('\t');
- from += tabsize - from % tabsize;
- }
+ {
+ putchar ('\t');
+ from += tabsize - from % tabsize;
+ }
else
- {
- putchar (' ');
- from++;
- }
+ {
+ putchar (' ');
+ from++;
+ }
}
}
-/* Put DIRNAME/NAME into DEST, handling `.' and `/' properly. */
+/* Put DIRNAME/NAME into DEST, handling '.' and '/' properly. */
/* FIXME: maybe remove this function someday. See about using a
non-malloc'ing version of file_name_concat. */
@@ -4132,10 +4672,10 @@ attach (char *dest, const char *dirname, const char *name)
if (dirname[0] != '.' || dirname[1] != 0)
{
while (*dirnamep)
- *dest++ = *dirnamep++;
- /* Add '/' if `dirname' doesn't already end with it. */
+ *dest++ = *dirnamep++;
+ /* Add '/' if 'dirname' doesn't already end with it. */
if (dirnamep > dirname && dirnamep[-1] != '/')
- *dest++ = '/';
+ *dest++ = '/';
}
while (*name)
*dest++ = *name++;
@@ -4161,41 +4701,41 @@ init_column_info (void)
size_t *p;
if (max_cols < max_idx / 2)
- {
- /* The number of columns is far less than the display width
- allows. Grow the allocation, but only so that it's
- double the current requirements. If the display is
- extremely wide, this avoids allocating a lot of memory
- that is never needed. */
- column_info = xnrealloc (column_info, max_cols,
- 2 * sizeof *column_info);
- new_column_info_alloc = 2 * max_cols;
- }
+ {
+ /* The number of columns is far less than the display width
+ allows. Grow the allocation, but only so that it's
+ double the current requirements. If the display is
+ extremely wide, this avoids allocating a lot of memory
+ that is never needed. */
+ column_info = xnrealloc (column_info, max_cols,
+ 2 * sizeof *column_info);
+ new_column_info_alloc = 2 * max_cols;
+ }
else
- {
- column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
- new_column_info_alloc = max_idx;
- }
+ {
+ column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
+ new_column_info_alloc = max_idx;
+ }
/* Allocate the new size_t objects by computing the triangle
- formula n * (n + 1) / 2, except that we don't need to
- allocate the part of the triangle that we've already
- allocated. Check for address arithmetic overflow. */
+ formula n * (n + 1) / 2, except that we don't need to
+ allocate the part of the triangle that we've already
+ allocated. Check for address arithmetic overflow. */
{
- size_t column_info_growth = new_column_info_alloc - column_info_alloc;
- size_t s = column_info_alloc + 1 + new_column_info_alloc;
- size_t t = s * column_info_growth;
- if (s < new_column_info_alloc || t / column_info_growth != s)
- xalloc_die ();
- p = xnmalloc (t / 2, sizeof *p);
+ size_t column_info_growth = new_column_info_alloc - column_info_alloc;
+ size_t s = column_info_alloc + 1 + new_column_info_alloc;
+ size_t t = s * column_info_growth;
+ if (s < new_column_info_alloc || t / column_info_growth != s)
+ xalloc_die ();
+ p = xnmalloc (t / 2, sizeof *p);
}
/* Grow the triangle by parceling out the cells just allocated. */
for (i = column_info_alloc; i < new_column_info_alloc; i++)
- {
- column_info[i].col_arr = p;
- p += i + 1;
- }
+ {
+ column_info[i].col_arr = p;
+ p += i + 1;
+ }
column_info_alloc = new_column_info_alloc;
}
@@ -4207,7 +4747,7 @@ init_column_info (void)
column_info[i].valid_len = true;
column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
for (j = 0; j <= i; ++j)
- column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
+ column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
}
}
@@ -4235,31 +4775,31 @@ calculate_columns (bool by_columns)
size_t i;
for (i = 0; i < max_cols; ++i)
- {
- if (column_info[i].valid_len)
- {
- size_t idx = (by_columns
- ? filesno / ((cwd_n_used + i) / (i + 1))
- : filesno % (i + 1));
- size_t real_length = name_length + (idx == i ? 0 : 2);
-
- if (column_info[i].col_arr[idx] < real_length)
- {
- column_info[i].line_len += (real_length
- - column_info[i].col_arr[idx]);
- column_info[i].col_arr[idx] = real_length;
- column_info[i].valid_len = (column_info[i].line_len
- < line_length);
- }
- }
- }
+ {
+ if (column_info[i].valid_len)
+ {
+ size_t idx = (by_columns
+ ? filesno / ((cwd_n_used + i) / (i + 1))
+ : filesno % (i + 1));
+ size_t real_length = name_length + (idx == i ? 0 : 2);
+
+ if (column_info[i].col_arr[idx] < real_length)
+ {
+ column_info[i].line_len += (real_length
+ - column_info[i].col_arr[idx]);
+ column_info[i].col_arr[idx] = real_length;
+ column_info[i].valid_len = (column_info[i].line_len
+ < line_length);
+ }
+ }
+ }
}
/* Find maximum allowed columns. */
for (cols = max_cols; 1 < cols; --cols)
{
if (column_info[cols - 1].valid_len)
- break;
+ break;
}
return cols;
@@ -4269,45 +4809,46 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
fputs (_("\
List information about the FILEs (the current directory by default).\n\
-Sort entries alphabetically if none of -cftuvSUX nor --sort.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-a, --all do not ignore entries starting with .\n\
-A, --almost-all do not list implied . and ..\n\
--author with -l, print the author of each file\n\
- -b, --escape print octal escapes for nongraphic characters\n\
+ -b, --escape print C-style escapes for nongraphic characters\n\
"), stdout);
fputs (_("\
- --block-size=SIZE use SIZE-byte blocks\n\
+ --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
+ '--block-size=M' prints sizes in units of\n\
+ 1,048,576 bytes; see SIZE format below\n\
-B, --ignore-backups do not list implied entries ending with ~\n\
-c with -lt: sort by, and show, ctime (time of last\n\
- modification of file status information)\n\
- with -l: show ctime and sort by name\n\
- otherwise: sort by ctime\n\
+ modification of file status information);\n\
+ with -l: show ctime and sort by name;\n\
+ otherwise: sort by ctime, newest first\n\
"), stdout);
fputs (_("\
-C list entries by columns\n\
- --color[=WHEN] control whether color is used to distinguish file\n\
- types. WHEN may be `never', `always', or `auto'\n\
- -d, --directory list directory entries instead of contents,\n\
- and do not dereference symbolic links\n\
+ --color[=WHEN] colorize the output; WHEN can be 'always' (default\
+\n\
+ if omitted), 'auto', or 'never'; more info below\
+\n\
+ -d, --directory list directories themselves, not their contents\n\
-D, --dired generate output designed for Emacs' dired mode\n\
"), stdout);
fputs (_("\
-f do not sort, enable -aU, disable -ls --color\n\
-F, --classify append indicator (one of */=>@|) to entries\n\
- --file-type likewise, except do not append `*'\n\
+ --file-type likewise, except do not append '*'\n\
--format=WORD across -x, commas -m, horizontal -x, long -l,\n\
single-column -1, verbose -l, vertical -C\n\
--full-time like -l --time-style=full-iso\n\
@@ -4317,11 +4858,13 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (_("\
--group-directories-first\n\
- group directories before files\n\
+ group directories before files;\n\
+ can be augmented with a --sort option, but any\n\
+ use of --sort=none (-U) disables grouping\n\
"), stdout);
fputs (_("\
-G, --no-group in a long listing, don't print group names\n\
- -h, --human-readable with -l, print sizes in human readable format\n\
+ -h, --human-readable with -l and/or -s, print human readable sizes\n\
(e.g., 1K 234M 2G)\n\
--si likewise, but use powers of 1000 not 1024\n\
"), stdout);
@@ -4330,24 +4873,28 @@ Mandatory arguments to long options are mandatory for short options too.\n\
follow symbolic links listed on the command line\n\
--dereference-command-line-symlink-to-dir\n\
follow each command line symbolic link\n\
- that points to a directory\n\
- --hide=PATTERN do not list implied entries matching shell PATTERN\n\
+ that points to a directory\n\
+ --hide=PATTERN do not list implied entries matching shell PATTERN\
+\n\
(overridden by -a or -A)\n\
"), stdout);
fputs (_("\
- --indicator-style=WORD append indicator with style WORD to entry names:\n\
+ --indicator-style=WORD append indicator with style WORD to entry names:\
+\n\
none (default), slash (-p),\n\
file-type (--file-type), classify (-F)\n\
-i, --inode print the index number of each file\n\
- -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\n\
- -k like --block-size=1K\n\
+ -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\
+\n\
+ -k, --kibibytes default to 1024-byte blocks for disk usage\n\
"), stdout);
fputs (_("\
-l use a long listing format\n\
-L, --dereference when showing file information for a symbolic\n\
link, show information for the file the link\n\
references rather than for the link itself\n\
- -m fill width with a comma separated list of entries\n\
+ -m fill width with a comma separated list of entries\
+\n\
"), stdout);
fputs (_("\
-n, --numeric-uid-gid like -l, but list numeric user and group IDs\n\
@@ -4358,73 +4905,80 @@ Mandatory arguments to long options are mandatory for short options too.\n\
append / indicator to directories\n\
"), stdout);
fputs (_("\
- -q, --hide-control-chars print ? instead of non graphic characters\n\
- --show-control-chars show non graphic characters as-is (default\n\
- unless program is `ls' and output is a terminal)\n\
+ -q, --hide-control-chars print ? instead of nongraphic characters\n\
+ --show-control-chars show nongraphic characters as-is (the default,\n\
+ unless program is 'ls' and output is a terminal)\
+\n\
-Q, --quote-name enclose entry names in double quotes\n\
--quoting-style=WORD use quoting style WORD for entry names:\n\
- literal, locale, shell, shell-always, c, escape\n\
+ literal, locale, shell, shell-always,\n\
+ shell-escape, shell-escape-always, c, escape\n\
"), stdout);
fputs (_("\
-r, --reverse reverse order while sorting\n\
-R, --recursive list subdirectories recursively\n\
- -s, --size print the size of each file, in blocks\n\
+ -s, --size print the allocated size of each file, in blocks\n\
"), stdout);
fputs (_("\
- -S sort by file size\n\
- --sort=WORD sort by WORD instead of name: none -U,\n\
- extension -X, size -S, time -t, version -v\n\
- --time=WORD with -l, show time as WORD instead of modification\n\
- time: atime -u, access -u, use -u, ctime -c,\n\
- or status -c; use specified time as sort key\n\
- if --sort=time\n\
+ -S sort by file size, largest first\n\
+ --sort=WORD sort by WORD instead of name: none (-U), size (-S)\
+,\n\
+ time (-t), version (-v), extension (-X)\n\
+ --time=WORD with -l, show time as WORD instead of default\n\
+ modification time: atime or access or use (-u);\
+\n\
+ ctime or status (-c); also use specified time\n\
+ as sort key if --sort=time (newest first)\n\
"), stdout);
fputs (_("\
--time-style=STYLE with -l, show times using style STYLE:\n\
- full-iso, long-iso, iso, locale, +FORMAT.\n\
- FORMAT is interpreted like `date'; if FORMAT is\n\
- FORMAT1<newline>FORMAT2, FORMAT1 applies to\n\
- non-recent files and FORMAT2 to recent files;\n\
- if STYLE is prefixed with `posix-', STYLE\n\
- takes effect only outside the POSIX locale\n\
+ full-iso, long-iso, iso, locale, or +FORMAT;\n\
+ FORMAT is interpreted like in 'date'; if FORMAT\
+\n\
+ is FORMAT1<newline>FORMAT2, then FORMAT1 applies\
+\n\
+ to non-recent files and FORMAT2 to recent files;\
+\n\
+ if STYLE is prefixed with 'posix-', STYLE\n\
+ takes effect only outside the POSIX locale\n\
"), stdout);
fputs (_("\
- -t sort by modification time\n\
+ -t sort by modification time, newest first\n\
-T, --tabsize=COLS assume tab stops at each COLS instead of 8\n\
"), stdout);
fputs (_("\
- -u with -lt: sort by, and show, access time\n\
- with -l: show access time and sort by name\n\
- otherwise: sort by access time\n\
+ -u with -lt: sort by, and show, access time;\n\
+ with -l: show access time and sort by name;\n\
+ otherwise: sort by access time, newest first\n\
-U do not sort; list entries in directory order\n\
- -v sort by version\n\
+ -v natural sort of (version) numbers within text\n\
"), stdout);
fputs (_("\
- -w, --width=COLS assume screen width instead of current value\n\
+ -w, --width=COLS set output width to COLS. 0 means no limit\n\
-x list entries by lines instead of by columns\n\
-X sort alphabetically by entry extension\n\
- -1 list one file per line\n\
+ -Z, --context print any security context of each file\n\
+ -1 list one file per line. Avoid '\\n' with -q or -b\
+\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\n\
-SIZE may be (or may be an integer optionally followed by) one of following:\n\
-kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
-"), stdout);
+ emit_size_note ();
fputs (_("\
\n\
-By default, color is not used to distinguish types of files. That is\n\
-equivalent to using --color=none. Using the --color option without the\n\
-optional WHEN argument is equivalent to using --color=always. With\n\
---color=auto, color codes are output only if standard output is connected\n\
-to a terminal (tty). The environment variable LS_COLORS can influence the\n\
-colors, and can be set easily by the dircolors command.\n\
+Using color to distinguish file types is disabled both by default and\n\
+with --color=never. With --color=auto, ls emits color codes only when\n\
+standard output is connected to a terminal. The LS_COLORS environment\n\
+variable can change the settings. Use the dircolors command to set it.\n\
"), stdout);
fputs (_("\
\n\
-Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.\n\
+Exit status:\n\
+ 0 if OK,\n\
+ 1 if minor problems (e.g., cannot access subdirectory),\n\
+ 2 if serious trouble (e.g., cannot access command-line argument).\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}