+// -*- 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);
+}