/* `dir', `vdir' and `ls' directory listing programs for GNU.
Copyright (C) 85, 88, 90, 91, 1995-2004 Free Software Foundation, Inc.
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* 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.
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.
If ls_mode is LS_LS,
the output format depends on whether the output
device is a terminal.
This is for the `ls' program. */
/* Written by Richard Stallman and David MacKenzie. */
/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
Flaherty <dennisf@denix.elk.miles.com> based on original patches by
Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */
#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>
/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth(). */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif
/* Get iswprint(). */
#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
/* If wcwidth() doesn't exist, assume all printable characters have
width 1. */
#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 "Richard Stallman", "David MacKenzie"
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
/* Return an int indicating the result of comparing two integers.
Subtracting doesn't always work, due to overflow. */
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))
/* Arrange to make lstat calls go through the wrapper function
on systems with an lstat function that does not dereference symlinks
that are specified with a trailing slash. */
#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) /* empty */
#endif
#if ! HAVE_STRUCT_STAT_ST_AUTHOR
# define st_author st_uid
#endif
/* Cray/Unicos DMF: use the file's migrated, not real, status */
#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
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
{
/* The file name. */
char *name;
struct stat stat;
/* For symbolic link, name of the file linked to, otherwise zero. */
char *linkname;
/* For symbolic link and long listing, st_mode of file linked to, otherwise
zero. */
mode_t linkmode;
/* For symbolic link and color printing, 1 if linked-to file
exists, otherwise 0. */
int linkok;
enum filetype filetype;
#if HAVE_ACL
/* For long listings, true if the file has an access control list. */
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
/* Null is a valid character in a color indicator (think about Epson
printers, for example) so we have to use a length/buffer string
type. */
struct bin_str
{
size_t len; /* Number of bytes */
const char *string; /* Pointer to the same */
};
#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 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 (size_t from, size_t to);
static size_t calculate_columns (bool by_columns);
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 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,
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);
/* The name the program was run with, stripped of any leading path. */
char *program_name;
/* 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
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
entries, and it is removed from the set just after ls has finished
processing it. This set is used solely