ls.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /* ls.c - list files
  2. *
  3. * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
  4. * Copyright 2012 Rob Landley <rob@landley.net>
  5. *
  6. * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html
  7. *
  8. * Deviations from posix:
  9. * add -b (and default to it instead of -q for an unambiguous representation
  10. * that doesn't cause collisions)
  11. * add -Z -ll --color
  12. * Posix says the -l date format should vary based on how recent it is
  13. * and we do --time-style=long-iso instead
  14. USE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstuw#=80<0x1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE))
  15. config LS
  16. bool "ls"
  17. default y
  18. help
  19. usage: ls [-ACFHLRSZacdfhiklmnpqrstuwx1] [--color[=auto]] [FILE...]
  20. List files.
  21. what to show:
  22. -a all files including .hidden -b escape nongraphic chars
  23. -c use ctime for timestamps -d directory, not contents
  24. -i inode number -p put a '/' after dir names
  25. -q unprintable chars as '?' -s storage used (1024 byte units)
  26. -u use access time for timestamps -A list all files but . and ..
  27. -H follow command line symlinks -L follow symlinks
  28. -R recursively list in subdirs -F append /dir *exe @sym |FIFO
  29. -Z security context
  30. output formats:
  31. -1 list one file per line -C columns (sorted vertically)
  32. -g like -l but no owner -h human readable sizes
  33. -l long (show full details) -m comma separated
  34. -n like -l but numeric uid/gid -o like -l but no group
  35. -w set column width -x columns (horizontal sort)
  36. -ll long with nanoseconds (--full-time)
  37. --color device=yellow symlink=turquoise/red dir=blue socket=purple
  38. files: exe=green suid=red suidfile=redback stickydir=greenback
  39. =auto means detect if output is a tty.
  40. sorting (default is alphabetical):
  41. -f unsorted -r reverse -t timestamp -S size
  42. */
  43. #define FOR_ls
  44. #include "toys.h"
  45. // test sst output (suid/sticky in ls flaglist)
  46. // ls -lR starts .: then ./subdir:
  47. GLOBALS(
  48. long w;
  49. long l;
  50. char *color;
  51. struct dirtree *files, *singledir;
  52. unsigned screen_width;
  53. int nl_title;
  54. char *escmore;
  55. )
  56. // Callback from crunch_str to represent unprintable chars
  57. static int crunch_qb(FILE *out, int cols, int wc)
  58. {
  59. int len = 1;
  60. char buf[32];
  61. if (FLAG(q)) *buf = '?';
  62. else {
  63. if (wc<256) *buf = wc;
  64. // scrute the inscrutable, eff the ineffable, print the unprintable
  65. else if ((len = wcrtomb(buf, wc, 0) ) == -1) len = 1;
  66. if (FLAG(b)) {
  67. char *to = buf, *from = buf+24;
  68. int i, j;
  69. memcpy(from, to, 8);
  70. for (i = 0; i<len; i++) {
  71. *to++ = '\\';
  72. if (strchr(TT.escmore, from[i])) *to++ = from[i];
  73. else if (-1 != (j = stridx("\\\a\b\e\f\n\r\t\v", from[i])))
  74. *to++ = "\\abefnrtv"[j];
  75. else to += sprintf(to, "%03o", from[i]);
  76. }
  77. len = to-buf;
  78. }
  79. }
  80. if (cols<len) len = cols;
  81. if (out) fwrite(buf, len, 1, out);
  82. return len;
  83. }
  84. // Returns wcwidth(utf8) version of strlen with -qb escapes
  85. static int strwidth(char *s)
  86. {
  87. return crunch_str(&s, INT_MAX, 0, TT.escmore, crunch_qb);
  88. }
  89. static char endtype(struct stat *st)
  90. {
  91. mode_t mode = st->st_mode;
  92. if ((FLAG(F)||FLAG(p)) && S_ISDIR(mode)) return '/';
  93. if (FLAG(F)) {
  94. if (S_ISLNK(mode)) return '@';
  95. if (S_ISREG(mode) && (mode&0111)) return '*';
  96. if (S_ISFIFO(mode)) return '|';
  97. if (S_ISSOCK(mode)) return '=';
  98. }
  99. return 0;
  100. }
  101. static int numlen(long long ll)
  102. {
  103. return snprintf(0, 0, "%llu", ll);
  104. }
  105. static int print_with_h(char *s, long long value, int units)
  106. {
  107. if (FLAG(h)) return human_readable(s, value*units, 0);
  108. else return sprintf(s, "%lld", value);
  109. }
  110. // Figure out size of printable entry fields for display indent/wrap
  111. static void entrylen(struct dirtree *dt, unsigned *len)
  112. {
  113. struct stat *st = &(dt->st);
  114. char tmp[64];
  115. *len = strwidth(dt->name);
  116. if (endtype(st)) ++*len;
  117. if (FLAG(m)) ++*len;
  118. len[1] = FLAG(i) ? numlen(st->st_ino) : 0;
  119. if (FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)) {
  120. len[2] = numlen(st->st_nlink);
  121. len[3] = FLAG(n) ? numlen(st->st_uid) : strwidth(getusername(st->st_uid));
  122. len[4] = FLAG(n) ? numlen(st->st_gid) : strwidth(getgroupname(st->st_gid));
  123. if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
  124. // cheating slightly here: assuming minor is always 3 digits to avoid
  125. // tracking another column
  126. len[5] = numlen(dev_major(st->st_rdev))+5;
  127. } else len[5] = print_with_h(tmp, st->st_size, 1);
  128. }
  129. len[6] = FLAG(s) ? print_with_h(tmp, st->st_blocks, 1024) : 0;
  130. len[7] = FLAG(Z) ? strwidth((char *)dt->extra) : 0;
  131. }
  132. static int compare(void *a, void *b)
  133. {
  134. struct dirtree *dta = *(struct dirtree **)a;
  135. struct dirtree *dtb = *(struct dirtree **)b;
  136. int ret = 0, reverse = FLAG(r) ? -1 : 1;
  137. if (FLAG(S)) {
  138. if (dta->st.st_size > dtb->st.st_size) ret = -1;
  139. else if (dta->st.st_size < dtb->st.st_size) ret = 1;
  140. }
  141. if (FLAG(t)) {
  142. if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1;
  143. else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1;
  144. else if (dta->st.st_mtim.tv_nsec > dtb->st.st_mtim.tv_nsec) ret = -1;
  145. else if (dta->st.st_mtim.tv_nsec < dtb->st.st_mtim.tv_nsec) ret = 1;
  146. }
  147. if (!ret) ret = strcmp(dta->name, dtb->name);
  148. return ret * reverse;
  149. }
  150. // callback from dirtree_recurse() determining how to handle this entry.
  151. static int filter(struct dirtree *new)
  152. {
  153. // Special case to handle enormous dirs without running out of memory.
  154. if (toys.optflags == (FLAG_1|FLAG_f)) {
  155. xprintf("%s\n", new->name);
  156. return 0;
  157. }
  158. if (FLAG(Z)) {
  159. if (!CFG_TOYBOX_LSM_NONE) {
  160. // Linux doesn't support fgetxattr(2) on O_PATH file descriptors (though
  161. // bionic works around that), and there are no *xattrat(2) calls, so we
  162. // just use lgetxattr(2).
  163. char *path = dirtree_path(new, 0);
  164. (FLAG(L) ? lsm_get_context : lsm_lget_context)(path,(char **)&new->extra);
  165. free(path);
  166. }
  167. if (CFG_TOYBOX_LSM_NONE || !new->extra) new->extra = (long)xstrdup("?");
  168. }
  169. if (FLAG(u)) new->st.st_mtime = new->st.st_atime;
  170. if (FLAG(c)) new->st.st_mtime = new->st.st_ctime;
  171. new->st.st_blocks >>= 1; // Use 1KiB blocks rather than 512B blocks.
  172. if (FLAG(a)||FLAG(f)) return DIRTREE_SAVE;
  173. if (!FLAG(A) && new->name[0]=='.') return 0;
  174. return dirtree_notdotdot(new) & DIRTREE_SAVE;
  175. }
  176. // For column view, calculate horizontal position (for padding) and return
  177. // index of next entry to display.
  178. static unsigned long next_column(unsigned long ul, unsigned long dtlen,
  179. unsigned columns, unsigned *xpos)
  180. {
  181. unsigned height, extra;
  182. // Horizontal sort is easy
  183. if (!FLAG(C)) {
  184. *xpos = ul % columns;
  185. return ul;
  186. }
  187. // vertical sort (-x), uneven rounding goes along right edge
  188. height = (dtlen+columns-1)/columns; // round up
  189. extra = dtlen%height; // how many rows are wider?
  190. if (extra && ul >= extra*columns) ul -= extra*columns--;
  191. else extra = 0;
  192. return (*xpos = ul % columns)*height + extra + ul/columns;
  193. }
  194. static int color_from_mode(mode_t mode)
  195. {
  196. int color = 0;
  197. if (S_ISDIR(mode)) color = 256+34;
  198. else if (S_ISLNK(mode)) color = 256+36;
  199. else if (S_ISBLK(mode) || S_ISCHR(mode)) color = 256+33;
  200. else if (S_ISREG(mode) && (mode&0111)) color = 256+32;
  201. else if (S_ISFIFO(mode)) color = 33;
  202. else if (S_ISSOCK(mode)) color = 256+35;
  203. return color;
  204. }
  205. static void zprint(int zap, char *pat, int len, unsigned long arg)
  206. {
  207. char tmp[32];
  208. sprintf(tmp, "%%*%s", zap ? "s" : pat);
  209. if (zap && pat[strlen(pat)-1]==' ') strcat(tmp, " ");
  210. printf(tmp, len, zap ? (unsigned long)"?" : arg);
  211. }
  212. // Display a list of dirtree entries, according to current format
  213. // Output types -1, -l, -C, or stream
  214. static void listfiles(int dirfd, struct dirtree *indir)
  215. {
  216. struct dirtree *dt, **sort;
  217. unsigned long dtlen, ul = 0;
  218. unsigned width, totals[8], len[8], totpad = 0,
  219. *colsizes = (unsigned *)toybuf, columns = sizeof(toybuf)/4;
  220. char tmp[64];
  221. if (-1 == dirfd) {
  222. perror_msg_raw(indir->name);
  223. return;
  224. }
  225. memset(totals, 0, sizeof(totals));
  226. memset(len, 0, sizeof(len));
  227. // Top level directory was already populated by main()
  228. if (!indir->parent) {
  229. // Silently descend into single directory listed by itself on command line.
  230. // In this case only show dirname/total header when given -R.
  231. dt = indir->child;
  232. if (dt && S_ISDIR(dt->st.st_mode) && !dt->next && !(FLAG(d)||FLAG(R))) {
  233. listfiles(open(dt->name, 0), TT.singledir = dt);
  234. return;
  235. }
  236. // Do preprocessing (Dirtree didn't populate, so callback wasn't called.)
  237. for (;dt; dt = dt->next) filter(dt);
  238. if (toys.optflags == (FLAG_1|FLAG_f)) return;
  239. // Read directory contents. We dup() the fd because this will close it.
  240. // This reads/saves contents to display later, except for in "ls -1f" mode.
  241. } else dirtree_recurse(indir, filter, dup(dirfd),
  242. DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*!!FLAG(L));
  243. // Copy linked list to array and sort it. Directories go in array because
  244. // we visit them in sorted order too. (The nested loops let us measure and
  245. // fill with the same inner loop.)
  246. for (sort = 0;;sort = xmalloc(dtlen*sizeof(void *))) {
  247. for (dtlen = 0, dt = indir->child; dt; dt = dt->next, dtlen++)
  248. if (sort) sort[dtlen] = dt;
  249. if (sort || !dtlen) break;
  250. }
  251. // Label directory if not top of tree, or if -R
  252. if (indir->parent && (TT.singledir!=indir || FLAG(R))) {
  253. char *path = dirtree_path(indir, 0);
  254. if (TT.nl_title++) xputc('\n');
  255. xprintf("%s:\n", path);
  256. free(path);
  257. }
  258. // Measure each entry to work out whitespace padding and total blocks
  259. if (!FLAG(f)) {
  260. unsigned long long blocks = 0;
  261. qsort(sort, dtlen, sizeof(void *), (void *)compare);
  262. for (ul = 0; ul<dtlen; ul++) {
  263. entrylen(sort[ul], len);
  264. for (width = 0; width<8; width++)
  265. if (len[width]>totals[width]) totals[width] = len[width];
  266. blocks += sort[ul]->st.st_blocks;
  267. }
  268. totpad = totals[1]+!!totals[1]+totals[6]+!!totals[6]+totals[7]+!!totals[7];
  269. if ((FLAG(h)||FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)||FLAG(s)) && indir->parent)
  270. {
  271. print_with_h(tmp, blocks, 1024);
  272. xprintf("total %s\n", tmp);
  273. }
  274. }
  275. // Find largest entry in each field for display alignment
  276. if (FLAG(C)||FLAG(x)) {
  277. // columns can't be more than toybuf can hold, or more than files,
  278. // or > 1/2 screen width (one char filename, one space).
  279. if (columns > TT.screen_width/2) columns = TT.screen_width/2;
  280. if (columns > dtlen) columns = dtlen;
  281. // Try to fit as many columns as we can, dropping down by one each time
  282. for (;columns > 1; columns--) {
  283. unsigned c, cc, totlen = columns;
  284. memset(colsizes, 0, columns*sizeof(unsigned));
  285. for (ul = 0; ul<dtlen; ul++) {
  286. cc = next_column(ul, dtlen, columns, &c);
  287. if (cc>=dtlen) break; // tilt: remainder bigger than height
  288. entrylen(sort[cc], len);
  289. if (c<columns-1) *len += totpad+2; // 2 spaces between filenames
  290. // Expand this column if necessary, break if that puts us over budget
  291. if (*len > colsizes[c]) {
  292. totlen += (*len)-colsizes[c];
  293. colsizes[c] = *len;
  294. if (totlen > TT.screen_width) break;
  295. }
  296. }
  297. // If everything fit, stop here
  298. if (ul == dtlen) break;
  299. }
  300. }
  301. // Loop through again to produce output.
  302. width = 0;
  303. for (ul = 0; ul<dtlen; ul++) {
  304. int ii, zap;
  305. unsigned curcol, lastlen = *len, color = 0;
  306. struct stat *st = &((dt = sort[next_column(ul,dtlen,columns,&curcol)])->st);
  307. mode_t mode = st->st_mode;
  308. char et = endtype(st), *ss;
  309. // If we couldn't stat, output ? for most fields
  310. zap = !st->st_blksize && !st->st_dev && !st->st_ino;
  311. // Skip directories at the top of the tree when -d isn't set
  312. if (S_ISDIR(mode) && !indir->parent && !FLAG(d)) continue;
  313. TT.nl_title=1;
  314. // Handle padding and wrapping for display purposes
  315. entrylen(dt, len);
  316. if (ul) {
  317. int mm = !!FLAG(m);
  318. if (mm) xputc(',');
  319. if (FLAG(C)||FLAG(x)) {
  320. if (!curcol) xputc('\n');
  321. else {
  322. if (ul) next_column(ul-1, dtlen, columns, &curcol);
  323. printf("%*c", colsizes[ul ? curcol : 0]-lastlen-totpad, ' ');
  324. }
  325. } else if (FLAG(1) || width+1+*len > TT.screen_width) {
  326. xputc('\n');
  327. width = 0;
  328. } else {
  329. printf(" "+mm, 0); // shut up the stupid compiler
  330. width += 2-mm;
  331. }
  332. }
  333. width += *len;
  334. if (FLAG(i)) zprint(zap, "lu ", totals[1], st->st_ino);
  335. if (FLAG(s)) {
  336. print_with_h(tmp, st->st_blocks, 1024);
  337. zprint(zap, "s ", totals[6], (unsigned long)tmp);
  338. }
  339. if (FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)) {
  340. mode_to_string(mode, tmp);
  341. if (zap) memset(tmp+1, '?', 9);
  342. printf("%s", tmp);
  343. zprint(zap, "ld", totals[2]+1, st->st_nlink);
  344. // print user
  345. if (!FLAG(g)) {
  346. putchar(' ');
  347. ii = -totals[3];
  348. if (zap || FLAG(n)) zprint(zap, "lu", ii, st->st_uid);
  349. else draw_trim_esc(getusername(st->st_uid), ii, abs(ii), TT.escmore,
  350. crunch_qb);
  351. }
  352. // print group
  353. if (!FLAG(o)) {
  354. putchar(' ');
  355. ii = -totals[4];
  356. if (zap || FLAG(n)) zprint(zap, "lu", ii, st->st_gid);
  357. else draw_trim_esc(getgroupname(st->st_gid), ii, abs(ii), TT.escmore,
  358. crunch_qb);
  359. }
  360. }
  361. if (FLAG(Z)) printf(" %-*s "+!FLAG(l), -(int)totals[7], (char *)dt->extra);
  362. if (FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)) {
  363. struct tm *tm;
  364. // print major/minor, or size
  365. if (!zap && (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)))
  366. printf("% *d,% 4d", totals[5]-4, dev_major(st->st_rdev),
  367. dev_minor(st->st_rdev));
  368. else {
  369. print_with_h(tmp, st->st_size, 1);
  370. zprint(zap, "s", totals[5]+1, (unsigned long)tmp);
  371. }
  372. // print time, always in --time-style=long-iso
  373. tm = localtime(&(st->st_mtime));
  374. strftime(tmp, sizeof(tmp), " %F %H:%M", tm);
  375. if (TT.l>1) {
  376. char *s = tmp+strlen(tmp);
  377. s += sprintf(s, ":%02d.%09d ", tm->tm_sec, (int)st->st_mtim.tv_nsec);
  378. strftime(s, sizeof(tmp)-(s-tmp), "%z", tm);
  379. }
  380. zprint(zap, "s ", 17+(TT.l>1)*13, (unsigned long)tmp);
  381. }
  382. if (FLAG(color)) {
  383. color = color_from_mode(st->st_mode);
  384. if (color) printf("\e[%d;%dm", color>>8, color&255);
  385. }
  386. ss = dt->name;
  387. crunch_str(&ss, INT_MAX, stdout, TT.escmore, crunch_qb);
  388. if (color) printf("\e[0m");
  389. if ((FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)) && S_ISLNK(mode)) {
  390. printf(" -> ");
  391. if (!zap && FLAG(color)) {
  392. struct stat st2;
  393. if (fstatat(dirfd, dt->symlink, &st2, 0)) color = 256+31;
  394. else color = color_from_mode(st2.st_mode);
  395. if (color) printf("\e[%d;%dm", color>>8, color&255);
  396. }
  397. zprint(zap, "s", 0, (unsigned long)dt->symlink);
  398. if (!zap && color) printf("\e[0m");
  399. }
  400. if (et) xputc(et);
  401. }
  402. if (width) xputc('\n');
  403. // Free directory entries, recursing first if necessary.
  404. for (ul = 0; ul<dtlen; free(sort[ul++])) {
  405. if (FLAG(d) || !S_ISDIR(sort[ul]->st.st_mode)) continue;
  406. // Recurse into dirs if at top of the tree or given -R
  407. if (!indir->parent || (FLAG(R) && dirtree_notdotdot(sort[ul])))
  408. listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]);
  409. free((void *)sort[ul]->extra);
  410. }
  411. free(sort);
  412. if (dirfd != AT_FDCWD) close(dirfd);
  413. }
  414. void ls_main(void)
  415. {
  416. char **s, *noargs[] = {".", 0};
  417. struct dirtree *dt;
  418. if (FLAG(full_time)) {
  419. toys.optflags |= FLAG_l;
  420. TT.l = 2;
  421. }
  422. // Do we have an implied -1
  423. if (isatty(1)) {
  424. if (!FLAG(show_control_chars)) toys.optflags |= FLAG_b;
  425. if (FLAG(l)||FLAG(o)||FLAG(n)||FLAG(g)) toys.optflags |= FLAG_1;
  426. else if (!(FLAG(1)||FLAG(x)||FLAG(m))) toys.optflags |= FLAG_C;
  427. } else {
  428. if (!FLAG(m)) toys.optflags |= FLAG_1;
  429. if (TT.color) toys.optflags ^= FLAG_color;
  430. }
  431. TT.screen_width = 80;
  432. if (FLAG(w)) TT.screen_width = TT.w+2;
  433. else terminal_size(&TT.screen_width, NULL);
  434. if (TT.screen_width<2) TT.screen_width = 2;
  435. if (FLAG(b)) TT.escmore = " \\";
  436. // The optflags parsing infrastructure should really do this for us,
  437. // but currently it has "switch off when this is set", so "-dR" and "-Rd"
  438. // behave differently
  439. if (FLAG(d)) toys.optflags &= ~FLAG_R;
  440. // Iterate through command line arguments, collecting directories and files.
  441. // Non-absolute paths are relative to current directory. Top of tree is
  442. // a dummy node to collect command line arguments into pseudo-directory.
  443. TT.files = dirtree_add_node(0, 0, 0);
  444. TT.files->dirfd = AT_FDCWD;
  445. for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
  446. int sym = !(FLAG(l)||FLAG(d)||FLAG(F)) || FLAG(L) || FLAG(H);
  447. dt = dirtree_add_node(0, *s, DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*sym);
  448. // note: double_list->prev temporarily goes in dirtree->parent
  449. if (dt) {
  450. if (dt->again&2) {
  451. perror_msg_raw(*s);
  452. free(dt);
  453. } else dlist_add_nomalloc((void *)&TT.files->child, (void *)dt);
  454. } else toys.exitval = 1;
  455. }
  456. // Convert double_list into dirtree.
  457. dlist_terminate(TT.files->child);
  458. for (dt = TT.files->child; dt; dt = dt->next) dt->parent = TT.files;
  459. // Display the files we collected
  460. listfiles(AT_FDCWD, TT.files);
  461. if (CFG_TOYBOX_FREE) free(TT.files);
  462. }