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