summaryrefslogtreecommitdiff
path: root/bin/ls
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ls')
-rw-r--r--bin/ls/cmp.c199
-rw-r--r--bin/ls/extern.h53
-rw-r--r--bin/ls/ls.1516
-rw-r--r--bin/ls/ls.c715
-rw-r--r--bin/ls/ls.h81
-rw-r--r--bin/ls/main.c50
-rw-r--r--bin/ls/print.c497
-rw-r--r--bin/ls/util.c168
8 files changed, 2279 insertions, 0 deletions
diff --git a/bin/ls/cmp.c b/bin/ls/cmp.c
new file mode 100644
index 0000000..21d50e1
--- /dev/null
+++ b/bin/ls/cmp.c
@@ -0,0 +1,199 @@
+/* $NetBSD: cmp.c,v 1.17 2003/08/07 09:05:14 agc Exp $ */
+
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: cmp.c,v 1.17 2003/08/07 09:05:14 agc Exp $");
+#endif
+#endif /* not lint */
+
+#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)
+#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)
+#endif
+
+int
+namecmp(const FTSENT *a, const FTSENT *b)
+{
+
+ return (strcmp(a->fts_name, b->fts_name));
+}
+
+int
+revnamecmp(const FTSENT *a, const FTSENT *b)
+{
+
+ return (strcmp(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(const FTSENT *a, const FTSENT *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(const FTSENT *a, const FTSENT *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));
+}
diff --git a/bin/ls/extern.h b/bin/ls/extern.h
new file mode 100644
index 0000000..0a9eea0
--- /dev/null
+++ b/bin/ls/extern.h
@@ -0,0 +1,53 @@
+/* $NetBSD: extern.h,v 1.17 2011/08/29 14:44:21 joerg Exp $ */
+
+/*-
+ * 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. 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 5/31/93
+ */
+
+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 ls_main(int, char *[]);
+
+int printescaped(const char *);
+void printacol(DISPLAY *);
+void printcol(DISPLAY *);
+void printlong(DISPLAY *);
+void printscol(DISPLAY *);
+void printstream(DISPLAY *);
+int safe_print(const char *);
diff --git a/bin/ls/ls.1 b/bin/ls/ls.1
new file mode 100644
index 0000000..1bd9161
--- /dev/null
+++ b/bin/ls/ls.1
@@ -0,0 +1,516 @@
+.\" $NetBSD: ls.1,v 1.80 2017/07/03 21:33:23 wiz Exp $
+.\"
+.\" 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. 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
+.\"
+.Dd August 10, 2016
+.Dt LS 1
+.Os
+.Sh NAME
+.Nm ls
+.Nd list directory contents
+.Sh SYNOPSIS
+.Nm
+.Op Fl 1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx
+.Op Ar
+.Sh DESCRIPTION
+For each
+.Ar file
+operand that names a file of a type other than
+directory,
+.Nm
+displays its name as well as any requested,
+associated information.
+For each
+.Ar file
+operand that names a file of type directory,
+.Nm
+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 \&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
+.Ql \&.
+and
+.Ql \&.. .
+Always set for the super-user.
+.It Fl a
+Include directory entries whose names begin with a
+dot
+.Pq Sq \&. .
+.It Fl B
+Force printing of non-graphic characters in file names as \exxx, where xxx
+is the numeric value of the character in octal.
+.It Fl b
+As
+.Fl B ,
+but use 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,
+instead of time of last modification of the file for printing
+.Pq Fl l
+or sorting
+.Pq Fl t .
+Overrides
+.Fl u .
+.It Fl d
+Directories are listed as plain files (not searched recursively) and
+symbolic links in the argument list are not followed.
+Turns off
+.Fl R
+if also given.
+.It Fl F
+Display a slash
+.Pq Sq \&/
+immediately after each pathname that is a directory,
+an asterisk
+.Pq Sq \&*
+after each that is executable,
+an at sign
+.Pq Sq \&@
+after each symbolic link,
+a percent sign
+.Pq Sq \&%
+after each whiteout,
+an equal sign
+.Pq Sq \&=
+after each socket,
+and a vertical bar
+.Pq Sq \&|
+after each that is a
+.Tn FIFO .
+.It Fl f
+Output is not sorted.
+This option implies
+.Fl a .
+.It Fl g
+The same as
+.Fl l ,
+except that the owner is not printed.
+.It Fl h
+Modifies the
+.Fl l
+and
+.Fl s
+options, causing the sizes to be reported in bytes displayed in a human
+readable format.
+Overrides
+.Fl k
+and
+.Fl M .
+.It Fl i
+For each file, print the file's file serial number (inode number).
+.It Fl k
+Modifies the
+.Fl s
+option, causing the sizes to be reported in kilobytes.
+Overrides
+.Fl h .
+.It Fl L
+For each file, if it's a link, evaluate file information and file type
+of the referenced file and not the link itself; however still print
+the link name, unless used with
+.Fl l ,
+for example.
+.It Fl l
+(The lowercase letter
+.Dq ell ) .
+List in long format.
+(See below.)
+.It Fl M
+Modifies the
+.Fl l
+and
+.Fl s
+options, causing the sizes or block counts reported to be separated with
+commas (or a locale appropriate separator) resulting in a more readable
+output.
+Overrides
+.Fl h ;
+does not override
+.Fl k .
+.It Fl m
+Stream output format; list files across the page, separated by commas.
+.It Fl n
+The same as
+.Fl l ,
+except that
+the owner and group IDs are displayed numerically rather than converting
+to a owner or group name.
+.It Fl O
+Output only leaf files (not directories), eliding other
+.Nm
+output.
+.It Fl o
+Include the file flags in a long
+.Pq Fl l
+output.
+If no file flags are set,
+.Dq -
+is displayed.
+(See
+.Xr chflags 1
+for a list of possible flags and their meanings.)
+.It Fl P
+Print the full pathname for each file.
+.It Fl p
+Display a slash
+.Pq Sq \&/
+immediately after each pathname that is a directory.
+.It Fl q
+Force printing of non-printable characters in file names as
+the character
+.Sq \&? ;
+this is the default when output is to a terminal.
+.It Fl R
+Recursively list subdirectories encountered.
+See also
+.Fl d .
+.It Fl r
+Reverse the order of the sort to get reverse
+lexicographical order or the smallest or oldest entries first.
+.It Fl S
+Sort by size, largest file first.
+.It Fl s
+Display the number of file system blocks actually used by each file, in units
+of 512 bytes or
+.Ev BLOCKSIZE
+(see
+.Sx ENVIRONMENT )
+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.
+.It Fl T
+When used with the
+.Fl l
+(the 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 printing
+.Pq Fl l
+or sorting
+.Pq Fl t .
+Overrides
+.Fl c .
+.It Fl W
+Display whiteouts when scanning directories.
+.It Fl w
+Force raw printing of non-printable characters.
+This is the default when output is not to a terminal.
+.It Fl x
+Multi-column output sorted across the page rather than down the page.
+.It Fl X
+Don't cross mount points when recursing.
+.El
+.Pp
+The
+.Fl B ,
+.Fl b ,
+.Fl q ,
+and
+.Fl w
+options all override each other; the last one specified determines
+the format used for non-printable characters.
+.Pp
+The
+.Fl 1 ,
+.Fl C ,
+.Fl g ,
+.Fl l ,
+.Fl m ,
+and
+.Fl x
+options all override each other; the last one specified determines
+the format used with the exception that if both
+.Fl l
+and
+.Fl g
+are specified,
+.Fl l
+will always override
+.Fl g ,
+even if
+.Fl g
+was specified last.
+.Pp
+By default,
+.Nm
+lists one entry per line to standard
+output; the exceptions are to terminals or when the
+.Fl C
+or
+.Fl m
+options are specified.
+.Pp
+File information is displayed with one or more
+.Aq blank
+characters separating the information associated with the
+.Fl i ,
+.Fl l ,
+and
+.Fl s
+options.
+.Ss The Long Format
+If the
+.Fl l
+option is given, the following information
+is displayed for each file:
+.Bl -item -offset indent -compact
+.It
+file mode
+.It
+number of links
+.It
+owner name
+.It
+group name
+.It
+file flags (if
+.Fl o
+given)
+.It
+number of bytes in the file
+.It
+abbreviated month file was last modified
+.It
+day-of-month file was last modified
+.It
+hour and minute file was last modified
+.It
+pathname
+.El
+.Pp
+In addition, for each directory whose contents are displayed, the total
+number of file system blocks in units of 512 bytes or
+.Ev BLOCKSIZE
+(see
+.Sx ENVIRONMENT )
+used by the files in the directory is displayed on a line by itself
+immediately before the information for the files in the directory.
+.Pp
+If the owner or group names are not a known owner 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 \-> .
+.Pp
+The file mode printed under the
+.Fl l
+option consists of the entry type, owner permissions, group
+permissions, and other permissions.
+The entry type character describes the type of file, as
+follows:
+.Pp
+.Bl -tag -width 4n -offset indent -compact
+.It Sy \-
+Regular file.
+.It Sy a
+Archive state 1.
+.It Sy A
+Archive state 2.
+.It Sy b
+Block special file.
+.It Sy c
+Character special file.
+.It Sy d
+Directory.
+.It Sy l
+Symbolic link.
+.It Sy p
+FIFO.
+.It Sy s
+Socket link.
+.It Sy w
+Whiteout.
+.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 7 . )
+.It Sy t
+The sticky bit is set (mode
+.Li 1000 ) ,
+and is searchable or executable.
+(See
+.Xr chmod 1
+or
+.Xr sticky 7 . )
+.El
+.El
+.Pp
+The number of bytes displayed for a directory is a function of the
+number of
+.Xr dirent 3
+structures in the directory, not all of which may be allocated to
+any existing file.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Fl k
+option is not specified, the block counts
+(see
+.Fl l
+and
+.Fl s )
+will be displayed in units of that size block.
+.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
+utility calculates how
+many pathname text columns to display
+based on the width provided.
+(See
+.Fl C . )
+.It Ev TZ
+The timezone to use when displaying dates.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.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 SEE ALSO
+.Xr chflags 1 ,
+.Xr chmod 1 ,
+.Xr stat 2 ,
+.Xr dirent 3 ,
+.Xr getbsize 3 ,
+.Xr sticky 7 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be a superset of the
+.St -p1003.2
+specification.
+.Sh HISTORY
+An
+.Nm
+utility appeared in
+.At v1 .
diff --git a/bin/ls/ls.c b/bin/ls/ls.c
new file mode 100644
index 0000000..282207f
--- /dev/null
+++ b/bin/ls/ls.c
@@ -0,0 +1,715 @@
+/* $NetBSD: ls.c,v 1.76 2017/02/06 21:06:04 rin Exp $ */
+
+/*
+ * 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. 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
+__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94";
+#else
+__RCSID("$NetBSD: ls.c,v 1.76 2017/02/06 21:06:04 rin Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#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 <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <pwd.h>
+#include <grp.h>
+#include <util.h>
+
+#include "ls.h"
+#include "extern.h"
+
+static void display(FTSENT *, FTSENT *);
+static int mastercmp(const FTSENT **, const FTSENT **);
+static void traverse(int, char **, int);
+
+static void (*printfcn)(DISPLAY *);
+static int (*sortfcn)(const FTSENT *, const FTSENT *);
+
+#define BY_NAME 0
+#define BY_SIZE 1
+#define BY_TIME 2
+
+long blocksize; /* block size units */
+int termwidth = 80; /* default terminal width */
+int sortkey = BY_NAME;
+int rval = EXIT_SUCCESS; /* exit value - set if error encountered */
+
+/* flags */
+int f_accesstime; /* use time of last access */
+int f_column; /* columnated format */
+int f_columnacross; /* columnated format, sorted across */
+int f_flags; /* show flags associated with a file */
+int f_grouponly; /* long listing without owner */
+int f_humanize; /* humanize the size field */
+int f_commas; /* separate size field with comma */
+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_nonprint; /* show unprintables as ? */
+int f_nosort; /* don't sort output */
+int f_numericonly; /* don't convert uid/gid to name */
+int f_octal; /* print octal escapes for nongraphic characters */
+int f_octal_escape; /* like f_octal but use C escapes if possible */
+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_stream; /* stream format */
+int f_type; /* add type character for non-regular files */
+int f_typedir; /* add type character for directories */
+int f_whiteout; /* show whiteout entries */
+int f_fullpath; /* print full pathname, not filename */
+int f_leafonly; /* when recursing, print leaf names only */
+
+__dead static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: %s [-1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx] [file ...]\n",
+ getprogname());
+ exit(EXIT_FAILURE);
+ /* NOTREACHED */
+}
+
+int
+ls_main(int argc, char *argv[])
+{
+ static char dot[] = ".", *dotav[] = { dot, NULL };
+ struct winsize win;
+ int ch, fts_options;
+ int kflag = 0;
+ const char *p;
+
+ setprogname(argv[0]);
+ (void)setlocale(LC_ALL, "");
+
+ /* Terminal defaults to -Cq, non-terminal defaults to -1. */
+ if (isatty(STDOUT_FILENO)) {
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
+ win.ws_col > 0)
+ termwidth = win.ws_col;
+ f_column = f_nonprint = 1;
+ } else
+ f_singlecol = 1;
+
+ /* Root is -A automatically. */
+ if (!getuid())
+ f_listdot = 1;
+
+ fts_options = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "1AaBbCcdFfghikLlMmnOoPpqRrSsTtuWwXx"))
+ != -1) {
+ switch (ch) {
+ /*
+ * The -1, -C, -l, -m and -x options all override each other so
+ * shell aliasing works correctly.
+ */
+ case '1':
+ f_singlecol = 1;
+ f_column = f_columnacross = f_longform = f_stream = 0;
+ break;
+ case 'C':
+ f_column = 1;
+ f_columnacross = f_longform = f_singlecol = f_stream =
+ 0;
+ break;
+ case 'g':
+ if (f_grouponly != -1)
+ f_grouponly = 1;
+ f_longform = 1;
+ f_column = f_columnacross = f_singlecol = f_stream = 0;
+ break;
+ case 'l':
+ f_longform = 1;
+ f_column = f_columnacross = f_singlecol = f_stream = 0;
+ /* Never let -g take precedence over -l. */
+ f_grouponly = -1;
+ break;
+ case 'm':
+ f_stream = 1;
+ f_column = f_columnacross = f_longform = f_singlecol =
+ 0;
+ break;
+ case 'x':
+ f_columnacross = 1;
+ f_column = f_longform = f_singlecol = f_stream = 0;
+ break;
+ /* The -c and -u options override each other. */
+ case 'c':
+ f_statustime = 1;
+ f_accesstime = 0;
+ break;
+ case 'u':
+ f_accesstime = 1;
+ f_statustime = 0;
+ break;
+ case 'F':
+ f_type = 1;
+ break;
+ case 'L':
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ break;
+ case 'R':
+ f_recursive = 1;
+ break;
+ case 'f':
+ f_nosort = 1;
+ /* FALLTHROUGH */
+ case 'a':
+ fts_options |= FTS_SEEDOT;
+ /* FALLTHROUGH */
+ case 'A':
+ f_listdot = 1;
+ break;
+ /* The -B option turns off the -b, -q and -w options. */
+ case 'B':
+ f_nonprint = 0;
+ f_octal = 1;
+ f_octal_escape = 0;
+ break;
+ /* The -b option turns off the -B, -q and -w options. */
+ case 'b':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 1;
+ break;
+ /* The -d option turns off the -R option. */
+ case 'd':
+ f_listdir = 1;
+ f_recursive = 0;
+ break;
+ case 'i':
+ f_inode = 1;
+ break;
+ case 'k':
+ blocksize = 1024;
+ kflag = 1;
+ f_humanize = 0;
+ break;
+ /* The -h option forces all sizes to be measured in bytes. */
+ case 'h':
+ f_humanize = 1;
+ kflag = 0;
+ f_commas = 0;
+ break;
+ case 'M':
+ f_humanize = 0;
+ f_commas = 1;
+ break;
+ case 'n':
+ f_numericonly = 1;
+ f_longform = 1;
+ f_column = f_columnacross = f_singlecol = f_stream = 0;
+ break;
+ case 'O':
+ f_leafonly = 1;
+ break;
+ case 'o':
+ f_flags = 1;
+ break;
+ case 'P':
+ f_fullpath = 1;
+ break;
+ case 'p':
+ f_typedir = 1;
+ break;
+ /* The -q option turns off the -B, -b and -w options. */
+ case 'q':
+ f_nonprint = 1;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'r':
+ f_reversesort = 1;
+ break;
+ case 'S':
+ sortkey = BY_SIZE;
+ break;
+ case 's':
+ f_size = 1;
+ break;
+ case 'T':
+ f_sectime = 1;
+ break;
+ case 't':
+ sortkey = BY_TIME;
+ break;
+ case 'W':
+ f_whiteout = 1;
+ break;
+ /* The -w option turns off the -B, -b and -q options. */
+ case 'w':
+ f_nonprint = 0;
+ f_octal = 0;
+ f_octal_escape = 0;
+ break;
+ case 'X':
+ fts_options |= FTS_XDEV;
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (f_column || f_columnacross || f_stream) {
+ if ((p = getenv("COLUMNS")) != NULL)
+ termwidth = atoi(p);
+ }
+
+ /*
+ * If both -g and -l options, let -l take precedence.
+ */
+ if (f_grouponly == -1)
+ f_grouponly = 0;
+
+ /*
+ * If not -F, -i, -l, -p, -S, -s or -t options, don't require stat
+ * information.
+ */
+ if (!f_inode && !f_longform && !f_size && !f_type && !f_typedir &&
+ sortkey == BY_NAME)
+ 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)
+ fts_options |= FTS_COMFOLLOW;
+
+ /*
+ * If -W, show whiteout entries
+ */
+#ifdef FTS_WHITEOUT
+ if (f_whiteout)
+ fts_options |= FTS_WHITEOUT;
+#endif
+
+ /* If -i, -l, or -s, figure out block size. */
+ if (f_inode || f_longform || f_size) {
+ if (!kflag)
+ (void)getbsize(NULL, &blocksize);
+ blocksize /= 512;
+ }
+
+ /* Select a sort function. */
+ if (f_reversesort) {
+ switch (sortkey) {
+ case BY_NAME:
+ sortfcn = revnamecmp;
+ break;
+ case BY_SIZE:
+ sortfcn = revsizecmp;
+ break;
+ case BY_TIME:
+ if (f_accesstime)
+ sortfcn = revacccmp;
+ else if (f_statustime)
+ sortfcn = revstatcmp;
+ else /* Use modification time. */
+ sortfcn = revmodcmp;
+ break;
+ }
+ } else {
+ switch (sortkey) {
+ case BY_NAME:
+ sortfcn = namecmp;
+ break;
+ case BY_SIZE:
+ sortfcn = sizecmp;
+ break;
+ case BY_TIME:
+ if (f_accesstime)
+ sortfcn = acccmp;
+ else if (f_statustime)
+ sortfcn = statcmp;
+ else /* Use modification time. */
+ sortfcn = modcmp;
+ break;
+ }
+ }
+
+ /* Select a print function. */
+ if (f_singlecol)
+ printfcn = printscol;
+ else if (f_columnacross)
+ printfcn = printacol;
+ else if (f_longform)
+ printfcn = printlong;
+ else if (f_stream)
+ printfcn = printstream;
+ else
+ printfcn = printcol;
+
+ if (argc)
+ traverse(argc, argv, fts_options);
+ else
+ traverse(1, dotav, fts_options);
+ return rval;
+ /* NOTREACHED */
+}
+
+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(EXIT_FAILURE, NULL);
+
+ display(NULL, fts_children(ftsp, 0));
+ if (f_listdir) {
+ (void)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);
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
+ rval = EXIT_FAILURE;
+ break;
+ case FTS_D:
+ if (p->fts_level != FTS_ROOTLEVEL &&
+ p->fts_name[0] == '.' && !f_listdot)
+ break;
+
+ /*
+ * If already output something, put out a newline as
+ * a separator. If multiple arguments, precede each
+ * directory with its name.
+ */
+ if (!f_leafonly) {
+ 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);
+ display(p, chp);
+
+ if (!f_recursive && chp != NULL)
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+ error = errno;
+ (void)fts_close(ftsp);
+ errno = error;
+ if (errno)
+ err(EXIT_FAILURE, "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;
+ u_int64_t btotal, stotal;
+ off_t maxsize;
+ blkcnt_t maxblock;
+ ino_t maxinode;
+ int maxmajor, maxminor;
+ uint32_t maxnlink;
+ int bcfile, entries, flen, glen, ulen, maxflags, maxgroup;
+ unsigned int maxlen;
+ int maxuser, needstats;
+ const char *user, *group;
+ char buf[21]; /* 64 bits == 20 digits, +1 for NUL */
+ char nuser[12], ngroup[12];
+ char *flags = NULL;
+
+ /*
+ * 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)
+ return;
+
+ needstats = f_inode || f_longform || f_size;
+ flen = 0;
+ maxinode = maxnlink = 0;
+ bcfile = 0;
+ maxuser = maxgroup = maxflags = maxlen = 0;
+ btotal = stotal = maxblock = maxsize = 0;
+ maxmajor = maxminor = 0;
+ 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 = EXIT_FAILURE;
+ 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 (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;
+ if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) {
+ bcfile = 1;
+ if (major(sp->st_rdev) > maxmajor)
+ maxmajor = major(sp->st_rdev);
+ if (minor(sp->st_rdev) > maxminor)
+ maxminor = minor(sp->st_rdev);
+ }
+
+ btotal += sp->st_blocks;
+ stotal += sp->st_size;
+ if (f_longform) {
+ if (f_numericonly ||
+ (user = user_from_uid(sp->st_uid, 0)) ==
+ NULL) {
+ (void)snprintf(nuser, sizeof(nuser),
+ "%u", sp->st_uid);
+ user = nuser;
+ }
+ if (f_numericonly ||
+ (group = group_from_gid(sp->st_gid, 0)) ==
+ NULL) {
+ (void)snprintf(ngroup, sizeof(ngroup),
+ "%u", sp->st_gid);
+ group = ngroup;
+ }
+ if ((ulen = strlen(user)) > maxuser)
+ maxuser = ulen;
+ if ((glen = strlen(group)) > maxgroup)
+ maxgroup = glen;
+ if (f_flags) {
+ flags =
+ flags_to_string((u_long)sp->st_flags, "-");
+ if ((flen = strlen(flags)) > maxflags)
+ maxflags = flen;
+ } else
+ flen = 0;
+
+ if ((np = malloc(sizeof(NAMES) +
+ ulen + glen + flen + 2)) == NULL)
+ err(EXIT_FAILURE, NULL);
+
+ np->user = &np->data[0];
+ (void)strcpy(np->user, user);
+ np->group = &np->data[ulen + 1];
+ (void)strcpy(np->group, group);
+
+ 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.btotal = btotal;
+ d.stotal = stotal;
+ if (f_humanize) {
+ d.s_block = 4; /* min buf length for humanize_number */
+ } else {
+ (void)snprintf(buf, sizeof(buf), "%lld",
+ (long long)howmany(maxblock, blocksize));
+ d.s_block = strlen(buf);
+ if (f_commas) /* allow for commas before every third digit */
+ d.s_block += (d.s_block - 1) / 3;
+ }
+ d.s_flags = maxflags;
+ d.s_group = maxgroup;
+ (void)snprintf(buf, sizeof(buf), "%llu",
+ (unsigned long long)maxinode);
+ d.s_inode = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%u", maxnlink);
+ d.s_nlink = strlen(buf);
+ if (f_humanize) {
+ d.s_size = 4; /* min buf length for humanize_number */
+ } else {
+ (void)snprintf(buf, sizeof(buf), "%lld",
+ (long long)maxsize);
+ d.s_size = strlen(buf);
+ if (f_commas) /* allow for commas before every third digit */
+ d.s_size += (d.s_size - 1) / 3;
+ }
+ d.s_user = maxuser;
+ if (bcfile) {
+ (void)snprintf(buf, sizeof(buf), "%d", maxmajor);
+ d.s_major = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%d", maxminor);
+ d.s_minor = strlen(buf);
+ if (d.s_major + d.s_minor + 2 > d.s_size)
+ d.s_size = d.s_major + d.s_minor + 2;
+ else if (d.s_size - d.s_minor - 2 > d.s_major)
+ d.s_major = d.s_size - d.s_minor - 2;
+ } else {
+ d.s_major = 0;
+ d.s_minor = 0;
+ }
+ }
+
+ printfcn(&d);
+ output = 1;
+
+ if (f_longform)
+ for (cur = list; cur; cur = cur->fts_link)
+ free(cur->fts_pointer);
+}
+
+/*
+ * 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) {
+ if (b_info != FTS_NS)
+ return (1);
+ else if (a_info != FTS_NS)
+ return (-1);
+ else
+ return (namecmp(*a, *b));
+ }
+
+ if (a_info != b_info && !f_listdir &&
+ (*a)->fts_level == FTS_ROOTLEVEL) {
+ if (a_info == FTS_D)
+ return (1);
+ else if (b_info == FTS_D)
+ return (-1);
+ }
+ return (sortfcn(*a, *b));
+}
diff --git a/bin/ls/ls.h b/bin/ls/ls.h
new file mode 100644
index 0000000..d9b6505
--- /dev/null
+++ b/bin/ls/ls.h
@@ -0,0 +1,81 @@
+/* $NetBSD: ls.h,v 1.19 2014/02/20 18:56:36 christos Exp $ */
+
+/*
+ * 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. 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.h 8.1 (Berkeley) 5/31/93
+ */
+
+#define NO_PRINT 1
+
+extern long blocksize; /* block size units */
+
+extern int f_accesstime; /* use time of last access */
+extern int f_flags; /* show flags associated with a file */
+extern int f_grouponly; /* long listing without owner */
+extern int f_humanize; /* humanize size field */
+extern int f_commas; /* separate size field with commas */
+extern int f_inode; /* print inode */
+extern int f_longform; /* long listing format */
+extern int f_octal; /* print octal escapes for nongraphic characters */
+extern int f_octal_escape; /* like f_octal but use C escapes if possible */
+extern int f_sectime; /* print the real time for all files */
+extern int f_size; /* list size in short listing */
+extern int f_statustime; /* use time of last mode change */
+extern int f_type; /* add type character for non-regular files */
+extern int f_typedir; /* add type character for directories */
+extern int f_nonprint; /* show unprintables as ? */
+extern int f_fullpath; /* print full pathname, not filename */
+extern int f_leafonly; /* when recursing, print leaf names only */
+
+typedef struct {
+ FTSENT *list;
+ u_int64_t btotal;
+ u_int64_t stotal;
+ int entries;
+ unsigned int maxlen;
+ int s_block;
+ int s_flags;
+ int s_group;
+ int s_inode;
+ int s_nlink;
+ int s_size;
+ int s_user;
+ int s_major;
+ int s_minor;
+} DISPLAY;
+
+typedef struct {
+ char *user;
+ char *group;
+ char *flags;
+ char data[1];
+} NAMES;
diff --git a/bin/ls/main.c b/bin/ls/main.c
new file mode 100644
index 0000000..c1316aa
--- /dev/null
+++ b/bin/ls/main.c
@@ -0,0 +1,50 @@
+/* $NetBSD: main.c,v 1.5 2016/09/05 01:00:07 sevan Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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
+__RCSID("$NetBSD: main.c,v 1.5 2016/09/05 01:00:07 sevan Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <fts.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int
+main(int argc, char *argv[])
+{
+
+ return ls_main(argc, argv);
+ /* NOTREACHED */
+}
diff --git a/bin/ls/print.c b/bin/ls/print.c
new file mode 100644
index 0000000..b3aecd8
--- /dev/null
+++ b/bin/ls/print.c
@@ -0,0 +1,497 @@
+/* $NetBSD: print.c,v 1.55 2014/05/10 09:39:18 martin Exp $ */
+
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94";
+#else
+__RCSID("$NetBSD: print.c,v 1.55 2014/05/10 09:39:18 martin Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "ls.h"
+#include "extern.h"
+
+extern int termwidth;
+
+static int printaname(FTSENT *, int, int);
+static void printlink(FTSENT *);
+static void printtime(time_t);
+static void printtotal(DISPLAY *dp);
+static int printtype(u_int);
+
+static time_t now;
+
+#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT)
+
+static int
+safe_printpath(const FTSENT *p) {
+ int chcnt;
+
+ if (f_fullpath) {
+ chcnt = safe_print(p->fts_path);
+ chcnt += safe_print("/");
+ } else
+ chcnt = 0;
+ return chcnt + safe_print(p->fts_name);
+}
+
+static int
+printescapedpath(const FTSENT *p) {
+ int chcnt;
+
+ if (f_fullpath) {
+ chcnt = printescaped(p->fts_path);
+ chcnt += printescaped("/");
+ } else
+ chcnt = 0;
+
+ return chcnt + printescaped(p->fts_name);
+}
+
+static int
+printpath(const FTSENT *p) {
+ if (f_fullpath)
+ return printf("%s/%s", p->fts_path, p->fts_name);
+ else
+ return printf("%s", p->fts_name);
+}
+
+void
+printscol(DISPLAY *dp)
+{
+ FTSENT *p;
+
+ 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');
+ }
+}
+
+void
+printlong(DISPLAY *dp)
+{
+ struct stat *sp;
+ FTSENT *p;
+ NAMES *np;
+ char buf[20], szbuf[5];
+
+ now = time(NULL);
+
+ if (!f_leafonly)
+ printtotal(dp); /* "total: %u\n" */
+
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ sp = p->fts_statp;
+ if (f_inode)
+ (void)printf("%*"PRIu64" ", dp->s_inode, sp->st_ino);
+ if (f_size) {
+ if (f_humanize) {
+ if ((humanize_number(szbuf, sizeof(szbuf),
+ sp->st_blocks * S_BLKSIZE,
+ "", HN_AUTOSCALE,
+ (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
+ err(1, "humanize_number");
+ (void)printf("%*s ", dp->s_block, szbuf);
+ } else {
+ (void)printf(f_commas ? "%'*llu " : "%*llu ",
+ dp->s_block,
+ (unsigned long long)howmany(sp->st_blocks,
+ blocksize));
+ }
+ }
+ (void)strmode(sp->st_mode, buf);
+ np = p->fts_pointer;
+ (void)printf("%s %*lu ", buf, dp->s_nlink,
+ (unsigned long)sp->st_nlink);
+ if (!f_grouponly)
+ (void)printf("%-*s ", dp->s_user, np->user);
+ (void)printf("%-*s ", dp->s_group, np->group);
+ if (f_flags)
+ (void)printf("%-*s ", dp->s_flags, np->flags);
+ if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode))
+ (void)printf("%*lld, %*lld ",
+ dp->s_major, (long long)major(sp->st_rdev),
+ dp->s_minor, (long long)minor(sp->st_rdev));
+ else
+ if (f_humanize) {
+ if ((humanize_number(szbuf, sizeof(szbuf),
+ sp->st_size, "", HN_AUTOSCALE,
+ (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
+ err(1, "humanize_number");
+ (void)printf("%*s ", dp->s_size, szbuf);
+ } else {
+ (void)printf(f_commas ? "%'*llu " : "%*llu ",
+ dp->s_size, (unsigned long long)
+ sp->st_size);
+ }
+ if (f_accesstime)
+ printtime(sp->st_atime);
+ else if (f_statustime)
+ printtime(sp->st_ctime);
+ else
+ printtime(sp->st_mtime);
+ if (f_octal || f_octal_escape)
+ (void)safe_printpath(p);
+ else if (f_nonprint)
+ (void)printescapedpath(p);
+ else
+ (void)printpath(p);
+
+ if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
+ (void)printtype(sp->st_mode);
+ if (S_ISLNK(sp->st_mode))
+ printlink(p);
+ (void)putchar('\n');
+ }
+}
+
+void
+printcol(DISPLAY *dp)
+{
+ static FTSENT **array;
+ static int lastentries = -1;
+ FTSENT *p;
+ int base, chcnt, col, colwidth, num;
+ int numcols, numrows, row;
+
+ colwidth = dp->maxlen;
+ if (f_inode)
+ colwidth += dp->s_inode + 1;
+ if (f_size) {
+ if (f_humanize)
+ colwidth += dp->s_size + 1;
+ else
+ colwidth += dp->s_block + 1;
+ }
+ if (f_type || f_typedir)
+ colwidth += 1;
+
+ colwidth += 1;
+
+ if (termwidth < 2 * colwidth) {
+ printscol(dp);
+ return;
+ }
+
+ /*
+ * Have to do random access in the linked list -- build a table
+ * of pointers.
+ */
+ if (dp->entries > lastentries) {
+ FTSENT **newarray;
+
+ newarray = realloc(array, dp->entries * sizeof(FTSENT *));
+ if (newarray == NULL) {
+ warn(NULL);
+ printscol(dp);
+ return;
+ }
+ lastentries = dp->entries;
+ array = newarray;
+ }
+ for (p = dp->list, num = 0; p; p = p->fts_link)
+ if (p->fts_number != NO_PRINT)
+ array[num++] = p;
+
+ numcols = termwidth / colwidth;
+ colwidth = termwidth / numcols; /* spread out if possible */
+ numrows = num / numcols;
+ if (num % numcols)
+ ++numrows;
+
+ printtotal(dp); /* "total: %u\n" */
+
+ for (row = 0; row < numrows; ++row) {
+ for (base = row, chcnt = col = 0; col < numcols; ++col) {
+ chcnt = printaname(array[base], dp->s_inode,
+ f_humanize ? dp->s_size : dp->s_block);
+ if ((base += numrows) >= num)
+ break;
+ while (chcnt++ < colwidth)
+ (void)putchar(' ');
+ }
+ (void)putchar('\n');
+ }
+}
+
+void
+printacol(DISPLAY *dp)
+{
+ FTSENT *p;
+ int chcnt, col, colwidth;
+ int numcols;
+
+ colwidth = dp->maxlen;
+ if (f_inode)
+ colwidth += dp->s_inode + 1;
+ if (f_size) {
+ if (f_humanize)
+ colwidth += dp->s_size + 1;
+ else
+ colwidth += dp->s_block + 1;
+ }
+ if (f_type || f_typedir)
+ colwidth += 1;
+
+ colwidth += 1;
+
+ if (termwidth < 2 * colwidth) {
+ printscol(dp);
+ return;
+ }
+
+ numcols = termwidth / colwidth;
+ colwidth = termwidth / numcols; /* spread out if possible */
+
+ printtotal(dp); /* "total: %u\n" */
+
+ chcnt = col = 0;
+ for (p = dp->list; p; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ if (col >= numcols) {
+ chcnt = col = 0;
+ (void)putchar('\n');
+ }
+ chcnt = printaname(p, dp->s_inode,
+ f_humanize ? dp->s_size : dp->s_block);
+ while (chcnt++ < colwidth)
+ (void)putchar(' ');
+ col++;
+ }
+ (void)putchar('\n');
+}
+
+void
+printstream(DISPLAY *dp)
+{
+ FTSENT *p;
+ int col;
+ int extwidth;
+
+ extwidth = 0;
+ if (f_inode)
+ extwidth += dp->s_inode + 1;
+ if (f_size) {
+ if (f_humanize)
+ extwidth += dp->s_size + 1;
+ else
+ extwidth += dp->s_block + 1;
+ }
+ if (f_type)
+ extwidth += 1;
+
+ for (col = 0, p = dp->list; p != NULL; p = p->fts_link) {
+ if (IS_NOPRINT(p))
+ continue;
+ if (col > 0) {
+ (void)putchar(','), col++;
+ if (col + 1 + extwidth + (int)p->fts_namelen >= termwidth)
+ (void)putchar('\n'), col = 0;
+ else
+ (void)putchar(' '), col++;
+ }
+ col += printaname(p, dp->s_inode,
+ f_humanize ? dp->s_size : dp->s_block);
+ }
+ (void)putchar('\n');
+}
+
+/*
+ * print [inode] [size] name
+ * return # of characters printed, no trailing characters.
+ */
+static int
+printaname(FTSENT *p, int inodefield, int sizefield)
+{
+ struct stat *sp;
+ int chcnt;
+ char szbuf[5];
+
+ sp = p->fts_statp;
+ chcnt = 0;
+ if (f_inode)
+ chcnt += printf("%*"PRIu64" ", inodefield, sp->st_ino);
+ if (f_size) {
+ if (f_humanize) {
+ if ((humanize_number(szbuf, sizeof(szbuf), sp->st_size,
+ "", HN_AUTOSCALE,
+ (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
+ err(1, "humanize_number");
+ chcnt += printf("%*s ", sizefield, szbuf);
+ } else {
+ chcnt += printf(f_commas ? "%'*llu " : "%*llu ",
+ sizefield, (unsigned long long)
+ howmany(sp->st_blocks, blocksize));
+ }
+ }
+ if (f_octal || f_octal_escape)
+ chcnt += safe_printpath(p);
+ else if (f_nonprint)
+ chcnt += printescapedpath(p);
+ else
+ chcnt += printpath(p);
+ if (f_type || (f_typedir && S_ISDIR(sp->st_mode)))
+ chcnt += printtype(sp->st_mode);
+ return (chcnt);
+}
+
+static void
+printtime(time_t ftime)
+{
+ int i;
+ const char *longstring;
+
+ if ((longstring = ctime(&ftime)) == NULL) {
+ /* 012345678901234567890123 */
+ longstring = "????????????????????????";
+ }
+ for (i = 4; i < 11; ++i)
+ (void)putchar(longstring[i]);
+
+#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY)
+ if (f_sectime)
+ for (i = 11; i < 24; i++)
+ (void)putchar(longstring[i]);
+ else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now)
+ for (i = 11; i < 16; ++i)
+ (void)putchar(longstring[i]);
+ else {
+ (void)putchar(' ');
+ for (i = 20; i < 24; ++i)
+ (void)putchar(longstring[i]);
+ }
+ (void)putchar(' ');
+}
+
+/*
+ * Display total used disk space in the form "total: %u\n".
+ * Note: POSIX (IEEE Std 1003.1-2001) says this should be always in 512 blocks,
+ * but we humanise it with -h, or separate it with commas with -M, and use 1024
+ * with -k.
+ */
+static void
+printtotal(DISPLAY *dp)
+{
+ char szbuf[5];
+
+ if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) {
+ if (f_humanize) {
+ if ((humanize_number(szbuf, sizeof(szbuf), (int64_t)dp->stotal,
+ "", HN_AUTOSCALE,
+ (HN_DECIMAL | HN_B | HN_NOSPACE))) == -1)
+ err(1, "humanize_number");
+ (void)printf("total %s\n", szbuf);
+ } else {
+ (void)printf(f_commas ? "total %'llu\n" :
+ "total %llu\n", (unsigned long long)
+ howmany(dp->btotal, blocksize));
+ }
+ }
+}
+
+static int
+printtype(u_int mode)
+{
+ 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);
+ }
+ if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ (void)putchar('*');
+ return (1);
+ }
+ return (0);
+}
+
+static void
+printlink(FTSENT *p)
+{
+ int lnklen;
+ char name[MAXPATHLEN + 1], 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(" -> ");
+ if (f_octal || f_octal_escape)
+ (void)safe_print(path);
+ else if (f_nonprint)
+ (void)printescaped(path);
+ else
+ (void)printf("%s", path);
+}
diff --git a/bin/ls/util.c b/bin/ls/util.c
new file mode 100644
index 0000000..61b0fda
--- /dev/null
+++ b/bin/ls/util.c
@@ -0,0 +1,168 @@
+/* $NetBSD: util.c,v 1.34 2011/08/29 14:44:21 joerg Exp $ */
+
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)util.c 8.5 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: util.c,v 1.34 2011/08/29 14:44:21 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vis.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int
+safe_print(const char *src)
+{
+ size_t len;
+ char *name;
+ int flags;
+
+ flags = VIS_NL | VIS_OCTAL | VIS_WHITE;
+ if (f_octal_escape)
+ flags |= VIS_CSTYLE;
+
+ len = strlen(src);
+ if (len != 0 && SIZE_T_MAX/len <= 4) {
+ errx(EXIT_FAILURE, "%s: name too long", src);
+ /* NOTREACHED */
+ }
+
+ name = (char *)malloc(4*len+1);
+ if (name != NULL) {
+ len = strvis(name, src, flags);
+ (void)printf("%s", name);
+ free(name);
+ return len;
+ } else
+ errx(EXIT_FAILURE, "out of memory!");
+ /* NOTREACHED */
+}
+
+/*
+ * The reasons why we don't use putwchar(wc) here are:
+ * - If wc == L'\0', we need to restore the initial shift state, but
+ * the C language standard doesn't say that putwchar(L'\0') does.
+ * - It isn't portable to mix a wide-oriented function (i.e. getwchar)
+ * with byte-oriented functions (printf et al.) in same FILE.
+ */
+static int
+printwc(wchar_t wc, mbstate_t *pst)
+{
+ size_t size;
+ char buf[MB_LEN_MAX];
+
+ size = wcrtomb(buf, wc, pst);
+ if (size == (size_t)-1) /* This shouldn't happen, but for sure */
+ return 0;
+ if (wc == L'\0') {
+ /* The following condition must be always true, but for sure */
+ if (size > 0 && buf[size - 1] == '\0')
+ --size;
+ }
+ if (size > 0)
+ fwrite(buf, 1, size, stdout);
+ return wc == L'\0' ? 0 : wcwidth(wc);
+}
+
+int
+printescaped(const char *src)
+{
+ int n = 0;
+ mbstate_t src_state, stdout_state;
+ /* The following +1 is to pass '\0' at the end of src to mbrtowc(). */
+ const char *endptr = src + strlen(src) + 1;
+
+ /*
+ * We have to reset src_state each time in this function, because
+ * the codeset of src pathname may not match with current locale.
+ * Note that if we pass NULL instead of src_state to mbrtowc(),
+ * there is no way to reset the state.
+ */
+ memset(&src_state, 0, sizeof(src_state));
+ memset(&stdout_state, 0, sizeof(stdout_state));
+ while (src < endptr) {
+ wchar_t wc;
+ size_t rv, span = endptr - src;
+
+#if 0
+ /*
+ * XXX - we should fix libc instead.
+ * Theoretically this should work, but our current
+ * implementation of iso2022 module doesn't actually work
+ * as expected, if there are redundant escape sequences
+ * which exceed 32 bytes.
+ */
+ if (span > MB_CUR_MAX)
+ span = MB_CUR_MAX;
+#endif
+ rv = mbrtowc(&wc, src, span, &src_state);
+ if (rv == 0) { /* assert(wc == L'\0'); */
+ /* The following may output a shift sequence. */
+ n += printwc(wc, &stdout_state);
+ break;
+ }
+ if (rv == (size_t)-1) { /* probably errno == EILSEQ */
+ n += printwc(L'?', &stdout_state);
+ /* try to skip 1byte, because there is no better way */
+ src++;
+ memset(&src_state, 0, sizeof(src_state));
+ } else if (rv == (size_t)-2) {
+ if (span < MB_CUR_MAX) { /* incomplete char */
+ n += printwc(L'?', &stdout_state);
+ break;
+ }
+ src += span; /* a redundant shift sequence? */
+ } else {
+ n += printwc(iswprint(wc) ? wc : L'?', &stdout_state);
+ src += rv;
+ }
+ }
+ return n;
+}