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