Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
5 #define dirbuf p9dirbuf /* avoid conflict on sun */
7 typedef struct NDir NDir;
8 struct NDir
9 {
10 Dir *d;
11 char *prefix;
12 };
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;
48 void
49 main(int argc, char *argv[])
50 {
51 int i;
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
71 doquote = needsrcquote;
72 quotefmtinstall();
73 fmtinstall('M', dirmodefmt);
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 }
85 int
86 ls(char *s, int multi)
87 {
88 int fd;
89 long i, n;
90 char *p;
91 Dir *db;
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;
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;
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);
134 ndir++;
136 return 0;
139 void
140 output(void)
142 int i;
143 char buf[4096];
144 char *s;
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);
159 ndir = 0;
160 Bflush(&bin);
163 void
164 dowidths(Dir *db)
166 char buf[256];
167 int n;
169 if(sflag) {
170 n = sprint(buf, "%llud", (db->length+1023)/1024);
171 if(n > swidth)
172 swidth = n;
174 if(qflag) {
175 n = sprint(buf, "%lud", db->qid.vers);
176 if(n > qwidth)
177 qwidth = n;
179 if(mflag) {
180 n = snprint(buf, sizeof buf, "[%s]", db->muid);
181 if(n > mwidth)
182 mwidth = n;
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;
198 char*
199 fileflag(Dir *db)
201 if(Fflag == 0)
202 return "";
203 if(QTDIR & db->qid.type)
204 return "/";
205 if(0111 & db->mode)
206 return "*";
207 return "";
210 void
211 format(Dir *db, char *name)
213 int i;
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, " ");
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));
242 void
243 growto(long n)
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");
255 int
256 compar(NDir *a, NDir *b)
258 long i;
259 Dir *ad, *bd;
261 ad = a->d;
262 bd = b->d;
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);
285 if(i == 0)
286 i = (ad<bd? -1 : 1);
287 if(rflag)
288 i = -i;
289 return i;
292 char*
293 asciitime(long l)
295 static char buf[32];
296 char *t;
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;