Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / libs / libdriver / printer.cpp
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
80 font_pointer_list::font_pointer_list(font *f, font_pointer_list *fp)
81 : p(f), next(fp)
82 {
83 }
84
85 printer::printer()
86 : font_list(0), font_table(0), nfonts(0)
87 {
88 }
89
90 printer::~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
103 void 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
134 font *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
146 font *printer::make_font(const char *nm)
147 {
148   return font::load_font(nm);
149 }
150
151 void printer::end_of_line()
152 {
153 }
154
155 void printer::special(char *, const environment *, char)
156 {
157 }
158
159 void printer::devtag(char *, const environment *, char)
160 {
161 }
162
163 void printer::draw(int, int *, int, const environment *)
164 {
165 }
166
167 void printer::change_color(const environment * const)
168 {
169 }
170
171 void printer::change_fill_color(const environment * const)
172 {
173 }
174
175 void 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
192 void 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
205 int 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
236 void 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
261 font *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 }