summaryrefslogtreecommitdiffstats
path: root/file_cmds/ls
diff options
context:
space:
mode:
Diffstat (limited to 'file_cmds/ls')
-rw-r--r--file_cmds/ls/cmp.c226
-rw-r--r--file_cmds/ls/extern.h73
-rw-r--r--file_cmds/ls/ls.1715
-rw-r--r--file_cmds/ls/ls.c977
-rw-r--r--file_cmds/ls/ls.h108
-rw-r--r--file_cmds/ls/print.c837
-rw-r--r--file_cmds/ls/util.c231
7 files changed, 3167 insertions, 0 deletions
diff --git a/file_cmds/ls/cmp.c b/file_cmds/ls/cmp.c
new file mode 100644
index 0000000..2ad5edd
--- /dev/null
+++ b/file_cmds/ls/cmp.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/ls/cmp.c,v 1.12 2002/06/30 05:13:54 obrien Exp $");
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fts.h>
+#include <string.h>
+
+#include "ls.h"
+#include "extern.h"
+
+#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \
+ defined(_XOPEN_SOURCE) || defined(__NetBSD__)
+#define ATIMENSEC_CMP(x, op, y) ((x)->st_atimensec op (y)->st_atimensec)
+#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctimensec op (y)->st_ctimensec)
+#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtimensec op (y)->st_mtimensec)
+#define BTIMENSEC_CMP(x, op, y) ((x)->st_birthtimensec op (y)->st_birthtimensec)
+#else
+#define ATIMENSEC_CMP(x, op, y) \
+ ((x)->st_atimespec.tv_nsec op (y)->st_atimespec.tv_nsec)
+#define CTIMENSEC_CMP(x, op, y) \
+ ((x)->st_ctimespec.tv_nsec op (y)->st_ctimespec.tv_nsec)
+#define MTIMENSEC_CMP(x, op, y) \
+ ((x)->st_mtimespec.tv_nsec op (y)->st_mtimespec.tv_nsec)
+#define BTIMENSEC_CMP(x, op, y) \
+ ((x)->st_birthtimespec.tv_nsec op (y)->st_birthtimespec.tv_nsec)
+#endif
+
+int
+namecmp(const FTSENT *a, const FTSENT *b)
+{
+ return (strcoll(a->fts_name, b->fts_name));
+}
+
+int
+revnamecmp(const FTSENT *a, const FTSENT *b)
+{
+ return (strcoll(b->fts_name, a->fts_name));
+}
+
+int
+modcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_mtime > a->fts_statp->st_mtime)
+ return (1);
+ else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime)
+ return (-1);
+ else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revmodcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_mtime > a->fts_statp->st_mtime)
+ return (-1);
+ else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime)
+ return (1);
+ else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+acccmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_atime > a->fts_statp->st_atime)
+ return (1);
+ else if (b->fts_statp->st_atime < a->fts_statp->st_atime)
+ return (-1);
+ else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revacccmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_atime > a->fts_statp->st_atime)
+ return (-1);
+ else if (b->fts_statp->st_atime < a->fts_statp->st_atime)
+ return (1);
+ else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+statcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_ctime > a->fts_statp->st_ctime)
+ return (1);
+ else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime)
+ return (-1);
+ else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revstatcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_ctime > a->fts_statp->st_ctime)
+ return (-1);
+ else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime)
+ return (1);
+ else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+sizecmp(a, b)
+ const FTSENT *a, *b;
+{
+ if (b->fts_statp->st_size > a->fts_statp->st_size)
+ return (1);
+ if (b->fts_statp->st_size < a->fts_statp->st_size)
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revsizecmp(a, b)
+ const FTSENT *a, *b;
+{
+ if (b->fts_statp->st_size > a->fts_statp->st_size)
+ return (-1);
+ if (b->fts_statp->st_size < a->fts_statp->st_size)
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
+
+int
+birthcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime)
+ return (1);
+ else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime)
+ return (-1);
+ else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (1);
+ else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (-1);
+ else
+ return (namecmp(a, b));
+}
+
+int
+revbirthcmp(const FTSENT *a, const FTSENT *b)
+{
+ if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime)
+ return (-1);
+ else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime)
+ return (1);
+ else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp))
+ return (-1);
+ else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp))
+ return (1);
+ else
+ return (revnamecmp(a, b));
+}
diff --git a/file_cmds/ls/extern.h b/file_cmds/ls/extern.h
new file mode 100644
index 0000000..a33bd97
--- /dev/null
+++ b/file_cmds/ls/extern.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)extern.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/bin/ls/extern.h,v 1.19 2002/05/19 02:51:36 tjr Exp $
+ */
+
+#ifndef _LS_EXTERN_H_
+#define _LS_EXTERN_H_
+
+int acccmp(const FTSENT *, const FTSENT *);
+int revacccmp(const FTSENT *, const FTSENT *);
+int modcmp(const FTSENT *, const FTSENT *);
+int revmodcmp(const FTSENT *, const FTSENT *);
+int namecmp(const FTSENT *, const FTSENT *);
+int revnamecmp(const FTSENT *, const FTSENT *);
+int statcmp(const FTSENT *, const FTSENT *);
+int revstatcmp(const FTSENT *, const FTSENT *);
+int sizecmp (const FTSENT *, const FTSENT *);
+int revsizecmp (const FTSENT *, const FTSENT *);
+int birthcmp(const FTSENT *, const FTSENT *);
+int revbirthcmp(const FTSENT *, const FTSENT *);
+
+void printcol(DISPLAY *);
+void printlong(DISPLAY *);
+void printscol(DISPLAY *);
+void printstream(DISPLAY *);
+void usage(void);
+int prn_normal(const char *);
+size_t len_octal(const char *, int);
+int prn_octal(const char *);
+int prn_printable(const char *);
+#ifdef COLORLS
+void parsecolors(const char *cs);
+void colorquit(int);
+
+extern char *ansi_fgcol;
+extern char *ansi_bgcol;
+extern char *ansi_coloff;
+extern char *attrs_off;
+extern char *enter_bold;
+#endif
+
+#endif /* _LS_EXTERN_H_ */
diff --git a/file_cmds/ls/ls.1 b/file_cmds/ls/ls.1
new file mode 100644
index 0000000..b75c1ae
--- /dev/null
+++ b/file_cmds/ls/ls.1
@@ -0,0 +1,715 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ls.1 8.7 (Berkeley) 7/29/94
+.\" $FreeBSD: src/bin/ls/ls.1,v 1.69 2002/08/21 17:32:34 trhodes Exp $
+.\"
+.Dd May 19, 2002
+.Dt LS 1
+.Os
+.Sh NAME
+.Nm ls
+.Nd list directory contents
+.Sh SYNOPSIS
+.Nm ls
+.Op Fl ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1%
+.Op Ar
+.Sh DESCRIPTION
+For each operand that names a
+.Ar file
+of a type other than
+directory,
+.Nm ls
+displays its name as well as any requested,
+associated information.
+For each operand that names a
+.Ar file
+of type directory,
+.Nm ls
+displays the names of files contained
+within that directory, as well as any requested, associated
+information.
+.Pp
+If no operands are given, the contents of the current
+directory are displayed.
+If more than one operand is given,
+non-directory operands are displayed first; directory
+and non-directory operands are sorted separately and in
+lexicographical order.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl @
+Display extended attribute keys and sizes in long
+.Pq Fl l
+output.
+.It Fl 1
+(The numeric digit
+.Dq one . )
+Force output to be
+one entry per line.
+This is the default when
+output is not to a terminal.
+.It Fl A
+List all entries except for
+.Pa \&.
+and
+.Pa .. .
+Always set for the super-user.
+.It Fl a
+Include directory entries whose names begin with a
+dot
+.Pq Pa \&. .
+.It Fl B
+Force printing of non-printable characters (as defined by
+.Xr ctype 3
+and current locale settings) in file names as
+.Li \e Ns Va xxx ,
+where
+.Va xxx
+is the numeric value of the character in octal.
+.It Fl b
+As
+.Fl B ,
+but use
+.Tn C
+escape codes whenever possible.
+.It Fl C
+Force multi-column output; this is the default when output is to a terminal.
+.It Fl c
+Use time when file status was last changed for sorting
+.Pq Fl t
+or long printing
+.Pq Fl l .
+.It Fl d
+Directories are listed as plain files (not searched recursively).
+.It Fl e
+Print the Access Control List (ACL) associated with the file, if present, in long
+.Pq Fl l
+output.
+.It Fl F
+Display a slash
+.Pq Ql /
+immediately after each pathname that is a directory,
+an asterisk
+.Pq Ql *
+after each that is executable,
+an at sign
+.Pq Ql @
+after each symbolic link,
+an equals sign
+.Pq Ql =
+after each socket,
+a percent sign
+.Pq Ql %
+after each whiteout,
+and a vertical bar
+.Pq Ql \&|
+after each that is a
+.Tn FIFO .
+.It Fl f
+Output is not sorted.
+This option turns on the
+.Fl a
+option.
+.It Fl G
+Enable colorized output.
+This option is equivalent to defining
+.Ev CLICOLOR
+in the environment.
+(See below.)
+.It Fl g
+This option is only available for compatibility with POSIX;
+it is used to display the group name in the long
+.Pq Fl l
+format output (the owner name is suppressed).
+.It Fl H
+Symbolic links on the command line are followed.
+This option is assumed if
+none of the
+.Fl F , d ,
+or
+.Fl l
+options are specified.
+.It Fl h
+When used with the
+.Fl l
+option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte
+and Petabyte in order to reduce the number of digits to three or less
+using base 2 for sizes.
+.It Fl i
+For each file, print the file's file serial number (inode number).
+.It Fl k
+If the
+.Fl s
+option is specified, print the file size allocation in kilobytes,
+not blocks.
+This option overrides the environment variable
+.Ev BLOCKSIZE .
+.It Fl L
+Follow all symbolic links to final target and list the file or directory the link references
+rather than the link itself.
+This option cancels the
+.Fl P
+option.
+.It Fl l
+(The lowercase letter
+.Dq ell . )
+List in long format.
+(See below.)
+A total sum for all the file
+sizes is output on a line before the long listing.
+.It Fl m
+Stream output format; list files across the page, separated by commas.
+.It Fl n
+Display user and group IDs numerically,
+rather than converting to a user or group name in a long
+.Pq Fl l
+output.
+This option turns on the
+.Fl l
+option.
+.It Fl O
+Include the file flags in a long
+.Pq Fl l
+output.
+.It Fl o
+List in long format, but omit the group id.
+.It Fl P
+If argument is a symbolic link, list the link itself rather than the
+object the link references.
+This option cancels the
+.Fl H
+and
+.Fl L
+options.
+.It Fl p
+Write a slash
+.Pq Ql /
+after each filename if that file is a directory.
+.It Fl q
+Force printing of non-graphic characters in file names as
+the character
+.Ql \&? ;
+this is the default when output is to a terminal.
+.It Fl R
+Recursively list subdirectories encountered.
+.It Fl r
+Reverse the order of the sort to get reverse
+lexicographical order or the oldest entries first (or largest files
+last, if combined with sort by size
+.It Fl S
+Sort files by size
+.It Fl s
+Display the number of file system blocks actually used by each file, in units
+of 512 bytes, where partial units are rounded up to the next integer value.
+If the output is to a terminal, a total sum for all the file
+sizes is output on a line before the listing.
+The environment variable
+.Ev BLOCKSIZE
+overrides the unit size of 512 bytes.
+.It Fl T
+When used with the
+.Fl l
+(lowercase letter
+.Dq ell )
+option, display complete time information for the file, including
+month, day, hour, minute, second, and year.
+.It Fl t
+Sort by time modified (most recently modified
+first) before sorting the operands by lexicographical
+order.
+.It Fl u
+Use time of last access,
+instead of last modification
+of the file for sorting
+.Pq Fl t
+or long printing
+.Pq Fl l .
+.It Fl U
+Use time of file creation, instead of last modification for sorting
+.Pq Fl t
+or long output
+.Pq Fl l .
+.It Fl v
+Force unedited printing of non-graphic characters; this is the default when
+output is not to a terminal.
+.It Fl W
+Display whiteouts when scanning directories.
+.Pq Fl S
+flag).
+.It Fl w
+Force raw printing of non-printable characters.
+This is the default
+when output is not to a terminal.
+.It Fl x
+The same as
+.Fl C ,
+except that the multi-column output is produced with entries sorted
+across, rather than down, the columns.
+.It Fl %
+Distinguish dataless files and directories with a '%' character in long
+.Pq Fl l
+output, and don't materialize dataless directories when listing them.
+.El
+.Pp
+The
+.Fl 1 , C , x ,
+and
+.Fl l
+options all override each other;
+the last one specified determines the format used.
+.Pp
+The
+.Fl c
+and
+.Fl u
+options override each other; the last one specified determines
+the file time used.
+.Pp
+The
+.Fl B , b , w ,
+and
+.Fl q
+options all override each other;
+the last one specified determines the format used
+for non-printable characters.
+.Pp
+The
+.Fl H , L
+and
+.Fl P
+options all override each other (either partially or fully); they
+are applied in the order specified.
+.Pp
+By default,
+.Nm ls
+lists one entry per line to standard
+output; the exceptions are to terminals or when the
+.Fl C
+or
+.Fl x
+options are specified.
+.Pp
+File information is displayed with one or more
+.Ao blank Ac Ns s
+separating the information associated with the
+.Fl i , s ,
+and
+.Fl l
+options.
+.Ss The Long Format
+If the
+.Fl l
+option is given, the following information
+is displayed for each file:
+file mode,
+number of links, owner name, group name,
+number of bytes in the file, abbreviated
+month, day-of-month file was last modified,
+hour file last modified, minute file last
+modified, and the pathname.
+In addition, for each directory whose contents are displayed,
+the total number of 512-byte blocks used by the files in the directory
+is displayed on a line by itself,
+immediately before the information for the files in the directory.
+If the file or directory has extended attributes,
+the permissions field printed by the
+.Fl l
+option is followed by a '@' character.
+Otherwise, if the file or directory has extended security information
+(such as an access control list),
+the permissions field printed by the
+.Fl l
+option is followed by a '+' character.
+If the
+.Fl %
+option is given, a '%' character follows the permissions field
+for dataless files and directories,
+possibly replacing the '@' or '+' character.
+.Pp
+If the modification time of the file
+is more than 6 months in the past or future,
+then the year of the last modification
+is displayed in place of the hour and minute fields.
+.Pp
+If the owner or group names are not a known user or group name,
+or the
+.Fl n
+option is given,
+the numeric ID's are displayed.
+.Pp
+If the file is a character special or block special file,
+the major and minor device numbers for the file are displayed
+in the size field.
+If the file is a symbolic link,
+the pathname of the linked-to file is preceded by
+.Dq Li -> .
+.Pp
+The file mode printed under the
+.Fl l
+option consists of the
+entry type, owner permissions, and group permissions.
+The entry type character describes the type of file,
+as follows:
+.Pp
+.Bl -tag -width 4n -offset indent -compact
+.It Sy b
+Block special file.
+.It Sy c
+Character special file.
+.It Sy d
+Directory.
+.It Sy l
+Symbolic link.
+.It Sy s
+Socket link.
+.It Sy p
+.Tn FIFO .
+.It Sy \-
+Regular file.
+.El
+.Pp
+The next three fields
+are three characters each:
+owner permissions,
+group permissions, and
+other permissions.
+Each field has three character positions:
+.Bl -enum -offset indent
+.It
+If
+.Sy r ,
+the file is readable; if
+.Sy \- ,
+it is not readable.
+.It
+If
+.Sy w ,
+the file is writable; if
+.Sy \- ,
+it is not writable.
+.It
+The first of the following that applies:
+.Bl -tag -width 4n -offset indent
+.It Sy S
+If in the owner permissions, the file is not executable and
+set-user-ID mode is set.
+If in the group permissions, the file is not executable
+and set-group-ID mode is set.
+.It Sy s
+If in the owner permissions, the file is executable
+and set-user-ID mode is set.
+If in the group permissions, the file is executable
+and setgroup-ID mode is set.
+.It Sy x
+The file is executable or the directory is
+searchable.
+.It Sy \-
+The file is neither readable, writable, executable,
+nor set-user-ID nor set-group-ID mode, nor sticky.
+(See below.)
+.El
+.Pp
+These next two apply only to the third character in the last group
+(other permissions).
+.Bl -tag -width 4n -offset indent
+.It Sy T
+The sticky bit is set
+(mode
+.Li 1000 ) ,
+but not execute or search permission.
+(See
+.Xr chmod 1
+or
+.Xr sticky 8 . )
+.It Sy t
+The sticky bit is set (mode
+.Li 1000 ) ,
+and is searchable or executable.
+(See
+.Xr chmod 1
+or
+.Xr sticky 8 . )
+.El
+.El
+.Sh EXAMPLES
+The following is how to do an
+.Nm ls
+listing sorted by increasing size
+.Pp
+.Dl "ls -lrS"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm ls :
+.Bl -tag -width ".Ev CLICOLOR_FORCE"
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, the block counts
+(see
+.Fl s )
+will be displayed in units of that size block.
+.It Ev CLICOLOR
+Use
+\*[Ai]
+color sequences to distinguish file types.
+See
+.Ev LSCOLORS
+below.
+In addition to the file types mentioned in the
+.Fl F
+option some extra attributes (setuid bit set, etc.) are also displayed.
+The colorization is dependent on a terminal type with the proper
+.Xr termcap 5
+capabilities.
+The default
+.Dq Li cons25
+console has the proper capabilities,
+but to display the colors in an
+.Xr xterm 1 ,
+for example,
+the
+.Ev TERM
+variable must be set to
+.Dq Li xterm-color .
+Other terminal types may require similar adjustments.
+Colorization
+is silently disabled if the output isn't directed to a terminal
+unless the
+.Ev CLICOLOR_FORCE
+variable is defined.
+.It Ev CLICOLOR_FORCE
+Color sequences are normally disabled if the output isn't directed to
+a terminal.
+This can be overridden by setting this flag.
+The
+.Ev TERM
+variable still needs to reference a color capable terminal however
+otherwise it is not possible to determine which color sequences to
+use.
+.It Ev COLUMNS
+If this variable contains a string representing a
+decimal integer, it is used as the
+column position width for displaying
+multiple-text-column output.
+The
+.Nm ls
+utility calculates how
+many pathname text columns to display
+based on the width provided.
+(See
+.Fl C
+and
+.Fl x . )
+.It Ev LANG
+The locale to use when determining the order of day and month in the long
+.Fl l
+format output.
+See
+.Xr environ 7
+for more information.
+.It Ev LSCOLORS
+The value of this variable describes what color to use for which
+attribute when colors are enabled with
+.Ev CLICOLOR .
+This string is a concatenation of pairs of the format
+.Ar f Ns Ar b ,
+where
+.Ar f
+is the foreground color and
+.Ar b
+is the background color.
+.Pp
+The color designators are as follows:
+.Pp
+.Bl -tag -width 4n -offset indent -compact
+.It Sy a
+black
+.It Sy b
+red
+.It Sy c
+green
+.It Sy d
+brown
+.It Sy e
+blue
+.It Sy f
+magenta
+.It Sy g
+cyan
+.It Sy h
+light grey
+.It Sy A
+bold black, usually shows up as dark grey
+.It Sy B
+bold red
+.It Sy C
+bold green
+.It Sy D
+bold brown, usually shows up as yellow
+.It Sy E
+bold blue
+.It Sy F
+bold magenta
+.It Sy G
+bold cyan
+.It Sy H
+bold light grey; looks like bright white
+.It Sy x
+default foreground or background
+.El
+.Pp
+Note that the above are standard
+\*[Ai]
+colors.
+The actual display may differ
+depending on the color capabilities of the terminal in use.
+.Pp
+The order of the attributes are as follows:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+directory
+.It
+symbolic link
+.It
+socket
+.It
+pipe
+.It
+executable
+.It
+block special
+.It
+character special
+.It
+executable with setuid bit set
+.It
+executable with setgid bit set
+.It
+directory writable to others, with sticky bit
+.It
+directory writable to others, without sticky bit
+.El
+.Pp
+The default is
+.Qq "exfxcxdxbxegedabagacad" ,
+i.e. blue foreground and
+default background for regular directories, black foreground and red
+background for setuid executables, etc.
+.It Ev LS_COLWIDTHS
+If this variable is set, it is considered to be a
+colon-delimited list of minimum column widths.
+Unreasonable
+and insufficient widths are ignored (thus zero signifies
+a dynamically sized column).
+Not all columns have changeable widths.
+The fields are,
+in order: inode, block count, number of links, user name,
+group name, flags, file size, file name.
+.It Ev TERM
+The
+.Ev CLICOLOR
+functionality depends on a terminal type with color capabilities.
+.It Ev TZ
+The timezone to use when displaying dates.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh COMPATIBILITY
+The group field is now automatically included in the long listing for
+files in order to be compatible with the
+.St -p1003.2
+specification.
+.Sh LEGACY DESCRIPTION
+In legacy mode, the
+.Fl f
+option does not turn on the
+.Fl a
+option and the
+.Fl g ,
+.Fl n ,
+and
+.Fl o
+options do not turn on the
+.Fl l
+option.
+.Pp
+Also, the
+.Fl o
+option causes the file flags to be included in a long (-l) output;
+there is no
+.Fl O
+option.
+.Pp
+When
+.Fl H
+is specified (and not overridden by
+.Fl L
+or
+.Fl P )
+and a file argument is a symlink
+that resolves to a non-directory file,
+the output will reflect the nature of the link,
+rather than that of the file.
+In legacy operation, the output will describe the file.
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chmod 1 ,
+.Xr sort 1 ,
+.Xr xterm 1 ,
+.Xr compat 5 ,
+.Xr termcap 5 ,
+.Xr symlink 7 ,
+.Xr sticky 8
+.Sh STANDARDS
+The
+.Nm ls
+utility conforms to
+.St -p1003.1-2001 .
+.Sh HISTORY
+An
+.Nm ls
+command appeared in
+.At v1 .
+.Sh BUGS
+To maintain backward compatibility, the relationships between the many
+options are quite complex.
diff --git a/file_cmds/ls/ls.c b/file_cmds/ls/ls.c
new file mode 100644
index 0000000..e079333
--- /dev/null
+++ b/file_cmds/ls/ls.c
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <limits.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef COLORLS
+#include <termcap.h>
+#include <signal.h>
+#endif
+#ifdef __APPLE__
+#include <sys/acl.h>
+#include <sys/xattr.h>
+#include <sys/param.h>
+#include <get_compat.h>
+#include <sys/sysctl.h>
+#include <System/sys/fsctl.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+#include "ls.h"
+#include "extern.h"
+
+/*
+ * Upward approximation of the maximum number of characters needed to
+ * represent a value of integral type t as a string, excluding the
+ * NUL terminator, with provision for a sign.
+ */
+#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1)
+
+#define IS_DATALESS(sp) (f_dataless && (sp) && ((sp)->st_flags & SF_DATALESS))
+
+static void display(FTSENT *, FTSENT *);
+static u_quad_t makenines(u_quad_t);
+static int mastercmp(const FTSENT **, const FTSENT **);
+static void traverse(int, char **, int);
+
+static void (*printfcn)(DISPLAY *);
+static int (*sortfcn)(const FTSENT *, const FTSENT *);
+
+long blocksize; /* block size units */
+int termwidth = 80; /* default terminal width */
+
+/* flags */
+ int f_accesstime; /* use time of last access */
+ int f_birthtime; /* use time of file birth */
+ int f_flags; /* show flags associated with a file */
+ int f_humanval; /* show human-readable file sizes */
+ int f_inode; /* print inode */
+static int f_kblocks; /* print size in kilobytes */
+static int f_listdir; /* list actual directory, not contents */
+static int f_listdot; /* list files beginning with . */
+ int f_longform; /* long listing format */
+ int f_nonprint; /* show unprintables as ? */
+static int f_nosort; /* don't sort output */
+ int f_notabs; /* don't use tab-separated multi-col output */
+ int f_numericonly; /* don't convert uid/gid to name */
+ int f_octal; /* show unprintables as \xxx */
+ int f_octal_escape; /* like f_octal but use C escapes if possible */
+static int f_recursive; /* ls subdirectories also */
+static int f_reversesort; /* reverse whatever sort is used */
+ int f_sectime; /* print the real time for all files */
+static int f_singlecol; /* use single column output */
+ int f_size; /* list size in short listing */
+ int f_slash; /* similar to f_type, but only for dirs */
+ int f_sortacross; /* sort across rows, not down columns */
+ int f_statustime; /* use time of last mode change */
+ int f_stream; /* stream the output, separate with commas */
+static int f_timesort; /* sort by time vice name */
+static int f_sizesort; /* sort by size */
+ int f_type; /* add type character for non-regular files */
+static int f_whiteout; /* show whiteout entries */
+ int f_acl; /* show ACLs in long listing */
+ int f_xattr; /* show extended attributes in long listing */
+ int f_group; /* show group */
+ int f_owner; /* show owner */
+ int f_dataless; /* distinguish dataless files in long listing,
+ and don't materialize dataless directories. */
+#ifdef COLORLS
+ int f_color; /* add type in color for non-regular files */
+
+char *ansi_bgcol; /* ANSI sequence to set background colour */
+char *ansi_fgcol; /* ANSI sequence to set foreground colour */
+char *ansi_coloff; /* ANSI sequence to reset colours */
+char *attrs_off; /* ANSI sequence to turn off attributes */
+char *enter_bold; /* ANSI sequence to set color to bold mode */
+#endif
+
+static int rval;
+
+int
+main(int argc, char *argv[])
+{
+ static char dot[] = ".", *dotav[] = {dot, NULL};
+ struct winsize win;
+ int ch, fts_options, notused;
+ char *p;
+#ifdef COLORLS
+ char termcapbuf[1024]; /* termcap definition buffer */
+ char tcapbuf[512]; /* capability buffer */
+ char *bp = tcapbuf;
+#endif
+
+ if (argc < 1)
+ usage();
+ (void)setlocale(LC_ALL, "");
+
+ /* Terminal defaults to -Cq, non-terminal defaults to -1. */
+ if (isatty(STDOUT_FILENO)) {
+ termwidth = 80;
+ if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
+ termwidth = atoi(p);
+ else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 &&
+ win.ws_col > 0)
+ termwidth = win.ws_col;
+ f_nonprint = 1;
+ } else {
+ f_singlecol = 1;
+ /* retrieve environment variable, in case of explicit -C */
+ p = getenv("COLUMNS");
+ if (p)
+ termwidth = atoi(p);
+ }
+
+ /* Root is -A automatically. */
+ if (!getuid())
+ f_listdot = 1;
+
+ fts_options = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "1@ABCFGHLOPRSTUWabcdefghiklmnopqrstuvwx%"))
+ != -1) {
+ switch (ch) {
+ /*
+ * The -1, -C, -x and -l options all override each other so
+ * shell aliasing works right.
+ */
+ case '1':
+ f_singlecol = 1;
+ f_longform = 0;
+ f_stream = 0;
+ break;
+ case 'B':
+ f_nonprint = 0;
+ f_octal = 1;
+ f_octal_escape = 0;
+ break;
+ case 'C':
+ f_sortacross = f_longform = f_singlecol = 0;
+ break;
+ case 'l':
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ break;
+ case 'x':
+ f_sortacross = 1;
+ f_longform = 0;
+ f_singlecol = 0;
+ break;
+ /* The -c and -u options override each other. */
+ case 'c':
+ f_statustime = 1;
+ f_accesstime = f_birthtime = 0;
+ break;
+ case 'u':
+ f_accesstime = 1;
+ f_statustime = f_birthtime = 0;
+ break;
+ case 'U':
+ f_birthtime = 1;
+ f_statustime = f_accesstime = 0;
+ break;
+ case 'F':
+ f_type = 1;
+ f_slash = 0;
+ break;
+ case 'H':
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options &= ~FTS_LOGICAL;
+ fts_options |= FTS_PHYSICAL;
+ fts_options |= FTS_COMFOLLOWDIR;
+ } else
+ fts_options |= FTS_COMFOLLOW;
+ break;
+ case 'G':
+ setenv("CLICOLOR", "", 1);
+ break;
+ case 'L':
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR);
+ }
+ break;
+ case 'P':
+ fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR);
+ fts_options &= ~FTS_LOGICAL;
+ fts_options |= FTS_PHYSICAL;
+ break;
+ case 'R':
+ f_recursive = 1;
+ break;
+ case 'a':
+ fts_options |= FTS_SEEDOT;
+ /* FALLTHROUGH */
+ case 'A':
+ f_listdot = 1;
+ break;
+ /* The -d option turns off the -R option. */
+ case 'd':
+ f_listdir = 1;
+ f_recursive = 0;
+ break;
+ case 'f':
+ f_nosort = 1;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ fts_options |= FTS_SEEDOT;
+ f_listdot = 1;
+ }
+ break;
+ case 'g': /* Compatibility with Unix03 */
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ f_group = 1;
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ }
+ break;
+ case 'h':
+ f_humanval = 1;
+ break;
+ case 'i':
+ f_inode = 1;
+ break;
+ case 'k':
+ f_kblocks = 1;
+ break;
+ case 'm':
+ f_stream = 1;
+ f_singlecol = 0;
+ f_longform = 0;
+ break;
+ case 'n':
+ f_numericonly = 1;
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ }
+ break;
+ case 'o':
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ f_owner = 1;
+ f_longform = 1;
+ f_singlecol = 0;
+ f_stream = 0;
+ } else {
+ f_flags = 1;
+ }
+ break;
+ case 'p':
+ f_slash = 1;
+ f_type = 1;
+ break;
+ case 'q':
+ f_nonprint = 1;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'r':
+ f_reversesort = 1;
+ break;
+ case 'S':
+ /* Darwin 1.4.1 compatibility */
+ f_sizesort = 1;
+ break;
+ case 's':
+ f_size = 1;
+ break;
+ case 'T':
+ f_sectime = 1;
+ break;
+ case 't':
+ f_timesort = 1;
+ break;
+ case 'W':
+ f_whiteout = 1;
+ break;
+ case 'v':
+ /* Darwin 1.4.1 compatibility */
+ f_nonprint = 0;
+ break;
+ case 'b':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 1;
+ break;
+ case 'w':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'e':
+ f_acl = 1;
+ break;
+ case '@':
+ f_xattr = 1;
+ break;
+ case 'O':
+ f_flags = 1;
+ break;
+ case '%':
+ f_dataless = 1;
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Enabling of colours is conditional on the environment. */
+ if (getenv("CLICOLOR") &&
+ (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE")))
+#ifdef COLORLS
+ if (tgetent(termcapbuf, getenv("TERM")) == 1) {
+ ansi_fgcol = tgetstr("AF", &bp);
+ ansi_bgcol = tgetstr("AB", &bp);
+ attrs_off = tgetstr("me", &bp);
+ enter_bold = tgetstr("md", &bp);
+
+ /* To switch colours off use 'op' if
+ * available, otherwise use 'oc', or
+ * don't do colours at all. */
+ ansi_coloff = tgetstr("op", &bp);
+ if (!ansi_coloff)
+ ansi_coloff = tgetstr("oc", &bp);
+ if (ansi_fgcol && ansi_bgcol && ansi_coloff)
+ f_color = 1;
+ }
+#else
+ (void)fprintf(stderr, "Color support not compiled in.\n");
+#endif /*COLORLS*/
+
+#ifdef COLORLS
+ if (f_color) {
+ /*
+ * We can't put tabs and color sequences together:
+ * column number will be incremented incorrectly
+ * for "stty oxtabs" mode.
+ */
+ f_notabs = 1;
+ (void)signal(SIGINT, colorquit);
+ (void)signal(SIGQUIT, colorquit);
+ parsecolors(getenv("LSCOLORS"));
+ }
+#endif
+
+ /*
+ * If not -F, -i, -l, -s, -t or -% options, don't require stat
+ * information, unless in color mode in which case we do
+ * need this to determine which colors to display.
+ */
+ if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort && !f_dataless
+#ifdef COLORLS
+ && !f_color
+#endif
+ )
+ fts_options |= FTS_NOSTAT;
+
+ /*
+ * If not -F, -d or -l options, follow any symbolic links listed on
+ * the command line.
+ */
+ if (!f_longform && !f_listdir && !f_type && !f_inode)
+ fts_options |= FTS_COMFOLLOW;
+
+ /*
+ * If -W, show whiteout entries
+ */
+#ifdef FTS_WHITEOUT
+ if (f_whiteout)
+ fts_options |= FTS_WHITEOUT;
+#endif
+
+ /* If -l or -s, figure out block size. */
+ if (f_longform || f_size) {
+ if (f_kblocks)
+ blocksize = 2;
+ else {
+ (void)getbsize(&notused, &blocksize);
+ blocksize /= 512;
+ }
+ }
+ /* Select a sort function. */
+ if (f_reversesort) {
+ if (f_sizesort)
+ sortfcn = revsizecmp;
+ else if (!f_timesort)
+ sortfcn = revnamecmp;
+ else if (f_accesstime)
+ sortfcn = revacccmp;
+ else if (f_statustime)
+ sortfcn = revstatcmp;
+ else if (f_birthtime)
+ sortfcn = revbirthcmp;
+ else /* Use modification time. */
+ sortfcn = revmodcmp;
+ } else {
+ if (f_sizesort)
+ sortfcn = sizecmp;
+ else if (!f_timesort)
+ sortfcn = namecmp;
+ else if (f_accesstime)
+ sortfcn = acccmp;
+ else if (f_statustime)
+ sortfcn = statcmp;
+ else if (f_birthtime)
+ sortfcn = birthcmp;
+ else /* Use modification time. */
+ sortfcn = modcmp;
+ }
+
+ /* Select a print function. */
+ if (f_singlecol)
+ printfcn = printscol;
+ else if (f_longform)
+ printfcn = printlong;
+ else if (f_stream)
+ printfcn = printstream;
+ else
+ printfcn = printcol;
+
+#ifdef __APPLE__
+ if (f_dataless) {
+ // don't materialize dataless directories from the cloud
+ // (particularly usefull when listing recursively)
+ int state = 1;
+ if (sysctlbyname("vfs.nspace.prevent_materialization", NULL, NULL, &state, sizeof(state)) < 0) {
+ err(1, "prevent_materialization");
+ }
+ }
+#endif /* __APPLE__ */
+
+ if (argc)
+ traverse(argc, argv, fts_options);
+ else
+ traverse(1, dotav, fts_options);
+ exit(rval);
+}
+
+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.
+ */
+static void
+traverse(int argc, char *argv[], int options)
+{
+ FTS *ftsp;
+ FTSENT *p, *chp;
+ int ch_options, error;
+
+ if ((ftsp =
+ fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL)
+ err(1, "fts_open");
+
+ display(NULL, fts_children(ftsp, 0));
+ if (f_listdir) {
+ fts_close(ftsp);
+ return;
+ }
+
+ /*
+ * If not recursing down this tree and don't need stat info, just get
+ * the names.
+ */
+ ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0;
+
+ while ((p = fts_read(ftsp)) != NULL)
+ switch (p->fts_info) {
+ case FTS_DC:
+ warnx("%s: directory causes a cycle", p->fts_name);
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ rval = 1;
+ }
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_D:
+ if (p->fts_level != FTS_ROOTLEVEL &&
+ p->fts_name[0] == '.' && !f_listdot) {
+ fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+
+ if (IS_DATALESS(p->fts_statp)) {
+ fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+
+ /*
+ * If already output something, put out a newline as
+ * a separator. If multiple arguments, precede each
+ * directory with its name.
+ */
+ if (output)
+ (void)printf("\n%s:\n", p->fts_path);
+ else if (argc > 1) {
+ (void)printf("%s:\n", p->fts_path);
+ output = 1;
+ }
+ chp = fts_children(ftsp, ch_options);
+ if (COMPAT_MODE("bin/ls", "Unix2003") && ((options & FTS_LOGICAL)!=0)) {
+ FTSENT *curr;
+ for (curr = chp; curr; curr = curr->fts_link) {
+ if (curr->fts_info == FTS_SLNONE)
+ curr->fts_number = NO_PRINT;
+ }
+ }
+ display(p, chp);
+
+ if (!f_recursive && chp != NULL)
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ case FTS_SLNONE: /* Same as default unless Unix conformance */
+ if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ if ((options & FTS_LOGICAL)!=0) { /* -L was specified */
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno ?: ENOENT));
+ rval = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ error = errno;
+ fts_close(ftsp);
+ errno = error;
+
+ if (errno)
+ err(1, "fts_read");
+}
+
+/*
+ * 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.
+ */
+static void
+display(FTSENT *p, FTSENT *list)
+{
+ struct stat *sp;
+ DISPLAY d;
+ FTSENT *cur;
+ NAMES *np;
+ off_t maxsize;
+ u_int64_t btotal, maxblock;
+ u_long lattrlen, maxlen, maxnlink, maxlattr;
+ ino_t maxinode;
+ int bcfile, maxflags;
+ gid_t maxgroup;
+ uid_t maxuser;
+ size_t flen, ulen, glen;
+ char *initmax;
+ int entries, needstats;
+ const char *user, *group;
+ char *flags, *lattr = NULL;
+ char buf[STRBUF_SIZEOF(u_quad_t) + 1];
+ char ngroup[STRBUF_SIZEOF(uid_t) + 1];
+ char nuser[STRBUF_SIZEOF(gid_t) + 1];
+#ifdef __APPLE__
+ acl_entry_t dummy;
+ ssize_t xattr_size;
+ char *filename;
+ char path[MAXPATHLEN+1];
+#endif // __APPLE__
+ /*
+ * 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 signaled in traverse().
+ */
+ if (list == NULL)
+ return;
+
+ needstats = f_inode || f_longform || f_size;
+ btotal = 0;
+ initmax = getenv("LS_COLWIDTHS");
+ /* Fields match -lios order. New ones should be added at the end. */
+ maxlattr = maxblock = maxinode = maxlen = maxnlink =
+ maxuser = maxgroup = maxflags = maxsize = 0;
+ if (initmax != NULL && *initmax != '\0') {
+ char *initmax2, *jinitmax;
+ int ninitmax;
+
+ /* Fill-in "::" as "0:0:0" for the sake of scanf. */
+ jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2);
+ if (jinitmax == NULL)
+ err(1, "malloc");
+ if (*initmax == ':')
+ strcpy(initmax2, "0:"), initmax2 += 2;
+ else
+ *initmax2++ = *initmax, *initmax2 = '\0';
+ for (initmax++; *initmax != '\0'; initmax++) {
+ if (initmax[-1] == ':' && initmax[0] == ':') {
+ *initmax2++ = '0';
+ *initmax2++ = initmax[0];
+ initmax2[1] = '\0';
+ } else {
+ *initmax2++ = initmax[0];
+ initmax2[1] = '\0';
+ }
+ }
+ if (initmax2[-1] == ':')
+ strcpy(initmax2, "0");
+
+ ninitmax = sscanf(jinitmax,
+#if _DARWIN_FEATURE_64_BIT_INODE
+ " %llu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ",
+#else
+ " %lu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ",
+#endif
+ &maxinode, &maxblock, &maxnlink, &maxuser,
+ &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr);
+ f_notabs = 1;
+ switch (ninitmax) {
+ case 0:
+ maxinode = 0;
+ /* FALLTHROUGH */
+ case 1:
+ maxblock = 0;
+ /* FALLTHROUGH */
+ case 2:
+ maxnlink = 0;
+ /* FALLTHROUGH */
+ case 3:
+ maxuser = 0;
+ /* FALLTHROUGH */
+ case 4:
+ maxgroup = 0;
+ /* FALLTHROUGH */
+ case 5:
+ maxflags = 0;
+ /* FALLTHROUGH */
+ case 6:
+ maxsize = 0;
+ /* FALLTHROUGH */
+ case 7:
+ maxlen = 0;
+ /* FALLTHROUGH */
+ case 8:
+ maxlattr = 0;
+ /* FALLTHROUGH */
+#ifdef COLORLS
+ if (!f_color)
+#endif
+ f_notabs = 0;
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ maxinode = makenines(maxinode);
+ maxblock = makenines(maxblock);
+ maxnlink = makenines(maxnlink);
+ maxsize = makenines(maxsize);
+ }
+ bcfile = 0;
+ flags = NULL;
+ for (cur = list, entries = 0; cur; cur = cur->fts_link) {
+ if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) {
+ warnx("%s: %s",
+ cur->fts_name, strerror(cur->fts_errno));
+ cur->fts_number = NO_PRINT;
+ rval = 1;
+ continue;
+ }
+ /*
+ * P is NULL if list is the argv list, to which different rules
+ * apply.
+ */
+ if (p == NULL) {
+ /* Directories will be displayed later. */
+ if (cur->fts_info == FTS_D && !f_listdir) {
+ cur->fts_number = NO_PRINT;
+ continue;
+ }
+ } else {
+ /* Only display dot file if -a/-A set. */
+ if (cur->fts_name[0] == '.' && !f_listdot) {
+ cur->fts_number = NO_PRINT;
+ continue;
+ }
+ }
+ if (cur->fts_namelen > maxlen)
+ maxlen = cur->fts_namelen;
+ if (f_octal || f_octal_escape) {
+ u_long t = len_octal(cur->fts_name, cur->fts_namelen);
+
+ if (t > maxlen)
+ maxlen = t;
+ }
+ if (needstats) {
+ sp = cur->fts_statp;
+ if (sp->st_blocks > maxblock)
+ maxblock = sp->st_blocks;
+ if (sp->st_ino > maxinode)
+ maxinode = sp->st_ino;
+ if (sp->st_nlink > maxnlink)
+ maxnlink = sp->st_nlink;
+ if (sp->st_size > maxsize)
+ maxsize = sp->st_size;
+
+ btotal += sp->st_blocks;
+ if (f_longform) {
+ if (f_numericonly) {
+ (void)snprintf(nuser, sizeof(nuser),
+ "%u", sp->st_uid);
+ (void)snprintf(ngroup, sizeof(ngroup),
+ "%u", sp->st_gid);
+ user = nuser;
+ group = ngroup;
+ } else {
+ user = user_from_uid(sp->st_uid, 0);
+ group = group_from_gid(sp->st_gid, 0);
+ }
+ if ((ulen = strlen(user)) > maxuser)
+ maxuser = ulen;
+ if ((glen = strlen(group)) > maxgroup)
+ maxgroup = glen;
+ if (f_flags) {
+ flags = fflagstostr(sp->st_flags);
+ if (flags != NULL && *flags == '\0') {
+ free(flags);
+ flags = strdup("-");
+ }
+ if (flags == NULL)
+ err(1, "fflagstostr");
+ flen = strlen(flags);
+ if (flen > (size_t)maxflags)
+ maxflags = flen;
+ } else
+ flen = 0;
+ lattr = NULL;
+ lattrlen = 0;
+
+ if ((np = calloc(1, sizeof(NAMES) + lattrlen +
+ ulen + glen + flen + 4)) == NULL)
+ err(1, "malloc");
+
+ np->user = &np->data[0];
+ (void)strcpy(np->user, user);
+ np->group = &np->data[ulen + 1];
+ (void)strcpy(np->group, group);
+#ifdef __APPLE__
+ if (cur->fts_level == FTS_ROOTLEVEL) {
+ filename = cur->fts_name;
+ } else {
+ snprintf(path, sizeof(path), "%s/%s", cur->fts_parent->fts_accpath, cur->fts_name);
+ filename = path;
+ }
+ xattr_size = listxattr(filename, NULL, 0, XATTR_NOFOLLOW);
+ if (xattr_size < 0) {
+ xattr_size = 0;
+ }
+ if ((xattr_size > 0) && f_xattr) {
+ /* collect sizes */
+ np->xattr_names = malloc(xattr_size);
+ listxattr(filename, np->xattr_names, xattr_size, XATTR_NOFOLLOW);
+ for (char *name = np->xattr_names; name < np->xattr_names + xattr_size;
+ name += strlen(name)+1) {
+ np->xattr_sizes = reallocf(np->xattr_sizes, (np->xattr_count+1) * sizeof(np->xattr_sizes[0]));
+ np->xattr_sizes[np->xattr_count] = getxattr(filename, name, 0, 0, 0, XATTR_NOFOLLOW);
+ np->xattr_count++;
+ }
+ }
+ /* symlinks can not have ACLs */
+ np->acl = acl_get_link_np(filename, ACL_TYPE_EXTENDED);
+ if (np->acl) {
+ if (acl_get_entry(np->acl, ACL_FIRST_ENTRY, &dummy) == -1) {
+ acl_free(np->acl);
+ np->acl = NULL;
+ }
+ }
+ if (xattr_size > 0) {
+ np->mode_suffix = '@';
+ } else if (np->acl) {
+ np->mode_suffix = '+';
+ } else {
+ np->mode_suffix = ' ';
+ }
+ if (IS_DATALESS(sp)) {
+ np->mode_suffix = '%';
+ }
+ if (!f_acl) {
+ acl_free(np->acl);
+ np->acl = NULL;
+ }
+#endif // __APPLE__
+ if (S_ISCHR(sp->st_mode) ||
+ S_ISBLK(sp->st_mode))
+ bcfile = 1;
+
+ if (f_flags) {
+ np->flags = &np->data[ulen + glen + 2];
+ (void)strcpy(np->flags, flags);
+ free(flags);
+ }
+ cur->fts_pointer = np;
+ }
+ }
+ ++entries;
+ }
+
+ if (!entries)
+ return;
+
+ d.list = list;
+ d.entries = entries;
+ d.maxlen = maxlen;
+ if (needstats) {
+ d.bcfile = bcfile;
+ d.btotal = btotal;
+ (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxblock);
+ d.s_block = strlen(buf);
+ d.s_flags = maxflags;
+ d.s_lattr = maxlattr;
+ d.s_group = maxgroup;
+#if _DARWIN_FEATURE_64_BIT_INODE
+ (void)snprintf(buf, sizeof(buf), "%llu", maxinode);
+#else
+ (void)snprintf(buf, sizeof(buf), "%lu", maxinode);
+#endif
+ d.s_inode = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%lu", maxnlink);
+ d.s_nlink = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxsize);
+ d.s_size = strlen(buf);
+ d.s_user = maxuser;
+ }
+ printfcn(&d);
+ output = 1;
+
+ if (f_longform) {
+ for (cur = list; cur; cur = cur->fts_link) {
+ np = cur->fts_pointer;
+ if (np) {
+ if (np->acl) {
+ acl_free(np->acl);
+ }
+ free(np->xattr_names);
+ free(np->xattr_sizes);
+ free(np);
+ cur->fts_pointer = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * 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.
+ */
+static int
+mastercmp(const FTSENT **a, const FTSENT **b)
+{
+ int a_info, b_info;
+
+ a_info = (*a)->fts_info;
+ if (a_info == FTS_ERR)
+ return (0);
+ b_info = (*b)->fts_info;
+ if (b_info == FTS_ERR)
+ return (0);
+
+ if (a_info == FTS_NS || b_info == FTS_NS)
+ return (namecmp(*a, *b));
+
+ if (a_info != b_info &&
+ (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) {
+ if (a_info == FTS_D)
+ return (1);
+ if (b_info == FTS_D)
+ return (-1);
+ }
+ return (sortfcn(*a, *b));
+}
+
+/*
+ * Makenines() returns (10**n)-1. This is useful for converting a width
+ * into a number that wide in decimal.
+ */
+static u_quad_t
+makenines(u_quad_t n)
+{
+ u_long i;
+ u_quad_t reg;
+
+ reg = 1;
+ /* Use a loop instead of pow(), since all values of n are small. */
+ for (i = 0; i < n; i++)
+ reg *= 10;
+ reg--;
+
+ return reg;
+}
diff --git a/file_cmds/ls/ls.h b/file_cmds/ls/ls.h
new file mode 100644
index 0000000..d1ef037
--- /dev/null
+++ b/file_cmds/ls/ls.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)ls.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/bin/ls/ls.h,v 1.18 2002/05/19 02:51:36 tjr Exp $
+ */
+
+#ifndef _LS_H_
+#define _LS_H_
+
+#define NO_PRINT 1
+
+extern long blocksize; /* block size units */
+
+extern int f_accesstime; /* use time of last access */
+extern int f_birthtime; /* use time of file birth */
+extern int f_flags; /* show flags associated with a file */
+extern int f_humanval; /* show human-readable file sizes */
+extern int f_inode; /* print inode */
+extern int f_longform; /* long listing format */
+extern int f_octal; /* print unprintables in octal */
+extern int f_octal_escape; /* like f_octal but use C escapes if possible */
+extern int f_nonprint; /* show unprintables as ? */
+extern int f_sectime; /* print the real time for all files */
+extern int f_size; /* list size in short listing */
+extern int f_slash; /* append a '/' if the file is a directory */
+extern int f_sortacross; /* sort across rows, not down columns */
+extern int f_statustime; /* use time of last mode change */
+extern int f_notabs; /* don't use tab-separated multi-col output */
+extern int f_type; /* add type character for non-regular files */
+extern int f_acl; /* print ACLs in long format */
+extern int f_xattr; /* print extended attributes in long format */
+extern int f_group; /* list group without owner */
+extern int f_owner; /* list owner without group */
+#ifdef COLORLS
+extern int f_color; /* add type in color for non-regular files */
+#endif
+extern int f_numericonly; /* don't convert uid/gid to name */
+
+#ifdef __APPLE__
+#include <sys/acl.h>
+#endif // __APPLE__
+
+typedef struct {
+ FTSENT *list;
+ u_int64_t btotal;
+ int bcfile;
+ int entries;
+ int maxlen;
+ u_int s_block;
+ u_int s_flags;
+ u_int s_lattr;
+ u_int s_group;
+ u_int s_inode;
+ u_int s_nlink;
+ u_int s_size;
+ u_int s_user;
+} DISPLAY;
+
+typedef struct {
+ char *user;
+ char *group;
+ char *flags;
+#ifndef __APPLE__
+ char *lattr;
+#else
+ char *xattr_names; /* f_xattr */
+ int *xattr_sizes;
+ acl_t acl; /* f_acl */
+ int xattr_count;
+ char mode_suffix; /* @ | + | % | <space> */
+#endif /* __APPLE__ */
+ char data[1];
+} NAMES;
+
+#endif /* _LS_H_ */
diff --git a/file_cmds/ls/print.c b/file_cmds/ls/print.c
new file mode 100644
index 0000000..de04bf3
--- /dev/null
+++ b/file_cmds/ls/print.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/ls/print.c,v 1.57 2002/08/29 14:29:09 keramida Exp $");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifdef __APPLE__
+#include <sys/acl.h>
+#include <sys/xattr.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <TargetConditionals.h>
+#include <membership.h>
+#include <membershipPriv.h>
+#include <uuid/uuid.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <math.h>
+#include <langinfo.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef COLORLS
+#include <ctype.h>
+#include <termcap.h>
+#include <signal.h>
+#endif
+#include <stdint.h> /* intmax_t */
+#include <assert.h>
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "ls.h"
+#include "extern.h"
+
+static int printaname(FTSENT *, u_long, u_long);
+static void printlink(FTSENT *);
+static void printtime(time_t);
+static int printtype(u_int);
+static void printsize(size_t, off_t);
+#ifdef COLORLS
+static void endcolor(int);
+static int colortype(mode_t);
+#endif
+
+#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
+
+#ifdef COLORLS
+/* Most of these are taken from <sys/stat.h> */
+typedef enum Colors {
+ C_DIR, /* directory */
+ C_LNK, /* symbolic link */
+ C_SOCK, /* socket */
+ C_FIFO, /* pipe */
+ C_EXEC, /* executable */
+ C_BLK, /* block special */
+ C_CHR, /* character special */
+ C_SUID, /* setuid executable */
+ C_SGID, /* setgid executable */
+ C_WSDIR, /* directory writeble to others, with sticky
+ * bit */
+ C_WDIR, /* directory writeble to others, without
+ * sticky bit */
+ C_NUMCOLORS /* just a place-holder */
+} Colors;
+
+static const char *defcolors = "exfxcxdxbxegedabagacad";
+
+/* colors for file types */
+static struct {
+ int num[2];
+ int bold;
+} colors[C_NUMCOLORS];
+#endif
+
+void
+printscol(DISPLAY *dp)
+{
+ FTSENT *p;
+
+ assert(dp);
+ if (COMPAT_MODE("bin/ls", "Unix2003") && (dp->list != NULL)) {
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+ }
+
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ (void)printaname(p, dp->s_inode, dp->s_block);
+ (void)putchar('\n');
+ }
+}
+
+/*
+ * print name in current style
+ */
+static int
+printname(const char *name)
+{
+ if (f_octal || f_octal_escape)
+ return prn_octal(name);
+ else if (f_nonprint)
+ return prn_printable(name);
+ else
+ return prn_normal(name);
+}
+
+/*
+ * print access control list
+ */
+static struct {
+ acl_perm_t perm;
+ char *name;
+ int flags;
+#define ACL_PERM_DIR (1<<0)
+#define ACL_PERM_FILE (1<<1)
+} acl_perms[] = {
+ {ACL_READ_DATA, "read", ACL_PERM_FILE},
+ {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
+ {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
+ {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
+ {ACL_EXECUTE, "execute", ACL_PERM_FILE},
+ {ACL_SEARCH, "search", ACL_PERM_DIR},
+ {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
+ {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
+ {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
+ {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+static struct {
+ acl_flag_t flag;
+ char *name;
+ int flags;
+} acl_flags[] = {
+ {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+static char *
+uuid_to_name(uuid_t *uu)
+{
+ int type;
+ char *name = NULL;
+ char *recname = NULL;
+
+#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */
+ name = (char *) malloc(MAXNAMETAG);
+
+ if (NULL == name) {
+ err(1, "malloc");
+ }
+
+ if (f_numericonly) {
+ goto errout;
+ }
+
+ if (mbr_identifier_translate(ID_TYPE_UUID, *uu, sizeof(*uu), ID_TYPE_NAME, (void **) &recname, &type)) {
+ goto errout;
+ }
+
+ snprintf(name, MAXNAMETAG, "%s:%s", (type == MBR_REC_TYPE_USER ? "user" : "group"), recname);
+ free(recname);
+
+ return name;
+errout:
+ uuid_unparse_upper(*uu, name);
+
+ return name;
+}
+
+static void
+printxattr(DISPLAY *dp, int count, char *buf, int sizes[])
+{
+ for (int i = 0; i < count; i++) {
+ putchar('\t');
+ printname(buf);
+ putchar('\t');
+ printsize(dp->s_size, sizes[i]);
+ putchar('\n');
+ buf += strlen(buf) + 1;
+ }
+}
+
+static void
+printacl(acl_t acl, int isdir)
+{
+ acl_entry_t entry = NULL;
+ int index;
+ uuid_t *applicable;
+ char *name = NULL;
+ acl_tag_t tag;
+ acl_flagset_t flags;
+ acl_permset_t perms;
+ char *type;
+ int i, first;
+
+
+ for (index = 0;
+ acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0;
+ index++) {
+ if (acl_get_tag_type(entry, &tag) != 0)
+ continue;
+ if (acl_get_flagset_np(entry, &flags) != 0)
+ continue;
+ if (acl_get_permset(entry, &perms) != 0)
+ continue;
+ if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL)
+ continue;
+ name = uuid_to_name(applicable);
+ acl_free(applicable);
+ switch(tag) {
+ case ACL_EXTENDED_ALLOW:
+ type = "allow";
+ break;
+ case ACL_EXTENDED_DENY:
+ type = "deny";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ (void)printf(" %d: %s%s %s ",
+ index,
+ name,
+ acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "",
+ type);
+
+ if (name)
+ free(name);
+
+ for (i = 0, first = 0; acl_perms[i].name != NULL; i++) {
+ if (acl_get_perm_np(perms, acl_perms[i].perm) == 0)
+ continue;
+ if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+ continue;
+ (void)printf("%s%s", first++ ? "," : "", acl_perms[i].name);
+ }
+ for (i = 0; acl_flags[i].name != NULL; i++) {
+ if (acl_get_flag_np(flags, acl_flags[i].flag) == 0)
+ continue;
+ if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE)))
+ continue;
+ (void)printf("%s%s", first++ ? "," : "", acl_flags[i].name);
+ }
+
+ (void)putchar('\n');
+ }
+
+}
+
+void
+printlong(DISPLAY *dp)
+{
+ struct stat *sp;
+ FTSENT *p;
+ NAMES *np;
+ char buf[20];
+#ifdef COLORLS
+ int color_printed = 0;
+#endif
+
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ sp = p->fts_statp;
+ if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ (void)printf("%*llu ", dp->s_inode, (u_quad_t)sp->st_ino);
+#else
+ (void)printf("%*lu ", dp->s_inode, (u_long)sp->st_ino);
+#endif
+ if (f_size)
+ (void)printf("%*qu ",
+ dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize));
+ strmode(sp->st_mode, buf);
+ np = p->fts_pointer;
+#ifdef __APPLE__
+ buf[10] = '\0'; /* make +/@ abut the mode */
+ char str[2] = { np->mode_suffix, '\0' };
+#endif /* __APPLE__ */
+ if (f_group && f_owner) { /* means print neither */
+#ifdef __APPLE__
+ (void)printf("%s%s %*u ", buf, str, dp->s_nlink,
+ sp->st_nlink);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u ", buf, dp->s_nlink,
+ sp->st_nlink);
+#endif /* __APPLE__ */
+ }
+ else if (f_group) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_group, np->group);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_group, np->group);
+#endif /* __APPLE__ */
+ }
+ else if (f_owner) {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user);
+#endif /* __APPLE__ */
+ }
+ else {
+#ifdef __APPLE__
+ (void)printf("%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user, dp->s_group,
+ np->group);
+#else /* ! __APPLE__ */
+ (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink,
+ sp->st_nlink, dp->s_user, np->user, dp->s_group,
+ np->group);
+#endif /* ! __APPLE__ */
+ }
+ if (f_flags)
+ (void)printf("%-*s ", dp->s_flags, np->flags);
+ if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
+ if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0)
+ (void)printf("%3d, 0x%08x ",
+ major(sp->st_rdev),
+ (u_int)minor(sp->st_rdev));
+ else
+ (void)printf("%3d, %3d ",
+ major(sp->st_rdev), minor(sp->st_rdev));
+ else if (dp->bcfile)
+ (void)printf("%*s%*qu ",
+ 8 - dp->s_size, "", dp->s_size, (u_int64_t)sp->st_size);
+ else
+ printsize(dp->s_size, sp->st_size);
+ if (f_accesstime)
+ printtime(sp->st_atime);
+ else if (f_statustime)
+ printtime(sp->st_ctime);
+ else if (f_birthtime)
+ printtime(sp->st_birthtime);
+ else
+ printtime(sp->st_mtime);
+#ifdef COLORLS
+ if (f_color)
+ color_printed = colortype(sp->st_mode);
+#endif
+ (void)printname(p->fts_name);
+#ifdef COLORLS
+ if (f_color && color_printed)
+ endcolor(0);
+#endif
+ if (f_type)
+ (void)printtype(sp->st_mode);
+ if (S_ISLNK(sp->st_mode))
+ printlink(p);
+ (void)putchar('\n');
+#ifdef __APPLE__
+ if (np->xattr_count && f_xattr) {
+ printxattr(dp, np->xattr_count, np->xattr_names, np->xattr_sizes);
+ }
+ if (np->acl != NULL && f_acl) {
+ printacl(np->acl, S_ISDIR(sp->st_mode));
+ }
+#endif /* __APPLE__ */
+ }
+}
+
+void
+printstream(DISPLAY *dp)
+{
+ FTSENT *p;
+ extern int termwidth;
+ int chcnt;
+
+ for (p = dp->list, chcnt = 0; p; p = p->fts_link) {
+ if (p->fts_number == NO_PRINT)
+ continue;
+ if (strlen(p->fts_name) + chcnt +
+ (p->fts_link ? 2 : 0) >= (unsigned)termwidth) {
+ putchar('\n');
+ chcnt = 0;
+ }
+ chcnt += printaname(p, dp->s_inode, dp->s_block);
+ if (p->fts_link) {
+ printf(", ");
+ chcnt += 2;
+ }
+ }
+ if (chcnt)
+ putchar('\n');
+}
+
+void
+printcol(DISPLAY *dp)
+{
+ extern int termwidth;
+ static FTSENT **array;
+ static int lastentries = -1;
+ FTSENT *p;
+ int base;
+ int chcnt;
+ int cnt;
+ int col;
+ int colwidth;
+ int endcol;
+ int num;
+ int numcols;
+ int numrows;
+ int row;
+ int tabwidth;
+
+ if (f_notabs)
+ tabwidth = 1;
+ else
+ tabwidth = 8;
+
+ /*
+ * Have to do random access in the linked list -- build a table
+ * of pointers.
+ */
+ if ((lastentries == -1) || (dp->entries > lastentries)) {
+ lastentries = dp->entries;
+ if ((array = realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) {
+ warn(NULL);
+ printscol(dp);
+ return;
+ }
+ }
+ memset(array, 0, dp->entries * sizeof(FTSENT *));
+ for (p = dp->list, num = 0; p; p = p->fts_link)
+ if (p->fts_number != NO_PRINT)
+ array[num++] = p;
+
+ colwidth = dp->maxlen;
+ if (f_inode)
+ colwidth += dp->s_inode + 1;
+ if (f_size)
+ colwidth += dp->s_block + 1;
+ if (f_type)
+ colwidth += 1;
+
+ colwidth = (colwidth + tabwidth) & ~(tabwidth - 1);
+ if (termwidth < 2 * colwidth) {
+ printscol(dp);
+ return;
+ }
+ numcols = termwidth / colwidth;
+ numrows = num / numcols;
+ if (num % numcols)
+ ++numrows;
+
+ assert(dp->list);
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size))
+ (void)printf("total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize));
+
+ base = 0;
+ for (row = 0; row < numrows; ++row) {
+ endcol = colwidth;
+ if (!f_sortacross)
+ base = row;
+ for (col = 0, chcnt = 0; col < numcols; ++col) {
+ assert(base < dp->entries);
+ chcnt += printaname(array[base], dp->s_inode, dp->s_block);
+ if (f_sortacross)
+ base++;
+ else
+ base += numrows;
+ if (base >= num)
+ break;
+ while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1)))
+ <= endcol) {
+ if (f_sortacross && col + 1 >= numcols)
+ break;
+ (void)putchar(f_notabs ? ' ' : '\t');
+ chcnt = cnt;
+ }
+ endcol += colwidth;
+ }
+ (void)putchar('\n');
+ }
+}
+
+/*
+ * print [inode] [size] name
+ * return # of characters printed, no trailing characters.
+ */
+static int
+printaname(FTSENT *p, u_long inodefield, u_long sizefield)
+{
+ struct stat *sp;
+ int chcnt;
+#ifdef COLORLS
+ int color_printed = 0;
+#endif
+
+ sp = p->fts_statp;
+ chcnt = 0;
+ if (f_inode)
+#if _DARWIN_FEATURE_64_BIT_INODE
+ chcnt += printf("%*llu ", (int)inodefield, (u_quad_t)sp->st_ino);
+#else
+ chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino);
+#endif
+ if (f_size)
+ chcnt += printf("%*qu ",
+ (int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize));
+#ifdef COLORLS
+ if (f_color)
+ color_printed = colortype(sp->st_mode);
+#endif
+ chcnt += printname(p->fts_name);
+#ifdef COLORLS
+ if (f_color && color_printed)
+ endcolor(0);
+#endif
+ if (f_type)
+ chcnt += printtype(sp->st_mode);
+ return (chcnt);
+}
+
+static void
+printtime(time_t ftime)
+{
+ char longstring[80];
+ static time_t now;
+ const char *format;
+ static int d_first = -1;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ if (now == 0)
+ now = time(NULL);
+
+#define SIXMONTHS ((365 / 2) * 86400)
+ if (f_sectime)
+ /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */
+ format = d_first ? "%e %b %T %Y " : "%b %e %T %Y ";
+ else if (COMPAT_MODE("bin/ls", "Unix2003")) {
+ if (ftime + SIXMONTHS > now && ftime <= now)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ }
+ else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
+ /* mmm dd hh:mm || dd mmm hh:mm */
+ format = d_first ? "%e %b %R " : "%b %e %R ";
+ else
+ /* mmm dd yyyy || dd mmm yyyy */
+ format = d_first ? "%e %b %Y " : "%b %e %Y ";
+ strftime(longstring, sizeof(longstring), format, localtime(&ftime));
+ fputs(longstring, stdout);
+}
+
+static int
+printtype(u_int mode)
+{
+
+ if (f_slash) {
+ if ((mode & S_IFMT) == S_IFDIR) {
+ (void)putchar('/');
+ return (1);
+ }
+ return (0);
+ }
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ (void)putchar('/');
+ return (1);
+ case S_IFIFO:
+ (void)putchar('|');
+ return (1);
+ case S_IFLNK:
+ (void)putchar('@');
+ return (1);
+ case S_IFSOCK:
+ (void)putchar('=');
+ return (1);
+ case S_IFWHT:
+ (void)putchar('%');
+ return (1);
+ default:
+ break;
+ }
+ if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ (void)putchar('*');
+ return (1);
+ }
+ return (0);
+}
+
+#ifdef COLORLS
+static int
+putch(int c)
+{
+ (void)putchar(c);
+ return 0;
+}
+
+static int
+writech(int c)
+{
+ char tmp = c;
+
+ (void)write(STDOUT_FILENO, &tmp, 1);
+ return 0;
+}
+
+static void
+printcolor(Colors c)
+{
+ char *ansiseq;
+
+ if (colors[c].bold)
+ tputs(enter_bold, 1, putch);
+
+ if (colors[c].num[0] != -1) {
+ ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]);
+ if (ansiseq)
+ tputs(ansiseq, 1, putch);
+ }
+ if (colors[c].num[1] != -1) {
+ ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]);
+ if (ansiseq)
+ tputs(ansiseq, 1, putch);
+ }
+}
+
+static void
+endcolor(int sig)
+{
+ tputs(ansi_coloff, 1, sig ? writech : putch);
+ tputs(attrs_off, 1, sig ? writech : putch);
+}
+
+static int
+colortype(mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ if (mode & S_IWOTH)
+ if (mode & S_ISTXT)
+ printcolor(C_WSDIR);
+ else
+ printcolor(C_WDIR);
+ else
+ printcolor(C_DIR);
+ return (1);
+ case S_IFLNK:
+ printcolor(C_LNK);
+ return (1);
+ case S_IFSOCK:
+ printcolor(C_SOCK);
+ return (1);
+ case S_IFIFO:
+ printcolor(C_FIFO);
+ return (1);
+ case S_IFBLK:
+ printcolor(C_BLK);
+ return (1);
+ case S_IFCHR:
+ printcolor(C_CHR);
+ return (1);
+ }
+ if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ if (mode & S_ISUID)
+ printcolor(C_SUID);
+ else if (mode & S_ISGID)
+ printcolor(C_SGID);
+ else
+ printcolor(C_EXEC);
+ return (1);
+ }
+ return (0);
+}
+
+void
+parsecolors(const char *cs)
+{
+ int i;
+ int j;
+ int len;
+ char c[2];
+ short legacy_warn = 0;
+
+ if (cs == NULL)
+ cs = ""; /* LSCOLORS not set */
+ len = strlen(cs);
+ for (i = 0; i < C_NUMCOLORS; i++) {
+ colors[i].bold = 0;
+
+ if (len <= 2 * i) {
+ c[0] = defcolors[2 * i];
+ c[1] = defcolors[2 * i + 1];
+ } else {
+ c[0] = cs[2 * i];
+ c[1] = cs[2 * i + 1];
+ }
+ for (j = 0; j < 2; j++) {
+ /* Legacy colours used 0-7 */
+ if (c[j] >= '0' && c[j] <= '7') {
+ colors[i].num[j] = c[j] - '0';
+ if (!legacy_warn) {
+ fprintf(stderr,
+ "warn: LSCOLORS should use "
+ "characters a-h instead of 0-9 ("
+ "see the manual page)\n");
+ }
+ legacy_warn = 1;
+ } else if (c[j] >= 'a' && c[j] <= 'h')
+ colors[i].num[j] = c[j] - 'a';
+ else if (c[j] >= 'A' && c[j] <= 'H') {
+ colors[i].num[j] = c[j] - 'A';
+ colors[i].bold = 1;
+ } else if (tolower((unsigned char)c[j] == 'x'))
+ colors[i].num[j] = -1;
+ else {
+ fprintf(stderr,
+ "error: invalid character '%c' in LSCOLORS"
+ " env var\n", c[j]);
+ colors[i].num[j] = -1;
+ }
+ }
+ }
+}
+
+void
+colorquit(int sig)
+{
+ endcolor(sig);
+
+ (void)signal(sig, SIG_DFL);
+ (void)kill(getpid(), sig);
+}
+
+#endif /* COLORLS */
+
+static void
+printlink(FTSENT *p)
+{
+ int lnklen;
+ char name[MAXPATHLEN + 1];
+ char path[MAXPATHLEN + 1];
+
+ if (p->fts_level == FTS_ROOTLEVEL)
+ (void)snprintf(name, sizeof(name), "%s", p->fts_name);
+ else
+ (void)snprintf(name, sizeof(name),
+ "%s/%s", p->fts_parent->fts_accpath, p->fts_name);
+ if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) {
+ (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno));
+ return;
+ }
+ path[lnklen] = '\0';
+ (void)printf(" -> ");
+ (void)printname(path);
+}
+
+static void
+printsize(size_t width, off_t bytes)
+{
+
+ if (f_humanval) {
+ char buf[5];
+
+ humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ (void)printf("%5s ", buf);
+ } else
+ (void)printf("%*jd ", (u_int)width, (intmax_t)bytes);
+}
diff --git a/file_cmds/ls/util.c b/file_cmds/ls/util.c
new file mode 100644
index 0000000..1aae3f1
--- /dev/null
+++ b/file_cmds/ls/util.c
@@ -0,0 +1,231 @@
+/*-
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/bin/ls/util.c,v 1.38 2005/06/03 11:05:58 dd Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int
+prn_normal(const char *s)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ int i, n;
+ size_t clen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ n = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ if (clen == (size_t)-2) {
+ n += printf("%s", s);
+ break;
+ }
+ if (clen == (size_t)-1) {
+ memset(&mbs, 0, sizeof(mbs));
+ putchar((unsigned char)*s);
+ s++;
+ n++;
+ continue;
+ }
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ s += clen;
+ if (iswprint(wc))
+ n += wcwidth(wc);
+ }
+ return (n);
+}
+
+int
+prn_printable(const char *s)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ int i, n;
+ size_t clen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ n = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ if (clen == (size_t)-1) {
+ putchar('?');
+ s++;
+ n++;
+ memset(&mbs, 0, sizeof(mbs));
+ continue;
+ }
+ if (clen == (size_t)-2) {
+ putchar('?');
+ n++;
+ break;
+ }
+ if (!iswprint(wc)) {
+ putchar('?');
+ s += clen;
+ n++;
+ continue;
+ }
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ s += clen;
+ n += wcwidth(wc);
+ }
+ return (n);
+}
+
+/*
+ * The fts system makes it difficult to replace fts_name with a different-
+ * sized string, so we just calculate the real length here and do the
+ * conversion in prn_octal()
+ *
+ * XXX when using f_octal_escape (-b) rather than f_octal (-B), the
+ * length computed by len_octal may be too big. I just can't be buggered
+ * to fix this as an efficient fix would involve a lookup table. Same goes
+ * for the rather inelegant code in prn_octal.
+ *
+ * DES 1998/04/23
+ */
+
+size_t
+len_octal(const char *s, int len)
+{
+ mbstate_t mbs;
+ wchar_t wc;
+ size_t clen, r;
+
+ memset(&mbs, 0, sizeof(mbs));
+ r = 0;
+ while (len != 0 && (clen = mbrtowc(&wc, s, len, &mbs)) != 0) {
+ if (clen == (size_t)-1) {
+ r += 4;
+ s++;
+ len--;
+ memset(&mbs, 0, sizeof(mbs));
+ continue;
+ }
+ if (clen == (size_t)-2) {
+ r += 4 * len;
+ break;
+ }
+ if (iswprint(wc))
+ r++;
+ else
+ r += 4 * clen;
+ s += clen;
+ }
+ return (r);
+}
+
+int
+prn_octal(const char *s)
+{
+ static const char esc[] = "\\\\\"\"\aa\bb\ff\nn\rr\tt\vv";
+ const char *p;
+ mbstate_t mbs;
+ wchar_t wc;
+ size_t clen;
+ unsigned char ch;
+ int goodchar, i, len, prtlen;
+
+ memset(&mbs, 0, sizeof(mbs));
+ len = 0;
+ while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) {
+ goodchar = clen != (size_t)-1 && clen != (size_t)-2;
+ if (goodchar && iswprint(wc) && wc != L'\"' && wc != L'\\') {
+ for (i = 0; i < (int)clen; i++)
+ putchar((unsigned char)s[i]);
+ len += wcwidth(wc);
+ } else if (goodchar && f_octal_escape && wc >= 0 &&
+ wc <= (wchar_t)UCHAR_MAX &&
+ (p = strchr(esc, (char)wc)) != NULL) {
+ putchar('\\');
+ putchar(p[1]);
+ len += 2;
+ } else {
+ if (goodchar)
+ prtlen = clen;
+ else if (clen == (size_t)-1)
+ prtlen = 1;
+ else
+ prtlen = strlen(s);
+ for (i = 0; i < prtlen; i++) {
+ ch = (unsigned char)s[i];
+ putchar('\\');
+ putchar('0' + (ch >> 6));
+ putchar('0' + ((ch >> 3) & 7));
+ putchar('0' + (ch & 7));
+ len += 4;
+ }
+ }
+ if (clen == (size_t)-2)
+ break;
+ if (clen == (size_t)-1) {
+ memset(&mbs, 0, sizeof(mbs));
+ s++;
+ } else
+ s += clen;
+ }
+ return (len);
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+#ifdef COLORLS
+ "usage: ls [-@ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1%%]"
+#else
+ "usage: ls [-@ABCFHLOPRSTUWabcdefghiklmnopqrstuwx1%%]"
+#endif
+ " [file ...]\n");
+ exit(1);
+}