--- /dev/null
+# $NetBSD: Makefile,v 1.14 2006/12/14 20:09:36 he Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/2/93
+
+PROG= ls
+SRCS= cmp.c ls.c main.c print.c util.c
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+.\" $NetBSD: ls.1,v 1.69 2011/04/02 08:38:56 mbalmer 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 April 2, 2011
+.Dt LS 1
+.Os
+.Sh NAME
+.Nm ls
+.Nd list directory contents
+.Sh SYNOPSIS
+.Nm
+.Op Fl AaBbCcdFfghikLlMmnopqRrSsTtuWwx1
+.Op Ar
+.Sh DESCRIPTION
+For each operand that names a
+.Ar file
+of a type other than
+directory,
+.Nm
+displays its name as well as any requested,
+associated information.
+For each operand that names a
+.Ar 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 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 sorting
+.Pq Fl t
+or printing
+.Pq Fl l .
+.It Fl d
+Directories are listed as plain files (not searched recursively) and
+symbolic links in the argument list are not followed.
+.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.
+.It Fl g
+The same as
+.Fl l ,
+except that the owner is not printed.
+.It Fl h
+Modifies the
+.Fl s
+and
+.Fl l
+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.
+The rightmost of the
+.Fl k
+and
+.Fl h
+flags overrides the previous flag.
+See also
+.Fl h
+and
+.Fl M .
+.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.)
+A total sum for all the file sizes is output on a line before the long
+listing.
+.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
+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
+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.
+.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 sorting
+.Pq Fl t
+or printing
+.Pq Fl l .
+.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 \&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.
+.El
+.Pp
+The
+.Fl B ,
+.Fl b ,
+.Fl w ,
+and
+.Fl q
+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
+The
+.Fl c
+and
+.Fl u
+options override each other; the last one specified determines
+the file time used.
+.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
+separating the information associated with the
+.Fl i ,
+.Fl s ,
+and
+.Fl l
+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 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.
+.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 \-\*[Gt] .
+.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 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 s
+Socket link.
+.It Sy p
+FIFO.
+.It Sy w
+Whiteout.
+.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 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 h
+and
+.Fl k
+options are not specified, the block counts
+(see
+.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 v5 .
--- /dev/null
+/* $NetBSD: ls.c,v 1.69 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
+__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.69 2011/08/29 14:44:21 joerg 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 */
+
+__dead static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: %s [-AaBbCcdFfghikLlMmnopqRrSsTtuWwx1] [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, "1ABCFLMRSTWabcdfghiklmnopqrstuwx")) != -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 '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 'f':
+ f_nosort = 1;
+ 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_flags = 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;
+ 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 -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 (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), "%llu",
+ (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), "%llu",
+ (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), "%u", maxmajor);
+ d.s_major = strlen(buf);
+ (void)snprintf(buf, sizeof(buf), "%u", 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));
+}
--- /dev/null
+/* $NetBSD: ls.h,v 1.18 2011/03/15 03:52:38 erh 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 ? */
+
+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;
--- /dev/null
+/* $NetBSD: main.c,v 1.4 2008/04/28 20:22:51 martin 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.4 2008/04/28 20:22:51 martin Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <fts.h>
+
+#include "ls.h"
+#include "extern.h"
+
+int main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+
+ return ls_main(argc, argv);
+ /* NOTREACHED */
+}
--- /dev/null
+/* $NetBSD: print.c,v 1.51 2012/06/29 12:51:38 yamt 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.51 2012/06/29 12:51:38 yamt Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.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)
+
+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);
+
+ 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("%*lu ", dp->s_inode,
+ (unsigned long)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_print(p->fts_name);
+ else if (f_nonprint)
+ (void)printescaped(p->fts_name);
+ else
+ (void)printf("%s", p->fts_name);
+
+ 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("%*lu ", inodefield, (unsigned long)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_print(p->fts_name);
+ else if (f_nonprint)
+ chcnt += printescaped(p->fts_name);
+ else
+ chcnt += printf("%s", p->fts_name);
+ 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);
+}
+
--- /dev/null
+/* $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;
+}
hostaddr id ifconfig ifdef \
intr ipcrm ipcs irdpd isoread last \
less loadkeys loadramdisk logger look lp \
- lpd ls lspci mail MAKEDEV \
+ lpd lspci mail MAKEDEV \
mesg mined mkfifo \
mount mt netconf nice acknm nohup \
nonamed od paste patch \
+++ /dev/null
-PROG= ls
-BINDIR= /bin
-MAN=
-
-# LSC Force usage of local mergesort
-CPPFLAGS.ls.c+= -D__NBSD_LIBC
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* ls 5.4 - List files. Author: Kees J. Bot
- * 25 Apr 1989
- *
- * About the amount of bytes for heap + stack under Minix:
- * Ls needs a average amount of 42 bytes per unserviced directory entry, so
- * scanning 10 directory levels deep in an ls -R with 100 entries per directory
- * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is
- * usually enough, 40000 is pessimistic.
- */
-
-/* The array l_ifmt[] is used in an 'ls -l' to map the type of a file to a
- * letter. This is done so that ls can list any future file or device type
- * other than symlinks, without recompilation. (Yes it's dirty.)
- */
-char l_ifmt[] = "0pcCd?bB-?l?s???";
-
-#define ifmt(mode) l_ifmt[((mode) >> 12) & 0xF]
-
-#define nil 0
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <time.h>
-#include <pwd.h>
-#include <grp.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-
-#ifndef major
-#define major(dev) ((int) (((dev) >> 8) & 0xFF))
-#define minor(dev) ((int) (((dev) >> 0) & 0xFF))
-#endif
-
-#if !__minix
-#define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */
-#else
-#define SUPER_ID gid
-#endif
-
-#ifdef S_IFLNK
-int (*status)(const char *file, struct stat *stp);
-#else
-#define status stat
-#endif
-
-/* Basic disk block size is 512 except for one niche O.S. */
-#if __minix
-#define BLOCK 1024
-#else
-#define BLOCK 512
-#endif
-
-/* Assume other systems have st_blocks. */
-#if !__minix
-#define ST_BLOCKS 1
-#endif
-
-/* Some terminals ignore more than 80 characters on a line. Dumb ones wrap
- * when the cursor hits the side. Nice terminals don't wrap until they have
- * to print the 81st character. Whether we like it or not, no column 80.
- */
-int ncols= 79;
-
-#define NSEP 3 /* # spaces between columns. */
-
-#define MAXCOLS 128 /* Max # of files per line. */
-
-char *arg0; /* Last component of argv[0]. */
-int uid, gid; /* callers id. */
-int ex= 0; /* Exit status to be. */
-int istty; /* Output is on a terminal. */
-
-/* Safer versions of malloc and realloc: */
-
-void heaperr(void)
-{
- fprintf(stderr, "%s: Out of memory\n", arg0);
- exit(-1);
-}
-
-void *allocate(size_t n)
-/* Deliver or die. */
-{
- void *a;
-
- if ((a= malloc(n)) == nil) heaperr();
- return a;
-}
-
-void *reallocate(void *a, size_t n)
-{
- if ((a= realloc(a, n)) == nil) heaperr();
- return a;
-}
-
-char allowed[] = "acdfghilnpqrstu1ACDFLMRTX";
-char flags[sizeof(allowed)];
-
-char arg0flag[] = "cdfmrtx"; /* These in argv[0] go to upper case. */
-
-void setflags(char *flgs)
-{
- int c;
-
- while ((c= *flgs++) != 0) {
- if (strchr(allowed, c) == nil) {
- fprintf(stderr, "Usage: %s [-%s] [file ...]\n",
- arg0, allowed);
- exit(1);
- } else
- if (strchr(flags, c) == nil) {
- flags[strlen(flags)] = c;
- }
- }
-}
-
-int present(int f)
-{
- return f == 0 || strchr(flags, f) != nil;
-}
-
-void report(char *f)
-/* Like perror(3), but in the style: "ls: junk: No such file or directory. */
-{
- fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno));
- ex= 1;
-}
-
-/* Two functions, uidname and gidname, translate id's to readable names.
- * All names are remembered to avoid searching the password file.
- */
-#define NNAMES (1 << (sizeof(int) + sizeof(char *)))
-enum whatmap { PASSWD, GROUP };
-
-struct idname { /* Hash list of names. */
- struct idname *next;
- char *name;
- uid_t id;
-} *uids[NNAMES], *gids[NNAMES];
-
-char *idname(unsigned id, enum whatmap map)
-/* Return name for a given user/group id. */
-{
- struct idname *i;
- struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES];
-
- while ((i= *ids) != nil && id < i->id) ids= &i->next;
-
- if (i == nil || id != i->id) {
- /* Not found, go look in the password or group map. */
- char *name= nil;
- char noname[3 * sizeof(uid_t)];
-
- if (!present('n')) {
- if (map == PASSWD) {
- struct passwd *pw= getpwuid(id);
-
- if (pw != nil) name= pw->pw_name;
- } else {
- struct group *gr= getgrgid(id);
-
- if (gr != nil) name= gr->gr_name;
- }
- }
- if (name == nil) {
- /* Can't find it, weird. Use numerical "name." */
- sprintf(noname, "%u", id);
- name= noname;
- }
-
- /* Add a new id-to-name cell. */
- i= allocate(sizeof(*i));
- i->id= id;
- i->name= allocate(strlen(name) + 1);
- strcpy(i->name, name);
- i->next= *ids;
- *ids= i;
- }
- return i->name;
-}
-
-#define uidname(uid) idname((uid), PASSWD)
-#define gidname(gid) idname((gid), GROUP)
-
-/* Path name construction, addpath adds a component, delpath removes it.
- * The string path is used throughout the program as the file under examination.
- */
-
-char *path; /* Path name constructed in path[]. */
-int plen= 0, pidx= 0; /* Lenght/index for path[]. */
-
-void addpath(int *didx, char *name)
-/* Add a component to path. (name may also be a full path at the first call)
- * The index where the current path ends is stored in *pdi.
- */
-{
- if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0]));
-
- if (pidx == 1 && path[0] == '.') pidx= 0; /* Remove "." */
-
- *didx= pidx; /* Record point to go back to for delpath. */
-
- if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
-
- do {
- if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
- if (pidx == plen) {
- path= (char *) reallocate((void *) path,
- (plen*= 2) * sizeof(path[0]));
- }
- path[pidx++]= *name;
- }
- } while (*name++ != 0);
-
- --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
- * statement will overwrite it at the next call.
- */
-}
-
-#define delpath(didx) (path[pidx= didx]= 0) /* Remove component. */
-
-int field = 0; /* (used to be) Fields that must be printed. */
- /* (now) Effects triggered by certain flags. */
-
-#define L_INODE 0x0001 /* -i */
-#define L_BLOCKS 0x0002 /* -s */
-#define L_EXTRA 0x0004 /* -X */
-#define L_MODE 0x0008 /* -lMX */
-#define L_LONG 0x0010 /* -l */
-#define L_GROUP 0x0020 /* -g */
-#define L_BYTIME 0x0040 /* -tuc */
-#define L_ATIME 0x0080 /* -u */
-#define L_CTIME 0x0100 /* -c */
-#define L_MARK 0x0200 /* -F */
-#define L_MARKDIR 0x0400 /* -p */
-#define L_TYPE 0x0800 /* -D */
-#define L_LONGTIME 0x1000 /* -T */
-#define L_DIR 0x2000 /* -d */
-#define L_KMG 0x4000 /* -h */
-
-struct file { /* A file plus stat(2) information. */
- struct file *next; /* Lists are made of them. */
- char *name; /* Null terminated name. */
- ino_t ino;
- mode_t mode;
- uid_t uid;
- gid_t gid;
- nlink_t nlink;
- dev_t rdev;
- off_t size;
- time_t mtime;
- time_t atime;
- time_t ctime;
-#if ST_BLOCKS
- long blocks;
-#endif
-};
-
-void setstat(struct file *f, struct stat *stp)
-{
- f->ino= stp->st_ino;
- f->mode= stp->st_mode;
- f->nlink= stp->st_nlink;
- f->uid= stp->st_uid;
- f->gid= stp->st_gid;
- f->rdev= stp->st_rdev;
- f->size= stp->st_size;
- f->mtime= stp->st_mtime;
- f->atime= stp->st_atime;
- f->ctime= stp->st_ctime;
-#if ST_BLOCKS
- f->blocks= stp->st_blocks;
-#endif
-}
-
-#define PAST (26*7*24*3600L) /* Half a year ago. */
-/* Between PAST and FUTURE from now a time is printed, otherwise a year. */
-#define FUTURE ( 1*7*24*3600L) /* One week. */
-
-static char *timestamp(struct file *f)
-/* Transform the right time field into something readable. */
-{
- struct tm *tm;
- time_t t;
- static time_t now;
- static int drift= 0;
- static char date[] = "Jan 19 03:14:07 2038";
- static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-
- t= f->mtime;
- if (field & L_ATIME) t= f->atime;
- if (field & L_CTIME) t= f->ctime;
-
- tm= localtime(&t);
- if (--drift < 0) { time(&now); drift= 50; } /* limit time() calls */
-
- if (field & L_LONGTIME) {
- sprintf(date, "%.3s %2d %02d:%02d:%02d %d",
- month + 3*tm->tm_mon,
- tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- 1900 + tm->tm_year);
- } else
- if (t < now - PAST || t > now + FUTURE) {
- sprintf(date, "%.3s %2d %d",
- month + 3*tm->tm_mon,
- tm->tm_mday,
- 1900 + tm->tm_year);
- } else {
- sprintf(date, "%.3s %2d %02d:%02d",
- month + 3*tm->tm_mon,
- tm->tm_mday,
- tm->tm_hour, tm->tm_min);
- }
- return date;
-}
-
-char *permissions(struct file *f)
-/* Compute long or short rwx bits. */
-{
- static char rwx[] = "drwxr-x--x";
-
- rwx[0] = ifmt(f->mode);
- /* Note that rwx[0] is a guess for the more alien file types. It is
- * correct for BSD4.3 and derived systems. I just don't know how
- * "standardized" these numbers are.
- */
-
- if (field & L_EXTRA) { /* Short style */
- int mode = f->mode, ucase= 0;
-
- if (uid == f->uid) { /* What group of bits to use. */
- /* mode<<= 0, */
- ucase= (mode<<3) | (mode<<6);
- /* Remember if group or others have permissions. */
- } else
- if (gid == f->gid) {
- mode<<= 3;
- } else {
- mode<<= 6;
- }
- rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-';
- rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-';
-
- if (mode&S_IXUSR) {
- static char sbit[]= { 'x', 'g', 'u', 's' };
-
- rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10];
- if (ucase&S_IXUSR) rwx[3] += 'A'-'a';
- } else {
- rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-';
- }
- rwx[4]= 0;
- } else { /* Long form. */
- char *p= rwx+1;
- int mode= f->mode;
-
- do {
- p[0] = (mode & S_IRUSR) ? 'r' : '-';
- p[1] = (mode & S_IWUSR) ? 'w' : '-';
- p[2] = (mode & S_IXUSR) ? 'x' : '-';
- mode<<= 3;
- } while ((p+=3) <= rwx+7);
-
- if (f->mode&S_ISUID) rwx[3]= f->mode&(S_IXUSR>>0) ? 's' : '=';
- if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '=';
- if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '=';
- }
- return rwx;
-}
-
-void numeral(int i, char **pp)
-{
- char itoa[3*sizeof(int)], *a=itoa;
-
- do *a++ = i%10 + '0'; while ((i/=10) > 0);
-
- do *(*pp)++ = *--a; while (a>itoa);
-}
-
-#define K 1024L /* A kilobyte counts in multiples of K */
-#define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */
-
-char *cxsize(struct file *f)
-/* Try and fail to turn a 32 bit size into 4 readable characters. */
-{
- static char siz[] = "1.2m";
- char *p= siz;
- off_t z;
-
- siz[1]= siz[2]= siz[3]= 0;
-
- if (f->size <= 5*K) { /* <= 5K prints as is. */
- numeral((int) f->size, &p);
- return siz;
- }
- z= (f->size + K-1) / K;
-
- if (z <= 999) { /* Print as 123k. */
- numeral((int) z, &p);
- *p = 'k'; /* Can't use 'K', looks bad */
- } else
- if (z*10 <= 99*T) { /* 1.2m (Try ls -X /dev/at0) */
- z= (z*10 + T-1) / T; /* Force roundup */
- numeral((int) z / 10, &p);
- *p++ = '.';
- numeral((int) z % 10, &p);
- *p = 'm';
- } else
- if (z <= 999*T) { /* 123m */
- numeral((int) ((z + T-1) / T), &p);
- *p = 'm';
- } else { /* 1.2g */
- z= (z*10 + T*T-1) / (T*T);
- numeral((int) z / 10, &p);
- *p++ = '.';
- numeral((int) z % 10, &p);
- *p = 'g';
- }
- return siz;
-}
-
-/* Transform size of file to number of blocks. This was once a function that
- * guessed the number of indirect blocks, but that nonsense has been removed.
- */
-#if ST_BLOCKS
-#define nblocks(f) ((f)->blocks)
-#else
-#define nblocks(f) (((f)->size + BLOCK-1) / BLOCK)
-#endif
-
-/* From number of blocks to kilobytes. */
-#if BLOCK < 1024
-#define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))
-#else
-#define nblk2k(nb) ((nb) * (BLOCK / 1024))
-#endif
-
-static int (*CMP)(struct file *f1, struct file *f2);
-static int (*rCMP)(struct file *f1, struct file *f2);
-
-#ifdef __NBSD_LIBC
-#define mergesort _ls_mergesort
-#endif
-
-static void mergesort(struct file **al)
-/* This is either a stable mergesort, or thermal noise, I'm no longer sure.
- * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
- */
-{
- /* static */ struct file *l1, **mid; /* Need not be local */
- struct file *l2;
-
- l1= *(mid= &(*al)->next);
- do {
- if ((l1= l1->next) == nil) break;
- mid= &(*mid)->next;
- } while ((l1= l1->next) != nil);
-
- l2= *mid;
- *mid= nil;
-
- if ((*al)->next != nil) mergesort(al);
- if (l2->next != nil) mergesort(&l2);
-
- l1= *al;
- for (;;) {
- if ((*CMP)(l1, l2) <= 0) {
- if ((l1= *(al= &l1->next)) == nil) {
- *al= l2;
- break;
- }
- } else {
- *al= l2;
- l2= *(al= &l2->next);
- *al= l1;
- if (l2 == nil) break;
- }
- }
-}
-
-int namecmp(struct file *f1, struct file *f2)
-{
- return strcmp(f1->name, f2->name);
-}
-
-int mtimecmp(struct file *f1, struct file *f2)
-{
- return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
-}
-
-int atimecmp(struct file *f1, struct file *f2)
-{
- return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
-}
-
-int ctimecmp(struct file *f1, struct file *f2)
-{
- return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
-}
-
-int typecmp(struct file *f1, struct file *f2)
-{
- return ifmt(f1->mode) - ifmt(f2->mode);
-}
-
-int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }
-
-static void sort(struct file **al)
-/* Sort the files according to the flags. */
-{
- if (!present('f') && *al != nil && (*al)->next != nil) {
- CMP= namecmp;
-
- if (!(field & L_BYTIME)) {
- /* Sort on name */
-
- if (present('r')) { rCMP= CMP; CMP= revcmp; }
- mergesort(al);
- } else {
- /* Sort on name first, then sort on time. */
-
- mergesort(al);
- if (field & L_CTIME) {
- CMP= ctimecmp;
- } else
- if (field & L_ATIME) {
- CMP= atimecmp;
- } else {
- CMP= mtimecmp;
- }
-
- if (present('r')) { rCMP= CMP; CMP= revcmp; }
- mergesort(al);
- }
- /* Separate by file type if so desired. */
-
- if (field & L_TYPE) {
- CMP= typecmp;
- mergesort(al);
- }
- }
-}
-
-struct file *newfile(char *name)
-/* Create file structure for given name. */
-{
- struct file *new;
-
- new= (struct file *) allocate(sizeof(*new));
- new->name= strcpy((char *) allocate(strlen(name)+1), name);
- return new;
-}
-
-void pushfile(struct file **flist, struct file *new)
-/* Add file to the head of a list. */
-{
- new->next= *flist;
- *flist= new;
-}
-
-void delfile(struct file *old)
-/* Release old file structure. */
-{
- free((void *) old->name);
- free((void *) old);
-}
-
-struct file *popfile(struct file **flist)
-/* Pop file off top of file list. */
-{
- struct file *f;
-
- f= *flist;
- *flist= f->next;
- return f;
-}
-
-int dotflag(char *name)
-/* Return flag that would make ls list this name: -a or -A. */
-{
- if (*name++ != '.') return 0;
-
- switch (*name++) {
- case 0: return 'a'; /* "." */
- case '.': if (*name == 0) return 'a'; /* ".." */
- default: return 'A'; /* ".*" */
- }
-}
-
-int adddir(struct file **aflist, char *name)
-/* Add directory entries of directory name to a file list. */
-{
- DIR *d;
- struct dirent *e;
-
- if (access(name, 0) < 0) {
- report(name);
- return 0;
- }
-
- if ((d= opendir(name)) == nil) {
- report(name);
- return 0;
- }
- while ((e= readdir(d)) != nil) {
- if (e->d_ino != 0 && present(dotflag(e->d_name))) {
- pushfile(aflist, newfile(e->d_name));
- aflist= &(*aflist)->next;
- }
- }
- closedir(d);
- return 1;
-}
-
-off_t countblocks(struct file *flist)
-/* Compute total block count for a list of files. */
-{
- off_t cb = 0;
-
- while (flist != nil) {
- switch (flist->mode & S_IFMT) {
- case S_IFDIR:
- case S_IFREG:
-#ifdef S_IFLNK
- case S_IFLNK:
-#endif
- cb += nblocks(flist);
- }
- flist= flist->next;
- }
- return cb;
-}
-
-void printname(char *name)
-/* Print a name with control characters as '?' (unless -q). The terminal is
- * assumed to be eight bit clean.
- */
-{
- int c, q= present('q');
-
- while ((c= (unsigned char) *name++) != 0) {
- if (q && (c < ' ' || c == 0177)) c= '?';
- putchar(c);
- }
-}
-
-int mark(struct file *f, int doit)
-{
- int c;
-
- c= 0;
-
- if (field & L_MARK) {
- switch (f->mode & S_IFMT) {
- case S_IFDIR: c= '/'; break;
-#ifdef S_IFIFO
- case S_IFIFO: c= '|'; break;
-#endif
-#ifdef S_IFLNK
- case S_IFLNK: c= '@'; break;
-#endif
-#ifdef S_IFSOCK
- case S_IFSOCK: c= '='; break;
-#endif
- case S_IFREG:
- if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) c= '*';
- break;
- }
- } else
- if (field & L_MARKDIR) {
- if (S_ISDIR(f->mode)) c= '/';
- }
-
- if (doit && c != 0) putchar(c);
- return c;
-}
-
-/* Width of entire column, and of several fields. */
-enum { W_COL, W_INO, W_BLK, W_NLINK, W_UID, W_GID, W_SIZE, W_NAME, MAXFLDS };
-
-unsigned char fieldwidth[MAXCOLS][MAXFLDS];
-
-void maxise(unsigned char *aw, int w)
-/* Set *aw to the larger of it and w. */
-{
- if (w > *aw) {
- if (w > UCHAR_MAX) w= UCHAR_MAX;
- *aw= w;
- }
-}
-
-int numwidth(unsigned long n)
-/* Compute width of 'n' when printed. */
-{
- int width= 0;
-
- do { width++; } while ((n /= 10) > 0);
- return width;
-}
-
-#if !__minix
-int numxwidth(unsigned long n)
-/* Compute width of 'n' when printed in hex. */
-{
- int width= 0;
-
- do { width++; } while ((n /= 16) > 0);
- return width;
-}
-#endif
-
-static int nsp= 0; /* This many spaces have not been printed yet. */
-#define spaces(n) (nsp= (n))
-#define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */
-
-void print1(struct file *f, int col, int doit)
-/* Either compute the number of spaces needed to print file f (doit == 0) or
- * really print it (doit == 1).
- */
-{
- int width= 0, n;
- char *p;
- unsigned char *f1width = fieldwidth[col];
-
- while (nsp>0) { putchar(' '); nsp--; }/* Fill gap between two columns */
-
- if (field & L_INODE) {
- if (doit) {
- printf("%*d ", f1width[W_INO], f->ino);
- } else {
- maxise(&f1width[W_INO], numwidth(f->ino));
- width++;
- }
- }
- if (field & L_BLOCKS) {
- unsigned long nb= nblk2k(nblocks(f));
- if (doit) {
- printf("%*lu ", f1width[W_BLK], nb);
- } else {
- maxise(&f1width[W_BLK], numwidth(nb));
- width++;
- }
- }
- if (field & L_MODE) {
- if (doit) {
- printf("%s ", permissions(f));
- } else {
- width+= (field & L_EXTRA) ? 5 : 11;
- }
- }
- if (field & L_EXTRA) {
- p= cxsize(f);
- n= strlen(p)+1;
-
- if (doit) {
- n= f1width[W_SIZE] - n;
- while (n > 0) { putchar(' '); --n; }
- printf("%s ", p);
- } else {
- maxise(&f1width[W_SIZE], n);
- }
- }
- if (field & L_LONG) {
- if (doit) {
- printf("%*u ", f1width[W_NLINK], (unsigned) f->nlink);
- } else {
- maxise(&f1width[W_NLINK], numwidth(f->nlink));
- width++;
- }
- if (!(field & L_GROUP)) {
- if (doit) {
- printf("%-*s ", f1width[W_UID],
- uidname(f->uid));
- } else {
- maxise(&f1width[W_UID],
- strlen(uidname(f->uid)));
- width+= 2;
- }
- }
- if (doit) {
- printf("%-*s ", f1width[W_GID], gidname(f->gid));
- } else {
- maxise(&f1width[W_GID], strlen(gidname(f->gid)));
- width+= 2;
- }
-
- switch (f->mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
-#ifdef S_IFMPB
- case S_IFMPB:
-#endif
-#ifdef S_IFMPC
- case S_IFMPC:
-#endif
-#if __minix
- if (doit) {
- printf("%*d, %3d ", f1width[W_SIZE] - 5,
- major(f->rdev), minor(f->rdev));
- } else {
- maxise(&f1width[W_SIZE],
- numwidth(major(f->rdev)) + 5);
- width++;
- }
-#else /* !__minix */
- if (doit) {
- printf("%*lX ", f1width[W_SIZE],
- (unsigned long) f->rdev);
- } else {
- maxise(&f1width[W_SIZE], numwidth(f->rdev));
- width++;
- }
-#endif /* !__minix */
- break;
- default:
- if (field & L_KMG) {
- p= cxsize(f);
- n= strlen(p)+1;
-
- if (doit) {
- n= f1width[W_SIZE] - n;
- while (n > 0) { putchar(' '); --n; }
- printf("%s ", p);
- } else {
- maxise(&f1width[W_SIZE], n);
- }
- } else {
- if (doit) {
- printf("%*lu ", f1width[W_SIZE],
- (unsigned long) f->size);
- } else {
- maxise(&f1width[W_SIZE],
- numwidth(f->size));
- width++;
- }
- }
- }
-
- if (doit) {
- printf("%s ", timestamp(f));
- } else {
- width+= (field & L_LONGTIME) ? 21 : 13;
- }
- }
-
- n= strlen(f->name);
- if (doit) {
- printname(f->name);
- if (mark(f, 1) != 0) n++;
-#ifdef S_IFLNK
- if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
- char *buf;
- int r, didx;
-
- buf= (char *) allocate(((size_t) f->size + 1)
- * sizeof(buf[0]));
- addpath(&didx, f->name);
- r= readlink(path, buf, (int) f->size);
- delpath(didx);
- if (r > 0) buf[r] = 0; else r=1, strcpy(buf, "?");
- printf(" -> ");
- printname(buf);
- free((void *) buf);
- n+= 4 + r;
- }
-#endif
- spaces(f1width[W_NAME] - n);
- } else {
- if (mark(f, 0) != 0) n++;
-#ifdef S_IFLNK
- if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
- n+= 4 + (int) f->size;
- }
-#endif
- maxise(&f1width[W_NAME], n + NSEP);
-
- for (n= 1; n < MAXFLDS; n++) width+= f1width[n];
- maxise(&f1width[W_COL], width);
- }
-}
-
-int countfiles(struct file *flist)
-/* Return number of files in the list. */
-{
- int n= 0;
-
- while (flist != nil) { n++; flist= flist->next; }
-
- return n;
-}
-
-struct file *filecol[MAXCOLS]; /* filecol[i] is list of files for column i. */
-int nfiles, nlines; /* # files to print, # of lines needed. */
-
-void columnise(struct file *flist, int nplin)
-/* Chop list of files up in columns. Note that 3 columns are used for 5 files
- * even though nplin may be 4, filecol[3] will simply be nil.
- */
-{
- int i, j;
-
- nlines= (nfiles + nplin - 1) / nplin; /* nlines needed for nfiles */
-
- filecol[0]= flist;
-
- for (i=1; i<nplin; i++) { /* Give nlines files to each column. */
- for (j=0; j<nlines && flist != nil; j++) flist= flist->next;
-
- filecol[i]= flist;
- }
-}
-
-int print(struct file *flist, int nplin, int doit)
-/* Try (doit == 0), or really print the list of files over nplin columns.
- * Return true if it can be done in nplin columns or if nplin == 1.
- */
-{
- register struct file *f;
- register int col, fld, totlen;
-
- columnise(flist, nplin);
-
- if (!doit) {
- for (col= 0; col < nplin; col++) {
- for (fld= 0; fld < MAXFLDS; fld++) {
- fieldwidth[col][fld]= 0;
- }
- }
- }
-
- while (--nlines >= 0) {
- totlen= 0;
-
- for (col= 0; col < nplin; col++) {
- if ((f= filecol[col]) != nil) {
- filecol[col]= f->next;
- print1(f, col, doit);
- }
- if (!doit && nplin > 1) {
- /* See if this line is not too long. */
- if (fieldwidth[col][W_COL] == UCHAR_MAX) {
- return 0;
- }
- totlen+= fieldwidth[col][W_COL];
- if (totlen > ncols+NSEP) return 0;
- }
- }
- if (doit) terpri();
- }
- return 1;
-}
-
-enum depth { SURFACE, SURFACE1, SUBMERGED };
-enum state { BOTTOM, SINKING, FLOATING };
-
-void listfiles(struct file *flist, enum depth depth, enum state state)
-/* Main workhorse of ls, it sorts and prints the list of files. Flags:
- * depth: working with the command line / just one file / listing dir.
- * state: How "recursive" do we have to be.
- */
-{
- struct file *dlist= nil, **afl= &flist, **adl= &dlist;
- int nplin;
- static int white = 1; /* Nothing printed yet. */
-
- /* Flush everything previously printed, so new error output will
- * not intermix with files listed earlier.
- */
- fflush(stdout);
-
- if (field != 0 || state != BOTTOM) { /* Need stat(2) info. */
- while (*afl != nil) {
- static struct stat st;
- int r, didx;
-
- addpath(&didx, (*afl)->name);
-
- if ((r= status(path, &st)) < 0
-#ifdef S_IFLNK
- && (status == lstat || lstat(path, &st) < 0)
-#endif
- ) {
- if (depth != SUBMERGED || errno != ENOENT)
- report((*afl)->name);
- delfile(popfile(afl));
- } else {
- setstat(*afl, &st);
- afl= &(*afl)->next;
- }
- delpath(didx);
- }
- }
- sort(&flist);
-
- if (depth == SUBMERGED && (field & (L_BLOCKS | L_LONG))) {
- printf("total %d\n", nblk2k(countblocks(flist)));
- }
-
- if (state == SINKING || depth == SURFACE1) {
- /* Don't list directories themselves, list their contents later. */
- afl= &flist;
- while (*afl != nil) {
- if (((*afl)->mode & S_IFMT) == S_IFDIR) {
- pushfile(adl, popfile(afl));
- adl= &(*adl)->next;
- } else {
- afl= &(*afl)->next;
- }
- }
- }
-
- if ((nfiles= countfiles(flist)) > 0) {
- /* Print files in how many columns? */
- nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
-
- while (!print(flist, nplin, 0)) nplin--; /* Try first */
-
- print(flist, nplin, 1); /* Then do it! */
- white = 0;
- }
-
- while (flist != nil) { /* Destroy file list */
- if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
- /* But keep these directories for ls -R. */
- pushfile(adl, popfile(&flist));
- adl= &(*adl)->next;
- } else {
- delfile(popfile(&flist));
- }
- }
-
- while (dlist != nil) { /* List directories */
- if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) {
- int didx;
-
- addpath(&didx, dlist->name);
-
- flist= nil;
- if (adddir(&flist, path)) {
- if (depth != SURFACE1) {
- if (!white) putchar('\n');
- printf("%s:\n", path);
- white = 0;
- }
- listfiles(flist, SUBMERGED,
- state == FLOATING ? FLOATING : BOTTOM);
- }
- delpath(didx);
- }
- delfile(popfile(&dlist));
- }
-}
-
-int main(int argc, char **argv)
-{
- struct file *flist= nil, **aflist= &flist;
- enum depth depth;
- char *lsflags;
- struct winsize ws;
-
- uid= geteuid();
- gid= getegid();
-
- if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
- argv++;
-
- if (strcmp(arg0, "ls") != 0) {
- char *p= arg0+1;
-
- while (*p != 0) {
- if (strchr(arg0flag, *p) != nil) *p += 'A' - 'a';
- p++;
- }
- setflags(arg0+1);
- }
- while (*argv != nil && (*argv)[0] == '-') {
- if ((*argv)[1] == '-' && (*argv)[2] == 0) {
- argv++;
- break;
- }
- setflags(*argv++ + 1);
- }
-
- istty= isatty(1);
-
- if (istty && (lsflags= getenv("LSOPTS")) != nil) {
- if (*lsflags == '-') lsflags++;
- setflags(lsflags);
- }
-
- if (!present('1') && !present('C') && !present('l')
- && (istty || present('M') || present('X') || present('F'))
- ) setflags("C");
-
- if (istty) setflags("q");
-
- if (SUPER_ID == 0 || present('a')) setflags("A");
-
- if (present('i')) field|= L_INODE;
- if (present('s')) field|= L_BLOCKS;
- if (present('M')) field|= L_MODE;
- if (present('X')) field|= L_EXTRA | L_MODE;
- if (present('t')) field|= L_BYTIME;
- if (present('u')) field|= L_ATIME;
- if (present('c')) field|= L_CTIME;
- if (present('l')) field|= L_MODE | L_LONG;
- if (present('g')) field|= L_MODE | L_LONG | L_GROUP;
- if (present('F')) field|= L_MARK;
- if (present('p')) field|= L_MARKDIR;
- if (present('D')) field|= L_TYPE;
- if (present('T')) field|= L_MODE | L_LONG | L_LONGTIME;
- if (present('d')) field|= L_DIR;
- if (present('h')) field|= L_KMG;
- if (field & L_LONG) field&= ~L_EXTRA;
-
-#ifdef S_IFLNK
- status= present('L') ? stat : lstat;
-#endif
-
- if (present('C')) {
- int t= istty ? 1 : open("/dev/tty", O_WRONLY);
-
- if (t >= 0 && ioctl(t, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
- ncols= ws.ws_col - 1;
-
- if (t != 1 && t != -1) close(t);
- }
-
- depth= SURFACE;
-
- if (*argv == nil) {
- if (!(field & L_DIR)) depth= SURFACE1;
- pushfile(aflist, newfile("."));
- } else {
- if (argv[1] == nil && !(field & L_DIR)) depth= SURFACE1;
-
- do {
- pushfile(aflist, newfile(*argv++));
- aflist= &(*aflist)->next;
- } while (*argv!=nil);
- }
- listfiles(flist, depth,
- (field & L_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING);
- return ex;
-}
.if ${MACHINE_ARCH} == "earm"
EXTRA+= rc.arm mylogin.sh ttys
PROG_DRIVERS+= mmc tty gpio
-PROG_COMMANDS+= cp dd getty ls time sync sleep stty umount
-PROG_BIN+= cat rm
+PROG_COMMANDS+= cp dd getty time sync sleep stty umount
+PROG_BIN+= cat ls rm
PROTO= proto.arm.small
.endif # ${MACHINE_ARCH} == "earm"
fsck.mfs.1 head.1 host.1 hostaddr.1 ifdef.1 \
isodir.1 isoinfo.1 isoread.1 \
last.1 loadfont.1 loadkeys.1 logger.1 \
- look.1 lp.1 ls.1 lspci.1 mail.1 \
+ look.1 lp.1 lspci.1 mail.1 \
mesg.1 mixer.1 mkfs.mfs.1 \
mkproto.1 mount.1 mt.1 nice.1 nm.1 nohup.1 od.1 \
paste.1 ping.1 playwave.1 pr.1 prep.1 \
+++ /dev/null
-.TH LS 1
-.SH NAME
-ls \- list the contents of a directory
-.SH SYNOPSIS
-\fBls\fP [\fB\-acdfghilnpqrstu1ACDFLMRTX\fP] [\fIname\fP...]
-.SH DESCRIPTION
-For each file argument, list it. For each directory argument, list its
-contents. The current working directory is listed when no files are named.
-Information is printed multicolumn on terminals, single column if the output
-is redirected. The options control what information is shown and how.
-.PP
-.B Ls
-has two sources other then the command line to draw options from, one is
-the environment variable
-.B LSOPTS
-that is scanned for option letters when the output of
-.B ls
-is displayed on a terminal. The other is the name of
-.B ls
-itself. If
-.B ls
-is linked to another name, then all the characters after the l are used as
-flags too, except that d, f, r, t and x are translated to D, F, R, T and X.
-Useful links are
-.BR ll ,
-.BR lf ,
-.B lm
-and
-.BR lx .
-.PP
-Files whose names start with a dot are by default not listed.
-.PP
-Note that standard MINIX 3 doesn't have sockets, and
-.B \-u
-and
-.B \-c
-are no-ops on a V1 file system, since only modified times are stored in V1
-inodes.
-.SH OPTIONS
-.TP
-.B \-a
-All entries are listed, even
-.B .
-and
-.B ..
-.TP
-.B \-c
-Use inode changed time for sorting, listing or searching.
-.TP
-.B \-d
-Do not list contents of directories, but list the directory itself.
-.TP
-.B \-f
-Do not sort (should also be: treat a file as a directory, but that
-can't be implemented portably).
-.TP
-.B \-g
-Suppress the owner name on a long listing (implies
-.BR \-l ).
-.TP
-.B \-h
-Show file sizes in kilo, mega or gigabytes.
-.TP
-.B \-i
-I-node number printed in the first column.
-.TP
-.B \-l
-Long listing: mode, links, owner, group, size and time.
-.RB ( "ls \-lC"
-uses columns in a wide enough window!)
-.TP
-.B \-n
-Print numerical user and group id's.
-.TP
-.B \-p
-Mark directories with a '\fB/\fP'.
-.TP
-.B \-q
-Print nongraphic characters as '\fB?\fP' (default on terminals).
-.TP
-.B \-r
-Reverse the sort order.
-.TP
-.B \-s
-Give the size in kilobytes in the first
-.RB ( \-s )
-or second column
-.RB ( \-is ).
-.TP
-.B \-t
-Sort by time (modified time default), latest first.
-.TP
-.B \-u
-Use last accessed time for sorting, listing or searching.
-.TP
-.B \-1
-Print in one column.
-.TP
-.B \-A
-List all entries, but not
-.B .
-and
-.B ..
-(This is the default for privileged users.)
-.TP
-.B \-C
-Print multicolumn (default on terminals).
-.TP
-.B \-D
-Distinguish files by type, i.e. regular files together, directories
-together, etc.
-.TP
-.B \-F
-Mark directories with a '\fB/\fP', executables with a '\fB*\fP', \s-2UNIX\s+2
-domain sockets with a '\fB=\fP', named pipes with a '\fB|\fP' and symbolic
-links with a '\fB@\fP' behind the name.
-.TP
-.B \-L
-Print the file referenced by a symbolic link instead of the link.
-.TP
-.B \-M
-List mode before name (implies
-.BR \-C ).
-.TP
-.B \-R
-List directory trees recursively.
-.TP
-.B \-T
-Print file times in a long format, e.g. "Oct 24 21:37:41 1996".
-.TP
-.B \-X
-Print crunched mode and size before name (implies
-.BR \-C ).
-Only the rwx permissions that its caller has on the file are shown, but they
-are in upper case if the caller owns the file and has given the permission
-to the callers group or other users. The size is listed in bytes (<= 5K),
-or rounded up kilo, mega or gigabytes.
-.SH "SEE ALSO"
-.BR du (1),
-.BR stat (1),
-.BR stat (2).
-.SH BUGS
-Having to type
-.B ls \-C
-when viewing files through
-.BR more (1).
-.PP
-Is only portable to systems with the same
-.B st_mode
-(see
-.BR stat (2)).
-.PP
-The
-.B LSOPTS
-variable and the
-.BR -D ,
-.B -M
-and
-.B -X
-flags are not found on other
-.B ls
-implementations. (They have their own nonstandard flags.)
-.SH AUTHOR
-Kees J. Bot <kjb@cs.vu.nl>
2012/10/17 12:00:00,bin/expr
2012/10/17 12:00:00,bin/kill
2012/10/17 12:00:00,bin/ln
+2012/10/17 12:00:00,bin/ls
2012/10/17 12:00:00,bin/Makefile
2012/10/17 12:00:00,bin/Makefile.inc
2008/07/20 00:52:40,bin/mkdir