]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/groff/src/devices/grotty/tty.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / groff / src / devices / grotty / tty.cpp
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
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 2, or (at your option) any later
11 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 along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22 #include "driver.h"
23 #include "device.h"
24 #include "ptable.h"
25
26 typedef signed char schar;
27
28 declare_ptable(schar)
29 implement_ptable(schar)
30
31 extern "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
45 static int horizontal_tab_flag = 0;
46 static int form_feed_flag = 0;
47 static int bold_flag_option = 1;
48 static int bold_flag;
49 static int underline_flag_option = 1;
50 static int underline_flag;
51 static int overstrike_flag = 1;
52 static int draw_flag = 1;
53 static int italic_flag_option = 0;
54 static int italic_flag;
55 static int reverse_flag_option = 0;
56 static int reverse_flag;
57 static int old_drawing_scheme = 0;
58
59 static void update_options();
60 static void usage(FILE *stream);
61
62 static int hline_char = '-';
63 static int vline_char = '|';
64
65 enum {
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.
77 static unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
78 static 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
102 class tty_font : public font {
103   tty_font(const char *);
104   unsigned char mode;
105 public:
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
114 tty_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
135 tty_font::tty_font(const char *nm)
136 : font(nm), mode(0)
137 {
138 }
139
140 tty_font::~tty_font()
141 {
142 }
143
144 #if 0
145 void 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
154 class glyph {
155   static glyph *free_list;
156 public:
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
171 glyph *glyph::free_list = 0;
172
173 void *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
188 void glyph::operator delete(void *p)
189 {
190   if (p) {
191     ((glyph *)p)->next = free_list;
192     free_list = (glyph *)p;
193   }
194 }
195
196 class 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);
215 public:
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
230 char *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
252 int 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
270 tty_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
298 tty_printer::~tty_printer()
299 {
300   a_delete lines;
301 }
302
303 void 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
329 void 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
348 schar 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
363 void 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
374 void 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
436 void 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
477 void 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
482 void 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
487 void 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
549 void 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
576 void 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
613 static 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
620 void 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
785 font *tty_printer::make_font(const char *nm)
786 {
787   return tty_font::load_tty_font(nm);
788 }
789
790 printer *make_printer()
791 {
792   return new tty_printer(device);
793 }
794
795 static 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
813 int 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
903 static void usage(FILE *stream)
904 {
905   fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
906           program_name);
907 }