* This code is derived from software contributed to Berkeley by
* Michael Fischbein.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
*/
#ifndef lint
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)ls.c 5.17 (Berkeley) %G%";
+static char sccsid[] = "@(#)ls.c 5.48 (Berkeley) %G%";
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <dirent.h>
-#include <strings.h>
+#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "ls.h"
-int lstat(), strlen(), prablelen();
+int (*sortfcn)(), (*printfcn)();
+int lstat();
char *emalloc();
-int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg;
+int termwidth = 80; /* default terminal width */
/* flags */
int f_accesstime; /* use time of last access */
-int f_firsttime = 1; /* to control recursion */
+int f_column; /* columnated format */
int f_group; /* show group ownership of a file */
int f_ignorelink; /* indirect through symbolic link operands */
int f_inode; /* print inode */
+int f_kblocks; /* print size in kilobytes */
int f_listalldot; /* list . and .. as well */
int f_listdir; /* list actual directory, not contents */
int f_listdot; /* list files beginning with . */
int f_longform; /* long listing format */
+int f_needstat; /* if need to stat files */
+int f_newline; /* if precede with newline */
int f_nonprint; /* show unprintables as ? */
+int f_nosort; /* don't sort output */
int f_recursive; /* ls subdirectories also */
int f_reversesort; /* reverse whatever sort is used */
+int f_sectime; /* print the real time for all files */
int f_singlecol; /* use single column output */
int f_size; /* list size in short listing */
-int f_specialdir; /* force params to be directories */
int f_statustime; /* use time of last mode change */
+int f_dirname; /* if precede with directory name */
int f_timesort; /* sort by time vice name */
+int f_total; /* if precede with "total" line */
int f_type; /* add type character for non-regular files */
+int (*statfcn)(), stat(), lstat();
+
main(argc, argv)
int argc;
char **argv;
{
extern int optind, stat();
+ struct winsize win;
int ch;
- int namecmp(), revnamecmp(), acccmp(), revacccmp();
- int modcmp(), revmodcmp(), statcmp(), revstatcmp();
+ char *p, *getenv();
+ int acccmp(), modcmp(), namecmp(), prcopy(), printcol();
+ int printlong(), printscol(), revacccmp(), revmodcmp(), revnamecmp();
+ int revstatcmp(), statcmp();
- /*
- * terminal defaults to -C -q
- * non-terminal defaults to -1
- */
+ /* terminal defaults to -Cq, non-terminal defaults to -1 */
if (isatty(1)) {
f_nonprint = 1;
- lengthfcn = prablelen;
+ if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
+ if (p = getenv("COLUMNS"))
+ termwidth = atoi(p);
+ }
+ else
+ termwidth = win.ws_col;
+ f_column = 1;
} else
f_singlecol = 1;
if (!getuid())
f_listdot = 1;
- while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) {
+ while ((ch = getopt(argc, argv, "1ACFLRTacdfgiklqrstu")) != EOF) {
switch (ch) {
/*
* -1, -C and -l all override each other
*/
case '1':
f_singlecol = 1;
- f_longform = 0;
+ f_column = f_longform = 0;
break;
case 'C':
+ f_column = 1;
f_longform = f_singlecol = 0;
break;
case 'l':
f_longform = 1;
- f_singlecol = 0;
+ f_column = f_singlecol = 0;
break;
/* -c and -u override each other */
case 'c':
f_listdir = 1;
break;
case 'f':
- f_specialdir = 1;
+ f_nosort = 1;
break;
case 'g':
f_group = 1;
case 'i':
f_inode = 1;
break;
+ case 'k':
+ f_kblocks = 1;
+ break;
case 'q':
f_nonprint = 1;
- lengthfcn = prablelen;
break;
case 'r':
f_reversesort = 1;
case 's':
f_size = 1;
break;
+ case 'T':
+ f_sectime = 1;
+ break;
case 't':
f_timesort = 1;
break;
argc -= optind;
argv += optind;
- /* -f turns off -F, -R, -l, -t, -s, -r, turns on -a */
- if (f_specialdir) {
- f_longform = f_recursive = f_reversesort = f_size =
- f_timesort = f_type = 0;
- f_listdot = f_listalldot = 1;
- }
-
/* -d turns off -R */
if (f_listdir)
f_recursive = 0;
/* if need to stat files */
- needstat = f_longform || f_recursive || f_timesort || f_size ||
- f_inode || f_type;
+ f_needstat = f_longform || f_recursive || f_timesort ||
+ f_size || f_type;
/* select a sort function */
if (f_reversesort) {
sortfcn = modcmp;
}
- if (argc)
- args(argc, argv);
+ /* select a print function */
+ if (f_singlecol)
+ printfcn = printscol;
+ else if (f_longform)
+ printfcn = printlong;
else
- curdir();
- exit(0);
-}
+ printfcn = printcol;
-curdir()
-{
- LS local, *stats;
- int num;
- char *names;
+ /* if -l, -d or -F, and not ignoring the link, use lstat() */
+ statfcn =
+ (f_longform || f_listdir || f_type) && !f_ignorelink ? lstat : stat;
- if (lstat(local.name = ".", &local.lstat)) {
- (void)fprintf(stderr, "ls: .: %s\n", strerror(errno));
- exit(1);
+ if (!argc) {
+ static char dot[] = ".";
+
+ argc = 1;
+ argv[0] = dot;
+ argv[1] = NULL;
}
- if (num = buildstats(&local, &stats, &names))
- ls(stats, num);
+ doargs(argc, argv);
+ exit(0);
}
static char path[MAXPATHLEN + 1];
static char *endofpath = path;
-args(argc, argv)
+doargs(argc, argv)
int argc;
char **argv;
{
- register LS *dstats, *rstats;
- register int cnt, dircnt, regcnt;
+ register LS *dstatp, *rstatp;
+ register int cnt, dircnt, maxlen, regcnt;
+ LS *dstats, *rstats;
struct stat sb;
- LS *stats;
- int num, (*statfcn)(), stat(), lstat();
- char *names, top[MAXPATHLEN + 1];
+ char top[MAXPATHLEN + 1];
+ u_long blocks;
+ /*
+ * walk through the operands, building separate arrays of LS
+ * structures for directory and non-directory files.
+ */
dstats = rstats = NULL;
- statfcn = f_ignorelink ? stat : lstat;
for (dircnt = regcnt = 0; *argv; ++argv) {
- if (statfcn(*argv, &sb)) {
- (void)fprintf(stderr, "ls: %s: %s\n",
- *argv, strerror(errno));
+ if (statfcn(*argv, &sb) &&
+ (statfcn == lstat || lstat(*argv, &sb))) {
+ (void)fprintf(stderr,
+ "ls: %s: %s\n", *argv, strerror(errno));
if (errno == ENOENT)
continue;
exit(1);
}
- if (!f_specialdir && !f_listdir && S_ISDIR(sb.st_mode)) {
+ if (S_ISDIR(sb.st_mode) && !f_listdir) {
if (!dstats)
- dstats = (LS *)emalloc((u_int)argc *
+ dstatp = dstats = (LS *)emalloc((u_int)argc *
(sizeof(LS)));
- dstats[dircnt].name = *argv;
- dstats[dircnt].lstat = sb;
+ dstatp->name = *argv;
+ dstatp->lstat = sb;
+ ++dstatp;
++dircnt;
}
else {
- if (!rstats)
- rstats = (LS *)emalloc((u_int)argc *
+ if (!rstats) {
+ rstatp = rstats = (LS *)emalloc((u_int)argc *
(sizeof(LS)));
- rstats[regcnt].name = *argv;
- rstats[regcnt].lstat = sb;
+ blocks = 0;
+ maxlen = -1;
+ }
+ rstatp->name = *argv;
+ rstatp->lstat = sb;
+
+ /* save name length for -C format */
+ rstatp->len = strlen(*argv);
+
+ if (f_nonprint)
+ prcopy(*argv, *argv, rstatp->len);
+
+ /* calculate number of blocks if -l/-s formats */
+ if (f_longform || f_size)
+ blocks += sb.st_blocks;
+
+ /* save max length if -C format */
+ if (f_column && maxlen < rstatp->len)
+ maxlen = rstatp->len;
+
+ ++rstatp;
++regcnt;
}
}
+ /* display regular files */
if (regcnt) {
- if (f_specialdir) {
- for (cnt = 0; cnt < regcnt; ++cnt) {
- if (num = buildstats(rstats++, &stats, &names))
- ls(stats, num);
- (void)free((char *)stats);
- (void)free((char *)names);
- }
- } else
- ls(rstats, regcnt);
- if (dircnt)
- (void)putchar('\n');
+ rstats[0].lstat.st_btotal = blocks;
+ rstats[0].lstat.st_maxlen = maxlen;
+ displaydir(rstats, regcnt);
+ f_newline = f_dirname = 1;
}
+ /* display directories */
if (dircnt) {
register char *p;
+ f_total = 1;
if (dircnt > 1) {
(void)getwd(top);
qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
+ f_dirname = 1;
}
for (cnt = 0; cnt < dircnt; ++dstats) {
for (endofpath = path, p = dstats->name;
*endofpath = *p++; ++endofpath);
- ls_dir(dstats, cnt, regcnt || dircnt > 1);
+ subdir(dstats);
+ f_newline = 1;
if (++cnt < dircnt && chdir(top)) {
(void)fprintf(stderr, "ls: %s: %s\n",
top, strerror(errno));
}
}
}
-#ifdef whybother
- (void)free((char *)rstats);
- (void)free((char *)dstats);
-#endif
}
-ls(stats, num)
+displaydir(stats, num)
LS *stats;
register int num;
{
register char *p, *savedpath;
LS *lp;
- if (num > 1 && !f_specialdir)
+ if (num > 1 && !f_nosort) {
+ u_long save1, save2;
+
+ save1 = stats[0].lstat.st_btotal;
+ save2 = stats[0].lstat.st_maxlen;
qsort((char *)stats, num, sizeof(LS), sortfcn);
+ stats[0].lstat.st_btotal = save1;
+ stats[0].lstat.st_maxlen = save2;
+ }
- printdir(stats, num);
+ printfcn(stats, num);
if (f_recursive) {
savedpath = endofpath;
if (endofpath != path && endofpath[-1] != '/')
*endofpath++ = '/';
for (; *endofpath = *p++; ++endofpath);
- ls_dir(lp, 1, 1);
+ f_newline = f_dirname = f_total = 1;
+ subdir(lp);
*(endofpath = savedpath) = '\0';
}
}
}
-ls_dir(lp, newline, tag)
+subdir(lp)
LS *lp;
- int newline, tag;
{
LS *stats;
int num;
char *names;
- if (newline)
+ if (f_newline)
(void)putchar('\n');
- if (tag)
+ if (f_dirname)
(void)printf("%s:\n", path);
if (chdir(lp->name)) {
- (void)fprintf(stderr, "ls: %s: %s\n",
- lp->name, strerror(errno));
+ (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
+ strerror(errno));
return;
}
- if (num = buildstats(lp, &stats, &names))
- ls(stats, num);
- (void)free((char *)stats);
- (void)free((char *)names);
+ if (num = tabdir(lp, &stats, &names)) {
+ displaydir(stats, num);
+ (void)free((char *)stats);
+ (void)free((char *)names);
+ }
if (chdir("..")) {
(void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
exit(1);
}
}
-static
-buildstats(lp, s_stats, s_names)
+tabdir(lp, s_stats, s_names)
LS *lp, **s_stats;
char **s_names;
{
- register int cnt, maxentry;
+ register DIR *dirp;
+ register int cnt, maxentry, maxlen;
register char *p, *names;
- struct dirent *entry;
+ struct dirent *dp;
+ u_long blocks;
LS *stats;
- DIR *dirp;
-
- /* make this big so we don't realloc often */
-#define DEFNUM 256
- maxentry = DEFNUM;
- *s_stats = stats = (LS *)emalloc((u_int)DEFNUM * sizeof(LS));
- *s_names = names = emalloc((u_int)lp->lstat.st_size);
- if (!(dirp = opendir(f_specialdir ? lp->name : "."))) {
- (void)fprintf(stderr, "ls: %s: %s\n",
- lp->name, strerror(errno));
+ if (!(dirp = opendir("."))) {
+ (void)fprintf(stderr, "ls: %s: %s\n", lp->name,
+ strerror(errno));
return(0);
}
- for (cnt = 0; entry = readdir(dirp);) {
- p = entry->d_name;
+ blocks = maxentry = maxlen = 0;
+ stats = NULL;
+ for (cnt = 0; dp = readdir(dirp);) {
+ /* this does -A and -a */
+ p = dp->d_name;
if (p[0] == '.') {
if (!f_listdot)
continue;
continue;
}
if (cnt == maxentry) {
+ if (!maxentry)
+ *s_names = names =
+ emalloc((u_int)lp->lstat.st_size);
+#define DEFNUM 256
maxentry += DEFNUM;
- if (!(stats = (LS *)realloc((char *)stats,
+ if (!(*s_stats = stats = (LS *)realloc((char *)stats,
(u_int)maxentry * sizeof(LS))))
nomem();
}
- if (needstat && lstat(entry->d_name, &stats[cnt].lstat)) {
- (void)fprintf(stderr, "ls: %s: %s\n",
- entry->d_name, strerror(errno));
- if (errno == ENOENT)
- continue;
- exit(1);
+ if (f_needstat && statfcn(dp->d_name, &stats[cnt].lstat) &&
+ statfcn == stat && lstat(dp->d_name, &stats[cnt].lstat)) {
+ /*
+ * don't exit -- this could be an NFS mount that has
+ * gone away. Flush stdout so the messages line up.
+ */
+ (void)fflush(stdout);
+ (void)fprintf(stderr,
+ "ls: %s: %s\n", dp->d_name, strerror(errno));
+ continue;
}
stats[cnt].name = names;
- bcopy(entry->d_name, names, (int)entry->d_namlen);
- names += entry->d_namlen;
+
+ if (f_nonprint)
+ prcopy(dp->d_name, names, (int)dp->d_namlen);
+ else
+ bcopy(dp->d_name, names, (int)dp->d_namlen);
+ names += dp->d_namlen;
*names++ = '\0';
+
+ /*
+ * get the inode from the directory, so the -f flag
+ * works right.
+ */
+ stats[cnt].lstat.st_ino = dp->d_ino;
+
+ /* save name length for -C format */
+ stats[cnt].len = dp->d_namlen;
+
+ /* calculate number of blocks if -l/-s formats */
+ if (f_longform || f_size)
+ blocks += stats[cnt].lstat.st_blocks;
+
+ /* save max length if -C format */
+ if (f_column && maxlen < (int)dp->d_namlen)
+ maxlen = dp->d_namlen;
++cnt;
}
- closedir(dirp);
+ (void)closedir(dirp);
+
+ if (cnt) {
+ stats[0].lstat.st_btotal = blocks;
+ stats[0].lstat.st_maxlen = maxlen;
+ } else if (stats) {
+ (void)free((char *)stats);
+ (void)free((char *)names);
+ }
return(cnt);
}