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