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