summaryrefslogtreecommitdiffstats
path: root/text-utils/colcrt.c
diff options
context:
space:
mode:
authorSami Kerola2015-09-23 10:42:58 +0200
committerSami Kerola2016-02-02 18:27:28 +0100
commitea4ccc9ea1a1fc5ff5b4ef39ef8d438aeb7fd937 (patch)
treef03165103823a64e5b4d1af0fb682216a6b62405 /text-utils/colcrt.c
parenttests: add colcrt functional tests (diff)
downloadkernel-qcow2-util-linux-ea4ccc9ea1a1fc5ff5b4ef39ef8d438aeb7fd937.tar.gz
kernel-qcow2-util-linux-ea4ccc9ea1a1fc5ff5b4ef39ef8d438aeb7fd937.tar.xz
kernel-qcow2-util-linux-ea4ccc9ea1a1fc5ff5b4ef39ef8d438aeb7fd937.zip
colcrt: reimplementation
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@iki.fi>
Diffstat (limited to 'text-utils/colcrt.c')
-rw-r--r--text-utils/colcrt.c419
1 files changed, 176 insertions, 243 deletions
diff --git a/text-utils/colcrt.c b/text-utils/colcrt.c
index be7f84795..b54da8beb 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;
}