2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 typedef signed char schar;
27 implement_ptable(schar)
29 extern "C" const char *Version_string;
31 #define putstring(s) fputs(s, stdout)
34 #define SHRT_MIN (-32768)
38 #define SHRT_MAX 32767
43 // A character of the output device fits in a 32-bit word.
44 typedef unsigned int output_character;
46 static int horizontal_tab_flag = 0;
47 static int form_feed_flag = 0;
48 static int bold_flag_option = 1;
50 static int underline_flag_option = 1;
51 static int underline_flag;
52 static int overstrike_flag = 1;
53 static int draw_flag = 1;
54 static int italic_flag_option = 0;
55 static int italic_flag;
56 static int reverse_flag_option = 0;
57 static int reverse_flag;
58 static int old_drawing_scheme = 0;
60 static void update_options();
61 static void usage(FILE *stream);
63 static int hline_char = '-';
64 static int vline_char = '|';
67 UNDERLINE_MODE = 0x01,
77 // Mode to use for bold-underlining.
78 static unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
79 static unsigned char bold_underline_mode;
81 #ifndef IS_EBCDIC_HOST
87 // SGR handling (ISO 6429)
88 #define SGR_BOLD CSI "1m"
89 #define SGR_NO_BOLD CSI "22m"
90 #define SGR_ITALIC CSI "3m"
91 #define SGR_NO_ITALIC CSI "23m"
92 #define SGR_UNDERLINE CSI "4m"
93 #define SGR_NO_UNDERLINE CSI "24m"
94 #define SGR_REVERSE CSI "7m"
95 #define SGR_NO_REVERSE CSI "27m"
96 // many terminals can't handle `CSI 39 m' and `CSI 49 m' to reset
97 // the foreground and background color, respectively; we thus use
98 // `CSI 0 m' exclusively
99 #define SGR_DEFAULT CSI "0m"
101 #define DEFAULT_COLOR_IDX -1
103 class tty_font : public font {
104 tty_font(const char *);
108 unsigned char get_mode() { return mode; }
110 void handle_x_command(int argc, const char **argv);
112 static tty_font *load_tty_font(const char *);
115 tty_font *tty_font::load_tty_font(const char *s)
117 tty_font *f = new tty_font(s);
122 const char *num = f->get_internal_name();
124 if (num != 0 && (n = strtol(num, 0, 0)) != 0)
125 f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
127 f->mode &= ~UNDERLINE_MODE;
129 f->mode &= ~BOLD_MODE;
130 if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
131 f->mode = (unsigned char)((f->mode & ~(BOLD_MODE|UNDERLINE_MODE))
132 | bold_underline_mode);
136 tty_font::tty_font(const char *nm)
141 tty_font::~tty_font()
146 void tty_font::handle_x_command(int argc, const char **argv)
148 if (argc >= 1 && strcmp(argv[0], "bold") == 0)
150 else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
151 mode |= UNDERLINE_MODE;
156 static tty_glyph *free_list;
163 schar back_color_idx;
164 schar fore_color_idx;
165 void *operator new(size_t);
166 void operator delete(void *);
167 inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
169 return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
172 tty_glyph *tty_glyph::free_list = 0;
174 void *tty_glyph::operator new(size_t)
177 const int BLOCK = 1024;
178 free_list = (tty_glyph *)new char[sizeof(tty_glyph) * BLOCK];
179 for (int i = 0; i < BLOCK - 1; i++)
180 free_list[i].next = free_list + i + 1;
181 free_list[BLOCK - 1].next = 0;
183 tty_glyph *p = free_list;
184 free_list = free_list->next;
189 void tty_glyph::operator delete(void *p)
192 ((tty_glyph *)p)->next = free_list;
193 free_list = (tty_glyph *)p;
197 class tty_printer : public printer {
207 PTABLE(schar) tty_colors;
208 void make_underline(int);
209 void make_bold(output_character, int);
210 schar color_to_idx(color *);
211 void add_char(output_character, int, int, int, color *, color *,
213 char *make_rgb_string(unsigned int, unsigned int, unsigned int);
214 int tty_color(unsigned int, unsigned int, unsigned int, schar *,
215 schar = DEFAULT_COLOR_IDX);
216 void line(int, int, int, int, color *, color *);
217 void draw_line(int *, int, const environment *);
218 void draw_polygon(int *, int, const environment *);
222 void set_char(glyph *, font *, const environment *, int, const char *);
223 void draw(int, int *, int, const environment *);
224 void special(char *, const environment *, char);
225 void change_color(const environment * const);
226 void change_fill_color(const environment * const);
227 void put_char(output_character);
228 void put_color(schar, int);
229 void begin_page(int) { }
231 font *make_font(const char *);
234 char *tty_printer::make_rgb_string(unsigned int r,
238 char *s = new char[8];
240 s[1] = char(r & 0xff);
242 s[3] = char(g & 0xff);
244 s[5] = char(b & 0xff);
247 // avoid null-bytes in string
248 for (int i = 0; i < 6; i++)
256 int tty_printer::tty_color(unsigned int r,
258 unsigned int b, schar *idx, schar value)
260 int unknown_color = 0;
261 char *s = make_rgb_string(r, g, b);
262 schar *i = tty_colors.lookup(s);
267 tty_colors.define(s, i);
271 return unknown_color;
274 tty_printer::tty_printer() : cached_v(0)
276 if (font::is_unicode) {
282 (void)tty_color(0, 0, 0, &dummy, 0);
283 (void)tty_color(color::MAX_COLOR_VAL,
284 color::MAX_COLOR_VAL,
285 color::MAX_COLOR_VAL, &dummy, 7);
287 (void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
288 (void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
289 (void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
290 // yellow, magenta, cyan
291 (void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
292 (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
293 (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
295 lines = new tty_glyph *[nlines];
296 for (int i = 0; i < nlines; i++)
301 tty_printer::~tty_printer()
306 void tty_printer::make_underline(int w)
308 if (old_drawing_scheme) {
310 warning("can't underline zero-width character");
319 putstring(SGR_ITALIC);
320 else if (reverse_flag)
321 putstring(SGR_REVERSE);
323 putstring(SGR_UNDERLINE);
329 void tty_printer::make_bold(output_character c, int w)
331 if (old_drawing_scheme) {
333 warning("can't print zero-width character in bold");
346 schar tty_printer::color_to_idx(color *col)
348 if (col->is_default())
349 return DEFAULT_COLOR_IDX;
350 unsigned int r, g, b;
351 col->get_rgb(&r, &g, &b);
353 if (tty_color(r, g, b, &idx)) {
354 char *s = col->print_color();
355 error("Unknown color (%1) mapped to default", s);
361 void tty_printer::set_char(glyph *g, font *f, const environment *env,
364 if (w % font::hor != 0)
365 fatal("width of character not a multiple of horizontal resolution");
366 add_char(f->get_code(g), w,
367 env->hpos, env->vpos,
369 ((tty_font *)f)->get_mode());
372 void tty_printer::add_char(output_character c, int w,
374 color *fore, color *back,
378 // This is too expensive.
379 if (h % font::hor != 0)
380 fatal("horizontal position not a multiple of horizontal resolution");
382 int hpos = h / font::hor;
383 if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
384 error("character with ridiculous horizontal position discarded");
388 if (v == cached_v && cached_v != 0)
391 if (v % font::vert != 0)
392 fatal("vertical position not a multiple of vertical resolution");
393 vpos = v / font::vert;
395 tty_glyph **old_lines = lines;
396 lines = new tty_glyph *[vpos + 1];
397 memcpy(lines, old_lines, nlines * sizeof(tty_glyph *));
398 for (int i = nlines; i <= vpos; i++)
403 // Note that the first output line corresponds to groff
404 // position font::vert.
406 error("character above first line discarded");
412 tty_glyph *g = new tty_glyph;
416 g->fore_color_idx = color_to_idx(fore);
417 g->back_color_idx = color_to_idx(back);
420 // The list will be reversed later. After reversal, it must be in
421 // increasing order of hpos, with COLOR_CHANGE and CU specials before
422 // HDRAW characters before VDRAW characters before normal characters
423 // at each hpos, and otherwise in order of occurrence.
426 for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
427 if ((*pp)->hpos < hpos
428 || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
434 void tty_printer::special(char *arg, const environment *env, char type)
437 add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
444 for (p = arg; *p == ' ' || *p == '\n'; p++)
447 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
449 if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
450 error("X command without `tty:' tag ignored");
454 for (; *p == ' ' || *p == '\n'; p++)
457 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
459 if (*command == '\0') {
460 error("empty X command ignored");
463 if (strncmp(command, "sgr", p - command) == 0) {
464 for (; *p == ' ' || *p == '\n'; p++)
467 if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
468 old_drawing_scheme = 1;
470 old_drawing_scheme = 0;
475 void tty_printer::change_color(const environment * const env)
477 add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
480 void tty_printer::change_fill_color(const environment * const env)
482 add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
485 void tty_printer::draw(int code, int *p, int np, const environment *env)
490 draw_line(p, np, env);
492 draw_polygon(p, np, env);
495 void tty_printer::draw_polygon(int *p, int np, const environment *env)
498 error("even number of arguments required for polygon");
502 error("no arguments for polygon");
505 // We only draw polygons which consist entirely of horizontal and
509 for (int i = 0; i < np; i += 2) {
510 if (!(p[i] == 0 || p[i + 1] == 0))
515 if (!(hpos == 0 || vpos == 0))
517 int start_hpos = env->hpos;
518 int start_vpos = env->vpos;
521 for (int i = 0; i < np; i += 2) {
522 line(hpos, vpos, p[i], p[i + 1], env->col, env->fill);
526 line(hpos, vpos, start_hpos - hpos, start_vpos - vpos,
527 env->col, env->fill);
530 void tty_printer::draw_line(int *p, int np, const environment *env)
533 error("2 arguments required for line");
536 line(env->hpos, env->vpos, p[0], p[1], env->col, env->fill);
539 void tty_printer::line(int hpos, int vpos, int dx, int dy,
540 color *col, color *fill)
551 add_char(vline_char, font::hor, hpos, v, col, fill,
552 VDRAW_MODE|START_LINE|END_LINE);
554 add_char(vline_char, font::hor, hpos, v, col, fill,
555 VDRAW_MODE|START_LINE);
559 add_char(vline_char, font::hor, hpos, v, col, fill,
560 VDRAW_MODE|START_LINE|END_LINE);
564 add_char(vline_char, font::hor, hpos, v, col, fill,
565 VDRAW_MODE|END_LINE);
577 add_char(hline_char, font::hor, h, vpos, col, fill,
578 HDRAW_MODE|START_LINE|END_LINE);
580 add_char(hline_char, font::hor, h, vpos, col, fill,
581 HDRAW_MODE|START_LINE);
585 add_char(hline_char, font::hor, h, vpos, col, fill,
586 HDRAW_MODE|START_LINE|END_LINE);
590 add_char(hline_char, font::hor, h, vpos, col, fill,
591 HDRAW_MODE|END_LINE);
596 void tty_printer::put_char(output_character wc)
598 if (font::is_unicode && wc >= 0x80) {
603 count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
604 else if (wc < 0x10000)
605 count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
606 else if (wc < 0x200000)
607 count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
608 else if (wc < 0x4000000)
609 count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
610 else if (wc <= 0x7fffffff)
611 count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
614 do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
623 void tty_printer::put_color(schar color_index, int back)
625 if (color_index == DEFAULT_COLOR_IDX) {
626 putstring(SGR_DEFAULT);
627 // set bold and underline again
632 putstring(SGR_ITALIC);
633 else if (reverse_flag)
634 putstring(SGR_REVERSE);
636 putstring(SGR_UNDERLINE);
638 // set other color again
640 color_index = back ? curr_back_idx : curr_fore_idx;
642 if (color_index != DEFAULT_COLOR_IDX) {
648 putchar(color_index + '0');
653 // The possible Unicode combinations for crossing characters.
655 // ` ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
657 // ` ' = 0, ` ' = 1, `|' = 2, `|' = 3
660 static output_character crossings[4*4] = {
661 0x0000, 0x2577, 0x2575, 0x2502,
662 0x2576, 0x250C, 0x2514, 0x251C,
663 0x2574, 0x2510, 0x2518, 0x2524,
664 0x2500, 0x252C, 0x2534, 0x253C
667 void tty_printer::end_page(int page_length)
669 if (page_length % font::vert != 0)
670 error("vertical position at end of page not multiple of vertical resolution");
671 int lines_per_page = page_length / font::vert;
673 for (last_line = nlines; last_line > 0; last_line--)
674 if (lines[last_line - 1])
677 if (last_line > lines_per_page) {
678 error("characters past last line discarded");
681 while (lines[last_line]) {
682 tty_glyph *tem = lines[last_line];
683 lines[last_line] = tem->next;
686 } while (last_line > lines_per_page);
689 for (int i = 0; i < last_line; i++) {
690 tty_glyph *p = lines[i];
694 tty_glyph *tem = p->next;
701 curr_fore_idx = DEFAULT_COLOR_IDX;
702 curr_back_idx = DEFAULT_COLOR_IDX;
705 for (p = g; p; delete p, p = nextp) {
707 if (p->mode & CU_MODE) {
708 cu_flag = (p->code != 0);
711 if (nextp && p->hpos == nextp->hpos) {
712 if (p->draw_mode() == HDRAW_MODE &&
713 nextp->draw_mode() == VDRAW_MODE) {
714 if (font::is_unicode)
716 crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
717 + ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
722 if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
723 nextp->code = p->code;
726 if (!overstrike_flag)
729 if (hpos > p->hpos) {
733 } while (hpos > p->hpos);
736 if (horizontal_tab_flag) {
738 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
739 if (next_tab_pos > p->hpos)
742 make_underline(p->w);
743 else if (!old_drawing_scheme && is_underline) {
745 putstring(SGR_NO_ITALIC);
746 else if (reverse_flag)
747 putstring(SGR_NO_REVERSE);
749 putstring(SGR_NO_UNDERLINE);
756 for (; hpos < p->hpos; hpos++) {
758 make_underline(p->w);
759 else if (!old_drawing_scheme && is_underline) {
761 putstring(SGR_NO_ITALIC);
762 else if (reverse_flag)
763 putstring(SGR_NO_REVERSE);
765 putstring(SGR_NO_UNDERLINE);
771 assert(hpos == p->hpos);
772 if (p->mode & COLOR_CHANGE) {
773 if (!old_drawing_scheme) {
774 if (p->fore_color_idx != curr_fore_idx) {
775 put_color(p->fore_color_idx, 0);
776 curr_fore_idx = p->fore_color_idx;
778 if (p->back_color_idx != curr_back_idx) {
779 put_color(p->back_color_idx, 1);
780 curr_back_idx = p->back_color_idx;
785 if (p->mode & UNDERLINE_MODE)
786 make_underline(p->w);
787 else if (!old_drawing_scheme && is_underline) {
789 putstring(SGR_NO_ITALIC);
790 else if (reverse_flag)
791 putstring(SGR_NO_REVERSE);
793 putstring(SGR_NO_UNDERLINE);
796 if (p->mode & BOLD_MODE)
797 make_bold(p->code, p->w);
798 else if (!old_drawing_scheme && is_bold) {
799 putstring(SGR_NO_BOLD);
802 if (!old_drawing_scheme) {
803 if (p->fore_color_idx != curr_fore_idx) {
804 put_color(p->fore_color_idx, 0);
805 curr_fore_idx = p->fore_color_idx;
807 if (p->back_color_idx != curr_back_idx) {
808 put_color(p->back_color_idx, 1);
809 curr_back_idx = p->back_color_idx;
813 hpos += p->w / font::hor;
815 if (!old_drawing_scheme
816 && (is_bold || is_underline
817 || curr_fore_idx != DEFAULT_COLOR_IDX
818 || curr_back_idx != DEFAULT_COLOR_IDX))
819 putstring(SGR_DEFAULT);
822 if (form_feed_flag) {
823 if (last_line < lines_per_page)
827 for (; last_line < lines_per_page; last_line++)
832 font *tty_printer::make_font(const char *nm)
834 return tty_font::load_tty_font(nm);
837 printer *make_printer()
839 return new tty_printer();
842 static void update_options()
844 if (old_drawing_scheme) {
847 bold_underline_mode = bold_underline_mode_option;
848 bold_flag = bold_flag_option;
849 underline_flag = underline_flag_option;
852 italic_flag = italic_flag_option;
853 reverse_flag = reverse_flag_option;
854 bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
860 int main(int argc, char **argv)
862 program_name = argv[0];
863 static char stderr_buf[BUFSIZ];
864 if (getenv("GROFF_NO_SGR"))
865 old_drawing_scheme = 1;
866 setbuf(stderr, stderr_buf);
867 setlocale(LC_CTYPE, "");
869 static const struct option long_options[] = {
870 { "help", no_argument, 0, CHAR_MAX + 1 },
871 { "version", no_argument, 0, 'v' },
874 while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
878 printf("GNU grotty (groff) version %s\n", Version_string);
882 // Use italic font instead of underlining.
883 italic_flag_option = 1;
886 // ignore include search path
889 // Do not embolden by overstriking.
890 bold_flag_option = 0;
893 // Use old scheme for emboldening and underline.
894 old_drawing_scheme = 1;
898 underline_flag_option = 0;
901 // Do not overstrike (other than emboldening and underlining).
905 // Use reverse mode instead of underlining.
906 reverse_flag_option = 1;
909 // Do bold-underlining as bold.
910 bold_underline_mode_option = BOLD_MODE;
913 // Do bold-underlining as underlining.
914 bold_underline_mode_option = UNDERLINE_MODE;
917 // Use horizontal tabs.
918 horizontal_tab_flag = 1;
924 font::command_line_font_dir(optarg);
927 // Ignore \D commands.
930 case CHAR_MAX + 1: // --help
945 for (int i = optind; i < argc; i++)
951 static void usage(FILE *stream)
953 fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",