Commit | Line | Data |
---|---|---|
b0f007a2 NH |
1 | // -*- C++ -*- |
2 | /* Copyright (C) 1989, 1990 Free Software Foundation, Inc. | |
3 | Written by James Clark (jjc@jclark.uucp) | |
4 | ||
5 | This file is part of groff. | |
6 | ||
7 | groff is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 1, or (at your option) any later | |
10 | version. | |
11 | ||
12 | groff is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License along | |
18 | with groff; see the file LICENSE. If not, write to the Free Software | |
19 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | #include "driver.h" | |
22 | ||
23 | #ifndef USHRT_MAX | |
24 | #define USHRT_MAX 65535 | |
25 | #endif | |
26 | ||
27 | #define DEFAULT_LINES_PER_PAGE 66 | |
28 | ||
29 | #define TAB_WIDTH 8 | |
30 | ||
31 | static int horizontal_tab_flag = 0; | |
32 | static int form_feed_flag = 0; | |
33 | static int bold_flag = 1; | |
34 | static int underline_flag = 1; | |
35 | static int overstrike_flag = 1; | |
36 | ||
37 | enum { UNDERLINE_MODE = 01, BOLD_MODE = 02 }; | |
38 | ||
39 | // Mode to use for bold-underlining. | |
40 | static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE; | |
41 | ||
42 | class tty_font : public font { | |
43 | tty_font(const char *); | |
44 | unsigned char mode; | |
45 | public: | |
46 | ~tty_font(); | |
47 | unsigned char get_mode() { return mode; } | |
48 | #if 0 | |
49 | void handle_x_command(int argc, const char **argv); | |
50 | #endif | |
51 | static tty_font *load_tty_font(const char *); | |
52 | }; | |
53 | ||
54 | tty_font *tty_font::load_tty_font(const char *s) | |
55 | { | |
56 | tty_font *f = new tty_font(s); | |
57 | if (!f->load()) { | |
58 | delete f; | |
59 | return 0; | |
60 | } | |
61 | const char *s = f->get_internal_name(); | |
62 | long n; | |
63 | if (s != 0 && (n = strtol(s, 0, 0)) != 0) | |
64 | f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE)); | |
65 | if (!underline_flag) | |
66 | f->mode &= ~UNDERLINE_MODE; | |
67 | if (!bold_flag) | |
68 | f->mode &= ~BOLD_MODE; | |
69 | if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE)) | |
70 | f->mode = (f->mode & ~(BOLD_MODE|UNDERLINE_MODE)) | bold_underline_mode; | |
71 | return f; | |
72 | } | |
73 | ||
74 | tty_font::tty_font(const char *nm) | |
75 | : font(nm), mode(0) | |
76 | { | |
77 | } | |
78 | ||
79 | tty_font::~tty_font() | |
80 | { | |
81 | } | |
82 | ||
83 | #if 0 | |
84 | void tty_font::handle_x_command(int argc, const char **argv) | |
85 | { | |
86 | if (argc >= 1 && strcmp(argv[0], "bold") == 0) | |
87 | mode |= BOLD_MODE; | |
88 | else if (argc >= 1 && strcmp(argv[0], "underline") == 0) | |
89 | mode |= UNDERLINE_MODE; | |
90 | } | |
91 | #endif | |
92 | ||
93 | // hpos and vpos must be non-adjacent, to work round a bug in g++ 1.37.1 | |
94 | ||
95 | struct glyph { | |
96 | unsigned short hpos; | |
97 | unsigned short serial; | |
98 | unsigned short vpos; | |
99 | unsigned char code; | |
100 | unsigned char mode; | |
101 | }; | |
102 | ||
103 | class tty_printer : public printer { | |
104 | enum { INITIAL_VEC_SIZE = 32 }; | |
105 | glyph *vec; | |
106 | int vec_used; | |
107 | int vec_size; | |
108 | int lines_per_page; | |
109 | int columns_per_page; | |
110 | public: | |
111 | tty_printer(); | |
112 | ~tty_printer(); | |
113 | void set_char(int, font *, const environment *, int); | |
114 | void begin_page(int) { } | |
115 | void end_page(); | |
116 | font *make_font(const char *); | |
117 | }; | |
118 | ||
119 | tty_printer::tty_printer() | |
120 | : vec_used(0), vec_size(0), vec(0) | |
121 | { | |
122 | if (font::paperlength == 0) | |
123 | lines_per_page = DEFAULT_LINES_PER_PAGE; | |
124 | else if (font::paperlength % font::vert != 0) | |
125 | fatal("paperlength not a multiple of vertical resolution"); | |
126 | else | |
127 | lines_per_page = font::paperlength/font::vert; | |
128 | if (lines_per_page > USHRT_MAX || lines_per_page <= 0) | |
129 | fatal("ridiculous paperlength"); | |
130 | columns_per_page = font::paperwidth/font::hor; | |
131 | // If columns_per_page is zero, we won't truncate. | |
132 | if (columns_per_page < 0) | |
133 | columns_per_page = 0; | |
134 | } | |
135 | ||
136 | tty_printer::~tty_printer() | |
137 | { | |
138 | delete vec; | |
139 | } | |
140 | ||
141 | void tty_printer::set_char(int i, font *f, const environment *env, int w) | |
142 | { | |
143 | int h = env->hpos; | |
144 | if (h % font::hor != 0) | |
145 | fatal("horizontal position not a multiple of horizontal resolution"); | |
146 | h /= font::hor; | |
147 | if (h < 0) { | |
148 | error("character to the left of first column discarded"); | |
149 | return; | |
150 | } | |
151 | if (columns_per_page != 0 && h >= columns_per_page) { | |
152 | error("character to the right of last column discarded"); | |
153 | return; | |
154 | } | |
155 | if (h > USHRT_MAX) { | |
156 | error("character with ridiculously large horizontal position discarded"); | |
157 | return; | |
158 | } | |
159 | int v = env->vpos; | |
160 | if (v % font::vert != 0) | |
161 | fatal("vertical position not a multiple of vertical resolution"); | |
162 | v /= font::vert; | |
163 | // Note that the first output line corresponds to groff position font::vert. | |
164 | if (v <= 0) { | |
165 | error("character above first line discarded"); | |
166 | return; | |
167 | } | |
168 | if (v > lines_per_page) { | |
169 | error("character below last line discarded"); | |
170 | return; | |
171 | } | |
172 | if (w != font::hor) | |
173 | fatal("width of character not equal to horizontal resolution"); | |
174 | if (vec_used >= vec_size) { | |
175 | if (vec_size == 0) | |
176 | vec_size = INITIAL_VEC_SIZE; | |
177 | else { | |
178 | if (vec_size > USHRT_MAX/2) { | |
179 | if (vec_size >= USHRT_MAX) | |
180 | fatal("too many characters on the page"); | |
181 | vec_size = USHRT_MAX; | |
182 | } | |
183 | else | |
184 | vec_size *= 2; | |
185 | } | |
186 | glyph *old_vec = vec; | |
187 | vec = new glyph [vec_size]; | |
188 | if (vec_used) | |
189 | memcpy(vec, old_vec, vec_used*sizeof(glyph)); | |
190 | delete old_vec; | |
191 | } | |
192 | // We need a stable sort, but qsort is not stable, so we fake it. | |
193 | vec[vec_used].serial = vec_used; | |
194 | vec[vec_used].hpos = h; | |
195 | vec[vec_used].vpos = v; | |
196 | vec[vec_used].code = f->get_code(i); | |
197 | vec[vec_used].mode = ((tty_font *)f)->get_mode(); | |
198 | vec_used++; | |
199 | } | |
200 | ||
201 | extern "C" { | |
202 | static int compare_glyph(void *p1, void *p2) | |
203 | { | |
204 | int v1 = ((glyph *)p1)->vpos; | |
205 | int v2 = ((glyph *)p2)->vpos; | |
206 | if (v1 < v2) | |
207 | return -1; | |
208 | if (v1 > v2) | |
209 | return 1; | |
210 | int h1 = ((glyph *)p1)->hpos; | |
211 | int h2 = ((glyph *)p2)->hpos; | |
212 | if (h1 < h2) | |
213 | return -1; | |
214 | if (h1 > h2) | |
215 | return 1; | |
216 | if (((glyph *)p1)->serial < ((glyph *)p2)->serial) | |
217 | return -1; | |
218 | return 1; | |
219 | } | |
220 | } | |
221 | ||
222 | void tty_printer::end_page() | |
223 | { | |
224 | qsort(vec, vec_used, sizeof(glyph), compare_glyph); | |
225 | int hpos = 0; | |
226 | int vpos = 1; | |
227 | // We have already discarded characters with vpos < 1 or > lines_per_page. | |
228 | for (int i = 0; i < vec_used; i++) { | |
229 | assert(vpos <= vec[i].vpos); | |
230 | if (!overstrike_flag | |
231 | && i + 1 < vec_used | |
232 | && vec[i].hpos == vec[i + 1].hpos | |
233 | && vec[i].vpos == vec[i + 1].vpos) | |
234 | continue; | |
235 | for (; vpos < vec[i].vpos; vpos++) { | |
236 | putchar('\n'); | |
237 | hpos = 0; | |
238 | } | |
239 | if (hpos > vec[i].hpos) { | |
240 | putchar('\b'); | |
241 | hpos--; | |
242 | } | |
243 | else { | |
244 | if (horizontal_tab_flag) { | |
245 | for (;;) { | |
246 | int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH; | |
247 | if (next_tab_pos > vec[i].hpos) | |
248 | break; | |
249 | putchar('\t'); | |
250 | hpos = next_tab_pos; | |
251 | } | |
252 | } | |
253 | for (; hpos < vec[i].hpos; hpos++) | |
254 | putchar(' '); | |
255 | } | |
256 | assert(hpos == vec[i].hpos && vpos == vec[i].vpos); | |
257 | if (isalnum(vec[i].code) && vec[i].mode & UNDERLINE_MODE) { | |
258 | putchar('_'); | |
259 | putchar('\b'); | |
260 | } | |
261 | if (vec[i].mode & BOLD_MODE) { | |
262 | putchar(vec[i].code); | |
263 | putchar('\b'); | |
264 | } | |
265 | putchar(vec[i].code); | |
266 | hpos++; | |
267 | } | |
268 | if (form_feed_flag) { | |
269 | if (hpos != 0) { | |
270 | putchar('\n'); | |
271 | vpos++; | |
272 | } | |
273 | if (vpos <= lines_per_page) | |
274 | putchar('\f'); | |
275 | } | |
276 | else { | |
277 | for (; vpos <= lines_per_page; vpos++) | |
278 | putchar('\n'); | |
279 | } | |
280 | vec_used = 0; | |
281 | } | |
282 | ||
283 | font *tty_printer::make_font(const char *nm) | |
284 | { | |
285 | return tty_font::load_tty_font(nm); | |
286 | } | |
287 | ||
288 | printer *make_printer() | |
289 | { | |
290 | return new tty_printer; | |
291 | } | |
292 | ||
293 | static void usage(); | |
294 | ||
295 | int main(int argc, char **argv) | |
296 | { | |
297 | program_name = argv[0]; | |
298 | static char stderr_buf[BUFSIZ]; | |
299 | setbuf(stderr, stderr_buf); | |
300 | int c; | |
301 | while ((c = getopt(argc, argv, "F:vhfbuoBU")) != EOF) | |
302 | switch(c) { | |
303 | case 'v': | |
304 | { | |
305 | extern const char *version_string; | |
306 | fprintf(stderr, "grotty version %s\n", version_string); | |
307 | fflush(stderr); | |
308 | break; | |
309 | } | |
310 | case 'b': | |
311 | // Do not embolden by overstriking. | |
312 | bold_flag = 0; | |
313 | break; | |
314 | case 'u': | |
315 | // Do not underline. | |
316 | underline_flag = 0; | |
317 | break; | |
318 | case 'o': | |
319 | // Do not overstrike (other than emboldening and underlining). | |
320 | overstrike_flag = 0; | |
321 | break; | |
322 | case 'B': | |
323 | // Do bold-underlining as bold. | |
324 | bold_underline_mode = BOLD_MODE; | |
325 | break; | |
326 | case 'U': | |
327 | // Do bold-underlining as underlining. | |
328 | bold_underline_mode = UNDERLINE_MODE; | |
329 | break; | |
330 | case 'h': | |
331 | // Use horizontal tabs. | |
332 | horizontal_tab_flag = 1; | |
333 | break; | |
334 | case 'f': | |
335 | form_feed_flag = 1; | |
336 | break; | |
337 | case 'F': | |
338 | font::command_line_font_dir(optarg); | |
339 | break; | |
340 | case '?': | |
341 | usage(); | |
342 | break; | |
343 | default: | |
344 | assert(0); | |
345 | } | |
346 | if (optind >= argc) | |
347 | do_file("-"); | |
348 | else { | |
349 | for (int i = optind; i < argc; i++) | |
350 | do_file(argv[i]); | |
351 | } | |
352 | delete pr; | |
353 | exit(0); | |
354 | } | |
355 | ||
356 | static void usage() | |
357 | { | |
358 | fprintf(stderr, "usage: %s [-hfvbuoBU] [-F dir] [files ...]\n", | |
359 | program_name); | |
360 | exit(1); | |
361 | } |