Dear Jim & others, Here is a patch that improves column alignment for `ls l' output. The problems addressed are: (a) `ls -l' truncates long user/group names (b) big file sizes cause mis-alignment in the output (c) The mode string has been widened to 11 characters recently, which leaves one column less space for the file name. This usually is not needed. The patch keeps columns aligned whenever possible, and long fields never get truncated. This is done by keeping track of the amount of whitespace between fields. This space is then used by other fields that are too wide as needed. The whole thing is implemented as a {sn,f}printf_align function that keeps some state about alignment between invocations. Here are some examples: This is traditional `ls -l' (no space reserved for the '+'): ---------- --- -------- -------- -------- ------------ --------> drwxr-xr-x 21 longname longgrou 4096 Nov 2 22:32 dir -rw-r--r-- 21 longname longgrou 123456789 Nov 2 22:32 bigfile -rw-r--r-- 21 longname longgrou 1234567890 Nov 2 22:32 biggerfile This is what the patch does: ---------- --- -------- -------- -------- ------------ --------> drwxr-xr-x 21 longname1 longgroup 4096 Nov 2 22:32 dir -rw-r--r--+ 1234 longname1 longgroup 124 Nov 2 22:32 manylinks -rw-r--r-- 123 name group 123456789 Nov 2 22:32 bigfile -rw-r--r-- 21 longname1 group 1234567890 Nov 2 22:32 biggerfile Note that the timestamp and filenames are properly aligned in all cases. Comments, please? Thanks, Andreas. ------------------------------------------------------------------------ Andreas Gruenbacher, [EMAIL PROTECTED] Contact information: http://www.bestbits.at/~ag/
diff -Nur fileutils-4.0.31/src/Makefile.am fileutils-4.0.31-align/src/Makefile.am --- fileutils-4.0.31/src/Makefile.am Sun Aug 6 14:21:03 2000 +++ fileutils-4.0.31-align/src/Makefile.am Wed Nov 8 23:19:04 2000 @@ -10,7 +10,7 @@ datadir = $(prefix)/@DATADIRNAME@ localedir = $(datadir)/locale -noinst_HEADERS = system.h sys2.h copy.h cp-hash.h ls.h dircolors.h remove.h +noinst_HEADERS = system.h sys2.h copy.h cp-hash.h ls.h dircolors.h remove.h +printf_align.h printf_align_body.c EXTRA_DIST = dcgen dircolors.hin INCLUDES = -I.. -I$(srcdir) -I$(top_srcdir)/lib -I../intl @@ -29,9 +29,9 @@ ginstall_SOURCES = install.c copy.c cp-hash.c cp_SOURCES = cp.c copy.c cp-hash.c -dir_SOURCES = ls.c ls-dir.c -vdir_SOURCES = ls.c ls-vdir.c -ls_SOURCES = ls.c ls-ls.c +dir_SOURCES = ls.c ls-dir.c printf_align.c +vdir_SOURCES = ls.c ls-vdir.c printf_align.c +ls_SOURCES = ls.c ls-ls.c printf_align.c mv_SOURCES = mv.c copy.c cp-hash.c remove.c rm_SOURCES = rm.c remove.c diff -Nur fileutils-4.0.31/src/ls.c fileutils-4.0.31-align/src/ls.c --- fileutils-4.0.31/src/ls.c Sun Oct 29 08:46:04 2000 +++ fileutils-4.0.31-align/src/ls.c Wed Nov 8 23:08:35 2000 @@ -128,6 +128,7 @@ #include "quotearg.h" #include "strverscmp.h" #include "xstrtol.h" +#include "printf_align.h" /* Use access control lists only under all the following conditions. Some systems (OSF4, Irix5, Irix6) have the acl function, but not @@ -2326,6 +2327,7 @@ print_long_format (const struct fileinfo *f) { char modebuf[12]; + struct align_state state; /* 7 fields that may require LONGEST_HUMAN_READABLE bytes, 1 10-byte mode string, @@ -2338,6 +2340,7 @@ + 9 + 1]; char *buf = init_bigbuf; size_t bufsize = sizeof (init_bigbuf); + ssize_t bufrest = bufsize; size_t s; char *p; time_t when; @@ -2345,6 +2348,10 @@ const char *fmt; char *user_name; +restart: + + reset_align(&state); + #if HAVE_ST_DM_MODE /* Cray DMF: look at the file's migrated, not real, status */ mode_string (f->stat.st_dm_mode, modebuf); @@ -2352,7 +2359,7 @@ mode_string (f->stat.st_mode, modebuf); #endif - modebuf[10] = (FILE_HAS_ACL (f) ? '+' : ' '); + modebuf[10] = (FILE_HAS_ACL (f) ? '+' : '\0'); modebuf[11] = '\0'; switch (time_type) @@ -2396,71 +2403,83 @@ if (print_inode) { char hbuf[LONGEST_HUMAN_READABLE + 1]; - sprintf (p, "%*s ", INODE_DIGITS, + s = snprintf_align (&state, p, bufrest, "%*s ", INODE_DIGITS, human_readable ((uintmax_t) f->stat.st_ino, hbuf, 1, 1)); - p += strlen (p); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; } if (print_block_size) { char hbuf[LONGEST_HUMAN_READABLE + 1]; - sprintf (p, "%*s ", block_size_size, + s = snprintf_align (&state, p, bufrest, "%*s ", block_size_size, human_readable_inexact ((uintmax_t) ST_NBLOCKS (f->stat), hbuf, ST_NBLOCKSIZE, output_block_size, human_ceiling)); - p += strlen (p); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; } /* The last byte of the mode string is the POSIX "optional alternate access method flag". */ - sprintf (p, "%s %3u ", modebuf, (unsigned int) f->stat.st_nlink); - p += strlen (p); + s = snprintf_align (&state, p, bufrest, "%10s %3u ", modebuf, + (unsigned int) f->stat.st_nlink); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; user_name = (numeric_ids ? NULL : getuser (f->stat.st_uid)); if (user_name) - sprintf (p, "%-8.8s ", user_name); + s = snprintf_align (&state, p, bufrest, "%-8s ", user_name); else - sprintf (p, "%-8u ", (unsigned int) f->stat.st_uid); - p += strlen (p); + s = snprintf_align (&state, p, bufrest, "%-8u ", (unsigned int) f->stat.st_uid); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; if (!inhibit_group) { char *group_name = (numeric_ids ? NULL : getgroup (f->stat.st_gid)); if (group_name) - sprintf (p, "%-8.8s ", group_name); + s = snprintf_align (&state, p, bufrest, "%-8s ", group_name); else - sprintf (p, "%-8u ", (unsigned int) f->stat.st_gid); - p += strlen (p); + s = snprintf_align (&state, p, bufrest, "%-8u ", + (unsigned int) f->stat.st_gid); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; } if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode)) - sprintf (p, "%3u, %3u ", (unsigned) major (f->stat.st_rdev), - (unsigned) minor (f->stat.st_rdev)); + s = snprintf_align (&state, p, bufrest, "%3u, %3u ", + (unsigned) major (f->stat.st_rdev), + (unsigned) minor (f->stat.st_rdev)); else { char hbuf[LONGEST_HUMAN_READABLE + 1]; - sprintf (p, "%8s ", + s = snprintf_align (&state, p, bufrest, "%8s ", human_readable ((uintmax_t) f->stat.st_size, hbuf, 1, output_block_size < 0 ? output_block_size : 1)); } - p += strlen (p); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; /* Use strftime rather than ctime, because the former can produce locale-dependent names for the weekday (%a) and month (%b). */ if ((when_local = localtime (&when))) { - while (! (s = strftime (p, buf + bufsize - p - 1, fmt, when_local))) - { - char *newbuf = (char *) alloca (bufsize *= 2); - memcpy (newbuf, buf, p - buf); - p = newbuf + (p - buf); - buf = newbuf; - } - + if (bufrest <= 0) + goto reallocate; + if (! (s = strftime (p, bufrest-1, fmt, when_local))) + goto reallocate; p += s; *p++ = ' '; + bufrest -= s+1; /* NUL-terminate the string -- fputs (via DIRED_FPUTS) requires it. */ *p = '\0'; @@ -2476,13 +2495,16 @@ { const char *num = human_readable (- (uintmax_t) when, hbuf, 1, 1); int sign_width = width - strlen (num); - sprintf (p, "%*s%s ", sign_width < 0 ? 0 : sign_width, "-", num); + s = snprintf_align (&state, p, bufrest, "%*s%s ", + sign_width < 0 ? 0 : sign_width, "-", num); } else - sprintf (p, "%*s ", width, - human_readable ((uintmax_t) when, hbuf, 1, 1)); + s = snprintf_align (&state, p, bufrest, "%*s ", width, + human_readable ((uintmax_t) when, hbuf, 1, 1)); - p += strlen (p); + if ((bufrest -= s) < 0) + goto reallocate; + p += s; } DIRED_INDENT (); @@ -2503,6 +2525,13 @@ } else if (indicator_style != none) print_type_indicator (f->stat.st_mode); + + return; + +reallocate: + + buf = alloca (bufsize *= 2); + goto restart; } /* Output to OUT a quoted representation of the file name NAME, diff -Nur fileutils-4.0.31/src/printf_align.c fileutils-4.0.31-align/src/printf_align.c --- fileutils-4.0.31/src/printf_align.c Thu Jan 1 01:00:00 1970 +++ fileutils-4.0.31-align/src/printf_align.c Wed Nov 8 23:23:37 2000 @@ -0,0 +1,75 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include "printf_align.h" + +void reset_align(struct align_state *align) +{ + align->extra_width = 0; +} + +int printf_align(struct align_state *state, + const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = vfprintf_align(state, stdout, format, ap); + va_end(ap); + + return ret; +} + +int fprintf_align(struct align_state *state, FILE *stream, + const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = vfprintf_align(state, stream, format, ap); + va_end(ap); + + return ret; +} + +#define OUT(c) do { \ + n++; \ + putc(c, stream); \ + } while(0) +int vfprintf_align(struct align_state *state, FILE *stream, + const char *format, va_list ap) +{ +# include "printf_align_body.c" + return n; +} +#undef OUT + +int snprintf_align(struct align_state *state, char *str, size_t size, + const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = vsnprintf_align(state, str, size, format, ap); + va_end(ap); + + return ret; +} + +#define OUT(c) if (++n < size) \ + *str++ = (c) +int vsnprintf_align(struct align_state *state, char *str, size_t size, + const char *format, va_list ap) +{ +# include "printf_align_body.c" + if (n <= size) + size = n; + if (size > 0) + str[size-1] = '\0'; + + return n; +} +#undef OUT diff -Nur fileutils-4.0.31/src/printf_align.h fileutils-4.0.31-align/src/printf_align.h --- fileutils-4.0.31/src/printf_align.h Thu Jan 1 01:00:00 1970 +++ fileutils-4.0.31-align/src/printf_align.h Wed Nov 8 22:57:22 2000 @@ -0,0 +1,21 @@ +#ifndef __PRINTF_ALIGN +#define __PRINTF_ALIGN + +#include <stdio.h> +#include <stdarg.h> + +struct align_state { + int extra_width; +}; + +void reset_align(struct align_state *align); + +extern int vfprintf_align(struct align_state *, FILE *, const char *, va_list); +extern int fprintf_align(struct align_state *, FILE *, const char *, ...); +extern int printf_align(struct align_state *, const char *, ...); + +extern int vsnprintf_align(struct align_state *, char *, size_t, const char *, +va_list); +extern int snprintf_align(struct align_state *, char *, size_t, const char *, ...); + +#endif /* __PRINTF_ALIGN */ + diff -Nur fileutils-4.0.31/src/printf_align_body.c fileutils-4.0.31-align/src/printf_align_body.c --- fileutils-4.0.31/src/printf_align_body.c Thu Jan 1 01:00:00 1970 +++ fileutils-4.0.31-align/src/printf_align_body.c Fri Nov 10 09:09:19 2000 @@ -0,0 +1,132 @@ + int field_width, width, type_long, delta, n = 0; + enum { left_align, right_align } align; + char *s, *t; + union { + long s; + unsigned long u; + } arg; + unsigned long fact; + + if (*format == '\0') { + /* flush remembered spaces */ + while (state->extra_width > 0) { + OUT(' '); + state->extra_width--; + } + } + while (*format != '\0') { + while (*format != '%' && *format != '\0') { + if (*format != ' ') { + while (state->extra_width > 0) { + OUT(' '); + state->extra_width--; + } + } + OUT(*format); + format++; + } + if (*format == '%') { + format++; + if (*format == '*') { + format++; + field_width = va_arg(ap, int); + align = right_align; + if (field_width < 0) { + field_width = -field_width; + align = left_align; + } + } else { + align = right_align; + if (*format == '-') { + format++; + align = left_align; + } + field_width = 0; + while (*format >= '0' && *format <= '9') + field_width = (field_width * 10) + + (*format++ - '0'); + } + type_long = 0; + if (*format == 'l') { + format++; + type_long = 1; + } + switch(*format) { + case 's': + s = va_arg(ap, char *); + width = strlen(s); + break; + case 'd': case 'i': { + long m; + + arg.s = type_long ? + va_arg(ap, long) : + va_arg(ap, int); + width = 1; fact = 1; + if (arg.s < 0) + width++; + for (m = arg.s; m /= 10;) { + fact *= 10; + width++; + } + break; + } + case 'u': { + unsigned long m; + + arg.u = type_long ? + va_arg(ap, unsigned long) : + va_arg(ap, unsigned int); + width = 1; fact = 1; + for (m = arg.u; m /= 10;) { + fact *= 10; + width++; + } + break; + } + default: + OUT(*format); + format++; + goto next; + } + + if (field_width == 0) + field_width = width; + delta = field_width - width; + if (align == right_align) + state->extra_width += delta; + while (state->extra_width > 0) { + OUT(' '); + state->extra_width--; + } + if (align == left_align) + state->extra_width += delta; + + switch(*format) { + case 's': + while (*s) { + OUT(*s); + s++; + } + break; + case 'd': case 'i': + if (arg.s < 0) { + OUT('-'); + arg.s = -arg.s; + } + arg.u = (unsigned long)arg.s; + /* fall through */ + + case 'u': + do { + unsigned long d = arg.u / fact; + arg.u = arg.u % fact; + fact /= 10; + OUT(d + '0'); + } while (fact > 0); + break; + } + format++; + next: + } + }