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