groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / devices / grotty / tty.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548 2/* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
92d0a6a6
JR
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
4d3e9548
JL
10Software Foundation, either version 3 of the License, or
11(at your option) any later version.
92d0a6a6
JR
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
4d3e9548
JL
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
20
21#include "driver.h"
22#include "device.h"
23#include "ptable.h"
24
25typedef signed char schar;
26
27declare_ptable(schar)
28implement_ptable(schar)
29
30extern "C" const char *Version_string;
31
32#define putstring(s) fputs(s, stdout)
33
34#ifndef SHRT_MIN
35#define SHRT_MIN (-32768)
36#endif
37
38#ifndef SHRT_MAX
39#define SHRT_MAX 32767
40#endif
41
42#define TAB_WIDTH 8
43
4d3e9548
JL
44// A character of the output device fits in a 32-bit word.
45typedef unsigned int output_character;
46
92d0a6a6
JR
47static int horizontal_tab_flag = 0;
48static int form_feed_flag = 0;
49static int bold_flag_option = 1;
50static int bold_flag;
51static int underline_flag_option = 1;
52static int underline_flag;
53static int overstrike_flag = 1;
54static int draw_flag = 1;
55static int italic_flag_option = 0;
56static int italic_flag;
57static int reverse_flag_option = 0;
58static int reverse_flag;
59static int old_drawing_scheme = 0;
60
61static void update_options();
62static void usage(FILE *stream);
63
64static int hline_char = '-';
65static int vline_char = '|';
66
67enum {
68 UNDERLINE_MODE = 0x01,
69 BOLD_MODE = 0x02,
70 VDRAW_MODE = 0x04,
71 HDRAW_MODE = 0x08,
72 CU_MODE = 0x10,
73 COLOR_CHANGE = 0x20,
74 START_LINE = 0x40,
75 END_LINE = 0x80
76};
77
78// Mode to use for bold-underlining.
79static unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
80static unsigned char bold_underline_mode;
81
82#ifndef IS_EBCDIC_HOST
83#define CSI "\033["
84#else
85#define CSI "\047["
86#endif
87
88// SGR handling (ISO 6429)
89#define SGR_BOLD CSI "1m"
90#define SGR_NO_BOLD CSI "22m"
91#define SGR_ITALIC CSI "3m"
92#define SGR_NO_ITALIC CSI "23m"
93#define SGR_UNDERLINE CSI "4m"
94#define SGR_NO_UNDERLINE CSI "24m"
95#define SGR_REVERSE CSI "7m"
96#define SGR_NO_REVERSE CSI "27m"
97// many terminals can't handle `CSI 39 m' and `CSI 49 m' to reset
465b256c 98// the foreground and background color, respectively; we thus use
92d0a6a6
JR
99// `CSI 0 m' exclusively
100#define SGR_DEFAULT CSI "0m"
101
102#define DEFAULT_COLOR_IDX -1
103
104class tty_font : public font {
105 tty_font(const char *);
106 unsigned char mode;
107public:
108 ~tty_font();
109 unsigned char get_mode() { return mode; }
110#if 0
111 void handle_x_command(int argc, const char **argv);
112#endif
113 static tty_font *load_tty_font(const char *);
114};
115
116tty_font *tty_font::load_tty_font(const char *s)
117{
118 tty_font *f = new tty_font(s);
119 if (!f->load()) {
120 delete f;
121 return 0;
122 }
123 const char *num = f->get_internal_name();
124 long n;
125 if (num != 0 && (n = strtol(num, 0, 0)) != 0)
126 f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
127 if (!underline_flag)
128 f->mode &= ~UNDERLINE_MODE;
129 if (!bold_flag)
130 f->mode &= ~BOLD_MODE;
131 if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
132 f->mode = (unsigned char)((f->mode & ~(BOLD_MODE|UNDERLINE_MODE))
133 | bold_underline_mode);
134 return f;
135}
136
137tty_font::tty_font(const char *nm)
138: font(nm), mode(0)
139{
140}
141
142tty_font::~tty_font()
143{
144}
145
146#if 0
147void tty_font::handle_x_command(int argc, const char **argv)
148{
149 if (argc >= 1 && strcmp(argv[0], "bold") == 0)
150 mode |= BOLD_MODE;
151 else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
152 mode |= UNDERLINE_MODE;
153}
154#endif
155
4d3e9548
JL
156class tty_glyph {
157 static tty_glyph *free_list;
92d0a6a6 158public:
4d3e9548 159 tty_glyph *next;
465b256c 160 int w;
92d0a6a6
JR
161 int hpos;
162 unsigned int code;
163 unsigned char mode;
164 schar back_color_idx;
165 schar fore_color_idx;
166 void *operator new(size_t);
167 void operator delete(void *);
168 inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
169 inline int order() {
170 return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
171};
172
4d3e9548 173tty_glyph *tty_glyph::free_list = 0;
92d0a6a6 174
4d3e9548 175void *tty_glyph::operator new(size_t)
92d0a6a6
JR
176{
177 if (!free_list) {
178 const int BLOCK = 1024;
4d3e9548 179 free_list = (tty_glyph *)new char[sizeof(tty_glyph) * BLOCK];
92d0a6a6
JR
180 for (int i = 0; i < BLOCK - 1; i++)
181 free_list[i].next = free_list + i + 1;
182 free_list[BLOCK - 1].next = 0;
183 }
4d3e9548 184 tty_glyph *p = free_list;
92d0a6a6
JR
185 free_list = free_list->next;
186 p->next = 0;
187 return p;
188}
189
4d3e9548 190void tty_glyph::operator delete(void *p)
92d0a6a6
JR
191{
192 if (p) {
4d3e9548
JL
193 ((tty_glyph *)p)->next = free_list;
194 free_list = (tty_glyph *)p;
92d0a6a6
JR
195 }
196}
197
198class tty_printer : public printer {
199 int is_utf8;
4d3e9548 200 tty_glyph **lines;
92d0a6a6
JR
201 int nlines;
202 int cached_v;
203 int cached_vpos;
204 schar curr_fore_idx;
205 schar curr_back_idx;
206 int is_underline;
207 int is_bold;
208 int cu_flag;
209 PTABLE(schar) tty_colors;
465b256c 210 void make_underline(int);
4d3e9548
JL
211 void make_bold(output_character, int);
212 schar color_to_idx(color *);
213 void add_char(output_character, int, int, int, color *, color *,
214 unsigned char);
92d0a6a6
JR
215 char *make_rgb_string(unsigned int, unsigned int, unsigned int);
216 int tty_color(unsigned int, unsigned int, unsigned int, schar *,
217 schar = DEFAULT_COLOR_IDX);
4d3e9548
JL
218 void line(int, int, int, int, color *, color *);
219 void draw_line(int *, int, const environment *);
220 void draw_polygon(int *, int, const environment *);
92d0a6a6 221public:
4d3e9548 222 tty_printer(const char *);
92d0a6a6 223 ~tty_printer();
4d3e9548
JL
224 void set_char(glyph *, font *, const environment *, int, const char *);
225 void draw(int, int *, int, const environment *);
226 void special(char *, const environment *, char);
227 void change_color(const environment * const);
228 void change_fill_color(const environment * const);
229 void put_char(output_character);
92d0a6a6
JR
230 void put_color(schar, int);
231 void begin_page(int) { }
4d3e9548 232 void end_page(int);
92d0a6a6
JR
233 font *make_font(const char *);
234};
235
236char *tty_printer::make_rgb_string(unsigned int r,
237 unsigned int g,
238 unsigned int b)
239{
240 char *s = new char[8];
241 s[0] = char(r >> 8);
242 s[1] = char(r & 0xff);
243 s[2] = char(g >> 8);
244 s[3] = char(g & 0xff);
245 s[4] = char(b >> 8);
246 s[5] = char(b & 0xff);
247 s[6] = char(0x80);
248 s[7] = 0;
249 // avoid null-bytes in string
250 for (int i = 0; i < 6; i++)
251 if (!s[i]) {
252 s[i] = 1;
253 s[6] |= 1 << i;
254 }
255 return s;
256}
257
258int tty_printer::tty_color(unsigned int r,
259 unsigned int g,
260 unsigned int b, schar *idx, schar value)
261{
262 int unknown_color = 0;
263 char *s = make_rgb_string(r, g, b);
264 schar *i = tty_colors.lookup(s);
265 if (!i) {
266 unknown_color = 1;
267 i = new schar[1];
268 *i = value;
269 tty_colors.define(s, i);
270 }
271 *idx = *i;
272 a_delete s;
273 return unknown_color;
274}
275
276tty_printer::tty_printer(const char *dev) : cached_v(0)
277{
278 is_utf8 = !strcmp(dev, "utf8");
279 if (is_utf8) {
280 hline_char = 0x2500;
281 vline_char = 0x2502;
282 }
283 schar dummy;
284 // black, white
285 (void)tty_color(0, 0, 0, &dummy, 0);
286 (void)tty_color(color::MAX_COLOR_VAL,
287 color::MAX_COLOR_VAL,
288 color::MAX_COLOR_VAL, &dummy, 7);
289 // red, green, blue
290 (void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
291 (void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
292 (void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
293 // yellow, magenta, cyan
294 (void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
295 (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
296 (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
297 nlines = 66;
4d3e9548 298 lines = new tty_glyph *[nlines];
92d0a6a6
JR
299 for (int i = 0; i < nlines; i++)
300 lines[i] = 0;
301 cu_flag = 0;
302}
303
304tty_printer::~tty_printer()
305{
306 a_delete lines;
307}
308
465b256c 309void tty_printer::make_underline(int w)
92d0a6a6
JR
310{
311 if (old_drawing_scheme) {
465b256c
JR
312 if (!w)
313 warning("can't underline zero-width character");
314 else {
315 int n = w / font::hor;
316 for (int i = 0; i < n; i++)
317 putchar('_');
318 for (int j = 0; j < n; j++)
319 putchar('\b');
320 }
92d0a6a6
JR
321 }
322 else {
323 if (!is_underline) {
324 if (italic_flag)
325 putstring(SGR_ITALIC);
326 else if (reverse_flag)
327 putstring(SGR_REVERSE);
328 else
329 putstring(SGR_UNDERLINE);
330 }
331 is_underline = 1;
332 }
333}
334
4d3e9548 335void tty_printer::make_bold(output_character c, int w)
92d0a6a6
JR
336{
337 if (old_drawing_scheme) {
465b256c
JR
338 if (!w)
339 warning("can't print zero-width character in bold");
340 else {
341 int n = w / font::hor;
342 put_char(c);
343 for (int i = 0; i < n; i++)
344 putchar('\b');
345 }
92d0a6a6
JR
346 }
347 else {
348 if (!is_bold)
349 putstring(SGR_BOLD);
350 is_bold = 1;
351 }
352}
353
354schar tty_printer::color_to_idx(color *col)
355{
356 if (col->is_default())
357 return DEFAULT_COLOR_IDX;
358 unsigned int r, g, b;
359 col->get_rgb(&r, &g, &b);
360 schar idx;
361 if (tty_color(r, g, b, &idx)) {
362 char *s = col->print_color();
363 error("Unknown color (%1) mapped to default", s);
364 a_delete s;
365 }
366 return idx;
367}
368
4d3e9548 369void tty_printer::set_char(glyph *g, font *f, const environment *env,
92d0a6a6
JR
370 int w, const char *)
371{
465b256c
JR
372 if (w % font::hor != 0)
373 fatal("width of character not a multiple of horizontal resolution");
4d3e9548 374 add_char(f->get_code(g), w,
92d0a6a6
JR
375 env->hpos, env->vpos,
376 env->col, env->fill,
377 ((tty_font *)f)->get_mode());
378}
379
4d3e9548 380void tty_printer::add_char(output_character c, int w,
92d0a6a6
JR
381 int h, int v,
382 color *fore, color *back,
383 unsigned char mode)
384{
385#if 0
386 // This is too expensive.
387 if (h % font::hor != 0)
388 fatal("horizontal position not a multiple of horizontal resolution");
389#endif
390 int hpos = h / font::hor;
391 if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
392 error("character with ridiculous horizontal position discarded");
393 return;
394 }
395 int vpos;
396 if (v == cached_v && cached_v != 0)
397 vpos = cached_vpos;
398 else {
399 if (v % font::vert != 0)
400 fatal("vertical position not a multiple of vertical resolution");
401 vpos = v / font::vert;
402 if (vpos > nlines) {
4d3e9548
JL
403 tty_glyph **old_lines = lines;
404 lines = new tty_glyph *[vpos + 1];
405 memcpy(lines, old_lines, nlines * sizeof(tty_glyph *));
92d0a6a6
JR
406 for (int i = nlines; i <= vpos; i++)
407 lines[i] = 0;
408 a_delete old_lines;
409 nlines = vpos + 1;
410 }
411 // Note that the first output line corresponds to groff
412 // position font::vert.
413 if (vpos <= 0) {
414 error("character above first line discarded");
415 return;
416 }
417 cached_v = v;
418 cached_vpos = vpos;
419 }
4d3e9548 420 tty_glyph *g = new tty_glyph;
465b256c 421 g->w = w;
92d0a6a6
JR
422 g->hpos = hpos;
423 g->code = c;
424 g->fore_color_idx = color_to_idx(fore);
425 g->back_color_idx = color_to_idx(back);
426 g->mode = mode;
427
428 // The list will be reversed later. After reversal, it must be in
429 // increasing order of hpos, with COLOR_CHANGE and CU specials before
430 // HDRAW characters before VDRAW characters before normal characters
431 // at each hpos, and otherwise in order of occurrence.
432
4d3e9548 433 tty_glyph **pp;
92d0a6a6
JR
434 for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
435 if ((*pp)->hpos < hpos
436 || ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
437 break;
438 g->next = *pp;
439 *pp = g;
440}
441
442void tty_printer::special(char *arg, const environment *env, char type)
443{
444 if (type == 'u') {
465b256c
JR
445 add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
446 CU_MODE);
92d0a6a6
JR
447 return;
448 }
449 if (type != 'p')
450 return;
451 char *p;
452 for (p = arg; *p == ' ' || *p == '\n'; p++)
453 ;
454 char *tag = p;
455 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
456 ;
457 if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
458 error("X command without `tty:' tag ignored");
459 return;
460 }
461 p++;
462 for (; *p == ' ' || *p == '\n'; p++)
463 ;
464 char *command = p;
465 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
466 ;
467 if (*command == '\0') {
468 error("empty X command ignored");
469 return;
470 }
471 if (strncmp(command, "sgr", p - command) == 0) {
472 for (; *p == ' ' || *p == '\n'; p++)
473 ;
474 int n;
475 if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
476 old_drawing_scheme = 1;
477 else
478 old_drawing_scheme = 0;
479 update_options();
480 }
481}
482
483void tty_printer::change_color(const environment * const env)
484{
465b256c 485 add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
92d0a6a6
JR
486}
487
488void tty_printer::change_fill_color(const environment * const env)
489{
465b256c 490 add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
92d0a6a6
JR
491}
492
493void tty_printer::draw(int code, int *p, int np, const environment *env)
494{
4d3e9548
JL
495 if (!draw_flag)
496 return;
497 if (code == 'l')
498 draw_line(p, np, env);
499 if (code == 'p')
500 draw_polygon(p, np, env);
501}
502
503void tty_printer::draw_polygon(int *p, int np, const environment *env)
504{
505 if (np & 1) {
506 error("even number of arguments required for polygon");
507 return;
508 }
509 if (np == 0) {
510 error("no arguments for polygon");
511 return;
512 }
513 // We only draw polygons which consist entirely of horizontal and
514 // vertical lines.
515 int hpos = 0;
516 int vpos = 0;
517 for (int i = 0; i < np; i += 2) {
518 if (!(p[i] == 0 || p[i + 1] == 0))
519 return;
520 hpos += p[i];
521 vpos += p[i + 1];
522 }
523 if (!(hpos == 0 || vpos == 0))
92d0a6a6 524 return;
4d3e9548
JL
525 int start_hpos = env->hpos;
526 int start_vpos = env->vpos;
527 hpos = start_hpos;
528 vpos = start_vpos;
529 for (int i = 0; i < np; i += 2) {
530 line(hpos, vpos, p[i], p[i + 1], env->col, env->fill);
531 hpos += p[i];
532 vpos += p[i + 1];
533 }
534 line(hpos, vpos, start_hpos - hpos, start_vpos - vpos,
535 env->col, env->fill);
536}
537
538void tty_printer::draw_line(int *p, int np, const environment *env)
539{
92d0a6a6
JR
540 if (np != 2) {
541 error("2 arguments required for line");
542 return;
543 }
4d3e9548
JL
544 line(env->hpos, env->vpos, p[0], p[1], env->col, env->fill);
545}
546
547void tty_printer::line(int hpos, int vpos, int dx, int dy,
548 color *col, color *fill)
549{
550 if (dx == 0) {
92d0a6a6 551 // vertical line
4d3e9548
JL
552 int v = vpos;
553 int len = dy;
92d0a6a6
JR
554 if (len < 0) {
555 v += len;
556 len = -len;
557 }
4d3e9548
JL
558 if (len == 0)
559 add_char(vline_char, font::hor, hpos, v, col, fill,
92d0a6a6
JR
560 VDRAW_MODE|START_LINE|END_LINE);
561 else {
4d3e9548 562 add_char(vline_char, font::hor, hpos, v, col, fill,
92d0a6a6
JR
563 VDRAW_MODE|START_LINE);
564 len -= font::vert;
565 v += font::vert;
566 while (len > 0) {
4d3e9548 567 add_char(vline_char, font::hor, hpos, v, col, fill,
92d0a6a6
JR
568 VDRAW_MODE|START_LINE|END_LINE);
569 len -= font::vert;
570 v += font::vert;
571 }
4d3e9548 572 add_char(vline_char, font::hor, hpos, v, col, fill,
92d0a6a6
JR
573 VDRAW_MODE|END_LINE);
574 }
575 }
4d3e9548 576 if (dy == 0) {
92d0a6a6 577 // horizontal line
4d3e9548
JL
578 int h = hpos;
579 int len = dx;
92d0a6a6
JR
580 if (len < 0) {
581 h += len;
582 len = -len;
583 }
4d3e9548
JL
584 if (len == 0)
585 add_char(hline_char, font::hor, h, vpos, col, fill,
92d0a6a6
JR
586 HDRAW_MODE|START_LINE|END_LINE);
587 else {
4d3e9548 588 add_char(hline_char, font::hor, h, vpos, col, fill,
92d0a6a6
JR
589 HDRAW_MODE|START_LINE);
590 len -= font::hor;
591 h += font::hor;
592 while (len > 0) {
4d3e9548 593 add_char(hline_char, font::hor, h, vpos, col, fill,
92d0a6a6
JR
594 HDRAW_MODE|START_LINE|END_LINE);
595 len -= font::hor;
596 h += font::hor;
597 }
4d3e9548 598 add_char(hline_char, font::hor, h, vpos, col, fill,
92d0a6a6
JR
599 HDRAW_MODE|END_LINE);
600 }
601 }
602}
603
4d3e9548 604void tty_printer::put_char(output_character wc)
92d0a6a6
JR
605{
606 if (is_utf8 && wc >= 0x80) {
607 char buf[6 + 1];
608 int count;
609 char *p = buf;
610 if (wc < 0x800)
611 count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
612 else if (wc < 0x10000)
613 count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
614 else if (wc < 0x200000)
615 count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
616 else if (wc < 0x4000000)
617 count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
618 else if (wc <= 0x7fffffff)
619 count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
620 else
621 return;
622 do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
623 while (count > 0);
624 *++p = '\0';
625 putstring(buf);
626 }
627 else
628 putchar(wc);
629}
630
631void tty_printer::put_color(schar color_index, int back)
632{
633 if (color_index == DEFAULT_COLOR_IDX) {
634 putstring(SGR_DEFAULT);
635 // set bold and underline again
636 if (is_bold)
637 putstring(SGR_BOLD);
638 if (is_underline) {
639 if (italic_flag)
640 putstring(SGR_ITALIC);
641 else if (reverse_flag)
642 putstring(SGR_REVERSE);
643 else
644 putstring(SGR_UNDERLINE);
645 }
646 // set other color again
647 back = !back;
648 color_index = back ? curr_back_idx : curr_fore_idx;
649 }
650 if (color_index != DEFAULT_COLOR_IDX) {
651 putstring(CSI);
652 if (back)
653 putchar('4');
654 else
655 putchar('3');
656 putchar(color_index + '0');
657 putchar('m');
658 }
659}
660
661// The possible Unicode combinations for crossing characters.
662//
663// ` ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
664//
665// ` ' = 0, ` ' = 1, `|' = 2, `|' = 3
666// | |
667
4d3e9548 668static output_character crossings[4*4] = {
92d0a6a6
JR
669 0x0000, 0x2577, 0x2575, 0x2502,
670 0x2576, 0x250C, 0x2514, 0x251C,
671 0x2574, 0x2510, 0x2518, 0x2524,
672 0x2500, 0x252C, 0x2534, 0x253C
673};
674
675void tty_printer::end_page(int page_length)
676{
677 if (page_length % font::vert != 0)
678 error("vertical position at end of page not multiple of vertical resolution");
679 int lines_per_page = page_length / font::vert;
680 int last_line;
681 for (last_line = nlines; last_line > 0; last_line--)
682 if (lines[last_line - 1])
683 break;
684#if 0
685 if (last_line > lines_per_page) {
686 error("characters past last line discarded");
687 do {
688 --last_line;
689 while (lines[last_line]) {
4d3e9548 690 tty_glyph *tem = lines[last_line];
92d0a6a6
JR
691 lines[last_line] = tem->next;
692 delete tem;
693 }
694 } while (last_line > lines_per_page);
695 }
696#endif
697 for (int i = 0; i < last_line; i++) {
4d3e9548 698 tty_glyph *p = lines[i];
92d0a6a6 699 lines[i] = 0;
4d3e9548 700 tty_glyph *g = 0;
92d0a6a6 701 while (p) {
4d3e9548 702 tty_glyph *tem = p->next;
92d0a6a6
JR
703 p->next = g;
704 g = p;
705 p = tem;
706 }
707 int hpos = 0;
4d3e9548 708 tty_glyph *nextp;
92d0a6a6
JR
709 curr_fore_idx = DEFAULT_COLOR_IDX;
710 curr_back_idx = DEFAULT_COLOR_IDX;
711 is_underline = 0;
712 is_bold = 0;
713 for (p = g; p; delete p, p = nextp) {
714 nextp = p->next;
715 if (p->mode & CU_MODE) {
4d3e9548 716 cu_flag = (p->code != 0);
92d0a6a6
JR
717 continue;
718 }
719 if (nextp && p->hpos == nextp->hpos) {
720 if (p->draw_mode() == HDRAW_MODE &&
721 nextp->draw_mode() == VDRAW_MODE) {
722 if (is_utf8)
723 nextp->code =
724 crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
725 + ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
726 else
727 nextp->code = '+';
728 continue;
729 }
730 if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
731 nextp->code = p->code;
732 continue;
733 }
734 if (!overstrike_flag)
735 continue;
736 }
737 if (hpos > p->hpos) {
738 do {
739 putchar('\b');
740 hpos--;
741 } while (hpos > p->hpos);
742 }
743 else {
744 if (horizontal_tab_flag) {
745 for (;;) {
746 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
747 if (next_tab_pos > p->hpos)
748 break;
749 if (cu_flag)
465b256c 750 make_underline(p->w);
92d0a6a6
JR
751 else if (!old_drawing_scheme && is_underline) {
752 if (italic_flag)
753 putstring(SGR_NO_ITALIC);
754 else if (reverse_flag)
755 putstring(SGR_NO_REVERSE);
756 else
757 putstring(SGR_NO_UNDERLINE);
758 is_underline = 0;
759 }
760 putchar('\t');
761 hpos = next_tab_pos;
762 }
763 }
764 for (; hpos < p->hpos; hpos++) {
765 if (cu_flag)
465b256c 766 make_underline(p->w);
92d0a6a6
JR
767 else if (!old_drawing_scheme && is_underline) {
768 if (italic_flag)
769 putstring(SGR_NO_ITALIC);
770 else if (reverse_flag)
771 putstring(SGR_NO_REVERSE);
772 else
773 putstring(SGR_NO_UNDERLINE);
774 is_underline = 0;
775 }
776 putchar(' ');
777 }
778 }
779 assert(hpos == p->hpos);
780 if (p->mode & COLOR_CHANGE) {
781 if (!old_drawing_scheme) {
782 if (p->fore_color_idx != curr_fore_idx) {
783 put_color(p->fore_color_idx, 0);
784 curr_fore_idx = p->fore_color_idx;
785 }
786 if (p->back_color_idx != curr_back_idx) {
787 put_color(p->back_color_idx, 1);
788 curr_back_idx = p->back_color_idx;
789 }
790 }
791 continue;
792 }
793 if (p->mode & UNDERLINE_MODE)
465b256c 794 make_underline(p->w);
92d0a6a6
JR
795 else if (!old_drawing_scheme && is_underline) {
796 if (italic_flag)
797 putstring(SGR_NO_ITALIC);
798 else if (reverse_flag)
799 putstring(SGR_NO_REVERSE);
800 else
801 putstring(SGR_NO_UNDERLINE);
802 is_underline = 0;
803 }
804 if (p->mode & BOLD_MODE)
465b256c 805 make_bold(p->code, p->w);
92d0a6a6
JR
806 else if (!old_drawing_scheme && is_bold) {
807 putstring(SGR_NO_BOLD);
808 is_bold = 0;
809 }
810 if (!old_drawing_scheme) {
811 if (p->fore_color_idx != curr_fore_idx) {
812 put_color(p->fore_color_idx, 0);
813 curr_fore_idx = p->fore_color_idx;
814 }
815 if (p->back_color_idx != curr_back_idx) {
816 put_color(p->back_color_idx, 1);
817 curr_back_idx = p->back_color_idx;
818 }
819 }
820 put_char(p->code);
465b256c 821 hpos += p->w / font::hor;
92d0a6a6
JR
822 }
823 if (!old_drawing_scheme
824 && (is_bold || is_underline
825 || curr_fore_idx != DEFAULT_COLOR_IDX
826 || curr_back_idx != DEFAULT_COLOR_IDX))
827 putstring(SGR_DEFAULT);
828 putchar('\n');
829 }
830 if (form_feed_flag) {
831 if (last_line < lines_per_page)
832 putchar('\f');
833 }
834 else {
835 for (; last_line < lines_per_page; last_line++)
836 putchar('\n');
837 }
838}
839
840font *tty_printer::make_font(const char *nm)
841{
842 return tty_font::load_tty_font(nm);
843}
844
845printer *make_printer()
846{
847 return new tty_printer(device);
848}
849
850static void update_options()
851{
852 if (old_drawing_scheme) {
853 italic_flag = 0;
854 reverse_flag = 0;
855 bold_underline_mode = bold_underline_mode_option;
856 bold_flag = bold_flag_option;
857 underline_flag = underline_flag_option;
858 }
859 else {
860 italic_flag = italic_flag_option;
861 reverse_flag = reverse_flag_option;
862 bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
863 bold_flag = 1;
864 underline_flag = 1;
865 }
866}
867
868int main(int argc, char **argv)
869{
870 program_name = argv[0];
871 static char stderr_buf[BUFSIZ];
872 if (getenv("GROFF_NO_SGR"))
873 old_drawing_scheme = 1;
874 setbuf(stderr, stderr_buf);
875 int c;
876 static const struct option long_options[] = {
877 { "help", no_argument, 0, CHAR_MAX + 1 },
878 { "version", no_argument, 0, 'v' },
879 { NULL, 0, 0, 0 }
880 };
881 while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
882 != EOF)
883 switch(c) {
884 case 'v':
885 printf("GNU grotty (groff) version %s\n", Version_string);
886 exit(0);
887 break;
888 case 'i':
889 // Use italic font instead of underlining.
890 italic_flag_option = 1;
891 break;
892 case 'I':
893 // ignore include search path
894 break;
895 case 'b':
896 // Do not embolden by overstriking.
897 bold_flag_option = 0;
898 break;
899 case 'c':
900 // Use old scheme for emboldening and underline.
901 old_drawing_scheme = 1;
902 break;
903 case 'u':
904 // Do not underline.
905 underline_flag_option = 0;
906 break;
907 case 'o':
908 // Do not overstrike (other than emboldening and underlining).
909 overstrike_flag = 0;
910 break;
911 case 'r':
912 // Use reverse mode instead of underlining.
913 reverse_flag_option = 1;
914 break;
915 case 'B':
916 // Do bold-underlining as bold.
917 bold_underline_mode_option = BOLD_MODE;
918 break;
919 case 'U':
920 // Do bold-underlining as underlining.
921 bold_underline_mode_option = UNDERLINE_MODE;
922 break;
923 case 'h':
924 // Use horizontal tabs.
925 horizontal_tab_flag = 1;
926 break;
927 case 'f':
928 form_feed_flag = 1;
929 break;
930 case 'F':
931 font::command_line_font_dir(optarg);
932 break;
933 case 'd':
934 // Ignore \D commands.
935 draw_flag = 0;
936 break;
937 case CHAR_MAX + 1: // --help
938 usage(stdout);
939 exit(0);
940 break;
941 case '?':
942 usage(stderr);
943 exit(1);
944 break;
945 default:
946 assert(0);
947 }
948 update_options();
949 if (optind >= argc)
950 do_file("-");
951 else {
952 for (int i = optind; i < argc; i++)
953 do_file(argv[i]);
954 }
955 return 0;
956}
957
958static void usage(FILE *stream)
959{
960 fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
961 program_name);
962}