Add the new -r option to grotty. It is similar to the -i option
[s-roff.git] / src / devices / grotty / tty.cc
blob7cfb658b4e5df513f64430b211980bd195307222
1 // -*- C++ -*-
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
10 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
15 for more details.
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. */
21 #include "driver.h"
22 #include "device.h"
24 extern "C" const char *Version_string;
26 #define putstring(s) fputs(s, stdout)
28 #ifndef SHRT_MIN
29 #define SHRT_MIN (-32768)
30 #endif
32 #ifndef SHRT_MAX
33 #define SHRT_MAX 32767
34 #endif
36 #define TAB_WIDTH 8
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;
48 enum {
49 UNDERLINE_MODE = 0x01,
50 BOLD_MODE = 0x02,
51 VDRAW_MODE = 0x04,
52 HDRAW_MODE = 0x08,
53 CU_MODE = 0x10,
54 COLOR_CHANGE = 0x20
57 // Mode to use for bold-underlining.
58 static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
60 #ifndef IS_EBCDIC_HOST
61 #define CSI "\033["
62 #else
63 #define CSI "\047["
64 #endif
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 *);
85 unsigned char mode;
86 public:
87 ~tty_font();
88 unsigned char get_mode() { return mode; }
89 #if 0
90 void handle_x_command(int argc, const char **argv);
91 #endif
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);
98 if (!f->load()) {
99 delete f;
100 return 0;
102 const char *num = f->get_internal_name();
103 long n;
104 if (num != 0 && (n = strtol(num, 0, 0)) != 0)
105 f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE));
106 if (!underline_flag)
107 f->mode &= ~UNDERLINE_MODE;
108 if (!bold_flag)
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;
112 return f;
115 tty_font::tty_font(const char *nm)
116 : font(nm), mode(0)
120 tty_font::~tty_font()
124 #if 0
125 void tty_font::handle_x_command(int argc, const char **argv)
127 if (argc >= 1 && strcmp(argv[0], "bold") == 0)
128 mode |= BOLD_MODE;
129 else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
130 mode |= UNDERLINE_MODE;
132 #endif
134 class glyph {
135 static glyph *free_list;
136 public:
137 glyph *next;
138 short hpos;
139 unsigned int code;
140 unsigned char mode;
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); }
146 inline int order() {
147 return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
150 glyph *glyph::free_list = 0;
152 void *glyph::operator new(size_t)
154 if (!free_list) {
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;
163 p->next = 0;
164 return p;
167 void glyph::operator delete(void *p)
169 if (p) {
170 ((glyph *)p)->next = free_list;
171 free_list = (glyph *)p;
175 class tty_printer : public printer {
176 int is_utf8;
177 glyph **lines;
178 int nlines;
179 int cached_v;
180 int cached_vpos;
181 unsigned char curr_fore_idx;
182 unsigned char curr_back_idx;
183 int is_underline;
184 int is_bold;
185 int cu_flag;
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);
191 public:
192 tty_printer(const char *device);
193 ~tty_printer();
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);
233 nlines = 66;
234 lines = new glyph *[nlines];
235 for (int i = 0; i < nlines; i++)
236 lines[i] = 0;
237 cu_flag = 0;
240 tty_printer::~tty_printer()
242 a_delete lines;
245 void tty_printer::make_underline()
247 if (old_drawing_scheme) {
248 putchar('_');
249 putchar('\b');
251 else {
252 if (!is_underline) {
253 if (italic_flag)
254 putstring(SGR_ITALIC);
255 else if (reverse_flag)
256 putstring(SGR_REVERSE);
257 else
258 putstring(SGR_UNDERLINE);
260 is_underline = 1;
264 void tty_printer::make_bold(unsigned int c)
266 if (old_drawing_scheme) {
267 put_char(c);
268 putchar('\b');
270 else {
271 if (!is_bold)
272 putstring(SGR_BOLD);
273 is_bold = 1;
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;
284 unsigned r, g, b;
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)
293 if (w != font::hor)
294 fatal("width of character not equal to horizontal resolution");
295 add_char(f->get_code(i),
296 env->hpos, env->vpos,
297 env->col, env->fill,
298 ((tty_font *)f)->get_mode());
301 void tty_printer::add_char(unsigned int c,
302 int h, int v,
303 color *fore, color *back,
304 unsigned char mode)
306 #if 0
307 // This is too expensive.
308 if (h % font::hor != 0)
309 fatal("horizontal position not a multiple of horizontal resolution");
310 #endif
311 int hpos = h / font::hor;
312 if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
313 error("character with ridiculous horizontal position discarded");
314 return;
316 int vpos;
317 if (v == cached_v && cached_v != 0)
318 vpos = cached_vpos;
319 else {
320 if (v % font::vert != 0)
321 fatal("vertical position not a multiple of vertical resolution");
322 vpos = v / font::vert;
323 if (vpos > nlines) {
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++)
328 lines[i] = 0;
329 a_delete old_lines;
330 nlines = vpos + 1;
332 // Note that the first output line corresponds to groff
333 // position font::vert.
334 if (vpos <= 0) {
335 error("character above first line discarded");
336 return;
338 cached_v = v;
339 cached_vpos = vpos;
341 glyph *g = new glyph;
342 g->hpos = hpos;
343 g->code = c;
344 g->fore_color_idx = color_to_idx(fore);
345 g->back_color_idx = color_to_idx(back);
346 g->mode = mode;
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.
353 glyph **pp;
354 for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
355 if ((*pp)->hpos < hpos
356 || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
357 break;
358 g->next = *pp;
359 *pp = g;
362 void tty_printer::special(char *arg, const environment *env, char type)
364 if (type == 'u') {
365 add_char(*arg - '0', env->hpos, env->vpos, env->col, env->fill, CU_MODE);
366 return;
368 if (type != 'p')
369 return;
370 char *p;
371 for (p = arg; *p == ' ' || *p == '\n'; p++)
373 char *tag = 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");
378 return;
380 p++;
381 for (; *p == ' ' || *p == '\n'; p++)
383 char *command = p;
384 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
386 if (*command == '\0') {
387 error("empty X command ignored");
388 return;
390 if (strncmp(command, "sgr", p - command) == 0) {
391 for (; *p == ' ' || *p == '\n'; p++)
393 int n;
394 if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
395 old_drawing_scheme = 1;
396 else
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)
414 return;
415 if (np != 2) {
416 error("2 arguments required for line");
417 return;
419 if (p[0] == 0) {
420 // vertical line
421 int v = env->vpos;
422 int len = p[1];
423 if (len < 0) {
424 v += len;
425 len = -len;
427 while (len >= 0) {
428 add_char('|', env->hpos, v, env->col, env->fill, VDRAW_MODE);
429 len -= font::vert;
430 v += font::vert;
433 if (p[1] == 0) {
434 // horizontal line
435 int h = env->hpos;
436 int len = p[0];
437 if (len < 0) {
438 h += len;
439 len = -len;
441 while (len >= 0) {
442 add_char('-', h, env->vpos, env->col, env->fill, HDRAW_MODE);
443 len -= font::hor;
444 h += font::hor;
449 void tty_printer::put_char(unsigned int wc)
451 if (is_utf8 && wc >= 0x80) {
452 char buf[6 + 1];
453 int count;
454 char *p = buf;
455 if (wc < 0x800)
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);
465 else
466 return;
467 do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
468 while (count > 0);
469 *++p = '\0';
470 putstring(buf);
472 else
473 putchar(wc);
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
481 if (is_bold)
482 putstring(SGR_BOLD);
483 if (is_underline) {
484 if (italic_flag)
485 putstring(SGR_ITALIC);
486 else if (reverse_flag)
487 putstring(SGR_REVERSE);
488 else
489 putstring(SGR_UNDERLINE);
491 // set other color again
492 back = !back;
493 color_index = back ? curr_back_idx : curr_fore_idx;
495 putstring(CSI);
496 if (back)
497 putchar('4');
498 else
499 putchar('3');
500 putchar(color_index + '0');
501 putchar('m');
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;
509 int last_line;
510 for (last_line = nlines; last_line > 0; last_line--)
511 if (lines[last_line - 1])
512 break;
513 #if 0
514 if (last_line > lines_per_page) {
515 error("characters past last line discarded");
516 do {
517 --last_line;
518 while (lines[last_line]) {
519 glyph *tem = lines[last_line];
520 lines[last_line] = tem->next;
521 delete tem;
523 } while (last_line > lines_per_page);
525 #endif
526 for (int i = 0; i < last_line; i++) {
527 glyph *p = lines[i];
528 lines[i] = 0;
529 glyph *g = 0;
530 while (p) {
531 glyph *tem = p->next;
532 p->next = g;
533 g = p;
534 p = tem;
536 int hpos = 0;
537 glyph *nextp;
538 curr_fore_idx = DEFAULT_COLOR_IDX;
539 curr_back_idx = DEFAULT_COLOR_IDX;
540 is_underline = 0;
541 is_bold = 0;
542 for (p = g; p; delete p, p = nextp) {
543 nextp = p->next;
544 if (p->mode & CU_MODE) {
545 cu_flag = p->code;
546 continue;
548 if (nextp && p->hpos == nextp->hpos) {
549 if (p->draw_mode() == HDRAW_MODE &&
550 nextp->draw_mode() == VDRAW_MODE) {
551 nextp->code = '+';
552 continue;
554 if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
555 nextp->code = p->code;
556 continue;
558 if (!overstrike_flag)
559 continue;
561 if (hpos > p->hpos) {
562 do {
563 putchar('\b');
564 hpos--;
565 } while (hpos > p->hpos);
567 else {
568 if (horizontal_tab_flag) {
569 for (;;) {
570 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
571 if (next_tab_pos > p->hpos)
572 break;
573 if (cu_flag)
574 make_underline();
575 else if (!old_drawing_scheme && is_underline) {
576 if (italic_flag)
577 putstring(SGR_NO_ITALIC);
578 else if (reverse_flag)
579 putstring(SGR_NO_REVERSE);
580 else
581 putstring(SGR_NO_UNDERLINE);
582 is_underline = 0;
584 putchar('\t');
585 hpos = next_tab_pos;
588 for (; hpos < p->hpos; hpos++) {
589 if (cu_flag)
590 make_underline();
591 else if (!old_drawing_scheme && is_underline) {
592 if (italic_flag)
593 putstring(SGR_NO_ITALIC);
594 else if (reverse_flag)
595 putstring(SGR_NO_REVERSE);
596 else
597 putstring(SGR_NO_UNDERLINE);
598 is_underline = 0;
600 putchar(' ');
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;
615 continue;
617 if (p->mode & UNDERLINE_MODE)
618 make_underline();
619 else if (!old_drawing_scheme && is_underline) {
620 if (italic_flag)
621 putstring(SGR_NO_ITALIC);
622 else if (reverse_flag)
623 putstring(SGR_NO_REVERSE);
624 else
625 putstring(SGR_NO_UNDERLINE);
626 is_underline = 0;
628 if (p->mode & BOLD_MODE)
629 make_bold(p->code);
630 else if (!old_drawing_scheme && is_bold) {
631 putstring(SGR_NO_BOLD);
632 is_bold = 0;
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;
644 put_char(p->code);
645 hpos++;
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);
652 putchar('\n');
654 if (form_feed_flag) {
655 if (last_line < lines_per_page)
656 putchar('\f');
658 else {
659 for (; last_line < lines_per_page; last_line++)
660 putchar('\n');
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);
683 int c;
684 static const struct option long_options[] = {
685 { "help", no_argument, 0, CHAR_MAX + 1 },
686 { "version", no_argument, 0, 'v' },
687 { NULL, 0, 0, 0 }
689 while ((c = getopt_long(argc, argv, "bBcdfF:hioruUv", long_options, NULL))
690 != EOF)
691 switch(c) {
692 case 'v':
693 printf("GNU grotty (groff) version %s\n", Version_string);
694 exit(0);
695 break;
696 case 'i':
697 // Use italic font instead of underlining.
698 italic_flag = 1;
699 break;
700 case 'b':
701 // Do not embolden by overstriking.
702 bold_flag = 0;
703 break;
704 case 'c':
705 // Use old scheme for emboldening and underline.
706 old_drawing_scheme = 1;
707 break;
708 case 'u':
709 // Do not underline.
710 underline_flag = 0;
711 break;
712 case 'o':
713 // Do not overstrike (other than emboldening and underlining).
714 overstrike_flag = 0;
715 break;
716 case 'r':
717 // Use reverse mode instead of underlining.
718 reverse_flag = 1;
719 break;
720 case 'B':
721 // Do bold-underlining as bold.
722 bold_underline_mode = BOLD_MODE;
723 break;
724 case 'U':
725 // Do bold-underlining as underlining.
726 bold_underline_mode = UNDERLINE_MODE;
727 break;
728 case 'h':
729 // Use horizontal tabs.
730 horizontal_tab_flag = 1;
731 break;
732 case 'f':
733 form_feed_flag = 1;
734 break;
735 case 'F':
736 font::command_line_font_dir(optarg);
737 break;
738 case 'd':
739 // Ignore \D commands.
740 draw_flag = 0;
741 break;
742 case CHAR_MAX + 1: // --help
743 usage(stdout);
744 exit(0);
745 break;
746 case '?':
747 usage(stderr);
748 exit(1);
749 break;
750 default:
751 assert(0);
753 if (old_drawing_scheme) {
754 italic_flag = 0;
755 reverse_flag = 0;
757 else {
758 bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
759 bold_flag = 1;
760 underline_flag = 1;
762 if (optind >= argc)
763 do_file("-");
764 else {
765 for (int i = optind; i < argc; i++)
766 do_file(argv[i]);
768 delete pr;
769 return 0;
772 static void usage(FILE *stream)
774 fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
775 program_name);