xref: /plan9/sys/src/cmd/ls.c (revision 07c323958a448aa76afd8e66ba1570d6bb6c6c19)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <fcall.h>
5 
6 typedef struct NDir NDir;
7 struct NDir
8 {
9 	Dir *d;
10 	char	*prefix;
11 };
12 
13 int	errs = 0;
14 int	dflag;
15 int	lflag;
16 int	mflag;
17 int	nflag;
18 int	pflag;
19 int	qflag;
20 int	Qflag;
21 int	rflag;
22 int	sflag;
23 int	tflag;
24 int	Tflag;
25 int	uflag;
26 int	Fflag;
27 int	ndirbuf;
28 int	ndir;
29 NDir*	dirbuf;
30 int	ls(char*, int);
31 int	compar(NDir*, NDir*);
32 char*	asciitime(long);
33 char*	darwx(long);
34 void	rwx(long, char*);
35 void	growto(long);
36 void	dowidths(Dir*);
37 void	format(Dir*, char*);
38 void	output(void);
39 char*	xcleanname(char*);
40 ulong	clk;
41 int	swidth;			/* max width of -s size */
42 int	qwidth;			/* max width of -q version */
43 int	vwidth;			/* max width of dev */
44 int	uwidth;			/* max width of userid */
45 int	mwidth;			/* max width of muid */
46 int	lwidth;			/* max width of length */
47 int	gwidth;			/* max width of groupid */
48 Biobuf	bin;
49 
50 void
main(int argc,char * argv[])51 main(int argc, char *argv[])
52 {
53 	int i;
54 
55 	Binit(&bin, 1, OWRITE);
56 	ARGBEGIN{
57 	case 'F':	Fflag++; break;
58 	case 'd':	dflag++; break;
59 	case 'l':	lflag++; break;
60 	case 'm':	mflag++; break;
61 	case 'n':	nflag++; break;
62 	case 'p':	pflag++; break;
63 	case 'q':	qflag++; break;
64 	case 'Q':	Qflag++; break;
65 	case 'r':	rflag++; break;
66 	case 's':	sflag++; break;
67 	case 't':	tflag++; break;
68 	case 'T':	Tflag++; break;
69 	case 'u':	uflag++; break;
70 	default:	fprint(2, "usage: ls [-dlmnpqrstuFQT] [file ...]\n");
71 			exits("usage");
72 	}ARGEND
73 
74 	doquote = needsrcquote;
75 	quotefmtinstall();
76 	fmtinstall('M', dirmodefmt);
77 
78 	if(lflag)
79 		clk = time(0);
80 	if(argc == 0)
81 		errs = ls(".", 0);
82 	else for(i=0; i<argc; i++)
83 		errs |= ls(argv[i], 1);
84 	output();
85 	exits(errs? "errors" : 0);
86 }
87 
88 int
ls(char * s,int multi)89 ls(char *s, int multi)
90 {
91 	int fd;
92 	long i, n;
93 	char *p;
94 	Dir *db;
95 
96 	db = dirstat(s);
97 	if(db == nil){
98     error:
99 		fprint(2, "ls: %s: %r\n", s);
100 		return 1;
101 	}
102 	if((db->qid.type&QTDIR) && dflag==0){
103 		free(db);
104 		output();
105 		fd = open(s, OREAD);
106 		if(fd == -1)
107 			goto error;
108 		n = dirreadall(fd, &db);
109 		if(n < 0)
110 			goto error;
111 		xcleanname(s);
112 		growto(ndir+n);
113 		for(i=0; i<n; i++){
114 			dirbuf[ndir+i].d = db+i;
115 			dirbuf[ndir+i].prefix = multi? s : 0;
116 		}
117 		ndir += n;
118 		close(fd);
119 		output();
120 	}else{
121 		growto(ndir+1);
122 		dirbuf[ndir].d = db;
123 		dirbuf[ndir].prefix = 0;
124 		xcleanname(s);
125 		p = utfrrune(s, '/');
126 		if(p){
127 			dirbuf[ndir].prefix = s;
128 			*p = 0;
129 		}
130 		ndir++;
131 	}
132 	return 0;
133 }
134 
135 void
output(void)136 output(void)
137 {
138 	int i;
139 	char buf[4096];
140 	char *s;
141 
142 	if(!nflag)
143 		qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(void*, void*))compar);
144 	for(i=0; i<ndir; i++)
145 		dowidths(dirbuf[i].d);
146 	for(i=0; i<ndir; i++) {
147 		if(!pflag && (s = dirbuf[i].prefix)) {
148 			if(strcmp(s, "/") ==0)	/* / is a special case */
149 				s = "";
150 			sprint(buf, "%s/%s", s, dirbuf[i].d->name);
151 			format(dirbuf[i].d, buf);
152 		} else
153 			format(dirbuf[i].d, dirbuf[i].d->name);
154 	}
155 	ndir = 0;
156 	Bflush(&bin);
157 }
158 
159 void
dowidths(Dir * db)160 dowidths(Dir *db)
161 {
162 	char buf[256];
163 	int n;
164 
165 	if(sflag) {
166 		n = sprint(buf, "%llud", (db->length+1023)/1024);
167 		if(n > swidth)
168 			swidth = n;
169 	}
170 	if(qflag) {
171 		n = sprint(buf, "%lud", db->qid.vers);
172 		if(n > qwidth)
173 			qwidth = n;
174 	}
175 	if(mflag) {
176 		n = snprint(buf, sizeof buf, "[%q]", db->muid);
177 		if(n > mwidth)
178 			mwidth = n;
179 	}
180 	if(lflag) {
181 		n = sprint(buf, "%ud", db->dev);
182 		if(n > vwidth)
183 			vwidth = n;
184 		n = sprint(buf, "%q", db->uid);
185 		if(n > uwidth)
186 			uwidth = n;
187 		n = sprint(buf, "%q", db->gid);
188 		if(n > gwidth)
189 			gwidth = n;
190 		n = sprint(buf, "%llud", db->length);
191 		if(n > lwidth)
192 			lwidth = n;
193 	}
194 }
195 
196 char*
fileflag(Dir * db)197 fileflag(Dir *db)
198 {
199 	if(Fflag == 0)
200 		return "";
201 	if(QTDIR & db->qid.type)
202 		return "/";
203 	if(0111 & db->mode)
204 		return "*";
205 	return "";
206 }
207 
208 void
format(Dir * db,char * name)209 format(Dir *db, char *name)
210 {
211 	int i;
212 
213 	if(sflag)
214 		Bprint(&bin, "%*llud ",
215 			swidth, (db->length+1023)/1024);
216 	if(mflag){
217 		Bprint(&bin, "[%q] ", db->muid);
218 		for(i=2+strlen(db->muid); i<mwidth; i++)
219 			Bprint(&bin, " ");
220 	}
221 	if(qflag)
222 		Bprint(&bin, "(%.16llux %*lud %.2ux) ",
223 			db->qid.path,
224 			qwidth, db->qid.vers,
225 			db->qid.type);
226 	if(Tflag)
227 		Bprint(&bin, "%c ", (db->mode&DMTMP)? 't': '-');
228 
229 	if(lflag)
230 		Bprint(&bin, "%M %C %*ud %*q %*q %*llud %s ",
231 			db->mode, db->type,
232 			vwidth, db->dev,
233 			-uwidth, db->uid,
234 			-gwidth, db->gid,
235 			lwidth, db->length,
236 			asciitime(uflag? db->atime: db->mtime));
237 	Bprint(&bin, Qflag? "%s%s\n": "%q%s\n", name, fileflag(db));
238 }
239 
240 void
growto(long n)241 growto(long n)
242 {
243 	if(n <= ndirbuf)
244 		return;
245 	ndirbuf = n;
246 	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
247 	if(dirbuf == 0){
248 		fprint(2, "ls: malloc fail\n");
249 		exits("malloc fail");
250 	}
251 }
252 
253 int
compar(NDir * a,NDir * b)254 compar(NDir *a, NDir *b)
255 {
256 	long i;
257 	Dir *ad, *bd;
258 
259 	ad = a->d;
260 	bd = b->d;
261 
262 	if(tflag){
263 		if(uflag)
264 			i = bd->atime-ad->atime;
265 		else
266 			i = bd->mtime-ad->mtime;
267 	}else{
268 		if(a->prefix && b->prefix){
269 			i = strcmp(a->prefix, b->prefix);
270 			if(i == 0)
271 				i = strcmp(ad->name, bd->name);
272 		}else if(a->prefix){
273 			i = strcmp(a->prefix, bd->name);
274 			if(i == 0)
275 				i = 1;	/* a is longer than b */
276 		}else if(b->prefix){
277 			i = strcmp(ad->name, b->prefix);
278 			if(i == 0)
279 				i = -1;	/* b is longer than a */
280 		}else
281 			i = strcmp(ad->name, bd->name);
282 	}
283 	if(i == 0)
284 		i = (a<b? -1 : 1);
285 	if(rflag)
286 		i = -i;
287 	return i;
288 }
289 
290 char*
asciitime(long l)291 asciitime(long l)
292 {
293 	static char buf[32];
294 	char *t;
295 
296 	t = ctime(l);
297 	/* 6 months in the past or a day in the future */
298 	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
299 		memmove(buf, t+4, 7);		/* month and day */
300 		memmove(buf+7, t+23, 5);		/* year */
301 	}else
302 		memmove(buf, t+4, 12);		/* skip day of week */
303 	buf[12] = 0;
304 	return buf;
305 }
306 
307 /*
308  * Compress slashes, remove trailing slash.  Don't worry about . and ..
309  */
310 char*
xcleanname(char * name)311 xcleanname(char *name)
312 {
313 	char *r, *w;
314 
315 	for(r=w=name; *r; r++){
316 		if(*r=='/' && r>name && *(r-1)=='/')
317 			continue;
318 		if(w != r)
319 			*w = *r;
320 		w++;
321 	}
322 	*w = 0;
323 	while(w-1>name && *(w-1)=='/')
324 		*--w = 0;
325 	return name;
326 }
327