xref: /plan9-contrib/sys/src/cmd/ls.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <fcall.h>
6 
7 typedef struct NDir NDir;
8 struct NDir
9 {
10 	Dir;
11 	char	*prefix;
12 };
13 
14 int	errs = 0;
15 int	dflag;
16 int	lflag;
17 int	nflag;
18 int	pflag;
19 int	qflag;
20 int	rflag;
21 int	sflag;
22 int	tflag;
23 int	uflag;
24 int	Fflag;
25 int	ndirbuf;
26 int	ndir;
27 NDir*	dirbuf;
28 int	ls(char*, int);
29 int	compar(NDir*, NDir*);
30 char*	asciitime(long);
31 char*	darwx(long);
32 void	rwx(long, char*);
33 void	growto(long);
34 void	dowidths(Dir*);
35 void	format(Dir*, char*);
36 void	output(void);
37 ulong	clk;
38 int	swidth;
39 int	qwidth;
40 int	vwidth;
41 int	uwidth;
42 int	glwidth;
43 Biobuf	bin;
44 
45 #define		HUNK	50
46 
47 void
48 main(int argc, char *argv[])
49 {
50 	int i, fd;
51 	char buf[64];
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 'n':	nflag++; break;
59 	case 'p':	pflag++; break;
60 	case 'q':	qflag++; break;
61 	case 'r':	rflag++; break;
62 	case 's':	sflag++; break;
63 	case 't':	tflag++; break;
64 	case 'u':	uflag++; break;
65 	default:	fprint(2, "usage: ls [-dlnpqrstuF] [file ...]\n");
66 			exits("usage");
67 	}ARGEND
68 
69 	fmtinstall('M', dirmodeconv);
70 
71 	if(lflag){
72 		fd = open("/dev/time", OREAD);
73 		if(fd<0 || read(fd, buf, sizeof buf-1)<=0)
74 			fprint(2, "ls: can't open /dev/time\n");
75 		close(fd);
76 		clk = strtoul(buf, 0, 0);
77 	}
78 	if(argc == 0)
79 		errs = ls(".", 0);
80 	else for(i=0; i<argc; i++)
81 		errs |= ls(argv[i], 1);
82 	output();
83 	exits(errs? "errors" : 0);
84 }
85 
86 int
87 ls(char *s, int multi)
88 {
89 	int fd;
90 	long i, n;
91 	char *p;
92 	Dir db[HUNK];
93 
94 	for(;;) {
95 		p = utfrrune(s, '/');
96 		if(p == 0 || p[1] != 0 || p == s)
97 			break;
98 		*p = 0;
99 	}
100 	if(dirstat(s, db)==-1){
101     error:
102 		fprint(2, "ls: %s: %r\n", s);
103 		return 1;
104 	}
105 	if(db[0].qid.path&CHDIR && dflag==0){
106 		output();
107 		fd = open(s, OREAD);
108 		if(fd == -1)
109 			goto error;
110 		while((n=dirread(fd, db, sizeof db)) > 0){
111 			n /= sizeof(Dir);
112 			growto(ndir+n);
113 			for(i=0; i<n; i++){
114 				memmove(dirbuf+ndir+i, db+i, sizeof(Dir));
115 				dirbuf[ndir+i].prefix = multi? s : 0;
116 			}
117 			ndir += n;
118 		}
119 		close(fd);
120 		output();
121 	}else{
122 		growto(ndir+1);
123 		memmove(dirbuf+ndir, db, sizeof(Dir));
124 		dirbuf[ndir].prefix = 0;
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
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 	swidth = 0;	/* max width of -s size */
145 	qwidth = 0;	/* max width of -q version */
146 	vwidth = 0;	/* max width of dev */
147 	uwidth = 0;	/* max width of userid */
148 	glwidth = 0;	/* max width of groupid and length */
149 	for(i=0; i<ndir; i++)
150 		dowidths(&dirbuf[i]);
151 	for(i=0; i<ndir; i++) {
152 		if(!pflag && (s = dirbuf[i].prefix)) {
153 			if(strcmp(s, "/") ==0)	/* / is a special case */
154 				s = "";
155 			sprint(buf, "%s/%s", s, dirbuf[i].name);
156 			format(&dirbuf[i], buf);
157 		} else
158 			format(&dirbuf[i], dirbuf[i].name);
159 	}
160 	ndir = 0;
161 	Bflush(&bin);
162 }
163 
164 void
165 dowidths(Dir *db)
166 {
167 	char buf[100];
168 	int n;
169 
170 	if(sflag) {
171 		sprint(buf, "%ld", (db->length+1023)/1024);
172 		n = strlen(buf);
173 		if(n > swidth)
174 			swidth = n;
175 	}
176 	if(qflag) {
177 		sprint(buf, "%ld", db->qid.vers);
178 		n = strlen(buf);
179 		if(n > qwidth)
180 			qwidth = n;
181 	}
182 	if(lflag) {
183 		sprint(buf, "%d", db->dev);
184 		n = strlen(buf);
185 		if(n > vwidth)
186 			vwidth = n;
187 		n = strlen(db->uid);
188 		if(n > uwidth)
189 			uwidth = n;
190 		sprint(buf, "%ld", db->length);
191 		n = strlen(buf) + strlen(db->gid);
192 		if(n > glwidth)
193 			glwidth = n;
194 	}
195 }
196 
197 char*
198 fileflag(Dir *db)
199 {
200 	if(Fflag == 0)
201 		return "";
202 	if(CHDIR & db->qid.path)
203 		return "/";
204 	if(0111 & db->mode)
205 		return "*";
206 	return "";
207 }
208 
209 void
210 format(Dir *db, char *name)
211 {
212 	if(sflag)
213 		Bprint(&bin, "%*ld ",
214 			swidth, (db->length+1023)/1024);
215 	if(qflag)
216 		Bprint(&bin, "%.8lux %*lud ",
217 			db->qid.path,
218 			qwidth, db->qid.vers);
219 	if(lflag)
220 		Bprint(&bin, "%M %C %*d %*s %s %*ld %s %s\n",
221 			db->mode, db->type,
222 			vwidth, db->dev,
223 			-uwidth, db->uid,
224 			db->gid,
225 			glwidth-strlen(db->gid), db->length,
226 			asciitime(uflag? db->atime : db->mtime), name);
227 	else
228 		Bprint(&bin, "%s%s\n", name, fileflag(db));
229 }
230 
231 void
232 growto(long n)
233 {
234 	if(n <= ndirbuf)
235 		return;
236 	ndirbuf = n;
237 	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
238 	if(dirbuf == 0){
239 		fprint(2, "ls: malloc fail\n");
240 		exits("malloc fail");
241 	}
242 }
243 
244 int
245 compar(NDir *a, NDir *b)
246 {
247 	long i;
248 
249 	if(tflag){
250 		if(uflag)
251 			i = b->atime-a->atime;
252 		else
253 			i = b->mtime-a->mtime;
254 	}else{
255 		if(a->prefix && b->prefix){
256 			i = strcmp(a->prefix, b->prefix);
257 			if(i == 0)
258 				i = strcmp(a->name, b->name);
259 		}else if(a->prefix){
260 			i = strcmp(a->prefix, b->name);
261 			if(i == 0)
262 				i = 1;	/* a is longer than b */
263 		}else if(b->prefix){
264 			i = strcmp(a->name, b->prefix);
265 			if(i == 0)
266 				i = -1;	/* b is longer than a */
267 		}else
268 			i = strcmp(a->name, b->name);
269 	}
270 	if(i == 0)
271 		i = (a<b? -1 : 1);
272 	if(rflag)
273 		i = -i;
274 	return i;
275 }
276 
277 char*
278 asciitime(long l)
279 {
280 	static char buf[32];
281 	char *t;
282 
283 	t = ctime(l);
284 	/* 6 months in the past or a day in the future */
285 	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
286 		memmove(buf, t+4, 7);		/* month and day */
287 		memmove(buf+7, t+23, 5);		/* year */
288 	}else
289 		memmove(buf, t+4, 12);		/* skip day of week */
290 	buf[12] = 0;
291 	return buf;
292 }
293