X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c573514d9fb741092a754b8e00b49a4375350321..d662a9c36c88e3aca3cebd9ede5fc7af570de16f:/usr/src/bin/ls/ls.c diff --git a/usr/src/bin/ls/ls.c b/usr/src/bin/ls/ls.c index 4160143e7f..9628f7cf25 100644 --- a/usr/src/bin/ls/ls.c +++ b/usr/src/bin/ls/ls.c @@ -1,107 +1,117 @@ /* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * 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 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 -char copyright[] = -"@(#) Copyright (c) 1989 The Regents of the University of California.\n\ - All rights reserved.\n"; +static char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)ls.c 5.18 (Berkeley) %G%"; +static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) %G%"; #endif /* not lint */ -#include +#include #include +#include + #include -#include +#include #include +#include #include +#include +#include +#include + #include "ls.h" +#include "extern.h" + +static void display __P((FTSENT *, FTSENT *)); +static int mastercmp __P((const FTSENT **, const FTSENT **)); +static void traverse __P((int, char **, int)); -int lstat(), strlen(), prablelen(); -char *emalloc(); +static void (*printfcn) __P((DISPLAY *)); +static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); -int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg; +long blocksize; /* block size units */ +int termwidth = 80; /* default terminal width */ /* flags */ int f_accesstime; /* use time of last access */ -int f_firsttime = 1; /* to control recursion */ -int f_group; /* show group ownership of a file */ -int f_ignorelink; /* indirect through symbolic link operands */ +int f_column; /* columnated format */ +int f_flags; /* show flags associated with a file */ int f_inode; /* print inode */ -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_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_type; /* add type character for non-regular files */ +int main(argc, argv) int argc; - char **argv; + char *argv[]; { - extern int optind, stat(); - int ch; - int namecmp(), revnamecmp(), acccmp(), revacccmp(); - int modcmp(), revmodcmp(), statcmp(), revstatcmp(); + static char dot[] = ".", *dotav[] = { dot, NULL }; + struct winsize win; + int ch, fts_options, notused; + char *p; - /* - * terminal defaults to -C -q - * non-terminal defaults to -1 - */ - if (isatty(1)) { - f_nonprint = 1; - lengthfcn = prablelen; + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + if (isatty(STDOUT_FILENO)) { + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || + !win.ws_col) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); + } + else + termwidth = win.ws_col; + f_column = f_nonprint = 1; } else f_singlecol = 1; - /* root is -A automatically */ + /* Root is -A automatically. */ if (!getuid()) f_listdot = 1; - while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) { + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1ACFLRTacdfgiloqrstu")) != EOF) { switch (ch) { /* - * -1, -C and -l all override each other - * so shell aliasing works right + * The -1, -C and -l options all override each other so shell + * aliasing works right. */ 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 */ + /* The -c and -u options override each other. */ case 'c': f_statustime = 1; f_accesstime = 0; @@ -114,34 +124,38 @@ main(argc, argv) f_type = 1; break; case 'L': - f_ignorelink = 1; + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; break; case 'R': f_recursive = 1; break; case 'a': - f_listalldot = 1; + fts_options |= FTS_SEEDOT; /* FALLTHROUGH */ case 'A': f_listdot = 1; break; + /* The -d option turns off the -R option. */ case 'S': Sflg++; /* fall into... */ case 'd': f_listdir = 1; + f_recursive = 0; break; case 'f': - f_specialdir = 1; + f_nosort = 1; break; - case 'g': - f_group = 1; + case 'g': /* Compatibility with 4.3BSD. */ break; case 'i': f_inode = 1; break; + case 'o': + f_flags = 1; + break; case 'q': f_nonprint = 1; - lengthfcn = prablelen; break; case 'r': f_reversesort = 1; @@ -149,6 +163,9 @@ main(argc, argv) case 's': f_size = 1; break; + case 'T': + f_sectime = 1; + break; case 't': f_timesort = 1; break; @@ -160,22 +177,27 @@ 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; - } + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) + fts_options |= FTS_NOSTAT; - /* -d turns off -R */ - if (f_listdir) - f_recursive = 0; + /* + * 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 need to stat files */ - needstat = f_longform || f_recursive || f_timesort || f_size || - f_inode || f_type; + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } - /* select a sort function */ + /* Select a sort function. */ if (f_reversesort) { if (!f_timesort) sortfcn = revnamecmp; @@ -183,7 +205,7 @@ main(argc, argv) sortfcn = revacccmp; else if (f_statustime) sortfcn = revstatcmp; - else /* use modification time */ + else /* Use modification time. */ sortfcn = revmodcmp; } else { if (!f_timesort) @@ -192,232 +214,269 @@ main(argc, argv) sortfcn = acccmp; else if (f_statustime) sortfcn = statcmp; - else /* use modification time */ + else /* Use modification time. */ sortfcn = modcmp; } + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_longform) + printfcn = printlong; + else + printfcn = printcol; + if (argc) - args(argc, argv); + traverse(argc, argv, fts_options); else - curdir(); + traverse(1, dotav, fts_options); exit(0); } -curdir() +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(argc, argv, options) + int argc, options; + char *argv[]; { - LS local, *stats; - int num; - char *names; + FTS *ftsp; + FTSENT *p, *chp; + int ch_options; - if (lstat(local.name = ".", &local.lstat)) { - (void)fprintf(stderr, "ls: .: %s\n", strerror(errno)); - exit(1); - } - if (num = buildstats(&local, &stats, &names)) - ls(stats, num); -} + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) + err(1, NULL); -static char path[MAXPATHLEN + 1]; -static char *endofpath = path; + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) + return; -args(argc, argv) - int argc; - char **argv; + /* + * 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)); + 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; + } + if (errno) + err(1, "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(p, list) + FTSENT *p, *list; { - register LS *dstatp, *rstatp; - LS *dstats, *rstats; - register int cnt, dircnt, regcnt; - struct stat sb; - LS *stats; - int num, (*statfcn)(), stat(), lstat(); - char *names, top[MAXPATHLEN + 1]; + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + u_quad_t maxsize; + u_long btotal, maxblock, maxinode, maxlen, maxnlink; + int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; + int entries, needstats; + char *user, *group, *flags, buf[20]; /* 32 bits == 10 digits */ /* - * walk through the operands, building separate arrays of LS - * structures for directory and non-directory files. + * 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(). */ - 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 (errno == ENOENT) - continue; - exit(1); - } - if (!f_specialdir && !f_listdir && S_ISDIR(sb.st_mode)) { - if (!dstats) - dstatp = dstats = (LS *)emalloc((u_int)argc * - (sizeof(LS))); - dstatp->name = *argv; - dstatp->lstat = sb; - ++dstatp; - ++dircnt; - } - else { - if (!rstats) - rstatp = rstats = (LS *)emalloc((u_int)argc * - (sizeof(LS))); - rstatp->name = *argv; - rstatp->lstat = sb; - ++rstatp; - ++regcnt; + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + btotal = maxblock = maxinode = maxlen = maxnlink = 0; + bcfile = 0; + maxuser = maxgroup = maxflags = 0; + maxsize = 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; + continue; } - } - /* display regular files */ - if (regcnt) { + /* - * for -f flag -- switch above treats all -f operands as - * regular files; this code uses buildstats() to read - * them as directories. + * P is NULL if list is the argv list, to which different rules + * apply. */ - if (f_specialdir) { - for (cnt = regcnt; cnt--;) { - if (num = buildstats(rstats++, &stats, &names)) - ls(stats, num); - (void)free((char *)stats); - (void)free((char *)names); + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; } - } else - ls(rstats, regcnt); - } - /* display directories */ - if (dircnt) { - register char *p; - - if (dircnt > 1) { - (void)getwd(top); - qsort((char *)dstats, dircnt, sizeof(LS), sortfcn); - } - for (cnt = 0; cnt < dircnt; ++dstats) { - for (endofpath = path, p = dstats->name; - *endofpath = *p++; ++endofpath); - ls_dir(dstats, regcnt, regcnt || dircnt > 1); - if (++cnt < dircnt && chdir(top)) { - (void)fprintf(stderr, "ls: %s: %s\n", - top, strerror(errno)); - exit(1); + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; } } - } -} + if (f_nonprint) + prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); + 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; -ls(stats, num) - LS *stats; - register int num; -{ - register char *p, *savedpath; - LS *lp; + btotal += sp->st_blocks; + if (f_longform) { + user = user_from_uid(sp->st_uid, 0); + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + group = group_from_gid(sp->st_gid, 0); + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = + flags_to_string(sp->st_flags, "-"); + if ((flen = strlen(flags)) > maxflags) + maxflags = flen; + } else + flen = 0; - if (num > 1 && !f_specialdir) - qsort((char *)stats, num, sizeof(LS), sortfcn); + if ((np = malloc(sizeof(NAMES) + + ulen + glen + flen + 3)) == NULL) + err(1, NULL); - printdir(stats, num); + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); - if (f_recursive) { - savedpath = endofpath; - for (lp = stats; num--; ++lp) { - if (!S_ISDIR(lp->lstat.st_mode)) - continue; - p = lp->name; - if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2])) - continue; - if (endofpath != path && endofpath[-1] != '/') - *endofpath++ = '/'; - for (; *endofpath = *p++; ++endofpath); - ls_dir(lp, 1, 1); - *(endofpath = savedpath) = '\0'; + if (S_ISCHR(sp->st_mode) || + S_ISBLK(sp->st_mode)) + bcfile = 1; + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + } + cur->fts_pointer = np; + } } + ++entries; } -} -ls_dir(lp, newline, tag) - LS *lp; - int newline, tag; -{ - LS *stats; - int num; - char *names; - - /* - * this doesn't really belong here, but it's the only place that - * everybody goes through; the `tag' variable is so that we don't - * print the header for directories unless we're going to display - * more directories, or we've already displayed files or directories. - * The `newline' variable keeps us from inserting a newline before - * we've displayed anything at all. - */ - if (newline) - (void)putchar('\n'); - if (tag) - (void)printf("%s:\n", path); - - if (chdir(lp->name)) { - (void)fprintf(stderr, "ls: %s: %s\n", - lp->name, strerror(errno)); + if (!entries) return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.bcfile = bcfile; + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%lu", maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_group = maxgroup; + (void)snprintf(buf, sizeof(buf), "%lu", maxinode); + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; } - if (num = buildstats(lp, &stats, &names)) - ls(stats, num); - (void)free((char *)stats); - (void)free((char *)names); - if (chdir("..")) { - (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno)); - exit(1); - } + + printfcn(&d); + output = 1; + + if (f_longform) + for (cur = list; cur; cur = cur->fts_link) + free(cur->fts_pointer); } -static -buildstats(lp, s_stats, s_names) - LS *lp, **s_stats; - char **s_names; +/* + * 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(a, b) + const FTSENT **a, **b; { - register int cnt, maxentry; - register char *p, *names; - struct dirent *entry; - 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)); - return(0); - } - for (cnt = 0; entry = readdir(dirp);) { - /* this does -A and -a */ - p = entry->d_name; - if (p[0] == '.') { - if (!f_listdot) - continue; - if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2])) - continue; - } - if (cnt == maxentry) { - maxentry += DEFNUM; - if (!(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); - } - stats[cnt].name = names; - bcopy(entry->d_name, names, (int)entry->d_namlen); - names += entry->d_namlen; - *names++ = '\0'; - ++cnt; - } - closedir(dirp); - return(cnt); + 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) + return (namecmp(*a, *b)); + + if (a_info == b_info) + return (sortfcn(*a, *b)); + + if ((*a)->fts_level == FTS_ROOTLEVEL) + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + else + return (sortfcn(*a, *b)); + else + return (sortfcn(*a, *b)); }