2 /* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, 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 background color, respectively; we thus 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
;
162 schar back_color_idx
;
163 schar fore_color_idx
;
164 void *operator new(size_t);
165 void operator delete(void *);
166 inline int draw_mode() { return mode
& (VDRAW_MODE
|HDRAW_MODE
); }
168 return mode
& (VDRAW_MODE
|HDRAW_MODE
|CU_MODE
|COLOR_CHANGE
); }
171 glyph
*glyph::free_list
= 0;
173 void *glyph::operator new(size_t)
176 const int BLOCK
= 1024;
177 free_list
= (glyph
*)new char[sizeof(glyph
) * BLOCK
];
178 for (int i
= 0; i
< BLOCK
- 1; i
++)
179 free_list
[i
].next
= free_list
+ i
+ 1;
180 free_list
[BLOCK
- 1].next
= 0;
182 glyph
*p
= free_list
;
183 free_list
= free_list
->next
;
188 void glyph::operator delete(void *p
)
191 ((glyph
*)p
)->next
= free_list
;
192 free_list
= (glyph
*)p
;
196 class tty_printer
: public printer
{
207 PTABLE(schar
) tty_colors
;
208 void make_underline(int);
209 void make_bold(unsigned int, int);
210 schar
color_to_idx(color
*col
);
211 void add_char(unsigned int, int, int, int, color
*, color
*, unsigned char);
212 char *make_rgb_string(unsigned int, unsigned int, unsigned int);
213 int tty_color(unsigned int, unsigned int, unsigned int, schar
*,
214 schar
= DEFAULT_COLOR_IDX
);
216 tty_printer(const char *device
);
218 void set_char(int, font
*, const environment
*, int, const char *name
);
219 void draw(int code
, int *p
, int np
, const environment
*env
);
220 void special(char *arg
, const environment
*env
, char type
);
221 void change_color(const environment
* const env
);
222 void change_fill_color(const environment
* const env
);
223 void put_char(unsigned int);
224 void put_color(schar
, int);
225 void begin_page(int) { }
226 void end_page(int page_length
);
227 font
*make_font(const char *);
230 char *tty_printer::make_rgb_string(unsigned int r
,
234 char *s
= new char[8];
236 s
[1] = char(r
& 0xff);
238 s
[3] = char(g
& 0xff);
240 s
[5] = char(b
& 0xff);
243 // avoid null-bytes in string
244 for (int i
= 0; i
< 6; i
++)
252 int tty_printer::tty_color(unsigned int r
,
254 unsigned int b
, schar
*idx
, schar value
)
256 int unknown_color
= 0;
257 char *s
= make_rgb_string(r
, g
, b
);
258 schar
*i
= tty_colors
.lookup(s
);
263 tty_colors
.define(s
, i
);
267 return unknown_color
;
270 tty_printer::tty_printer(const char *dev
) : cached_v(0)
272 is_utf8
= !strcmp(dev
, "utf8");
279 (void)tty_color(0, 0, 0, &dummy
, 0);
280 (void)tty_color(color::MAX_COLOR_VAL
,
281 color::MAX_COLOR_VAL
,
282 color::MAX_COLOR_VAL
, &dummy
, 7);
284 (void)tty_color(color::MAX_COLOR_VAL
, 0, 0, &dummy
, 1);
285 (void)tty_color(0, color::MAX_COLOR_VAL
, 0, &dummy
, 2);
286 (void)tty_color(0, 0, color::MAX_COLOR_VAL
, &dummy
, 4);
287 // yellow, magenta, cyan
288 (void)tty_color(color::MAX_COLOR_VAL
, color::MAX_COLOR_VAL
, 0, &dummy
, 3);
289 (void)tty_color(color::MAX_COLOR_VAL
, 0, color::MAX_COLOR_VAL
, &dummy
, 5);
290 (void)tty_color(0, color::MAX_COLOR_VAL
, color::MAX_COLOR_VAL
, &dummy
, 6);
292 lines
= new glyph
*[nlines
];
293 for (int i
= 0; i
< nlines
; i
++)
298 tty_printer::~tty_printer()
303 void tty_printer::make_underline(int w
)
305 if (old_drawing_scheme
) {
307 warning("can't underline zero-width character");
309 int n
= w
/ font::hor
;
310 for (int i
= 0; i
< n
; i
++)
312 for (int j
= 0; j
< n
; j
++)
319 putstring(SGR_ITALIC
);
320 else if (reverse_flag
)
321 putstring(SGR_REVERSE
);
323 putstring(SGR_UNDERLINE
);
329 void tty_printer::make_bold(unsigned int c
, int w
)
331 if (old_drawing_scheme
) {
333 warning("can't print zero-width character in bold");
335 int n
= w
/ font::hor
;
337 for (int i
= 0; i
< n
; i
++)
348 schar
tty_printer::color_to_idx(color
*col
)
350 if (col
->is_default())
351 return DEFAULT_COLOR_IDX
;
352 unsigned int r
, g
, b
;
353 col
->get_rgb(&r
, &g
, &b
);
355 if (tty_color(r
, g
, b
, &idx
)) {
356 char *s
= col
->print_color();
357 error("Unknown color (%1) mapped to default", s
);
363 void tty_printer::set_char(int i
, font
*f
, const environment
*env
,
366 if (w
% font::hor
!= 0)
367 fatal("width of character not a multiple of horizontal resolution");
368 add_char(f
->get_code(i
), w
,
369 env
->hpos
, env
->vpos
,
371 ((tty_font
*)f
)->get_mode());
374 void tty_printer::add_char(unsigned int c
, int w
,
376 color
*fore
, color
*back
,
380 // This is too expensive.
381 if (h
% font::hor
!= 0)
382 fatal("horizontal position not a multiple of horizontal resolution");
384 int hpos
= h
/ font::hor
;
385 if (hpos
< SHRT_MIN
|| hpos
> SHRT_MAX
) {
386 error("character with ridiculous horizontal position discarded");
390 if (v
== cached_v
&& cached_v
!= 0)
393 if (v
% font::vert
!= 0)
394 fatal("vertical position not a multiple of vertical resolution");
395 vpos
= v
/ font::vert
;
397 glyph
**old_lines
= lines
;
398 lines
= new glyph
*[vpos
+ 1];
399 memcpy(lines
, old_lines
, nlines
* sizeof(glyph
*));
400 for (int i
= nlines
; i
<= vpos
; i
++)
405 // Note that the first output line corresponds to groff
406 // position font::vert.
408 error("character above first line discarded");
414 glyph
*g
= new glyph
;
418 g
->fore_color_idx
= color_to_idx(fore
);
419 g
->back_color_idx
= color_to_idx(back
);
422 // The list will be reversed later. After reversal, it must be in
423 // increasing order of hpos, with COLOR_CHANGE and CU specials before
424 // HDRAW characters before VDRAW characters before normal characters
425 // at each hpos, and otherwise in order of occurrence.
428 for (pp
= lines
+ (vpos
- 1); *pp
; pp
= &(*pp
)->next
)
429 if ((*pp
)->hpos
< hpos
430 || ((*pp
)->hpos
== hpos
&& (*pp
)->order() >= g
->order()))
436 void tty_printer::special(char *arg
, const environment
*env
, char type
)
439 add_char(*arg
- '0', 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
,
446 for (p
= arg
; *p
== ' ' || *p
== '\n'; p
++)
449 for (; *p
!= '\0' && *p
!= ':' && *p
!= ' ' && *p
!= '\n'; p
++)
451 if (*p
== '\0' || strncmp(tag
, "tty", p
- tag
) != 0) {
452 error("X command without `tty:' tag ignored");
456 for (; *p
== ' ' || *p
== '\n'; p
++)
459 for (; *p
!= '\0' && *p
!= ' ' && *p
!= '\n'; p
++)
461 if (*command
== '\0') {
462 error("empty X command ignored");
465 if (strncmp(command
, "sgr", p
- command
) == 0) {
466 for (; *p
== ' ' || *p
== '\n'; p
++)
469 if (*p
!= '\0' && sscanf(p
, "%d", &n
) == 1 && n
== 0)
470 old_drawing_scheme
= 1;
472 old_drawing_scheme
= 0;
477 void tty_printer::change_color(const environment
* const env
)
479 add_char(0, 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
482 void tty_printer::change_fill_color(const environment
* const env
)
484 add_char(0, 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
487 void tty_printer::draw(int code
, int *p
, int np
, const environment
*env
)
489 if (code
!= 'l' || !draw_flag
)
492 error("2 arguments required for line");
503 if (len
>= 0 && len
<= font::vert
)
504 add_char(vline_char
, font::hor
, env
->hpos
, v
, env
->col
, env
->fill
,
505 VDRAW_MODE
|START_LINE
|END_LINE
);
507 add_char(vline_char
, font::hor
, env
->hpos
, v
, env
->col
, env
->fill
,
508 VDRAW_MODE
|START_LINE
);
512 add_char(vline_char
, font::hor
, env
->hpos
, v
, env
->col
, env
->fill
,
513 VDRAW_MODE
|START_LINE
|END_LINE
);
517 add_char(vline_char
, font::hor
, env
->hpos
, v
, env
->col
, env
->fill
,
518 VDRAW_MODE
|END_LINE
);
529 if (len
>= 0 && len
<= font::hor
)
530 add_char(hline_char
, font::hor
, h
, env
->vpos
, env
->col
, env
->fill
,
531 HDRAW_MODE
|START_LINE
|END_LINE
);
533 add_char(hline_char
, font::hor
, h
, env
->vpos
, env
->col
, env
->fill
,
534 HDRAW_MODE
|START_LINE
);
538 add_char(hline_char
, font::hor
, h
, env
->vpos
, env
->col
, env
->fill
,
539 HDRAW_MODE
|START_LINE
|END_LINE
);
543 add_char(hline_char
, font::hor
, h
, env
->vpos
, env
->col
, env
->fill
,
544 HDRAW_MODE
|END_LINE
);
549 void tty_printer::put_char(unsigned int wc
)
551 if (is_utf8
&& wc
>= 0x80) {
556 count
= 1, *p
= (unsigned char)((wc
>> 6) | 0xc0);
557 else if (wc
< 0x10000)
558 count
= 2, *p
= (unsigned char)((wc
>> 12) | 0xe0);
559 else if (wc
< 0x200000)
560 count
= 3, *p
= (unsigned char)((wc
>> 18) | 0xf0);
561 else if (wc
< 0x4000000)
562 count
= 4, *p
= (unsigned char)((wc
>> 24) | 0xf8);
563 else if (wc
<= 0x7fffffff)
564 count
= 5, *p
= (unsigned char)((wc
>> 30) | 0xfC);
567 do *++p
= (unsigned char)(((wc
>> (6 * --count
)) & 0x3f) | 0x80);
576 void tty_printer::put_color(schar color_index
, int back
)
578 if (color_index
== DEFAULT_COLOR_IDX
) {
579 putstring(SGR_DEFAULT
);
580 // set bold and underline again
585 putstring(SGR_ITALIC
);
586 else if (reverse_flag
)
587 putstring(SGR_REVERSE
);
589 putstring(SGR_UNDERLINE
);
591 // set other color again
593 color_index
= back
? curr_back_idx
: curr_fore_idx
;
595 if (color_index
!= DEFAULT_COLOR_IDX
) {
601 putchar(color_index
+ '0');
606 // The possible Unicode combinations for crossing characters.
608 // ` ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
610 // ` ' = 0, ` ' = 1, `|' = 2, `|' = 3
613 static int crossings
[4*4] = {
614 0x0000, 0x2577, 0x2575, 0x2502,
615 0x2576, 0x250C, 0x2514, 0x251C,
616 0x2574, 0x2510, 0x2518, 0x2524,
617 0x2500, 0x252C, 0x2534, 0x253C
620 void tty_printer::end_page(int page_length
)
622 if (page_length
% font::vert
!= 0)
623 error("vertical position at end of page not multiple of vertical resolution");
624 int lines_per_page
= page_length
/ font::vert
;
626 for (last_line
= nlines
; last_line
> 0; last_line
--)
627 if (lines
[last_line
- 1])
630 if (last_line
> lines_per_page
) {
631 error("characters past last line discarded");
634 while (lines
[last_line
]) {
635 glyph
*tem
= lines
[last_line
];
636 lines
[last_line
] = tem
->next
;
639 } while (last_line
> lines_per_page
);
642 for (int i
= 0; i
< last_line
; i
++) {
647 glyph
*tem
= p
->next
;
654 curr_fore_idx
= DEFAULT_COLOR_IDX
;
655 curr_back_idx
= DEFAULT_COLOR_IDX
;
658 for (p
= g
; p
; delete p
, p
= nextp
) {
660 if (p
->mode
& CU_MODE
) {
664 if (nextp
&& p
->hpos
== nextp
->hpos
) {
665 if (p
->draw_mode() == HDRAW_MODE
&&
666 nextp
->draw_mode() == VDRAW_MODE
) {
669 crossings
[((p
->mode
& (START_LINE
|END_LINE
)) >> 4)
670 + ((nextp
->mode
& (START_LINE
|END_LINE
)) >> 6)];
675 if (p
->draw_mode() != 0 && p
->draw_mode() == nextp
->draw_mode()) {
676 nextp
->code
= p
->code
;
679 if (!overstrike_flag
)
682 if (hpos
> p
->hpos
) {
686 } while (hpos
> p
->hpos
);
689 if (horizontal_tab_flag
) {
691 int next_tab_pos
= ((hpos
+ TAB_WIDTH
) / TAB_WIDTH
) * TAB_WIDTH
;
692 if (next_tab_pos
> p
->hpos
)
695 make_underline(p
->w
);
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
);
709 for (; hpos
< p
->hpos
; hpos
++) {
711 make_underline(p
->w
);
712 else if (!old_drawing_scheme
&& is_underline
) {
714 putstring(SGR_NO_ITALIC
);
715 else if (reverse_flag
)
716 putstring(SGR_NO_REVERSE
);
718 putstring(SGR_NO_UNDERLINE
);
724 assert(hpos
== p
->hpos
);
725 if (p
->mode
& COLOR_CHANGE
) {
726 if (!old_drawing_scheme
) {
727 if (p
->fore_color_idx
!= curr_fore_idx
) {
728 put_color(p
->fore_color_idx
, 0);
729 curr_fore_idx
= p
->fore_color_idx
;
731 if (p
->back_color_idx
!= curr_back_idx
) {
732 put_color(p
->back_color_idx
, 1);
733 curr_back_idx
= p
->back_color_idx
;
738 if (p
->mode
& UNDERLINE_MODE
)
739 make_underline(p
->w
);
740 else if (!old_drawing_scheme
&& is_underline
) {
742 putstring(SGR_NO_ITALIC
);
743 else if (reverse_flag
)
744 putstring(SGR_NO_REVERSE
);
746 putstring(SGR_NO_UNDERLINE
);
749 if (p
->mode
& BOLD_MODE
)
750 make_bold(p
->code
, p
->w
);
751 else if (!old_drawing_scheme
&& is_bold
) {
752 putstring(SGR_NO_BOLD
);
755 if (!old_drawing_scheme
) {
756 if (p
->fore_color_idx
!= curr_fore_idx
) {
757 put_color(p
->fore_color_idx
, 0);
758 curr_fore_idx
= p
->fore_color_idx
;
760 if (p
->back_color_idx
!= curr_back_idx
) {
761 put_color(p
->back_color_idx
, 1);
762 curr_back_idx
= p
->back_color_idx
;
766 hpos
+= p
->w
/ font::hor
;
768 if (!old_drawing_scheme
769 && (is_bold
|| is_underline
770 || curr_fore_idx
!= DEFAULT_COLOR_IDX
771 || curr_back_idx
!= DEFAULT_COLOR_IDX
))
772 putstring(SGR_DEFAULT
);
775 if (form_feed_flag
) {
776 if (last_line
< lines_per_page
)
780 for (; last_line
< lines_per_page
; last_line
++)
785 font
*tty_printer::make_font(const char *nm
)
787 return tty_font::load_tty_font(nm
);
790 printer
*make_printer()
792 return new tty_printer(device
);
795 static void update_options()
797 if (old_drawing_scheme
) {
800 bold_underline_mode
= bold_underline_mode_option
;
801 bold_flag
= bold_flag_option
;
802 underline_flag
= underline_flag_option
;
805 italic_flag
= italic_flag_option
;
806 reverse_flag
= reverse_flag_option
;
807 bold_underline_mode
= BOLD_MODE
|UNDERLINE_MODE
;
813 int main(int argc
, char **argv
)
815 program_name
= argv
[0];
816 static char stderr_buf
[BUFSIZ
];
817 if (getenv("GROFF_NO_SGR"))
818 old_drawing_scheme
= 1;
819 setbuf(stderr
, stderr_buf
);
821 static const struct option long_options
[] = {
822 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
823 { "version", no_argument
, 0, 'v' },
826 while ((c
= getopt_long(argc
, argv
, "bBcdfF:hiI:oruUv", long_options
, NULL
))
830 printf("GNU grotty (groff) version %s\n", Version_string
);
834 // Use italic font instead of underlining.
835 italic_flag_option
= 1;
838 // ignore include search path
841 // Do not embolden by overstriking.
842 bold_flag_option
= 0;
845 // Use old scheme for emboldening and underline.
846 old_drawing_scheme
= 1;
850 underline_flag_option
= 0;
853 // Do not overstrike (other than emboldening and underlining).
857 // Use reverse mode instead of underlining.
858 reverse_flag_option
= 1;
861 // Do bold-underlining as bold.
862 bold_underline_mode_option
= BOLD_MODE
;
865 // Do bold-underlining as underlining.
866 bold_underline_mode_option
= UNDERLINE_MODE
;
869 // Use horizontal tabs.
870 horizontal_tab_flag
= 1;
876 font::command_line_font_dir(optarg
);
879 // Ignore \D commands.
882 case CHAR_MAX
+ 1: // --help
897 for (int i
= optind
; i
< argc
; i
++)
903 static void usage(FILE *stream
)
905 fprintf(stream
, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",