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