# HG changeset patch # User Rob Landley # Date 1338517330 18000 # Node ID ca6875170b9a19d7f293ea6589ebdec21fe7e866 # Parent 9802b2afbce874a6d27d1987de87c6a79056170c Implement -C and -x for ls. diff -r 9802b2afbce8 -r ca6875170b9a toys/ls.c --- a/toys/ls.c Thu May 31 21:17:11 2012 -0500 +++ b/toys/ls.c Thu May 31 21:22:10 2012 -0500 @@ -7,6 +7,7 @@ * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html +// "[-Cl]" USE_LS(NEWTOY(ls, "ACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN)) config LS @@ -16,17 +17,30 @@ usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...] list files - -1 list one file per line - -a list all files + what to show: + -a list all files + -d directory, not contents + -i inode number + -p put a '/' after directory names -A list all files except . and .. - -F append a character as a file type indicator - -l show full details for each file + -R recursively list files in subdirectories + -F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO) + + output formats: + -1 list one file per line + -C columns (sorted vertically) + -x columns (sorted horizontally) + -l long (show full details for each file) + -m comma separated + + sorting: + -f unsorted */ #include "toys.h" #define FLAG_1 (1<<0) -//#define FLAG_x (1<<1) +#define FLAG_x (1<<1) //#define FLAG_u (1<<2) //#define FLAG_t (1<<3) //#define FLAG_s (1<<4) @@ -47,18 +61,18 @@ //#define FLAG_L (1<<19) //#define FLAG_H (1<<20) #define FLAG_F (1<<21) -//#define FLAG_C (1<<21) -#define FLAG_A (1<<22) +#define FLAG_C (1<<22) +#define FLAG_A (1<<23) // test sst output (suid/sticky in ls flaglist) // ls -lR starts .: then ./subdir: DEFINE_GLOBALS( - struct dirtree *files; + struct dirtree *files; - unsigned width; - int nl_title; + unsigned screen_width; + int nl_title; ) #define TT this.ls @@ -91,14 +105,14 @@ static char *getusername(uid_t uid) { - struct passwd *pw = getpwuid(uid); - return pw ? pw->pw_name : utoa(uid); + struct passwd *pw = getpwuid(uid); + return pw ? pw->pw_name : utoa(uid); } static char *getgroupname(gid_t gid) { - struct group *gr = getgrgid(gid); - return gr ? gr->gr_name : utoa(gid); + struct group *gr = getgrgid(gid); + return gr ? gr->gr_name : utoa(gid); } // Figure out size of printable entry fields for display indent/wrap @@ -145,6 +159,48 @@ return dirtree_notdotdot(new); } +// For column view, calculate horizontal position (for padding) and return +// index of next entry to display. + +static unsigned long next_column(unsigned long ul, unsigned long dtlen, + unsigned columns, unsigned *xpos) +{ + unsigned long transition; + unsigned height, widecols; + + // Horizontal sort is easy + if (!(toys.optflags & FLAG_C)) { + *xpos = ul % columns; + return ul; + } + + // vertical sort + + // For -x, calculate height of display, rounded up + height = (dtlen+columns-1)/columns; + + // Sanity check: does wrapping render this column count impossible + // due to the right edge wrapping eating a whole row? + if (height*columns - dtlen >= height) { + *xpos = columns; + return 0; + } + + // Uneven rounding goes along right edge + widecols = dtlen % height; + if (!widecols) widecols = height; + transition = widecols * columns; + if (ul < transition) { + *xpos = ul % columns; + return (*xpos*height) + (ul/columns); + } + + ul -= transition; + *xpos = ul % (columns-1); + + return (*xpos*height) + widecols + (ul/(columns-1)); +} + // Display a list of dirtree entries, according to current format // Output types -1, -l, -C, or stream @@ -152,14 +208,16 @@ { struct dirtree *dt, **sort = 0; unsigned long dtlen = 0, ul = 0; - unsigned width, flags = toys.optflags, totals[6], len[6]; + unsigned width, flags = toys.optflags, totals[6], len[6], + *colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4; + + memset(totals, 0, 6*sizeof(unsigned)); - // Silently descend into single directory listed by itself on command line. // In this case only show dirname/total header when given -R. if (!indir->parent) { if (!(dt = indir->child)) return; - if (S_ISDIR(dt->st.st_mode) && !dt->next && !(toys.optflags&FLAG_d)) { + if (S_ISDIR(dt->st.st_mode) && !dt->next && !(flags & FLAG_d)) { dt->extra = 1; listfiles(open(dt->name, 0), dt); return; @@ -186,20 +244,40 @@ if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare); - // Find largest entry in each field + // Find largest entry in each field for display alignment + if (flags & (FLAG_C|FLAG_x)) { + + // columns can't be more than toybuf can hold, or more than files, + // or > 1/2 screen width (one char filename, one space). + if (columns > TT.screen_width/2) columns = TT.screen_width/2; + if (columns > dtlen) columns = dtlen; + + // Try to fit as many columns as we can, dropping down by one each time + for (;columns > 1; columns--) { + unsigned c, totlen = columns; - memset(totals, 0, 6*sizeof(unsigned)); - for (ul = 0; ul colsizes[c]) { + totlen += *len-colsizes[c]; + colsizes[c] = *len; + if (totlen > TT.screen_width) break; + } + } + // If it fit, stop here + if (ul == dtlen) break; + } + } else if (flags & FLAG_l) for (ul = 0; ul totals[width]) totals[width] = len[width]; -//TODO } else if (flags & FLAG_C) { - } else if (*len > *totals) *totals = *len; + for (width=0; width<6; width++) + if (len[width] > totals[width]) totals[width] = len[width]; } // Label directory if not top of tree, or if -R - if (indir->parent && (!indir->extra || (flags&FLAG_R))) + if (indir->parent && (!indir->extra || (flags & FLAG_R))) { char *path = dirtree_path(indir, 0); @@ -212,10 +290,12 @@ if (indir->parent && (flags & FLAG_l)) xprintf("total %lu\n", dtlen); // Loop through again to produce output. + memset(toybuf, ' ', 256); width = 0; - memset(toybuf, ' ', 256); for (ul = 0; ulst); + unsigned curcol; + unsigned long next = next_column(ul, dtlen, columns, &curcol); + struct stat *st = &(sort[next]->st); mode_t mode = st->st_mode; char et = endtype(st); @@ -223,11 +303,13 @@ if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue; TT.nl_title=1; - // Do we need to wrap at right edge of screen? - entrylen(sort[ul], len); + // Handle padding and wrapping for display purposes + entrylen(sort[next], len); if (ul) { - if (toys.optflags & FLAG_m) xputc(','); - if ((flags & FLAG_1) || width+1+*len > TT.width) { + if (flags & FLAG_m) xputc(','); + if (flags & (FLAG_C|FLAG_x)) { + if (!curcol) xputc('\n'); + } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) { xputc('\n'); width = 0; } else { @@ -274,11 +356,17 @@ totals[5]+1, st->st_size, thyme); } - xprintf("%s", sort[ul]->name); + xprintf("%s", sort[next]->name); if ((flags & FLAG_l) && S_ISLNK(mode)) - xprintf(" -> %s", sort[ul]->symlink); + xprintf(" -> %s", sort[next]->symlink); if (et) xputc(et); + + // Pad columns + if (flags & (FLAG_C|FLAG_x)) { + curcol = colsizes[curcol] - *len; + if (curcol >= 0) xprintf("%s", toybuf+255-curcol); + } } if (width) xputc('\n'); @@ -306,8 +394,8 @@ // Do we have an implied -1 if (!isatty(1) || (toys.optflags&FLAG_l)) toys.optflags |= FLAG_1; else { - TT.width = 80; - terminal_size(&TT.width, NULL); + TT.screen_width = 80; + terminal_size(&TT.screen_width, NULL); } // The optflags parsing infrastructure should really do this for us, // but currently it has "switch off when this is set", so "-dR" and "-Rd"