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