327 lines
5.6 KiB
C
327 lines
5.6 KiB
C
|
#include <u.h>
|
||
|
#include <libc.h>
|
||
|
#include <bio.h>
|
||
|
#include <fcall.h>
|
||
|
|
||
|
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 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);
|
||
|
char* xcleanname(char*);
|
||
|
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 lwidth; /* max width of length */
|
||
|
int gwidth; /* max width of groupid */
|
||
|
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 'T': Tflag++; break;
|
||
|
case 'u': uflag++; break;
|
||
|
default: fprint(2, "usage: ls [-dlmnpqrstuFQT] [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;
|
||
|
|
||
|
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);
|
||
|
output();
|
||
|
fd = open(s, OREAD);
|
||
|
if(fd == -1)
|
||
|
goto error;
|
||
|
n = dirreadall(fd, &db);
|
||
|
if(n < 0)
|
||
|
goto error;
|
||
|
xcleanname(s);
|
||
|
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;
|
||
|
xcleanname(s);
|
||
|
p = utfrrune(s, '/');
|
||
|
if(p){
|
||
|
dirbuf[ndir].prefix = s;
|
||
|
*p = 0;
|
||
|
}
|
||
|
ndir++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
output(void)
|
||
|
{
|
||
|
int i;
|
||
|
char buf[4096];
|
||
|
char *s;
|
||
|
|
||
|
if(!nflag)
|
||
|
qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void*, 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, "[%q]", db->muid);
|
||
|
if(n > mwidth)
|
||
|
mwidth = n;
|
||
|
}
|
||
|
if(lflag) {
|
||
|
n = sprint(buf, "%ud", db->dev);
|
||
|
if(n > vwidth)
|
||
|
vwidth = n;
|
||
|
n = sprint(buf, "%q", db->uid);
|
||
|
if(n > uwidth)
|
||
|
uwidth = n;
|
||
|
n = sprint(buf, "%q", db->gid);
|
||
|
if(n > gwidth)
|
||
|
gwidth = n;
|
||
|
n = sprint(buf, "%llud", db->length);
|
||
|
if(n > lwidth)
|
||
|
lwidth = 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, "[%q] ", 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(Tflag)
|
||
|
Bprint(&bin, "%c ", (db->mode&DMTMP)? 't': '-');
|
||
|
|
||
|
if(lflag)
|
||
|
Bprint(&bin, "%M %C %*ud %*q %*q %*llud %s ",
|
||
|
db->mode, db->type,
|
||
|
vwidth, db->dev,
|
||
|
-uwidth, db->uid,
|
||
|
-gwidth, db->gid,
|
||
|
lwidth, db->length,
|
||
|
asciitime(uflag? db->atime: db->mtime));
|
||
|
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 = (a<b? -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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compress slashes, remove trailing slash. Don't worry about . and ..
|
||
|
*/
|
||
|
char*
|
||
|
xcleanname(char *name)
|
||
|
{
|
||
|
char *r, *w;
|
||
|
|
||
|
for(r=w=name; *r; r++){
|
||
|
if(*r=='/' && r>name && *(r-1)=='/')
|
||
|
continue;
|
||
|
if(w != r)
|
||
|
*w = *r;
|
||
|
w++;
|
||
|
}
|
||
|
*w = 0;
|
||
|
while(w-1>name && *(w-1)=='/')
|
||
|
*--w = 0;
|
||
|
return name;
|
||
|
}
|