10f66f451Sopenharmony_ci/* ls.c - list files 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Andre Renaud <andre@bluewatersys.com> 40f66f451Sopenharmony_ci * Copyright 2012 Rob Landley <rob@landley.net> 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * See http://opengroup.org/onlinepubs/9699919799/utilities/ls.html 70f66f451Sopenharmony_ci * 80f66f451Sopenharmony_ci * Deviations from posix: 90f66f451Sopenharmony_ci * add -b (and default to it instead of -q for an unambiguous representation 100f66f451Sopenharmony_ci * that doesn't cause collisions) 110f66f451Sopenharmony_ci * add -Z -ll --color 120f66f451Sopenharmony_ci * Posix says the -l date format should vary based on how recent it is 130f66f451Sopenharmony_ci * and we do --time-style=long-iso instead 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ciUSE_LS(NEWTOY(ls, "(color):;(full-time)(show-control-chars)ZgoACFHLRSabcdfhikl@mnpqrstux1[-Cxm1][-Cxml][-Cxmo][-Cxmg][-cu][-ftS][-HL][!qb]", TOYFLAG_BIN|TOYFLAG_LOCALE)) 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ciconfig LS 180f66f451Sopenharmony_ci bool "ls" 190f66f451Sopenharmony_ci default y 200f66f451Sopenharmony_ci help 210f66f451Sopenharmony_ci usage: ls [-ACFHLSZacdfhiklmnpqrstux1] [--color[=auto]] [directory...] 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci List files. 240f66f451Sopenharmony_ci 250f66f451Sopenharmony_ci what to show: 260f66f451Sopenharmony_ci -a all files including .hidden -b escape nongraphic chars 270f66f451Sopenharmony_ci -c use ctime for timestamps -d directory, not contents 280f66f451Sopenharmony_ci -i inode number -p put a '/' after dir names 290f66f451Sopenharmony_ci -q unprintable chars as '?' -s storage used (1024 byte units) 300f66f451Sopenharmony_ci -u use access time for timestamps -A list all files but . and .. 310f66f451Sopenharmony_ci -H follow command line symlinks -L follow symlinks 320f66f451Sopenharmony_ci -Z security context 330f66f451Sopenharmony_ci 340f66f451Sopenharmony_ci output formats: 350f66f451Sopenharmony_ci -1 list one file per line -C columns (sorted vertically) 360f66f451Sopenharmony_ci -g like -l but no owner -h human readable sizes 370f66f451Sopenharmony_ci -l long (show full details) -m comma separated 380f66f451Sopenharmony_ci -n like -l but numeric uid/gid -o like -l but no group 390f66f451Sopenharmony_ci -x columns (horizontal sort) -ll long with nanoseconds (--full-time) 400f66f451Sopenharmony_ci --color device=yellow symlink=turquoise/red dir=blue socket=purple 410f66f451Sopenharmony_ci files: exe=green suid=red suidfile=redback stickydir=greenback 420f66f451Sopenharmony_ci =auto means detect if output is a tty. 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_ci sorting (default is alphabetical): 450f66f451Sopenharmony_ci -f unsorted -r reverse -t timestamp -S size 460f66f451Sopenharmony_ci*/ 470f66f451Sopenharmony_ci 480f66f451Sopenharmony_ci#define FOR_ls 490f66f451Sopenharmony_ci#include "toys.h" 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci// test sst output (suid/sticky in ls flaglist) 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci// ls -lR starts .: then ./subdir: 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ciGLOBALS( 560f66f451Sopenharmony_ci long l; 570f66f451Sopenharmony_ci char *color; 580f66f451Sopenharmony_ci 590f66f451Sopenharmony_ci struct dirtree *files, *singledir; 600f66f451Sopenharmony_ci unsigned screen_width; 610f66f451Sopenharmony_ci int nl_title; 620f66f451Sopenharmony_ci char *escmore; 630f66f451Sopenharmony_ci) 640f66f451Sopenharmony_ci 650f66f451Sopenharmony_ci// Callback from crunch_str to represent unprintable chars 660f66f451Sopenharmony_cistatic int crunch_qb(FILE *out, int cols, int wc) 670f66f451Sopenharmony_ci{ 680f66f451Sopenharmony_ci int len = 1; 690f66f451Sopenharmony_ci char buf[32]; 700f66f451Sopenharmony_ci 710f66f451Sopenharmony_ci if (FLAG(q)) *buf = '?'; 720f66f451Sopenharmony_ci else { 730f66f451Sopenharmony_ci if (wc<256) *buf = wc; 740f66f451Sopenharmony_ci // scrute the inscrutable, eff the ineffable, print the unprintable 750f66f451Sopenharmony_ci else if ((len = wcrtomb(buf, wc, 0) ) == -1) len = 1; 760f66f451Sopenharmony_ci if (FLAG(b)) { 770f66f451Sopenharmony_ci char *to = buf, *from = buf+24; 780f66f451Sopenharmony_ci int i, j; 790f66f451Sopenharmony_ci 800f66f451Sopenharmony_ci memcpy(from, to, 8); 810f66f451Sopenharmony_ci for (i = 0; i<len; i++) { 820f66f451Sopenharmony_ci *to++ = '\\'; 830f66f451Sopenharmony_ci if (strchr(TT.escmore, from[i])) *to++ = from[i]; 840f66f451Sopenharmony_ci else if (-1 != (j = stridx("\\\a\b\033\f\n\r\t\v", from[i]))) 850f66f451Sopenharmony_ci *to++ = "\\abefnrtv"[j]; 860f66f451Sopenharmony_ci else to += sprintf(to, "%03o", from[i]); 870f66f451Sopenharmony_ci } 880f66f451Sopenharmony_ci len = to-buf; 890f66f451Sopenharmony_ci } 900f66f451Sopenharmony_ci } 910f66f451Sopenharmony_ci 920f66f451Sopenharmony_ci if (cols<len) len = cols; 930f66f451Sopenharmony_ci if (out) fwrite(buf, len, 1, out); 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci return len; 960f66f451Sopenharmony_ci} 970f66f451Sopenharmony_ci 980f66f451Sopenharmony_ci// Returns wcwidth(utf8) version of strlen with -qb escapes 990f66f451Sopenharmony_cistatic int strwidth(char *s) 1000f66f451Sopenharmony_ci{ 1010f66f451Sopenharmony_ci return crunch_str(&s, INT_MAX, 0, TT.escmore, crunch_qb); 1020f66f451Sopenharmony_ci} 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_cistatic char endtype(struct stat *st) 1050f66f451Sopenharmony_ci{ 1060f66f451Sopenharmony_ci mode_t mode = st->st_mode; 1070f66f451Sopenharmony_ci if ((FLAG(F)||FLAG(p)) && S_ISDIR(mode)) return '/'; 1080f66f451Sopenharmony_ci if (FLAG(F)) { 1090f66f451Sopenharmony_ci if (S_ISLNK(mode)) return '@'; 1100f66f451Sopenharmony_ci if (S_ISREG(mode) && (mode&0111)) return '*'; 1110f66f451Sopenharmony_ci if (S_ISFIFO(mode)) return '|'; 1120f66f451Sopenharmony_ci if (S_ISSOCK(mode)) return '='; 1130f66f451Sopenharmony_ci } 1140f66f451Sopenharmony_ci return 0; 1150f66f451Sopenharmony_ci} 1160f66f451Sopenharmony_ci 1170f66f451Sopenharmony_cistatic int numlen(long long ll) 1180f66f451Sopenharmony_ci{ 1190f66f451Sopenharmony_ci return snprintf(0, 0, "%llu", ll); 1200f66f451Sopenharmony_ci} 1210f66f451Sopenharmony_ci 1220f66f451Sopenharmony_cistatic int print_with_h(char *s, long long value, int units) 1230f66f451Sopenharmony_ci{ 1240f66f451Sopenharmony_ci if (FLAG(h)) return human_readable(s, value*units, 0); 1250f66f451Sopenharmony_ci else return sprintf(s, "%lld", value); 1260f66f451Sopenharmony_ci} 1270f66f451Sopenharmony_ci 1280f66f451Sopenharmony_ci// Figure out size of printable entry fields for display indent/wrap 1290f66f451Sopenharmony_ci 1300f66f451Sopenharmony_cistatic void entrylen(struct dirtree *dt, unsigned *len) 1310f66f451Sopenharmony_ci{ 1320f66f451Sopenharmony_ci struct stat *st = &(dt->st); 1330f66f451Sopenharmony_ci unsigned flags = toys.optflags; 1340f66f451Sopenharmony_ci char tmp[64]; 1350f66f451Sopenharmony_ci 1360f66f451Sopenharmony_ci *len = strwidth(dt->name); 1370f66f451Sopenharmony_ci if (endtype(st)) ++*len; 1380f66f451Sopenharmony_ci if (flags & FLAG_m) ++*len; 1390f66f451Sopenharmony_ci 1400f66f451Sopenharmony_ci len[1] = (flags & FLAG_i) ? numlen(st->st_ino) : 0; 1410f66f451Sopenharmony_ci if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { 1420f66f451Sopenharmony_ci unsigned fn = flags & FLAG_n; 1430f66f451Sopenharmony_ci 1440f66f451Sopenharmony_ci len[2] = numlen(st->st_nlink); 1450f66f451Sopenharmony_ci len[3] = fn ? numlen(st->st_uid) : strwidth(getusername(st->st_uid)); 1460f66f451Sopenharmony_ci len[4] = fn ? numlen(st->st_gid) : strwidth(getgroupname(st->st_gid)); 1470f66f451Sopenharmony_ci if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { 1480f66f451Sopenharmony_ci // cheating slightly here: assuming minor is always 3 digits to avoid 1490f66f451Sopenharmony_ci // tracking another column 1500f66f451Sopenharmony_ci len[5] = numlen(dev_major(st->st_rdev))+5; 1510f66f451Sopenharmony_ci } else len[5] = print_with_h(tmp, st->st_size, 1); 1520f66f451Sopenharmony_ci } 1530f66f451Sopenharmony_ci 1540f66f451Sopenharmony_ci len[6] = (flags & FLAG_s) ? print_with_h(tmp, st->st_blocks, 512) : 0; 1550f66f451Sopenharmony_ci len[7] = (flags & FLAG_Z) ? strwidth((char *)dt->extra) : 0; 1560f66f451Sopenharmony_ci} 1570f66f451Sopenharmony_ci 1580f66f451Sopenharmony_cistatic int compare(void *a, void *b) 1590f66f451Sopenharmony_ci{ 1600f66f451Sopenharmony_ci struct dirtree *dta = *(struct dirtree **)a; 1610f66f451Sopenharmony_ci struct dirtree *dtb = *(struct dirtree **)b; 1620f66f451Sopenharmony_ci int ret = 0, reverse = FLAG(r) ? -1 : 1; 1630f66f451Sopenharmony_ci 1640f66f451Sopenharmony_ci if (FLAG(S)) { 1650f66f451Sopenharmony_ci if (dta->st.st_size > dtb->st.st_size) ret = -1; 1660f66f451Sopenharmony_ci else if (dta->st.st_size < dtb->st.st_size) ret = 1; 1670f66f451Sopenharmony_ci } 1680f66f451Sopenharmony_ci if (FLAG(t)) { 1690f66f451Sopenharmony_ci if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1; 1700f66f451Sopenharmony_ci else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1; 1710f66f451Sopenharmony_ci else if (dta->st.st_mtim.tv_nsec > dtb->st.st_mtim.tv_nsec) ret = -1; 1720f66f451Sopenharmony_ci else if (dta->st.st_mtim.tv_nsec < dtb->st.st_mtim.tv_nsec) ret = 1; 1730f66f451Sopenharmony_ci } 1740f66f451Sopenharmony_ci if (!ret) ret = strcmp(dta->name, dtb->name); 1750f66f451Sopenharmony_ci return ret * reverse; 1760f66f451Sopenharmony_ci} 1770f66f451Sopenharmony_ci 1780f66f451Sopenharmony_ci// callback from dirtree_recurse() determining how to handle this entry. 1790f66f451Sopenharmony_ci 1800f66f451Sopenharmony_cistatic int filter(struct dirtree *new) 1810f66f451Sopenharmony_ci{ 1820f66f451Sopenharmony_ci int flags = toys.optflags; 1830f66f451Sopenharmony_ci 1840f66f451Sopenharmony_ci // Special case to handle enormous dirs without running out of memory. 1850f66f451Sopenharmony_ci if (flags == (FLAG_1|FLAG_f)) { 1860f66f451Sopenharmony_ci xprintf("%s\n", new->name); 1870f66f451Sopenharmony_ci return 0; 1880f66f451Sopenharmony_ci } 1890f66f451Sopenharmony_ci 1900f66f451Sopenharmony_ci if (flags & FLAG_Z) { 1910f66f451Sopenharmony_ci if (!CFG_TOYBOX_LSM_NONE) { 1920f66f451Sopenharmony_ci 1930f66f451Sopenharmony_ci // (Wouldn't it be nice if the lsm functions worked like openat(), 1940f66f451Sopenharmony_ci // fchmodat(), mknodat(), readlinkat() so we could do this without 1950f66f451Sopenharmony_ci // even O_PATH? But no, this is 1990's tech.) 1960f66f451Sopenharmony_ci int fd = openat(dirtree_parentfd(new), new->name, 1970f66f451Sopenharmony_ci O_PATH|(O_NOFOLLOW*!FLAG(L))); 1980f66f451Sopenharmony_ci 1990f66f451Sopenharmony_ci if (fd != -1) { 2000f66f451Sopenharmony_ci if (-1 == lsm_fget_context(fd, (char **)&new->extra) && errno == EBADF) 2010f66f451Sopenharmony_ci { 2020f66f451Sopenharmony_ci char hack[32]; 2030f66f451Sopenharmony_ci 2040f66f451Sopenharmony_ci // Work around kernel bug that won't let us read this "metadata" from 2050f66f451Sopenharmony_ci // the filehandle unless we have permission to read the data. (We can 2060f66f451Sopenharmony_ci // query the same data in by path, but can't do it through an O_PATH 2070f66f451Sopenharmony_ci // filehandle, because reasons. But for some reason, THIS is ok? If 2080f66f451Sopenharmony_ci // they ever fix the kernel, this should stop triggering.) 2090f66f451Sopenharmony_ci 2100f66f451Sopenharmony_ci sprintf(hack, "/proc/self/fd/%d", fd); 2110f66f451Sopenharmony_ci lsm_lget_context(hack, (char **)&new->extra); 2120f66f451Sopenharmony_ci } 2130f66f451Sopenharmony_ci close(fd); 2140f66f451Sopenharmony_ci } 2150f66f451Sopenharmony_ci } 2160f66f451Sopenharmony_ci if (CFG_TOYBOX_LSM_NONE || !new->extra) new->extra = (long)xstrdup("?"); 2170f66f451Sopenharmony_ci } 2180f66f451Sopenharmony_ci 2190f66f451Sopenharmony_ci if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime; 2200f66f451Sopenharmony_ci if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime; 2210f66f451Sopenharmony_ci new->st.st_blocks >>= 1; 2220f66f451Sopenharmony_ci 2230f66f451Sopenharmony_ci if (flags & (FLAG_a|FLAG_f)) return DIRTREE_SAVE; 2240f66f451Sopenharmony_ci if (!(flags & FLAG_A) && new->name[0]=='.') return 0; 2250f66f451Sopenharmony_ci 2260f66f451Sopenharmony_ci return dirtree_notdotdot(new) & DIRTREE_SAVE; 2270f66f451Sopenharmony_ci} 2280f66f451Sopenharmony_ci 2290f66f451Sopenharmony_ci// For column view, calculate horizontal position (for padding) and return 2300f66f451Sopenharmony_ci// index of next entry to display. 2310f66f451Sopenharmony_ci 2320f66f451Sopenharmony_cistatic unsigned long next_column(unsigned long ul, unsigned long dtlen, 2330f66f451Sopenharmony_ci unsigned columns, unsigned *xpos) 2340f66f451Sopenharmony_ci{ 2350f66f451Sopenharmony_ci unsigned long transition; 2360f66f451Sopenharmony_ci unsigned height, widecols; 2370f66f451Sopenharmony_ci 2380f66f451Sopenharmony_ci // Horizontal sort is easy 2390f66f451Sopenharmony_ci if (!FLAG(C)) { 2400f66f451Sopenharmony_ci *xpos = ul % columns; 2410f66f451Sopenharmony_ci return ul; 2420f66f451Sopenharmony_ci } 2430f66f451Sopenharmony_ci 2440f66f451Sopenharmony_ci // vertical sort 2450f66f451Sopenharmony_ci 2460f66f451Sopenharmony_ci // For -x, calculate height of display, rounded up 2470f66f451Sopenharmony_ci height = (dtlen+columns-1)/columns; 2480f66f451Sopenharmony_ci 2490f66f451Sopenharmony_ci // Sanity check: does wrapping render this column count impossible 2500f66f451Sopenharmony_ci // due to the right edge wrapping eating a whole row? 2510f66f451Sopenharmony_ci if (height*columns - dtlen >= height) { 2520f66f451Sopenharmony_ci *xpos = columns; 2530f66f451Sopenharmony_ci return 0; 2540f66f451Sopenharmony_ci } 2550f66f451Sopenharmony_ci 2560f66f451Sopenharmony_ci // Uneven rounding goes along right edge 2570f66f451Sopenharmony_ci widecols = dtlen % height; 2580f66f451Sopenharmony_ci if (!widecols) widecols = height; 2590f66f451Sopenharmony_ci transition = widecols * columns; 2600f66f451Sopenharmony_ci if (ul < transition) { 2610f66f451Sopenharmony_ci *xpos = ul % columns; 2620f66f451Sopenharmony_ci return (*xpos*height) + (ul/columns); 2630f66f451Sopenharmony_ci } 2640f66f451Sopenharmony_ci 2650f66f451Sopenharmony_ci ul -= transition; 2660f66f451Sopenharmony_ci *xpos = ul % (columns-1); 2670f66f451Sopenharmony_ci 2680f66f451Sopenharmony_ci return (*xpos*height) + widecols + (ul/(columns-1)); 2690f66f451Sopenharmony_ci} 2700f66f451Sopenharmony_ci 2710f66f451Sopenharmony_cistatic int color_from_mode(mode_t mode) 2720f66f451Sopenharmony_ci{ 2730f66f451Sopenharmony_ci int color = 0; 2740f66f451Sopenharmony_ci 2750f66f451Sopenharmony_ci if (S_ISDIR(mode)) color = 256+34; 2760f66f451Sopenharmony_ci else if (S_ISLNK(mode)) color = 256+36; 2770f66f451Sopenharmony_ci else if (S_ISBLK(mode) || S_ISCHR(mode)) color = 256+33; 2780f66f451Sopenharmony_ci else if (S_ISREG(mode) && (mode&0111)) color = 256+32; 2790f66f451Sopenharmony_ci else if (S_ISFIFO(mode)) color = 33; 2800f66f451Sopenharmony_ci else if (S_ISSOCK(mode)) color = 256+35; 2810f66f451Sopenharmony_ci 2820f66f451Sopenharmony_ci return color; 2830f66f451Sopenharmony_ci} 2840f66f451Sopenharmony_ci 2850f66f451Sopenharmony_cistatic void zprint(int zap, char *pat, int len, unsigned long arg) 2860f66f451Sopenharmony_ci{ 2870f66f451Sopenharmony_ci char tmp[32]; 2880f66f451Sopenharmony_ci 2890f66f451Sopenharmony_ci sprintf(tmp, "%%*%s", zap ? "s" : pat); 2900f66f451Sopenharmony_ci if (zap && pat[strlen(pat)-1]==' ') strcat(tmp, " "); 2910f66f451Sopenharmony_ci printf(tmp, len, zap ? (unsigned long)"?" : arg); 2920f66f451Sopenharmony_ci} 2930f66f451Sopenharmony_ci 2940f66f451Sopenharmony_ci// Display a list of dirtree entries, according to current format 2950f66f451Sopenharmony_ci// Output types -1, -l, -C, or stream 2960f66f451Sopenharmony_ci 2970f66f451Sopenharmony_cistatic void listfiles(int dirfd, struct dirtree *indir) 2980f66f451Sopenharmony_ci{ 2990f66f451Sopenharmony_ci struct dirtree *dt, **sort; 3000f66f451Sopenharmony_ci unsigned long dtlen, ul = 0; 3010f66f451Sopenharmony_ci unsigned width, flags = toys.optflags, totals[8], len[8], totpad = 0, 3020f66f451Sopenharmony_ci *colsizes = (unsigned *)toybuf, columns = sizeof(toybuf)/4; 3030f66f451Sopenharmony_ci char tmp[64]; 3040f66f451Sopenharmony_ci 3050f66f451Sopenharmony_ci if (-1 == dirfd) { 3060f66f451Sopenharmony_ci perror_msg_raw(indir->name); 3070f66f451Sopenharmony_ci 3080f66f451Sopenharmony_ci return; 3090f66f451Sopenharmony_ci } 3100f66f451Sopenharmony_ci 3110f66f451Sopenharmony_ci memset(totals, 0, sizeof(totals)); 3120f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG) memset(len, 0, sizeof(len)); 3130f66f451Sopenharmony_ci 3140f66f451Sopenharmony_ci // Top level directory was already populated by main() 3150f66f451Sopenharmony_ci if (!indir->parent) { 3160f66f451Sopenharmony_ci // Silently descend into single directory listed by itself on command line. 3170f66f451Sopenharmony_ci // In this case only show dirname/total header when given -R. 3180f66f451Sopenharmony_ci dt = indir->child; 3190f66f451Sopenharmony_ci if (dt && S_ISDIR(dt->st.st_mode) && !dt->next && !(flags&(FLAG_d|FLAG_R))) 3200f66f451Sopenharmony_ci { 3210f66f451Sopenharmony_ci listfiles(open(dt->name, O_DIRECTORY), TT.singledir = dt); 3220f66f451Sopenharmony_ci return; 3230f66f451Sopenharmony_ci } 3240f66f451Sopenharmony_ci 3250f66f451Sopenharmony_ci // Do preprocessing (Dirtree didn't populate, so callback wasn't called.) 3260f66f451Sopenharmony_ci for (;dt; dt = dt->next) filter(dt); 3270f66f451Sopenharmony_ci if (flags == (FLAG_1|FLAG_f)) return; 3280f66f451Sopenharmony_ci // Read directory contents. We dup() the fd because this will close it. 3290f66f451Sopenharmony_ci // This reads/saves contents to display later, except for in "ls -1f" mode. 3300f66f451Sopenharmony_ci } else dirtree_recurse(indir, filter, dup(dirfd), 3310f66f451Sopenharmony_ci DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*!!(flags&FLAG_L)); 3320f66f451Sopenharmony_ci 3330f66f451Sopenharmony_ci // Copy linked list to array and sort it. Directories go in array because 3340f66f451Sopenharmony_ci // we visit them in sorted order too. (The nested loops let us measure and 3350f66f451Sopenharmony_ci // fill with the same inner loop.) 3360f66f451Sopenharmony_ci for (sort = 0;;sort = xmalloc(dtlen*sizeof(void *))) { 3370f66f451Sopenharmony_ci for (dtlen = 0, dt = indir->child; dt; dt = dt->next, dtlen++) 3380f66f451Sopenharmony_ci if (sort) sort[dtlen] = dt; 3390f66f451Sopenharmony_ci if (sort || !dtlen) break; 3400f66f451Sopenharmony_ci } 3410f66f451Sopenharmony_ci 3420f66f451Sopenharmony_ci // Label directory if not top of tree, or if -R 3430f66f451Sopenharmony_ci if (indir->parent && (TT.singledir!=indir || (flags&FLAG_R))) 3440f66f451Sopenharmony_ci { 3450f66f451Sopenharmony_ci char *path = dirtree_path(indir, 0); 3460f66f451Sopenharmony_ci 3470f66f451Sopenharmony_ci if (TT.nl_title++) xputc('\n'); 3480f66f451Sopenharmony_ci xprintf("%s:\n", path); 3490f66f451Sopenharmony_ci free(path); 3500f66f451Sopenharmony_ci } 3510f66f451Sopenharmony_ci 3520f66f451Sopenharmony_ci // Measure each entry to work out whitespace padding and total blocks 3530f66f451Sopenharmony_ci if (!(flags & FLAG_f)) { 3540f66f451Sopenharmony_ci unsigned long long blocks = 0; 3550f66f451Sopenharmony_ci 3560f66f451Sopenharmony_ci qsort(sort, dtlen, sizeof(void *), (void *)compare); 3570f66f451Sopenharmony_ci for (ul = 0; ul<dtlen; ul++) { 3580f66f451Sopenharmony_ci entrylen(sort[ul], len); 3590f66f451Sopenharmony_ci for (width = 0; width<8; width++) 3600f66f451Sopenharmony_ci if (len[width]>totals[width]) totals[width] = len[width]; 3610f66f451Sopenharmony_ci blocks += sort[ul]->st.st_blocks; 3620f66f451Sopenharmony_ci } 3630f66f451Sopenharmony_ci totpad = totals[1]+!!totals[1]+totals[6]+!!totals[6]+totals[7]+!!totals[7]; 3640f66f451Sopenharmony_ci if ((flags&(FLAG_h|FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) && indir->parent) { 3650f66f451Sopenharmony_ci print_with_h(tmp, blocks, 512); 3660f66f451Sopenharmony_ci xprintf("total %s\n", tmp); 3670f66f451Sopenharmony_ci } 3680f66f451Sopenharmony_ci } 3690f66f451Sopenharmony_ci 3700f66f451Sopenharmony_ci // Find largest entry in each field for display alignment 3710f66f451Sopenharmony_ci if (flags & (FLAG_C|FLAG_x)) { 3720f66f451Sopenharmony_ci 3730f66f451Sopenharmony_ci // columns can't be more than toybuf can hold, or more than files, 3740f66f451Sopenharmony_ci // or > 1/2 screen width (one char filename, one space). 3750f66f451Sopenharmony_ci if (columns > TT.screen_width/2) columns = TT.screen_width/2; 3760f66f451Sopenharmony_ci if (columns > dtlen) columns = dtlen; 3770f66f451Sopenharmony_ci 3780f66f451Sopenharmony_ci // Try to fit as many columns as we can, dropping down by one each time 3790f66f451Sopenharmony_ci for (;columns > 1; columns--) { 3800f66f451Sopenharmony_ci unsigned c, totlen = columns; 3810f66f451Sopenharmony_ci 3820f66f451Sopenharmony_ci memset(colsizes, 0, columns*sizeof(unsigned)); 3830f66f451Sopenharmony_ci for (ul=0; ul<dtlen; ul++) { 3840f66f451Sopenharmony_ci entrylen(sort[next_column(ul, dtlen, columns, &c)], len); 3850f66f451Sopenharmony_ci // Add `2` to `totpad` to ensure two spaces between filenames 3860f66f451Sopenharmony_ci *len += totpad+2; 3870f66f451Sopenharmony_ci if (c == columns) break; 3880f66f451Sopenharmony_ci // Expand this column if necessary, break if that puts us over budget 3890f66f451Sopenharmony_ci if (*len > colsizes[c]) { 3900f66f451Sopenharmony_ci totlen += (*len)-colsizes[c]; 3910f66f451Sopenharmony_ci colsizes[c] = *len; 3920f66f451Sopenharmony_ci if (totlen > TT.screen_width) break; 3930f66f451Sopenharmony_ci } 3940f66f451Sopenharmony_ci } 3950f66f451Sopenharmony_ci // If everything fit, stop here 3960f66f451Sopenharmony_ci if (ul == dtlen) break; 3970f66f451Sopenharmony_ci } 3980f66f451Sopenharmony_ci } 3990f66f451Sopenharmony_ci 4000f66f451Sopenharmony_ci // Loop through again to produce output. 4010f66f451Sopenharmony_ci width = 0; 4020f66f451Sopenharmony_ci for (ul = 0; ul<dtlen; ul++) { 4030f66f451Sopenharmony_ci int ii, zap; 4040f66f451Sopenharmony_ci unsigned curcol, color = 0; 4050f66f451Sopenharmony_ci unsigned long next = next_column(ul, dtlen, columns, &curcol); 4060f66f451Sopenharmony_ci struct stat *st = &(sort[next]->st); 4070f66f451Sopenharmony_ci mode_t mode = st->st_mode; 4080f66f451Sopenharmony_ci char et = endtype(st), *ss; 4090f66f451Sopenharmony_ci 4100f66f451Sopenharmony_ci // If we couldn't stat, output ? for most fields 4110f66f451Sopenharmony_ci zap = 0; 4120f66f451Sopenharmony_ci // Skip directories at the top of the tree when -d isn't set 4130f66f451Sopenharmony_ci if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue; 4140f66f451Sopenharmony_ci TT.nl_title=1; 4150f66f451Sopenharmony_ci 4160f66f451Sopenharmony_ci // Handle padding and wrapping for display purposes 4170f66f451Sopenharmony_ci entrylen(sort[next], len); 4180f66f451Sopenharmony_ci if (ul) { 4190f66f451Sopenharmony_ci int mm = !!(flags & FLAG_m); 4200f66f451Sopenharmony_ci 4210f66f451Sopenharmony_ci if (mm) xputc(','); 4220f66f451Sopenharmony_ci if (flags & (FLAG_C|FLAG_x)) { 4230f66f451Sopenharmony_ci if (!curcol) xputc('\n'); 4240f66f451Sopenharmony_ci } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) { 4250f66f451Sopenharmony_ci xputc('\n'); 4260f66f451Sopenharmony_ci width = 0; 4270f66f451Sopenharmony_ci } else { 4280f66f451Sopenharmony_ci printf(" "+mm, 0); // shut up the stupid compiler 4290f66f451Sopenharmony_ci width += 2-mm; 4300f66f451Sopenharmony_ci } 4310f66f451Sopenharmony_ci } 4320f66f451Sopenharmony_ci width += *len; 4330f66f451Sopenharmony_ci 4340f66f451Sopenharmony_ci if (flags & FLAG_i) zprint(zap, "lu ", totals[1], st->st_ino); 4350f66f451Sopenharmony_ci 4360f66f451Sopenharmony_ci if (flags & FLAG_s) { 4370f66f451Sopenharmony_ci print_with_h(tmp, st->st_blocks, 512); 4380f66f451Sopenharmony_ci zprint(zap, "s ", totals[6], (unsigned long)tmp); 4390f66f451Sopenharmony_ci } 4400f66f451Sopenharmony_ci 4410f66f451Sopenharmony_ci if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { 4420f66f451Sopenharmony_ci 4430f66f451Sopenharmony_ci // (long) is to coerce the st types into something we know we can print. 4440f66f451Sopenharmony_ci mode_to_string(mode, tmp); 4450f66f451Sopenharmony_ci if (zap) memset(tmp+1, '?', 9); 4460f66f451Sopenharmony_ci printf("%s", tmp); 4470f66f451Sopenharmony_ci zprint(zap, "ld", totals[2]+1, st->st_nlink); 4480f66f451Sopenharmony_ci 4490f66f451Sopenharmony_ci // print user 4500f66f451Sopenharmony_ci if (!(flags&FLAG_g)) { 4510f66f451Sopenharmony_ci putchar(' '); 4520f66f451Sopenharmony_ci ii = -totals[3]; 4530f66f451Sopenharmony_ci if (zap || (flags&FLAG_n)) zprint(zap, "lu", ii, st->st_uid); 4540f66f451Sopenharmony_ci else draw_trim_esc(getusername(st->st_uid), ii, abs(ii), TT.escmore, 4550f66f451Sopenharmony_ci crunch_qb); 4560f66f451Sopenharmony_ci } 4570f66f451Sopenharmony_ci 4580f66f451Sopenharmony_ci // print group 4590f66f451Sopenharmony_ci if (!(flags&FLAG_o)) { 4600f66f451Sopenharmony_ci putchar(' '); 4610f66f451Sopenharmony_ci ii = -totals[4]; 4620f66f451Sopenharmony_ci if (zap || (flags&FLAG_n)) zprint(zap, "lu", ii, st->st_gid); 4630f66f451Sopenharmony_ci else draw_trim_esc(getgroupname(st->st_gid), ii, abs(ii), TT.escmore, 4640f66f451Sopenharmony_ci crunch_qb); 4650f66f451Sopenharmony_ci } 4660f66f451Sopenharmony_ci } 4670f66f451Sopenharmony_ci if (FLAG(Z)) 4680f66f451Sopenharmony_ci printf(" %-*s "+!FLAG(l), -(int)totals[7], (char *)sort[next]->extra); 4690f66f451Sopenharmony_ci 4700f66f451Sopenharmony_ci if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) { 4710f66f451Sopenharmony_ci struct tm *tm; 4720f66f451Sopenharmony_ci 4730f66f451Sopenharmony_ci // print major/minor, or size 4740f66f451Sopenharmony_ci if (!zap && (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))) 4750f66f451Sopenharmony_ci printf("% *d,% 4d", totals[5]-4, dev_major(st->st_rdev), 4760f66f451Sopenharmony_ci dev_minor(st->st_rdev)); 4770f66f451Sopenharmony_ci else { 4780f66f451Sopenharmony_ci print_with_h(tmp, st->st_size, 1); 4790f66f451Sopenharmony_ci zprint(zap, "s", totals[5]+1, (unsigned long)tmp); 4800f66f451Sopenharmony_ci } 4810f66f451Sopenharmony_ci 4820f66f451Sopenharmony_ci // print time, always in --time-style=long-iso 4830f66f451Sopenharmony_ci tm = localtime(&(st->st_mtime)); 4840f66f451Sopenharmony_ci strftime(tmp, sizeof(tmp), " %F %H:%M", tm); 4850f66f451Sopenharmony_ci if (TT.l>1) { 4860f66f451Sopenharmony_ci char *s = tmp+strlen(tmp); 4870f66f451Sopenharmony_ci 4880f66f451Sopenharmony_ci s += sprintf(s, ":%02d.%09d ", tm->tm_sec, (int)st->st_mtim.tv_nsec); 4890f66f451Sopenharmony_ci strftime(s, sizeof(tmp)-(s-tmp), "%z", tm); 4900f66f451Sopenharmony_ci } 4910f66f451Sopenharmony_ci zprint(zap, "s ", 17+(TT.l>1)*13, (unsigned long)tmp); 4920f66f451Sopenharmony_ci } 4930f66f451Sopenharmony_ci 4940f66f451Sopenharmony_ci if (flags & FLAG_color) { 4950f66f451Sopenharmony_ci color = color_from_mode(st->st_mode); 4960f66f451Sopenharmony_ci if (color) printf("\033[%d;%dm", color>>8, color&255); 4970f66f451Sopenharmony_ci } 4980f66f451Sopenharmony_ci 4990f66f451Sopenharmony_ci ss = sort[next]->name; 5000f66f451Sopenharmony_ci crunch_str(&ss, INT_MAX, stdout, TT.escmore, crunch_qb); 5010f66f451Sopenharmony_ci if (color) printf("\033[0m"); 5020f66f451Sopenharmony_ci 5030f66f451Sopenharmony_ci if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode)) { 5040f66f451Sopenharmony_ci printf(" -> "); 5050f66f451Sopenharmony_ci if (!zap && (flags & FLAG_color)) { 5060f66f451Sopenharmony_ci struct stat st2; 5070f66f451Sopenharmony_ci 5080f66f451Sopenharmony_ci if (fstatat(dirfd, sort[next]->symlink, &st2, 0)) color = 256+31; 5090f66f451Sopenharmony_ci else color = color_from_mode(st2.st_mode); 5100f66f451Sopenharmony_ci 5110f66f451Sopenharmony_ci if (color) printf("\033[%d;%dm", color>>8, color&255); 5120f66f451Sopenharmony_ci } 5130f66f451Sopenharmony_ci 5140f66f451Sopenharmony_ci zprint(zap, "s", 0, (unsigned long)sort[next]->symlink); 5150f66f451Sopenharmony_ci if (!zap && color) printf("\033[0m"); 5160f66f451Sopenharmony_ci } 5170f66f451Sopenharmony_ci 5180f66f451Sopenharmony_ci if (et) xputc(et); 5190f66f451Sopenharmony_ci 5200f66f451Sopenharmony_ci // Pad columns 5210f66f451Sopenharmony_ci if (flags & (FLAG_C|FLAG_x)) { 5220f66f451Sopenharmony_ci curcol = colsizes[curcol]-(*len)-totpad; 5230f66f451Sopenharmony_ci if (curcol < 255) printf("%*c", curcol, ' '); 5240f66f451Sopenharmony_ci } 5250f66f451Sopenharmony_ci } 5260f66f451Sopenharmony_ci 5270f66f451Sopenharmony_ci if (width) xputc('\n'); 5280f66f451Sopenharmony_ci 5290f66f451Sopenharmony_ci // Free directory entries, recursing first if necessary. 5300f66f451Sopenharmony_ci 5310f66f451Sopenharmony_ci for (ul = 0; ul<dtlen; free(sort[ul++])) { 5320f66f451Sopenharmony_ci if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)) continue; 5330f66f451Sopenharmony_ci 5340f66f451Sopenharmony_ci // Recurse into dirs if at top of the tree or given -R 5350f66f451Sopenharmony_ci if (!indir->parent || ((flags&FLAG_R) && dirtree_notdotdot(sort[ul]))) 5360f66f451Sopenharmony_ci listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]); 5370f66f451Sopenharmony_ci free((void *)sort[ul]->extra); 5380f66f451Sopenharmony_ci } 5390f66f451Sopenharmony_ci free(sort); 5400f66f451Sopenharmony_ci if (dirfd != AT_FDCWD) close(dirfd); 5410f66f451Sopenharmony_ci} 5420f66f451Sopenharmony_ci 5430f66f451Sopenharmony_civoid ls_main(void) 5440f66f451Sopenharmony_ci{ 5450f66f451Sopenharmony_ci char **s, *noargs[] = {".", 0}; 5460f66f451Sopenharmony_ci struct dirtree *dt; 5470f66f451Sopenharmony_ci 5480f66f451Sopenharmony_ci if (FLAG(full_time)) { 5490f66f451Sopenharmony_ci toys.optflags |= FLAG_l; 5500f66f451Sopenharmony_ci TT.l = 2; 5510f66f451Sopenharmony_ci } 5520f66f451Sopenharmony_ci 5530f66f451Sopenharmony_ci // Do we have an implied -1 5540f66f451Sopenharmony_ci if (isatty(1)) { 5550f66f451Sopenharmony_ci if (!FLAG(show_control_chars)) toys.optflags |= FLAG_b; 5560f66f451Sopenharmony_ci if (toys.optflags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g)) toys.optflags |= FLAG_1; 5570f66f451Sopenharmony_ci else if (!(toys.optflags&(FLAG_1|FLAG_x|FLAG_m))) toys.optflags |= FLAG_C; 5580f66f451Sopenharmony_ci } else { 5590f66f451Sopenharmony_ci if (!FLAG(m)) toys.optflags |= FLAG_1; 5600f66f451Sopenharmony_ci if (TT.color) toys.optflags ^= FLAG_color; 5610f66f451Sopenharmony_ci } 5620f66f451Sopenharmony_ci 5630f66f451Sopenharmony_ci TT.screen_width = 80; 5640f66f451Sopenharmony_ci terminal_size(&TT.screen_width, NULL); 5650f66f451Sopenharmony_ci if (TT.screen_width<2) TT.screen_width = 2; 5660f66f451Sopenharmony_ci if (FLAG(b)) TT.escmore = " \\"; 5670f66f451Sopenharmony_ci 5680f66f451Sopenharmony_ci // The optflags parsing infrastructure should really do this for us, 5690f66f451Sopenharmony_ci // but currently it has "switch off when this is set", so "-dR" and "-Rd" 5700f66f451Sopenharmony_ci // behave differently 5710f66f451Sopenharmony_ci if (FLAG(d)) toys.optflags &= ~FLAG_R; 5720f66f451Sopenharmony_ci 5730f66f451Sopenharmony_ci // Iterate through command line arguments, collecting directories and files. 5740f66f451Sopenharmony_ci // Non-absolute paths are relative to current directory. Top of tree is 5750f66f451Sopenharmony_ci // a dummy node to collect command line arguments into pseudo-directory. 5760f66f451Sopenharmony_ci TT.files = dirtree_add_node(0, 0, 0); 5770f66f451Sopenharmony_ci TT.files->dirfd = AT_FDCWD; 5780f66f451Sopenharmony_ci for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) { 5790f66f451Sopenharmony_ci int sym = !(toys.optflags&(FLAG_l|FLAG_d|FLAG_F)) 5800f66f451Sopenharmony_ci || (toys.optflags&(FLAG_L|FLAG_H)); 5810f66f451Sopenharmony_ci 5820f66f451Sopenharmony_ci dt = dirtree_add_node(0, *s, DIRTREE_STATLESS|DIRTREE_SYMFOLLOW*sym); 5830f66f451Sopenharmony_ci 5840f66f451Sopenharmony_ci // note: double_list->prev temporarily goes in dirtree->parent 5850f66f451Sopenharmony_ci if (dt) { 5860f66f451Sopenharmony_ci if (dt->again&2) { 5870f66f451Sopenharmony_ci perror_msg_raw(*s); 5880f66f451Sopenharmony_ci free(dt); 5890f66f451Sopenharmony_ci } else dlist_add_nomalloc((void *)&TT.files->child, (void *)dt); 5900f66f451Sopenharmony_ci } else toys.exitval = 1; 5910f66f451Sopenharmony_ci } 5920f66f451Sopenharmony_ci 5930f66f451Sopenharmony_ci // Convert double_list into dirtree. 5940f66f451Sopenharmony_ci dlist_terminate(TT.files->child); 5950f66f451Sopenharmony_ci for (dt = TT.files->child; dt; dt = dt->next) dt->parent = TT.files; 5960f66f451Sopenharmony_ci 5970f66f451Sopenharmony_ci // Display the files we collected 5980f66f451Sopenharmony_ci listfiles(AT_FDCWD, TT.files); 5990f66f451Sopenharmony_ci 6000f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) free(TT.files); 6010f66f451Sopenharmony_ci} 602