| @@ -0,0 +1,172 @@
+.TH LS 1
+.SH NAME
+ls, lc \- list contents of directory
+.SH SYNOPSIS
+.B ls
+[
+.B -dlmnpqrstuFQ
+]
+.I name ...
+.PP
+.B lc
+[
+.B -dlmnpqrstuFQ
+]
+.I name ...
+.SH DESCRIPTION
+For each directory argument,
+.I ls
+lists the contents of the directory;
+for each file argument,
+.I ls
+repeats its name and any other information requested.
+When no argument is given, the current directory is listed.
+By default, the output is sorted alphabetically by name.
+.PP
+.I Lc
+is the same as
+.IR ls ,
+but sets the
+.B -p
+option and pipes the output through
+.IR mc (1).
+.PP
+There are a number of options:
+.TP
+.B -d
+If argument is a directory, list it, not
+its contents.
+.TP
+.B -l
+List in long format, giving mode (see below), file system type
+(e.g., for devices, the
+.B #
+code letter that names it; see
+.IR intro (3)),
+the instance or subdevice number, owner, group,
+size in bytes, and time of last modification
+for each file.
+.TP
+.B -m
+List the name of the user who most recently modified the file.
+.TP
+.B -n
+Don't sort the listing.
+.TP
+.B -p
+Print only the final path element of each file name.
+.TP
+.B -q
+List the
+.I qid
+(see
+.IR stat (3))
+of each file; the printed fields are in the order
+path, version, and type.
+.TP
+.B -r
+Reverse the order of sort.
+.TP
+.B -s
+Give size in Kbytes for each entry.
+.TP
+.B -t
+Sort by time modified (latest first) instead of
+by name.
+.TP
+.B -u
+Under
+.B -t
+sort by time of last access;
+under
+.B -l
+print time of last access.
+.TP
+.B -F
+Add the character
+.B /
+after all directory names
+and the character
+.B *
+after all executable files.
+.TP
+.B -L
+Print the character
+.B t
+before each file if it has the temporary flag set, and
+.B -
+otherwise.
+.TP
+.B -Q
+By default, printed file names are quoted if they contain characters special to
+.IR rc (1).
+The
+.B -Q
+flag disables this behavior.
+.PP
+The mode printed under the
+.B -l
+option contains 11 characters,
+interpreted
+as follows:
+the first character is
+.TP
+.B d
+if the entry is a directory;
+.TP
+.B a
+if the entry is an append-only file;
+.TP
+.B D
+if the entry is a Unix device;
+.TP
+.B L
+if the entry is a symbolic link;
+.TP
+.B P
+if the entry is a named pipe;
+.TP
+.B S
+if the entry is a socket;
+.TP
+.B -
+if the entry is a plain file.
+.PD
+.PP
+The next letter is
+.B l
+if the file is exclusive access (one writer or reader at a time).
+.PP
+The last 9 characters are interpreted
+as three sets of three bits each.
+The first set refers to owner permissions;
+the next to permissions to others in the same user-group;
+and the last to all others.
+Within each set the three characters indicate
+permission respectively to read, to write, or to
+execute the file as a program.
+For a directory, `execute' permission is interpreted
+to mean permission to search the directory
+for a specified file.
+The permissions are indicated as follows:
+.TP 3
+.B r
+if the file is readable;
+.PD 0
+.TP 3
+.B w
+if the file is writable;
+.TP 3
+.B x
+if the file is executable;
+.TP 3
+.B -
+if none of the above permissions is granted.
+.PD
+.SH SOURCE
+.B \*9/src/cmd/ls.c
+.br
+.B \*9/bin/lc
+.SH SEE ALSO
+.IR stat (3),
+.IR mc (1) |
| @@ -0,0 +1,309 @@
+#include
+#include
+#include
+
+#define dirbuf p9dirbuf /* avoid conflict on sun */
+
+typedef struct NDir NDir;
+struct NDir
+{
+ Dir *d;
+ char *prefix;
+};
+
+int errs = 0;
+int dflag;
+int lflag;
+int mflag;
+int nflag;
+int pflag;
+int qflag;
+int Qflag;
+int rflag;
+int sflag;
+int tflag;
+int uflag;
+int Fflag;
+int ndirbuf;
+int ndir;
+NDir* dirbuf;
+int ls(char*, int);
+int compar(NDir*, NDir*);
+char* asciitime(long);
+char* darwx(long);
+void rwx(long, char*);
+void growto(long);
+void dowidths(Dir*);
+void format(Dir*, char*);
+void output(void);
+ulong clk;
+int swidth; /* max width of -s size */
+int qwidth; /* max width of -q version */
+int vwidth; /* max width of dev */
+int uwidth; /* max width of userid */
+int mwidth; /* max width of muid */
+int glwidth; /* max width of groupid and length */
+Biobuf bin;
+
+void
+main(int argc, char *argv[])
+{
+ int i;
+
+ Binit(&bin, 1, OWRITE);
+ ARGBEGIN{
+ case 'F': Fflag++; break;
+ case 'd': dflag++; break;
+ case 'l': lflag++; break;
+ case 'm': mflag++; break;
+ case 'n': nflag++; break;
+ case 'p': pflag++; break;
+ case 'q': qflag++; break;
+ case 'Q': Qflag++; break;
+ case 'r': rflag++; break;
+ case 's': sflag++; break;
+ case 't': tflag++; break;
+ case 'u': uflag++; break;
+ default: fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
+ exits("usage");
+ }ARGEND
+
+ doquote = needsrcquote;
+ quotefmtinstall();
+ fmtinstall('M', dirmodefmt);
+
+ if(lflag)
+ clk = time(0);
+ if(argc == 0)
+ errs = ls(".", 0);
+ else for(i=0; iqid.type&QTDIR && dflag==0){
+ free(db);
+ db = nil;
+ output();
+ fd = open(s, OREAD);
+ if(fd == -1)
+ goto error;
+ n = dirreadall(fd, &db);
+ if(n < 0)
+ goto error;
+ growto(ndir+n);
+ for(i=0; iname = strdup(p+1);
+ }
+ ndir++;
+ }
+ return 0;
+}
+
+void
+output(void)
+{
+ int i;
+ char buf[4096];
+ char *s;
+
+ if(!nflag)
+ qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
+ for(i=0; iname);
+ format(dirbuf[i].d, buf);
+ } else
+ format(dirbuf[i].d, dirbuf[i].d->name);
+ }
+ ndir = 0;
+ Bflush(&bin);
+}
+
+void
+dowidths(Dir *db)
+{
+ char buf[256];
+ int n;
+
+ if(sflag) {
+ n = sprint(buf, "%llud", (db->length+1023)/1024);
+ if(n > swidth)
+ swidth = n;
+ }
+ if(qflag) {
+ n = sprint(buf, "%lud", db->qid.vers);
+ if(n > qwidth)
+ qwidth = n;
+ }
+ if(mflag) {
+ n = snprint(buf, sizeof buf, "[%s]", db->muid);
+ if(n > mwidth)
+ mwidth = n;
+ }
+ if(lflag) {
+ n = sprint(buf, "%ud", db->dev);
+ if(n > vwidth)
+ vwidth = n;
+ n = strlen(db->uid);
+ if(n > uwidth)
+ uwidth = n;
+ n = sprint(buf, "%llud", db->length);
+ n += strlen(db->gid);
+ if(n > glwidth)
+ glwidth = n;
+ }
+}
+
+char*
+fileflag(Dir *db)
+{
+ if(Fflag == 0)
+ return "";
+ if(QTDIR & db->qid.type)
+ return "/";
+ if(0111 & db->mode)
+ return "*";
+ return "";
+}
+
+void
+format(Dir *db, char *name)
+{
+ int i;
+
+ if(sflag)
+ Bprint(&bin, "%*llud ",
+ swidth, (db->length+1023)/1024);
+ if(mflag){
+ Bprint(&bin, "[%s] ", db->muid);
+ for(i=2+strlen(db->muid); iqid.path,
+ qwidth, db->qid.vers,
+ db->qid.type);
+ if(lflag)
+ Bprint(&bin,
+ Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n",
+ db->mode, db->type,
+ vwidth, db->dev,
+ -uwidth, db->uid,
+ db->gid,
+ (int)(glwidth-strlen(db->gid)), db->length,
+ asciitime(uflag? db->atime : db->mtime), name);
+ else
+ Bprint(&bin,
+ Qflag? "%s%s\n" : "%q%s\n",
+ name, fileflag(db));
+}
+
+void
+growto(long n)
+{
+ if(n <= ndirbuf)
+ return;
+ ndirbuf = n;
+ dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
+ if(dirbuf == 0){
+ fprint(2, "ls: malloc fail\n");
+ exits("malloc fail");
+ }
+}
+
+int
+compar(NDir *a, NDir *b)
+{
+ long i;
+ Dir *ad, *bd;
+
+ ad = a->d;
+ bd = b->d;
+
+ if(tflag){
+ if(uflag)
+ i = bd->atime-ad->atime;
+ else
+ i = bd->mtime-ad->mtime;
+ }else{
+ if(a->prefix && b->prefix){
+ i = strcmp(a->prefix, b->prefix);
+ if(i == 0)
+ i = strcmp(ad->name, bd->name);
+ }else if(a->prefix){
+ i = strcmp(a->prefix, bd->name);
+ if(i == 0)
+ i = 1; /* a is longer than b */
+ }else if(b->prefix){
+ i = strcmp(ad->name, b->prefix);
+ if(i == 0)
+ i = -1; /* b is longer than a */
+ }else
+ i = strcmp(ad->name, bd->name);
+ }
+ if(i == 0)
+ i = (ad |