1// -*- C++ -*-
2
3// <groff_src_dir>/src/libs/libdriver/printer.cpp
4
5/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
6   Free Software Foundation, Inc.
7   Written by James Clark (jjc@jclark.com)
8
9   Last update: 02 Mar 2005
10
11   This file is part of groff.
12
13   groff is free software; you can redistribute it and/or modify it
14   under the terms of the GNU General Public License as published by
15   the Free Software Foundation; either version 2, or (at your option)
16   any later version.
17
18   groff is distributed in the hope that it will be useful, but
19   WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21   General Public License for more details.
22
23   You should have received a copy of the GNU General Public License
24   along with groff; see the file COPYING.  If not, write to the Free
25   Software Foundation, 51 Franklin St - Fifth Floor, Boston, MA
26   02110-1301, USA.
27*/
28
29#include "driver.h"
30
31/* If we are sending output to an onscreen pager (as is the normal case
32   when reading man pages), then we may get an error state on the output
33   stream, if the user does not read all the way to the end.
34
35   We normally expect to catch this, and clean up the error context, when
36   the pager exits, because we should get, and handle, a SIGPIPE.
37
38   However ...
39*/
40
41#if (defined(_MSC_VER) || defined(_WIN32)) \
42    && !defined(__CYGWIN__) && !defined(_UWIN)
43
44  /* Native MS-Windows doesn't know about SIGPIPE, so we cannot detect the
45     early exit from the pager, and therefore, cannot clean up the error
46     context; thus we use the following static function to identify this
47     particular error context, and so suppress unwanted diagnostics.
48  */
49
50  static int
51  check_for_output_error (FILE* stream)
52  {
53    /* First, clean up any prior error context on the output stream */
54    if (ferror (stream))
55      clearerr (stream);
56    /* Clear errno, in case clearerr() and fflush() don't */
57    errno = 0;
58    /* Flush the output stream, so we can capture any error context, other
59       than the specific case we wish to suppress.
60
61       Microsoft doesn't document it, but the error code for the specific
62       context we are trying to suppress seems to be EINVAL -- a strange
63       choice, since it is not normally associated with fflush(); of course,
64       it *should* be EPIPE, but this *definitely* is not used, and *is* so
65       documented.
66    */
67    return ((fflush(stream) < 0) && (errno != EINVAL));
68  }
69
70#else
71
72  /* For other systems, we simply assume that *any* output error context
73     is to be reported.
74  */
75# define check_for_output_error(stream) ferror(stream) || fflush(stream) < 0
76
77#endif
78
79
80font_pointer_list::font_pointer_list(font *f, font_pointer_list *fp)
81: p(f), next(fp)
82{
83}
84
85printer::printer()
86: font_list(0), font_table(0), nfonts(0)
87{
88}
89
90printer::~printer()
91{
92  a_delete font_table;
93  while (font_list) {
94    font_pointer_list *tem = font_list;
95    font_list = font_list->next;
96    delete tem->p;
97    delete tem;
98  }
99  if (check_for_output_error(stdout))
100    fatal("output error");
101}
102
103void printer::load_font(int n, const char *nm)
104{
105  assert(n >= 0);
106  if (n >= nfonts) {
107    if (nfonts == 0) {
108      nfonts = 10;
109      if (nfonts <= n)
110	nfonts = n + 1;
111      font_table = new font *[nfonts];
112      for (int i = 0; i < nfonts; i++)
113	font_table[i] = 0;
114    }
115    else {
116      font **old_font_table = font_table;
117      int old_nfonts = nfonts;
118      nfonts *= 2;
119      if (n >= nfonts)
120	nfonts = n + 1;
121      font_table = new font *[nfonts];
122      int i;
123      for (i = 0; i < old_nfonts; i++)
124	font_table[i] = old_font_table[i];
125      for (i = old_nfonts; i < nfonts; i++)
126	font_table[i] = 0;
127      a_delete old_font_table;
128    }
129  }
130  font *f = find_font(nm);
131  font_table[n] = f;
132}
133
134font *printer::find_font(const char *nm)
135{
136  for (font_pointer_list *p = font_list; p; p = p->next)
137    if (strcmp(p->p->get_name(), nm) == 0)
138      return p->p;
139  font *f = make_font(nm);
140  if (!f)
141    fatal("sorry, I can't continue");
142  font_list = new font_pointer_list(f, font_list);
143  return f;
144}
145
146font *printer::make_font(const char *nm)
147{
148  return font::load_font(nm);
149}
150
151void printer::end_of_line()
152{
153}
154
155void printer::special(char *, const environment *, char)
156{
157}
158
159void printer::devtag(char *, const environment *, char)
160{
161}
162
163void printer::draw(int, int *, int, const environment *)
164{
165}
166
167void printer::change_color(const environment * const)
168{
169}
170
171void printer::change_fill_color(const environment * const)
172{
173}
174
175void printer::set_ascii_char(unsigned char c, const environment *env,
176			     int *widthp)
177{
178  char  buf[2];
179  int   w;
180  font *f;
181
182  buf[0] = c;
183  buf[1] = '\0';
184
185  int i = set_char_and_width(buf, env, &w, &f);
186  set_char(i, f, env, w, 0);
187  if (widthp) {
188    *widthp = w;
189  }
190}
191
192void printer::set_special_char(const char *nm, const environment *env,
193			       int *widthp)
194{
195  font *f;
196  int w;
197  int i = set_char_and_width(nm, env, &w, &f);
198  if (i != -1) {
199    set_char(i, f, env, w, nm);
200    if (widthp)
201      *widthp = w;
202  }
203}
204
205int printer::set_char_and_width(const char *nm, const environment *env,
206				int *widthp, font **f)
207{
208  int i = font::name_to_index(nm);
209  int fn = env->fontno;
210  if (fn < 0 || fn >= nfonts) {
211    error("bad font position `%1'", fn);
212    return(-1);
213  }
214  *f = font_table[fn];
215  if (*f == 0) {
216    error("no font mounted at `%1'", fn);
217    return(-1);
218  }
219  if (!(*f)->contains(i)) {
220    if (nm[0] != '\0' && nm[1] == '\0')
221      error("font `%1' does not contain ascii character `%2'",
222	    (*f)->get_name(),
223	    nm[0]);
224    else
225      error("font `%1' does not contain special character `%2'",
226	    (*f)->get_name(),
227	    nm);
228    return(-1);
229  }
230  int w = (*f)->get_width(i, env->size);
231  if (widthp)
232    *widthp = w;
233  return( i );
234}
235
236void printer::set_numbered_char(int num, const environment *env, int *widthp)
237{
238  int i = font::number_to_index(num);
239  int fn = env->fontno;
240  if (fn < 0 || fn >= nfonts) {
241    error("bad font position `%1'", fn);
242    return;
243  }
244  font *f = font_table[fn];
245  if (f == 0) {
246    error("no font mounted at `%1'", fn);
247    return;
248  }
249  if (!f->contains(i)) {
250    error("font `%1' does not contain numbered character %2",
251	  f->get_name(),
252	  num);
253    return;
254  }
255  int w = f->get_width(i, env->size);
256  if (widthp)
257    *widthp = w;
258  set_char(i, f, env, w, 0);
259}
260
261font *printer::get_font_from_index(int fontno)
262{
263  if ((fontno >= 0) && (fontno < nfonts))
264    return(font_table[fontno]);
265  else
266    return(0);
267}
268