xref: /plan9/sys/src/cmd/ls.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
4*219b2ee8SDavid du Colombier #include <auth.h>
53e12c5d1SDavid du Colombier #include <fcall.h>
63e12c5d1SDavid du Colombier 
73e12c5d1SDavid du Colombier typedef struct NDir NDir;
83e12c5d1SDavid du Colombier struct NDir
93e12c5d1SDavid du Colombier {
103e12c5d1SDavid du Colombier 	Dir;
113e12c5d1SDavid du Colombier 	char	*prefix;
123e12c5d1SDavid du Colombier };
133e12c5d1SDavid du Colombier 
143e12c5d1SDavid du Colombier int	errs = 0;
153e12c5d1SDavid du Colombier int	dflag;
163e12c5d1SDavid du Colombier int	lflag;
173e12c5d1SDavid du Colombier int	nflag;
183e12c5d1SDavid du Colombier int	pflag;
193e12c5d1SDavid du Colombier int	qflag;
203e12c5d1SDavid du Colombier int	rflag;
213e12c5d1SDavid du Colombier int	sflag;
223e12c5d1SDavid du Colombier int	tflag;
233e12c5d1SDavid du Colombier int	uflag;
24*219b2ee8SDavid du Colombier int	Fflag;
253e12c5d1SDavid du Colombier int	ndirbuf;
263e12c5d1SDavid du Colombier int	ndir;
273e12c5d1SDavid du Colombier NDir*	dirbuf;
283e12c5d1SDavid du Colombier int	ls(char*, int);
293e12c5d1SDavid du Colombier int	compar(NDir*, NDir*);
303e12c5d1SDavid du Colombier char*	asciitime(long);
313e12c5d1SDavid du Colombier char*	darwx(long);
323e12c5d1SDavid du Colombier void	rwx(long, char*);
333e12c5d1SDavid du Colombier void	growto(long);
343e12c5d1SDavid du Colombier void	dowidths(Dir*);
353e12c5d1SDavid du Colombier void	format(Dir*, char*);
363e12c5d1SDavid du Colombier void	output(void);
373e12c5d1SDavid du Colombier ulong	clk;
383e12c5d1SDavid du Colombier int	swidth;
393e12c5d1SDavid du Colombier int	qwidth;
403e12c5d1SDavid du Colombier int	vwidth;
413e12c5d1SDavid du Colombier int	uwidth;
423e12c5d1SDavid du Colombier int	glwidth;
433e12c5d1SDavid du Colombier Biobuf	bin;
443e12c5d1SDavid du Colombier 
453e12c5d1SDavid du Colombier #define		HUNK	50
463e12c5d1SDavid du Colombier 
473e12c5d1SDavid du Colombier void
483e12c5d1SDavid du Colombier main(int argc, char *argv[])
493e12c5d1SDavid du Colombier {
503e12c5d1SDavid du Colombier 	int i, fd;
513e12c5d1SDavid du Colombier 	char buf[64];
523e12c5d1SDavid du Colombier 
533e12c5d1SDavid du Colombier 	Binit(&bin, 1, OWRITE);
543e12c5d1SDavid du Colombier 	ARGBEGIN{
55*219b2ee8SDavid du Colombier 	case 'F':	Fflag++; break;
563e12c5d1SDavid du Colombier 	case 'd':	dflag++; break;
573e12c5d1SDavid du Colombier 	case 'l':	lflag++; break;
583e12c5d1SDavid du Colombier 	case 'n':	nflag++; break;
593e12c5d1SDavid du Colombier 	case 'p':	pflag++; break;
603e12c5d1SDavid du Colombier 	case 'q':	qflag++; break;
613e12c5d1SDavid du Colombier 	case 'r':	rflag++; break;
623e12c5d1SDavid du Colombier 	case 's':	sflag++; break;
633e12c5d1SDavid du Colombier 	case 't':	tflag++; break;
643e12c5d1SDavid du Colombier 	case 'u':	uflag++; break;
65*219b2ee8SDavid du Colombier 	default:	fprint(2, "usage: ls [-dlnpqrstuF] [file ...]\n");
663e12c5d1SDavid du Colombier 			exits("usage");
673e12c5d1SDavid du Colombier 	}ARGEND
683e12c5d1SDavid du Colombier 
693e12c5d1SDavid du Colombier 	fmtinstall('M', dirmodeconv);
703e12c5d1SDavid du Colombier 
713e12c5d1SDavid du Colombier 	if(lflag){
723e12c5d1SDavid du Colombier 		fd = open("/dev/time", OREAD);
733e12c5d1SDavid du Colombier 		if(fd<0 || read(fd, buf, sizeof buf-1)<=0)
743e12c5d1SDavid du Colombier 			fprint(2, "ls: can't open /dev/time\n");
753e12c5d1SDavid du Colombier 		close(fd);
763e12c5d1SDavid du Colombier 		clk = strtoul(buf, 0, 0);
773e12c5d1SDavid du Colombier 	}
783e12c5d1SDavid du Colombier 	if(argc == 0)
793e12c5d1SDavid du Colombier 		errs = ls(".", 0);
803e12c5d1SDavid du Colombier 	else for(i=0; i<argc; i++)
813e12c5d1SDavid du Colombier 		errs |= ls(argv[i], 1);
823e12c5d1SDavid du Colombier 	output();
833e12c5d1SDavid du Colombier 	exits(errs? "errors" : 0);
843e12c5d1SDavid du Colombier }
853e12c5d1SDavid du Colombier 
863e12c5d1SDavid du Colombier int
873e12c5d1SDavid du Colombier ls(char *s, int multi)
883e12c5d1SDavid du Colombier {
893e12c5d1SDavid du Colombier 	int fd;
903e12c5d1SDavid du Colombier 	long i, n;
913e12c5d1SDavid du Colombier 	char *p;
923e12c5d1SDavid du Colombier 	Dir db[HUNK];
933e12c5d1SDavid du Colombier 
943e12c5d1SDavid du Colombier 	for(;;) {
953e12c5d1SDavid du Colombier 		p = utfrrune(s, '/');
963e12c5d1SDavid du Colombier 		if(p == 0 || p[1] != 0 || p == s)
973e12c5d1SDavid du Colombier 			break;
983e12c5d1SDavid du Colombier 		*p = 0;
993e12c5d1SDavid du Colombier 	}
1003e12c5d1SDavid du Colombier 	if(dirstat(s, db)==-1){
1013e12c5d1SDavid du Colombier     error:
102*219b2ee8SDavid du Colombier 		fprint(2, "ls: %s: %r\n", s);
1033e12c5d1SDavid du Colombier 		return 1;
1043e12c5d1SDavid du Colombier 	}
1053e12c5d1SDavid du Colombier 	if(db[0].qid.path&CHDIR && dflag==0){
1063e12c5d1SDavid du Colombier 		output();
1073e12c5d1SDavid du Colombier 		fd = open(s, OREAD);
1083e12c5d1SDavid du Colombier 		if(fd == -1)
1093e12c5d1SDavid du Colombier 			goto error;
1103e12c5d1SDavid du Colombier 		while((n=dirread(fd, db, sizeof db)) > 0){
1113e12c5d1SDavid du Colombier 			n /= sizeof(Dir);
1123e12c5d1SDavid du Colombier 			growto(ndir+n);
1133e12c5d1SDavid du Colombier 			for(i=0; i<n; i++){
1143e12c5d1SDavid du Colombier 				memmove(dirbuf+ndir+i, db+i, sizeof(Dir));
1153e12c5d1SDavid du Colombier 				dirbuf[ndir+i].prefix = multi? s : 0;
1163e12c5d1SDavid du Colombier 			}
1173e12c5d1SDavid du Colombier 			ndir += n;
1183e12c5d1SDavid du Colombier 		}
1193e12c5d1SDavid du Colombier 		close(fd);
1203e12c5d1SDavid du Colombier 		output();
1213e12c5d1SDavid du Colombier 	}else{
1223e12c5d1SDavid du Colombier 		growto(ndir+1);
1233e12c5d1SDavid du Colombier 		memmove(dirbuf+ndir, db, sizeof(Dir));
1243e12c5d1SDavid du Colombier 		dirbuf[ndir].prefix = 0;
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
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 	swidth = 0;	/* max width of -s size */
1453e12c5d1SDavid du Colombier 	qwidth = 0;	/* max width of -q version */
1463e12c5d1SDavid du Colombier 	vwidth = 0;	/* max width of dev */
1473e12c5d1SDavid du Colombier 	uwidth = 0;	/* max width of userid */
1483e12c5d1SDavid du Colombier 	glwidth = 0;	/* max width of groupid and length */
1493e12c5d1SDavid du Colombier 	for(i=0; i<ndir; i++)
1503e12c5d1SDavid du Colombier 		dowidths(&dirbuf[i]);
1513e12c5d1SDavid du Colombier 	for(i=0; i<ndir; i++) {
1523e12c5d1SDavid du Colombier 		if(!pflag && (s = dirbuf[i].prefix)) {
1533e12c5d1SDavid du Colombier 			if(strcmp(s, "/") ==0)	/* / is a special case */
1543e12c5d1SDavid du Colombier 				s = "";
1553e12c5d1SDavid du Colombier 			sprint(buf, "%s/%s", s, dirbuf[i].name);
1563e12c5d1SDavid du Colombier 			format(&dirbuf[i], buf);
1573e12c5d1SDavid du Colombier 		} else
1583e12c5d1SDavid du Colombier 			format(&dirbuf[i], dirbuf[i].name);
1593e12c5d1SDavid du Colombier 	}
1603e12c5d1SDavid du Colombier 	ndir = 0;
1613e12c5d1SDavid du Colombier 	Bflush(&bin);
1623e12c5d1SDavid du Colombier }
1633e12c5d1SDavid du Colombier 
1643e12c5d1SDavid du Colombier void
1653e12c5d1SDavid du Colombier dowidths(Dir *db)
1663e12c5d1SDavid du Colombier {
1673e12c5d1SDavid du Colombier 	char buf[100];
1683e12c5d1SDavid du Colombier 	int n;
1693e12c5d1SDavid du Colombier 
1703e12c5d1SDavid du Colombier 	if(sflag) {
1713e12c5d1SDavid du Colombier 		sprint(buf, "%ld", (db->length+1023)/1024);
1723e12c5d1SDavid du Colombier 		n = strlen(buf);
1733e12c5d1SDavid du Colombier 		if(n > swidth)
1743e12c5d1SDavid du Colombier 			swidth = n;
1753e12c5d1SDavid du Colombier 	}
1763e12c5d1SDavid du Colombier 	if(qflag) {
1773e12c5d1SDavid du Colombier 		sprint(buf, "%ld", db->qid.vers);
1783e12c5d1SDavid du Colombier 		n = strlen(buf);
1793e12c5d1SDavid du Colombier 		if(n > qwidth)
1803e12c5d1SDavid du Colombier 			qwidth = n;
1813e12c5d1SDavid du Colombier 	}
1823e12c5d1SDavid du Colombier 	if(lflag) {
1833e12c5d1SDavid du Colombier 		sprint(buf, "%d", db->dev);
1843e12c5d1SDavid du Colombier 		n = strlen(buf);
1853e12c5d1SDavid du Colombier 		if(n > vwidth)
1863e12c5d1SDavid du Colombier 			vwidth = n;
1873e12c5d1SDavid du Colombier 		n = strlen(db->uid);
1883e12c5d1SDavid du Colombier 		if(n > uwidth)
1893e12c5d1SDavid du Colombier 			uwidth = n;
1903e12c5d1SDavid du Colombier 		sprint(buf, "%ld", db->length);
1913e12c5d1SDavid du Colombier 		n = strlen(buf) + strlen(db->gid);
1923e12c5d1SDavid du Colombier 		if(n > glwidth)
1933e12c5d1SDavid du Colombier 			glwidth = n;
1943e12c5d1SDavid du Colombier 	}
1953e12c5d1SDavid du Colombier }
1963e12c5d1SDavid du Colombier 
197*219b2ee8SDavid du Colombier char*
198*219b2ee8SDavid du Colombier fileflag(Dir *db)
199*219b2ee8SDavid du Colombier {
200*219b2ee8SDavid du Colombier 	if(Fflag == 0)
201*219b2ee8SDavid du Colombier 		return "";
202*219b2ee8SDavid du Colombier 	if(CHDIR & db->qid.path)
203*219b2ee8SDavid du Colombier 		return "/";
204*219b2ee8SDavid du Colombier 	if(0111 & db->mode)
205*219b2ee8SDavid du Colombier 		return "*";
206*219b2ee8SDavid du Colombier 	return "";
207*219b2ee8SDavid du Colombier }
208*219b2ee8SDavid du Colombier 
2093e12c5d1SDavid du Colombier void
2103e12c5d1SDavid du Colombier format(Dir *db, char *name)
2113e12c5d1SDavid du Colombier {
2123e12c5d1SDavid du Colombier 	if(sflag)
2133e12c5d1SDavid du Colombier 		Bprint(&bin, "%*ld ",
2143e12c5d1SDavid du Colombier 			swidth, (db->length+1023)/1024);
2153e12c5d1SDavid du Colombier 	if(qflag)
2163e12c5d1SDavid du Colombier 		Bprint(&bin, "%.8lux %*lud ",
2173e12c5d1SDavid du Colombier 			db->qid.path,
2183e12c5d1SDavid du Colombier 			qwidth, db->qid.vers);
2193e12c5d1SDavid du Colombier 	if(lflag)
220*219b2ee8SDavid du Colombier 		Bprint(&bin, "%M %C %*d %*s %s %*ld %s %s\n",
2213e12c5d1SDavid du Colombier 			db->mode, db->type,
2223e12c5d1SDavid du Colombier 			vwidth, db->dev,
2233e12c5d1SDavid du Colombier 			-uwidth, db->uid,
2243e12c5d1SDavid du Colombier 			db->gid,
2253e12c5d1SDavid du Colombier 			glwidth-strlen(db->gid), db->length,
2263e12c5d1SDavid du Colombier 			asciitime(uflag? db->atime : db->mtime), name);
2273e12c5d1SDavid du Colombier 	else
228*219b2ee8SDavid du Colombier 		Bprint(&bin, "%s%s\n", name, fileflag(db));
2293e12c5d1SDavid du Colombier }
2303e12c5d1SDavid du Colombier 
2313e12c5d1SDavid du Colombier void
2323e12c5d1SDavid du Colombier growto(long n)
2333e12c5d1SDavid du Colombier {
2343e12c5d1SDavid du Colombier 	if(n <= ndirbuf)
2353e12c5d1SDavid du Colombier 		return;
2363e12c5d1SDavid du Colombier 	ndirbuf = n;
2373e12c5d1SDavid du Colombier 	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
2383e12c5d1SDavid du Colombier 	if(dirbuf == 0){
2393e12c5d1SDavid du Colombier 		fprint(2, "ls: malloc fail\n");
2403e12c5d1SDavid du Colombier 		exits("malloc fail");
2413e12c5d1SDavid du Colombier 	}
2423e12c5d1SDavid du Colombier }
2433e12c5d1SDavid du Colombier 
2443e12c5d1SDavid du Colombier int
2453e12c5d1SDavid du Colombier compar(NDir *a, NDir *b)
2463e12c5d1SDavid du Colombier {
2473e12c5d1SDavid du Colombier 	long i;
2483e12c5d1SDavid du Colombier 
2493e12c5d1SDavid du Colombier 	if(tflag){
2503e12c5d1SDavid du Colombier 		if(uflag)
2513e12c5d1SDavid du Colombier 			i = b->atime-a->atime;
2523e12c5d1SDavid du Colombier 		else
2533e12c5d1SDavid du Colombier 			i = b->mtime-a->mtime;
2543e12c5d1SDavid du Colombier 	}else{
2553e12c5d1SDavid du Colombier 		if(a->prefix && b->prefix){
2563e12c5d1SDavid du Colombier 			i = strcmp(a->prefix, b->prefix);
2573e12c5d1SDavid du Colombier 			if(i == 0)
2583e12c5d1SDavid du Colombier 				i = strcmp(a->name, b->name);
2593e12c5d1SDavid du Colombier 		}else if(a->prefix){
2603e12c5d1SDavid du Colombier 			i = strcmp(a->prefix, b->name);
2613e12c5d1SDavid du Colombier 			if(i == 0)
2623e12c5d1SDavid du Colombier 				i = 1;	/* a is longer than b */
2633e12c5d1SDavid du Colombier 		}else if(b->prefix){
2643e12c5d1SDavid du Colombier 			i = strcmp(a->name, b->prefix);
2653e12c5d1SDavid du Colombier 			if(i == 0)
2663e12c5d1SDavid du Colombier 				i = -1;	/* b is longer than a */
2673e12c5d1SDavid du Colombier 		}else
2683e12c5d1SDavid du Colombier 			i = strcmp(a->name, b->name);
2693e12c5d1SDavid du Colombier 	}
2703e12c5d1SDavid du Colombier 	if(i == 0)
2713e12c5d1SDavid du Colombier 		i = (a<b? -1 : 1);
2723e12c5d1SDavid du Colombier 	if(rflag)
273*219b2ee8SDavid du Colombier 		i = -i;
2743e12c5d1SDavid du Colombier 	return i;
2753e12c5d1SDavid du Colombier }
2763e12c5d1SDavid du Colombier 
2773e12c5d1SDavid du Colombier char*
2783e12c5d1SDavid du Colombier asciitime(long l)
2793e12c5d1SDavid du Colombier {
2803e12c5d1SDavid du Colombier 	static char buf[32];
2813e12c5d1SDavid du Colombier 	char *t;
2823e12c5d1SDavid du Colombier 
2833e12c5d1SDavid du Colombier 	t = ctime(l);
2843e12c5d1SDavid du Colombier 	/* 6 months in the past or a day in the future */
2853e12c5d1SDavid du Colombier 	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
2863e12c5d1SDavid du Colombier 		memmove(buf, t+4, 7);		/* month and day */
2873e12c5d1SDavid du Colombier 		memmove(buf+7, t+23, 5);		/* year */
2883e12c5d1SDavid du Colombier 	}else
2893e12c5d1SDavid du Colombier 		memmove(buf, t+4, 12);		/* skip day of week */
2903e12c5d1SDavid du Colombier 	buf[12] = 0;
2913e12c5d1SDavid du Colombier 	return buf;
2923e12c5d1SDavid du Colombier }
293