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