2 /* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
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
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
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. */
26 typedef signed char schar;
29 implement_ptable(schar)
31 extern "C" const char *Version_string;
33 #define putstring(s) fputs(s, stdout)
36 #define SHRT_MIN (-32768)
40 #define SHRT_MAX 32767
45 static int horizontal_tab_flag = 0;
46 static int form_feed_flag = 0;
47 static int bold_flag_option = 1;
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;
59 static void update_options();
60 static void usage(FILE *stream);
62 static int hline_char = '-';
63 static int vline_char = '|';
66 UNDERLINE_MODE = 0x01,
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;
80 #ifndef IS_EBCDIC_HOST
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"
100 #define DEFAULT_COLOR_IDX -1
102 class tty_font : public font {
103 tty_font(const char *);
107 unsigned char get_mode() { return mode; }
109 void handle_x_command(int argc, const char **argv);
111 static tty_font *load_tty_font(const char *);
114 tty_font *tty_font::load_tty_font(const char *s)
116 tty_font *f = new tty_font(s);
121 const char *num = f->get_internal_name();
123 if (num != 0 && (n = strtol(num, 0, 0)) != 0)
124 f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
126 f->mode &= ~UNDERLINE_MODE;
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);
135 tty_font::tty_font(const char *nm)
140 tty_font::~tty_font()
145 void tty_font::handle_x_command(int argc, const char **argv)
147 if (argc >= 1 && strcmp(argv[0], "bold") == 0)
149 else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
150 mode |= UNDERLINE_MODE;
155 static glyph *free_list;
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); }
167 return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
170 glyph *glyph::free_list = 0;
172 void *glyph::operator new(size_t)
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;
181 glyph *p = free_list;
182 free_list = free_list->next;
187 void glyph::operator delete(void *p)
190 ((glyph *)p)->next = free_list;
191 free_list = (glyph *)p;
195 class tty_printer : public printer {
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);
215 tty_printer(const char *device);
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 *);
229 char *tty_printer::make_rgb_string(unsigned int r,
233 char *s = new char[8];
235 s[1] = char(r & 0xff);
237 s[3] = char(g & 0xff);
239 s[5] = char(b & 0xff);
242 // avoid null-bytes in string
243 for (int i = 0; i < 6; i++)
251 int tty_printer::tty_color(unsigned int r,
253 unsigned int b, schar *idx, schar value)
255 int unknown_color = 0;
256 char *s = make_rgb_string(r, g, b);
257 schar *i = tty_colors.lookup(s);
262 tty_colors.define(s, i);
266 return unknown_color;
269 tty_printer::tty_printer(const char *dev) : cached_v(0)
271 is_utf8 = !strcmp(dev, "utf8");
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);
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);
291 lines = new glyph *[nlines];
292 for (int i = 0; i < nlines; i++)
297 tty_printer::~tty_printer()
302 void tty_printer::make_underline()
304 if (old_drawing_scheme) {
311 putstring(SGR_ITALIC);
312 else if (reverse_flag)
313 putstring(SGR_REVERSE);
315 putstring(SGR_UNDERLINE);
321 void tty_printer::make_bold(unsigned int c)
323 if (old_drawing_scheme) {
334 schar tty_printer::color_to_idx(color *col)
336 if (col->is_default())
337 return DEFAULT_COLOR_IDX;
338 unsigned int r, g, b;
339 col->get_rgb(&r, &g, &b);
341 if (tty_color(r, g, b, &idx)) {
342 char *s = col->print_color();
343 error("Unknown color (%1) mapped to default", s);
349 void tty_printer::set_char(int i, font *f, const environment *env,
353 fatal("width of character not equal to horizontal resolution");
354 add_char(f->get_code(i),
355 env->hpos, env->vpos,
357 ((tty_font *)f)->get_mode());
360 void tty_printer::add_char(unsigned int c,
362 color *fore, color *back,
366 // This is too expensive.
367 if (h % font::hor != 0)
368 fatal("horizontal position not a multiple of horizontal resolution");
370 int hpos = h / font::hor;
371 if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
372 error("character with ridiculous horizontal position discarded");
376 if (v == cached_v && cached_v != 0)
379 if (v % font::vert != 0)
380 fatal("vertical position not a multiple of vertical resolution");
381 vpos = v / font::vert;
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++)
391 // Note that the first output line corresponds to groff
392 // position font::vert.
394 error("character above first line discarded");
400 glyph *g = new glyph;
403 g->fore_color_idx = color_to_idx(fore);
404 g->back_color_idx = color_to_idx(back);
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.
413 for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
414 if ((*pp)->hpos < hpos
415 || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
421 void tty_printer::special(char *arg, const environment *env, char type)
424 add_char(*arg - '0', env->hpos, env->vpos, env->col, env->fill, CU_MODE);
430 for (p = arg; *p == ' ' || *p == '\n'; p++)
433 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
435 if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
436 error("X command without `tty:' tag ignored");
440 for (; *p == ' ' || *p == '\n'; p++)
443 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
445 if (*command == '\0') {
446 error("empty X command ignored");
449 if (strncmp(command, "sgr", p - command) == 0) {
450 for (; *p == ' ' || *p == '\n'; p++)
453 if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
454 old_drawing_scheme = 1;
456 old_drawing_scheme = 0;
461 void tty_printer::change_color(const environment * const env)
463 add_char(0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
466 void tty_printer::change_fill_color(const environment * const env)
468 add_char(0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
471 void tty_printer::draw(int code, int *p, int np, const environment *env)
473 if (code != 'l' || !draw_flag)
476 error("2 arguments required for line");
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);
491 add_char(vline_char, env->hpos, v, env->col, env->fill,
492 VDRAW_MODE|START_LINE);
496 add_char(vline_char, env->hpos, v, env->col, env->fill,
497 VDRAW_MODE|START_LINE|END_LINE);
501 add_char(vline_char, env->hpos, v, env->col, env->fill,
502 VDRAW_MODE|END_LINE);
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);
517 add_char(hline_char, h, env->vpos, env->col, env->fill,
518 HDRAW_MODE|START_LINE);
522 add_char(hline_char, h, env->vpos, env->col, env->fill,
523 HDRAW_MODE|START_LINE|END_LINE);
527 add_char(hline_char, h, env->vpos, env->col, env->fill,
528 HDRAW_MODE|END_LINE);
533 void tty_printer::put_char(unsigned int wc)
535 if (is_utf8 && wc >= 0x80) {
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);
551 do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
560 void tty_printer::put_color(schar color_index, int back)
562 if (color_index == DEFAULT_COLOR_IDX) {
563 putstring(SGR_DEFAULT);
564 // set bold and underline again
569 putstring(SGR_ITALIC);
570 else if (reverse_flag)
571 putstring(SGR_REVERSE);
573 putstring(SGR_UNDERLINE);
575 // set other color again
577 color_index = back ? curr_back_idx : curr_fore_idx;
579 if (color_index != DEFAULT_COLOR_IDX) {
585 putchar(color_index + '0');
590 // The possible Unicode combinations for crossing characters.
592 // ` ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
594 // ` ' = 0, ` ' = 1, `|' = 2, `|' = 3
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
604 void tty_printer::end_page(int page_length)
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;
610 for (last_line = nlines; last_line > 0; last_line--)
611 if (lines[last_line - 1])
614 if (last_line > lines_per_page) {
615 error("characters past last line discarded");
618 while (lines[last_line]) {
619 glyph *tem = lines[last_line];
620 lines[last_line] = tem->next;
623 } while (last_line > lines_per_page);
626 for (int i = 0; i < last_line; i++) {
631 glyph *tem = p->next;
638 curr_fore_idx = DEFAULT_COLOR_IDX;
639 curr_back_idx = DEFAULT_COLOR_IDX;
642 for (p = g; p; delete p, p = nextp) {
644 if (p->mode & CU_MODE) {
648 if (nextp && p->hpos == nextp->hpos) {
649 if (p->draw_mode() == HDRAW_MODE &&
650 nextp->draw_mode() == VDRAW_MODE) {
653 crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
654 + ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
659 if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
660 nextp->code = p->code;
663 if (!overstrike_flag)
666 if (hpos > p->hpos) {
670 } while (hpos > p->hpos);
673 if (horizontal_tab_flag) {
675 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
676 if (next_tab_pos > p->hpos)
680 else if (!old_drawing_scheme && is_underline) {
682 putstring(SGR_NO_ITALIC);
683 else if (reverse_flag)
684 putstring(SGR_NO_REVERSE);
686 putstring(SGR_NO_UNDERLINE);
693 for (; hpos < p->hpos; hpos++) {
696 else if (!old_drawing_scheme && is_underline) {
698 putstring(SGR_NO_ITALIC);
699 else if (reverse_flag)
700 putstring(SGR_NO_REVERSE);
702 putstring(SGR_NO_UNDERLINE);
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;
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;
722 if (p->mode & UNDERLINE_MODE)
724 else if (!old_drawing_scheme && is_underline) {
726 putstring(SGR_NO_ITALIC);
727 else if (reverse_flag)
728 putstring(SGR_NO_REVERSE);
730 putstring(SGR_NO_UNDERLINE);
733 if (p->mode & BOLD_MODE)
735 else if (!old_drawing_scheme && is_bold) {
736 putstring(SGR_NO_BOLD);
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;
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;
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);
759 if (form_feed_flag) {
760 if (last_line < lines_per_page)
764 for (; last_line < lines_per_page; last_line++)
769 font *tty_printer::make_font(const char *nm)
771 return tty_font::load_tty_font(nm);
774 printer *make_printer()
776 return new tty_printer(device);
779 static void update_options()
781 if (old_drawing_scheme) {
784 bold_underline_mode = bold_underline_mode_option;
785 bold_flag = bold_flag_option;
786 underline_flag = underline_flag_option;
789 italic_flag = italic_flag_option;
790 reverse_flag = reverse_flag_option;
791 bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
797 int main(int argc, char **argv)
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);
805 static const struct option long_options[] = {
806 { "help", no_argument, 0, CHAR_MAX + 1 },
807 { "version", no_argument, 0, 'v' },
810 while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
814 printf("GNU grotty (groff) version %s\n", Version_string);
818 // Use italic font instead of underlining.
819 italic_flag_option = 1;
822 // ignore include search path
825 // Do not embolden by overstriking.
826 bold_flag_option = 0;
829 // Use old scheme for emboldening and underline.
830 old_drawing_scheme = 1;
834 underline_flag_option = 0;
837 // Do not overstrike (other than emboldening and underlining).
841 // Use reverse mode instead of underlining.
842 reverse_flag_option = 1;
845 // Do bold-underlining as bold.
846 bold_underline_mode_option = BOLD_MODE;
849 // Do bold-underlining as underlining.
850 bold_underline_mode_option = UNDERLINE_MODE;
853 // Use horizontal tabs.
854 horizontal_tab_flag = 1;
860 font::command_line_font_dir(optarg);
863 // Ignore \D commands.
866 case CHAR_MAX + 1: // --help
881 for (int i = optind; i < argc; i++)
887 static void usage(FILE *stream)
889 fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",