1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005
3151497Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "driver.h"
23114402Sru#include "device.h"
24114402Sru#include "ptable.h"
25114402Sru
26151497Srutypedef signed char schar;
27114402Sru
28151497Srudeclare_ptable(schar)
29151497Sruimplement_ptable(schar)
30151497Sru
31114402Sruextern "C" const char *Version_string;
32114402Sru
33114402Sru#define putstring(s) fputs(s, stdout)
34114402Sru
35114402Sru#ifndef SHRT_MIN
36114402Sru#define SHRT_MIN (-32768)
37114402Sru#endif
38114402Sru
39114402Sru#ifndef SHRT_MAX
40114402Sru#define SHRT_MAX 32767
41114402Sru#endif
42114402Sru
43114402Sru#define TAB_WIDTH 8
44114402Sru
45114402Srustatic int horizontal_tab_flag = 0;
46114402Srustatic int form_feed_flag = 0;
47151497Srustatic int bold_flag_option = 1;
48151497Srustatic int bold_flag;
49151497Srustatic int underline_flag_option = 1;
50151497Srustatic int underline_flag;
51114402Srustatic int overstrike_flag = 1;
52114402Srustatic int draw_flag = 1;
53151497Srustatic int italic_flag_option = 0;
54151497Srustatic int italic_flag;
55151497Srustatic int reverse_flag_option = 0;
56151497Srustatic int reverse_flag;
57114402Srustatic int old_drawing_scheme = 0;
58114402Sru
59151497Srustatic void update_options();
60151497Srustatic void usage(FILE *stream);
61151497Sru
62151497Srustatic int hline_char = '-';
63151497Srustatic int vline_char = '|';
64151497Sru
65114402Sruenum {
66114402Sru  UNDERLINE_MODE = 0x01,
67114402Sru  BOLD_MODE = 0x02,
68114402Sru  VDRAW_MODE = 0x04,
69114402Sru  HDRAW_MODE = 0x08,
70114402Sru  CU_MODE = 0x10,
71151497Sru  COLOR_CHANGE = 0x20,
72151497Sru  START_LINE = 0x40,
73151497Sru  END_LINE = 0x80
74114402Sru};
75114402Sru
76114402Sru// Mode to use for bold-underlining.
77151497Srustatic unsigned char bold_underline_mode_option = BOLD_MODE|UNDERLINE_MODE;
78151497Srustatic unsigned char bold_underline_mode;
79114402Sru
80114402Sru#ifndef IS_EBCDIC_HOST
81114402Sru#define CSI "\033["
82114402Sru#else
83114402Sru#define CSI "\047["
84114402Sru#endif
85114402Sru
86114402Sru// SGR handling (ISO 6429)
87114402Sru#define SGR_BOLD CSI "1m"
88114402Sru#define SGR_NO_BOLD CSI "22m"
89114402Sru#define SGR_ITALIC CSI "3m"
90114402Sru#define SGR_NO_ITALIC CSI "23m"
91114402Sru#define SGR_UNDERLINE CSI "4m"
92114402Sru#define SGR_NO_UNDERLINE CSI "24m"
93114402Sru#define SGR_REVERSE CSI "7m"
94114402Sru#define SGR_NO_REVERSE CSI "27m"
95114402Sru// many terminals can't handle `CSI 39 m' and `CSI 49 m' to reset
96151497Sru// the foreground and background color, respectively; we thus use
97114402Sru// `CSI 0 m' exclusively
98114402Sru#define SGR_DEFAULT CSI "0m"
99114402Sru
100114402Sru#define DEFAULT_COLOR_IDX -1
101114402Sru
102114402Sruclass tty_font : public font {
103114402Sru  tty_font(const char *);
104114402Sru  unsigned char mode;
105114402Srupublic:
106114402Sru  ~tty_font();
107114402Sru  unsigned char get_mode() { return mode; }
108114402Sru#if 0
109114402Sru  void handle_x_command(int argc, const char **argv);
110114402Sru#endif
111114402Sru  static tty_font *load_tty_font(const char *);
112114402Sru};
113114402Sru
114114402Srutty_font *tty_font::load_tty_font(const char *s)
115114402Sru{
116114402Sru  tty_font *f = new tty_font(s);
117114402Sru  if (!f->load()) {
118114402Sru    delete f;
119114402Sru    return 0;
120114402Sru  }
121114402Sru  const char *num = f->get_internal_name();
122114402Sru  long n;
123114402Sru  if (num != 0 && (n = strtol(num, 0, 0)) != 0)
124151497Sru    f->mode = (unsigned char)(n & (BOLD_MODE|UNDERLINE_MODE));
125114402Sru  if (!underline_flag)
126114402Sru    f->mode &= ~UNDERLINE_MODE;
127114402Sru  if (!bold_flag)
128114402Sru    f->mode &= ~BOLD_MODE;
129114402Sru  if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
130151497Sru    f->mode = (unsigned char)((f->mode & ~(BOLD_MODE|UNDERLINE_MODE))
131151497Sru			      | bold_underline_mode);
132114402Sru  return f;
133114402Sru}
134114402Sru
135114402Srutty_font::tty_font(const char *nm)
136114402Sru: font(nm), mode(0)
137114402Sru{
138114402Sru}
139114402Sru
140114402Srutty_font::~tty_font()
141114402Sru{
142114402Sru}
143114402Sru
144114402Sru#if 0
145114402Sruvoid tty_font::handle_x_command(int argc, const char **argv)
146114402Sru{
147114402Sru  if (argc >= 1 && strcmp(argv[0], "bold") == 0)
148114402Sru    mode |= BOLD_MODE;
149114402Sru  else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
150114402Sru    mode |= UNDERLINE_MODE;
151114402Sru}
152114402Sru#endif
153114402Sru
154114402Sruclass glyph {
155114402Sru  static glyph *free_list;
156114402Srupublic:
157114402Sru  glyph *next;
158151497Sru  int w;
159151497Sru  int hpos;
160114402Sru  unsigned int code;
161114402Sru  unsigned char mode;
162151497Sru  schar back_color_idx;
163151497Sru  schar fore_color_idx;
164114402Sru  void *operator new(size_t);
165114402Sru  void operator delete(void *);
166114402Sru  inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
167114402Sru  inline int order() {
168114402Sru    return mode & (VDRAW_MODE|HDRAW_MODE|CU_MODE|COLOR_CHANGE); }
169114402Sru};
170114402Sru
171114402Sruglyph *glyph::free_list = 0;
172114402Sru
173114402Sruvoid *glyph::operator new(size_t)
174114402Sru{
175114402Sru  if (!free_list) {
176114402Sru    const int BLOCK = 1024;
177114402Sru    free_list = (glyph *)new char[sizeof(glyph) * BLOCK];
178114402Sru    for (int i = 0; i < BLOCK - 1; i++)
179114402Sru      free_list[i].next = free_list + i + 1;
180114402Sru    free_list[BLOCK - 1].next = 0;
181114402Sru  }
182114402Sru  glyph *p = free_list;
183114402Sru  free_list = free_list->next;
184114402Sru  p->next = 0;
185114402Sru  return p;
186114402Sru}
187114402Sru
188114402Sruvoid glyph::operator delete(void *p)
189114402Sru{
190114402Sru  if (p) {
191114402Sru    ((glyph *)p)->next = free_list;
192114402Sru    free_list = (glyph *)p;
193114402Sru  }
194114402Sru}
195114402Sru
196114402Sruclass tty_printer : public printer {
197114402Sru  int is_utf8;
198114402Sru  glyph **lines;
199114402Sru  int nlines;
200114402Sru  int cached_v;
201114402Sru  int cached_vpos;
202151497Sru  schar curr_fore_idx;
203151497Sru  schar curr_back_idx;
204114402Sru  int is_underline;
205114402Sru  int is_bold;
206114402Sru  int cu_flag;
207151497Sru  PTABLE(schar) tty_colors;
208151497Sru  void make_underline(int);
209151497Sru  void make_bold(unsigned int, int);
210151497Sru  schar color_to_idx(color *col);
211151497Sru  void add_char(unsigned int, int, int, int, color *, color *, unsigned char);
212114402Sru  char *make_rgb_string(unsigned int, unsigned int, unsigned int);
213151497Sru  int tty_color(unsigned int, unsigned int, unsigned int, schar *,
214151497Sru		schar = DEFAULT_COLOR_IDX);
215114402Srupublic:
216114402Sru  tty_printer(const char *device);
217114402Sru  ~tty_printer();
218114402Sru  void set_char(int, font *, const environment *, int, const char *name);
219114402Sru  void draw(int code, int *p, int np, const environment *env);
220114402Sru  void special(char *arg, const environment *env, char type);
221114402Sru  void change_color(const environment * const env);
222114402Sru  void change_fill_color(const environment * const env);
223114402Sru  void put_char(unsigned int);
224151497Sru  void put_color(schar, int);
225114402Sru  void begin_page(int) { }
226114402Sru  void end_page(int page_length);
227114402Sru  font *make_font(const char *);
228114402Sru};
229114402Sru
230114402Sruchar *tty_printer::make_rgb_string(unsigned int r,
231114402Sru				   unsigned int g,
232114402Sru				   unsigned int b)
233114402Sru{
234114402Sru  char *s = new char[8];
235114402Sru  s[0] = char(r >> 8);
236114402Sru  s[1] = char(r & 0xff);
237114402Sru  s[2] = char(g >> 8);
238114402Sru  s[3] = char(g & 0xff);
239114402Sru  s[4] = char(b >> 8);
240114402Sru  s[5] = char(b & 0xff);
241114402Sru  s[6] = char(0x80);
242114402Sru  s[7] = 0;
243114402Sru  // avoid null-bytes in string
244114402Sru  for (int i = 0; i < 6; i++)
245114402Sru    if (!s[i]) {
246114402Sru      s[i] = 1;
247114402Sru      s[6] |= 1 << i;
248114402Sru    }
249114402Sru  return s;
250114402Sru}
251114402Sru
252114402Sruint tty_printer::tty_color(unsigned int r,
253114402Sru			   unsigned int g,
254151497Sru			   unsigned int b, schar *idx, schar value)
255114402Sru{
256114402Sru  int unknown_color = 0;
257114402Sru  char *s = make_rgb_string(r, g, b);
258151497Sru  schar *i = tty_colors.lookup(s);
259114402Sru  if (!i) {
260114402Sru    unknown_color = 1;
261151497Sru    i = new schar[1];
262114402Sru    *i = value;
263114402Sru    tty_colors.define(s, i);
264114402Sru  }
265114402Sru  *idx = *i;
266114402Sru  a_delete s;
267114402Sru  return unknown_color;
268114402Sru}
269114402Sru
270151497Srutty_printer::tty_printer(const char *dev) : cached_v(0)
271114402Sru{
272151497Sru  is_utf8 = !strcmp(dev, "utf8");
273151497Sru  if (is_utf8) {
274151497Sru    hline_char = 0x2500;
275151497Sru    vline_char = 0x2502;
276151497Sru  }
277151497Sru  schar dummy;
278114402Sru  // black, white
279114402Sru  (void)tty_color(0, 0, 0, &dummy, 0);
280114402Sru  (void)tty_color(color::MAX_COLOR_VAL,
281114402Sru		  color::MAX_COLOR_VAL,
282114402Sru		  color::MAX_COLOR_VAL, &dummy, 7);
283114402Sru  // red, green, blue
284114402Sru  (void)tty_color(color::MAX_COLOR_VAL, 0, 0, &dummy, 1);
285114402Sru  (void)tty_color(0, color::MAX_COLOR_VAL, 0, &dummy, 2);
286114402Sru  (void)tty_color(0, 0, color::MAX_COLOR_VAL, &dummy, 4);
287114402Sru  // yellow, magenta, cyan
288114402Sru  (void)tty_color(color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, 0, &dummy, 3);
289114402Sru  (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
290114402Sru  (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
291114402Sru  nlines = 66;
292114402Sru  lines = new glyph *[nlines];
293114402Sru  for (int i = 0; i < nlines; i++)
294114402Sru    lines[i] = 0;
295114402Sru  cu_flag = 0;
296114402Sru}
297114402Sru
298114402Srutty_printer::~tty_printer()
299114402Sru{
300114402Sru  a_delete lines;
301114402Sru}
302114402Sru
303151497Sruvoid tty_printer::make_underline(int w)
304114402Sru{
305114402Sru  if (old_drawing_scheme) {
306151497Sru    if (!w)
307151497Sru      warning("can't underline zero-width character");
308151497Sru    else {
309151497Sru      int n = w / font::hor;
310151497Sru      for (int i = 0; i < n; i++)
311151497Sru	putchar('_');
312151497Sru      for (int j = 0; j < n; j++)
313151497Sru	putchar('\b');
314151497Sru    }
315114402Sru  }
316114402Sru  else {
317114402Sru    if (!is_underline) {
318114402Sru      if (italic_flag)
319114402Sru	putstring(SGR_ITALIC);
320114402Sru      else if (reverse_flag)
321114402Sru	putstring(SGR_REVERSE);
322114402Sru      else
323114402Sru	putstring(SGR_UNDERLINE);
324114402Sru    }
325114402Sru    is_underline = 1;
326114402Sru  }
327114402Sru}
328114402Sru
329151497Sruvoid tty_printer::make_bold(unsigned int c, int w)
330114402Sru{
331114402Sru  if (old_drawing_scheme) {
332151497Sru    if (!w)
333151497Sru      warning("can't print zero-width character in bold");
334151497Sru    else {
335151497Sru      int n = w / font::hor;
336151497Sru      put_char(c);
337151497Sru      for (int i = 0; i < n; i++)
338151497Sru	putchar('\b');
339151497Sru    }
340114402Sru  }
341114402Sru  else {
342114402Sru    if (!is_bold)
343114402Sru      putstring(SGR_BOLD);
344114402Sru    is_bold = 1;
345114402Sru  }
346114402Sru}
347114402Sru
348151497Sruschar tty_printer::color_to_idx(color *col)
349114402Sru{
350114402Sru  if (col->is_default())
351114402Sru    return DEFAULT_COLOR_IDX;
352114402Sru  unsigned int r, g, b;
353114402Sru  col->get_rgb(&r, &g, &b);
354151497Sru  schar idx;
355114402Sru  if (tty_color(r, g, b, &idx)) {
356114402Sru    char *s = col->print_color();
357114402Sru    error("Unknown color (%1) mapped to default", s);
358114402Sru    a_delete s;
359114402Sru  }
360114402Sru  return idx;
361114402Sru}
362114402Sru
363114402Sruvoid tty_printer::set_char(int i, font *f, const environment *env,
364114402Sru			   int w, const char *)
365114402Sru{
366151497Sru  if (w % font::hor != 0)
367151497Sru    fatal("width of character not a multiple of horizontal resolution");
368151497Sru  add_char(f->get_code(i), w,
369114402Sru	   env->hpos, env->vpos,
370114402Sru	   env->col, env->fill,
371114402Sru	   ((tty_font *)f)->get_mode());
372114402Sru}
373114402Sru
374151497Sruvoid tty_printer::add_char(unsigned int c, int w,
375114402Sru			   int h, int v,
376114402Sru			   color *fore, color *back,
377114402Sru			   unsigned char mode)
378114402Sru{
379114402Sru#if 0
380114402Sru  // This is too expensive.
381114402Sru  if (h % font::hor != 0)
382114402Sru    fatal("horizontal position not a multiple of horizontal resolution");
383114402Sru#endif
384114402Sru  int hpos = h / font::hor;
385114402Sru  if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
386114402Sru    error("character with ridiculous horizontal position discarded");
387114402Sru    return;
388114402Sru  }
389114402Sru  int vpos;
390114402Sru  if (v == cached_v && cached_v != 0)
391114402Sru    vpos = cached_vpos;
392114402Sru  else {
393114402Sru    if (v % font::vert != 0)
394114402Sru      fatal("vertical position not a multiple of vertical resolution");
395114402Sru    vpos = v / font::vert;
396114402Sru    if (vpos > nlines) {
397114402Sru      glyph **old_lines = lines;
398114402Sru      lines = new glyph *[vpos + 1];
399114402Sru      memcpy(lines, old_lines, nlines * sizeof(glyph *));
400114402Sru      for (int i = nlines; i <= vpos; i++)
401114402Sru	lines[i] = 0;
402114402Sru      a_delete old_lines;
403114402Sru      nlines = vpos + 1;
404114402Sru    }
405114402Sru    // Note that the first output line corresponds to groff
406114402Sru    // position font::vert.
407114402Sru    if (vpos <= 0) {
408114402Sru      error("character above first line discarded");
409114402Sru      return;
410114402Sru    }
411114402Sru    cached_v = v;
412114402Sru    cached_vpos = vpos;
413114402Sru  }
414114402Sru  glyph *g = new glyph;
415151497Sru  g->w = w;
416114402Sru  g->hpos = hpos;
417114402Sru  g->code = c;
418114402Sru  g->fore_color_idx = color_to_idx(fore);
419114402Sru  g->back_color_idx = color_to_idx(back);
420114402Sru  g->mode = mode;
421114402Sru
422114402Sru  // The list will be reversed later.  After reversal, it must be in
423114402Sru  // increasing order of hpos, with COLOR_CHANGE and CU specials before
424114402Sru  // HDRAW characters before VDRAW characters before normal characters
425114402Sru  // at each hpos, and otherwise in order of occurrence.
426114402Sru
427114402Sru  glyph **pp;
428114402Sru  for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
429114402Sru    if ((*pp)->hpos < hpos
430114402Sru	|| ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
431114402Sru      break;
432114402Sru  g->next = *pp;
433114402Sru  *pp = g;
434114402Sru}
435114402Sru
436114402Sruvoid tty_printer::special(char *arg, const environment *env, char type)
437114402Sru{
438114402Sru  if (type == 'u') {
439151497Sru    add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill,
440151497Sru	     CU_MODE);
441114402Sru    return;
442114402Sru  }
443114402Sru  if (type != 'p')
444114402Sru    return;
445114402Sru  char *p;
446114402Sru  for (p = arg; *p == ' ' || *p == '\n'; p++)
447114402Sru    ;
448114402Sru  char *tag = p;
449114402Sru  for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
450114402Sru    ;
451114402Sru  if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) {
452114402Sru    error("X command without `tty:' tag ignored");
453114402Sru    return;
454114402Sru  }
455114402Sru  p++;
456114402Sru  for (; *p == ' ' || *p == '\n'; p++)
457114402Sru    ;
458114402Sru  char *command = p;
459114402Sru  for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
460114402Sru    ;
461114402Sru  if (*command == '\0') {
462114402Sru    error("empty X command ignored");
463114402Sru    return;
464114402Sru  }
465114402Sru  if (strncmp(command, "sgr", p - command) == 0) {
466114402Sru    for (; *p == ' ' || *p == '\n'; p++)
467114402Sru      ;
468114402Sru    int n;
469114402Sru    if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0)
470114402Sru      old_drawing_scheme = 1;
471114402Sru    else
472114402Sru      old_drawing_scheme = 0;
473151497Sru    update_options();
474114402Sru  }
475114402Sru}
476114402Sru
477114402Sruvoid tty_printer::change_color(const environment * const env)
478114402Sru{
479151497Sru  add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
480114402Sru}
481114402Sru
482114402Sruvoid tty_printer::change_fill_color(const environment * const env)
483114402Sru{
484151497Sru  add_char(0, 0, env->hpos, env->vpos, env->col, env->fill, COLOR_CHANGE);
485114402Sru}
486114402Sru
487114402Sruvoid tty_printer::draw(int code, int *p, int np, const environment *env)
488114402Sru{
489114402Sru  if (code != 'l' || !draw_flag)
490114402Sru    return;
491114402Sru  if (np != 2) {
492114402Sru    error("2 arguments required for line");
493114402Sru    return;
494114402Sru  }
495114402Sru  if (p[0] == 0) {
496114402Sru    // vertical line
497114402Sru    int v = env->vpos;
498114402Sru    int len = p[1];
499114402Sru    if (len < 0) {
500114402Sru      v += len;
501114402Sru      len = -len;
502114402Sru    }
503151497Sru    if (len >= 0 && len <= font::vert)
504151497Sru      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
505151497Sru	       VDRAW_MODE|START_LINE|END_LINE);
506151497Sru    else {
507151497Sru      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
508151497Sru	       VDRAW_MODE|START_LINE);
509114402Sru      len -= font::vert;
510114402Sru      v += font::vert;
511151497Sru      while (len > 0) {
512151497Sru	add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
513151497Sru		 VDRAW_MODE|START_LINE|END_LINE);
514151497Sru	len -= font::vert;
515151497Sru	v += font::vert;
516151497Sru      }
517151497Sru      add_char(vline_char, font::hor, env->hpos, v, env->col, env->fill,
518151497Sru	       VDRAW_MODE|END_LINE);
519114402Sru    }
520114402Sru  }
521114402Sru  if (p[1] == 0) {
522114402Sru    // horizontal line
523114402Sru    int h = env->hpos;
524114402Sru    int len = p[0];
525114402Sru    if (len < 0) {
526114402Sru      h += len;
527114402Sru      len = -len;
528114402Sru    }
529151497Sru    if (len >= 0 && len <= font::hor)
530151497Sru      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
531151497Sru	       HDRAW_MODE|START_LINE|END_LINE);
532151497Sru    else {
533151497Sru      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
534151497Sru	       HDRAW_MODE|START_LINE);
535114402Sru      len -= font::hor;
536114402Sru      h += font::hor;
537151497Sru      while (len > 0) {
538151497Sru	add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
539151497Sru		 HDRAW_MODE|START_LINE|END_LINE);
540151497Sru	len -= font::hor;
541151497Sru	h += font::hor;
542151497Sru      }
543151497Sru      add_char(hline_char, font::hor, h, env->vpos, env->col, env->fill,
544151497Sru	       HDRAW_MODE|END_LINE);
545114402Sru    }
546114402Sru  }
547114402Sru}
548114402Sru
549114402Sruvoid tty_printer::put_char(unsigned int wc)
550114402Sru{
551114402Sru  if (is_utf8 && wc >= 0x80) {
552114402Sru    char buf[6 + 1];
553114402Sru    int count;
554114402Sru    char *p = buf;
555114402Sru    if (wc < 0x800)
556114402Sru      count = 1, *p = (unsigned char)((wc >> 6) | 0xc0);
557114402Sru    else if (wc < 0x10000)
558114402Sru      count = 2, *p = (unsigned char)((wc >> 12) | 0xe0);
559114402Sru    else if (wc < 0x200000)
560114402Sru      count = 3, *p = (unsigned char)((wc >> 18) | 0xf0);
561114402Sru    else if (wc < 0x4000000)
562114402Sru      count = 4, *p = (unsigned char)((wc >> 24) | 0xf8);
563114402Sru    else if (wc <= 0x7fffffff)
564114402Sru      count = 5, *p = (unsigned char)((wc >> 30) | 0xfC);
565114402Sru    else
566114402Sru      return;
567114402Sru    do *++p = (unsigned char)(((wc >> (6 * --count)) & 0x3f) | 0x80);
568114402Sru      while (count > 0);
569114402Sru    *++p = '\0';
570114402Sru    putstring(buf);
571114402Sru  }
572114402Sru  else
573114402Sru    putchar(wc);
574114402Sru}
575114402Sru
576151497Sruvoid tty_printer::put_color(schar color_index, int back)
577114402Sru{
578114402Sru  if (color_index == DEFAULT_COLOR_IDX) {
579114402Sru    putstring(SGR_DEFAULT);
580114402Sru    // set bold and underline again
581114402Sru    if (is_bold)
582114402Sru      putstring(SGR_BOLD);
583114402Sru    if (is_underline) {
584114402Sru      if (italic_flag)
585114402Sru	putstring(SGR_ITALIC);
586114402Sru      else if (reverse_flag)
587114402Sru	putstring(SGR_REVERSE);
588114402Sru      else
589114402Sru	putstring(SGR_UNDERLINE);
590114402Sru    }
591114402Sru    // set other color again
592114402Sru    back = !back;
593114402Sru    color_index = back ? curr_back_idx : curr_fore_idx;
594114402Sru  }
595114402Sru  if (color_index != DEFAULT_COLOR_IDX) {
596114402Sru    putstring(CSI);
597114402Sru    if (back)
598114402Sru      putchar('4');
599114402Sru    else
600114402Sru      putchar('3');
601114402Sru    putchar(color_index + '0');
602114402Sru    putchar('m');
603114402Sru  }
604114402Sru}
605114402Sru
606151497Sru// The possible Unicode combinations for crossing characters.
607151497Sru//
608151497Sru// `  ' = 0, ` -' = 4, `- ' = 8, `--' = 12,
609151497Sru//
610151497Sru// `  ' = 0, ` ' = 1, `|' = 2, `|' = 3
611151497Sru//            |                 |
612151497Sru
613151497Srustatic int crossings[4*4] = {
614151497Sru  0x0000, 0x2577, 0x2575, 0x2502,
615151497Sru  0x2576, 0x250C, 0x2514, 0x251C,
616151497Sru  0x2574, 0x2510, 0x2518, 0x2524,
617151497Sru  0x2500, 0x252C, 0x2534, 0x253C
618151497Sru};
619151497Sru
620114402Sruvoid tty_printer::end_page(int page_length)
621114402Sru{
622114402Sru  if (page_length % font::vert != 0)
623114402Sru    error("vertical position at end of page not multiple of vertical resolution");
624114402Sru  int lines_per_page = page_length / font::vert;
625114402Sru  int last_line;
626114402Sru  for (last_line = nlines; last_line > 0; last_line--)
627114402Sru    if (lines[last_line - 1])
628114402Sru      break;
629114402Sru#if 0
630114402Sru  if (last_line > lines_per_page) {
631114402Sru    error("characters past last line discarded");
632114402Sru    do {
633114402Sru      --last_line;
634114402Sru      while (lines[last_line]) {
635114402Sru	glyph *tem = lines[last_line];
636114402Sru	lines[last_line] = tem->next;
637114402Sru	delete tem;
638114402Sru      }
639114402Sru    } while (last_line > lines_per_page);
640114402Sru  }
641114402Sru#endif
642114402Sru  for (int i = 0; i < last_line; i++) {
643114402Sru    glyph *p = lines[i];
644114402Sru    lines[i] = 0;
645114402Sru    glyph *g = 0;
646114402Sru    while (p) {
647114402Sru      glyph *tem = p->next;
648114402Sru      p->next = g;
649114402Sru      g = p;
650114402Sru      p = tem;
651114402Sru    }
652114402Sru    int hpos = 0;
653114402Sru    glyph *nextp;
654114402Sru    curr_fore_idx = DEFAULT_COLOR_IDX;
655114402Sru    curr_back_idx = DEFAULT_COLOR_IDX;
656114402Sru    is_underline = 0;
657114402Sru    is_bold = 0;
658114402Sru    for (p = g; p; delete p, p = nextp) {
659114402Sru      nextp = p->next;
660114402Sru      if (p->mode & CU_MODE) {
661114402Sru	cu_flag = p->code;
662114402Sru	continue;
663114402Sru      }
664114402Sru      if (nextp && p->hpos == nextp->hpos) {
665114402Sru	if (p->draw_mode() == HDRAW_MODE &&
666114402Sru	    nextp->draw_mode() == VDRAW_MODE) {
667151497Sru	  if (is_utf8)
668151497Sru	    nextp->code =
669151497Sru	      crossings[((p->mode & (START_LINE|END_LINE)) >> 4)
670151497Sru			+ ((nextp->mode & (START_LINE|END_LINE)) >> 6)];
671151497Sru	  else
672151497Sru	    nextp->code = '+';
673114402Sru	  continue;
674114402Sru	}
675114402Sru	if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
676114402Sru	  nextp->code = p->code;
677114402Sru	  continue;
678114402Sru	}
679114402Sru	if (!overstrike_flag)
680114402Sru	  continue;
681114402Sru      }
682114402Sru      if (hpos > p->hpos) {
683114402Sru	do {
684114402Sru	  putchar('\b');
685114402Sru	  hpos--;
686114402Sru	} while (hpos > p->hpos);
687114402Sru      }
688114402Sru      else {
689114402Sru	if (horizontal_tab_flag) {
690114402Sru	  for (;;) {
691114402Sru	    int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
692114402Sru	    if (next_tab_pos > p->hpos)
693114402Sru	      break;
694114402Sru	    if (cu_flag)
695151497Sru	      make_underline(p->w);
696114402Sru	    else if (!old_drawing_scheme && is_underline) {
697114402Sru	      if (italic_flag)
698114402Sru		putstring(SGR_NO_ITALIC);
699114402Sru	      else if (reverse_flag)
700114402Sru		putstring(SGR_NO_REVERSE);
701114402Sru	      else
702114402Sru		putstring(SGR_NO_UNDERLINE);
703114402Sru	      is_underline = 0;
704114402Sru	    }
705114402Sru	    putchar('\t');
706114402Sru	    hpos = next_tab_pos;
707114402Sru	  }
708114402Sru	}
709114402Sru	for (; hpos < p->hpos; hpos++) {
710114402Sru	  if (cu_flag)
711151497Sru	    make_underline(p->w);
712114402Sru	  else if (!old_drawing_scheme && is_underline) {
713114402Sru	    if (italic_flag)
714114402Sru	      putstring(SGR_NO_ITALIC);
715114402Sru	    else if (reverse_flag)
716114402Sru	      putstring(SGR_NO_REVERSE);
717114402Sru	    else
718114402Sru	      putstring(SGR_NO_UNDERLINE);
719114402Sru	    is_underline = 0;
720114402Sru	  }
721114402Sru	  putchar(' ');
722114402Sru	}
723114402Sru      }
724114402Sru      assert(hpos == p->hpos);
725114402Sru      if (p->mode & COLOR_CHANGE) {
726114402Sru	if (!old_drawing_scheme) {
727114402Sru	  if (p->fore_color_idx != curr_fore_idx) {
728114402Sru	    put_color(p->fore_color_idx, 0);
729114402Sru	    curr_fore_idx = p->fore_color_idx;
730114402Sru	  }
731114402Sru	  if (p->back_color_idx != curr_back_idx) {
732114402Sru	    put_color(p->back_color_idx, 1);
733114402Sru	    curr_back_idx = p->back_color_idx;
734114402Sru	  }
735114402Sru	}
736114402Sru	continue;
737114402Sru      }
738114402Sru      if (p->mode & UNDERLINE_MODE)
739151497Sru	make_underline(p->w);
740114402Sru      else if (!old_drawing_scheme && is_underline) {
741114402Sru	if (italic_flag)
742114402Sru	  putstring(SGR_NO_ITALIC);
743114402Sru	else if (reverse_flag)
744114402Sru	  putstring(SGR_NO_REVERSE);
745114402Sru	else
746114402Sru	  putstring(SGR_NO_UNDERLINE);
747114402Sru	is_underline = 0;
748114402Sru      }
749114402Sru      if (p->mode & BOLD_MODE)
750151497Sru	make_bold(p->code, p->w);
751114402Sru      else if (!old_drawing_scheme && is_bold) {
752114402Sru	putstring(SGR_NO_BOLD);
753114402Sru	is_bold = 0;
754114402Sru      }
755114402Sru      if (!old_drawing_scheme) {
756114402Sru	if (p->fore_color_idx != curr_fore_idx) {
757114402Sru	  put_color(p->fore_color_idx, 0);
758114402Sru	  curr_fore_idx = p->fore_color_idx;
759114402Sru	}
760114402Sru	if (p->back_color_idx != curr_back_idx) {
761114402Sru	  put_color(p->back_color_idx, 1);
762114402Sru	  curr_back_idx = p->back_color_idx;
763114402Sru	}
764114402Sru      }
765114402Sru      put_char(p->code);
766151497Sru      hpos += p->w / font::hor;
767114402Sru    }
768114402Sru    if (!old_drawing_scheme
769114402Sru	&& (is_bold || is_underline
770114402Sru	    || curr_fore_idx != DEFAULT_COLOR_IDX
771114402Sru	    || curr_back_idx != DEFAULT_COLOR_IDX))
772114402Sru      putstring(SGR_DEFAULT);
773114402Sru    putchar('\n');
774114402Sru  }
775114402Sru  if (form_feed_flag) {
776114402Sru    if (last_line < lines_per_page)
777114402Sru      putchar('\f');
778114402Sru  }
779114402Sru  else {
780114402Sru    for (; last_line < lines_per_page; last_line++)
781114402Sru      putchar('\n');
782114402Sru  }
783114402Sru}
784114402Sru
785114402Srufont *tty_printer::make_font(const char *nm)
786114402Sru{
787114402Sru  return tty_font::load_tty_font(nm);
788114402Sru}
789114402Sru
790114402Sruprinter *make_printer()
791114402Sru{
792114402Sru  return new tty_printer(device);
793114402Sru}
794114402Sru
795151497Srustatic void update_options()
796151497Sru{
797151497Sru  if (old_drawing_scheme) {
798151497Sru    italic_flag = 0;
799151497Sru    reverse_flag = 0;
800151497Sru    bold_underline_mode = bold_underline_mode_option;
801151497Sru    bold_flag = bold_flag_option;
802151497Sru    underline_flag = underline_flag_option;
803151497Sru  }
804151497Sru  else {
805151497Sru    italic_flag = italic_flag_option;
806151497Sru    reverse_flag = reverse_flag_option;
807151497Sru    bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
808151497Sru    bold_flag = 1;
809151497Sru    underline_flag = 1;
810151497Sru  }
811151497Sru}
812114402Sru
813114402Sruint main(int argc, char **argv)
814114402Sru{
815114402Sru  program_name = argv[0];
816114402Sru  static char stderr_buf[BUFSIZ];
817114402Sru  if (getenv("GROFF_NO_SGR"))
818114402Sru    old_drawing_scheme = 1;
819114402Sru  setbuf(stderr, stderr_buf);
820114402Sru  int c;
821114402Sru  static const struct option long_options[] = {
822114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
823114402Sru    { "version", no_argument, 0, 'v' },
824114402Sru    { NULL, 0, 0, 0 }
825114402Sru  };
826151497Sru  while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
827114402Sru	 != EOF)
828114402Sru    switch(c) {
829114402Sru    case 'v':
830114402Sru      printf("GNU grotty (groff) version %s\n", Version_string);
831114402Sru      exit(0);
832114402Sru      break;
833114402Sru    case 'i':
834114402Sru      // Use italic font instead of underlining.
835151497Sru      italic_flag_option = 1;
836114402Sru      break;
837151497Sru    case 'I':
838151497Sru      // ignore include search path
839151497Sru      break;
840114402Sru    case 'b':
841114402Sru      // Do not embolden by overstriking.
842151497Sru      bold_flag_option = 0;
843114402Sru      break;
844114402Sru    case 'c':
845114402Sru      // Use old scheme for emboldening and underline.
846114402Sru      old_drawing_scheme = 1;
847114402Sru      break;
848114402Sru    case 'u':
849114402Sru      // Do not underline.
850151497Sru      underline_flag_option = 0;
851114402Sru      break;
852114402Sru    case 'o':
853114402Sru      // Do not overstrike (other than emboldening and underlining).
854114402Sru      overstrike_flag = 0;
855114402Sru      break;
856114402Sru    case 'r':
857114402Sru      // Use reverse mode instead of underlining.
858151497Sru      reverse_flag_option = 1;
859114402Sru      break;
860114402Sru    case 'B':
861114402Sru      // Do bold-underlining as bold.
862151497Sru      bold_underline_mode_option = BOLD_MODE;
863114402Sru      break;
864114402Sru    case 'U':
865114402Sru      // Do bold-underlining as underlining.
866151497Sru      bold_underline_mode_option = UNDERLINE_MODE;
867114402Sru      break;
868114402Sru    case 'h':
869114402Sru      // Use horizontal tabs.
870114402Sru      horizontal_tab_flag = 1;
871114402Sru      break;
872114402Sru    case 'f':
873114402Sru      form_feed_flag = 1;
874114402Sru      break;
875114402Sru    case 'F':
876114402Sru      font::command_line_font_dir(optarg);
877114402Sru      break;
878114402Sru    case 'd':
879114402Sru      // Ignore \D commands.
880114402Sru      draw_flag = 0;
881114402Sru      break;
882114402Sru    case CHAR_MAX + 1: // --help
883114402Sru      usage(stdout);
884114402Sru      exit(0);
885114402Sru      break;
886114402Sru    case '?':
887114402Sru      usage(stderr);
888114402Sru      exit(1);
889114402Sru      break;
890114402Sru    default:
891114402Sru      assert(0);
892114402Sru    }
893151497Sru  update_options();
894114402Sru  if (optind >= argc)
895114402Sru    do_file("-");
896114402Sru  else {
897114402Sru    for (int i = optind; i < argc; i++)
898114402Sru      do_file(argv[i]);
899114402Sru  }
900114402Sru  return 0;
901114402Sru}
902114402Sru
903114402Srustatic void usage(FILE *stream)
904114402Sru{
905114402Sru  fprintf(stream, "usage: %s [-bBcdfhioruUv] [-F dir] [files ...]\n",
906114402Sru	  program_name);
907114402Sru}
908