BSD 4_3_Net_2 development
[unix-history] / usr / src / usr.bin / groff / grotty / grotty.cc
// -*- C++ -*-
/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
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 1, 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 groff; see the file LICENSE. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "driver.h"
#ifndef USHRT_MAX
#define USHRT_MAX 65535
#endif
#define DEFAULT_LINES_PER_PAGE 66
#define TAB_WIDTH 8
static int horizontal_tab_flag = 0;
static int form_feed_flag = 0;
static int bold_flag = 1;
static int underline_flag = 1;
static int overstrike_flag = 1;
enum { UNDERLINE_MODE = 01, BOLD_MODE = 02 };
// Mode to use for bold-underlining.
static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
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 *s = f->get_internal_name();
long n;
if (s != 0 && (n = strtol(s, 0, 0)) != 0)
f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE));
if (!underline_flag)
f->mode &= ~UNDERLINE_MODE;
if (!bold_flag)
f->mode &= ~BOLD_MODE;
if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
f->mode = (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
// hpos and vpos must be non-adjacent, to work round a bug in g++ 1.37.1
struct glyph {
unsigned short hpos;
unsigned short serial;
unsigned short vpos;
unsigned char code;
unsigned char mode;
};
class tty_printer : public printer {
enum { INITIAL_VEC_SIZE = 32 };
glyph *vec;
int vec_used;
int vec_size;
int lines_per_page;
int columns_per_page;
public:
tty_printer();
~tty_printer();
void set_char(int, font *, const environment *, int);
void begin_page(int) { }
void end_page();
font *make_font(const char *);
};
tty_printer::tty_printer()
: vec_used(0), vec_size(0), vec(0)
{
if (font::paperlength == 0)
lines_per_page = DEFAULT_LINES_PER_PAGE;
else if (font::paperlength % font::vert != 0)
fatal("paperlength not a multiple of vertical resolution");
else
lines_per_page = font::paperlength/font::vert;
if (lines_per_page > USHRT_MAX || lines_per_page <= 0)
fatal("ridiculous paperlength");
columns_per_page = font::paperwidth/font::hor;
// If columns_per_page is zero, we won't truncate.
if (columns_per_page < 0)
columns_per_page = 0;
}
tty_printer::~tty_printer()
{
delete vec;
}
void tty_printer::set_char(int i, font *f, const environment *env, int w)
{
int h = env->hpos;
if (h % font::hor != 0)
fatal("horizontal position not a multiple of horizontal resolution");
h /= font::hor;
if (h < 0) {
error("character to the left of first column discarded");
return;
}
if (columns_per_page != 0 && h >= columns_per_page) {
error("character to the right of last column discarded");
return;
}
if (h > USHRT_MAX) {
error("character with ridiculously large horizontal position discarded");
return;
}
int v = env->vpos;
if (v % font::vert != 0)
fatal("vertical position not a multiple of vertical resolution");
v /= font::vert;
// Note that the first output line corresponds to groff position font::vert.
if (v <= 0) {
error("character above first line discarded");
return;
}
if (v > lines_per_page) {
error("character below last line discarded");
return;
}
if (w != font::hor)
fatal("width of character not equal to horizontal resolution");
if (vec_used >= vec_size) {
if (vec_size == 0)
vec_size = INITIAL_VEC_SIZE;
else {
if (vec_size > USHRT_MAX/2) {
if (vec_size >= USHRT_MAX)
fatal("too many characters on the page");
vec_size = USHRT_MAX;
}
else
vec_size *= 2;
}
glyph *old_vec = vec;
vec = new glyph [vec_size];
if (vec_used)
memcpy(vec, old_vec, vec_used*sizeof(glyph));
delete old_vec;
}
// We need a stable sort, but qsort is not stable, so we fake it.
vec[vec_used].serial = vec_used;
vec[vec_used].hpos = h;
vec[vec_used].vpos = v;
vec[vec_used].code = f->get_code(i);
vec[vec_used].mode = ((tty_font *)f)->get_mode();
vec_used++;
}
extern "C" {
static int compare_glyph(void *p1, void *p2)
{
int v1 = ((glyph *)p1)->vpos;
int v2 = ((glyph *)p2)->vpos;
if (v1 < v2)
return -1;
if (v1 > v2)
return 1;
int h1 = ((glyph *)p1)->hpos;
int h2 = ((glyph *)p2)->hpos;
if (h1 < h2)
return -1;
if (h1 > h2)
return 1;
if (((glyph *)p1)->serial < ((glyph *)p2)->serial)
return -1;
return 1;
}
}
void tty_printer::end_page()
{
qsort(vec, vec_used, sizeof(glyph), compare_glyph);
int hpos = 0;
int vpos = 1;
// We have already discarded characters with vpos < 1 or > lines_per_page.
for (int i = 0; i < vec_used; i++) {
assert(vpos <= vec[i].vpos);
if (!overstrike_flag
&& i + 1 < vec_used
&& vec[i].hpos == vec[i + 1].hpos
&& vec[i].vpos == vec[i + 1].vpos)
continue;
for (; vpos < vec[i].vpos; vpos++) {
putchar('\n');
hpos = 0;
}
if (hpos > vec[i].hpos) {
putchar('\b');
hpos--;
}
else {
if (horizontal_tab_flag) {
for (;;) {
int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
if (next_tab_pos > vec[i].hpos)
break;
putchar('\t');
hpos = next_tab_pos;
}
}
for (; hpos < vec[i].hpos; hpos++)
putchar(' ');
}
assert(hpos == vec[i].hpos && vpos == vec[i].vpos);
if (isalnum(vec[i].code) && vec[i].mode & UNDERLINE_MODE) {
putchar('_');
putchar('\b');
}
if (vec[i].mode & BOLD_MODE) {
putchar(vec[i].code);
putchar('\b');
}
putchar(vec[i].code);
hpos++;
}
if (form_feed_flag) {
if (hpos != 0) {
putchar('\n');
vpos++;
}
if (vpos <= lines_per_page)
putchar('\f');
}
else {
for (; vpos <= lines_per_page; vpos++)
putchar('\n');
}
vec_used = 0;
}
font *tty_printer::make_font(const char *nm)
{
return tty_font::load_tty_font(nm);
}
printer *make_printer()
{
return new tty_printer;
}
static void usage();
int main(int argc, char **argv)
{
program_name = argv[0];
static char stderr_buf[BUFSIZ];
setbuf(stderr, stderr_buf);
int c;
while ((c = getopt(argc, argv, "F:vhfbuoBU")) != EOF)
switch(c) {
case 'v':
{
extern const char *version_string;
fprintf(stderr, "grotty version %s\n", version_string);
fflush(stderr);
break;
}
case 'b':
// Do not embolden by overstriking.
bold_flag = 0;
break;
case 'u':
// Do not underline.
underline_flag = 0;
break;
case 'o':
// Do not overstrike (other than emboldening and underlining).
overstrike_flag = 0;
break;
case 'B':
// Do bold-underlining as bold.
bold_underline_mode = BOLD_MODE;
break;
case 'U':
// Do bold-underlining as underlining.
bold_underline_mode = UNDERLINE_MODE;
break;
case 'h':
// Use horizontal tabs.
horizontal_tab_flag = 1;
break;
case 'f':
form_feed_flag = 1;
break;
case 'F':
font::command_line_font_dir(optarg);
break;
case '?':
usage();
break;
default:
assert(0);
}
if (optind >= argc)
do_file("-");
else {
for (int i = optind; i < argc; i++)
do_file(argv[i]);
}
delete pr;
exit(0);
}
static void usage()
{
fprintf(stderr, "usage: %s [-hfvbuoBU] [-F dir] [files ...]\n",
program_name);
exit(1);
}