[PATCH 2/3] colcrt: reimplementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This implementation aims to be easier to read, more robust dealing all sorts
of unexpected inputs, and possibly even more correct.  The correctness
refers to last line handling this implementation does differently than the
previous.  With the previous last line that ended to EOF without \n was not
printed.

Signed-off-by: Sami Kerola <kerolasa@xxxxxx>
---
 text-utils/colcrt.c | 419 ++++++++++++++++++++++------------------------------
 1 file changed, 176 insertions(+), 243 deletions(-)

diff --git a/text-utils/colcrt.c b/text-utils/colcrt.c
index be7f847..b54da8b 100644
--- a/text-utils/colcrt.c
+++ b/text-utils/colcrt.c
@@ -40,26 +40,18 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>		/* for close() */
-#include <string.h>
 #include <getopt.h>
-#include "nls.h"
 
+#include "nls.h"
 #include "widechar.h"
 #include "c.h"
 #include "closestream.h"
 
-int plus(wchar_t c, wchar_t d);
-void move(int l, int m);
-void pflush(int ol);
-static void __attribute__ ((__noreturn__)) usage(FILE * out);
-
 /*
  * colcrt - replaces col for crts with new nroff esp. when using tbl.
  * Bill Joy UCB July 14, 1977
  *
- * This filter uses a screen buffer, 267 half-lines by 132 columns.
- * It interprets the up and down sequences generated by the new
+ * This filter uses the up and down sequences generated by the new
  * nroff when used with tbl and by \u \d and \r.
  * General overstriking doesn't work correctly.
  * Underlining is split onto multiple lines, etc.
@@ -68,266 +60,207 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out);
  * Option -2 forces printing of all half lines.
  */
 
-#define FLUSH_SIZE 62
-#define PAGE_ARRAY_ROWS 267
-#define PAGE_ARRAY_COLS 132
-wchar_t	page[PAGE_ARRAY_ROWS + 1][PAGE_ARRAY_COLS + 1];
-
-int	outline = 1;
-int	outcol;
+enum { OUTPUT_COLS = 132 };
 
-char	suppresul;
-char	printall;
-
-void colcrt(FILE *f);
-
-int main(int argc, char **argv) {
+struct colcrt_control {
 	FILE *f;
-	int i, opt;
-	enum { NO_UL_OPTION = CHAR_MAX + 1 };
+	wchar_t line[OUTPUT_COLS + 1];
+	wchar_t line_under[OUTPUT_COLS + 1];
+	unsigned int
+	 print_nl:1,
+	 need_line_under:1,
+	 no_underlining:1,
+	 half_lines:1;
+};
 
-	static const struct option longopts[] = {
-		{ "no-underlining",	no_argument, 0, NO_UL_OPTION },
-		{ "half-lines",		no_argument, 0, '2' },
-		{ "version",		no_argument, 0, 'V' },
-		{ "help",		no_argument, 0, 'h' },
-		{ NULL, 0, 0, 0}
-	};
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+	fputs(USAGE_HEADER, out);
+	fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
+	fputs(USAGE_SEPARATOR, out);
+	fputs(_("Filter nroff output for CRT previewing.\n"), out);
+	fputs(USAGE_OPTIONS, out);
+	fputs(_(" -,  --no-underlining    suppress all underlining\n"), out);
+	fputs(_(" -2, --half-lines        print all half-lines\n"), out);
+	fputs(USAGE_SEPARATOR, out);
+	fputs(USAGE_HELP, out);
+	fputs(USAGE_VERSION, out);
+	fprintf(out, USAGE_MAN_TAIL("colcrt(1)"));
+	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
 
-	setlocale(LC_ALL, "");
-	bindtextdomain(PACKAGE, LOCALEDIR);
-	textdomain(PACKAGE);
-	atexit(close_stdout);
+static void trim_trailing_spaces(wchar_t *s)
+{
+	size_t size;
+	wchar_t *end;
 
-	/* Take care of lonely hyphen option. */
-	for (i = 0; i < argc; i++)
-		if (argv[i][0] == '-' && argv[i][1] == '\0') {
-			suppresul = 1;
-			argc--;
-			memmove(argv + i, argv + i + 1,
-				sizeof(char *) * (argc - i));
-			i--;
-		}
+	size = wcslen(s);
+	if (!size)
+		return;
+	end = s + size - 1;
+	while (s <= end && iswspace(*end))
+		end--;
+	*(end + 1) = L'\0';
+}
 
-	while ((opt = getopt_long(argc, argv, "2Vh", longopts, NULL)) != -1)
-		switch (opt) {
-			case NO_UL_OPTION:
-				suppresul = 1;
-				break;
-			case '2':
-				printall = 1;
-				break;
-			case 'V':
-				printf(UTIL_LINUX_VERSION);
-				return EXIT_SUCCESS;
-			case 'h':
-				usage(stdout);
-			default:
-				usage(stderr);
-		}
-	argc -= optind;
-	argv += optind;
 
-	do {
-		if (argc > 0) {
-			if (!(f = fopen(argv[0], "r"))) {
-				fflush(stdout);
-				err(EXIT_FAILURE, "%s", argv[0]);
-			}
-			argc--;
-			argv++;
-		} else {
-			f = stdin;
-		}
-		colcrt(f);
-		if (f != stdin)
-			fclose(f);
-	} while (argc > 0);
-	fflush(stdout);
-	return EXIT_SUCCESS;
+static void output_lines(struct colcrt_control *ctl, int col)
+{
+	/* first line */
+	trim_trailing_spaces(ctl->line);
+	fputws(ctl->line, stdout);
+	if (ctl->print_nl)
+		fputwc(L'\n', stdout);
+	if (!ctl->half_lines && !ctl->no_underlining)
+		ctl->print_nl = 0;
+	wmemset(ctl->line, L'\0', OUTPUT_COLS);
+	/* second line */
+	if (ctl->need_line_under) {
+		ctl->need_line_under = 0;
+		ctl->line_under[col] = L'\0';
+		trim_trailing_spaces(ctl->line_under);
+		fputws(ctl->line_under, stdout);
+		fputwc(L'\n', stdout);
+		wmemset(ctl->line_under, L' ', OUTPUT_COLS);
+	} else if (ctl->half_lines && 0 < col)
+		fputwc(L'\n', stdout);
 }
 
-void colcrt(FILE *f) {
+static int rubchars(struct colcrt_control *ctl, int col, int n)
+{
+	while (0 < n && 0 < col) {
+		ctl->line[col] = L'\0';
+		ctl->line_under[col] = L' ';
+		n--;
+		col--;
+	}
+	return col;
+}
+
+static void colcrt(struct colcrt_control *ctl)
+{
+	int col;
 	wint_t c;
-	wchar_t *cp, *dp;
-	int i, w;
 
-	for (;;) {
-		c = getwc(f);
-		if (c == WEOF) {
-			pflush(outline);
-			fflush(stdout);
-			break;
+	ctl->print_nl = 1;
+	if (ctl->half_lines)
+		fputwc(L'\n', stdout);
+	for (col = 0; /* nothing */; col++) {
+		if (OUTPUT_COLS - 1 < col) {
+			output_lines(ctl, col);
+			errno = 0;
+			while ((c = getwc(ctl->f)) != L'\n') {
+				if (errno == 0 && c == WEOF)
+					return;
+				else
+					errno = 0;
+			}
+			col = -1;
+			continue;
 		}
+		c = getwc(ctl->f);
 		switch (c) {
-		case '\n':
-			if (outline >= (PAGE_ARRAY_ROWS - 2))
-				pflush(FLUSH_SIZE);
-			outline += 2;
-			outcol = 0;
-			continue;
-		case '\016':
-		case '\017':
-			continue;
-		case 033:
-			c = getwc(f);
-			switch (c) {
-			case '9':
-				if (outline >= (PAGE_ARRAY_ROWS - 1))
-					pflush(FLUSH_SIZE);
-				outline++;
-				continue;
-			case '8':
-				if (outline >= 1)
-					outline--;
-				continue;
-			case '7':
-				outline -= 2;
-				if (outline < 0)
-					outline = 0;
-				continue;
-			default:
+		case 033:	/* ESC */
+			c = getwc(ctl->f);
+			if (c == L'8') {
+				col = rubchars(ctl, col, 1);
 				continue;
 			}
-		case '\b':
-			if (outcol)
-				outcol--;
-			continue;
-		case '\t':
-			outcol += 8;
-			outcol &= ~7;
-			outcol--;
-			c = ' ';
-			/* fallthrough */
-		default:
-			w = wcwidth(c);
-			if (w < 0)
-				continue;
-			if (outcol + w > PAGE_ARRAY_COLS) {
-				outcol++;
+			if (c == L'7') {
+				col = rubchars(ctl, col, 2);
 				continue;
 			}
-			cp = &page[outline][outcol];
-			outcol += w;
-			if (c == '_') {
-				if (suppresul)
-					continue;
-				cp += PAGE_ARRAY_COLS;
-				c = '-';
+			continue;
+		case WEOF:
+			ctl->print_nl = 0;
+			output_lines(ctl, col);
+			return;
+		case L'\n':
+			output_lines(ctl, col);
+			col = -1;
+			continue;
+		case L'\t':
+			for (/* nothing */; col % 8 && col < OUTPUT_COLS; col++) {
+				ctl->line[col] = L' ';
 			}
-			if (*cp == 0) {
-				/* trick! */
-				for (i = 0; i < w; i++)
-					cp[i] = c;
-				dp = cp - (outcol - w);
-				for (cp--; cp >= dp && *cp == 0; cp--)
-					*cp = ' ';
-			} else {
-				if (plus(c, *cp) || plus(*cp, c))
-					*cp = '+';
-				else if (*cp == ' ' || *cp == 0) {
-					for (i = 1; i < w; i++)
-						if (cp[i] != ' ' && cp[i] != 0)
-							continue;
-					for (i = 0; i < w; i++)
-						cp[i] = c;
-				}
+			col--;
+			continue;
+		case L'_':
+			ctl->line[col] = L' ';
+			if (!ctl->no_underlining) {
+				ctl->need_line_under = 1;
+				ctl->line_under[col] = L'-';
 			}
 			continue;
+		default:
+			if (!iswprint(c)) {
+				col--;
+				continue;
+			}
+			ctl->print_nl = 1;
+			ctl->line[col] = c;
 		}
 	}
 }
 
-int plus(wchar_t c, wchar_t d)
+int main(int argc, char **argv)
 {
+	struct colcrt_control ctl = { 0 };
+	int opt;
+	enum { NO_UL_OPTION = CHAR_MAX + 1 };
 
-	return (c == '|' && (d == '-' || d == '_'));
-}
-
-int first;
-
-void pflush(int ol)
-{
-	register int i;
-	register wchar_t *cp;
-	char lastomit;
-	int l, w;
+	static const struct option longopts[] = {
+		{"no-underlining", no_argument, NULL, NO_UL_OPTION},
+		{"half-lines", no_argument, NULL, '2'},
+		{"version", no_argument, NULL, 'V'},
+		{"help", no_argument, NULL, 'h'},
+		{NULL, 0, NULL, 0}
+	};
 
-	l = ol;
-	lastomit = 0;
-	if (l > (PAGE_ARRAY_ROWS - 1))
-		l = PAGE_ARRAY_ROWS - 1;
-	else
-		l |= 1;
-	for (i = first | 1; i < l; i++) {
-		move(i, i - 1);
-		move(i, i + 1);
-	}
-	for (i = first; i < l; i++) {
-		cp = page[i];
-		if (printall == 0 && lastomit == 0 && *cp == 0) {
-			lastomit = 1;
-			continue;
-		}
-		lastomit = 0;
-		while (*cp) {
-			if ((w = wcwidth(*cp)) > 0) {
-				putwchar(*cp);
-				cp += w;
-			} else
-				cp++;
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+	atexit(close_stdout);
+	/* Take care of lonely hyphen option. */
+	for (opt = 0; opt < argc; opt++)
+		if (argv[opt][0] == '-' && argv[opt][1] == '\0') {
+			ctl.no_underlining = 1;
+			argc--;
+			memmove(argv + opt, argv + opt + 1,
+				sizeof(char *) * (argc - opt));
+			opt--;
 		}
-		putwchar('\n');
-	}
-	memmove(page, page[ol], (PAGE_ARRAY_ROWS - ol) * PAGE_ARRAY_COLS * sizeof(wchar_t));
-	memset(page[PAGE_ARRAY_ROWS - ol], '\0', ol * PAGE_ARRAY_COLS * sizeof(wchar_t));
-	outline -= ol;
-	outcol = 0;
-	first = 1;
-}
-
-void move(int l, int m)
-{
-	register wchar_t *cp, *dp;
-
-	for (cp = page[l], dp = page[m]; *cp; cp++, dp++) {
-		switch (*cp) {
-			case '|':
-				if (*dp != ' ' && *dp != '|' && *dp != 0)
-					return;
-				break;
-			case ' ':
-				break;
-			default:
-				return;
+	while ((opt = getopt_long(argc, argv, "2Vh", longopts, NULL)) != -1)
+		switch (opt) {
+		case NO_UL_OPTION:
+			ctl.no_underlining = 1;
+			break;
+		case '2':
+			ctl.half_lines = 1;
+			break;
+		case 'V':
+			printf(UTIL_LINUX_VERSION);
+			return EXIT_SUCCESS;
+		case 'h':
+			usage(stdout);
+		default:
+			usage(stderr);
 		}
-	}
-	if (*cp == 0) {
-		for (cp = page[l], dp = page[m]; *cp; cp++, dp++)
-			if (*cp == '|')
-				*dp = '|';
-			else if (*dp == 0)
-				*dp = ' ';
-		page[l][0] = 0;
-	}
-}
-
-static void __attribute__ ((__noreturn__)) usage(FILE * out)
-{
-	fputs(USAGE_HEADER, out);
-	fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
-
-	fputs(USAGE_SEPARATOR, out);
-	fputs(_("Filter nroff output for CRT previewing.\n"), out);
-
-	fputs(USAGE_OPTIONS, out);
-	fputs(_(" -,  --no-underlining    suppress all underlining\n"), out);
-	fputs(_(" -2, --half-lines        print all half-lines\n"), out);
-
-	fputs(USAGE_SEPARATOR, out);
-	fputs(USAGE_HELP, out);
-	fputs(USAGE_VERSION, out);
-	fprintf(out, USAGE_MAN_TAIL("colcrt(1)"));
-
-	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+	argc -= optind;
+	argv += optind;
+	do {
+		wmemset(ctl.line, L'\0', OUTPUT_COLS);
+		wmemset(ctl.line_under, L' ', OUTPUT_COLS);
+		if (argc > 0) {
+			if (!(ctl.f = fopen(argv[0], "r")))
+				err(EXIT_FAILURE, _("cannot open %s"), argv[0]);
+			argc--;
+			argv++;
+		} else
+			ctl.f = stdin;
+		colcrt(&ctl);
+		if (ctl.f != stdin)
+			fclose(ctl.f);
+	} while (argc > 0);
+	return EXIT_SUCCESS;
 }
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux