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