9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

commit 6ccdc8cffd953f6dae2692e687d19ac6e58a7e2b
parent b7abc7dd08640a98b0af92b05bb0a57e614f3160
Author: Anselm R. Garbe <garbeam@wmii.de>
Date:   Tue, 31 Jan 2006 20:37:59 +0200

added ls

Diffstat:
Makefile | 4++--
config.mk | 2+-
ls/Makefile | 7+++++++
ls/ls.1 | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +} +