commit 6ccdc8cffd953f6dae2692e687d19ac6e58a7e2b
parent b7abc7dd08640a98b0af92b05bb0a57e614f3160
Author: Anselm R. Garbe <garbeam@wmii.de>
Date: Tue, 31 Jan 2006 20:37:59 +0200
added ls
Diffstat:
M | Makefile | | | 4 | ++-- |
M | config.mk | | | 2 | +- |
A | ls/Makefile | | | 7 | +++++++ |
A | ls/ls.1 | | | 172 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ls/ls.c | | | 309 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 491 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,8 +3,8 @@
include config.mk
-SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep mk \
- rc read sed seq sleep sort tee test touch tr uniq
+SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep ls \
+ mk rc read sed seq sleep sort tee test touch tr uniq
all:
@echo 9base build options:
diff --git a/config.mk b/config.mk
@@ -4,7 +4,7 @@
PREFIX = /usr/local/9
MANPREFIX = ${PREFIX}/share/man
-VERSION = 2
+VERSION = 20060129
# Linux/BSD
CFLAGS = -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\""
diff --git a/ls/Makefile b/ls/Makefile
@@ -0,0 +1,7 @@
+# ls - ls unix port from plan9
+#
+# Depends on ../lib9
+
+TARG = ls
+
+include ../std.mk
diff --git a/ls/ls.1 b/ls/ls.1
@@ -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)
diff --git a/ls/ls.c b/ls/ls.c
@@ -0,0 +1,309 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#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; i<argc; i++)
+ errs |= ls(argv[i], 1);
+ output();
+ exits(errs? "errors" : 0);
+}
+
+int
+ls(char *s, int multi)
+{
+ int fd;
+ long i, n;
+ char *p;
+ Dir *db;
+
+ for(;;) {
+ p = utfrrune(s, '/');
+ if(p == 0 || p[1] != 0 || p == s)
+ break;
+ *p = 0;
+ }
+ db = dirstat(s);
+ if(db == nil){
+ error:
+ fprint(2, "ls: %s: %r\n", s);
+ return 1;
+ }
+ if(db->qid.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; i<n; i++){
+ dirbuf[ndir+i].d = db+i;
+ dirbuf[ndir+i].prefix = multi? s : 0;
+ }
+ ndir += n;
+ close(fd);
+ output();
+ }else{
+ growto(ndir+1);
+ dirbuf[ndir].d = db;
+ dirbuf[ndir].prefix = 0;
+ p = utfrrune(s, '/');
+ if(p){
+ dirbuf[ndir].prefix = s;
+ *p = 0;
+ /* restore original name; don't use result of stat */
+ dirbuf[ndir].d->name = 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; i<ndir; i++)
+ dowidths(dirbuf[i].d);
+ for(i=0; i<ndir; i++) {
+ if(!pflag && (s = dirbuf[i].prefix)) {
+ if(strcmp(s, "/") ==0) /* / is a special case */
+ s = "";
+ sprint(buf, "%s/%s", s, dirbuf[i].d->name);
+ 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); i<mwidth; i++)
+ Bprint(&bin, " ");
+ }
+ if(qflag)
+ Bprint(&bin, "(%.16llux %*lud %.2ux) ",
+ db->qid.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<bd? -1 : 1);
+ if(rflag)
+ i = -i;
+ return i;
+}
+
+char*
+asciitime(long l)
+{
+ static char buf[32];
+ char *t;
+
+ t = ctime(l);
+ /* 6 months in the past or a day in the future */
+ if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
+ memmove(buf, t+4, 7); /* month and day */
+ memmove(buf+7, t+23, 5); /* year */
+ }else
+ memmove(buf, t+4, 12); /* skip day of week */
+ buf[12] = 0;
+ return buf;
+}
+