Mercurial > hg > toybox
comparison toys/ls.c @ 591:7c4ca3f0536b
Add ls -kqsunort, and fix -F @symlink.
author | Rob Landley <rob@landley.net> |
---|---|
date | Sat, 09 Jun 2012 19:06:49 -0500 |
parents | ca6875170b9a |
children | fb582378a36a |
comparison
equal
deleted
inserted
replaced
590:7becb497c3c4 | 591:7c4ca3f0536b |
---|---|
6 * Copyright 2012 Rob Landley <rob@landley.net> | 6 * Copyright 2012 Rob Landley <rob@landley.net> |
7 * | 7 * |
8 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html | 8 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html |
9 | 9 |
10 // "[-Cl]" | 10 // "[-Cl]" |
11 USE_LS(NEWTOY(ls, "ACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN)) | 11 USE_LS(NEWTOY(ls, "oACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN)) |
12 | 12 |
13 config LS | 13 config LS |
14 bool "ls" | 14 bool "ls" |
15 default y | 15 default y |
16 help | 16 help |
17 usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...] | 17 usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...] |
18 list files | 18 list files |
19 | 19 |
20 what to show: | 20 what to show: |
21 -a list all files | 21 -a all files including .hidden |
22 -d directory, not contents | 22 -d directory, not contents |
23 -i inode number | 23 -i inode number |
24 -k block sizes in kilobytes | |
24 -p put a '/' after directory names | 25 -p put a '/' after directory names |
26 -q unprintable chars as '?' | |
27 -s size (in blocks) | |
28 -u use access time for timestamps | |
25 -A list all files except . and .. | 29 -A list all files except . and .. |
26 -R recursively list files in subdirectories | 30 -R recursively list files in subdirectories |
27 -F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO) | 31 -F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO) |
28 | 32 |
29 output formats: | 33 output formats: |
30 -1 list one file per line | 34 -1 list one file per line |
31 -C columns (sorted vertically) | 35 -C columns (sorted vertically) |
32 -x columns (sorted horizontally) | 36 -x columns (sorted horizontally) |
33 -l long (show full details for each file) | 37 -l long (show full details for each file) |
34 -m comma separated | 38 -m comma separated |
39 -n like -l but numeric uid/gid | |
40 -o like -l but no group | |
35 | 41 |
36 sorting: | 42 sorting: |
37 -f unsorted | 43 -f unsorted |
44 -r reverse | |
45 -t by timestamp | |
38 */ | 46 */ |
39 | 47 |
40 #include "toys.h" | 48 #include "toys.h" |
41 | 49 |
42 #define FLAG_1 (1<<0) | 50 #define FLAG_1 (1<<0) |
43 #define FLAG_x (1<<1) | 51 #define FLAG_x (1<<1) |
44 //#define FLAG_u (1<<2) | 52 #define FLAG_u (1<<2) |
45 //#define FLAG_t (1<<3) | 53 #define FLAG_t (1<<3) |
46 //#define FLAG_s (1<<4) | 54 #define FLAG_s (1<<4) |
47 //#define FLAG_r (1<<5) | 55 #define FLAG_r (1<<5) |
48 //#define FLAG_q (1<<6) | 56 #define FLAG_q (1<<6) |
49 #define FLAG_p (1<<7) | 57 #define FLAG_p (1<<7) |
50 //#define FLAG_n (1<<8) | 58 #define FLAG_n (1<<8) |
51 #define FLAG_m (1<<9) | 59 #define FLAG_m (1<<9) |
52 #define FLAG_l (1<<10) | 60 #define FLAG_l (1<<10) |
53 //#define FLAG_k (1<<11) | 61 #define FLAG_k (1<<11) |
54 #define FLAG_i (1<<12) | 62 #define FLAG_i (1<<12) |
55 #define FLAG_f (1<<13) | 63 #define FLAG_f (1<<13) |
56 #define FLAG_d (1<<14) | 64 #define FLAG_d (1<<14) |
57 //#define FLAG_c (1<<15) | 65 //#define FLAG_c (1<<15) |
58 #define FLAG_a (1<<16) | 66 #define FLAG_a (1<<16) |
61 //#define FLAG_L (1<<19) | 69 //#define FLAG_L (1<<19) |
62 //#define FLAG_H (1<<20) | 70 //#define FLAG_H (1<<20) |
63 #define FLAG_F (1<<21) | 71 #define FLAG_F (1<<21) |
64 #define FLAG_C (1<<22) | 72 #define FLAG_C (1<<22) |
65 #define FLAG_A (1<<23) | 73 #define FLAG_A (1<<23) |
74 #define FLAG_o (1<<24) | |
66 | 75 |
67 // test sst output (suid/sticky in ls flaglist) | 76 // test sst output (suid/sticky in ls flaglist) |
68 | 77 |
69 // ls -lR starts .: then ./subdir: | 78 // ls -lR starts .: then ./subdir: |
70 | 79 |
93 static char endtype(struct stat *st) | 102 static char endtype(struct stat *st) |
94 { | 103 { |
95 mode_t mode = st->st_mode; | 104 mode_t mode = st->st_mode; |
96 if ((toys.optflags&(FLAG_F|FLAG_p)) && S_ISDIR(mode)) return '/'; | 105 if ((toys.optflags&(FLAG_F|FLAG_p)) && S_ISDIR(mode)) return '/'; |
97 if (toys.optflags & FLAG_F) { | 106 if (toys.optflags & FLAG_F) { |
98 if (S_ISLNK(mode) && !(toys.optflags & FLAG_F)) return '@'; | 107 if (S_ISLNK(mode)) return '@'; |
99 if (S_ISREG(mode) && (mode&0111)) return '*'; | 108 if (S_ISREG(mode) && (mode&0111)) return '*'; |
100 if (S_ISFIFO(mode)) return '|'; | 109 if (S_ISFIFO(mode)) return '|'; |
101 if (S_ISSOCK(mode)) return '='; | 110 if (S_ISSOCK(mode)) return '='; |
102 } | 111 } |
103 return 0; | 112 return 0; |
125 *len = strlen(dt->name); | 134 *len = strlen(dt->name); |
126 if (endtype(st)) ++*len; | 135 if (endtype(st)) ++*len; |
127 if (flags & FLAG_m) ++*len; | 136 if (flags & FLAG_m) ++*len; |
128 | 137 |
129 if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino)); | 138 if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino)); |
130 if (flags & FLAG_l) { | 139 if (flags & (FLAG_l|FLAG_o|FLAG_n)) { |
140 unsigned fn = flags & FLAG_n; | |
131 len[2] = numlen(st->st_nlink); | 141 len[2] = numlen(st->st_nlink); |
132 len[3] = strlen(getusername(st->st_uid)); | 142 len[3] = strlen(fn ? utoa(st->st_uid) : getusername(st->st_uid)); |
133 len[4] = strlen(getgroupname(st->st_gid)); | 143 len[4] = strlen(fn ? utoa(st->st_gid) : getgroupname(st->st_gid)); |
134 len[5] = numlen(st->st_size); | 144 len[5] = numlen(st->st_size); |
135 } | 145 } |
146 if (flags & FLAG_s) *len += (len[6] = numlen(st->st_blocks)); | |
136 } | 147 } |
137 | 148 |
138 static int compare(void *a, void *b) | 149 static int compare(void *a, void *b) |
139 { | 150 { |
140 struct dirtree *dta = *(struct dirtree **)a; | 151 struct dirtree *dta = *(struct dirtree **)a; |
141 struct dirtree *dtb = *(struct dirtree **)b; | 152 struct dirtree *dtb = *(struct dirtree **)b; |
142 | 153 int ret = 0, reverse = (toys.optflags & FLAG_r) ? -1 : 1; |
143 // TODO handle flags | 154 |
144 return strcmp(dta->name, dtb->name); | 155 if (toys.optflags & FLAG_t) { |
156 if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1; | |
157 else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1; | |
158 } | |
159 if (!ret) ret = strcmp(dta->name, dtb->name); | |
160 return ret * reverse; | |
145 } | 161 } |
146 | 162 |
147 // callback from dirtree_recurse() determining how to handle this entry. | 163 // callback from dirtree_recurse() determining how to handle this entry. |
148 | 164 |
149 static int filter(struct dirtree *new) | 165 static int filter(struct dirtree *new) |
150 { | 166 { |
151 int flags = toys.optflags; | 167 int flags = toys.optflags; |
152 | 168 |
153 // TODO should -1f print here to handle enormous dirs without runing | 169 // Special case to handle enormous dirs without running out of memory. |
154 // out of mem? | 170 if (flags == (FLAG_1|FLAG_f)) { |
155 | 171 xprintf("%s\n", new->name); |
156 if (flags & FLAG_a) return 0; | 172 return 0; |
157 if (!(flags & FLAG_A) && new->name[0]=='.') return 0; | 173 } |
158 | 174 |
175 if (!(flags&FLAG_f)) { | |
176 if (flags & FLAG_a) return 0; | |
177 if (!(flags & FLAG_A) && new->name[0]=='.') return 0; | |
178 } | |
179 | |
180 if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime; | |
181 if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2; | |
159 return dirtree_notdotdot(new); | 182 return dirtree_notdotdot(new); |
160 } | 183 } |
161 | 184 |
162 // For column view, calculate horizontal position (for padding) and return | 185 // For column view, calculate horizontal position (for padding) and return |
163 // index of next entry to display. | 186 // index of next entry to display. |
164 | 187 |
165 static unsigned long next_column(unsigned long ul, unsigned long dtlen, | 188 static unsigned long next_column(unsigned long ul, unsigned long dtlen, |
166 unsigned columns, unsigned *xpos) | 189 unsigned columns, unsigned *xpos) |
167 { | 190 { |
168 unsigned long transition; | 191 unsigned long transition; |
169 unsigned height, widecols; | 192 unsigned height, widecols; |
170 | 193 |
171 // Horizontal sort is easy | 194 // Horizontal sort is easy |
206 | 229 |
207 static void listfiles(int dirfd, struct dirtree *indir) | 230 static void listfiles(int dirfd, struct dirtree *indir) |
208 { | 231 { |
209 struct dirtree *dt, **sort = 0; | 232 struct dirtree *dt, **sort = 0; |
210 unsigned long dtlen = 0, ul = 0; | 233 unsigned long dtlen = 0, ul = 0; |
211 unsigned width, flags = toys.optflags, totals[6], len[6], | 234 unsigned width, flags = toys.optflags, totals[7], len[7], |
212 *colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4; | 235 *colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4; |
213 | 236 |
214 memset(totals, 0, 6*sizeof(unsigned)); | 237 memset(totals, 0, sizeof(totals)); |
215 | 238 |
216 // Silently descend into single directory listed by itself on command line. | 239 // Silently descend into single directory listed by itself on command line. |
217 // In this case only show dirname/total header when given -R. | 240 // In this case only show dirname/total header when given -R. |
218 if (!indir->parent) { | 241 if (!indir->parent) { |
219 if (!(dt = indir->child)) return; | 242 if (!(dt = indir->child)) return; |
268 } | 291 } |
269 } | 292 } |
270 // If it fit, stop here | 293 // If it fit, stop here |
271 if (ul == dtlen) break; | 294 if (ul == dtlen) break; |
272 } | 295 } |
273 } else if (flags & FLAG_l) for (ul = 0; ul<dtlen; ul++) { | 296 } else if (flags & (FLAG_l|FLAG_o|FLAG_n)) for (ul = 0; ul<dtlen; ul++) { |
274 entrylen(sort[ul], len); | 297 entrylen(sort[ul], len); |
275 for (width=0; width<6; width++) | 298 for (width=0; width<6; width++) |
276 if (len[width] > totals[width]) totals[width] = len[width]; | 299 if (len[width] > totals[width]) totals[width] = len[width]; |
277 } | 300 } |
278 | 301 |
285 xprintf("%s:\n", path); | 308 xprintf("%s:\n", path); |
286 free(path); | 309 free(path); |
287 } | 310 } |
288 | 311 |
289 // This is wrong, should be blocks used not file count. | 312 // This is wrong, should be blocks used not file count. |
290 if (indir->parent && (flags & FLAG_l)) xprintf("total %lu\n", dtlen); | 313 if (indir->parent && (flags & (FLAG_l|FLAG_o|FLAG_n))) |
314 xprintf("total %lu\n", dtlen); | |
291 | 315 |
292 // Loop through again to produce output. | 316 // Loop through again to produce output. |
293 memset(toybuf, ' ', 256); | 317 memset(toybuf, ' ', 256); |
294 width = 0; | 318 width = 0; |
295 for (ul = 0; ul<dtlen; ul++) { | 319 for (ul = 0; ul<dtlen; ul++) { |
319 } | 343 } |
320 width += *len; | 344 width += *len; |
321 | 345 |
322 if (flags & FLAG_i) | 346 if (flags & FLAG_i) |
323 xprintf("% *lu ", len[1], (unsigned long)st->st_ino); | 347 xprintf("% *lu ", len[1], (unsigned long)st->st_ino); |
324 | 348 if (flags & FLAG_s) |
325 if (flags & FLAG_l) { | 349 xprintf("% *lu ", len[6], (unsigned long)st->st_blocks); |
350 | |
351 if (flags & (FLAG_l|FLAG_o|FLAG_n)) { | |
326 struct tm *tm; | 352 struct tm *tm; |
327 char perm[11], thyme[64], c, d; | 353 char perm[11], thyme[64], c, d, *usr, buf[12], *grp, *grpad; |
328 int i, bit; | 354 int i, bit; |
329 | 355 |
330 perm[10]=0; | 356 perm[10]=0; |
331 for (i=0; i<9; i++) { | 357 for (i=0; i<9; i++) { |
332 bit = mode & (1<<i); | 358 bit = mode & (1<<i); |
348 *perm = c; | 374 *perm = c; |
349 | 375 |
350 tm = localtime(&(st->st_mtime)); | 376 tm = localtime(&(st->st_mtime)); |
351 strftime(thyme, sizeof(thyme), "%F %H:%M", tm); | 377 strftime(thyme, sizeof(thyme), "%F %H:%M", tm); |
352 | 378 |
379 if (flags&FLAG_n) { | |
380 usr = buf; | |
381 utoa_to_buf(st->st_uid, buf, 12); | |
382 } else usr = getusername(st->st_uid); | |
383 if (flags&FLAG_o) grp = grpad = toybuf+256; | |
384 else { | |
385 grp = (flags&FLAG_n) ? utoa(st->st_gid) | |
386 : getgroupname(st->st_gid); | |
387 grpad = toybuf+256-(totals[4]-len[4]); | |
388 } | |
353 xprintf("%s% *d %s%s%s%s% *d %s ", perm, totals[2]+1, st->st_nlink, | 389 xprintf("%s% *d %s%s%s%s% *d %s ", perm, totals[2]+1, st->st_nlink, |
354 getusername(st->st_uid), toybuf+255-(totals[3]-len[3]), | 390 usr, toybuf+255-(totals[3]-len[3]), |
355 getgroupname(st->st_gid), toybuf+256-(totals[4]-len[4]), | 391 grp, grpad, totals[5]+1, st->st_size, thyme); |
356 totals[5]+1, st->st_size, thyme); | 392 } |
357 } | 393 |
358 | 394 if (flags & FLAG_q) { |
359 xprintf("%s", sort[next]->name); | 395 char *p; |
360 if ((flags & FLAG_l) && S_ISLNK(mode)) | 396 for (p=sort[next]->name; *p; p++) xputc(isprint(*p) ? *p : '?'); |
397 } else xprintf("%s", sort[next]->name); | |
398 if ((flags & (FLAG_l|FLAG_o|FLAG_n)) && S_ISLNK(mode)) | |
361 xprintf(" -> %s", sort[next]->symlink); | 399 xprintf(" -> %s", sort[next]->symlink); |
362 | 400 |
363 if (et) xputc(et); | 401 if (et) xputc(et); |
364 | 402 |
365 // Pad columns | 403 // Pad columns |
390 { | 428 { |
391 char **s, *noargs[] = {".", 0}; | 429 char **s, *noargs[] = {".", 0}; |
392 struct dirtree *dt; | 430 struct dirtree *dt; |
393 | 431 |
394 // Do we have an implied -1 | 432 // Do we have an implied -1 |
395 if (!isatty(1) || (toys.optflags&FLAG_l)) toys.optflags |= FLAG_1; | 433 if (!isatty(1) || (toys.optflags&(FLAG_l|FLAG_o|FLAG_n))) |
434 toys.optflags |= FLAG_1; | |
396 else { | 435 else { |
397 TT.screen_width = 80; | 436 TT.screen_width = 80; |
398 terminal_size(&TT.screen_width, NULL); | 437 terminal_size(&TT.screen_width, NULL); |
399 } | 438 } |
400 // The optflags parsing infrastructure should really do this for us, | 439 // The optflags parsing infrastructure should really do this for us, |