2 /* Copyright (C) 1989-2000, 2001, 2002 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 2, or (at your option) any later
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 along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 extern "C" const char *Version_string
;
26 #define putstring(s) fputs(s, stdout)
29 #define SHRT_MIN (-32768)
33 #define SHRT_MAX 32767
38 static int horizontal_tab_flag
= 0;
39 static int form_feed_flag
= 0;
40 static int bold_flag
= 1;
41 static int underline_flag
= 1;
42 static int overstrike_flag
= 1;
43 static int draw_flag
= 1;
44 static int italic_flag
= 0;
45 static int reverse_flag
= 0;
46 static int old_drawing_scheme
= 0;
49 UNDERLINE_MODE
= 0x01,
57 // Mode to use for bold-underlining.
58 static unsigned char bold_underline_mode
= BOLD_MODE
|UNDERLINE_MODE
;
60 #ifndef IS_EBCDIC_HOST
66 // SGR handling (ISO 6429)
67 #define SGR_BOLD CSI "1m"
68 #define SGR_NO_BOLD CSI "22m"
69 #define SGR_ITALIC CSI "3m"
70 #define SGR_NO_ITALIC CSI "23m"
71 #define SGR_UNDERLINE CSI "4m"
72 #define SGR_NO_UNDERLINE CSI "24m"
73 #define SGR_REVERSE CSI "7m"
74 #define SGR_NO_REVERSE CSI "27m"
75 // many terminals can't handle `CSI 39 m' and `CSI 49 m' to reset
76 // the foreground and bachground color, respectively; thus we use
77 // `CSI 0 m' exclusively
78 #define SGR_DEFAULT CSI "0m"
80 #define TTY_MAX_COLORS 8
81 #define DEFAULT_COLOR_IDX TTY_MAX_COLORS
83 class tty_font
: public font
{
84 tty_font(const char *);
88 unsigned char get_mode() { return mode
; }
90 void handle_x_command(int argc
, const char **argv
);
92 static tty_font
*load_tty_font(const char *);
95 tty_font
*tty_font::load_tty_font(const char *s
)
97 tty_font
*f
= new tty_font(s
);
102 const char *num
= f
->get_internal_name();
104 if (num
!= 0 && (n
= strtol(num
, 0, 0)) != 0)
105 f
->mode
= int(n
& (BOLD_MODE
|UNDERLINE_MODE
));
107 f
->mode
&= ~UNDERLINE_MODE
;
109 f
->mode
&= ~BOLD_MODE
;
110 if ((f
->mode
& (BOLD_MODE
|UNDERLINE_MODE
)) == (BOLD_MODE
|UNDERLINE_MODE
))
111 f
->mode
= (f
->mode
& ~(BOLD_MODE
|UNDERLINE_MODE
)) | bold_underline_mode
;
115 tty_font::tty_font(const char *nm
)
120 tty_font::~tty_font()
125 void tty_font::handle_x_command(int argc
, const char **argv
)
127 if (argc
>= 1 && strcmp(argv
[0], "bold") == 0)
129 else if (argc
>= 1 && strcmp(argv
[0], "underline") == 0)
130 mode
|= UNDERLINE_MODE
;
135 static glyph
*free_list
;
141 unsigned char back_color_idx
;
142 unsigned char fore_color_idx
;
143 void *operator new(size_t);
144 void operator delete(void *);
145 inline int draw_mode() { return mode
& (VDRAW_MODE
|HDRAW_MODE
); }
147 return mode
& (VDRAW_MODE
|HDRAW_MODE
|CU_MODE
|COLOR_CHANGE
); }
150 glyph
*glyph::free_list
= 0;
152 void *glyph::operator new(size_t)
155 const int BLOCK
= 1024;
156 free_list
= (glyph
*)new char[sizeof(glyph
) * BLOCK
];
157 for (int i
= 0; i
< BLOCK
- 1; i
++)
158 free_list
[i
].next
= free_list
+ i
+ 1;
159 free_list
[BLOCK
- 1].next
= 0;
161 glyph
*p
= free_list
;
162 free_list
= free_list
->next
;
167 void glyph::operator delete(void *p
)
170 ((glyph
*)p
)->next
= free_list
;
171 free_list
= (glyph
*)p
;
175 class tty_printer
: public printer
{
181 unsigned char curr_fore_idx
;
182 unsigned char curr_back_idx
;
186 color tty_colors
[TTY_MAX_COLORS
];
187 void make_underline();
188 void make_bold(unsigned int);
189 unsigned char color_to_idx(color
*col
);
190 void add_char(unsigned int, int, int, color
*, color
*, unsigned char);
192 tty_printer(const char *device
);
194 void set_char(int, font
*, const environment
*, int, const char *name
);
195 void draw(int code
, int *p
, int np
, const environment
*env
);
196 void special(char *arg
, const environment
*env
, char type
);
197 void change_color(const environment
*env
);
198 void change_fill_color(const environment
*env
);
199 void put_char(unsigned int);
200 void put_color(unsigned char, int);
201 void begin_page(int) { }
202 void end_page(int page_length
);
203 font
*make_font(const char *);
206 tty_printer::tty_printer(const char *device
) : cached_v(0)
208 is_utf8
= !strcmp(device
, "utf8");
209 tty_colors
[0].set_rgb(0, // black
212 tty_colors
[1].set_rgb(color::MAX_COLOR_VAL
, // red
215 tty_colors
[2].set_rgb(0, // green
216 color::MAX_COLOR_VAL
,
218 tty_colors
[3].set_rgb(color::MAX_COLOR_VAL
, // yellow
219 color::MAX_COLOR_VAL
,
221 tty_colors
[4].set_rgb(0, // blue
223 color::MAX_COLOR_VAL
);
224 tty_colors
[5].set_rgb(color::MAX_COLOR_VAL
, // magenta
226 color::MAX_COLOR_VAL
);
227 tty_colors
[6].set_rgb(0, // cyan
228 color::MAX_COLOR_VAL
,
229 color::MAX_COLOR_VAL
);
230 tty_colors
[7].set_rgb(color::MAX_COLOR_VAL
, // white
231 color::MAX_COLOR_VAL
,
232 color::MAX_COLOR_VAL
);
234 lines
= new glyph
*[nlines
];
235 for (int i
= 0; i
< nlines
; i
++)
240 tty_printer::~tty_printer()
245 void tty_printer::make_underline()
247 if (old_drawing_scheme
) {
254 putstring(SGR_ITALIC
);
255 else if (reverse_flag
)
256 putstring(SGR_REVERSE
);
258 putstring(SGR_UNDERLINE
);
264 void tty_printer::make_bold(unsigned int c
)
266 if (old_drawing_scheme
) {
277 unsigned char tty_printer::color_to_idx(color
*col
)
279 if (col
->is_default())
280 return DEFAULT_COLOR_IDX
;
281 for (int i
= 0; i
< TTY_MAX_COLORS
; i
++)
282 if (*col
== tty_colors
[i
])
283 return (unsigned char)i
;
285 col
->get_rgb(&r
, &g
, &b
);
286 error("Unknown color (%1, %2, %3) mapped to default", r
, g
, b
);
287 return DEFAULT_COLOR_IDX
;
290 void tty_printer::set_char(int i
, font
*f
, const environment
*env
,
291 int w
, const char *name
)
294 fatal("width of character not equal to horizontal resolution");
295 add_char(f
->get_code(i
),
296 env
->hpos
, env
->vpos
,
298 ((tty_font
*)f
)->get_mode());
301 void tty_printer::add_char(unsigned int c
,
303 color
*fore
, color
*back
,
307 // This is too expensive.
308 if (h
% font::hor
!= 0)
309 fatal("horizontal position not a multiple of horizontal resolution");
311 int hpos
= h
/ font::hor
;
312 if (hpos
< SHRT_MIN
|| hpos
> SHRT_MAX
) {
313 error("character with ridiculous horizontal position discarded");
317 if (v
== cached_v
&& cached_v
!= 0)
320 if (v
% font::vert
!= 0)
321 fatal("vertical position not a multiple of vertical resolution");
322 vpos
= v
/ font::vert
;
324 glyph
**old_lines
= lines
;
325 lines
= new glyph
*[vpos
+ 1];
326 memcpy(lines
, old_lines
, nlines
* sizeof(glyph
*));
327 for (int i
= nlines
; i
<= vpos
; i
++)
332 // Note that the first output line corresponds to groff
333 // position font::vert.
335 error("character above first line discarded");
341 glyph
*g
= new glyph
;
344 g
->fore_color_idx
= color_to_idx(fore
);
345 g
->back_color_idx
= color_to_idx(back
);
348 // The list will be reversed later. After reversal, it must be in
349 // increasing order of hpos, with COLOR_CHANGE and CU specials before
350 // HDRAW characters before VDRAW characters before normal characters
351 // at each hpos, and otherwise in order of occurrence.
354 for (pp
= lines
+ (vpos
- 1); *pp
; pp
= &(*pp
)->next
)
355 if ((*pp
)->hpos
< hpos
356 || ((*pp
)->hpos
== hpos
&& (*pp
)->order() >= g
->order()))
362 void tty_printer::special(char *arg
, const environment
*env
, char type
)
365 add_char(*arg
- '0', env
->hpos
, env
->vpos
, env
->col
, env
->fill
, CU_MODE
);
371 for (p
= arg
; *p
== ' ' || *p
== '\n'; p
++)
374 for (; *p
!= '\0' && *p
!= ':' && *p
!= ' ' && *p
!= '\n'; p
++)
376 if (*p
== '\0' || strncmp(tag
, "tty", p
- tag
) != 0) {
377 error("X command without `tty:' tag ignored");
381 for (; *p
== ' ' || *p
== '\n'; p
++)
384 for (; *p
!= '\0' && *p
!= ' ' && *p
!= '\n'; p
++)
386 if (*command
== '\0') {
387 error("empty X command ignored");
390 if (strncmp(command
, "sgr", p
- command
) == 0) {
391 for (; *p
== ' ' || *p
== '\n'; p
++)
394 if (*p
!= '\0' && sscanf(p
, "%d", &n
) == 1 && n
== 0)
395 old_drawing_scheme
= 1;
397 old_drawing_scheme
= 0;
401 void tty_printer::change_color(const environment
*env
)
403 add_char(0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
406 void tty_printer::change_fill_color(const environment
*env
)
408 add_char(0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
411 void tty_printer::draw(int code
, int *p
, int np
, const environment
*env
)
413 if (code
!= 'l' || !draw_flag
)
416 error("2 arguments required for line");
428 add_char('|', env
->hpos
, v
, env
->col
, env
->fill
, VDRAW_MODE
);
442 add_char('-', h
, env
->vpos
, env
->col
, env
->fill
, HDRAW_MODE
);
449 void tty_printer::put_char(unsigned int wc
)
451 if (is_utf8
&& wc
>= 0x80) {
456 count
= 1, *p
= (unsigned char)((wc
>> 6) | 0xc0);
457 else if (wc
< 0x10000)
458 count
= 2, *p
= (unsigned char)((wc
>> 12) | 0xe0);
459 else if (wc
< 0x200000)
460 count
= 3, *p
= (unsigned char)((wc
>> 18) | 0xf0);
461 else if (wc
< 0x4000000)
462 count
= 4, *p
= (unsigned char)((wc
>> 24) | 0xf8);
463 else if (wc
<= 0x7fffffff)
464 count
= 5, *p
= (unsigned char)((wc
>> 30) | 0xfC);
467 do *++p
= (unsigned char)(((wc
>> (6 * --count
)) & 0x3f) | 0x80);
476 void tty_printer::put_color(unsigned char color_index
, int back
)
478 if (color_index
== DEFAULT_COLOR_IDX
) {
479 putstring(SGR_DEFAULT
);
480 // set bold and underline again
485 putstring(SGR_ITALIC
);
486 else if (reverse_flag
)
487 putstring(SGR_REVERSE
);
489 putstring(SGR_UNDERLINE
);
491 // set other color again
493 color_index
= back
? curr_back_idx
: curr_fore_idx
;
500 putchar(color_index
+ '0');
504 void tty_printer::end_page(int page_length
)
506 if (page_length
% font::vert
!= 0)
507 error("vertical position at end of page not multiple of vertical resolution");
508 int lines_per_page
= page_length
/ font::vert
;
510 for (last_line
= nlines
; last_line
> 0; last_line
--)
511 if (lines
[last_line
- 1])
514 if (last_line
> lines_per_page
) {
515 error("characters past last line discarded");
518 while (lines
[last_line
]) {
519 glyph
*tem
= lines
[last_line
];
520 lines
[last_line
] = tem
->next
;
523 } while (last_line
> lines_per_page
);
526 for (int i
= 0; i
< last_line
; i
++) {
531 glyph
*tem
= p
->next
;
538 curr_fore_idx
= DEFAULT_COLOR_IDX
;
539 curr_back_idx
= DEFAULT_COLOR_IDX
;
542 for (p
= g
; p
; delete p
, p
= nextp
) {
544 if (p
->mode
& CU_MODE
) {
548 if (nextp
&& p
->hpos
== nextp
->hpos
) {
549 if (p
->draw_mode() == HDRAW_MODE
&&
550 nextp
->draw_mode() == VDRAW_MODE
) {
554 if (p
->draw_mode() != 0 && p
->draw_mode() == nextp
->draw_mode()) {
555 nextp
->code
= p
->code
;
558 if (!overstrike_flag
)
561 if (hpos
> p
->hpos
) {
565 } while (hpos
> p
->hpos
);
568 if (horizontal_tab_flag
) {
570 int next_tab_pos
= ((hpos
+ TAB_WIDTH
) / TAB_WIDTH
) * TAB_WIDTH
;
571 if (next_tab_pos
> p
->hpos
)
575 else if (!old_drawing_scheme
&& is_underline
) {
577 putstring(SGR_NO_ITALIC
);
578 else if (reverse_flag
)
579 putstring(SGR_NO_REVERSE
);
581 putstring(SGR_NO_UNDERLINE
);
588 for (; hpos
< p
->hpos
; hpos
++) {
591 else if (!old_drawing_scheme
&& is_underline
) {
593 putstring(SGR_NO_ITALIC
);
594 else if (reverse_flag
)
595 putstring(SGR_NO_REVERSE
);
597 putstring(SGR_NO_UNDERLINE
);
603 assert(hpos
== p
->hpos
);
604 if (p
->mode
& COLOR_CHANGE
) {
605 if (!old_drawing_scheme
) {
606 if (p
->fore_color_idx
!= curr_fore_idx
) {
607 put_color(p
->fore_color_idx
, 0);
608 curr_fore_idx
= p
->fore_color_idx
;
610 if (p
->back_color_idx
!= curr_back_idx
) {
611 put_color(p
->back_color_idx
, 1);
612 curr_back_idx
= p
->back_color_idx
;
617 if (p
->mode
& UNDERLINE_MODE
)
619 else if (!old_drawing_scheme
&& is_underline
) {
621 putstring(SGR_NO_ITALIC
);
622 else if (reverse_flag
)
623 putstring(SGR_NO_REVERSE
);
625 putstring(SGR_NO_UNDERLINE
);
628 if (p
->mode
& BOLD_MODE
)
630 else if (!old_drawing_scheme
&& is_bold
) {
631 putstring(SGR_NO_BOLD
);
634 if (!old_drawing_scheme
) {
635 if (p
->fore_color_idx
!= curr_fore_idx
) {
636 put_color(p
->fore_color_idx
, 0);
637 curr_fore_idx
= p
->fore_color_idx
;
639 if (p
->back_color_idx
!= curr_back_idx
) {
640 put_color(p
->back_color_idx
, 1);
641 curr_back_idx
= p
->back_color_idx
;
647 if (!old_drawing_scheme
648 && (is_bold
|| is_underline
649 || curr_fore_idx
!= DEFAULT_COLOR_IDX
650 || curr_back_idx
!= DEFAULT_COLOR_IDX
))
651 putstring(SGR_DEFAULT
);
654 if (form_feed_flag
) {
655 if (last_line
< lines_per_page
)
659 for (; last_line
< lines_per_page
; last_line
++)
664 font
*tty_printer::make_font(const char *nm
)
666 return tty_font::load_tty_font(nm
);
669 printer
*make_printer()
671 return new tty_printer(device
);
674 static void usage(FILE *stream
);
676 int main(int argc
, char **argv
)
678 program_name
= argv
[0];
679 static char stderr_buf
[BUFSIZ
];
680 if (getenv("GROFF_NO_SGR"))
681 old_drawing_scheme
= 1;
682 setbuf(stderr
, stderr_buf
);
684 static const struct option long_options
[] = {
685 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
686 { "version", no_argument
, 0, 'v' },
689 while ((c
= getopt_long(argc
, argv
, "bBcdfF:hioruUv", long_options
, NULL
))
693 printf("GNU grotty (groff) version %s\n", Version_string
);
697 // Use italic font instead of underlining.
701 // Do not embolden by overstriking.
705 // Use old scheme for emboldening and underline.
706 old_drawing_scheme
= 1;
713 // Do not overstrike (other than emboldening and underlining).
717 // Use reverse mode instead of underlining.
721 // Do bold-underlining as bold.
722 bold_underline_mode
= BOLD_MODE
;
725 // Do bold-underlining as underlining.
726 bold_underline_mode
= UNDERLINE_MODE
;
729 // Use horizontal tabs.
730 horizontal_tab_flag
= 1;
736 font::command_line_font_dir(optarg
);
739 // Ignore \D commands.
742 case CHAR_MAX
+ 1: // --help
753 if (old_drawing_scheme
) {
758 bold_underline_mode
= BOLD_MODE
|UNDERLINE_MODE
;
765 for (int i
= optind
; i
< argc
; i
++)
772 static void usage(FILE *stream
)
774 fprintf(stream
, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",