Audit cols(1)

1) Refactor manpage.
2) De-globalize local values.
3) update usage().
4) sort local variable declarations.
5) fix wrong argument in strtonum (3 -> 1).
6) argc-argv style, boolean style.
7) check bytes > 0 before accessing b.lines[i][bytes - 1]
   relying on len only makes sense but let's not push it.
7) don't break on maxlen > (chars - 1) / 2. This didn't even
   make sense.
8) _correctly_ calculate cols and rows in a readable way.
9) Rewrite loop over rows and cols in a readable way and
   using putchar in a loop instead of printf-magic or fputs
   where not necessary.
This commit is contained in:
FRIGN 2015-03-08 19:33:46 +01:00
parent 78187474cf
commit f140403fca
3 changed files with 43 additions and 52 deletions

2
README
View File

@ -18,7 +18,7 @@ The following tools are implemented ('*' == finished, '#' == UTF-8 support,
=*| chroot non-posix none =*| chroot non-posix none
=* cksum yes none =* cksum yes none
=* cmp yes none =* cmp yes none
#* cols non-posix none #*| cols non-posix none
col yes none col yes none
=* comm yes none =* comm yes none
=*| cp yes none (-i) =*| cp yes none (-i)

19
cols.1
View File

@ -1,4 +1,4 @@
.Dd February 19, 2015 .Dd March 8, 2015
.Dt COLS 1 .Dt COLS 1
.Os sbase .Os sbase
.Sh NAME .Sh NAME
@ -15,7 +15,8 @@ reads each
in sequence and writes them to stdout, in as many vertical in sequence and writes them to stdout, in as many vertical
columns as will fit in columns as will fit in
.Ar num .Ar num
character columns. If no character columns.
If no
.Ar file .Ar file
is given, is given,
.Nm .Nm
@ -27,17 +28,15 @@ tries to figure out the width of the output
device. If that fails, it defaults to 65 chars. device. If that fails, it defaults to 65 chars.
.Sh OPTIONS .Sh OPTIONS
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl c Ar chars .It Fl c Ar num
Set the maximum number of character columns to use Set maximum number of character columns to
(unless the input contains lines longer than .Ar num ,
.Ar num unless input lines exceed this limit.
characters).
.El .El
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Bl -tag -width COLUMNS .Bl -tag -width Ds
.It COLUMNS .It COLUMNS
If this variable is set, the value is used as the The width of the output device.
width of the output device.
.El .El
.Sh HISTORY .Sh HISTORY
.Nm .Nm

70
cols.c
View File

@ -12,82 +12,74 @@
#include "utf.h" #include "utf.h"
#include "util.h" #include "util.h"
static size_t chars = 65;
static int cflag;
static struct linebuf b = EMPTY_LINEBUF;
static size_t n_columns;
static size_t n_rows;
static void static void
usage(void) usage(void)
{ {
eprintf("usage: %s [-c chars] [file ...]\n", argv0); eprintf("usage: %s [-c num] [file ...]\n", argv0);
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
size_t i, l, col, len, bytes, maxlen = 0;
struct winsize w;
FILE *fp; FILE *fp;
struct winsize w;
struct linebuf b = EMPTY_LINEBUF;
size_t chars = 65, maxlen = 0, i, j, k, len, bytes, cols, rows;
int cflag = 0;
char *p; char *p;
ARGBEGIN { ARGBEGIN {
case 'c': case 'c':
cflag = 1; cflag = 1;
chars = estrtonum(EARGF(usage()), 3, MIN(LLONG_MAX, SIZE_MAX)); chars = estrtonum(EARGF(usage()), 1, MIN(LLONG_MAX, SIZE_MAX));
break; break;
default: default:
usage(); usage();
} ARGEND; } ARGEND;
if (cflag == 0) { if (!cflag) {
if ((p = getenv("COLUMNS"))) if ((p = getenv("COLUMNS")))
chars = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX)); chars = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
else if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0) else if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) && w.ws_col > 0)
chars = w.ws_col; chars = w.ws_col;
} }
if (argc == 0) { if (!argc) {
getlines(stdin, &b); getlines(stdin, &b);
} else for (; argc > 0; argc--, argv++) { } else {
if (!(fp = fopen(argv[0], "r"))) for (; *argv; argc--, argv++) {
eprintf("fopen %s:", argv[0]); if (!(fp = fopen(*argv, "r"))) {
weprintf("fopen %s:", *argv);
continue;
}
getlines(fp, &b); getlines(fp, &b);
fclose(fp); fclose(fp);
} }
}
for (l = 0; l < b.nlines; ++l) { for (i = 0; i < b.nlines; i++) {
len = utflen(b.lines[l]); len = utflen(b.lines[i]);
bytes = strlen(b.lines[l]); bytes = strlen(b.lines[i]);
if (len > 0 && b.lines[l][bytes - 1] == '\n') { if (len && bytes && b.lines[i][bytes - 1] == '\n') {
b.lines[l][bytes - 1] = '\0'; b.lines[i][bytes - 1] = '\0';
--len; len--;
} }
if (len > maxlen) if (len > maxlen)
maxlen = len; maxlen = len;
if (maxlen > (chars - 1) / 2)
break;
} }
n_columns = (chars + 1) / (maxlen + 1); for (cols = 1; (cols + 1) * maxlen + cols <= chars; cols++);
if (n_columns <= 1) { rows = b.nlines / cols + (b.nlines % cols > 0);
for (l = 0; l < b.nlines; ++l) {
fputs(b.lines[l], stdout);
}
return 0;
}
n_rows = (b.nlines + (n_columns - 1)) / n_columns; for (i = 0; i < rows; i++) {
for (i = 0; i < n_rows; ++i) { for (j = 0; j < cols && i + j * rows < b.nlines; j++) {
for (l = i, col = 1; l < b.nlines; l += n_rows, ++col) { len = utflen(b.lines[i + j * rows]);
len = utflen(b.lines[l]); fputs(b.lines[i + j * rows], stdout);
fputs(b.lines[l], stdout); if (j < cols - 1)
if (col < n_columns) for (k = len; k < maxlen + 1; k++)
printf("%*s", (int)(maxlen + 1 - len), ""); putchar(' ');
} }
fputs("\n", stdout); putchar('\n');
} }
return 0; return 0;