]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
colcrt: reimplementation
authorSami Kerola <kerolasa@iki.fi>
Wed, 23 Sep 2015 08:42:58 +0000 (09:42 +0100)
committerSami Kerola <kerolasa@iki.fi>
Tue, 2 Feb 2016 17:27:28 +0000 (17:27 +0000)
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>
text-utils/colcrt.c

index be7f8479586064db89f99506b603c123e59b29e1..b54da8bebd3bcd60555bac06d6d3e63135cfac64 100644 (file)
 
 #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;
 }