60d46e8a9640c563fa1267c2bfccca0a76e28652
[platform/upstream/groff.git] / src / devices / grotty / tty.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2014  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   static tty_glyph *free_list;
157 public:
158   tty_glyph *next;
159   int w;
160   int hpos;
161   unsigned int code;
162   unsigned char mode;
163   schar back_color_idx;
164   schar fore_color_idx;
165   void *operator new(size_t);
166   void operator delete(void *);
167   inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
168   inline int order() {
169     return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
170 };
171
172 tty_glyph *tty_glyph::free_list = 0;
173
174 void *tty_glyph::operator new(size_t)
175 {
176   if (!free_list) {
177     const int BLOCK = 1024;
178     free_list = (tty_glyph *)new char[sizeof(tty_glyph) * BLOCK];
179     for (int i = 0; i < BLOCK - 1; i++)
180       free_list[i].next = free_list + i + 1;
181     free_list[BLOCK - 1].next = 0;
182   }
183   tty_glyph *p = free_list;
184   free_list = free_list->next;
185   p->next = 0;
186   return p;
187 }
188
189 void tty_glyph::operator delete(void *p)
190 {
191   if (p) {
192     ((tty_glyph *)p)->next = free_list;
193     free_list = (tty_glyph *)p;
194   }
195 }
196
197 class tty_printer : public printer {
198   tty_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(output_character, int);
210   schar color_to_idx(color *);
211   void add_char(output_character, int, int, int, color *, color *,
212                 unsigned char);
213   char *make_rgb_string(unsigned int, unsigned int, unsigned int);
214   int tty_color(unsigned int, unsigned int, unsigned int, schar *,
215                 schar = DEFAULT_COLOR_IDX);
216   void line(int, int, int, int, color *, color *);
217   void draw_line(int *, int, const environment *);
218   void draw_polygon(int *, int, const environment *);
219 public:
220   tty_printer();
221   ~tty_printer();
222   void set_char(glyph *, font *, const environment *, int, const char *);
223   void draw(int, int *, int, const environment *);
224   void special(char *, const environment *, char);
225   void change_color(const environment * const);
226   void change_fill_color(const environment * const);
227   void put_char(output_character);
228   void put_color(schar, int);
229   void begin_page(int) { }
230   void end_page(int);
231   font *make_font(const char *);
232 };
233
234 char *tty_printer::make_rgb_string(unsigned int r,
235                                    unsigned int g,
236                                    unsigned int b)
237 {
238   char *s = new char[8];
239   s[0] = char(r >> 8);
240   s[1] = char(r & 0xff);
241   s[2] = char(g >> 8);
242   s[3] = char(g & 0xff);
243   s[4] = char(b >> 8);
244   s[5] = char(b & 0xff);
245   s[6] = char(0x80);
246   s[7] = 0;
247   // avoid null-bytes in string
248   for (int i = 0; i < 6; i++)
249     if (!s[i]) {
250       s[i] = 1;
251       s[6] |= 1 << i;
252     }
253   return s;
254 }
255
256 int tty_printer::tty_color(unsigned int r,
257                            unsigned int g,
258                            unsigned int b, schar *idx, schar value)
259 {
260   int unknown_color = 0;
261   char *s = make_rgb_string(r, g, b);
262   schar *i = tty_colors.lookup(s);
263   if (!i) {
264     unknown_color = 1;
265     i = new schar[1];
266     *i = value;
267     tty_colors.define(s, i);
268   }
269   *idx = *i;
270   a_delete s;
271   return unknown_color;
272 }
273
274 tty_printer::tty_printer() : cached_v(0)
275 {
276   if (font::is_unicode) {
277     hline_char = 0x2500;
278     vline_char = 0x2502;
279   }
280   schar dummy;
281   // black, white
282   (void)tty_color(0, 0, 0, &dummy, 0);
283   (void)tty_color(color::MAX_COLOR_VAL,
284                   color::MAX_COLOR_VAL,
285                   color::MAX_COLOR_VAL, &dummy, 7);
286   // red, green, blue
287   (void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
288   (void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
289   (void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
290   // yellow, magenta, cyan
291   (void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
292   (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
293   (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
294   nlines = 66;
295   lines = new tty_glyph *[nlines];
296   for (int i = 0; i < nlines; i++)
297     lines[i] = 0;
298   cu_flag = 0;
299 }
300
301 tty_printer::~tty_printer()
302 {
303   a_delete lines;
304 }
305
306 void tty_printer::make_underline(int w)
307 {
308   if (old_drawing_scheme) {
309     if (!w)
310       warning("can't underline zero-width character");
311     else {
312       putchar('_');
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
329 void tty_printer::make_bold(output_character 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       put_char(c);
336       putchar('\b');
337     }
338   }
339   else {
340     if (!is_bold)
341       putstring(SGR_BOLD);
342     is_bold = 1;
343   }
344 }
345
346 schar tty_printer::color_to_idx(color *col)
347 {
348   if (col->is_default())
349     return DEFAULT_COLOR_IDX;
350   unsigned int r, g, b;
351   col->get_rgb(&r, &g, &b);
352   schar idx;
353   if (tty_color(r, g, b, &idx)) {
354     char *s = col->print_color();
355     error("Unknown color (%1) mapped to default", s);
356     a_delete s;
357   }
358   return idx;
359 }
360
361 void tty_printer::set_char(glyph *g, font *f, const environment *env,
362                            int w, const char *)
363 {
364   if (w % font::hor != 0)
365     fatal("width of character not a multiple of horizontal resolution");
366   add_char(f->get_code(g), w,
367            env->hpos, env->vpos,
368            env->col, env->fill,
369            ((tty_font *)f)->get_mode());
370 }
371
372 void tty_printer::add_char(output_character c, int w,
373                            int h, int v,
374                            color *fore, color *back,
375                            unsigned char mode)
376 {
377 #if 0
378   // This is too expensive.
379   if (h % font::hor != 0)
380     fatal("horizontal position not a multiple of horizontal resolution");
381 #endif
382   int hpos = h / font::hor;
383   if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
384     error("character with ridiculous horizontal position discarded");
385     return;
386   }
387   int vpos;
388   if (v == cached_v && cached_v != 0)
389     vpos = cached_vpos;
390   else {
391     if (v % font::vert != 0)
392       fatal("vertical position not a multiple of vertical resolution");
393     vpos = v / font::vert;
394     if (vpos > nlines) {
395       tty_glyph **old_lines = lines;
396       lines = new tty_glyph *[vpos + 1];
397       memcpy(lines, old_lines, nlines * sizeof(tty_glyph *));
398       for (int i = nlines; i <= vpos; i++)
399         lines[i] = 0;
400       a_delete old_lines;
401       nlines = vpos + 1;
402     }
403     // Note that the first output line corresponds to groff
404     // position font::vert.
405     if (vpos <= 0) {
406       error("character above first line discarded");
407       return;
408     }
409     cached_v = v;
410     cached_vpos = vpos;
411   }
412   tty_glyph *g = new tty_glyph;
413   g->w = w;
414   g->hpos = hpos;
415   g->code = c;
416   g->fore_color_idx = color_to_idx(fore);
417   g->back_color_idx = color_to_idx(back);
418   g->mode = mode;
419
420   // The list will be reversed later.  After reversal, it must be in
421   // increasing order of hpos, with COLOR_CHANGE and CU specials before
422   // HDRAW characters before VDRAW characters before normal characters
423   // at each hpos, and otherwise in order of occurrence.
424
425   tty_glyph **pp;
426   for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
427     if ((*pp)->hpos < hpos
428         || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
429       break;
430   g->next = *pp;
431   *pp = g;
432 }
433
434 void tty_printer::special(char *arg, const environment *env, char type)
435 {
436   if (type == 'u') {
437     add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
438              CU_MODE);
439     return;
440   }
441   if (type != 'p')
442     return;
443   char *p;
444   for (p = arg; *p == ' ' || *p == '\n'; p++)
445     ;
446   char *tag = p;
447   for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
448     ;
449   if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
450     error("X command without `tty:' tag ignored");
451     return;
452   }
453   p++;
454   for (; *p == ' ' || *p == '\n'; p++)
455     ;
456   char *command = p;
457   for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
458     ;
459   if (*command == '\0') {
460     error("empty X command ignored");
461     return;
462   }
463   if (strncmp(command, "sgr", p - command) == 0) {
464     for (; *p == ' ' || *p == '\n'; p++)
465       ;
466     int n;
467     if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
468       old_drawing_scheme = 1;
469     else
470       old_drawing_scheme = 0;
471     update_options();
472   }
473 }
474
475 void tty_printer::change_color(const environment * const env)
476 {
477   add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
478 }
479
480 void tty_printer::change_fill_color(const environment * const env)
481 {
482   add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
483 }
484
485 void tty_printer::draw(int code, int *p, int np, const environment *env)
486 {
487   if (!draw_flag)
488     return;
489   if (code == 'l')
490     draw_line(p, np, env);
491   if (code == 'p')
492     draw_polygon(p, np, env);
493 }
494
495 void tty_printer::draw_polygon(int *p, int np, const environment *env)
496 {
497   if (np & 1) {
498     error("even number of arguments required for polygon");
499     return;
500   }
501   if (np == 0) {
502     error("no arguments for polygon");
503     return;
504   }
505   // We only draw polygons which consist entirely of horizontal and
506   // vertical lines.
507   int hpos = 0;
508   int vpos = 0;
509   for (int i = 0; i < np; i += 2) {
510     if (!(p[i] == 0 || p[i + 1] == 0))
511       return;
512     hpos += p[i];
513     vpos += p[i + 1];
514   }
515   if (!(hpos == 0 || vpos == 0))
516     return;
517   int start_hpos = env->hpos;
518   int start_vpos = env->vpos;
519   hpos = start_hpos;
520   vpos = start_vpos;
521   for (int i = 0; i < np; i += 2) {
522     line(hpos, vpos, p[i], p[i + 1], env->col, env->fill);
523     hpos += p[i];
524     vpos += p[i + 1];
525   }
526   line(hpos, vpos, start_hpos - hpos, start_vpos - vpos,
527        env->col, env->fill);
528 }
529
530 void tty_printer::draw_line(int *p, int np, const environment *env)
531 {
532   if (np != 2) {
533     error("2 arguments required for line");
534     return;
535   }
536   line(env->hpos, env->vpos, p[0], p[1], env->col, env->fill);
537 }
538
539 void tty_printer::line(int hpos, int vpos, int dx, int dy,
540                        color *col, color *fill)
541 {
542   if (dx == 0) {
543     // vertical line
544     int v = vpos;
545     int len = dy;
546     if (len < 0) {
547       v += len;
548       len = -len;
549     }
550     if (len == 0)
551       add_char(vline_char, font::hor, hpos, v, col, fill,
552                VDRAW_MODE|START_LINE|END_LINE);
553     else {
554       add_char(vline_char, font::hor, hpos, v, col, fill,
555                VDRAW_MODE|START_LINE);
556       len -= font::vert;
557       v += font::vert;
558       while (len > 0) {
559         add_char(vline_char, font::hor, hpos, v, col, fill,
560                  VDRAW_MODE|START_LINE|END_LINE);
561         len -= font::vert;
562         v += font::vert;
563       }
564       add_char(vline_char, font::hor, hpos, v, col, fill,
565                VDRAW_MODE|END_LINE);
566     }
567   }
568   if (dy == 0) {
569     // horizontal line
570     int h = hpos;
571     int len = dx;
572     if (len < 0) {
573       h += len;
574       len = -len;
575     }
576     if (len == 0)
577       add_char(hline_char, font::hor, h, vpos, col, fill,
578                HDRAW_MODE|START_LINE|END_LINE);
579     else {
580       add_char(hline_char, font::hor, h, vpos, col, fill,
581                HDRAW_MODE|START_LINE);
582       len -= font::hor;
583       h += font::hor;
584       while (len > 0) {
585         add_char(hline_char, font::hor, h, vpos, col, fill,
586                  HDRAW_MODE|START_LINE|END_LINE);
587         len -= font::hor;
588         h += font::hor;
589       }
590       add_char(hline_char, font::hor, h, vpos, col, fill,
591                HDRAW_MODE|END_LINE);
592     }
593   }
594 }
595
596 void tty_printer::put_char(output_character wc)
597 {
598   if (font::is_unicode && wc >= 0x80) {
599     char buf[6 + 1];
600     int count;
601     char *p = buf;
602     if (wc < 0x800)
603       count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
604     else if (wc < 0x10000)
605       count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
606     else if (wc < 0x200000)
607       count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
608     else if (wc < 0x4000000)
609       count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
610     else if (wc <= 0x7fffffff)
611       count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
612     else
613       return;
614     do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
615       while (count > 0);
616     *++p = '\0';
617     putstring(buf);
618   }
619   else
620     putchar(wc);
621 }
622
623 void tty_printer::put_color(schar color_index, int back)
624 {
625   if (color_index == DEFAULT_COLOR_IDX) {
626     putstring(SGR_DEFAULT);
627     // set bold and underline again
628     if (is_bold)
629       putstring(SGR_BOLD);
630     if (is_underline) {
631       if (italic_flag)
632         putstring(SGR_ITALIC);
633       else if (reverse_flag)
634         putstring(SGR_REVERSE);
635       else
636         putstring(SGR_UNDERLINE);
637     }
638     // set other color again
639     back = !back;
640     color_index = back ? curr_back_idx : curr_fore_idx;
641   }
642   if (color_index != DEFAULT_COLOR_IDX) {
643     putstring(CSI);
644     if (back)
645       putchar('4');
646     else
647       putchar('3');
648     putchar(color_index + '0');
649     putchar('m');
650   }
651 }
652
653 // The possible Unicode combinations for crossing characters.
654 //
655 // `  ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
656 //
657 // `  ' = 0, ` ' = 1, `|' = 2, `|' = 3
658 //            |                 |
659
660 static output_character crossings[4*4] = {
661   0x0000, 0x2577, 0x2575, 0x2502,
662   0x2576, 0x250C, 0x2514, 0x251C,
663   0x2574, 0x2510, 0x2518, 0x2524,
664   0x2500, 0x252C, 0x2534, 0x253C
665 };
666
667 void tty_printer::end_page(int page_length)
668 {
669   if (page_length % font::vert != 0)
670     error("vertical position at end of page not multiple of vertical resolution");
671   int lines_per_page = page_length / font::vert;
672   int last_line;
673   for (last_line = nlines; last_line > 0; last_line--)
674     if (lines[last_line - 1])
675       break;
676 #if 0
677   if (last_line > lines_per_page) {
678     error("characters past last line discarded");
679     do {
680       --last_line;
681       while (lines[last_line]) {
682         tty_glyph *tem = lines[last_line];
683         lines[last_line] = tem->next;
684         delete tem;
685       }
686     } while (last_line > lines_per_page);
687   }
688 #endif
689   for (int i = 0; i < last_line; i++) {
690     tty_glyph *p = lines[i];
691     lines[i] = 0;
692     tty_glyph *g = 0;
693     while (p) {
694       tty_glyph *tem = p->next;
695       p->next = g;
696       g = p;
697       p = tem;
698     }
699     int hpos = 0;
700     tty_glyph *nextp;
701     curr_fore_idx = DEFAULT_COLOR_IDX;
702     curr_back_idx = DEFAULT_COLOR_IDX;
703     is_underline = 0;
704     is_bold = 0;
705     for (p = g; p; delete p, p = nextp) {
706       nextp = p->next;
707       if (p->mode & CU_MODE) {
708         cu_flag = (p->code != 0);
709         continue;
710       }
711       if (nextp && p->hpos == nextp->hpos) {
712         if (p->draw_mode() == HDRAW_MODE &&
713             nextp->draw_mode() == VDRAW_MODE) {
714           if (font::is_unicode)
715             nextp->code =
716               crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
717                         + ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
718           else
719             nextp->code = '+';
720           continue;
721         }
722         if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
723           nextp->code = p->code;
724           continue;
725         }
726         if (!overstrike_flag)
727           continue;
728       }
729       if (hpos > p->hpos) {
730         do {
731           putchar('\b');
732           hpos--;
733         } while (hpos > p->hpos);
734       }
735       else {
736         if (horizontal_tab_flag) {
737           for (;;) {
738             int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
739             if (next_tab_pos > p->hpos)
740               break;
741             if (cu_flag)
742               make_underline(p->w);
743             else if (!old_drawing_scheme && is_underline) {
744               if (italic_flag)
745                 putstring(SGR_NO_ITALIC);
746               else if (reverse_flag)
747                 putstring(SGR_NO_REVERSE);
748               else
749                 putstring(SGR_NO_UNDERLINE);
750               is_underline = 0;
751             }
752             putchar('\t');
753             hpos = next_tab_pos;
754           }
755         }
756         for (; hpos < p->hpos; hpos++) {
757           if (cu_flag)
758             make_underline(p->w);
759           else if (!old_drawing_scheme && is_underline) {
760             if (italic_flag)
761               putstring(SGR_NO_ITALIC);
762             else if (reverse_flag)
763               putstring(SGR_NO_REVERSE);
764             else
765               putstring(SGR_NO_UNDERLINE);
766             is_underline = 0;
767           }
768           putchar(' ');
769         }
770       }
771       assert(hpos == p->hpos);
772       if (p->mode & COLOR_CHANGE) {
773         if (!old_drawing_scheme) {
774           if (p->fore_color_idx != curr_fore_idx) {
775             put_color(p->fore_color_idx, 0);
776             curr_fore_idx = p->fore_color_idx;
777           }
778           if (p->back_color_idx != curr_back_idx) {
779             put_color(p->back_color_idx, 1);
780             curr_back_idx = p->back_color_idx;
781           }
782         }
783         continue;
784       }
785       if (p->mode & UNDERLINE_MODE)
786         make_underline(p->w);
787       else if (!old_drawing_scheme && is_underline) {
788         if (italic_flag)
789           putstring(SGR_NO_ITALIC);
790         else if (reverse_flag)
791           putstring(SGR_NO_REVERSE);
792         else
793           putstring(SGR_NO_UNDERLINE);
794         is_underline = 0;
795       }
796       if (p->mode & BOLD_MODE)
797         make_bold(p->code, p->w);
798       else if (!old_drawing_scheme && is_bold) {
799         putstring(SGR_NO_BOLD);
800         is_bold = 0;
801       }
802       if (!old_drawing_scheme) {
803         if (p->fore_color_idx != curr_fore_idx) {
804           put_color(p->fore_color_idx, 0);
805           curr_fore_idx = p->fore_color_idx;
806         }
807         if (p->back_color_idx != curr_back_idx) {
808           put_color(p->back_color_idx, 1);
809           curr_back_idx = p->back_color_idx;
810         }
811       }
812       put_char(p->code);
813       hpos += p->w / font::hor;
814     }
815     if (!old_drawing_scheme
816         && (is_bold || is_underline
817             || curr_fore_idx != DEFAULT_COLOR_IDX
818             || curr_back_idx != DEFAULT_COLOR_IDX))
819       putstring(SGR_DEFAULT);
820     putchar('\n');
821   }
822   if (form_feed_flag) {
823     if (last_line < lines_per_page)
824       putchar('\f');
825   }
826   else {
827     for (; last_line < lines_per_page; last_line++)
828       putchar('\n');
829   }
830 }
831
832 font *tty_printer::make_font(const char *nm)
833 {
834   return tty_font::load_tty_font(nm);
835 }
836
837 printer *make_printer()
838 {
839   return new tty_printer();
840 }
841
842 static void update_options()
843 {
844   if (old_drawing_scheme) {
845     italic_flag = 0;
846     reverse_flag = 0;
847     bold_underline_mode = bold_underline_mode_option;
848     bold_flag = bold_flag_option;
849     underline_flag = underline_flag_option;
850   }
851   else {
852     italic_flag = italic_flag_option;
853     reverse_flag = reverse_flag_option;
854     bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
855     bold_flag = 1;
856     underline_flag = 1;
857   }
858 }
859
860 int main(int argc, char **argv)
861 {
862   program_name = argv[0];
863   static char stderr_buf[BUFSIZ];
864   if (getenv("GROFF_NO_SGR"))
865     old_drawing_scheme = 1;
866   setbuf(stderr, stderr_buf);
867   setlocale(LC_CTYPE, "");
868   int c;
869   static const struct option long_options[] = {
870     { "help", no_argument, 0, CHAR_MAX + 1 },
871     { "version", no_argument, 0, 'v' },
872     { NULL, 0, 0, 0 }
873   };
874   while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
875          != EOF)
876     switch(c) {
877     case 'v':
878       printf("GNU grotty (groff) version %s\n", Version_string);
879       exit(0);
880       break;
881     case 'i':
882       // Use italic font instead of underlining.
883       italic_flag_option = 1;
884       break;
885     case 'I':
886       // ignore include search path
887       break;
888     case 'b':
889       // Do not embolden by overstriking.
890       bold_flag_option = 0;
891       break;
892     case 'c':
893       // Use old scheme for emboldening and underline.
894       old_drawing_scheme = 1;
895       break;
896     case 'u':
897       // Do not underline.
898       underline_flag_option = 0;
899       break;
900     case 'o':
901       // Do not overstrike (other than emboldening and underlining).
902       overstrike_flag = 0;
903       break;
904     case 'r':
905       // Use reverse mode instead of underlining.
906       reverse_flag_option = 1;
907       break;
908     case 'B':
909       // Do bold-underlining as bold.
910       bold_underline_mode_option = BOLD_MODE;
911       break;
912     case 'U':
913       // Do bold-underlining as underlining.
914       bold_underline_mode_option = UNDERLINE_MODE;
915       break;
916     case 'h':
917       // Use horizontal tabs.
918       horizontal_tab_flag = 1;
919       break;
920     case 'f':
921       form_feed_flag = 1;
922       break;
923     case 'F':
924       font::command_line_font_dir(optarg);
925       break;
926     case 'd':
927       // Ignore \D commands.
928       draw_flag = 0;
929       break;
930     case CHAR_MAX + 1: // --help
931       usage(stdout);
932       exit(0);
933       break;
934     case '?':
935       usage(stderr);
936       exit(1);
937       break;
938     default:
939       assert(0);
940     }
941   update_options();
942   if (optind >= argc)
943     do_file("-");
944   else {
945     for (int i = optind; i < argc; i++)
946       do_file(argv[i]);
947   }
948   return 0;
949 }
950
951 static void usage(FILE *stream)
952 {
953   fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
954           program_name);
955 }