xref: /plan9/sys/src/cmd/ls.c (revision 07c323958a448aa76afd8e66ba1570d6bb6c6c19)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
43e12c5d1SDavid du Colombier #include <fcall.h>
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier typedef struct NDir NDir;
73e12c5d1SDavid du Colombier struct NDir
83e12c5d1SDavid du Colombier {
99a747e4fSDavid du Colombier 	Dir *d;
103e12c5d1SDavid du Colombier 	char	*prefix;
113e12c5d1SDavid du Colombier };
123e12c5d1SDavid du Colombier 
133e12c5d1SDavid du Colombier int	errs = 0;
143e12c5d1SDavid du Colombier int	dflag;
153e12c5d1SDavid du Colombier int	lflag;
169a747e4fSDavid du Colombier int	mflag;
173e12c5d1SDavid du Colombier int	nflag;
183e12c5d1SDavid du Colombier int	pflag;
193e12c5d1SDavid du Colombier int	qflag;
209a747e4fSDavid du Colombier int	Qflag;
213e12c5d1SDavid du Colombier int	rflag;
223e12c5d1SDavid du Colombier int	sflag;
233e12c5d1SDavid du Colombier int	tflag;
24e44fe4caSDavid du Colombier int	Tflag;
253e12c5d1SDavid du Colombier int	uflag;
26219b2ee8SDavid du Colombier int	Fflag;
273e12c5d1SDavid du Colombier int	ndirbuf;
283e12c5d1SDavid du Colombier int	ndir;
293e12c5d1SDavid du Colombier NDir*	dirbuf;
303e12c5d1SDavid du Colombier int	ls(char*, int);
313e12c5d1SDavid du Colombier int	compar(NDir*, NDir*);
323e12c5d1SDavid du Colombier char*	asciitime(long);
333e12c5d1SDavid du Colombier char*	darwx(long);
343e12c5d1SDavid du Colombier void	rwx(long, char*);
353e12c5d1SDavid du Colombier void	growto(long);
363e12c5d1SDavid du Colombier void	dowidths(Dir*);
373e12c5d1SDavid du Colombier void	format(Dir*, char*);
383e12c5d1SDavid du Colombier void	output(void);
39b4ed3b92SDavid du Colombier char*	xcleanname(char*);
403e12c5d1SDavid du Colombier ulong	clk;
417dd7cddfSDavid du Colombier int	swidth;			/* max width of -s size */
427dd7cddfSDavid du Colombier int	qwidth;			/* max width of -q version */
437dd7cddfSDavid du Colombier int	vwidth;			/* max width of dev */
447dd7cddfSDavid du Colombier int	uwidth;			/* max width of userid */
459a747e4fSDavid du Colombier int	mwidth;			/* max width of muid */
46*07c32395SDavid du Colombier int	lwidth;			/* max width of length */
47*07c32395SDavid du Colombier int	gwidth;			/* max width of groupid */
483e12c5d1SDavid du Colombier Biobuf	bin;
493e12c5d1SDavid du Colombier 
503e12c5d1SDavid du Colombier void
main(int argc,char * argv[])513e12c5d1SDavid du Colombier main(int argc, char *argv[])
523e12c5d1SDavid du Colombier {
536b6b9ac8SDavid du Colombier 	int i;
543e12c5d1SDavid du Colombier 
553e12c5d1SDavid du Colombier 	Binit(&bin, 1, OWRITE);
563e12c5d1SDavid du Colombier 	ARGBEGIN{
57219b2ee8SDavid du Colombier 	case 'F':	Fflag++; break;
583e12c5d1SDavid du Colombier 	case 'd':	dflag++; break;
593e12c5d1SDavid du Colombier 	case 'l':	lflag++; break;
609a747e4fSDavid du Colombier 	case 'm':	mflag++; break;
613e12c5d1SDavid du Colombier 	case 'n':	nflag++; break;
623e12c5d1SDavid du Colombier 	case 'p':	pflag++; break;
633e12c5d1SDavid du Colombier 	case 'q':	qflag++; break;
649a747e4fSDavid du Colombier 	case 'Q':	Qflag++; break;
653e12c5d1SDavid du Colombier 	case 'r':	rflag++; break;
663e12c5d1SDavid du Colombier 	case 's':	sflag++; break;
673e12c5d1SDavid du Colombier 	case 't':	tflag++; break;
68e44fe4caSDavid du Colombier 	case 'T':	Tflag++; break;
693e12c5d1SDavid du Colombier 	case 'u':	uflag++; break;
70e44fe4caSDavid du Colombier 	default:	fprint(2, "usage: ls [-dlmnpqrstuFQT] [file ...]\n");
713e12c5d1SDavid du Colombier 			exits("usage");
723e12c5d1SDavid du Colombier 	}ARGEND
733e12c5d1SDavid du Colombier 
74d9306527SDavid du Colombier 	doquote = needsrcquote;
759a747e4fSDavid du Colombier 	quotefmtinstall();
769a747e4fSDavid du Colombier 	fmtinstall('M', dirmodefmt);
773e12c5d1SDavid du Colombier 
786b6b9ac8SDavid du Colombier 	if(lflag)
796b6b9ac8SDavid du Colombier 		clk = time(0);
803e12c5d1SDavid du Colombier 	if(argc == 0)
813e12c5d1SDavid du Colombier 		errs = ls(".", 0);
823e12c5d1SDavid du Colombier 	else for(i=0; i<argc; i++)
833e12c5d1SDavid du Colombier 		errs |= ls(argv[i], 1);
843e12c5d1SDavid du Colombier 	output();
853e12c5d1SDavid du Colombier 	exits(errs? "errors" : 0);
863e12c5d1SDavid du Colombier }
873e12c5d1SDavid du Colombier 
883e12c5d1SDavid du Colombier int
ls(char * s,int multi)893e12c5d1SDavid du Colombier ls(char *s, int multi)
903e12c5d1SDavid du Colombier {
913e12c5d1SDavid du Colombier 	int fd;
923e12c5d1SDavid du Colombier 	long i, n;
933e12c5d1SDavid du Colombier 	char *p;
949a747e4fSDavid du Colombier 	Dir *db;
953e12c5d1SDavid du Colombier 
969a747e4fSDavid du Colombier 	db = dirstat(s);
979a747e4fSDavid du Colombier 	if(db == nil){
983e12c5d1SDavid du Colombier     error:
99219b2ee8SDavid du Colombier 		fprint(2, "ls: %s: %r\n", s);
1003e12c5d1SDavid du Colombier 		return 1;
1013e12c5d1SDavid du Colombier 	}
102dc5a79c1SDavid du Colombier 	if((db->qid.type&QTDIR) && dflag==0){
10364df97a2SDavid du Colombier 		free(db);
1043e12c5d1SDavid du Colombier 		output();
1053e12c5d1SDavid du Colombier 		fd = open(s, OREAD);
1063e12c5d1SDavid du Colombier 		if(fd == -1)
1073e12c5d1SDavid du Colombier 			goto error;
1089a747e4fSDavid du Colombier 		n = dirreadall(fd, &db);
1099a747e4fSDavid du Colombier 		if(n < 0)
1109a747e4fSDavid du Colombier 			goto error;
111b4ed3b92SDavid du Colombier 		xcleanname(s);
1123e12c5d1SDavid du Colombier 		growto(ndir+n);
1133e12c5d1SDavid du Colombier 		for(i=0; i<n; i++){
1149a747e4fSDavid du Colombier 			dirbuf[ndir+i].d = db+i;
1153e12c5d1SDavid du Colombier 			dirbuf[ndir+i].prefix = multi? s : 0;
1163e12c5d1SDavid du Colombier 		}
1173e12c5d1SDavid du Colombier 		ndir += n;
1183e12c5d1SDavid du Colombier 		close(fd);
1193e12c5d1SDavid du Colombier 		output();
1203e12c5d1SDavid du Colombier 	}else{
1213e12c5d1SDavid du Colombier 		growto(ndir+1);
1229a747e4fSDavid du Colombier 		dirbuf[ndir].d = db;
1233e12c5d1SDavid du Colombier 		dirbuf[ndir].prefix = 0;
124b4ed3b92SDavid du Colombier 		xcleanname(s);
1253e12c5d1SDavid du Colombier 		p = utfrrune(s, '/');
1263e12c5d1SDavid du Colombier 		if(p){
1273e12c5d1SDavid du Colombier 			dirbuf[ndir].prefix = s;
1283e12c5d1SDavid du Colombier 			*p = 0;
1293e12c5d1SDavid du Colombier 		}
1303e12c5d1SDavid du Colombier 		ndir++;
1313e12c5d1SDavid du Colombier 	}
1323e12c5d1SDavid du Colombier 	return 0;
1333e12c5d1SDavid du Colombier }
1343e12c5d1SDavid du Colombier 
1353e12c5d1SDavid du Colombier void
output(void)1363e12c5d1SDavid du Colombier output(void)
1373e12c5d1SDavid du Colombier {
1383e12c5d1SDavid du Colombier 	int i;
1393e12c5d1SDavid du Colombier 	char buf[4096];
1403e12c5d1SDavid du Colombier 	char *s;
1413e12c5d1SDavid du Colombier 
1423e12c5d1SDavid du Colombier 	if(!nflag)
1433e12c5d1SDavid du Colombier 		qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void*, void*))compar);
1443e12c5d1SDavid du Colombier 	for(i=0; i<ndir; i++)
1459a747e4fSDavid du Colombier 		dowidths(dirbuf[i].d);
1463e12c5d1SDavid du Colombier 	for(i=0; i<ndir; i++) {
1473e12c5d1SDavid du Colombier 		if(!pflag && (s = dirbuf[i].prefix)) {
1483e12c5d1SDavid du Colombier 			if(strcmp(s, "/") ==0)	/* / is a special case */
1493e12c5d1SDavid du Colombier 				s = "";
1509a747e4fSDavid du Colombier 			sprint(buf, "%s/%s", s, dirbuf[i].d->name);
1519a747e4fSDavid du Colombier 			format(dirbuf[i].d, buf);
1523e12c5d1SDavid du Colombier 		} else
1539a747e4fSDavid du Colombier 			format(dirbuf[i].d, dirbuf[i].d->name);
1543e12c5d1SDavid du Colombier 	}
1553e12c5d1SDavid du Colombier 	ndir = 0;
1563e12c5d1SDavid du Colombier 	Bflush(&bin);
1573e12c5d1SDavid du Colombier }
1583e12c5d1SDavid du Colombier 
1593e12c5d1SDavid du Colombier void
dowidths(Dir * db)1603e12c5d1SDavid du Colombier dowidths(Dir *db)
1613e12c5d1SDavid du Colombier {
1629a747e4fSDavid du Colombier 	char buf[256];
1633e12c5d1SDavid du Colombier 	int n;
1643e12c5d1SDavid du Colombier 
1653e12c5d1SDavid du Colombier 	if(sflag) {
1667dd7cddfSDavid du Colombier 		n = sprint(buf, "%llud", (db->length+1023)/1024);
1673e12c5d1SDavid du Colombier 		if(n > swidth)
1683e12c5d1SDavid du Colombier 			swidth = n;
1693e12c5d1SDavid du Colombier 	}
1703e12c5d1SDavid du Colombier 	if(qflag) {
1717dd7cddfSDavid du Colombier 		n = sprint(buf, "%lud", db->qid.vers);
1723e12c5d1SDavid du Colombier 		if(n > qwidth)
1733e12c5d1SDavid du Colombier 			qwidth = n;
1743e12c5d1SDavid du Colombier 	}
1759a747e4fSDavid du Colombier 	if(mflag) {
176*07c32395SDavid du Colombier 		n = snprint(buf, sizeof buf, "[%q]", db->muid);
1779a747e4fSDavid du Colombier 		if(n > mwidth)
1789a747e4fSDavid du Colombier 			mwidth = n;
1799a747e4fSDavid du Colombier 	}
1803e12c5d1SDavid du Colombier 	if(lflag) {
1817dd7cddfSDavid du Colombier 		n = sprint(buf, "%ud", db->dev);
1823e12c5d1SDavid du Colombier 		if(n > vwidth)
1833e12c5d1SDavid du Colombier 			vwidth = n;
184*07c32395SDavid du Colombier 		n = sprint(buf, "%q", db->uid);
1853e12c5d1SDavid du Colombier 		if(n > uwidth)
1863e12c5d1SDavid du Colombier 			uwidth = n;
187*07c32395SDavid du Colombier 		n = sprint(buf, "%q", db->gid);
188*07c32395SDavid du Colombier 		if(n > gwidth)
189*07c32395SDavid du Colombier 			gwidth = n;
1907dd7cddfSDavid du Colombier 		n = sprint(buf, "%llud", db->length);
191*07c32395SDavid du Colombier 		if(n > lwidth)
192*07c32395SDavid du Colombier 			lwidth = n;
1933e12c5d1SDavid du Colombier 	}
1943e12c5d1SDavid du Colombier }
1953e12c5d1SDavid du Colombier 
196219b2ee8SDavid du Colombier char*
fileflag(Dir * db)197219b2ee8SDavid du Colombier fileflag(Dir *db)
198219b2ee8SDavid du Colombier {
199219b2ee8SDavid du Colombier 	if(Fflag == 0)
200219b2ee8SDavid du Colombier 		return "";
2019a747e4fSDavid du Colombier 	if(QTDIR & db->qid.type)
202219b2ee8SDavid du Colombier 		return "/";
203219b2ee8SDavid du Colombier 	if(0111 & db->mode)
204219b2ee8SDavid du Colombier 		return "*";
205219b2ee8SDavid du Colombier 	return "";
206219b2ee8SDavid du Colombier }
207219b2ee8SDavid du Colombier 
2083e12c5d1SDavid du Colombier void
format(Dir * db,char * name)2093e12c5d1SDavid du Colombier format(Dir *db, char *name)
2103e12c5d1SDavid du Colombier {
2119a747e4fSDavid du Colombier 	int i;
2129a747e4fSDavid du Colombier 
2133e12c5d1SDavid du Colombier 	if(sflag)
2147dd7cddfSDavid du Colombier 		Bprint(&bin, "%*llud ",
2153e12c5d1SDavid du Colombier 			swidth, (db->length+1023)/1024);
2169a747e4fSDavid du Colombier 	if(mflag){
217*07c32395SDavid du Colombier 		Bprint(&bin, "[%q] ", db->muid);
2189a747e4fSDavid du Colombier 		for(i=2+strlen(db->muid); i<mwidth; i++)
2199a747e4fSDavid du Colombier 			Bprint(&bin, " ");
2209a747e4fSDavid du Colombier 	}
2213e12c5d1SDavid du Colombier 	if(qflag)
2229a747e4fSDavid du Colombier 		Bprint(&bin, "(%.16llux %*lud %.2ux) ",
2233e12c5d1SDavid du Colombier 			db->qid.path,
2249a747e4fSDavid du Colombier 			qwidth, db->qid.vers,
2259a747e4fSDavid du Colombier 			db->qid.type);
226e44fe4caSDavid du Colombier 	if(Tflag)
227e44fe4caSDavid du Colombier 		Bprint(&bin, "%c ", (db->mode&DMTMP)? 't': '-');
228e44fe4caSDavid du Colombier 
2293e12c5d1SDavid du Colombier 	if(lflag)
230*07c32395SDavid du Colombier 		Bprint(&bin, "%M %C %*ud %*q %*q %*llud %s ",
2313e12c5d1SDavid du Colombier 			db->mode, db->type,
2323e12c5d1SDavid du Colombier 			vwidth, db->dev,
2333e12c5d1SDavid du Colombier 			-uwidth, db->uid,
234*07c32395SDavid du Colombier 			-gwidth, db->gid,
235*07c32395SDavid du Colombier 			lwidth, db->length,
2364d61b946SDavid du Colombier 			asciitime(uflag? db->atime: db->mtime));
2374d61b946SDavid du Colombier 	Bprint(&bin, Qflag? "%s%s\n": "%q%s\n", name, fileflag(db));
2383e12c5d1SDavid du Colombier }
2393e12c5d1SDavid du Colombier 
2403e12c5d1SDavid du Colombier void
growto(long n)2413e12c5d1SDavid du Colombier growto(long n)
2423e12c5d1SDavid du Colombier {
2433e12c5d1SDavid du Colombier 	if(n <= ndirbuf)
2443e12c5d1SDavid du Colombier 		return;
2453e12c5d1SDavid du Colombier 	ndirbuf = n;
2463e12c5d1SDavid du Colombier 	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
2473e12c5d1SDavid du Colombier 	if(dirbuf == 0){
2483e12c5d1SDavid du Colombier 		fprint(2, "ls: malloc fail\n");
2493e12c5d1SDavid du Colombier 		exits("malloc fail");
2503e12c5d1SDavid du Colombier 	}
2513e12c5d1SDavid du Colombier }
2523e12c5d1SDavid du Colombier 
2533e12c5d1SDavid du Colombier int
compar(NDir * a,NDir * b)2543e12c5d1SDavid du Colombier compar(NDir *a, NDir *b)
2553e12c5d1SDavid du Colombier {
2563e12c5d1SDavid du Colombier 	long i;
2579a747e4fSDavid du Colombier 	Dir *ad, *bd;
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier 	ad = a->d;
2609a747e4fSDavid du Colombier 	bd = b->d;
2613e12c5d1SDavid du Colombier 
2623e12c5d1SDavid du Colombier 	if(tflag){
2633e12c5d1SDavid du Colombier 		if(uflag)
2649a747e4fSDavid du Colombier 			i = bd->atime-ad->atime;
2653e12c5d1SDavid du Colombier 		else
2669a747e4fSDavid du Colombier 			i = bd->mtime-ad->mtime;
2673e12c5d1SDavid du Colombier 	}else{
2683e12c5d1SDavid du Colombier 		if(a->prefix && b->prefix){
2693e12c5d1SDavid du Colombier 			i = strcmp(a->prefix, b->prefix);
2703e12c5d1SDavid du Colombier 			if(i == 0)
2719a747e4fSDavid du Colombier 				i = strcmp(ad->name, bd->name);
2723e12c5d1SDavid du Colombier 		}else if(a->prefix){
2739a747e4fSDavid du Colombier 			i = strcmp(a->prefix, bd->name);
2743e12c5d1SDavid du Colombier 			if(i == 0)
2753e12c5d1SDavid du Colombier 				i = 1;	/* a is longer than b */
2763e12c5d1SDavid du Colombier 		}else if(b->prefix){
2779a747e4fSDavid du Colombier 			i = strcmp(ad->name, b->prefix);
2783e12c5d1SDavid du Colombier 			if(i == 0)
2793e12c5d1SDavid du Colombier 				i = -1;	/* b is longer than a */
2803e12c5d1SDavid du Colombier 		}else
2819a747e4fSDavid du Colombier 			i = strcmp(ad->name, bd->name);
2823e12c5d1SDavid du Colombier 	}
2833e12c5d1SDavid du Colombier 	if(i == 0)
2843e12c5d1SDavid du Colombier 		i = (a<b? -1 : 1);
2853e12c5d1SDavid du Colombier 	if(rflag)
286219b2ee8SDavid du Colombier 		i = -i;
2873e12c5d1SDavid du Colombier 	return i;
2883e12c5d1SDavid du Colombier }
2893e12c5d1SDavid du Colombier 
2903e12c5d1SDavid du Colombier char*
asciitime(long l)2913e12c5d1SDavid du Colombier asciitime(long l)
2923e12c5d1SDavid du Colombier {
2933e12c5d1SDavid du Colombier 	static char buf[32];
2943e12c5d1SDavid du Colombier 	char *t;
2953e12c5d1SDavid du Colombier 
2963e12c5d1SDavid du Colombier 	t = ctime(l);
2973e12c5d1SDavid du Colombier 	/* 6 months in the past or a day in the future */
2983e12c5d1SDavid du Colombier 	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
2993e12c5d1SDavid du Colombier 		memmove(buf, t+4, 7);		/* month and day */
3003e12c5d1SDavid du Colombier 		memmove(buf+7, t+23, 5);		/* year */
3013e12c5d1SDavid du Colombier 	}else
3023e12c5d1SDavid du Colombier 		memmove(buf, t+4, 12);		/* skip day of week */
3033e12c5d1SDavid du Colombier 	buf[12] = 0;
3043e12c5d1SDavid du Colombier 	return buf;
3053e12c5d1SDavid du Colombier }
3069a747e4fSDavid du Colombier 
307b4ed3b92SDavid du Colombier /*
308b4ed3b92SDavid du Colombier  * Compress slashes, remove trailing slash.  Don't worry about . and ..
309b4ed3b92SDavid du Colombier  */
310b4ed3b92SDavid du Colombier char*
xcleanname(char * name)311b4ed3b92SDavid du Colombier xcleanname(char *name)
312b4ed3b92SDavid du Colombier {
313b4ed3b92SDavid du Colombier 	char *r, *w;
314b4ed3b92SDavid du Colombier 
315b4ed3b92SDavid du Colombier 	for(r=w=name; *r; r++){
316b4ed3b92SDavid du Colombier 		if(*r=='/' && r>name && *(r-1)=='/')
317b4ed3b92SDavid du Colombier 			continue;
318*07c32395SDavid du Colombier 		if(w != r)
319*07c32395SDavid du Colombier 			*w = *r;
320*07c32395SDavid du Colombier 		w++;
321b4ed3b92SDavid du Colombier 	}
322d6ef7babSDavid du Colombier 	*w = 0;
323b4ed3b92SDavid du Colombier 	while(w-1>name && *(w-1)=='/')
324b4ed3b92SDavid du Colombier 		*--w = 0;
325b4ed3b92SDavid du Colombier 	return name;
326b4ed3b92SDavid du Colombier }
327