]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - text-utils/colcrt.c
misc: cosmetics, remove argument from usage(FILE*)
[thirdparty/util-linux.git] / text-utils / colcrt.c
index 3cf25cbbe70ff11737d4f0488f383392fe7f1cbd..375d0d6f0c826da641edab4ef893744178a15cc1 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2016 Sami Kerola <kerolasa@iki.fi>
+ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
+ *
  * Copyright (c) 1980, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
 
 /*
  * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
- *     added Native Language Support
+ *     added Native Language Support
  * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
- *     modified to work correctly in multi-byte locales
+ *     modified to work correctly in multi-byte locales
  */
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>            /* for close() */
-#include <string.h>
 #include <getopt.h>
-#include "nls.h"
 
-#include "widechar.h"
+#include "nls.h"
 #include "c.h"
+#include "widechar.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 +63,233 @@ 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][PAGE_ARRAY_COLS];
+enum { OUTPUT_COLS = 132 };
 
-int    outline = 1;
-int    outcol;
+struct colcrt_control {
+       FILE            *f;
+       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;
+};
 
-char   suppresul;
-char   printall;
+static void __attribute__((__noreturn__)) usage(void)
+{
+       FILE *out = stdout;
+       fputs(USAGE_HEADER, out);
+       fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
 
-void colcrt(FILE *f);
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Filter nroff output for CRT previewing.\n"), out);
 
-int main(int argc, char **argv) {
-       FILE *f;
-       int i, opt;
-       enum { NO_UL_OPTION = CHAR_MAX + 1 };
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -,  --no-underlining    suppress all underlining\n"), out);
+       fputs(_(" -2, --half-lines        print all half-lines\n"), out);
 
-       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}
-       };
+       fputs(USAGE_SEPARATOR, out);
+       fputs(USAGE_HELP, out);
+       fputs(USAGE_VERSION, out);
 
-       setlocale(LC_ALL, "");
-       bindtextdomain(PACKAGE, LOCALEDIR);
-       textdomain(PACKAGE);
-       atexit(close_stdout);
+       fprintf(out, USAGE_MAN_TAIL("colcrt(1)"));
 
-       /* 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--;
-               }
+       exit(EXIT_SUCCESS);
+}
 
-       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;
+static void trim_trailing_spaces(wchar_t *s)
+{
+       size_t size;
+       wchar_t *end;
 
-       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;
+       size = wcslen(s);
+       if (!size)
+               return;
+       end = s + size - 1;
+       while (s <= end && iswspace(*end))
+               end--;
+       *(end + 1) = L'\0';
+}
+
+
+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;
+       long old_pos;
+
+       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;
+                       old_pos = ftell(ctl->f);
+                       while ((c = getwc(ctl->f)) != L'\n') {
+                               long new_pos = ftell(ctl->f);
+                               if (old_pos == new_pos)
+                                       fseek(ctl->f, 1, SEEK_CUR);
+                               else
+                                       old_pos = new_pos;
+                               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 = { NULL };
+       int opt;
+       enum { NO_UL_OPTION = CHAR_MAX + 1 };
 
-       return (c == '|' && (d == '-' || d == '_'));
-}
+       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}
+       };
 
-int first;
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+       atexit(close_stdout);
 
-void pflush(int ol)
-{
-       register int i;
-       register wchar_t *cp;
-       char lastomit;
-       int l, w;
-
-       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++;
+       /* 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();
+               default:
+                       errtryhelp(EXIT_FAILURE);
                }
        }
-       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);
+       argc -= optind;
+       argv += optind;
 
-       fputs(USAGE_SEPARATOR, out);
-       fputs(_("Filter nroff output for CRT previewing.\n"), out);
+       do {
+               wmemset(ctl.line, L'\0', OUTPUT_COLS);
+               wmemset(ctl.line_under, L' ', OUTPUT_COLS);
 
-       fputs(USAGE_OPTIONS, out);
-       fputs(_(" -,  --no-underlining    suppress all underlining\n"), out);
-       fputs(_(" -2, --half-lines        print all half-lines\n"), out);
+               if (argc > 0) {
+                       if (!(ctl.f = fopen(*argv, "r")))
+                               err(EXIT_FAILURE, _("cannot open %s"), *argv);
+                       argc--;
+                       argv++;
+               } else
+                       ctl.f = stdin;
 
-       fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
-       fprintf(out, USAGE_MAN_TAIL("colcrt(1)"));
+               colcrt(&ctl);
+               if (ctl.f != stdin)
+                       fclose(ctl.f);
+       } while (argc > 0);
 
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+       return EXIT_SUCCESS;
 }