]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/groff/grotty/tty.cc
Virgin import of FSF groff v1.10
[FreeBSD/FreeBSD.git] / contrib / groff / grotty / tty.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
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 2, 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 COPYING.  If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #include "driver.h"
22
23 #ifndef SHRT_MIN
24 #define SHRT_MIN (-32768)
25 #endif
26
27 #ifndef SHRT_MAX
28 #define SHRT_MAX 32767
29 #endif
30
31 #define TAB_WIDTH 8
32
33 static int horizontal_tab_flag = 0;
34 static int form_feed_flag = 0;
35 static int bold_flag = 1;
36 static int underline_flag = 1;
37 static int overstrike_flag = 1;
38 static int draw_flag = 1;
39
40 enum {
41   UNDERLINE_MODE = 01,
42   BOLD_MODE = 02,
43   VDRAW_MODE = 04,
44   HDRAW_MODE = 010
45 };
46
47 // Mode to use for bold-underlining.
48 static unsigned char bold_underline_mode = BOLD_MODE|UNDERLINE_MODE;
49
50 class tty_font : public font {
51   tty_font(const char *);
52   unsigned char mode;
53 public:
54   ~tty_font();
55   unsigned char get_mode() { return mode; }
56 #if 0
57   void handle_x_command(int argc, const char **argv);
58 #endif
59   static tty_font *load_tty_font(const char *);
60 };
61
62 tty_font *tty_font::load_tty_font(const char *s)
63 {
64   tty_font *f = new tty_font(s);
65   if (!f->load()) {
66     delete f;
67     return 0;
68   }
69   const char *num = f->get_internal_name();
70   long n;
71   if (num != 0 && (n = strtol(num, 0, 0)) != 0)
72     f->mode = int(n & (BOLD_MODE|UNDERLINE_MODE));
73   if (!underline_flag)
74     f->mode &= ~UNDERLINE_MODE;
75   if (!bold_flag)
76     f->mode &= ~BOLD_MODE;
77   if ((f->mode & (BOLD_MODE|UNDERLINE_MODE)) == (BOLD_MODE|UNDERLINE_MODE))
78     f->mode = (f->mode & ~(BOLD_MODE|UNDERLINE_MODE)) | bold_underline_mode;
79   return f;
80 }
81
82 tty_font::tty_font(const char *nm)
83 : font(nm), mode(0)
84 {
85 }
86
87 tty_font::~tty_font()
88 {
89 }
90
91 #if 0
92 void tty_font::handle_x_command(int argc, const char **argv)
93 {
94   if (argc >= 1 && strcmp(argv[0], "bold") == 0)
95     mode |= BOLD_MODE;
96   else if (argc >= 1 && strcmp(argv[0], "underline") == 0)
97     mode |= UNDERLINE_MODE;
98 }
99 #endif
100
101 class glyph {
102   static glyph *free_list;
103 public:
104   glyph *next;
105   short hpos;
106   unsigned char code;
107   unsigned char mode;
108   void *operator new(size_t);
109   void operator delete(void *);
110   inline int draw_mode() { return mode & (VDRAW_MODE|HDRAW_MODE); }
111 };
112
113 glyph *glyph::free_list = 0;
114
115 void *glyph::operator new(size_t)
116 {
117   if (!free_list) {
118     const int BLOCK = 1024;
119     free_list = (glyph *)new char[sizeof(glyph)*BLOCK];
120     for (int i = 0; i < BLOCK - 1; i++)
121       free_list[i].next = free_list + i + 1;
122     free_list[BLOCK - 1].next = 0;
123   }
124   glyph *p = free_list;
125   free_list = free_list->next;
126   p->next = 0;
127   return p;
128 }
129
130 void glyph::operator delete(void *p)
131 {
132   if (p) {
133     ((glyph *)p)->next = free_list;
134     free_list = (glyph *)p;
135   }
136 }
137
138 class tty_printer : public printer {
139   glyph **lines;
140   int nlines;
141   int cached_v;
142   int cached_vpos;
143   void add_char(unsigned char, int, int, unsigned char);
144 public:
145   tty_printer();
146   ~tty_printer();
147   void set_char(int, font *, const environment *, int);
148   void draw(int code, int *p, int np, const environment *env);
149   void begin_page(int) { }
150   void end_page(int page_length);
151   font *make_font(const char *);
152 };
153
154 tty_printer::tty_printer() : cached_v(0)
155 {
156   nlines = 66;
157   lines = new glyph *[nlines];
158   for (int i = 0; i < nlines; i++)
159     lines[i] = 0;
160 }
161
162 tty_printer::~tty_printer()
163 {
164   a_delete lines;
165 }
166
167 void tty_printer::set_char(int i, font *f, const environment *env, int w)
168 {
169   if (w != font::hor)
170     fatal("width of character not equal to horizontal resolution");
171   add_char(f->get_code(i), env->hpos, env->vpos, ((tty_font *)f)->get_mode());
172 }
173
174 void tty_printer::add_char(unsigned char c, int h, int v, unsigned char mode)
175 {
176 #if 0
177   // This is too expensive.
178   if (h % font::hor != 0)
179     fatal("horizontal position not a multiple of horizontal resolution");
180 #endif
181   int hpos = h / font::hor;
182   if (hpos < SHRT_MIN || hpos > SHRT_MAX) {
183     error("character with ridiculous horizontal position discarded");
184     return;
185   }
186   int vpos;
187   if (v == cached_v && cached_v != 0)
188     vpos = cached_vpos;
189   else {
190     if (v % font::vert != 0)
191       fatal("vertical position not a multiple of vertical resolution");
192     vpos = v / font::vert;
193     if (vpos > nlines) {
194       glyph **old_lines = lines;
195       lines = new glyph *[vpos + 1];
196       memcpy(lines, old_lines, nlines*sizeof(glyph *));
197       for (int i = nlines; i <= vpos; i++)
198         lines[i] = 0;
199       a_delete old_lines;
200       nlines = vpos + 1;
201     }
202     // Note that the first output line corresponds to groff
203     // position font::vert.
204     if (vpos <= 0) {
205       error("character above first line discarded");
206       return;
207     }
208     cached_v = v;
209     cached_vpos = vpos;
210   }
211   glyph *g = new glyph;
212   g->hpos = hpos;
213   g->code = c;
214   g->mode = mode;
215
216   // The list will be reversed later.  After reversal, it must be in
217   // increasing order of hpos, with HDRAW characters before VDRAW
218   // characters before normal characters at each hpos, and otherwise
219   // in order of occurrence.
220
221   glyph **pp;
222   for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
223     if ((*pp)->hpos < hpos
224         || ((*pp)->hpos == hpos && (*pp)->draw_mode() >= g->draw_mode()))
225       break;
226
227   g->next = *pp;
228   *pp = g;
229 }
230
231 void tty_printer::draw(int code, int *p, int np, const environment *env)
232 {
233   if (code != 'l' || !draw_flag)
234     return;
235   if (np != 2) {
236     error("2 arguments required for line");
237     return;
238   }
239   if (p[0] == 0) {
240     // vertical line
241     int v = env->vpos;
242     int len = p[1];
243     if (len < 0) {
244       v += len;
245       len = -len;
246     }
247     while (len >= 0) {
248       add_char('|', env->hpos, v, VDRAW_MODE);
249       len -= font::vert;
250       v += font::vert;
251     }
252   }
253   if (p[1] == 0) {
254     // horizontal line
255     int h = env->hpos;
256     int len = p[0];
257     if (len < 0) {
258       h += len;
259       len = -len;
260     }
261     while (len >= 0) {
262       add_char('-', h, env->vpos, HDRAW_MODE);
263       len -= font::hor;
264       h += font::hor;
265     }
266   }
267 }
268
269 void tty_printer::end_page(int page_length)
270 {
271   if (page_length % font::vert != 0)
272     error("vertical position at end of page not multiple of vertical resolution");
273   int lines_per_page = page_length / font::vert;
274   int last_line;
275   for (last_line = nlines; last_line > 0; last_line--)
276     if (lines[last_line - 1])
277       break;
278 #if 0
279   if (last_line > lines_per_page) {
280     error("characters past last line discarded");
281     do {
282       --last_line;
283       while (lines[last_line]) {
284         glyph *tem = lines[last_line];
285         lines[last_line] = tem->next;
286         delete tem;
287       }
288     } while (last_line > lines_per_page);
289   }
290 #endif
291   for (int i = 0; i < last_line; i++) {
292     glyph *p = lines[i];
293     lines[i] = 0;
294     glyph *g = 0;
295     while (p) {
296       glyph *tem = p->next;
297       p->next = g;
298       g = p;
299       p = tem;
300     }
301     int hpos = 0;
302     
303     glyph *nextp;
304     for (p = g; p; delete p, p = nextp) {
305       nextp = p->next;
306       if (nextp && p->hpos == nextp->hpos) {
307         if (p->draw_mode() == HDRAW_MODE && nextp->draw_mode() == VDRAW_MODE) {
308           nextp->code = '+';
309           continue;
310         }
311         if (p->draw_mode() != 0 && p->draw_mode() == nextp->draw_mode()) {
312           nextp->code = p->code;
313           continue;
314         }
315         if (!overstrike_flag)
316           continue;
317       }
318       if (hpos > p->hpos) {
319         do {
320           putchar('\b');
321           hpos--;
322         } while (hpos > p->hpos);
323       }
324       else {
325         if (horizontal_tab_flag) {
326           for (;;) {
327             int next_tab_pos = ((hpos + TAB_WIDTH) / TAB_WIDTH) * TAB_WIDTH;
328             if (next_tab_pos > p->hpos)
329               break;
330             putchar('\t');
331             hpos = next_tab_pos;
332           }
333         }
334         for (; hpos < p->hpos; hpos++)
335           putchar(' ');
336       }
337       assert(hpos == p->hpos);
338       if (p->mode & UNDERLINE_MODE) {
339         putchar('_');
340         putchar('\b');
341       }
342       if (p->mode & BOLD_MODE) {
343         putchar(p->code);
344         putchar('\b');
345       }
346       putchar(p->code);
347       hpos++;
348     }
349     putchar('\n');
350   }
351   if (form_feed_flag) {
352     if (last_line < lines_per_page)
353       putchar('\f');
354   }
355   else {
356     for (; last_line < lines_per_page; last_line++)
357       putchar('\n');
358   }
359 }
360
361 font *tty_printer::make_font(const char *nm)
362 {
363   return tty_font::load_tty_font(nm);
364 }
365
366 printer *make_printer()
367 {
368   return new tty_printer;
369 }
370
371 static void usage();
372
373 int main(int argc, char **argv)
374 {
375   program_name = argv[0];
376   static char stderr_buf[BUFSIZ];
377   setbuf(stderr, stderr_buf);
378   int c;
379   while ((c = getopt(argc, argv, "F:vhfbuoBUd")) != EOF)
380     switch(c) {
381     case 'v':
382       {
383         extern const char *version_string;
384         fprintf(stderr, "grotty version %s\n", version_string);
385         fflush(stderr);
386         break;
387       }
388     case 'b':
389       // Do not embolden by overstriking.
390       bold_flag = 0;
391       break;
392     case 'u':
393       // Do not underline.
394       underline_flag = 0;
395       break;
396     case 'o':
397       // Do not overstrike (other than emboldening and underlining).
398       overstrike_flag = 0;
399       break;
400     case 'B':
401       // Do bold-underlining as bold.
402       bold_underline_mode = BOLD_MODE;
403       break;
404     case 'U':
405       // Do bold-underlining as underlining.
406       bold_underline_mode = UNDERLINE_MODE;
407       break;
408     case 'h':
409       // Use horizontal tabs.
410       horizontal_tab_flag = 1;
411       break;
412     case 'f':
413       form_feed_flag = 1;
414       break;
415     case 'F':
416       font::command_line_font_dir(optarg);
417       break;
418     case 'd':
419       // Ignore \D commands.
420       draw_flag = 0;
421       break;
422     case '?':
423       usage();
424       break;
425     default:
426       assert(0);
427     }
428   if (optind >= argc)
429     do_file("-");
430   else {
431     for (int i = optind; i < argc; i++)
432       do_file(argv[i]);
433   }
434   delete pr;
435   return 0;
436 }
437
438 static void usage()
439 {
440   fprintf(stderr, "usage: %s [-hfvbuodBU] [-F dir] [files ...]\n",
441           program_name);
442   exit(1);
443 }