2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 2006
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This 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 * This 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.
24 #include "tty-config.h"
30 typedef signed char schar
;
32 declare_ptable(schar
) /* TODO */
33 implement_ptable(schar
) /* TODO */
35 #define putstring(s) fputs(s, stdout)
37 // A character of the output device fits in a 32-bit word.
38 typedef unsigned int output_character
;
40 static int horizontal_tab_flag
= 0;
41 static int form_feed_flag
= 0;
42 static int bold_flag_option
= 1;
44 static int underline_flag_option
= 1;
45 static int underline_flag
;
46 static int overstrike_flag
= 1;
47 static int draw_flag
= 1;
48 static int italic_flag_option
= 0;
49 static int italic_flag
;
50 static int reverse_flag_option
= 0;
51 static int reverse_flag
;
52 static int old_drawing_scheme
= 0;
54 static void update_options();
55 static void usage(FILE *stream
);
57 static int hline_char
= '-';
58 static int vline_char
= '|';
61 UNDERLINE_MODE
= 0x01,
71 // Mode to use for bold-underlining.
72 static unsigned char bold_underline_mode_option
= BOLD_MODE
|UNDERLINE_MODE
;
73 static unsigned char bold_underline_mode
;
78 tty_font(const char *);
83 unsigned char get_mode() { return mode
; }
85 void handle_x_command(int argc
, const char **argv
);
87 static tty_font
*load_tty_font(const char *);
90 tty_font
*tty_font::load_tty_font(const char *s
)
92 tty_font
*f
= new tty_font(s
);
97 const char *num
= f
->get_internal_name();
99 if (num
!= 0 && (n
= strtol(num
, 0, 0)) != 0)
100 f
->mode
= (unsigned char)(n
& (BOLD_MODE
|UNDERLINE_MODE
));
102 f
->mode
&= ~UNDERLINE_MODE
;
104 f
->mode
&= ~BOLD_MODE
;
105 if ((f
->mode
& (BOLD_MODE
|UNDERLINE_MODE
)) == (BOLD_MODE
|UNDERLINE_MODE
))
106 f
->mode
= (unsigned char)((f
->mode
& ~(BOLD_MODE
|UNDERLINE_MODE
))
107 | bold_underline_mode
);
111 tty_font::tty_font(const char *nm
)
116 tty_font::~tty_font()
121 void tty_font::handle_x_command(int argc
, const char **argv
)
123 if (argc
>= 1 && strcmp(argv
[0], "bold") == 0)
125 else if (argc
>= 1 && strcmp(argv
[0], "underline") == 0)
126 mode
|= UNDERLINE_MODE
;
132 static tty_glyph
*free_list
;
140 schar back_color_idx
;
141 schar fore_color_idx
;
142 void *operator new(size_t);
143 void operator delete(void *);
144 inline int draw_mode() { return mode
& (VDRAW_MODE
|HDRAW_MODE
); }
146 return mode
& (VDRAW_MODE
|HDRAW_MODE
|CU_MODE
|COLOR_CHANGE
); }
149 tty_glyph
*tty_glyph::free_list
= 0;
151 void *tty_glyph::operator new(size_t)
154 const int BLOCK
= 1024;
155 free_list
= (tty_glyph
*)new char[sizeof(tty_glyph
) * BLOCK
];
156 for (int i
= 0; i
< BLOCK
- 1; i
++)
157 free_list
[i
].next
= free_list
+ i
+ 1;
158 free_list
[BLOCK
- 1].next
= 0;
160 tty_glyph
*p
= free_list
;
161 free_list
= free_list
->next
;
166 void tty_glyph::operator delete(void *p
)
169 ((tty_glyph
*)p
)->next
= free_list
;
170 free_list
= (tty_glyph
*)p
;
187 PTABLE(schar
) tty_colors
;
188 void make_underline(int);
189 void make_bold(output_character
, int);
190 schar
color_to_idx(color
*);
191 void add_char(output_character
, int, int, int, color
*, color
*,
193 char *make_rgb_string(unsigned int, unsigned int, unsigned int);
194 int tty_color(unsigned int, unsigned int, unsigned int, schar
*,
195 schar
= DEFAULT_COLOR_IDX
);
196 void line(int, int, int, int, color
*, color
*);
197 void draw_line(int *, int, const environment
*);
198 void draw_polygon(int *, int, const environment
*);
201 tty_printer(const char *);
203 void set_char(glyph
*, font
*, const environment
*, int, const char *);
204 void draw(int, int *, int, const environment
*);
205 void special(char *, const environment
*, char);
206 void change_color(const environment
* const);
207 void change_fill_color(const environment
* const);
208 void put_char(output_character
);
209 void put_color(schar
, int);
210 void begin_page(int) { }
212 font
*make_font(const char *);
215 char *tty_printer::make_rgb_string(unsigned int r
,
219 char *s
= new char[8];
221 s
[1] = char(r
& 0xff);
223 s
[3] = char(g
& 0xff);
225 s
[5] = char(b
& 0xff);
228 // avoid null-bytes in string
229 for (int i
= 0; i
< 6; i
++)
237 int tty_printer::tty_color(unsigned int r
,
239 unsigned int b
, schar
*idx
, schar value
)
241 int unknown_color
= 0;
242 char *s
= make_rgb_string(r
, g
, b
);
243 schar
*i
= tty_colors
.lookup(s
);
248 tty_colors
.define(s
, i
);
252 return unknown_color
;
255 tty_printer::tty_printer(const char *dev
) : cached_v(0)
257 is_utf8
= !strcmp(dev
, "utf8");
264 (void)tty_color(0, 0, 0, &dummy
, 0);
265 (void)tty_color(color::MAX_COLOR_VAL
,
266 color::MAX_COLOR_VAL
,
267 color::MAX_COLOR_VAL
, &dummy
, 7);
269 (void)tty_color(color::MAX_COLOR_VAL
, 0, 0, &dummy
, 1);
270 (void)tty_color(0, color::MAX_COLOR_VAL
, 0, &dummy
, 2);
271 (void)tty_color(0, 0, color::MAX_COLOR_VAL
, &dummy
, 4);
272 // yellow, magenta, cyan
273 (void)tty_color(color::MAX_COLOR_VAL
, color::MAX_COLOR_VAL
, 0, &dummy
, 3);
274 (void)tty_color(color::MAX_COLOR_VAL
, 0, color::MAX_COLOR_VAL
, &dummy
, 5);
275 (void)tty_color(0, color::MAX_COLOR_VAL
, color::MAX_COLOR_VAL
, &dummy
, 6);
277 lines
= new tty_glyph
*[nlines
];
278 for (int i
= 0; i
< nlines
; i
++)
283 tty_printer::~tty_printer()
288 void tty_printer::make_underline(int w
)
290 if (old_drawing_scheme
) {
292 warning("can't underline zero-width character");
294 int n
= w
/ font::hor
;
295 for (int i
= 0; i
< n
; i
++)
297 for (int j
= 0; j
< n
; j
++)
304 putstring(SGR_ITALIC
);
305 else if (reverse_flag
)
306 putstring(SGR_REVERSE
);
308 putstring(SGR_UNDERLINE
);
314 void tty_printer::make_bold(output_character c
, int w
)
316 if (old_drawing_scheme
) {
318 warning("can't print zero-width character in bold");
320 int n
= w
/ font::hor
;
322 for (int i
= 0; i
< n
; i
++)
333 schar
tty_printer::color_to_idx(color
*col
)
335 if (col
->is_default())
336 return DEFAULT_COLOR_IDX
;
337 unsigned int r
, g
, b
;
338 col
->get_rgb(&r
, &g
, &b
);
340 if (tty_color(r
, g
, b
, &idx
)) {
341 char *s
= col
->print_color();
342 error("Unknown color (%1) mapped to default", s
);
348 void tty_printer::set_char(glyph
*g
, font
*f
, const environment
*env
,
351 if (w
% font::hor
!= 0)
352 fatal("width of character not a multiple of horizontal resolution");
353 add_char(f
->get_code(g
), w
,
354 env
->hpos
, env
->vpos
,
356 ((tty_font
*)f
)->get_mode());
359 void tty_printer::add_char(output_character c
, int w
,
361 color
*fore
, color
*back
,
365 // This is too expensive.
366 if (h
% font::hor
!= 0)
367 fatal("horizontal position not a multiple of horizontal resolution");
369 int hpos
= h
/ font::hor
;
370 if (hpos
< HPOS_MIN
|| hpos
> HPOS_MAX
) {
371 error("character with ridiculous horizontal position discarded");
375 if (v
== cached_v
&& cached_v
!= 0)
378 if (v
% font::vert
!= 0)
379 fatal("vertical position not a multiple of vertical resolution");
380 vpos
= v
/ font::vert
;
382 tty_glyph
**old_lines
= lines
;
383 lines
= new tty_glyph
*[vpos
+ 1];
384 memcpy(lines
, old_lines
, nlines
* sizeof(tty_glyph
*));
385 for (int i
= nlines
; i
<= vpos
; i
++)
390 // Note that the first output line corresponds to groff
391 // position font::vert.
393 error("character above first line discarded");
399 tty_glyph
*g
= new tty_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', 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
,
431 for (p
= arg
; *p
== ' ' || *p
== '\n'; p
++)
434 for (; *p
!= '\0' && *p
!= ':' && *p
!= ' ' && *p
!= '\n'; p
++)
436 if (*p
== '\0' || strncmp(tag
, "tty", p
- tag
) != 0) {
437 error("X command without `tty:' tag ignored");
441 for (; *p
== ' ' || *p
== '\n'; p
++)
444 for (; *p
!= '\0' && *p
!= ' ' && *p
!= '\n'; p
++)
446 if (*command
== '\0') {
447 error("empty X command ignored");
450 if (strncmp(command
, "sgr", p
- command
) == 0) {
451 for (; *p
== ' ' || *p
== '\n'; p
++)
454 if (*p
!= '\0' && sscanf(p
, "%d", &n
) == 1 && n
== 0)
455 old_drawing_scheme
= 1;
457 old_drawing_scheme
= 0;
462 void tty_printer::change_color(const environment
* const env
)
464 add_char(0, 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
467 void tty_printer::change_fill_color(const environment
* const env
)
469 add_char(0, 0, env
->hpos
, env
->vpos
, env
->col
, env
->fill
, COLOR_CHANGE
);
472 void tty_printer::draw(int code
, int *p
, int np
, const environment
*env
)
477 draw_line(p
, np
, env
);
479 draw_polygon(p
, np
, env
);
482 void tty_printer::draw_polygon(int *p
, int np
, const environment
*env
)
485 error("even number of arguments required for polygon");
489 error("no arguments for polygon");
492 // We only draw polygons which consist entirely of horizontal and
496 for (int i
= 0; i
< np
; i
+= 2) {
497 if (!(p
[i
] == 0 || p
[i
+ 1] == 0))
502 if (!(hpos
== 0 || vpos
== 0))
504 int start_hpos
= env
->hpos
;
505 int start_vpos
= env
->vpos
;
508 for (int i
= 0; i
< np
; i
+= 2) {
509 line(hpos
, vpos
, p
[i
], p
[i
+ 1], env
->col
, env
->fill
);
513 line(hpos
, vpos
, start_hpos
- hpos
, start_vpos
- vpos
,
514 env
->col
, env
->fill
);
517 void tty_printer::draw_line(int *p
, int np
, const environment
*env
)
520 error("2 arguments required for line");
523 line(env
->hpos
, env
->vpos
, p
[0], p
[1], env
->col
, env
->fill
);
526 void tty_printer::line(int hpos
, int vpos
, int dx
, int dy
,
527 color
*col
, color
*fill
)
538 add_char(vline_char
, font::hor
, hpos
, v
, col
, fill
,
539 VDRAW_MODE
|START_LINE
|END_LINE
);
541 add_char(vline_char
, font::hor
, hpos
, v
, col
, fill
,
542 VDRAW_MODE
|START_LINE
);
546 add_char(vline_char
, font::hor
, hpos
, v
, col
, fill
,
547 VDRAW_MODE
|START_LINE
|END_LINE
);
551 add_char(vline_char
, font::hor
, hpos
, v
, col
, fill
,
552 VDRAW_MODE
|END_LINE
);
564 add_char(hline_char
, font::hor
, h
, vpos
, col
, fill
,
565 HDRAW_MODE
|START_LINE
|END_LINE
);
567 add_char(hline_char
, font::hor
, h
, vpos
, col
, fill
,
568 HDRAW_MODE
|START_LINE
);
572 add_char(hline_char
, font::hor
, h
, vpos
, col
, fill
,
573 HDRAW_MODE
|START_LINE
|END_LINE
);
577 add_char(hline_char
, font::hor
, h
, vpos
, col
, fill
,
578 HDRAW_MODE
|END_LINE
);
583 void tty_printer::put_char(output_character wc
)
585 if (is_utf8
&& wc
>= 0x80) {
590 count
= 1, *p
= (unsigned char)((wc
>> 6) | 0xc0);
591 else if (wc
< 0x10000)
592 count
= 2, *p
= (unsigned char)((wc
>> 12) | 0xe0);
593 else if (wc
< 0x200000)
594 count
= 3, *p
= (unsigned char)((wc
>> 18) | 0xf0);
595 else if (wc
< 0x4000000)
596 count
= 4, *p
= (unsigned char)((wc
>> 24) | 0xf8);
597 else if (wc
<= 0x7fffffff)
598 count
= 5, *p
= (unsigned char)((wc
>> 30) | 0xfC);
601 do *++p
= (unsigned char)(((wc
>> (6 * --count
)) & 0x3f) | 0x80);
610 void tty_printer::put_color(schar color_index
, int back
)
612 if (color_index
== DEFAULT_COLOR_IDX
) {
613 putstring(SGR_DEFAULT
);
614 // set bold and underline again
619 putstring(SGR_ITALIC
);
620 else if (reverse_flag
)
621 putstring(SGR_REVERSE
);
623 putstring(SGR_UNDERLINE
);
625 // set other color again
627 color_index
= back
? curr_back_idx
: curr_fore_idx
;
629 if (color_index
!= DEFAULT_COLOR_IDX
) {
635 putchar(color_index
+ '0');
640 // The possible Unicode combinations for crossing characters.
642 // ` ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
644 // ` ' = 0, ` ' = 1, `|' = 2, `|' = 3
647 static output_character crossings
[4*4] = {
648 0x0000, 0x2577, 0x2575, 0x2502,
649 0x2576, 0x250C, 0x2514, 0x251C,
650 0x2574, 0x2510, 0x2518, 0x2524,
651 0x2500, 0x252C, 0x2534, 0x253C
654 void tty_printer::end_page(int page_length
)
656 if (page_length
% font::vert
!= 0)
657 error("vertical position at end of page not multiple of vertical resolution");
658 int lines_per_page
= page_length
/ font::vert
;
660 for (last_line
= nlines
; last_line
> 0; last_line
--)
661 if (lines
[last_line
- 1])
664 if (last_line
> lines_per_page
) {
665 error("characters past last line discarded");
668 while (lines
[last_line
]) {
669 tty_glyph
*tem
= lines
[last_line
];
670 lines
[last_line
] = tem
->next
;
673 } while (last_line
> lines_per_page
);
676 for (int i
= 0; i
< last_line
; i
++) {
677 tty_glyph
*p
= lines
[i
];
681 tty_glyph
*tem
= p
->next
;
688 curr_fore_idx
= DEFAULT_COLOR_IDX
;
689 curr_back_idx
= DEFAULT_COLOR_IDX
;
692 for (p
= g
; p
; delete p
, p
= nextp
) {
694 if (p
->mode
& CU_MODE
) {
695 cu_flag
= (p
->code
!= 0);
698 if (nextp
&& p
->hpos
== nextp
->hpos
) {
699 if (p
->draw_mode() == HDRAW_MODE
&&
700 nextp
->draw_mode() == VDRAW_MODE
) {
703 crossings
[((p
->mode
& (START_LINE
|END_LINE
)) >> 4)
704 + ((nextp
->mode
& (START_LINE
|END_LINE
)) >> 6)];
709 if (p
->draw_mode() != 0 && p
->draw_mode() == nextp
->draw_mode()) {
710 nextp
->code
= p
->code
;
713 if (!overstrike_flag
)
716 if (hpos
> p
->hpos
) {
720 } while (hpos
> p
->hpos
);
723 if (horizontal_tab_flag
) {
725 int next_tab_pos
= ((hpos
+ TAB_WIDTH
) / TAB_WIDTH
) * TAB_WIDTH
;
726 if (next_tab_pos
> p
->hpos
)
729 make_underline(p
->w
);
730 else if (!old_drawing_scheme
&& is_underline
) {
732 putstring(SGR_NO_ITALIC
);
733 else if (reverse_flag
)
734 putstring(SGR_NO_REVERSE
);
736 putstring(SGR_NO_UNDERLINE
);
743 for (; hpos
< p
->hpos
; hpos
++) {
745 make_underline(p
->w
);
746 else if (!old_drawing_scheme
&& is_underline
) {
748 putstring(SGR_NO_ITALIC
);
749 else if (reverse_flag
)
750 putstring(SGR_NO_REVERSE
);
752 putstring(SGR_NO_UNDERLINE
);
758 assert(hpos
== p
->hpos
);
759 if (p
->mode
& COLOR_CHANGE
) {
760 if (!old_drawing_scheme
) {
761 if (p
->fore_color_idx
!= curr_fore_idx
) {
762 put_color(p
->fore_color_idx
, 0);
763 curr_fore_idx
= p
->fore_color_idx
;
765 if (p
->back_color_idx
!= curr_back_idx
) {
766 put_color(p
->back_color_idx
, 1);
767 curr_back_idx
= p
->back_color_idx
;
772 if (p
->mode
& UNDERLINE_MODE
)
773 make_underline(p
->w
);
774 else if (!old_drawing_scheme
&& is_underline
) {
776 putstring(SGR_NO_ITALIC
);
777 else if (reverse_flag
)
778 putstring(SGR_NO_REVERSE
);
780 putstring(SGR_NO_UNDERLINE
);
783 if (p
->mode
& BOLD_MODE
)
784 make_bold(p
->code
, p
->w
);
785 else if (!old_drawing_scheme
&& is_bold
) {
786 putstring(SGR_NO_BOLD
);
789 if (!old_drawing_scheme
) {
790 if (p
->fore_color_idx
!= curr_fore_idx
) {
791 put_color(p
->fore_color_idx
, 0);
792 curr_fore_idx
= p
->fore_color_idx
;
794 if (p
->back_color_idx
!= curr_back_idx
) {
795 put_color(p
->back_color_idx
, 1);
796 curr_back_idx
= p
->back_color_idx
;
800 hpos
+= p
->w
/ font::hor
;
802 if (!old_drawing_scheme
803 && (is_bold
|| is_underline
804 || curr_fore_idx
!= DEFAULT_COLOR_IDX
805 || curr_back_idx
!= DEFAULT_COLOR_IDX
))
806 putstring(SGR_DEFAULT
);
809 if (form_feed_flag
) {
810 if (last_line
< lines_per_page
)
814 for (; last_line
< lines_per_page
; last_line
++)
819 font
*tty_printer::make_font(const char *nm
)
821 return tty_font::load_tty_font(nm
);
824 printer
*make_printer()
826 return new tty_printer(device
);
829 static void update_options()
831 if (old_drawing_scheme
) {
834 bold_underline_mode
= bold_underline_mode_option
;
835 bold_flag
= bold_flag_option
;
836 underline_flag
= underline_flag_option
;
839 italic_flag
= italic_flag_option
;
840 reverse_flag
= reverse_flag_option
;
841 bold_underline_mode
= BOLD_MODE
|UNDERLINE_MODE
;
847 int main(int argc
, char **argv
)
849 program_name
= argv
[0];
850 static char stderr_buf
[BUFSIZ
];
851 if (getenv(U_ROFF_NO_SGR
))
852 old_drawing_scheme
= 1;
853 setbuf(stderr
, stderr_buf
);
855 static const struct option long_options
[] = {
856 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
857 { "version", no_argument
, 0, 'v' },
860 while ((c
= getopt_long(argc
, argv
, "bBcdfF:hiI:oruUv", long_options
, NULL
))
864 puts(L_D_TTY
" (" T_ROFF
") v" VERSION
);
868 // Use italic font instead of underlining.
869 italic_flag_option
= 1;
872 // ignore include search path
875 // Do not embolden by overstriking.
876 bold_flag_option
= 0;
879 // Use old scheme for emboldening and underline.
880 old_drawing_scheme
= 1;
884 underline_flag_option
= 0;
887 // Do not overstrike (other than emboldening and underlining).
891 // Use reverse mode instead of underlining.
892 reverse_flag_option
= 1;
895 // Do bold-underlining as bold.
896 bold_underline_mode_option
= BOLD_MODE
;
899 // Do bold-underlining as underlining.
900 bold_underline_mode_option
= UNDERLINE_MODE
;
903 // Use horizontal tabs.
904 horizontal_tab_flag
= 1;
910 font::command_line_font_dir(optarg
);
913 // Ignore \D commands.
916 case CHAR_MAX
+ 1: // --help
931 for (int i
= optind
; i
< argc
; i
++)
937 static void usage(FILE *stream
)
939 fprintf(stream
, "Synopsis: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",