* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
static char copyright
[] =
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)ls.c 5.72 (Berkeley) %G%";
char *getbsize
__P((char *, int *, long *));
static void display
__P((FTSENT
*, FTSENT
*));
static int mastercmp
__P((const FTSENT
**, const FTSENT
**));
static void traverse
__P((int, char **, int));
static void (*printfcn
) __P((DISPLAY
*));
static int (*sortfcn
) __P((const FTSENT
*, const FTSENT
*));
long blocksize
; /* block size units */
int termwidth
= 80; /* default terminal width */
int f_accesstime
; /* use time of last access */
int f_column
; /* columnated format */
int f_flags
; /* show flags associated with a file */
int f_inode
; /* print inode */
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 */
static char dot
[] = ".", *dotav
[] = { dot
, NULL
};
int ch
, fts_options
, notused
;
/* Terminal defaults to -Cq, non-terminal defaults to -1. */
if (isatty(STDOUT_FILENO
)) {
if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) == -1 ||
if (p
= getenv("COLUMNS"))
f_column
= f_nonprint
= 1;
/* 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;
/* The -c and -u options override each other. */
fts_options
&= ~FTS_PHYSICAL
;
fts_options
|= FTS_LOGICAL
;
fts_options
|= FTS_SEEDOT
;
/* The -d option turns off the -R option. */
Sflg
++; /* fall into... */
case 'g': /* Compatibility with 4.3BSD. */
case 'k': /* Delete before 4.4BSD. */
(void)fprintf(stderr
, "ls: -k no longer supported\n");
* If not -F, -i, -l, -s or -t options, don't require stat
if (!f_inode
&& !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
;
/* If -l or -s, figure out block size. */
if (f_longform
|| f_size
) {
(void)getbsize("ls", ¬used
, &blocksize
);
/* Select a sort function. */
else /* Use modification time. */
else /* Use modification time. */
/* Select a print function. */
traverse(argc
, argv
, fts_options
);
traverse(1, dotav
, fts_options
);
static int output
; /* If anything output. */
* 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
)
register FTSENT
*p
, *chp
;
fts_open(argv
, options
, f_nosort
? NULL
: mastercmp
)) == NULL
)
err(1, "%s", strerror(errno
));
display(NULL
, fts_children(ftsp
, 0));
* If not recursing down this tree and don't need stat info, just get
ch_options
= !f_recursive
&& options
& FTS_NOSTAT
? FTS_NAMEONLY
: 0;
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
)
* If already output something, put out a newline as
* a separator. If multiple arguments, precede each
* directory with its name.
(void)printf("\n%s:\n", p
->fts_path
);
(void)printf("%s:\n", p
->fts_path
);
chp
= fts_children(ftsp
, ch_options
);
if (!f_recursive
&& chp
!= NULL
)
(void)fts_set(ftsp
, p
, FTS_SKIP
);
* Display() takes a linked list of FTSENT structures and passes the list
* along with any other necessary information to the print function. P
* points to the parent directory of the display list.
u_long btotal
, maxblock
, maxinode
, maxlen
, maxnlink
;
int bcfile
, flen
, glen
, ulen
, maxflags
, maxgroup
, maxuser
;
char *user
, *group
, *flags
, buf
[20]; /* 32 bits == 10 digits */
* 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().
needstats
= f_inode
|| f_longform
|| f_size
;
btotal
= maxblock
= maxinode
= maxlen
= maxnlink
= 0;
maxuser
= maxgroup
= maxflags
= 0;
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
;
* P is NULL if list is the argv list, to which different rules
/* Directories will be displayed later. */
if (cur
->fts_info
== FTS_D
&& !f_listdir
) {
cur
->fts_number
= NO_PRINT
;
/* Only display dot file if -a/-A set. */
if (cur
->fts_name
[0] == '.' && !f_listdot
) {
cur
->fts_number
= NO_PRINT
;
prcopy(cur
->fts_name
, cur
->fts_name
, cur
->fts_namelen
);
if (cur
->fts_namelen
> maxlen
)
maxlen
= cur
->fts_namelen
;
if (sp
->st_blocks
> maxblock
)
maxblock
= sp
->st_blocks
;
if (sp
->st_ino
> maxinode
)
if (sp
->st_nlink
> maxnlink
)
if (sp
->st_size
> maxsize
)
user
= user_from_uid(sp
->st_uid
, 0);
if ((ulen
= strlen(user
)) > maxuser
)
group
= group_from_gid(sp
->st_gid
, 0);
if ((glen
= strlen(group
)) > maxgroup
)
flags_to_string(sp
->st_flags
, "-");
if ((flen
= strlen(flags
)) > maxflags
)
if ((np
= malloc(sizeof(NAMES
) +
ulen
+ glen
+ flen
+ 3)) == NULL
)
err(1, "%s", strerror(errno
));
(void)strcpy(np
->user
, user
);
np
->group
= &np
->data
[ulen
+ 1];
(void)strcpy(np
->group
, group
);
if (S_ISCHR(sp
->st_mode
) ||
np
->flags
= &np
->data
[ulen
+ glen
+ 2];
(void)strcpy(np
->flags
, flags
);
(void)snprintf(buf
, sizeof(buf
), "%lu", maxblock
);
(void)snprintf(buf
, sizeof(buf
), "%lu", maxinode
);
(void)snprintf(buf
, sizeof(buf
), "%lu", maxnlink
);
(void)snprintf(buf
, sizeof(buf
), "%qu", maxsize
);
for (cur
= list
; cur
; cur
= cur
->fts_link
)
* 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
));