From b7ef8cfb526b29a8298718baea6f7a71e155ac1d Mon Sep 17 00:00:00 2001 From: Lionel Sambuc Date: Wed, 30 Jan 2013 19:06:57 +0100 Subject: [PATCH] Upgrading ls Change-Id: Ie0bacf04e727fac5c6df7b2bcf5039e9ce800616 --- bin/ls/Makefile | 10 + bin/ls/cmp.c | 199 +++++++ bin/ls/extern.h | 53 ++ bin/ls/ls.1 | 510 +++++++++++++++++ bin/ls/ls.c | 701 +++++++++++++++++++++++ bin/ls/ls.h | 79 +++ bin/ls/main.c | 52 ++ bin/ls/print.c | 464 +++++++++++++++ bin/ls/util.c | 168 ++++++ commands/Makefile | 2 +- commands/ls/Makefile | 8 - commands/ls/ls.c | 1154 -------------------------------------- drivers/ramdisk/Makefile | 4 +- man/man1/Makefile | 2 +- man/man1/ls.1 | 164 ------ releasetools/nbsd_ports | 1 + 16 files changed, 2241 insertions(+), 1330 deletions(-) create mode 100644 bin/ls/Makefile create mode 100644 bin/ls/cmp.c create mode 100644 bin/ls/extern.h create mode 100644 bin/ls/ls.1 create mode 100644 bin/ls/ls.c create mode 100644 bin/ls/ls.h create mode 100644 bin/ls/main.c create mode 100644 bin/ls/print.c create mode 100644 bin/ls/util.c delete mode 100644 commands/ls/Makefile delete mode 100644 commands/ls/ls.c delete mode 100644 man/man1/ls.1 diff --git a/bin/ls/Makefile b/bin/ls/Makefile new file mode 100644 index 000000000..55497f661 --- /dev/null +++ b/bin/ls/Makefile @@ -0,0 +1,10 @@ +# $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 diff --git a/bin/ls/cmp.c b/bin/ls/cmp.c new file mode 100644 index 000000000..21d50e131 --- /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 +#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 +#include + +#include +#include + +#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 000000000..0a9eea016 --- /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 000000000..1d10b17a7 --- /dev/null +++ b/bin/ls/ls.1 @@ -0,0 +1,510 @@ +.\" $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 . diff --git a/bin/ls/ls.c b/bin/ls/ls.c new file mode 100644 index 000000000..aceaa8dea --- /dev/null +++ b/bin/ls/ls.c @@ -0,0 +1,701 @@ +/* $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 +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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)); +} diff --git a/bin/ls/ls.h b/bin/ls/ls.h new file mode 100644 index 000000000..46aad2a7b --- /dev/null +++ b/bin/ls/ls.h @@ -0,0 +1,79 @@ +/* $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; diff --git a/bin/ls/main.c b/bin/ls/main.c new file mode 100644 index 000000000..3b9e81c2c --- /dev/null +++ b/bin/ls/main.c @@ -0,0 +1,52 @@ +/* $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 + +#ifndef lint +__RCSID("$NetBSD: main.c,v 1.4 2008/04/28 20:22:51 martin Exp $"); +#endif /* not lint */ + +#include +#include + +#include "ls.h" +#include "extern.h" + +int main(int, char *[]); + +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 000000000..17a806363 --- /dev/null +++ b/bin/ls/print.c @@ -0,0 +1,464 @@ +/* $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 +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff --git a/bin/ls/util.c b/bin/ls/util.c new file mode 100644 index 000000000..61b0fda87 --- /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 +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/commands/Makefile b/commands/Makefile index 0921cf12e..2a7849217 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -14,7 +14,7 @@ SUBDIR= add_route arp ash at backup banner basename btrace cal \ 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 \ diff --git a/commands/ls/Makefile b/commands/ls/Makefile deleted file mode 100644 index b7d100432..000000000 --- a/commands/ls/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -PROG= ls -BINDIR= /bin -MAN= - -# LSC Force usage of local mergesort -CPPFLAGS.ls.c+= -D__NBSD_LIBC - -.include diff --git a/commands/ls/ls.c b/commands/ls/ls.c deleted file mode 100644 index dd404eb87..000000000 --- a/commands/ls/ls.c +++ /dev/null @@ -1,1154 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; inext; - - 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; -} diff --git a/drivers/ramdisk/Makefile b/drivers/ramdisk/Makefile index 5d81eccf5..b3f8d382e 100644 --- a/drivers/ramdisk/Makefile +++ b/drivers/ramdisk/Makefile @@ -58,8 +58,8 @@ PROG_DRIVERS+= acpi .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" diff --git a/man/man1/Makefile b/man/man1/Makefile index effbb4861..aa405d5e9 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -9,7 +9,7 @@ MAN= ash.1 at.1 banner.1 basename.1 \ 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 \ diff --git a/man/man1/ls.1 b/man/man1/ls.1 deleted file mode 100644 index d1cc54c4e..000000000 --- a/man/man1/ls.1 +++ /dev/null @@ -1,164 +0,0 @@ -.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 diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index c08df40d5..416d22364 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -13,6 +13,7 @@ 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