groff before CVS: release 1.06
[s-roff.git] / grotty / tty.cc
blob9ecabad5ff6f2394559991a4c1ef2f5aad4d5be2
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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 2, or (at your option) any later
10 version.
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.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "driver.h"
23 #ifndef USHRT_MAX
24 #define USHRT_MAX 65535
25 #endif
27 #define DEFAULT_LINES_PER_PAGE 66
29 #define TAB_WIDTH 8
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 static int draw_flag = 1;
38 enum {
39 UNDERLINE_MODE = 01,
40 BOLD_MODE = 02,
41 VDRAW_MODE = 04,
42 HDRAW_MODE = 010
45 // Mode to use for bold-underlining.
46 static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
48 class tty_font : public font {
49 tty_font(const char *);
50 unsigned char mode;
51 public:
52 ~tty_font();
53 unsigned char get_mode() { return mode; }
54 #if 0
55 void handle_x_command(int argc, const char **argv);
56 #endif
57 static tty_font *load_tty_font(const char *);
60 tty_font *tty_font::load_tty_font(const char *s)
62 tty_font *f = new tty_font(s);
63 if (!f->load()) {
64 delete f;
65 return 0;
67 const char *num = f->get_internal_name();
68 long n;
69 if (num != 0 && (n = strtol(num, 0, 0)) != 0)
70 f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE));
71 if (!underline_flag)
72 f->mode &= ~UNDERLINE_MODE;
73 if (!bold_flag)
74 f->mode &= ~BOLD_MODE;
75 if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
76 f->mode = (f->mode & ~(BOLD_MODE|UNDERLINE_MODE)) | bold_underline_mode;
77 return f;
80 tty_font::tty_font(const char *nm)
81 : font(nm), mode(0)
85 tty_font::~tty_font()
89 #if 0
90 void tty_font::handle_x_command(int argc, const char **argv)
92 if (argc >= 1 && strcmp(argv[0], "bold") == 0)
93 mode |= BOLD_MODE;
94 else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
95 mode |= UNDERLINE_MODE;
97 #endif
99 class glyph {
100 static glyph *free_list;
101 public:
102 glyph *next;
103 unsigned short hpos;
104 unsigned char code;
105 unsigned char mode;
106 void *operator new(size_t);
107 void operator delete(void *);
108 inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
111 glyph *glyph::free_list = 0;
113 void *glyph::operator new(size_t)
115 if (!free_list) {
116 const int BLOCK = 1024;
117 free_list = (glyph *)new char[sizeof(glyph)*BLOCK];
118 for (int i = 0; i < BLOCK - 1; i++)
119 free_list[i].next = free_list + i + 1;
120 free_list[BLOCK - 1].next = 0;
122 glyph *p = free_list;
123 free_list = free_list->next;
124 p->next = 0;
125 return p;
128 void glyph::operator delete(void *p)
130 if (p) {
131 ((glyph *)p)->next = free_list;
132 free_list = (glyph *)p;
136 class tty_printer : public printer {
137 glyph **lines;
138 int lines_per_page;
139 int columns_per_page;
140 int cached_v;
141 int cached_vpos;
142 void add_char(unsigned char, int, int, unsigned char);
143 public:
144 tty_printer();
145 ~tty_printer();
146 void set_char(int, font *, const environment *, int);
147 void draw(int code, int *p, int np, const environment *env);
148 void begin_page(int) { }
149 void end_page();
150 font *make_font(const char *);
153 tty_printer::tty_printer() : cached_v(0)
155 if (font::paperlength == 0)
156 lines_per_page = DEFAULT_LINES_PER_PAGE;
157 else if (font::paperlength % font::vert != 0)
158 fatal("paperlength not a multiple of vertical resolution");
159 else
160 lines_per_page = font::paperlength/font::vert;
161 if (lines_per_page <= 0)
162 fatal("paperlength too small");
163 lines = new glyph *[lines_per_page];
164 for (int i = 0; i < lines_per_page; i++)
165 lines[i] = 0;
166 columns_per_page = font::paperwidth/font::hor;
167 // If columns_per_page is zero, we won't truncate.
168 if (columns_per_page < 0)
169 columns_per_page = 0;
172 tty_printer::~tty_printer()
174 a_delete lines;
177 void tty_printer::set_char(int i, font *f, const environment *env, int w)
179 if (w != font::hor)
180 fatal("width of character not equal to horizontal resolution");
181 add_char(f->get_code(i), env->hpos, env->vpos, ((tty_font *)f)->get_mode());
184 void tty_printer::add_char(unsigned char c, int h, int v, unsigned char mode)
186 #if 0
187 // This is too expensive.
188 if (h % font::hor != 0)
189 fatal("horizontal position not a multiple of horizontal resolution");
190 #endif
191 int hpos = h / font::hor;
192 if (hpos < 0) {
193 error("character to the left of first column discarded");
194 return;
196 if (columns_per_page != 0 && hpos >= columns_per_page) {
197 error("character to the right of last column discarded");
198 return;
200 if (hpos > USHRT_MAX) {
201 error("character with ridiculously large horizontal position discarded");
202 return;
204 int vpos;
205 if (v == cached_v && cached_v != 0)
206 vpos = cached_vpos;
207 else {
208 if (v % font::vert != 0)
209 fatal("vertical position not a multiple of vertical resolution");
210 vpos = v / font::vert;
211 if (vpos > lines_per_page) {
212 error("character below last line discarded");
213 return;
215 // Note that the first output line corresponds to groff
216 // position font::vert.
217 if (vpos <= 0) {
218 error("character above first line discarded");
219 return;
221 cached_v = v;
222 cached_vpos = vpos;
224 glyph *g = new glyph;
225 g->hpos = hpos;
226 g->code = c;
227 g->mode = mode;
229 // The list will be reversed later. After reversal, it must be in
230 // increasing order of hpos, with HDRAW characters before VDRAW
231 // characters before normal characters at each hpos, and otherwise
232 // in order of occurrence.
234 for (glyph **pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
235 if ((*pp)->hpos < hpos
236 || ((*pp)->hpos == hpos && (*pp)->draw_mode() >= g->draw_mode()))
237 break;
239 g->next = *pp;
240 *pp = g;
243 void tty_printer::draw(int code, int *p, int np, const environment *env)
245 if (code != 'l' || !draw_flag)
246 return;
247 if (np != 2) {
248 error("2 arguments required for line");
249 return;
251 if (p[0] == 0) {
252 // vertical line
253 int v = env->vpos;
254 int len = p[1];
255 if (len < 0) {
256 v += len;
257 len = -len;
259 while (len >= 0) {
260 add_char('|', env->hpos, v, VDRAW_MODE);
261 len -= font::vert;
262 v += font::vert;
265 if (p[1] == 0) {
266 // horizontal line
267 int h = env->hpos;
268 int len = p[0];
269 if (len < 0) {
270 h += len;
271 len = -len;
273 while (len >= 0) {
274 add_char('-', h, env->vpos, HDRAW_MODE);
275 len -= font::hor;
276 h += font::hor;
281 void tty_printer::end_page()
283 for (int last_line = lines_per_page; last_line > 0; last_line--)
284 if (lines[last_line - 1])
285 break;
287 for (int i = 0; i < last_line; i++) {
288 glyph *p = lines[i];
289 lines[i] = 0;
290 glyph *g = 0;
291 while (p) {
292 glyph *tem = p->next;
293 p->next = g;
294 g = p;
295 p = tem;
297 int hpos = 0;
299 glyph *nextp;
300 for (p = g; p; delete p, p = nextp) {
301 nextp = p->next;
302 if (nextp && p->hpos == nextp->hpos) {
303 if (p->draw_mode() == HDRAW_MODE && nextp->draw_mode() == VDRAW_MODE) {
304 nextp->code = '+';
305 continue;
307 if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
308 nextp->code = p->code;
309 continue;
311 if (!overstrike_flag)
312 continue;
314 if (hpos > p->hpos) {
315 putchar('\b');
316 hpos--;
318 else {
319 if (horizontal_tab_flag) {
320 for (;;) {
321 int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
322 if (next_tab_pos > p->hpos)
323 break;
324 putchar('\t');
325 hpos = next_tab_pos;
328 for (; hpos < p->hpos; hpos++)
329 putchar(' ');
331 assert(hpos == p->hpos);
332 if (p->mode & UNDERLINE_MODE) {
333 putchar('_');
334 putchar('\b');
336 if (p->mode & BOLD_MODE) {
337 putchar(p->code);
338 putchar('\b');
340 putchar(p->code);
341 hpos++;
343 putchar('\n');
345 if (form_feed_flag) {
346 if (last_line < lines_per_page)
347 putchar('\f');
349 else {
350 for (; last_line < lines_per_page; last_line++)
351 putchar('\n');
355 font *tty_printer::make_font(const char *nm)
357 return tty_font::load_tty_font(nm);
360 printer *make_printer()
362 return new tty_printer;
365 static void usage();
367 int main(int argc, char **argv)
369 program_name = argv[0];
370 static char stderr_buf[BUFSIZ];
371 setbuf(stderr, stderr_buf);
372 int c;
373 while ((c = getopt(argc, argv, "F:vhfbuoBUd")) != EOF)
374 switch(c) {
375 case 'v':
377 extern const char *version_string;
378 fprintf(stderr, "grotty version %s\n", version_string);
379 fflush(stderr);
380 break;
382 case 'b':
383 // Do not embolden by overstriking.
384 bold_flag = 0;
385 break;
386 case 'u':
387 // Do not underline.
388 underline_flag = 0;
389 break;
390 case 'o':
391 // Do not overstrike (other than emboldening and underlining).
392 overstrike_flag = 0;
393 break;
394 case 'B':
395 // Do bold-underlining as bold.
396 bold_underline_mode = BOLD_MODE;
397 break;
398 case 'U':
399 // Do bold-underlining as underlining.
400 bold_underline_mode = UNDERLINE_MODE;
401 break;
402 case 'h':
403 // Use horizontal tabs.
404 horizontal_tab_flag = 1;
405 break;
406 case 'f':
407 form_feed_flag = 1;
408 break;
409 case 'F':
410 font::command_line_font_dir(optarg);
411 break;
412 case 'd':
413 // Ignore \D commands.
414 draw_flag = 0;
415 break;
416 case '?':
417 usage();
418 break;
419 default:
420 assert(0);
422 if (optind >= argc)
423 do_file("-");
424 else {
425 for (int i = optind; i < argc; i++)
426 do_file(argv[i]);
428 delete pr;
429 exit(0);
432 static void usage()
434 fprintf(stderr, "usage: %s [-hfvbuodBU] [-F dir] [files ...]\n",
435 program_name);
436 exit(1);