1// -*- C++ -*-
2/* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005
3   Free Software Foundation, Inc.
4     Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22#include "driver.h"
23#include "device.h"
24#include "ptable.h"
25
26typedef signed char schar;
27
28declare_ptable(schar)
29implement_ptable(schar)
30
31extern "C" const char *Version_string;
32
33#define putstring(s) fputs(s, stdout)
34
35#ifndef SHRT_MIN
36#define SHRT_MIN (-32768)
37#endif
38
39#ifndef SHRT_MAX
40#define SHRT_MAX 32767
41#endif
42
43#define TAB_WIDTH 8
44
45static int horizontal_tab_flag = 0;
46static int form_feed_flag = 0;
47static int bold_flag_option = 1;
48static int bold_flag;
49static int underline_flag_option = 1;
50static int underline_flag;
51static int overstrike_flag = 1;
52static int draw_flag = 1;
53static int italic_flag_option = 0;
54static int italic_flag;
55static int reverse_flag_option = 0;
56static int reverse_flag;
57static int old_drawing_scheme = 0;
58
59static void update_options();
60static void usage(FILE *stream);
61
62static int hline_char = '-';
63static int vline_char = '|';
64
65enum {
66  UNDERLINE_MODE = 0x01,
67  BOLD_MODE = 0x02,
68  VDRAW_MODE = 0x04,
69  HDRAW_MODE = 0x08,
70  CU_MODE = 0x10,
71  COLOR_CHANGE = 0x20,
72  START_LINE = 0x40,
73  END_LINE = 0x80
74};
75
76// Mode to use for bold-underlining.
77static unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
78static unsigned char bold_underline_mode;
79
80#ifndef IS_EBCDIC_HOST
81#define CSI "\033["
82#else
83#define CSI "\047["
84#endif
85
86// SGR handling (ISO 6429)
87#define SGR_BOLD CSI "1m"
88#define SGR_NO_BOLD CSI "22m"
89#define SGR_ITALIC CSI "3m"
90#define SGR_NO_ITALIC CSI "23m"
91#define SGR_UNDERLINE CSI "4m"
92#define SGR_NO_UNDERLINE CSI "24m"
93#define SGR_REVERSE CSI "7m"
94#define SGR_NO_REVERSE CSI "27m"
95// many terminals can't handle `CSI 39 m' and `CSI 49 m' to reset
96// the foreground and background color, respectively; we thus use
97// `CSI 0 m' exclusively
98#define SGR_DEFAULT CSI "0m"
99
100#define DEFAULT_COLOR_IDX -1
101
102class tty_font : public font {
103  tty_font(const char *);
104  unsigned char mode;
105public:
106  ~tty_font();
107  unsigned char get_mode() { return mode; }
108#if 0
109  void handle_x_command(int argc, const char **argv);
110#endif
111  static tty_font *load_tty_font(const char *);
112};
113
114tty_font *tty_font::load_tty_font(const char *s)
115{
116  tty_font *f = new tty_font(s);
117  if (!f->load()) {
118    delete f;
119    return 0;
120  }
121  const char *num = f->get_internal_name();
122  long n;
123  if (num != 0 && (n = strtol(num, 0, 0)) != 0)
124    f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
125  if (!underline_flag)
126    f->mode &= ~UNDERLINE_MODE;
127  if (!bold_flag)
128    f->mode &= ~BOLD_MODE;
129  if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
130    f->mode = (unsigned char)((f->mode & ~(BOLD_MODE|UNDERLINE_MODE))
131			      | bold_underline_mode);
132  return f;
133}
134
135tty_font::tty_font(const char *nm)
136: font(nm), mode(0)
137{
138}
139
140tty_font::~tty_font()
141{
142}
143
144#if 0
145void tty_font::handle_x_command(int argc, const char **argv)
146{
147  if (argc >= 1 && strcmp(argv[0], "bold") == 0)
148    mode |= BOLD_MODE;
149  else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
150    mode |= UNDERLINE_MODE;
151}
152#endif
153
154class glyph {
155  static glyph *free_list;
156public:
157  glyph *next;
158  int w;
159  int hpos;
160  unsigned int code;
161  unsigned char mode;
162  schar back_color_idx;
163  schar fore_color_idx;
164  void *operator new(size_t);
165  void operator delete(void *);
166  inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
167  inline int order() {
168    return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
169};
170
171glyph *glyph::free_list = 0;
172
173void *glyph::operator new(size_t)
174{
175  if (!free_list) {
176    const int BLOCK = 1024;
177    free_list = (glyph *)new char[sizeof(glyph) * BLOCK];
178    for (int i = 0; i < BLOCK - 1; i++)
179      free_list[i].next = free_list + i + 1;
180    free_list[BLOCK - 1].next = 0;
181  }
182  glyph *p = free_list;
183  free_list = free_list->next;
184  p->next = 0;
185  return p;
186}
187
188void glyph::operator delete(void *p)
189{
190  if (p) {
191    ((glyph *)p)->next = free_list;
192    free_list = (glyph *)p;
193  }
194}
195
196class tty_printer : public printer {
197  int is_utf8;
198  glyph **lines;
199  int nlines;
200  int cached_v;
201  int cached_vpos;
202  schar curr_fore_idx;
203  schar curr_back_idx;
204  int is_underline;
205  int is_bold;
206  int cu_flag;
207  PTABLE(schar) tty_colors;
208  void make_underline(int);
209  void make_bold(unsigned int, int);
210  schar color_to_idx(color *col);
211  void add_char(unsigned int, int, int, int, color *, color *, unsigned char);
212  char *make_rgb_string(unsigned int, unsigned int, unsigned int);
213  int tty_color(unsigned int, unsigned int, unsigned int, schar *,
214		schar = DEFAULT_COLOR_IDX);
215public:
216  tty_printer(const char *device);
217  ~tty_printer();
218  void set_char(int, font *, const environment *, int, const char *name);
219  void draw(int code, int *p, int np, const environment *env);
220  void special(char *arg, const environment *env, char type);
221  void change_color(const environment * const env);
222  void change_fill_color(const environment * const env);
223  void put_char(unsigned int);
224  void put_color(schar, int);
225  void begin_page(int) { }
226  void end_page(int page_length);
227  font *make_font(const char *);
228};
229
230char *tty_printer::make_rgb_string(unsigned int r,
231				   unsigned int g,
232				   unsigned int b)
233{
234  char *s = new char[8];
235  s[0] = char(r >> 8);
236  s[1] = char(r & 0xff);
237  s[2] = char(g >> 8);
238  s[3] = char(g & 0xff);
239  s[4] = char(b >> 8);
240  s[5] = char(b & 0xff);
241  s[6] = char(0x80);
242  s[7] = 0;
243  // avoid null-bytes in string
244  for (int i = 0; i < 6; i++)
245    if (!s[i]) {
246      s[i] = 1;
247      s[6] |= 1 << i;
248    }
249  return s;
250}
251
252int tty_printer::tty_color(unsigned int r,
253			   unsigned int g,
254			   unsigned int b, schar *idx, schar value)
255{
256  int unknown_color = 0;
257  char *s = make_rgb_string(r, g, b);
258  schar *i = tty_colors.lookup(s);
259  if (!i) {
260    unknown_color = 1;
261    i = new schar[1];
262    *i = value;
263    tty_colors.define(s, i);
264  }
265  *idx = *i;
266  a_delete s;
267  return unknown_color;
268}
269
270tty_printer::tty_printer(const char *dev) : cached_v(0)
271{
272  is_utf8 = !strcmp(dev, "utf8");
273  if (is_utf8) {
274    hline_char = 0x2500;
275    vline_char = 0x2502;
276  }
277  schar dummy;
278  // black, white
279  (void)tty_color(0, 0, 0, &dummy, 0);
280  (void)tty_color(color::MAX_COLOR_VAL,
281		  color::MAX_COLOR_VAL,
282		  color::MAX_COLOR_VAL, &dummy, 7);
283  // red, green, blue
284  (void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
285  (void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
286  (void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
287  // yellow, magenta, cyan
288  (void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
289  (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
290  (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
291  nlines = 66;
292  lines = new glyph *[nlines];
293  for (int i = 0; i < nlines; i++)
294    lines[i] = 0;
295  cu_flag = 0;
296}
297
298tty_printer::~tty_printer()
299{
300  a_delete lines;
301}
302
303void tty_printer::make_underline(int w)
304{
305  if (old_drawing_scheme) {
306    if (!w)
307      warning("can't underline zero-width character");
308    else {
309      int n = w / font::hor;
310      for (int i = 0; i < n; i++)
311	putchar('_');
312      for (int j = 0; j < n; j++)
313	putchar('\b');
314    }
315  }
316  else {
317    if (!is_underline) {
318      if (italic_flag)
319	putstring(SGR_ITALIC);
320      else if (reverse_flag)
321	putstring(SGR_REVERSE);
322      else
323	putstring(SGR_UNDERLINE);
324    }
325    is_underline = 1;
326  }
327}
328
329void tty_printer::make_bold(unsigned int c, int w)
330{
331  if (old_drawing_scheme) {
332    if (!w)
333      warning("can't print zero-width character in bold");
334    else {
335      int n = w / font::hor;
336      put_char(c);
337      for (int i = 0; i < n; i++)
338	putchar('\b');
339    }
340  }
341  else {
342    if (!is_bold)
343      putstring(SGR_BOLD);
344    is_bold = 1;
345  }
346}
347
348schar tty_printer::color_to_idx(color *col)
349{
350  if (col->is_default())
351    return DEFAULT_COLOR_IDX;
352  unsigned int r, g, b;
353  col->get_rgb(&r, &g, &b);
354  schar idx;
355  if (tty_color(r, g, b, &idx)) {
356    char *s = col->print_color();
357    error("Unknown color (%1) mapped to default", s);
358    a_delete s;
359  }
360  return idx;
361}
362
363void tty_printer::set_char(int i, font *f, const environment *env,
364			   int w, const char *)
365{
366  if (w % font::hor != 0)
367    fatal("width of character not a multiple of horizontal resolution");
368  add_char(f->get_code(i), w,
369	   env->hpos, env->vpos,
370	   env->col, env->fill,
371	   ((tty_font *)f)->get_mode());
372}
373
374void tty_printer::add_char(unsigned int c, int w,
375			   int h, int v,
376			   color *fore, color *back,
377			   unsigned char mode)
378{
379#if 0
380  // This is too expensive.
381  if (h % font::hor != 0)
382    fatal("horizontal position not a multiple of horizontal resolution");
383#endif
384  int hpos = h / font::hor;
385  if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
386    error("character with ridiculous horizontal position discarded");
387    return;
388  }
389  int vpos;
390  if (v == cached_v && cached_v != 0)
391    vpos = cached_vpos;
392  else {
393    if (v % font::vert != 0)
394      fatal("vertical position not a multiple of vertical resolution");
395    vpos = v / font::vert;
396    if (vpos > nlines) {
397      glyph **old_lines = lines;
398      lines = new glyph *[vpos + 1];
399      memcpy(lines, old_lines, nlines * sizeof(glyph *));
400      for (int i = nlines; i <= vpos; i++)
401	lines[i] = 0;
402      a_delete old_lines;
403      nlines = vpos + 1;
404    }
405    // Note that the first output line corresponds to groff
406    // position font::vert.
407    if (vpos <= 0) {
408      error("character above first line discarded");
409      return;
410    }
411    cached_v = v;
412    cached_vpos = vpos;
413  }
414  glyph *g = new glyph;
415  g->w = w;
416  g->hpos = hpos;
417  g->code = c;
418  g->fore_color_idx = color_to_idx(fore);
419  g->back_color_idx = color_to_idx(back);
420  g->mode = mode;
421
422  // The list will be reversed later.  After reversal, it must be in
423  // increasing order of hpos, with COLOR_CHANGE and CU specials before
424  // HDRAW characters before VDRAW characters before normal characters
425  // at each hpos, and otherwise in order of occurrence.
426
427  glyph **pp;
428  for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
429    if ((*pp)->hpos < hpos
430	|| ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
431      break;
432  g->next = *pp;
433  *pp = g;
434}
435
436void tty_printer::special(char *arg, const environment *env, char type)
437{
438  if (type == 'u') {
439    add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
440	     CU_MODE);
441    return;
442  }
443  if (type != 'p')
444    return;
445  char *p;
446  for (p = arg; *p == ' ' || *p == '\n'; p++)
447    ;
448  char *tag = p;
449  for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
450    ;
451  if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
452    error("X command without `tty:' tag ignored");
453    return;
454  }
455  p++;
456  for (; *p == ' ' || *p == '\n'; p++)
457    ;
458  char *command = p;
459  for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
460    ;
461  if (*command == '\0') {
462    error("empty X command ignored");
463    return;
464  }
465  if (strncmp(command, "sgr", p - command) == 0) {
466    for (; *p == ' ' || *p == '\n'; p++)
467      ;
468    int n;
469    if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
470      old_drawing_scheme = 1;
471    else
472      old_drawing_scheme = 0;
473    update_options();
474  }
475}
476
477void tty_printer::change_color(const environment * const env)
478{
479  add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
480}
481
482void tty_printer::change_fill_color(const environment * const env)
483{
484  add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
485}
486
487void tty_printer::draw(int code, int *p, int np, const environment *env)
488{
489  if (code != 'l' || !draw_flag)
490    return;
491  if (np != 2) {
492    error("2 arguments required for line");
493    return;
494  }
495  if (p[0] == 0) {
496    // vertical line
497    int v = env->vpos;
498    int len = p[1];
499    if (len < 0) {
500      v += len;
501      len = -len;
502    }
503    if (len >= 0 && len <= font::vert)
504      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
505	       VDRAW_MODE|START_LINE|END_LINE);
506    else {
507      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
508	       VDRAW_MODE|START_LINE);
509      len -= font::vert;
510      v += font::vert;
511      while (len > 0) {
512	add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
513		 VDRAW_MODE|START_LINE|END_LINE);
514	len -= font::vert;
515	v += font::vert;
516      }
517      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
518	       VDRAW_MODE|END_LINE);
519    }
520  }
521  if (p[1] == 0) {
522    // horizontal line
523    int h = env->hpos;
524    int len = p[0];
525    if (len < 0) {
526      h += len;
527      len = -len;
528    }
529    if (len >= 0 && len <= font::hor)
530      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
531	       HDRAW_MODE|START_LINE|END_LINE);
532    else {
533      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
534	       HDRAW_MODE|START_LINE);
535      len -= font::hor;
536      h += font::hor;
537      while (len > 0) {
538	add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
539		 HDRAW_MODE|START_LINE|END_LINE);
540	len -= font::hor;
541	h += font::hor;
542      }
543      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
544	       HDRAW_MODE|END_LINE);
545    }
546  }
547}
548
549void tty_printer::put_char(unsigned int wc)
550{
551  if (is_utf8 && wc >= 0x80) {
552    char buf[6 + 1];
553    int count;
554    char *p = buf;
555    if (wc < 0x800)
556      count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
557    else if (wc < 0x10000)
558      count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
559    else if (wc < 0x200000)
560      count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
561    else if (wc < 0x4000000)
562      count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
563    else if (wc <= 0x7fffffff)
564      count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
565    else
566      return;
567    do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
568      while (count > 0);
569    *++p = '\0';
570    putstring(buf);
571  }
572  else
573    putchar(wc);
574}
575
576void tty_printer::put_color(schar color_index, int back)
577{
578  if (color_index == DEFAULT_COLOR_IDX) {
579    putstring(SGR_DEFAULT);
580    // set bold and underline again
581    if (is_bold)
582      putstring(SGR_BOLD);
583    if (is_underline) {
584      if (italic_flag)
585	putstring(SGR_ITALIC);
586      else if (reverse_flag)
587	putstring(SGR_REVERSE);
588      else
589	putstring(SGR_UNDERLINE);
590    }
591    // set other color again
592    back = !back;
593    color_index = back ? curr_back_idx : curr_fore_idx;
594  }
595  if (color_index != DEFAULT_COLOR_IDX) {
596    putstring(CSI);
597    if (back)
598      putchar('4');
599    else
600      putchar('3');
601    putchar(color_index + '0');
602    putchar('m');
603  }
604}
605
606// The possible Unicode combinations for crossing characters.
607//
608// `  ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
609//
610// `  ' = 0, ` ' = 1, `|' = 2, `|' = 3
611//            |                 |
612
613static int crossings[4*4] = {
614  0x0000, 0x2577, 0x2575, 0x2502,
615  0x2576, 0x250C, 0x2514, 0x251C,
616  0x2574, 0x2510, 0x2518, 0x2524,
617  0x2500, 0x252C, 0x2534, 0x253C
618};
619
620void tty_printer::end_page(int page_length)
621{
622  if (page_length % font::vert != 0)
623    error("vertical position at end of page not multiple of vertical resolution");
624  int lines_per_page = page_length / font::vert;
625  int last_line;
626  for (last_line = nlines; last_line > 0; last_line--)
627    if (lines[last_line - 1])
628      break;
629#if 0
630  if (last_line > lines_per_page) {
631    error("characters past last line discarded");
632    do {
633      --last_line;
634      while (lines[last_line]) {
635	glyph *tem = lines[last_line];
636	lines[last_line] = tem->next;
637	delete tem;
638      }
639    } while (last_line > lines_per_page);
640  }
641#endif
642  for (int i = 0; i < last_line; i++) {
643    glyph *p = lines[i];
644    lines[i] = 0;
645    glyph *g = 0;
646    while (p) {
647      glyph *tem = p->next;
648      p->next = g;
649      g = p;
650      p = tem;
651    }
652    int hpos = 0;
653    glyph *nextp;
654    curr_fore_idx = DEFAULT_COLOR_IDX;
655    curr_back_idx = DEFAULT_COLOR_IDX;
656    is_underline = 0;
657    is_bold = 0;
658    for (p = g; p; delete p, p = nextp) {
659      nextp = p->next;
660      if (p->mode & CU_MODE) {
661	cu_flag = p->code;
662	continue;
663      }
664      if (nextp && p->hpos == nextp->hpos) {
665	if (p->draw_mode() == HDRAW_MODE &&
666	    nextp->draw_mode() == VDRAW_MODE) {
667	  if (is_utf8)
668	    nextp->code =
669	      crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
670			+ ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
671	  else
672	    nextp->code = '+';
673	  continue;
674	}
675	if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
676	  nextp->code = p->code;
677	  continue;
678	}
679	if (!overstrike_flag)
680	  continue;
681      }
682      if (hpos > p->hpos) {
683	do {
684	  putchar('\b');
685	  hpos--;
686	} while (hpos > p->hpos);
687      }
688      else {
689	if (horizontal_tab_flag) {
690	  for (;;) {
691	    int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
692	    if (next_tab_pos > p->hpos)
693	      break;
694	    if (cu_flag)
695	      make_underline(p->w);
696	    else if (!old_drawing_scheme && is_underline) {
697	      if (italic_flag)
698		putstring(SGR_NO_ITALIC);
699	      else if (reverse_flag)
700		putstring(SGR_NO_REVERSE);
701	      else
702		putstring(SGR_NO_UNDERLINE);
703	      is_underline = 0;
704	    }
705	    putchar('\t');
706	    hpos = next_tab_pos;
707	  }
708	}
709	for (; hpos < p->hpos; hpos++) {
710	  if (cu_flag)
711	    make_underline(p->w);
712	  else if (!old_drawing_scheme && is_underline) {
713	    if (italic_flag)
714	      putstring(SGR_NO_ITALIC);
715	    else if (reverse_flag)
716	      putstring(SGR_NO_REVERSE);
717	    else
718	      putstring(SGR_NO_UNDERLINE);
719	    is_underline = 0;
720	  }
721	  putchar(' ');
722	}
723      }
724      assert(hpos == p->hpos);
725      if (p->mode & COLOR_CHANGE) {
726	if (!old_drawing_scheme) {
727	  if (p->fore_color_idx != curr_fore_idx) {
728	    put_color(p->fore_color_idx, 0);
729	    curr_fore_idx = p->fore_color_idx;
730	  }
731	  if (p->back_color_idx != curr_back_idx) {
732	    put_color(p->back_color_idx, 1);
733	    curr_back_idx = p->back_color_idx;
734	  }
735	}
736	continue;
737      }
738      if (p->mode & UNDERLINE_MODE)
739	make_underline(p->w);
740      else if (!old_drawing_scheme && is_underline) {
741	if (italic_flag)
742	  putstring(SGR_NO_ITALIC);
743	else if (reverse_flag)
744	  putstring(SGR_NO_REVERSE);
745	else
746	  putstring(SGR_NO_UNDERLINE);
747	is_underline = 0;
748      }
749      if (p->mode & BOLD_MODE)
750	make_bold(p->code, p->w);
751      else if (!old_drawing_scheme && is_bold) {
752	putstring(SGR_NO_BOLD);
753	is_bold = 0;
754      }
755      if (!old_drawing_scheme) {
756	if (p->fore_color_idx != curr_fore_idx) {
757	  put_color(p->fore_color_idx, 0);
758	  curr_fore_idx = p->fore_color_idx;
759	}
760	if (p->back_color_idx != curr_back_idx) {
761	  put_color(p->back_color_idx, 1);
762	  curr_back_idx = p->back_color_idx;
763	}
764      }
765      put_char(p->code);
766      hpos += p->w / font::hor;
767    }
768    if (!old_drawing_scheme
769	&& (is_bold || is_underline
770	    || curr_fore_idx != DEFAULT_COLOR_IDX
771	    || curr_back_idx != DEFAULT_COLOR_IDX))
772      putstring(SGR_DEFAULT);
773    putchar('\n');
774  }
775  if (form_feed_flag) {
776    if (last_line < lines_per_page)
777      putchar('\f');
778  }
779  else {
780    for (; last_line < lines_per_page; last_line++)
781      putchar('\n');
782  }
783}
784
785font *tty_printer::make_font(const char *nm)
786{
787  return tty_font::load_tty_font(nm);
788}
789
790printer *make_printer()
791{
792  return new tty_printer(device);
793}
794
795static void update_options()
796{
797  if (old_drawing_scheme) {
798    italic_flag = 0;
799    reverse_flag = 0;
800    bold_underline_mode = bold_underline_mode_option;
801    bold_flag = bold_flag_option;
802    underline_flag = underline_flag_option;
803  }
804  else {
805    italic_flag = italic_flag_option;
806    reverse_flag = reverse_flag_option;
807    bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
808    bold_flag = 1;
809    underline_flag = 1;
810  }
811}
812
813int main(int argc, char **argv)
814{
815  program_name = argv[0];
816  static char stderr_buf[BUFSIZ];
817  if (getenv("GROFF_NO_SGR"))
818    old_drawing_scheme = 1;
819  setbuf(stderr, stderr_buf);
820  int c;
821  static const struct option long_options[] = {
822    { "help", no_argument, 0, CHAR_MAX + 1 },
823    { "version", no_argument, 0, 'v' },
824    { NULL, 0, 0, 0 }
825  };
826  while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
827	 != EOF)
828    switch(c) {
829    case 'v':
830      printf("GNU grotty (groff) version %s\n", Version_string);
831      exit(0);
832      break;
833    case 'i':
834      // Use italic font instead of underlining.
835      italic_flag_option = 1;
836      break;
837    case 'I':
838      // ignore include search path
839      break;
840    case 'b':
841      // Do not embolden by overstriking.
842      bold_flag_option = 0;
843      break;
844    case 'c':
845      // Use old scheme for emboldening and underline.
846      old_drawing_scheme = 1;
847      break;
848    case 'u':
849      // Do not underline.
850      underline_flag_option = 0;
851      break;
852    case 'o':
853      // Do not overstrike (other than emboldening and underlining).
854      overstrike_flag = 0;
855      break;
856    case 'r':
857      // Use reverse mode instead of underlining.
858      reverse_flag_option = 1;
859      break;
860    case 'B':
861      // Do bold-underlining as bold.
862      bold_underline_mode_option = BOLD_MODE;
863      break;
864    case 'U':
865      // Do bold-underlining as underlining.
866      bold_underline_mode_option = UNDERLINE_MODE;
867      break;
868    case 'h':
869      // Use horizontal tabs.
870      horizontal_tab_flag = 1;
871      break;
872    case 'f':
873      form_feed_flag = 1;
874      break;
875    case 'F':
876      font::command_line_font_dir(optarg);
877      break;
878    case 'd':
879      // Ignore \D commands.
880      draw_flag = 0;
881      break;
882    case CHAR_MAX + 1: // --help
883      usage(stdout);
884      exit(0);
885      break;
886    case '?':
887      usage(stderr);
888      exit(1);
889      break;
890    default:
891      assert(0);
892    }
893  update_options();
894  if (optind >= argc)
895    do_file("-");
896  else {
897    for (int i = optind; i < argc; i++)
898      do_file(argv[i]);
899  }
900  return 0;
901}
902
903static void usage(FILE *stream)
904{
905  fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
906	  program_name);
907}
908