#ifdef _AIX
#pragma alloca
#endif
#include <config.h>
#include <sys/types.h>
#if HAVE_TERMIOS_H
# include <termios.h>
#endif
#ifdef GWINSZ_IN_SYS_IOCTL
# include <sys/ioctl.h>
#endif
#ifdef WINSIZE_IN_PTEM
# include <sys/stream.h>
# include <sys/ptem.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <setjmp.h>
#include <grp.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_WCHAR_H
# include <wchar.h>
#endif
#if HAVE_WCTYPE_H
# include <wctype.h>
#endif
#if !defined iswprint && !HAVE_ISWPRINT
# define iswprint(wc) 1
#endif
#ifndef HAVE_DECL_WCWIDTH
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_WCWIDTH
int wcwidth ();
#endif
#ifndef wcwidth
# if !HAVE_WCWIDTH
# define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
# endif
#endif
#include "system.h"
#include <fnmatch.h>
#include "acl.h"
#include "argmatch.h"
#include "dev-ino.h"
#include "dirname.h"
#include "dirfd.h"
#include "error.h"
#include "full-write.h"
#include "hard-locale.h"
#include "hash.h"
#include "human.h"
#include "filemode.h"
#include "inttostr.h"
#include "ls.h"
#include "mbswidth.h"
#include "obstack.h"
#include "path-concat.h"
#include "quote.h"
#include "quotearg.h"
#include "same.h"
#include "strftime.h"
#include "strverscmp.h"
#include "xstrtol.h"
#include "xreadlink.h"
#define PROGRAM_NAME (ls_mode == LS_LS ? "ls" \
: (ls_mode == LS_MULTI_COL \
? "dir" : "vdir"))
#define AUTHORS N_ ("Richard Stallman and David MacKenzie")
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))
#ifndef INODE_DIGITS
# define INODE_DIGITS 7
#endif
#if ! LSTAT_FOLLOWS_SLASHED_SYMLINK
int rpl_lstat (const char *, struct stat *);
# undef lstat
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
#endif
#if HAVE_STRUCT_DIRENT_D_TYPE && defined DTTOIF
# define DT_INIT(Val) = Val
#else
# define DT_INIT(Val)
#endif
#ifdef ST_MTIM_NSEC
# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
#else
# define TIMESPEC_NS(timespec) 0
#endif
#if ! HAVE_STRUCT_STAT_ST_AUTHOR
# define st_author st_uid
#endif
#if HAVE_ST_DM_MODE
# define ST_DM_MODE(Stat_buf) ((Stat_buf).st_dm_mode)
#else
# define ST_DM_MODE(Stat_buf) ((Stat_buf).st_mode)
#endif
#ifndef LOGIN_NAME_MAX
# if _POSIX_LOGIN_NAME_MAX
# define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
# else
# define LOGIN_NAME_MAX 17
# endif
#endif
#define ID_LENGTH_MAX \
MAX (LOGIN_NAME_MAX - 1, LONGEST_HUMAN_READABLE)
enum filetype
{
unknown DT_INIT (DT_UNKNOWN),
fifo DT_INIT (DT_FIFO),
chardev DT_INIT (DT_CHR),
directory DT_INIT (DT_DIR),
blockdev DT_INIT (DT_BLK),
normal DT_INIT (DT_REG),
symbolic_link DT_INIT (DT_LNK),
sock DT_INIT (DT_SOCK),
arg_directory DT_INIT (2 * (DT_UNKNOWN | DT_FIFO | DT_CHR | DT_DIR | DT_BLK
| DT_REG | DT_LNK | DT_SOCK))
};
struct fileinfo
{
char *name;
struct stat stat;
char *linkname;
mode_t linkmode;
int linkok;
enum filetype filetype;
#if HAVE_ACL
bool have_acl;
#endif
};
#if HAVE_ACL
# define FILE_HAS_ACL(F) ((F)->have_acl)
#else
# define FILE_HAS_ACL(F) 0
#endif
#define LEN_STR_PAIR(s) sizeof (s) - 1, s
struct bin_str
{
int len;
const char *string;
};
#ifndef STDC_HEADERS
time_t time ();
#endif
char *getgroup ();
char *getuser ();
static size_t quote_name (FILE *out, const char *name,
struct quoting_options const *options,
size_t *width);
static char *make_link_path (const char *path, const char *linkname);
static int decode_switches (int argc, char **argv);
static int file_interesting (const struct dirent *next);
static uintmax_t gobble_file (const char *name, enum filetype type,
int explicit_arg, const char *dirname);
static void print_color_indicator (const char *name, mode_t mode, int linkok);
static void put_indicator (const struct bin_str *ind);
static int put_indicator_direct (const struct bin_str *ind);
static int length_of_file_name_and_frills (const struct fileinfo *f);
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 (const char *dirname,
int ignore_dot_and_dot_dot);
static void get_link_name (const char *filename, struct fileinfo *f);
static void indent (int from, int to);
static void init_column_info (void);
static void print_current_files (void);
static void print_dir (const char *name, const char *realname);
static void print_file_name_and_frills (const struct fileinfo *f);
static void print_horizontal (void);
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,
struct obstack *stack);
static void prep_non_filename_text (void);
static void print_type_indicator (mode_t mode);
static void print_with_commas (void);
static void queue_directory (const char *name, const char *realname);
static void sort_files (void);
static void parse_ls_color (void);
void usage (int status);
char *program_name;
#define INITIAL_TABLE_SIZE 30
static Hash_table *active_dir_set;
#define LOOP_DETECT (!!active_dir_set)
static struct fileinfo *files;
static int nfiles;
static int files_index;
static int color_symlink_as_referent;
#define FILE_OR_LINK_MODE(File) \
((color_symlink_as_referent && (File)->linkok) \
? (File)->linkmode : (File)->stat.st_mode)
struct pending
{
char *name;
char *realname;
struct pending *next;
};
static struct pending *pending_dirs;
static time_t current_time = TYPE_MINIMUM (time_t);
static int current_time_ns = -1;
static int block_size_size;
enum format
{
long_format,
one_per_line,
many_per_line,
horizontal,
with_commas
};
static enum format format;
enum time_style
{
full_iso_time_style,
long_iso_time_style,
iso_time_style,
locale_time_style
};
static char const *const time_style_args[] =
{
"full-iso", "long-iso", "iso", "locale", 0
};
static enum time_style const time_style_types[] =
{
full_iso_time_style, long_iso_time_style, iso_time_style,
locale_time_style, 0
};
enum time_type
{
time_mtime,
time_ctime,
time_atime
};
static enum time_type time_type;
enum sort_type
{
sort_none,
sort_name,
sort_extension,
sort_time,
sort_size,
sort_version
};
static enum sort_type sort_type;
static int sort_reverse;
static int print_owner = 1;
static bool print_author;
static int print_group = 1;
static int numeric_ids;
static int print_block_size;
static int human_output_opts;
static uintmax_t output_block_size;
static uintmax_t file_output_block_size = 1;
static int dired;
enum indicator_style
{
none,
classify,
file_type
};
static enum indicator_style indicator_style;
static char const *const indicator_style_args[] =
{
"none", "classify", "file-type", 0
};
static enum indicator_style const indicator_style_types[]=
{
none, classify, file_type
};
static int print_with_color;
enum color_type
{
color_never,
color_always,
color_if_tty
};
enum Dereference_symlink
{
DEREF_UNDEFINED = 1,
DEREF_NEVER,
DEREF_COMMAND_LINE_ARGUMENTS,
DEREF_COMMAND_LINE_SYMLINK_TO_DIR,
DEREF_ALWAYS
};
enum indicator_no
{
C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR
};
static const char *const indicator_name[]=
{
"lc", "rc", "ec", "no", "fi", "di", "ln", "pi", "so",
"bd", "cd", "mi", "or", "ex", "do", NULL
};
struct color_ext_type
{
struct bin_str ext;
struct bin_str seq;
struct color_ext_type *next;
};
static struct bin_str color_indicator[] =
{
{ LEN_STR_PAIR ("\033[") },
{ LEN_STR_PAIR ("m") },
{ 0, NULL },
{ LEN_STR_PAIR ("0") },
{ LEN_STR_PAIR ("0") },
{ LEN_STR_PAIR ("01;34") },
{ LEN_STR_PAIR ("01;36") },
{ LEN_STR_PAIR ("33") },
{ LEN_STR_PAIR ("01;35") },
{ LEN_STR_PAIR ("01;33") },
{ LEN_STR_PAIR ("01;33") },
{ 0, NULL },
{ 0, NULL },
{ LEN_STR_PAIR ("01;32") },
{ LEN_STR_PAIR ("01;35") }
};
static struct color_ext_type *color_ext_list = NULL;
static char *color_buf;
static int check_symlink_color;
static int print_inode;
static enum Dereference_symlink dereference;
static int recursive;
static int immediate_dirs;
static int all_files;
static int really_all_files;
struct ignore_pattern
{
const char *pattern;
struct ignore_pattern *next;
};
static struct ignore_pattern *ignore_patterns;
static int qmark_funny_chars;
static struct quoting_options *filename_quoting_options;
static struct quoting_options *dirname_quoting_options;
static int tabsize;
static int dir_defaulted;
static int print_dir_name;
static int line_length;
static int format_needs_stat;
static int format_needs_type;
static char const *long_time_format[2] =
{
N_("%b %e %Y"),
N_("%b %e %H:%M")
};
static int exit_status;
enum
{
AUTHOR_OPTION = CHAR_MAX + 1,
BLOCK_SIZE_OPTION,
COLOR_OPTION,
DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION,
FORMAT_OPTION,
FULL_TIME_OPTION,
INDICATOR_STYLE_OPTION,
QUOTING_STYLE_OPTION,
SHOW_CONTROL_CHARS_OPTION,
SI_OPTION,
SORT_OPTION,
TIME_OPTION,
TIME_STYLE_OPTION
};
static struct option const long_options[] =
{
{"all", no_argument, 0, 'a'},
{"escape", no_argument, 0, 'b'},
{"directory", no_argument, 0, 'd'},
{"dired", no_argument, 0, 'D'},
{"full-time", no_argument, 0, FULL_TIME_OPTION},
{"human-readable", no_argument, 0, 'h'},
{"inode", no_argument, 0, 'i'},
{"kilobytes", no_argument, 0, 'k'},
{"numeric-uid-gid", no_argument, 0, 'n'},
{"no-group", no_argument, 0, 'G'},
{"hide-control-chars", no_argument, 0, 'q'},
{"reverse", no_argument, 0, 'r'},
{"size", no_argument, 0, 's'},
{"width", required_argument, 0, 'w'},
{"almost-all", no_argument, 0, 'A'},
{"ignore-backups", no_argument, 0, 'B'},
{"classify", no_argument, 0, 'F'},
{"file-type", no_argument, 0, 'p'},
{"si", no_argument, 0, SI_OPTION},
{"dereference-command-line", no_argument, 0, 'H'},
{"dereference-command-line-symlink-to-dir", no_argument, 0,
DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION},
{"ignore", required_argument, 0, 'I'},
{"indicator-style", required_argument, 0, INDICATOR_STYLE_OPTION},
{"dereference", no_argument, 0, 'L'},
{"literal", no_argument, 0, 'N'},
{"quote-name", no_argument, 0, 'Q'},
{"quoting-style", required_argument, 0, QUOTING_STYLE_OPTION},
{"recursive", no_argument, 0, 'R'},
{"format", required_argument, 0, FORMAT_OPTION},
{"show-control-chars", no_argument, 0, SHOW_CONTROL_CHARS_OPTION},
{"sort", required_argument, 0, SORT_OPTION},
{"tabsize", required_argument, 0, 'T'},
{"time", required_argument, 0, TIME_OPTION},
{"time-style", required_argument, 0, TIME_STYLE_OPTION},
{"color", optional_argument, 0, COLOR_OPTION},
{"block-size", required_argument, 0, BLOCK_SIZE_OPTION},
{"author", no_argument, 0, AUTHOR_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
static char const *const format_args[] =
{
"verbose", "long", "commas", "horizontal", "across",
"vertical", "single-column", 0
};
static enum format const format_types[] =
{
long_format, long_format, with_commas, horizontal, horizontal,
many_per_line, one_per_line
};
static char const *const sort_args[] =
{
"none", "time", "size", "extension", "version", 0
};
static enum sort_type const sort_types[] =
{
sort_none, sort_time, sort_size, sort_extension, sort_version
};
static char const *const time_args[] =
{
"atime", "access", "use", "ctime", "status", 0
};
static enum time_type const time_types[] =
{
time_atime, time_atime, time_atime, time_ctime, time_ctime
};
static char const *const color_args[] =
{
"always", "yes", "force",
"never", "no", "none",
"auto", "tty", "if-tty", 0
};
static enum color_type const color_types[] =
{
color_always, color_always, color_always,
color_never, color_never, color_never,
color_if_tty, color_if_tty, color_if_tty
};
struct column_info
{
int valid_len;
int line_len;
int *col_arr;
};
static struct column_info *column_info;
static int max_idx;
#define MIN_COLUMN_WIDTH 3
static size_t dired_pos;
#define DIRED_PUTCHAR(c) do {putchar ((c)); ++dired_pos;} while (0)
#define DIRED_FPUTS(s, stream, s_len) \
do {fputs ((s), (stream)); dired_pos += s_len;} while (0)
#define DIRED_FPUTS_LITERAL(s, stream) \
do {fputs ((s), (stream)); dired_pos += sizeof((s)) - 1;} while (0)
#define DIRED_INDENT() \
do \
{ \
if (dired) \
DIRED_FPUTS_LITERAL (" ", stdout); \
} \
while (0)
static struct obstack dired_obstack;
static struct obstack subdired_obstack;
#define PUSH_CURRENT_DIRED_POS(obs) \
do \
{ \
if (dired) \
obstack_grow ((obs), &dired_pos, sizeof (dired_pos)); \
} \
while (0)
static struct obstack dev_ino_obstack;
#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 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);
}
#define ASSERT_MATCHING_DEV_INO(Name, Di) \
do \
{ \
struct stat sb; \
assert (Name); \
assert (0 <= stat (Name, &sb)); \
assert (sb.st_dev == Di.st_dev); \
assert (sb.st_ino == Di.st_ino); \
} \
while (0)
static void
dired_dump_obstack (const char *prefix, struct obstack *os)
{
int n_pos;
n_pos = obstack_object_size (os) / sizeof (dired_pos);
if (n_pos > 0)
{
int i;
size_t *pos;
pos = (size_t *) obstack_finish (os);
fputs (prefix, stdout);
for (i = 0; i < n_pos; i++)
printf (" %lu", (unsigned long) pos[i]);
putchar ('\n');
}
}
static unsigned int
dev_ino_hash (void const *x, unsigned int table_size)
{
struct dev_ino const *p = x;
return (uintmax_t) p->st_ino % table_size;
}
static bool
dev_ino_compare (void const *x, void const *y)
{
struct dev_ino const *a = x;
struct dev_ino const *b = y;
return SAME_INODE (*a, *b) ? true : false;
}
static void
dev_ino_free (void *x)
{
free (x);
}
static int
visit_dir (dev_t dev, ino_t ino)
{
struct dev_ino *ent;
struct dev_ino *ent_from_table;
int found_match;
ent = XMALLOC (struct dev_ino, 1);
ent->st_ino = ino;
ent->st_dev = dev;
ent_from_table = hash_insert (active_dir_set, ent);
if (ent_from_table == NULL)
{
xalloc_die ();
}
found_match = (ent_from_table != ent);
if (found_match)
{
free (ent);
}
return found_match;
}
static void
free_pending_ent (struct pending *p)
{
if (p->name)
free (p->name);
if (p->realname)
free (p->realname);
free (p);
}
static void
restore_default_color (void)
{
if (put_indicator_direct (&color_indicator[C_LEFT]) == 0)
put_indicator_direct (&color_indicator[C_RIGHT]);
}
static void
sighandler (int sig)
{
#ifndef SA_NOCLDSTOP
signal (sig, SIG_IGN);
#endif
restore_default_color ();
if (sig == SIGTSTP)
{
sig = SIGSTOP;
}
else
{
#ifdef SA_NOCLDSTOP
struct sigaction sigact;
sigact.sa_handler = SIG_DFL;
sigemptyset (&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction (sig, &sigact, NULL);
#else
signal (sig, SIG_DFL);
#endif
}
raise (sig);
}
int
main (int argc, char **argv)
{
register int i;
register struct pending *thispend;
unsigned int n_files;
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
#define N_ENTRIES(Array) (sizeof Array / sizeof *(Array))
assert (N_ENTRIES (color_indicator) + 1 == N_ENTRIES (indicator_name));
exit_status = 0;
dir_defaulted = 1;
print_dir_name = 1;
pending_dirs = 0;
i = decode_switches (argc, argv);
if (print_with_color)
parse_ls_color ();
if (print_with_color)
{
prep_non_filename_text ();
if (color_indicator[C_ORPHAN].string != NULL
|| (color_indicator[C_MISSING].string != NULL
&& format == long_format))
check_symlink_color = 1;
{
unsigned j;
static int const sigs[] = { SIGHUP, SIGINT, SIGPIPE,
SIGQUIT, SIGTERM, SIGTSTP };
unsigned nsigs = sizeof sigs / sizeof *sigs;
#ifdef SA_NOCLDSTOP
struct sigaction oldact, newact;
sigset_t caught_signals;
sigemptyset (&caught_signals);
for (j = 0; j < nsigs; j++)
sigaddset (&caught_signals, sigs[j]);
newact.sa_handler = sighandler;
newact.sa_mask = caught_signals;
newact.sa_flags = 0;
#endif
for (j = 0; j < nsigs; j++)
{
int sig = sigs[j];
#ifdef SA_NOCLDSTOP
sigaction (sig, NULL, &oldact);
if (oldact.sa_handler != SIG_IGN)
sigaction (sig, &newact, NULL);
#else
if (signal (sig, SIG_IGN) != SIG_IGN)
signal (sig, sighandler);
#endif
}
}
}
if (dereference == DEREF_UNDEFINED)
dereference = ((immediate_dirs
|| indicator_style == classify
|| format == long_format)
? DEREF_NEVER
: DEREF_COMMAND_LINE_SYMLINK_TO_DIR);
if (recursive)
{
active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, NULL,
dev_ino_hash,
dev_ino_compare,
dev_ino_free);
if (active_dir_set == NULL)
xalloc_die ();
obstack_init (&dev_ino_obstack);
}
format_needs_stat = sort_type == sort_time || sort_type == sort_size
|| format == long_format
|| dereference == DEREF_ALWAYS
|| print_block_size || print_inode;
format_needs_type = (format_needs_stat == 0
&& (recursive || print_with_color
|| indicator_style != none));
if (dired)
{
obstack_init (&dired_obstack);
obstack_init (&subdired_obstack);
}
nfiles = 100;
files = XMALLOC (struct fileinfo, nfiles);
files_index = 0;
clear_files ();
n_files = argc - i;
if (0 < n_files)
dir_defaulted = 0;
for (; i < argc; i++)
{
gobble_file (argv[i], unknown, 1, "");
}
if (dir_defaulted)
{
if (immediate_dirs)
gobble_file (".", directory, 1, "");
else
queue_directory (".", 0);
}
if (files_index)
{
sort_files ();
if (!immediate_dirs)
extract_dirs_from_files ("", 0);
}
if (files_index)
{
print_current_files ();
if (pending_dirs)
DIRED_PUTCHAR ('\n');
}
else if (n_files <= 1 && pending_dirs && pending_dirs->next == 0)
print_dir_name = 0;
while (pending_dirs)
{
thispend = pending_dirs;
pending_dirs = pending_dirs->next;
if (LOOP_DETECT)
{
if (thispend->name == NULL)
{
struct dev_ino di = dev_ino_pop ();
struct dev_ino *found = hash_delete (active_dir_set, &di);
assert (found);
dev_ino_free (found);
free_pending_ent (thispend);
continue;
}
}
print_dir (thispend->name, thispend->realname);
free_pending_ent (thispend);
print_dir_name = 1;
}
if (dired)
{
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)]);
}
if (print_with_color)
{
put_indicator (&color_indicator[C_LEFT]);
put_indicator (&color_indicator[C_RIGHT]);
}
if (LOOP_DETECT)
{
assert (hash_get_n_entries (active_dir_set) == 0);
hash_free (active_dir_set);
}
exit (exit_status);
}
static int
decode_switches (int argc, char **argv)
{
int c;
char *time_style_option = 0;
int sort_type_specified = 0;
qmark_funny_chars = 0;
switch (ls_mode)
{
case LS_MULTI_COL:
format = many_per_line;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LONG_FORMAT:
format = long_format;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LS:
if (isatty (STDOUT_FILENO))
{
format = many_per_line;
qmark_funny_chars = 1;
}
else
{
format = one_per_line;
qmark_funny_chars = 0;
}
break;
default:
abort ();
}
time_type = time_mtime;
sort_type = sort_name;
sort_reverse = 0;
numeric_ids = 0;
print_block_size = 0;
indicator_style = none;
print_inode = 0;
dereference = DEREF_UNDEFINED;
recursive = 0;
immediate_dirs = 0;
all_files = 0;
really_all_files = 0;
ignore_patterns = 0;
{
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;
}
line_length = 80;
{
char const *p = getenv ("COLUMNS");
if (p && *p)
{
long int tmp_long;
if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
&& 0 < tmp_long && tmp_long <= INT_MAX)
{
line_length = (int) tmp_long;
}
else
{
error (0, 0,
_("ignoring invalid width in environment variable COLUMNS: %s"),
quotearg (p));
}
}
}
#ifdef TIOCGWINSZ
{
struct winsize ws;
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
line_length = ws.ws_col;
}
#endif
{
char const *p;
tabsize = 8;
if (!getenv ("POSIXLY_CORRECT") && (p = getenv ("TABSIZE")))
{
long int tmp_long;
if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
&& 0 <= tmp_long && tmp_long <= INT_MAX)
{
tabsize = (int) tmp_long;
}
else
{
error (0, 0,
_("ignoring invalid tab size in environment variable TABSIZE: %s"),
quotearg (p));
}
}
}
while ((c = getopt_long (argc, argv,
"abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1",
long_options, NULL)) != -1)
{
switch (c)
{
case 0:
break;
case 'a':
all_files = 1;
really_all_files = 1;
break;
case 'b':
set_quoting_style (NULL, escape_quoting_style);
break;
case 'c':
time_type = time_ctime;
break;
case 'd':
immediate_dirs = 1;
break;
case 'f':
all_files = 1;
really_all_files = 1;
sort_type = sort_none;
sort_type_specified = 1;
if (format == long_format)
format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
print_block_size = 0;
print_with_color = 0;
break;
case 'g':
format = long_format;
print_owner = 0;
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 = 1;
break;
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 = 1;
format = long_format;
break;
case 'o':
format = long_format;
print_group = 0;
break;
case 'p':
indicator_style = file_type;
break;
case 'q':
qmark_funny_chars = 1;
break;
case 'r':
sort_reverse = 1;
break;
case 's':
print_block_size = 1;
break;
case 't':
sort_type = sort_time;
sort_type_specified = 1;
break;
case 'u':
time_type = time_atime;
break;
case 'v':
sort_type = sort_version;
sort_type_specified = 1;
break;
case 'w':
{
long int tmp_long;
if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
|| tmp_long <= 0 || tmp_long > INT_MAX)
error (EXIT_FAILURE, 0, _("invalid line width: %s"),
quotearg (optarg));
line_length = (int) tmp_long;
break;
}
case 'x':
format = horizontal;
break;
case 'A':
really_all_files = 0;
all_files = 1;
break;
case 'B':
add_ignore_pattern ("*~");
add_ignore_pattern (".*~");
break;
case 'C':
format = many_per_line;
break;
case 'D':
dired = 1;
break;
case 'F':
indicator_style = classify;
break;
case 'G':
print_group = 0;
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 = 1;
break;
case 'S':
sort_type = sort_size;
sort_type_specified = 1;
break;
case 'T':
{
long int tmp_long;
if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
|| tmp_long < 0 || tmp_long > INT_MAX)
error (EXIT_FAILURE, 0, _("invalid tab size: %s"),
quotearg (optarg));
tabsize = (int) tmp_long;
break;
}
case 'U':
sort_type = sort_none;
sort_type_specified = 1;
break;
case 'X':
sort_type = sort_extension;
sort_type_specified = 1;
break;
case '1':
if (format != long_format)
format = one_per_line;
break;
case AUTHOR_OPTION:
print_author = true;
break;
case SORT_OPTION:
sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
sort_type_specified = 1;
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
i = color_always;
print_with_color = (i == color_always
|| (i == color_if_tty
&& isatty (STDOUT_FILENO)));
if (print_with_color)
{
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 = 0;
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 (EXIT_FAILURE);
}
}
filename_quoting_options = clone_quoting_options (NULL);
if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
set_char_quoting (filename_quoting_options, ' ', 1);
if (indicator_style != none)
{
char const *p;
for (p = "*=@|" + (int) indicator_style - 1; *p; p++)
set_char_quoting (filename_quoting_options, *p, 1);
}
dirname_quoting_options = clone_quoting_options (NULL);
set_char_quoting (dirname_quoting_options, ':', 1);
if (dired && format != long_format)
dired = 0;
if ((time_type == time_ctime || time_type == time_atime)
&& !sort_type_specified && format != long_format)
{
sort_type = sort_time;
}
if (format == long_format)
{
char *style = time_style_option;
static char const posix_prefix[] = "posix-";
if (! style)
if (! (style = getenv ("TIME_STYLE")))
style = "posix-long-iso";
while (strncmp (style, posix_prefix, sizeof posix_prefix - 1) == 0)
{
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 (EXIT_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:
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))
{
unsigned int i;
for (i = 0; i < 2; i++)
long_time_format[i] =
dcgettext (NULL, long_time_format[i], LC_TIME);
}
}
}
return optind;
}
static int
get_funky_string (char **dest, const char **src, int equals_end)
{
int num;
int count;
enum {
ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
} state;
const char *p;
char *q;
p = *src;
q = *dest;
count = 0;
num = 0;
state = ST_GND;
while (state < ST_END)
{
switch (state)
{
case ST_GND:
switch (*p)
{
case ':':
case '\0':
state = ST_END;
break;
case '\\':
state = ST_BACKSLASH;
++p;
break;
case '^':
state = ST_CARET;
++p;
break;
case '=':
if (equals_end)
{
state = ST_END;
break;
}
default:
*(q++) = *(p++);
++count;
break;
}
break;
case ST_BACKSLASH:
switch (*p)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
state = ST_OCTAL;
num = *p - '0';
break;
case 'x':
case 'X':
state = ST_HEX;
num = 0;
break;
case 'a':
num = 7;
break;
case 'b':
num = '\b';
break;
case 'e':
num = 27;
break;
case 'f':
num = '\f';
break;
case 'n':
num = '\n';
break;
case 'r':
num = '\r';
break;
case 't':
num = '\t';
break;
case 'v':
num = '\v';
break;
case '?':
num = 127;
break;
case '_':
num = ' ';
break;
case '\0':
state = ST_ERROR;
break;
default:
num = *p;
break;
}
if (state == ST_BACKSLASH)
{
*(q++) = num;
++count;
state = ST_GND;
}
++p;
break;
case ST_OCTAL:
if (*p < '0' || *p > '7')
{
*(q++) = num;
++count;
state = ST_GND;
}
else
num = (num << 3) + (*(p++) - '0');
break;
case ST_HEX:
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:
state = ST_GND;
if (*p >= '@' && *p <= '~')
{
*(q++) = *(p++) & 037;
++count;
}
else if (*p == '?')
{
*(q++) = 127;
++count;
}
else
state = ST_ERROR;
break;
default:
abort ();
}
}
*dest = q;
*src = p;
return state == ST_ERROR ? -1 : count;
}
static void
parse_ls_color (void)
{
const char *p;
char *buf;
int state;
int ind_no;
char label[3];
struct color_ext_type *ext;
if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
return;
ext = NULL;
strcpy (label, "??");
buf = color_buf = xstrdup (p);
state = 1;
while (state > 0)
{
switch (state)
{
case 1:
switch (*p)
{
case ':':
++p;
break;
case '*':
ext = XMALLOC (struct color_ext_type, 1);
ext->next = color_ext_list;
color_ext_list = ext;
++p;
ext->ext.string = buf;
state = (ext->ext.len =
get_funky_string (&buf, &p, 1)) < 0 ? -1 : 4;
break;
case '\0':
state = 0;
break;
default:
label[0] = *(p++);
state = 2;
break;
}
break;
case 2:
if (*p)
{
label[1] = *(p++);
state = 3;
}
else
state = -1;
break;
case 3:
state = -1;
if (*(p++) == '=')
{
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 = ((color_indicator[ind_no].len =
get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1);
break;
}
}
if (state == -1)
error (0, 0, _("unrecognized prefix: %s"), quotearg (label));
}
break;
case 4:
if (*(p++) == '=')
{
ext->seq.string = buf;
state = (ext->seq.len =
get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1;
}
else
state = -1;
break;
}
}
if (state < 0)
{
struct color_ext_type *e;
struct color_ext_type *e2;
error (0, 0,
_("unparsable value for LS_COLORS environment variable"));
free (color_buf);
for (e = color_ext_list; e != NULL; )
{
e2 = e;
e = e->next;
free (e2);
}
print_with_color = 0;
}
if (color_indicator[C_LINK].len == 6
&& !strncmp (color_indicator[C_LINK].string, "target", 6))
color_symlink_as_referent = 1;
}
static void
queue_directory (const char *name, const char *realname)
{
struct pending *new;
new = XMALLOC (struct pending, 1);
new->realname = realname ? xstrdup (realname) : NULL;
new->name = name ? xstrdup (name) : NULL;
new->next = pending_dirs;
pending_dirs = new;
}
static void
print_dir (const char *name, const char *realname)
{
register DIR *dirp;
register struct dirent *next;
register uintmax_t total_blocks = 0;
static int first = 1;
errno = 0;
dirp = opendir (name);
if (!dirp)
{
error (0, errno, "%s", quotearg_colon (name));
exit_status = 1;
return;
}
if (LOOP_DETECT)
{
struct stat dir_stat;
int fd = dirfd (dirp);
if ((0 <= fd
? fstat (fd, &dir_stat)
: stat (name, &dir_stat)) < 0)
{
error (0, errno, _("cannot determine device and inode of %s"),
quotearg_colon (name));
exit_status = 1;
return;
}
if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
{
error (0, 0, _("not listing already-listed directory: %s"),
quotearg_colon (name));
return;
}
DEV_INO_PUSH (dir_stat.st_dev, dir_stat.st_ino);
}
clear_files ();
while (1)
{
errno = 0;
if ((next = readdir (dirp)) == NULL)
{
if (errno)
{
int e = errno;
closedir (dirp);
errno = e;
dirp = NULL;
}
break;
}
if (file_interesting (next))
{
enum filetype type = unknown;
#if HAVE_STRUCT_DIRENT_D_TYPE
if (next->d_type == DT_BLK
|| next->d_type == DT_CHR
|| next->d_type == DT_DIR
|| next->d_type == DT_FIFO
|| next->d_type == DT_LNK
|| next->d_type == DT_REG
|| next->d_type == DT_SOCK)
type = next->d_type;
#endif
total_blocks += gobble_file (next->d_name, type, 0, name);
}
}
if (dirp == NULL || CLOSEDIR (dirp))
{
error (0, errno, _("reading directory %s"), quotearg_colon (name));
exit_status = 1;
}
sort_files ();
if (recursive)
extract_dirs_from_files (name, 1);
if (recursive || print_dir_name)
{
if (!first)
DIRED_PUTCHAR ('\n');
first = 0;
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);
}
if (format == long_format || print_block_size)
{
const char *p;
char buf[LONGEST_HUMAN_READABLE + 1];
DIRED_INDENT ();
p = _("total");
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR (' ');
p = human_readable (total_blocks, buf, human_output_opts,
ST_NBLOCKSIZE, output_block_size);
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR ('\n');
}
if (files_index)
print_current_files ();
}
static void
add_ignore_pattern (const char *pattern)
{
register struct ignore_pattern *ignore;
ignore = XMALLOC (struct ignore_pattern, 1);
ignore->pattern = pattern;
ignore->next = ignore_patterns;
ignore_patterns = ignore;
}
static int
file_interesting (const struct dirent *next)
{
register struct ignore_pattern *ignore;
for (ignore = ignore_patterns; ignore; ignore = ignore->next)
if (fnmatch (ignore->pattern, next->d_name, FNM_PERIOD) == 0)
return 0;
if (really_all_files
|| next->d_name[0] != '.'
|| (all_files
&& next->d_name[1] != '\0'
&& (next->d_name[1] != '.' || next->d_name[2] != '\0')))
return 1;
return 0;
}
static void
clear_files (void)
{
register int i;
for (i = 0; i < files_index; i++)
{
free (files[i].name);
if (files[i].linkname)
free (files[i].linkname);
}
files_index = 0;
block_size_size = 4;
}
static uintmax_t
gobble_file (const char *name, enum filetype type, int explicit_arg,
const char *dirname)
{
register uintmax_t blocks;
register char *path;
if (files_index == nfiles)
{
nfiles *= 2;
files = XREALLOC (files, struct fileinfo, nfiles);
}
files[files_index].linkname = 0;
files[files_index].linkmode = 0;
files[files_index].linkok = 0;
if (explicit_arg
|| format_needs_stat
|| (format_needs_type
&& (type == unknown
|| type == symbolic_link
|| (type == normal && (indicator_style == classify
|| print_with_color)))))
{
int err;
if (name[0] == '/' || dirname[0] == 0)
path = (char *) name;
else
{
path = (char *) alloca (strlen (name) + strlen (dirname) + 2);
attach (path, dirname, name);
}
switch (dereference)
{
case DEREF_ALWAYS:
err = stat (path, &files[files_index].stat);
break;
case DEREF_COMMAND_LINE_ARGUMENTS:
case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
if (explicit_arg)
{
int need_lstat;
err = stat (path, &files[files_index].stat);
if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
break;
need_lstat = (err < 0
? errno == ENOENT
: ! S_ISDIR (files[files_index].stat.st_mode));
if (!need_lstat)
break;
}
default:
err = lstat (path, &files[files_index].stat);
break;
}
if (err < 0)
{
error (0, errno, "%s", quotearg_colon (path));
exit_status = 1;
return 0;
}
#if HAVE_ACL
if (format == long_format)
{
int n = file_has_acl (path, &files[files_index].stat);
files[files_index].have_acl = (0 < n);
if (n < 0)
error (0, errno, "%s", quotearg_colon (path));
}
#endif
if (S_ISLNK (files[files_index].stat.st_mode)
&& (format == long_format || check_symlink_color))
{
char *linkpath;
struct stat linkstats;
get_link_name (path, &files[files_index]);
linkpath = make_link_path (path, files[files_index].linkname);
if (linkpath
&& (indicator_style != none || check_symlink_color)
&& stat (linkpath, &linkstats) == 0)
{
files[files_index].linkok = 1;
if (!explicit_arg || format == long_format
|| !S_ISDIR (linkstats.st_mode))
{
files[files_index].linkmode = linkstats.st_mode;
files[files_index].linkok = 1;
}
}
if (linkpath)
free (linkpath);
}
if (S_ISLNK (files[files_index].stat.st_mode))
files[files_index].filetype = symbolic_link;
else if (S_ISDIR (files[files_index].stat.st_mode))
{
if (explicit_arg && !immediate_dirs)
files[files_index].filetype = arg_directory;
else
files[files_index].filetype = directory;
}
else
files[files_index].filetype = normal;
blocks = ST_NBLOCKS (files[files_index].stat);
{
char buf[LONGEST_HUMAN_READABLE + 1];
int len = strlen (human_readable (blocks, buf, human_output_opts,
ST_NBLOCKSIZE, output_block_size));
if (block_size_size < len)
block_size_size = len < 7 ? len : 7;
}
}
else
{
files[files_index].filetype = type;
#if HAVE_STRUCT_DIRENT_D_TYPE
files[files_index].stat.st_mode = DTTOIF (type);
#endif
blocks = 0;
}
files[files_index].name = xstrdup (name);
files_index++;
return blocks;
}
#ifdef S_ISLNK
static void
get_link_name (const char *filename, struct fileinfo *f)
{
f->linkname = xreadlink (filename);
if (f->linkname == NULL)
{
error (0, errno, _("cannot read symbolic link %s"),
quotearg_colon (filename));
exit_status = 1;
}
}
static char *
make_link_path (const char *path, const char *linkname)
{
char *linkbuf;
size_t bufsiz;
if (linkname == 0)
return 0;
if (*linkname == '/')
return xstrdup (linkname);
linkbuf = strrchr (path, '/');
if (linkbuf == 0)
return xstrdup (linkname);
bufsiz = linkbuf - path + 1;
linkbuf = xmalloc (bufsiz + strlen (linkname) + 1);
strncpy (linkbuf, path, bufsiz);
strcpy (linkbuf + bufsiz, linkname);
return linkbuf;
}
#endif
static int
basename_is_dot_or_dotdot (const char *name)
{
char const *base = base_name (name);
return DOT_OR_DOTDOT (base);
}
static void
extract_dirs_from_files (const char *dirname, int ignore_dot_and_dot_dot)
{
register int i, j;
if (*dirname && LOOP_DETECT)
{
queue_directory (NULL, dirname);
}
for (i = files_index - 1; i >= 0; i--)
if ((files[i].filetype == directory || files[i].filetype == arg_directory)
&& (!ignore_dot_and_dot_dot
|| !basename_is_dot_or_dotdot (files[i].name)))
{
if (files[i].name[0] == '/' || dirname[0] == 0)
{
queue_directory (files[i].name, files[i].linkname);
}
else
{
char *path = path_concat (dirname, files[i].name, NULL);
queue_directory (path, files[i].linkname);
free (path);
}
if (files[i].filetype == arg_directory)
free (files[i].name);
}
for (i = 0, j = 0; i < files_index; i++)
if (files[i].filetype != arg_directory)
files[j++] = files[i];
files_index = j;
}
static jmp_buf failed_strcoll;
static int
xstrcoll (char const *a, char const *b)
{
int diff;
errno = 0;
diff = strcoll (a, b);
if (errno)
{
error (0, errno, _("cannot compare file names %s and %s"),
quote_n (0, a), quote_n (1, b));
exit_status = 1;
longjmp (failed_strcoll, 1);
}
return diff;
}
typedef void const *V;
static inline int
cmp_ctime (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
int diff = CTIME_CMP (b->stat, a->stat);
return diff ? diff : cmp (a->name, b->name);
}
static int compare_ctime (V a, V b) { return cmp_ctime (a, b, xstrcoll); }
static int compstr_ctime (V a, V b) { return cmp_ctime (a, b, strcmp); }
static int rev_cmp_ctime (V a, V b) { return compare_ctime (b, a); }
static int rev_str_ctime (V a, V b) { return compstr_ctime (b, a); }
static inline int
cmp_mtime (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
int diff = MTIME_CMP (b->stat, a->stat);
return diff ? diff : cmp (a->name, b->name);
}
static int compare_mtime (V a, V b) { return cmp_mtime (a, b, xstrcoll); }
static int compstr_mtime (V a, V b) { return cmp_mtime (a, b, strcmp); }
static int rev_cmp_mtime (V a, V b) { return compare_mtime (b, a); }
static int rev_str_mtime (V a, V b) { return compstr_mtime (b, a); }
static inline int
cmp_atime (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
int diff = ATIME_CMP (b->stat, a->stat);
return diff ? diff : cmp (a->name, b->name);
}
static int compare_atime (V a, V b) { return cmp_atime (a, b, xstrcoll); }
static int compstr_atime (V a, V b) { return cmp_atime (a, b, strcmp); }
static int rev_cmp_atime (V a, V b) { return compare_atime (b, a); }
static int rev_str_atime (V a, V b) { return compstr_atime (b, a); }
static inline int
cmp_size (struct fileinfo const *a, struct fileinfo const *b,
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);
}
static int compare_size (V a, V b) { return cmp_size (a, b, xstrcoll); }
static int compstr_size (V a, V b) { return cmp_size (a, b, strcmp); }
static int rev_cmp_size (V a, V b) { return compare_size (b, a); }
static int rev_str_size (V a, V b) { return compstr_size (b, a); }
static inline int
cmp_version (struct fileinfo const *a, struct fileinfo const *b)
{
return strverscmp (a->name, b->name);
}
static int compare_version (V a, V b) { return cmp_version (a, b); }
static int rev_cmp_version (V a, V b) { return compare_version (b, a); }
static inline int
cmp_name (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
return cmp (a->name, b->name);
}
static int compare_name (V a, V b) { return cmp_name (a, b, xstrcoll); }
static int compstr_name (V a, V b) { return cmp_name (a, b, strcmp); }
static int rev_cmp_name (V a, V b) { return compare_name (b, a); }
static int rev_str_name (V a, V b) { return compstr_name (b, a); }
static inline int
cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
char const *base1 = strrchr (a->name, '.');
char const *base2 = strrchr (b->name, '.');
int diff = cmp (base1 ? base1 : "", base2 ? base2 : "");
return diff ? diff : cmp (a->name, b->name);
}
static int compare_extension (V a, V b) { return cmp_extension (a, b, xstrcoll); }
static int compstr_extension (V a, V b) { return cmp_extension (a, b, strcmp); }
static int rev_cmp_extension (V a, V b) { return compare_extension (b, a); }
static int rev_str_extension (V a, V b) { return compstr_extension (b, a); }
static void
sort_files (void)
{
int (*func) (V, V);
switch (sort_type)
{
case sort_none:
return;
case sort_time:
switch (time_type)
{
case time_ctime:
func = sort_reverse ? rev_cmp_ctime : compare_ctime;
break;
case time_mtime:
func = sort_reverse ? rev_cmp_mtime : compare_mtime;
break;
case time_atime:
func = sort_reverse ? rev_cmp_atime : compare_atime;
break;
default:
abort ();
}
break;
case sort_name:
func = sort_reverse ? rev_cmp_name : compare_name;
break;
case sort_extension:
func = sort_reverse ? rev_cmp_extension : compare_extension;
break;
case sort_size:
func = sort_reverse ? rev_cmp_size : compare_size;
break;
case sort_version:
func = sort_reverse ? rev_cmp_version : compare_version;
break;
default:
abort ();
}
if (setjmp (failed_strcoll))
{
switch (sort_type)
{
case sort_time:
switch (time_type)
{
case time_ctime:
func = sort_reverse ? rev_str_ctime : compstr_ctime;
break;
case time_mtime:
func = sort_reverse ? rev_str_mtime : compstr_mtime;
break;
case time_atime:
func = sort_reverse ? rev_str_atime : compstr_atime;
break;
default:
abort ();
}
break;
case sort_name:
func = sort_reverse ? rev_str_name : compstr_name;
break;
case sort_extension:
func = sort_reverse ? rev_str_extension : compstr_extension;
break;
case sort_size:
func = sort_reverse ? rev_str_size : compstr_size;
break;
default:
abort ();
}
}
qsort (files, files_index, sizeof (struct fileinfo), func);
}
static void
print_current_files (void)
{
register int i;
switch (format)
{
case one_per_line:
for (i = 0; i < files_index; i++)
{
print_file_name_and_frills (files + i);
putchar ('\n');
}
break;
case many_per_line:
init_column_info ();
print_many_per_line ();
break;
case horizontal:
init_column_info ();
print_horizontal ();
break;
case with_commas:
print_with_commas ();
break;
case long_format:
for (i = 0; i < files_index; i++)
{
print_long_format (files + i);
DIRED_PUTCHAR ('\n');
}
break;
}
}
static int
long_time_expected_width (void)
{
static int width = -1;
if (width < 0)
{
time_t epoch = 0;
struct tm const *tm = localtime (&epoch);
char const *fmt = long_time_format[0];
char initbuf[100];
char *buf = initbuf;
size_t bufsize = sizeof initbuf;
size_t len;
for (;;)
{
*buf = '\1';
len = nstrftime (buf, bufsize, fmt, tm, 0, 0);
if (len || ! *buf)
break;
buf = alloca (bufsize *= 2);
}
width = mbsnwidth (buf, len, 0);
if (width < 0)
width = 0;
}
return width;
}
static void
get_current_time (void)
{
#if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
{
struct timespec timespec;
if (clock_gettime (CLOCK_REALTIME, ×pec) == 0)
{
current_time = timespec.tv_sec;
current_time_ns = timespec.tv_nsec;
return;
}
}
#endif
#if HAVE_GETTIMEOFDAY
{
struct timeval timeval;
if (gettimeofday (&timeval, NULL) == 0)
{
current_time = timeval.tv_sec;
current_time_ns = timeval.tv_usec * 1000 + 999;
return;
}
}
#endif
current_time = time (NULL);
current_time_ns = 999999999;
}
static size_t
format_user (char *buffer, uid_t u)
{
char const *name = (numeric_ids ? NULL : getuser (u));
if (name)
sprintf (buffer, "%-8s ", name);
else
sprintf (buffer, "%-8lu ", (unsigned long) u);
return strlen (buffer);
}
static void
print_long_format (const struct fileinfo *f)
{
char modebuf[12];
char init_bigbuf
[LONGEST_HUMAN_READABLE + 1
+ LONGEST_HUMAN_READABLE + 1
+ sizeof (modebuf) - 1 + 1
+ LONGEST_HUMAN_READABLE + 1
+ ID_LENGTH_MAX + 1
+ ID_LENGTH_MAX + 1
+ ID_LENGTH_MAX + 1
+ LONGEST_HUMAN_READABLE + 1
+ LONGEST_HUMAN_READABLE + 1
+ 35 + 1
];
char *buf = init_bigbuf;
size_t bufsize = sizeof (init_bigbuf);
size_t s;
char *p;
time_t when;
int when_ns IF_LINT (= 0);
struct tm *when_local;
mode_string (ST_DM_MODE (f->stat), modebuf);
modebuf[10] = (FILE_HAS_ACL (f) ? '+' : ' ');
modebuf[11] = '\0';
switch (time_type)
{
case time_ctime:
when = f->stat.st_ctime;
when_ns = TIMESPEC_NS (f->stat.st_ctim);
break;
case time_mtime:
when = f->stat.st_mtime;
when_ns = TIMESPEC_NS (f->stat.st_mtim);
break;
case time_atime:
when = f->stat.st_atime;
when_ns = TIMESPEC_NS (f->stat.st_atim);
break;
}
p = buf;
if (print_inode)
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
sprintf (p, "%*s ", INODE_DIGITS, umaxtostr (f->stat.st_ino, hbuf));
p += strlen (p);
}
if (print_block_size)
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
sprintf (p, "%*s ", block_size_size,
human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
ST_NBLOCKSIZE, output_block_size));
p += strlen (p);
}
sprintf (p, "%s %3lu ", modebuf, (unsigned long) f->stat.st_nlink);
p += strlen (p);
if (print_owner)
p += format_user (p, f->stat.st_uid);
if (print_group)
{
char const *group_name = (numeric_ids ? NULL : getgroup (f->stat.st_gid));
if (group_name)
sprintf (p, "%-8s ", group_name);
else
sprintf (p, "%-8lu ", (unsigned long) f->stat.st_gid);
p += strlen (p);
}
if (print_author)
p += format_user (p, f->stat.st_author);
if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
sprintf (p, "%3lu, %3lu ",
(unsigned long) major (f->stat.st_rdev),
(unsigned long) minor (f->stat.st_rdev));
else
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
uintmax_t size = f->stat.st_size;
size += (f->stat.st_size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);
sprintf (p, "%8s ",
human_readable (size, hbuf, human_output_opts,
1, file_output_block_size));
}
p += strlen (p);
if ((when_local = localtime (&when)))
{
time_t six_months_ago;
int recent;
char const *fmt;
if (current_time < when
|| (current_time == when && current_time_ns < when_ns))
{
get_current_time ();
}
six_months_ago = current_time - 31556952 / 2;
recent = (six_months_ago <= when
&& (when < current_time
|| (when == current_time && when_ns <= current_time_ns)));
fmt = long_time_format[recent];
for (;;)
{
char *newbuf;
*p = '\1';
s = nstrftime (p, buf + bufsize - p - 1, fmt,
when_local, 0, when_ns);
if (s || ! *p)
break;
newbuf = alloca (bufsize *= 2);
memcpy (newbuf, buf, p - buf);
p = newbuf + (p - buf);
buf = newbuf;
}
p += s;
*p++ = ' ';
*p = '\0';
}
else
{
char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
sprintf (p, "%*s ", long_time_expected_width (),
(TYPE_SIGNED (time_t)
? imaxtostr (when, hbuf)
: umaxtostr (when, hbuf)));
p += strlen (p);
}
DIRED_INDENT ();
DIRED_FPUTS (buf, stdout, p - buf);
print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
&dired_obstack);
if (f->filetype == symbolic_link)
{
if (f->linkname)
{
DIRED_FPUTS_LITERAL (" -> ", stdout);
print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1,
NULL);
if (indicator_style != none)
print_type_indicator (f->linkmode);
}
}
else if (indicator_style != none)
print_type_indicator (f->stat.st_mode);
}
static size_t
quote_name (FILE *out, const char *name, struct quoting_options const *options,
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);
if (len < sizeof smallbuf)
buf = smallbuf;
else
{
buf = (char *) alloca (len + 1);
quotearg_buffer (buf, len + 1, name, -1, options);
}
if (qmark_funny_chars)
{
#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 '~':
*q++ = *p++;
displayed_width += 1;
break;
default:
{
mbstate_t mbstate;
memset (&mbstate, 0, sizeof mbstate);
do
{
wchar_t wc;
size_t bytes;
int w;
bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
if (bytes == (size_t) -1)
{
p++;
*q++ = '?';
displayed_width += 1;
break;
}
if (bytes == (size_t) -2)
{
p = plimit;
*q++ = '?';
displayed_width += 1;
break;
}
if (bytes == 0)
bytes = 1;
w = wcwidth (wc);
if (w >= 0)
{
for (; bytes > 0; --bytes)
*q++ = *p++;
displayed_width += w;
}
else
{
p += bytes;
*q++ = '?';
displayed_width += 1;
}
}
while (! mbsinit (&mbstate));
}
break;
}
len = q - buf;
}
else
#endif
{
char *p = buf;
char const *plimit = buf + len;
while (p < plimit)
{
if (! ISPRINT ((unsigned char) *p))
*p = '?';
p++;
}
displayed_width = len;
}
}
else if (width != NULL)
{
#if HAVE_MBRTOWC
if (MB_CUR_MAX > 1)
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 ((unsigned char) *p))
displayed_width++;
p++;
}
}
}
if (out != NULL)
fwrite (buf, 1, len, out);
if (width != NULL)
*width = displayed_width;
return len;
}
static void
print_name_with_quoting (const char *p, mode_t mode, int linkok,
struct obstack *stack)
{
if (print_with_color)
print_color_indicator (p, mode, linkok);
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
dired_pos += quote_name (stdout, p, filename_quoting_options, NULL);
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
if (print_with_color)
prep_non_filename_text ();
}
static void
prep_non_filename_text (void)
{
if (color_indicator[C_END].string != NULL)
put_indicator (&color_indicator[C_END]);
else
{
put_indicator (&color_indicator[C_LEFT]);
put_indicator (&color_indicator[C_NORM]);
put_indicator (&color_indicator[C_RIGHT]);
}
}
static void
print_file_name_and_frills (const struct fileinfo *f)
{
char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
if (print_inode)
printf ("%*s ", INODE_DIGITS, umaxtostr (f->stat.st_ino, buf));
if (print_block_size)
printf ("%*s ", block_size_size,
human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
ST_NBLOCKSIZE, output_block_size));
print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, NULL);
if (indicator_style != none)
print_type_indicator (f->stat.st_mode);
}
static void
print_type_indicator (mode_t mode)
{
int c;
if (S_ISREG (mode))
{
if (indicator_style == classify && (mode & S_IXUGO))
c ='*';
else
c = 0;
}
else
{
if (S_ISDIR (mode))
c = '/';
else if (S_ISLNK (mode))
c = '@';
else if (S_ISFIFO (mode))
c = '|';
else if (S_ISSOCK (mode))
c = '=';
else if (S_ISDOOR (mode))
c = '>';
else
c = 0;
}
if (c)
DIRED_PUTCHAR (c);
}
static void
print_color_indicator (const char *name, mode_t mode, int linkok)
{
int type = C_FILE;
struct color_ext_type *ext;
size_t len;
if (linkok == -1 && color_indicator[C_MISSING].string != NULL)
{
ext = NULL;
type = C_MISSING;
}
else
{
if (S_ISDIR (mode))
type = C_DIR;
else if (S_ISLNK (mode))
type = ((!linkok && color_indicator[C_ORPHAN].string)
? C_ORPHAN : C_LINK);
else if (S_ISFIFO (mode))
type = C_FIFO;
else if (S_ISSOCK (mode))
type = C_SOCK;
else if (S_ISBLK (mode))
type = C_BLK;
else if (S_ISCHR (mode))
type = C_CHR;
else if (S_ISDOOR (mode))
type = C_DOOR;
if (type == C_FILE && (mode & S_IXUGO) != 0)
type = C_EXEC;
ext = NULL;
if (type == C_FILE)
{
len = strlen (name);
name += len;
for (ext = color_ext_list; ext != NULL; ext = ext->next)
{
if ((size_t) ext->ext.len <= len
&& strncmp (name - ext->ext.len, ext->ext.string,
ext->ext.len) == 0)
break;
}
}
}
put_indicator (&color_indicator[C_LEFT]);
put_indicator (ext ? &(ext->seq) : &color_indicator[type]);
put_indicator (&color_indicator[C_RIGHT]);
}
static void
put_indicator (const struct bin_str *ind)
{
register int i;
register const char *p;
p = ind->string;
for (i = ind->len; i > 0; --i)
putchar (*(p++));
}
static int
put_indicator_direct (const struct bin_str *ind)
{
size_t len;
if (ind->len <= 0)
return 0;
len = ind->len;
return (full_write (STDOUT_FILENO, ind->string, len) != len);
}
static int
length_of_file_name_and_frills (const struct fileinfo *f)
{
register int len = 0;
size_t name_width;
if (print_inode)
len += INODE_DIGITS + 1;
if (print_block_size)
len += 1 + block_size_size;
quote_name (NULL, f->name, filename_quoting_options, &name_width);
len += name_width;
if (indicator_style != none)
{
mode_t filetype = f->stat.st_mode;
if (S_ISREG (filetype))
{
if (indicator_style == classify
&& (f->stat.st_mode & S_IXUGO))
len += 1;
}
else if (S_ISDIR (filetype)
|| S_ISLNK (filetype)
|| S_ISFIFO (filetype)
|| S_ISSOCK (filetype)
|| S_ISDOOR (filetype)
)
len += 1;
}
return len;
}
static void
print_many_per_line (void)
{
struct column_info *line_fmt;
int filesno;
int row;
int max_name_length;
int name_length;
int pos;
int cols;
int rows;
int max_cols;
max_cols = max_idx > files_index ? files_index : max_idx;
for (filesno = 0; filesno < files_index; ++filesno)
{
int i;
name_length = length_of_file_name_and_frills (files + filesno);
for (i = 0; i < max_cols; ++i)
{
if (column_info[i].valid_len)
{
int idx = filesno / ((files_index + i) / (i + 1));
int real_length = name_length + (idx == i ? 0 : 2);
if (real_length > column_info[i].col_arr[idx])
{
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;
}
}
}
}
for (cols = max_cols; cols > 1; --cols)
{
if (column_info[cols - 1].valid_len)
break;
}
line_fmt = &column_info[cols - 1];
rows = files_index / cols + (files_index % cols != 0);
for (row = 0; row < rows; row++)
{
int col = 0;
filesno = row;
pos = 0;
while (1)
{
print_file_name_and_frills (files + filesno);
name_length = length_of_file_name_and_frills (files + filesno);
max_name_length = line_fmt->col_arr[col++];
filesno += rows;
if (filesno >= files_index)
break;
indent (pos + name_length, pos + max_name_length);
pos += max_name_length;
}
putchar ('\n');
}
}
static void
print_horizontal (void)
{
struct column_info *line_fmt;
int filesno;
int max_name_length;
int name_length;
int cols;
int pos;
int max_cols;
max_cols = max_idx > files_index ? files_index : max_idx;
max_name_length = 0;
for (filesno = 0; filesno < files_index; ++filesno)
{
int i;
name_length = length_of_file_name_and_frills (files + filesno);
for (i = 0; i < max_cols; ++i)
{
if (column_info[i].valid_len)
{
int idx = filesno % (i + 1);
int real_length = name_length + (idx == i ? 0 : 2);
if (real_length > column_info[i].col_arr[idx])
{
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;
}
}
}
}
for (cols = max_cols; cols > 1; --cols)
{
if (column_info[cols - 1].valid_len)
break;
}
line_fmt = &column_info[cols - 1];
pos = 0;
print_file_name_and_frills (files);
name_length = length_of_file_name_and_frills (files);
max_name_length = line_fmt->col_arr[0];
for (filesno = 1; filesno < files_index; ++filesno)
{
int col = filesno % cols;
if (col == 0)
{
putchar ('\n');
pos = 0;
}
else
{
indent (pos + name_length, pos + max_name_length);
pos += max_name_length;
}
print_file_name_and_frills (files + filesno);
name_length = length_of_file_name_and_frills (files + filesno);
max_name_length = line_fmt->col_arr[col];
}
putchar ('\n');
}
static void
print_with_commas (void)
{
int filesno;
int pos, old_pos;
pos = 0;
for (filesno = 0; filesno < files_index; filesno++)
{
old_pos = pos;
pos += length_of_file_name_and_frills (files + filesno);
if (filesno + 1 < files_index)
pos += 2;
if (old_pos != 0 && pos >= line_length)
{
putchar ('\n');
pos -= old_pos;
}
print_file_name_and_frills (files + filesno);
if (filesno + 1 < files_index)
{
putchar (',');
putchar (' ');
}
}
putchar ('\n');
}
static void
indent (int from, int to)
{
while (from < to)
{
if (tabsize > 0 && to / tabsize > (from + 1) / tabsize)
{
putchar ('\t');
from += tabsize - from % tabsize;
}
else
{
putchar (' ');
from++;
}
}
}
static void
attach (char *dest, const char *dirname, const char *name)
{
const char *dirnamep = dirname;
if (dirname[0] != '.' || dirname[1] != 0)
{
while (*dirnamep)
*dest++ = *dirnamep++;
if (dirnamep > dirname && dirnamep[-1] != '/')
*dest++ = '/';
}
while (*name)
*dest++ = *name++;
*dest = 0;
}
static void
init_column_info (void)
{
int i;
int allocate = 0;
max_idx = line_length / MIN_COLUMN_WIDTH;
if (max_idx == 0)
max_idx = 1;
if (column_info == NULL)
{
column_info = XMALLOC (struct column_info, max_idx);
allocate = 1;
}
for (i = 0; i < max_idx; ++i)
{
int j;
column_info[i].valid_len = 1;
column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
if (allocate)
column_info[i].col_arr = XMALLOC (int, i + 1);
for (j = 0; j <= i; ++j)
column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
}
}
void
usage (int status)
{
if (status != 0)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
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 -cftuSUX nor --sort.\n\
\n\
"), stdout);
fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (_("\
-a, --all do not hide entries starting with .\n\
-A, --almost-all do not list implied . and ..\n\
--author print the author of each file\n\
-b, --escape print octal escapes for nongraphic characters\n\
"), stdout);
fputs (_("\
--block-size=SIZE use SIZE-byte blocks\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\
"), 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\
-D, --dired generate output designed for Emacs' dired mode\n\
"), stdout);
fputs (_("\
-f do not sort, enable -aU, disable -lst\n\
-F, --classify append indicator (one of */=@|) to entries\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\
"), stdout);
fputs (_("\
-g like -l, but do not list owner\n\
-G, --no-group inhibit display of group information\n\
-h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
--si likewise, but use powers of 1000 not 1024\n\
-H, --dereference-command-line\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\
"), stdout);
fputs (_("\
--indicator-style=WORD append indicator with style WORD to entry names:\n\
none (default), classify (-F), file-type (-p)\n\
-i, --inode print index number of each file\n\
-I, --ignore=PATTERN do not list implied entries matching shell PATTERN\n\
-k like --block-size=1K\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\
"), stdout);
fputs (_("\
-n, --numeric-uid-gid like -l, but list numeric UIDs and GIDs\n\
-N, --literal print raw entry names (don't treat e.g. control\n\
characters specially)\n\
-o like -l, but do not list group information\n\
-p, --file-type append indicator (one of /=@|) to entries\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, --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\
"), stdout);
fputs (_("\
-r, --reverse reverse order while sorting\n\
-R, --recursive list subdirectories recursively\n\
-s, --size print size of each file, in blocks\n\
"), stdout);
fputs (_("\
-S sort by file size\n\
--sort=WORD extension -X, none -U, size -S, time -t,\n\
version -v\n\
status -c, time -t, atime -u, access -u, use -u\n\
--time=WORD show time as WORD instead of modification time:\n\
atime, access, use, ctime or status; use\n\
specified time as sort key if --sort=time\n\
"), stdout);
fputs (_("\
--time-style=STYLE 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\
-t sort by modification time\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 do not sort; list entries in directory order\n\
-v sort by version\n\
"), stdout);
fputs (_("\
-w, --width=COLS assume screen width instead of current value\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\
"), 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 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
"), stdout);
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).\n\
"), stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
exit (status);
}