/* Copyright (C) 1989-2021 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.com)
OSC 8 support by G. Branden Robinson
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or
(at your option) any later version.
groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "driver.h"
#include "device.h"
#include "ptable.h"
typedef signed char schar;
declare_ptable(schar)
implement_ptable(schar)
extern "C" const char *Version_string;
#define putstring(s) fputs(s, stdout)
#ifndef SHRT_MIN
#define SHRT_MIN (-32768)
#endif
#ifndef SHRT_MAX
#define SHRT_MAX 32767
#endif
#define TAB_WIDTH 8
// A character of the output device fits in a 32-bit word.
typedef unsigned int output_character;
static bool want_horizontal_tabs = false;
static bool want_form_feeds = false;
static bool want_emboldening_by_overstriking = true;
static bool do_bold;
static bool want_italics_by_underlining = true;
static bool do_underline;
static bool want_glyph_composition_by_overstriking = true;
static bool allow_drawing_commands = true;
static bool want_sgr_italics = false;
static bool do_sgr_italics;
static bool want_reverse_video_for_italics = false;
static bool do_reverse_video;
static bool use_overstriking_drawing_scheme = false;
static void update_options();
static void usage(FILE *stream);
static int hline_char = '-';
static int vline_char = '|';
enum {
UNDERLINE_MODE = 0x01,
BOLD_MODE = 0x02,
VDRAW_MODE = 0x04,
HDRAW_MODE = 0x08,
CU_MODE = 0x10,
COLOR_CHANGE = 0x20,
START_LINE = 0x40,
END_LINE = 0x80
};
// Mode to use for bold-underlining.
static unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
static unsigned char bold_underline_mode;
#ifndef IS_EBCDIC_HOST
#define CSI "\033["
#define OSC8 "\033]8"
#define ST "\033\\"
#else
#define CSI "\047["
#define OSC8 "\047]8"
#define ST "\047\\"
#endif
// SGR handling (ISO 6429)
#define SGR_BOLD CSI "1m"
#define SGR_NO_BOLD CSI "22m"
#define SGR_ITALIC CSI "3m"
#define SGR_NO_ITALIC CSI "23m"
#define SGR_UNDERLINE CSI "4m"
#define SGR_NO_UNDERLINE CSI "24m"
#define SGR_REVERSE CSI "7m"
#define SGR_NO_REVERSE CSI "27m"
// many terminals can't handle 'CSI 39 m' and 'CSI 49 m' to reset
// the foreground and background color, respectively; we thus use
// 'CSI 0 m' exclusively
#define SGR_DEFAULT CSI "0m"
#define DEFAULT_COLOR_IDX -1
class tty_font : public font {
tty_font(const char *);
unsigned char mode;
public:
~tty_font();
unsigned char get_mode() { return mode; }
#if 0
void handle_x_command(int argc, const char **argv);
#endif
static tty_font *load_tty_font(const char *);
};
tty_font *tty_font::load_tty_font(const char *s)
{
tty_font *f = new tty_font(s);
if (!f->load()) {
delete f;
return 0;
}
const char *num = f->get_internal_name();
long n;
if (num != 0 && (n = strtol(num, 0, 0)) != 0)
f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
if (!do_underline)
f->mode &= ~UNDERLINE_MODE;
if (!do_bold)
f->mode &= ~BOLD_MODE;
if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
f->mode = (unsigned char)((f->mode & ~(BOLD_MODE|UNDERLINE_MODE))
| bold_underline_mode);
return f;
}
tty_font::tty_font(const char *nm)
: font(nm), mode(0)
{
}
tty_font::~tty_font()
{
}
#if 0
void tty_font::handle_x_command(int argc, const char **argv)
{
if (argc >= 1 && strcmp(argv[0], "bold") == 0)
mode |= BOLD_MODE;
else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
mode |= UNDERLINE_MODE;
}
#endif
class tty_glyph {
public:
tty_glyph *next;
int w;
int hpos;
unsigned int code;
unsigned char mode;
schar back_color_idx;
schar fore_color_idx;
inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
inline int order() {
return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
};
class tty_printer : public printer {
tty_glyph **lines;
int nlines;
int cached_v;
int cached_vpos;
schar curr_fore_idx;
schar curr_back_idx;
bool is_underlining;
bool is_boldfacing;
bool is_continuously_underlining;
PTABLE(schar) tty_colors;
void make_underline(int);
void make_bold(output_character, int);
schar color_to_idx(color *);
void add_char(output_character, int, int, int, color *, color *,
unsigned char);
void simple_add_char(const output_character, const environment *);
char *make_rgb_string(unsigned int, unsigned int, unsigned int);
bool tty_color(unsigned int, unsigned int, unsigned int, schar *,
schar = DEFAULT_COLOR_IDX);
void line(int, int, int, int, color *, color *);
void draw_line(int *, int, const environment *);
void draw_polygon(int *, int, const environment *);
void special_link(const char *, const environment *);
public:
tty_printer();
~tty_printer();
void set_char(glyph *, font *, const environment *, int, const char *);
void draw(int, int *, int, const environment *);
void special(char *, const environment *, char);
void change_color(const environment * const);
void change_fill_color(const environment * const);
void put_char(output_character);
void put_color(schar, int);
void begin_page(int) { }
void end_page(int);
font *make_font(const char *);
};
char *tty_printer::make_rgb_string(unsigned int r,
unsigned int g,
unsigned int b)
{
char *s = new char[8];
s[0] = char(r >> 8);
s[1] = char(r & 0xff);
s[2] = char(g >> 8);
s[3] = char(g & 0xff);
s[4] = char(b >> 8);
s[5] = char(b & 0xff);
s[6] = char(0x80);
s[7] = 0;
// avoid null-bytes in string
for (int i = 0; i < 6; i++)
if (!s[i]) {
s[i] = 1;
s[6] |= 1 << i;
}
return s;
}
bool tty_printer::tty_color(unsigned int r,
unsigned int g,
unsigned int b, schar *idx, schar value)
{
bool is_known_color = true;
char *s = make_rgb_string(r, g, b);
schar *i = tty_colors.lookup(s);
if (!i) {
is_known_color = false;
i = new schar[1];
*i = value;
tty_colors.define(s, i);
}
*idx = *i;
delete[] s;
return is_known_color;
}
tty_printer::tty_printer() : cached_v(0)
{
if (font::is_unicode) {
hline_char = 0x2500;
vline_char = 0x2502;
}
schar dummy;
// black, white
(void)tty_color(0, 0, 0, &dummy, 0);
(void)tty_color(color::MAX_COLOR_VAL,
color::MAX_COLOR_VAL,
color::MAX_COLOR_VAL, &dummy, 7);
// red, green, blue
(void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
(void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
(void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
// yellow, magenta, cyan
(void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
(void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
(void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
nlines = 66;
lines = new tty_glyph *[nlines];
for (int i = 0; i < nlines; i++)
lines[i] = 0;
is_continuously_underlining = false;
}
tty_printer::~tty_printer()
{
delete[] lines;
}
void tty_printer::make_underline(int w)
{
if (use_overstriking_drawing_scheme) {
if (!w)
warning("can't underline zero-width character");
else {
putchar('_');
putchar('\b');
}
}
else {
if (!is_underlining) {
if (do_sgr_italics)
putstring(SGR_ITALIC);
else if (do_reverse_video)
putstring(SGR_REVERSE);
else
putstring(SGR_UNDERLINE);
}
is_underlining = true;
}
}
void tty_printer::make_bold(output_character c, int w)
{
if (use_overstriking_drawing_scheme) {
if (!w)
warning("can't print zero-width character in bold");
else {
put_char(c);
putchar('\b');
}
}
else {
if (!is_boldfacing)
putstring(SGR_BOLD);
is_boldfacing = true;
}
}
schar tty_printer::color_to_idx(color *col)
{
if (col->is_default())
return DEFAULT_COLOR_IDX;
unsigned int r, g, b;
col->get_rgb(&r, &g, &b);
schar idx;
if (!tty_color(r, g, b, &idx)) {
char *s = col->print_color();
error("unrecognized color '%1' mapped to default", s);
delete[] s;
}
return idx;
}
void tty_printer::set_char(glyph *g, font *f, const environment *env,
int w, const char *)
{
if (w % font::hor != 0)
fatal("glyph width is not a multiple of horizontal motion quantum");
add_char(f->get_code(g), w,
env->hpos, env->vpos,
env->col, env->fill,
((tty_font *)f)->get_mode());
}
void tty_printer::add_char(output_character c, int w,
int h, int v,
color *fore, color *back,
unsigned char mode)
{
#if 0
// This is too expensive.
if (h % font::hor != 0)
fatal("horizontal position not a multiple of horizontal motion quantum");
#endif
int hpos = h / font::hor;
if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
error("character with ridiculous horizontal position discarded");
return;
}
int vpos;
if (v == cached_v && cached_v != 0)
vpos = cached_vpos;
else {
if (v % font::vert != 0)
fatal("vertical position not a multiple of vertical motion"
" quantum");
vpos = v / font::vert;
if (vpos > nlines) {
tty_glyph **old_lines = lines;
lines = new tty_glyph *[vpos + 1];
memcpy(lines, old_lines, nlines * sizeof(tty_glyph *));
for (int i = nlines; i <= vpos; i++)
lines[i] = 0;
delete[] old_lines;
nlines = vpos + 1;
}
// Note that the first output line corresponds to groff
// position font::vert.
if (vpos <= 0) {
error("output above first line discarded");
return;
}
cached_v = v;
cached_vpos = vpos;
}
tty_glyph *g = new tty_glyph;
g->w = w;
g->hpos = hpos;
g->code = c;
g->fore_color_idx = color_to_idx(fore);
g->back_color_idx = color_to_idx(back);
g->mode = mode;
// The list will be reversed later. After reversal, it must be in
// increasing order of hpos, with COLOR_CHANGE and CU specials before
// HDRAW characters before VDRAW characters before normal characters
// at each hpos, and otherwise in order of occurrence.
tty_glyph **pp;
for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
if ((*pp)->hpos < hpos
|| ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
break;
g->next = *pp;
*pp = g;
}
void tty_printer::simple_add_char(const output_character c,
const environment *env)
{
add_char(c, 0, env->hpos, env->vpos, env->col, env->fill, 0);
}
void tty_printer::special(char *arg, const environment *env, char type)
{
if (type == 'u') {
add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
CU_MODE);
return;
}
if (type != 'p')
return;
char *p;
for (p = arg; *p == ' ' || *p == '\n'; p++)
;
char *tag = p;
for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
;
if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
error("X command without 'tty:' tag ignored");
return;
}
p++;
for (; *p == ' ' || *p == '\n'; p++)
;
char *command = p;
for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
;
if (*command == '\0') {
error("empty X command ignored");
return;
}
if (strncmp(command, "link", p - command) == 0)
special_link(p, env);
else
warning("unrecognized X command '%1' ignored", command);
}
// Produce an OSC 8 hyperlink. Given ditroff input of the form:
// x X tty: link [URI[ KEY=VALUE] ...]
// produce "OSC 8 [;KEY=VALUE];[URI] ST". KEY/VALUE pairs can be
// repeated arbitrarily and are separated by colons. Omission of the
// URI ends the hyperlink that was begun by specifying it. See
// .
void tty_printer::special_link(const char *arg, const environment *env)
{
static bool is_link_active = false;
if (use_overstriking_drawing_scheme)
return;
for (const char *s = OSC8; *s != '\0'; s++)
simple_add_char(*s, env);
simple_add_char(';', env);
char c = *arg;
if ('\0' == c || '\n' == c) {
simple_add_char(';', env);
if (!is_link_active)
warning("ending hyperlink when none was started");
is_link_active = false;
}
else {
// Our caller ensures that we see white space after 'link'.
assert(c == ' ' || c == '\t');
if (is_link_active) {
warning("new hyperlink started without ending previous one;"
" recovering");
simple_add_char(';', env);
for (const char *s = ST OSC8; *s != '\0'; s++)
simple_add_char(*s, env);
simple_add_char(';', env);
}
is_link_active = true;
do
c = *arg++;
while (c == ' ' || c == '\t');
arg--;
// The first argument is the URI.
const char *uri = arg;
do
c = *arg++;
while (c != '\0' && c != ' ' && c != '\t');
arg--;
ptrdiff_t uri_len = arg - uri;
// Any remaining arguments are "key=value" pairs.
const char *pair = 0;
bool done = false;
do {
if (pair != 0)
simple_add_char(':', env);
pair = arg;
bool in_pair = true;
do {
c = *arg++;
if ('\0' == c)
done = true;
else if (' ' == c || '\t' == c)
in_pair = false;
else
simple_add_char(c, env);
} while (!done && in_pair);
} while (!done);
simple_add_char(';', env);
for (size_t i = uri_len; i > 0; i--)
simple_add_char(*uri++, env);
}
for (const char *s = ST; *s != '\0'; s++)
simple_add_char(*s, env);
}
void tty_printer::change_color(const environment * const env)
{
add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
}
void tty_printer::change_fill_color(const environment * const env)
{
add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
}
void tty_printer::draw(int code, int *p, int np, const environment *env)
{
if (!allow_drawing_commands)
return;
if (code == 'l')
draw_line(p, np, env);
else if (code == 'p')
draw_polygon(p, np, env);
else
warning("ignoring unsupported drawing command '%1'", char(code));
}
void tty_printer::draw_polygon(int *p, int np, const environment *env)
{
if (np & 1) {
error("even number of arguments required for polygon");
return;
}
if (np == 0) {
error("no arguments for polygon");
return;
}
// We only draw polygons which consist entirely of horizontal and
// vertical lines.
int hpos = 0;
int vpos = 0;
for (int i = 0; i < np; i += 2) {
if (!(p[i] == 0 || p[i + 1] == 0))
return;
hpos += p[i];
vpos += p[i + 1];
}
if (!(hpos == 0 || vpos == 0))
return;
int start_hpos = env->hpos;
int start_vpos = env->vpos;
hpos = start_hpos;
vpos = start_vpos;
for (int i = 0; i < np; i += 2) {
line(hpos, vpos, p[i], p[i + 1], env->col, env->fill);
hpos += p[i];
vpos += p[i + 1];
}
line(hpos, vpos, start_hpos - hpos, start_vpos - vpos,
env->col, env->fill);
}
void tty_printer::draw_line(int *p, int np, const environment *env)
{
if (np != 2) {
error("2 arguments required for line");
return;
}
line(env->hpos, env->vpos, p[0], p[1], env->col, env->fill);
}
void tty_printer::line(int hpos, int vpos, int dx, int dy,
color *col, color *fill)
{
// XXX: zero-length lines get drawn as '+' crossings in nroff, even
// when there is no crossing, but they nevertheless occur frequently
// in input. Does tbl produce them?
#if 0
if (0 == dx)
fatal("cannot draw zero-length horizontal line");
if (0 == dy)
fatal("cannot draw zero-length vertical line");
#endif
if ((dx != 0) && (dy != 0))
warning("cannot draw diagonal line");
if (dx % font::hor != 0)
fatal("length of horizontal line %1 is not a multiple of horizontal"
" motion quantum %2", dx, font::hor);
if (dy % font::vert != 0)
fatal("length of vertical line %1 is not a multiple of vertical"
" motion quantum %2", dy, font::vert);
if (dx == 0) {
// vertical line
int v = vpos;
int len = dy;
if (len < 0) {
v += len;
len = -len;
}
if (len == 0)
add_char(vline_char, font::hor, hpos, v, col, fill,
VDRAW_MODE|START_LINE|END_LINE);
else {
add_char(vline_char, font::hor, hpos, v, col, fill,
VDRAW_MODE|START_LINE);
len -= font::vert;
v += font::vert;
while (len > 0) {
add_char(vline_char, font::hor, hpos, v, col, fill,
VDRAW_MODE|START_LINE|END_LINE);
len -= font::vert;
v += font::vert;
}
add_char(vline_char, font::hor, hpos, v, col, fill,
VDRAW_MODE|END_LINE);
}
}
if (dy == 0) {
// horizontal line
int h = hpos;
int len = dx;
if (len < 0) {
h += len;
len = -len;
}
if (len == 0)
add_char(hline_char, font::hor, h, vpos, col, fill,
HDRAW_MODE|START_LINE|END_LINE);
else {
add_char(hline_char, font::hor, h, vpos, col, fill,
HDRAW_MODE|START_LINE);
len -= font::hor;
h += font::hor;
while (len > 0) {
add_char(hline_char, font::hor, h, vpos, col, fill,
HDRAW_MODE|START_LINE|END_LINE);
len -= font::hor;
h += font::hor;
}
add_char(hline_char, font::hor, h, vpos, col, fill,
HDRAW_MODE|END_LINE);
}
}
}
void tty_printer::put_char(output_character wc)
{
if (font::is_unicode && wc >= 0x80) {
char buf[6 + 1];
int count;
char *p = buf;
if (wc < 0x800)
count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
else if (wc < 0x10000)
count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
else if (wc < 0x200000)
count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
else if (wc < 0x4000000)
count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
else if (wc <= 0x7fffffff)
count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
else
return;
do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
while (count > 0);
*++p = '\0';
putstring(buf);
}
else
putchar(wc);
}
void tty_printer::put_color(schar color_index, int back)
{
if (color_index == DEFAULT_COLOR_IDX) {
putstring(SGR_DEFAULT);
// set bold and underline again
if (is_boldfacing)
putstring(SGR_BOLD);
if (is_underlining) {
if (do_sgr_italics)
putstring(SGR_ITALIC);
else if (do_reverse_video)
putstring(SGR_REVERSE);
else
putstring(SGR_UNDERLINE);
}
// set other color again
back = !back;
color_index = back ? curr_back_idx : curr_fore_idx;
}
if (color_index != DEFAULT_COLOR_IDX) {
putstring(CSI);
if (back)
putchar('4');
else
putchar('3');
putchar(color_index + '0');
putchar('m');
}
}
// The possible Unicode combinations for crossing characters.
//
// ' ' = 0, ' -' = 4, '- ' = 8, '--' = 12,
//
// ' ' = 0, ' ' = 1, '|' = 2, '|' = 3
// | |
static output_character crossings[4*4] = {
0x0000, 0x2577, 0x2575, 0x2502,
0x2576, 0x250C, 0x2514, 0x251C,
0x2574, 0x2510, 0x2518, 0x2524,
0x2500, 0x252C, 0x2534, 0x253C
};
void tty_printer::end_page(int page_length)
{
if (page_length % font::vert != 0)
error("vertical position at end of page not multiple of vertical"
" motion quantum");
int lines_per_page = page_length / font::vert;
int last_line;
for (last_line = nlines; last_line > 0; last_line--)
if (lines[last_line - 1])
break;
#if 0
if (last_line > lines_per_page) {
error("characters past last line discarded");
do {
--last_line;
while (lines[last_line]) {
tty_glyph *tem = lines[last_line];
lines[last_line] = tem->next;
delete tem;
}
} while (last_line > lines_per_page);
}
#endif
for (int i = 0; i < last_line; i++) {
tty_glyph *p = lines[i];
lines[i] = 0;
tty_glyph *g = 0;
while (p) {
tty_glyph *tem = p->next;
p->next = g;
g = p;
p = tem;
}
int hpos = 0;
tty_glyph *nextp;
curr_fore_idx = DEFAULT_COLOR_IDX;
curr_back_idx = DEFAULT_COLOR_IDX;
is_underlining = false;
is_boldfacing = false;
for (p = g; p; delete p, p = nextp) {
nextp = p->next;
if (p->mode & CU_MODE) {
is_continuously_underlining = (p->code != 0);
continue;
}
if (nextp && p->hpos == nextp->hpos) {
if (p->draw_mode() == HDRAW_MODE &&
nextp->draw_mode() == VDRAW_MODE) {
if (font::is_unicode)
nextp->code =
crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
+ ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
else
nextp->code = '+';
continue;
}
if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
nextp->code = p->code;
continue;
}
if (!want_glyph_composition_by_overstriking)
continue;
}
if (hpos > p->hpos) {
do {
putchar('\b');
hpos--;
} while (hpos > p->hpos);
}
else {
if (want_horizontal_tabs) {
for (;;) {
int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
if (next_tab_pos > p->hpos)
break;
if (is_continuously_underlining)
make_underline(p->w);
else if (!use_overstriking_drawing_scheme
&& is_underlining) {
if (do_sgr_italics)
putstring(SGR_NO_ITALIC);
else if (do_reverse_video)
putstring(SGR_NO_REVERSE);
else
putstring(SGR_NO_UNDERLINE);
is_underlining = false;
}
putchar('\t');
hpos = next_tab_pos;
}
}
for (; hpos < p->hpos; hpos++) {
if (is_continuously_underlining)
make_underline(p->w);
else if (!use_overstriking_drawing_scheme && is_underlining) {
if (do_sgr_italics)
putstring(SGR_NO_ITALIC);
else if (do_reverse_video)
putstring(SGR_NO_REVERSE);
else
putstring(SGR_NO_UNDERLINE);
is_underlining = false;
}
putchar(' ');
}
}
assert(hpos == p->hpos);
if (p->mode & COLOR_CHANGE) {
if (!use_overstriking_drawing_scheme) {
if (p->fore_color_idx != curr_fore_idx) {
put_color(p->fore_color_idx, 0);
curr_fore_idx = p->fore_color_idx;
}
if (p->back_color_idx != curr_back_idx) {
put_color(p->back_color_idx, 1);
curr_back_idx = p->back_color_idx;
}
}
continue;
}
if (p->mode & UNDERLINE_MODE)
make_underline(p->w);
else if (!use_overstriking_drawing_scheme && is_underlining) {
if (do_sgr_italics)
putstring(SGR_NO_ITALIC);
else if (do_reverse_video)
putstring(SGR_NO_REVERSE);
else
putstring(SGR_NO_UNDERLINE);
is_underlining = false;
}
if (p->mode & BOLD_MODE)
make_bold(p->code, p->w);
else if (!use_overstriking_drawing_scheme && is_boldfacing) {
putstring(SGR_NO_BOLD);
is_boldfacing = false;
}
if (!use_overstriking_drawing_scheme) {
if (p->fore_color_idx != curr_fore_idx) {
put_color(p->fore_color_idx, 0);
curr_fore_idx = p->fore_color_idx;
}
if (p->back_color_idx != curr_back_idx) {
put_color(p->back_color_idx, 1);
curr_back_idx = p->back_color_idx;
}
}
put_char(p->code);
hpos += p->w / font::hor;
}
if (!use_overstriking_drawing_scheme
&& (is_boldfacing || is_underlining
|| curr_fore_idx != DEFAULT_COLOR_IDX
|| curr_back_idx != DEFAULT_COLOR_IDX))
putstring(SGR_DEFAULT);
putchar('\n');
}
if (want_form_feeds) {
if (last_line < lines_per_page)
putchar('\f');
}
else {
for (; last_line < lines_per_page; last_line++)
putchar('\n');
}
}
font *tty_printer::make_font(const char *nm)
{
return tty_font::load_tty_font(nm);
}
printer *make_printer()
{
return new tty_printer();
}
static void update_options()
{
if (use_overstriking_drawing_scheme) {
do_sgr_italics = false;
do_reverse_video = false;
bold_underline_mode = bold_underline_mode_option;
do_bold = want_emboldening_by_overstriking;
do_underline = want_italics_by_underlining;
}
else {
do_sgr_italics = want_sgr_italics;
do_reverse_video = want_reverse_video_for_italics;
bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
do_bold = true;
do_underline = true;
}
}
int main(int argc, char **argv)
{
program_name = argv[0];
static char stderr_buf[BUFSIZ];
if (getenv("GROFF_NO_SGR"))
use_overstriking_drawing_scheme = true;
setbuf(stderr, stderr_buf);
setlocale(LC_CTYPE, "");
int c;
static const struct option long_options[] = {
{ "help", no_argument, 0, CHAR_MAX + 1 },
{ "version", no_argument, 0, 'v' },
{ NULL, 0, 0, 0 }
};
while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
!= EOF)
switch(c) {
case 'v':
printf("GNU grotty (groff) version %s\n", Version_string);
exit(EXIT_SUCCESS);
break;
case 'i':
// Use italic font instead of underlining.
want_sgr_italics = true;
break;
case 'I':
// ignore include search path
break;
case 'b':
// Do not embolden by overstriking.
want_emboldening_by_overstriking = false;
break;
case 'c':
// Use old scheme for emboldening and underline.
use_overstriking_drawing_scheme = true;
break;
case 'u':
// Do not underline.
want_italics_by_underlining = false;
break;
case 'o':
// Do not overstrike (other than emboldening and underlining).
want_glyph_composition_by_overstriking = false;
break;
case 'r':
// Use reverse mode instead of underlining.
want_reverse_video_for_italics = true;
break;
case 'B':
// Do bold-underlining as bold.
bold_underline_mode_option = BOLD_MODE;
break;
case 'U':
// Do bold-underlining as underlining.
bold_underline_mode_option = UNDERLINE_MODE;
break;
case 'h':
// Use horizontal tabs.
want_horizontal_tabs = true;
break;
case 'f':
want_form_feeds = true;
break;
case 'F':
font::command_line_font_dir(optarg);
break;
case 'd':
// Ignore \D commands.
allow_drawing_commands = false;
break;
case CHAR_MAX + 1: // --help
usage(stdout);
break;
case '?':
usage(stderr);
exit(EXIT_FAILURE);
break;
default:
assert(0 == "unhandled getopt_long return value");
}
update_options();
if (optind >= argc)
do_file("-");
else {
for (int i = optind; i < argc; i++)
do_file(argv[i]);
}
return 0;
}
static void usage(FILE *stream)
{
fprintf(stream,
"usage: %s [-bBcdfhioruU] [-F font-directory] [file ...]\n"
"usage: %s {-v | --version}\n"
"usage: %s --help\n",
program_name, program_name, program_name);
if (stdout == stream) {
fputs(
"\n"
"Translate the output of troff(1) into a form suitable for\n"
"typewriter‐like devices, including terminal emulators. See the\n"
"grotty(1) manual page.\n",
stream);
exit(EXIT_SUCCESS);
}
}
// Local Variables:
// fill-column: 72
// mode: C++
// End:
// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: