X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/be4f2604382b7c11ea42ab3bd925ca001cdc3953..410e3bfc6d136508e5c9ffddc777ba6bdc4a3c18:/usr/src/bin/ls/ls.c diff --git a/usr/src/bin/ls/ls.c b/usr/src/bin/ls/ls.c index 73c66ae878..22a75a326d 100644 --- a/usr/src/bin/ls/ls.c +++ b/usr/src/bin/ls/ls.c @@ -5,17 +5,7 @@ * 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 @@ -25,79 +15,116 @@ char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)ls.c 5.15 (Berkeley) %G%"; +static char sccsid[] = "@(#)ls.c 5.48 (Berkeley) %G%"; #endif /* not lint */ #include #include +#include #include -#include +#include #include #include #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_listdot; /* list files beginning with . */ -int f_listalldot; /* list . and .. as well */ -int f_modtime; /* use time of last change for time */ int f_accesstime; /* use time of last access */ -int f_statustime; /* use time of last mode change */ -int f_singlecol; /* use single column output */ -int f_listdir; /* list actual directory, not contents */ -int f_specialdir; /* force params to be directories */ -int f_type; /* add type character for non-regular files */ +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_reversesort; /* reverse whatever sort is used */ +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_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_firsttime = 1; /* to control recursion */ +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(); - /* set up defaults for terminal/nonterminal stdout */ + /* 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; - /* root sees all files automatically */ + /* root is -A automatically */ 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 + * so shell aliasing works right + */ case '1': f_singlecol = 1; + f_column = f_longform = 0; break; case 'C': - f_singlecol = 0; + f_column = 1; + f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_column = f_singlecol = 0; + break; + /* -c and -u 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': - statfcn = stat; + f_ignorelink = 1; break; case 'R': f_recursive = 1; - statfcn = lstat; break; case 'a': f_listalldot = 1; @@ -105,17 +132,13 @@ main(argc, argv) case 'A': f_listdot = 1; break; - case 'c': - f_statustime = 1; - f_modtime = f_accesstime = 0; - break; case 'S': Sflg++; /* fall into... */ case 'd': f_listdir = 1; break; case 'f': - f_specialdir = 1; + f_nosort = 1; break; case 'g': f_group = 1; @@ -123,32 +146,23 @@ main(argc, argv) case 'i': f_inode = 1; break; - case 'l': - f_longform = 1; - f_singlecol = 0; - statfcn = lstat; - if (!f_accesstime && !f_statustime) - f_modtime = 1; + case 'k': + f_kblocks = 1; break; case 'q': f_nonprint = 1; - lengthfcn = prablelen; break; case 'r': f_reversesort = 1; break; case 's': f_size = 1; - statfcn = lstat; + break; + case 'T': + f_sectime = 1; break; case 't': f_timesort = 1; - if (!f_accesstime && !f_statustime) - f_modtime = 1; - break; - case 'u': - f_modtime = f_statustime = 0; - f_accesstime = 1; break; default: case '?': @@ -158,20 +172,13 @@ main(argc, argv) 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 will need to stat files */ - needstat = f_longform || f_recursive || f_timesort || f_size || - f_inode || f_type; + /* if need to stat files */ + f_needstat = f_longform || f_recursive || f_timesort || + f_size || f_type; /* select a sort function */ if (f_reversesort) { @@ -179,123 +186,158 @@ main(argc, argv) sortfcn = revnamecmp; else if (f_accesstime) sortfcn = revacccmp; - else if (f_modtime) - sortfcn = revmodcmp; else if (f_statustime) sortfcn = revstatcmp; + else /* use modification time */ + sortfcn = revmodcmp; } else { if (!f_timesort) sortfcn = namecmp; else if (f_accesstime) sortfcn = acccmp; - else if (f_modtime) - sortfcn = modcmp; else if (f_statustime) sortfcn = statcmp; + else /* use modification time */ + 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 (statfcn(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; - char *names; + 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; 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; - if (dircnt > 1) + f_total = 1; + if (dircnt > 1) { + (void)getwd(top); qsort((char *)dstats, dircnt, sizeof(LS), sortfcn); - for (cnt = 0; cnt < dircnt; ++cnt) { + 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)); + exit(1); + } } } -#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; @@ -308,64 +350,62 @@ ls(stats, num) 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; @@ -373,24 +413,61 @@ buildstats(lp, s_stats, s_names) 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 && statfcn(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); }