* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)ls.c 5.56 (Berkeley) %G%";
void display
__P((int, FTSENT
*, FTSENT
*));
int mastercmp
__P((const FTSENT
**, const FTSENT
**));
void traverse
__P((int, char **, int));
static void (*printfcn
) __P((FTSENT
*, int, u_long
, int));
static int (*sortfcn
) __P((const FTSENT
*, const FTSENT
*));
int termwidth
= 80; /* default terminal width */
int f_accesstime
; /* use time of last access */
int f_column
; /* columnated format */
int f_group
; /* show group ownership of a file */
int f_flags
; /* show flags associated with a file */
int f_inode
; /* print inode */
int f_kblocks
; /* print size in kilobytes */
int f_listdir
; /* list actual directory, not contents */
int f_listdot
; /* list files beginning with . */
int f_longform
; /* long listing format */
int f_newline
; /* if precede with newline */
int f_nonprint
; /* show unprintables as ? */
int f_nosort
; /* don't sort output */
int f_recursive
; /* ls subdirectories also */
int f_reversesort
; /* reverse whatever sort is used */
int f_sectime
; /* print the real time for all files */
int f_singlecol
; /* use single column output */
int f_size
; /* list size in short listing */
int f_statustime
; /* use time of last mode change */
int f_dirname
; /* if precede with directory name */
int f_timesort
; /* sort by time vice name */
int f_type
; /* add type character for non-regular files */
/* Terminal defaults to -Cq, non-terminal defaults to -1. */
if (ioctl(1, TIOCGWINSZ
, &win
) == -1 || !win
.ws_col
) {
if (p
= getenv("COLUMNS"))
/* Root is -A automatically. */
fts_options
= FTS_PHYSICAL
;
while ((ch
= getopt(argc
, argv
, "1ACFLRTacdfgikloqrstu")) != EOF
) {
* The -1, -C and -l options all override each other so shell
f_column
= f_longform
= 0;
f_longform
= f_singlecol
= 0;
f_column
= f_singlecol
= 0;
/* -c and -u override each other */
fts_options
&= ~FTS_PHYSICAL
;
fts_options
|= FTS_LOGICAL
;
fts_options
|= FTS_SEEDOT
;
Sflg
++; /* fall into... */
/* The -d option turns off the -R option. */
/* If not -F, -l -s or -t options, don't require stat information. */
if (!f_longform
&& !f_size
&& !f_timesort
&& !f_type
)
fts_options
|= FTS_NOSTAT
;
* If not -F, -d or -l options, follow any symbolic links listed on
if (!f_longform
&& !f_listdir
&& !f_type
)
fts_options
|= FTS_COMFOLLOW
;
/* Select a sort function. */
else /* Use modification time. */
else /* Use modification time. */
/* Select a print function. */
traverse(argc
, argv
, fts_options
);
static char dot
[] = ".", *dotav
[] = { dot
, NULL
};
traverse(1, dotav
, fts_options
);
* Traverse() walks the logical directory structure specified by the argv list
* in the order specified by the mastercmp() comparison function. During the
* traversal it passes linked lists of structures to display() which represent
* a superset (may be exact set) of the files to be displayed.
traverse(argc
, argv
, options
)
fts_open(argv
, options
, f_nosort
? NULL
: mastercmp
)) == NULL
)
err(1, "fts_open: %s", strerror(errno
));
display(argc
, NULL
, fts_children(ftsp
));
while (p
= fts_read(ftsp
))
err(0, "%s: directory causes a cycle", p
->fts_name
);
p
->fts_name
, strerror(p
->fts_errno
));
if (p
->fts_level
!= FTS_ROOTLEVEL
&&
p
->fts_name
[0] == '.' && !f_listdot
)
display(argc
, p
, fts_children(ftsp
));
(void)fts_set(ftsp
, p
, FTS_SKIP
);
* Display() takes a linked list of FTSENT structures and based on the flags
* set on the command line passes the list along with any other necessary
* information to the print function (printfcn()). P always points to the
* parent directory of the display list.
* If list is NULL there are two possibilities: that the parent
* directory p has no children, or that fts_children() returned an
* error. We ignore the error case since it will be replicated
* on the next call to fts_read() on the post-order visit to the
* directory p, and will be signalled in traverse().
if (list
== NULL
&& (p
->fts_level
!= FTS_ROOTLEVEL
|| argc
!= 1)) {
(void)printf("\n%s:\n", p
->fts_path
);
* P can only be NULL if list is the argv list. This list must be
* handled slightly differently due to the fact that there does not
* exist a proper parent directory and different rules apply to it.
for (cur
= list
, entries
= 0; cur
; cur
= cur
->fts_link
) {
if (cur
->fts_info
== FTS_ERR
||
cur
->fts_info
== FTS_NS
) {
cur
->fts_name
, strerror(cur
->fts_errno
));
cur
->fts_number
= NO_PRINT
;
* If cur is a directory, do not print it out now. Its
* contents will be printed out when fts_read() reads
if (cur
->fts_info
== FTS_D
&& !f_listdir
) {
cur
->fts_number
= NO_PRINT
;
prcopy(cur
->fts_name
, cur
->fts_name
,
if (f_column
&& cur
->fts_namelen
> maxlen
)
maxlen
= cur
->fts_namelen
;
if (p
->fts_level
!= FTS_ROOTLEVEL
|| argc
!= 1)
(void)printf("\n%s:\n", p
->fts_path
);
for (cur
= list
, entries
= 0; cur
; cur
= cur
->fts_link
) {
if (cur
->fts_info
== FTS_ERR
||
cur
->fts_info
== FTS_NS
) {
cur
->fts_name
, strerror(cur
->fts_errno
));
cur
->fts_number
= NO_PRINT
;
/* Don't display dot file if -a/-A not set. */
if (cur
->fts_name
[0] == '.' && !f_listdot
) {
cur
->fts_number
= NO_PRINT
;
prcopy(cur
->fts_name
, cur
->fts_name
,
if (f_column
&& cur
->fts_namelen
> maxlen
)
maxlen
= cur
->fts_namelen
;
if (f_longform
|| f_size
)
btotal
+= cur
->fts_statp
->st_blocks
;
printfcn(list
, entries
, btotal
, maxlen
);
* Ordering for mastercmp:
* If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories
* as larger than directories. Within either group, use the sort function.
* All other levels use the sort function. Error entries remain unsorted.
register int a_info
, b_info
;
if (a_info
== FTS_NS
|| b_info
== FTS_NS
)
return (namecmp(*a
, *b
));
return (sortfcn(*a
, *b
));
if ((*a
)->fts_level
== FTS_ROOTLEVEL
)
else if (b_info
== FTS_D
)
return (sortfcn(*a
, *b
));
return (sortfcn(*a
, *b
));