]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ls.c
libaml: fix gc bug, need to amltake()/amldrop() temporary buffer
[plan9front.git] / sys / src / cmd / ls.c
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
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
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
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
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*
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
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
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
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*
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*
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 }