blob: 6113880e40a9044fa2e1161b77485fa797cf667c [file] [log] [blame]
/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
* added Native Language Support
* 1999-09-19 Bruno Haible <haible@clisp.cons.org>
* modified to work correctly in multi-byte locales
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "nls.h"
#include "c.h"
#include "widechar.h"
#include "closestream.h"
/*
* colcrt - replaces col for crts with new nroff esp. when using tbl.
* Bill Joy UCB July 14, 1977
*
* 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.
*
* Option - suppresses all underlining.
* Option -2 forces printing of all half lines.
*/
enum { OUTPUT_COLS = 132 };
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;
};
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
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);
printf(USAGE_HELP_OPTIONS(25));
printf(USAGE_MAN_TAIL("colcrt(1)"));
exit(EXIT_SUCCESS);
}
static void trim_trailing_spaces(wchar_t *s)
{
size_t size;
wchar_t *end;
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);
}
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;
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 033: /* ESC */
c = getwc(ctl->f);
if (c == L'8') {
col = rubchars(ctl, col, 1);
continue;
}
if (c == L'7') {
col = rubchars(ctl, col, 2);
continue;
}
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' ';
}
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 main(int argc, char **argv)
{
struct colcrt_control ctl = { NULL };
int opt;
enum { NO_UL_OPTION = CHAR_MAX + 1 };
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}
};
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--;
}
}
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);
}
}
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, "r")))
err(EXIT_FAILURE, _("cannot open %s"), *argv);
argc--;
argv++;
} else
ctl.f = stdin;
colcrt(&ctl);
if (ctl.f != stdin)
fclose(ctl.f);
} while (argc > 0);
return EXIT_SUCCESS;
}