Blob


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