Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / roff / troff / node.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22 extern int debug_state;
23
24 #include "troff.h"
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #include "dictionary.h"
31 #include "hvunits.h"
32 #include "stringclass.h"
33 #include "mtsm.h"
34 #include "env.h"
35 #include "request.h"
36 #include "node.h"
37 #include "token.h"
38 #include "div.h"
39 #include "reg.h"
40 #include "charinfo.h"
41 #include "font.h"
42 #include "input.h"
43 #include "geometry.h"
44
45 #include "nonposix.h"
46
47 #ifdef _POSIX_VERSION
48
49 #include <sys/wait.h>
50
51 #else /* not _POSIX_VERSION */
52
53 /* traditional Unix */
54
55 #define WIFEXITED(s) (((s) & 0377) == 0)
56 #define WEXITSTATUS(s) (((s) >> 8) & 0377)
57 #define WTERMSIG(s) ((s) & 0177)
58 #define WIFSTOPPED(s) (((s) & 0377) == 0177)
59 #define WSTOPSIG(s) (((s) >> 8) & 0377)
60 #define WIFSIGNALED(s) (((s) & 0377) != 0 && (((s) & 0377) != 0177))
61
62 #endif /* not _POSIX_VERSION */
63
64 // declarations to avoid friend name injections
65 class tfont;
66 class tfont_spec;
67 tfont *make_tfont(tfont_spec &);
68
69
70 /*
71  *  how many boundaries of images have been written? Useful for
72  *  debugging grohtml
73  */
74
75 int image_no = 0;
76 static int suppress_start_page = 0;
77
78 #define STORE_WIDTH 1
79
80 symbol HYPHEN_SYMBOL("hy");
81
82 // Character used when a hyphen is inserted at a line break.
83 static charinfo *soft_hyphen_char;
84
85 enum constant_space_type {
86   CONSTANT_SPACE_NONE,
87   CONSTANT_SPACE_RELATIVE,
88   CONSTANT_SPACE_ABSOLUTE
89   };
90
91 struct special_font_list {
92   int n;
93   special_font_list *next;
94 };
95
96 special_font_list *global_special_fonts;
97 static int global_ligature_mode = 1;
98 static int global_kern_mode = 1;
99
100 class track_kerning_function {
101   int non_zero;
102   units min_size;
103   hunits min_amount;
104   units max_size;
105   hunits max_amount;
106 public:
107   track_kerning_function();
108   track_kerning_function(units, hunits, units, hunits);
109   int operator==(const track_kerning_function &);
110   int operator!=(const track_kerning_function &);
111   hunits compute(int point_size);
112 };
113
114 // embolden fontno when this is the current font
115
116 struct conditional_bold {
117   conditional_bold *next;
118   int fontno;
119   hunits offset;
120   conditional_bold(int, hunits, conditional_bold * = 0);
121 };
122
123 class font_info {
124   tfont *last_tfont;
125   int number;
126   font_size last_size;
127   int last_height;
128   int last_slant;
129   symbol internal_name;
130   symbol external_name;
131   font *fm;
132   char is_bold;
133   hunits bold_offset;
134   track_kerning_function track_kern;
135   constant_space_type is_constant_spaced;
136   units constant_space;
137   int last_ligature_mode;
138   int last_kern_mode;
139   conditional_bold *cond_bold_list;
140   void flush();
141 public:
142   special_font_list *sf;
143   font_info(symbol, int, symbol, font *);
144   int contains(charinfo *);
145   void set_bold(hunits);
146   void unbold();
147   void set_conditional_bold(int, hunits);
148   void conditional_unbold(int);
149   void set_track_kern(track_kerning_function &);
150   void set_constant_space(constant_space_type, units = 0);
151   int is_named(symbol);
152   symbol get_name();
153   tfont *get_tfont(font_size, int, int, int);
154   hunits get_space_width(font_size, int);
155   hunits get_narrow_space_width(font_size);
156   hunits get_half_narrow_space_width(font_size);
157   int get_bold(hunits *);
158   int is_special();
159   int is_style();
160   friend symbol get_font_name(int, environment *);
161   friend symbol get_style_name(int);
162 };
163
164 class tfont_spec {
165 protected:
166   symbol name;
167   int input_position;
168   font *fm;
169   font_size size;
170   char is_bold;
171   char is_constant_spaced;
172   int ligature_mode;
173   int kern_mode;
174   hunits bold_offset;
175   hunits track_kern;                    // add this to the width
176   hunits constant_space_width;
177   int height;
178   int slant;
179 public:
180   tfont_spec(symbol, int, font *, font_size, int, int);
181   tfont_spec(const tfont_spec &spec) { *this = spec; }
182   tfont_spec plain();
183   int operator==(const tfont_spec &);
184   friend tfont *font_info::get_tfont(font_size fs, int, int, int);
185 };
186
187 class tfont : public tfont_spec {
188   static tfont *tfont_list;
189   tfont *next;
190   tfont *plain_version;
191 public:
192   tfont(tfont_spec &);
193   int contains(charinfo *);
194   hunits get_width(charinfo *c);
195   int get_bold(hunits *);
196   int get_constant_space(hunits *);
197   hunits get_track_kern();
198   tfont *get_plain();
199   font_size get_size();
200   symbol get_name();
201   charinfo *get_lig(charinfo *c1, charinfo *c2);
202   int get_kern(charinfo *c1, charinfo *c2, hunits *res);
203   int get_input_position();
204   int get_character_type(charinfo *);
205   int get_height();
206   int get_slant();
207   vunits get_char_height(charinfo *);
208   vunits get_char_depth(charinfo *);
209   hunits get_char_skew(charinfo *);
210   hunits get_italic_correction(charinfo *);
211   hunits get_left_italic_correction(charinfo *);
212   hunits get_subscript_correction(charinfo *);
213   friend tfont *make_tfont(tfont_spec &);
214 };
215
216 inline int env_definite_font(environment *env)
217 {
218   return env->get_family()->make_definite(env->get_font());
219 }
220
221 /* font_info functions */
222
223 static font_info **font_table = 0;
224 static int font_table_size = 0;
225
226 font_info::font_info(symbol nm, int n, symbol enm, font *f)
227 : last_tfont(0), number(n), last_size(0),
228   internal_name(nm), external_name(enm), fm(f),
229   is_bold(0), is_constant_spaced(CONSTANT_SPACE_NONE), last_ligature_mode(1),
230   last_kern_mode(1), cond_bold_list(0), sf(0)
231 {
232 }
233
234 inline int font_info::contains(charinfo *ci)
235 {
236   return fm != 0 && fm->contains(ci->get_index());
237 }
238
239 inline int font_info::is_special()
240 {
241   return fm != 0 && fm->is_special();
242 }
243
244 inline int font_info::is_style()
245 {
246   return fm == 0;
247 }
248
249 tfont *make_tfont(tfont_spec &spec)
250 {
251   for (tfont *p = tfont::tfont_list; p; p = p->next)
252     if (*p == spec)
253       return p;
254   return new tfont(spec);
255 }
256
257 // this is the current_font, fontno is where we found the character,
258 // presumably a special font
259
260 tfont *font_info::get_tfont(font_size fs, int height, int slant, int fontno)
261 {
262   if (last_tfont == 0 || fs != last_size
263       || height != last_height || slant != last_slant
264       || global_ligature_mode != last_ligature_mode
265       || global_kern_mode != last_kern_mode
266       || fontno != number) {
267         font_info *f = font_table[fontno];
268         tfont_spec spec(f->external_name, f->number, f->fm, fs, height, slant);
269         for (conditional_bold *p = cond_bold_list; p; p = p->next)
270           if (p->fontno == fontno) {
271             spec.is_bold = 1;
272             spec.bold_offset = p->offset;
273             break;
274           }
275         if (!spec.is_bold && is_bold) {
276           spec.is_bold = 1;
277           spec.bold_offset = bold_offset;
278         }
279         spec.track_kern = track_kern.compute(fs.to_scaled_points());
280         spec.ligature_mode = global_ligature_mode;
281         spec.kern_mode = global_kern_mode;
282         switch (is_constant_spaced) {
283         case CONSTANT_SPACE_NONE:
284           break;
285         case CONSTANT_SPACE_ABSOLUTE:
286           spec.is_constant_spaced = 1;
287           spec.constant_space_width = constant_space;
288           break;
289         case CONSTANT_SPACE_RELATIVE:
290           spec.is_constant_spaced = 1;
291           spec.constant_space_width
292             = scale(constant_space*fs.to_scaled_points(),
293                     units_per_inch,
294                     36*72*sizescale);
295           break;
296         default:
297           assert(0);
298         }
299         if (fontno != number)
300           return make_tfont(spec);
301         last_tfont = make_tfont(spec);
302         last_size = fs;
303         last_height = height;
304         last_slant = slant;
305         last_ligature_mode = global_ligature_mode;
306         last_kern_mode = global_kern_mode;
307       }
308   return last_tfont;
309 }
310
311 int font_info::get_bold(hunits *res)
312 {
313   if (is_bold) {
314     *res = bold_offset;
315     return 1;
316   }
317   else
318     return 0;
319 }
320
321 void font_info::unbold()
322 {
323   if (is_bold) {
324     is_bold = 0;
325     flush();
326   }
327 }
328
329 void font_info::set_bold(hunits offset)
330 {
331   if (!is_bold || offset != bold_offset) {
332     is_bold = 1;
333     bold_offset = offset;
334     flush();
335   }
336 }
337
338 void font_info::set_conditional_bold(int fontno, hunits offset)
339 {
340   for (conditional_bold *p = cond_bold_list; p; p = p->next)
341     if (p->fontno == fontno) {
342       if (offset != p->offset) {
343         p->offset = offset;
344         flush();
345       }
346       return;
347     }
348   cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
349 }
350
351 conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
352 : next(x), fontno(f), offset(h)
353 {
354 }
355
356 void font_info::conditional_unbold(int fontno)
357 {
358   for (conditional_bold **p = &cond_bold_list; *p; p = &(*p)->next)
359     if ((*p)->fontno == fontno) {
360       conditional_bold *tem = *p;
361       *p = (*p)->next;
362       delete tem;
363       flush();
364       return;
365     }
366 }
367
368 void font_info::set_constant_space(constant_space_type type, units x)
369 {
370   if (type != is_constant_spaced
371       || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
372     flush();
373     is_constant_spaced = type;
374     constant_space = x;
375   }
376 }
377
378 void font_info::set_track_kern(track_kerning_function &tk)
379 {
380   if (track_kern != tk) {
381     track_kern = tk;
382     flush();
383   }
384 }
385
386 void font_info::flush()
387 {
388   last_tfont = 0;
389 }
390
391 int font_info::is_named(symbol s)
392 {
393   return internal_name == s;
394 }
395
396 symbol font_info::get_name()
397 {
398   return internal_name;
399 }
400
401 symbol get_font_name(int fontno, environment *env)
402 {
403   symbol f = font_table[fontno]->get_name();
404   if (font_table[fontno]->is_style()) {
405     return concat(env->get_family()->nm, f);
406   }
407   return f;
408 }
409
410 symbol get_style_name(int fontno)
411 {
412   if (font_table[fontno]->is_style())
413     return font_table[fontno]->get_name();
414   else
415     return EMPTY_SYMBOL;
416 }
417
418 hunits font_info::get_space_width(font_size fs, int space_sz)
419 {
420   if (is_constant_spaced == CONSTANT_SPACE_NONE)
421     return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
422                         space_sz, 12);
423   else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
424     return constant_space;
425   else
426     return scale(constant_space*fs.to_scaled_points(),
427                  units_per_inch, 36*72*sizescale);
428 }
429
430 hunits font_info::get_narrow_space_width(font_size fs)
431 {
432   charinfo *ci = get_charinfo(symbol("|"));
433   if (fm->contains(ci->get_index()))
434     return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
435   else
436     return hunits(fs.to_units()/6);
437 }
438
439 hunits font_info::get_half_narrow_space_width(font_size fs)
440 {
441   charinfo *ci = get_charinfo(symbol("^"));
442   if (fm->contains(ci->get_index()))
443     return hunits(fm->get_width(ci->get_index(), fs.to_scaled_points()));
444   else
445     return hunits(fs.to_units()/12);
446 }
447
448 /* tfont */
449
450 tfont_spec::tfont_spec(symbol nm, int n, font *f,
451                        font_size s, int h, int sl)
452 : name(nm), input_position(n), fm(f), size(s),
453   is_bold(0), is_constant_spaced(0), ligature_mode(1), kern_mode(1),
454   height(h), slant(sl)
455 {
456   if (height == size.to_scaled_points())
457     height = 0;
458 }
459
460 int tfont_spec::operator==(const tfont_spec &spec)
461 {
462   if (fm == spec.fm
463       && size == spec.size
464       && input_position == spec.input_position
465       && name == spec.name
466       && height == spec.height
467       && slant == spec.slant
468       && (is_bold
469           ? (spec.is_bold && bold_offset == spec.bold_offset)
470           : !spec.is_bold)
471       && track_kern == spec.track_kern
472       && (is_constant_spaced
473           ? (spec.is_constant_spaced
474              && constant_space_width == spec.constant_space_width)
475           : !spec.is_constant_spaced)
476       && ligature_mode == spec.ligature_mode
477       && kern_mode == spec.kern_mode)
478     return 1;
479   else
480     return 0;
481 }
482
483 tfont_spec tfont_spec::plain()
484 {
485   return tfont_spec(name, input_position, fm, size, height, slant);
486 }
487
488 hunits tfont::get_width(charinfo *c)
489 {
490   if (is_constant_spaced)
491     return constant_space_width;
492   else if (is_bold)
493     return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
494             + track_kern + bold_offset);
495   else
496     return (hunits(fm->get_width(c->get_index(), size.to_scaled_points()))
497             + track_kern);
498 }
499
500 vunits tfont::get_char_height(charinfo *c)
501 {
502   vunits v = fm->get_height(c->get_index(), size.to_scaled_points());
503   if (height != 0 && height != size.to_scaled_points())
504     return scale(v, height, size.to_scaled_points());
505   else
506     return v;
507 }
508
509 vunits tfont::get_char_depth(charinfo *c)
510 {
511   vunits v = fm->get_depth(c->get_index(), size.to_scaled_points());
512   if (height != 0 && height != size.to_scaled_points())
513     return scale(v, height, size.to_scaled_points());
514   else
515     return v;
516 }
517
518 hunits tfont::get_char_skew(charinfo *c)
519 {
520   return hunits(fm->get_skew(c->get_index(), size.to_scaled_points(), slant));
521 }
522
523 hunits tfont::get_italic_correction(charinfo *c)
524 {
525   return hunits(fm->get_italic_correction(c->get_index(), size.to_scaled_points()));
526 }
527
528 hunits tfont::get_left_italic_correction(charinfo *c)
529 {
530   return hunits(fm->get_left_italic_correction(c->get_index(),
531                                                size.to_scaled_points()));
532 }
533
534 hunits tfont::get_subscript_correction(charinfo *c)
535 {
536   return hunits(fm->get_subscript_correction(c->get_index(),
537                                              size.to_scaled_points()));
538 }
539
540 inline int tfont::get_input_position()
541 {
542   return input_position;
543 }
544
545 inline int tfont::contains(charinfo *ci)
546 {
547   return fm->contains(ci->get_index());
548 }
549
550 inline int tfont::get_character_type(charinfo *ci)
551 {
552   return fm->get_character_type(ci->get_index());
553 }
554
555 inline int tfont::get_bold(hunits *res)
556 {
557   if (is_bold) {
558     *res = bold_offset;
559     return 1;
560   }
561   else
562     return 0;
563 }
564
565 inline int tfont::get_constant_space(hunits *res)
566 {
567   if (is_constant_spaced) {
568     *res = constant_space_width;
569     return 1;
570   }
571   else
572     return 0;
573 }
574
575 inline hunits tfont::get_track_kern()
576 {
577   return track_kern;
578 }
579
580 inline tfont *tfont::get_plain()
581 {
582   return plain_version;
583 }
584
585 inline font_size tfont::get_size()
586 {
587   return size;
588 }
589
590 inline symbol tfont::get_name()
591 {
592   return name;
593 }
594
595 inline int tfont::get_height()
596 {
597   return height;
598 }
599
600 inline int tfont::get_slant()
601 {
602   return slant;
603 }
604
605 symbol SYMBOL_ff("ff");
606 symbol SYMBOL_fi("fi");
607 symbol SYMBOL_fl("fl");
608 symbol SYMBOL_Fi("Fi");
609 symbol SYMBOL_Fl("Fl");
610
611 charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
612 {
613   if (ligature_mode == 0)
614     return 0;
615   charinfo *ci = 0;
616   if (c1->get_ascii_code() == 'f') {
617     switch (c2->get_ascii_code()) {
618     case 'f':
619       if (fm->has_ligature(font::LIG_ff))
620         ci = get_charinfo(SYMBOL_ff);
621       break;
622     case 'i':
623       if (fm->has_ligature(font::LIG_fi))
624         ci = get_charinfo(SYMBOL_fi);
625       break;
626     case 'l':
627       if (fm->has_ligature(font::LIG_fl))
628         ci = get_charinfo(SYMBOL_fl);
629       break;
630     }
631   }
632   else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
633     switch (c2->get_ascii_code()) {
634     case 'i':
635       if (fm->has_ligature(font::LIG_ffi))
636         ci = get_charinfo(SYMBOL_Fi);
637       break;
638     case 'l':
639       if (fm->has_ligature(font::LIG_ffl))
640         ci = get_charinfo(SYMBOL_Fl);
641       break;
642     }
643   }
644   if (ci != 0 && fm->contains(ci->get_index()))
645     return ci;
646   return 0;
647 }
648
649 inline int tfont::get_kern(charinfo *c1, charinfo *c2, hunits *res)
650 {
651   if (kern_mode == 0)
652     return 0;
653   else {
654     int n = fm->get_kern(c1->get_index(),
655                          c2->get_index(),
656                          size.to_scaled_points());
657     if (n) {
658       *res = hunits(n);
659       return 1;
660     }
661     else
662       return 0;
663   }
664 }
665
666 tfont *tfont::tfont_list = 0;
667
668 tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
669 {
670   next = tfont_list;
671   tfont_list = this;
672   tfont_spec plain_spec = plain();
673   tfont *p;
674   for (p = tfont_list; p; p = p->next)
675     if (*p == plain_spec) {
676       plain_version = p;
677       break;
678     }
679   if (!p)
680     plain_version = new tfont(plain_spec);
681 }
682
683 /* output_file */
684
685 class real_output_file : public output_file {
686 #ifndef POPEN_MISSING
687   int piped;
688 #endif
689   int printing;        // decision via optional page list
690   int output_on;       // \O[0] or \O[1] escape calls
691   virtual void really_transparent_char(unsigned char) = 0;
692   virtual void really_print_line(hunits x, vunits y, node *n,
693                                  vunits before, vunits after, hunits width) = 0;
694   virtual void really_begin_page(int pageno, vunits page_length) = 0;
695   virtual void really_copy_file(hunits x, vunits y, const char *filename);
696   virtual void really_put_filename(const char *filename);
697   virtual void really_on();
698   virtual void really_off();
699 public:
700   FILE *fp;
701   real_output_file();
702   ~real_output_file();
703   void flush();
704   void transparent_char(unsigned char);
705   void print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
706   void begin_page(int pageno, vunits page_length);
707   void put_filename(const char *filename);
708   void on();
709   void off();
710   int is_on();
711   int is_printing();
712   void copy_file(hunits x, vunits y, const char *filename);
713 };
714
715 class suppress_output_file : public real_output_file {
716 public:
717   suppress_output_file();
718   void really_transparent_char(unsigned char);
719   void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
720   void really_begin_page(int pageno, vunits page_length);
721 };
722
723 class ascii_output_file : public real_output_file {
724 public:
725   ascii_output_file();
726   void really_transparent_char(unsigned char);
727   void really_print_line(hunits x, vunits y, node *n, vunits, vunits, hunits width);
728   void really_begin_page(int pageno, vunits page_length);
729   void outc(unsigned char c);
730   void outs(const char *s);
731 };
732
733 void ascii_output_file::outc(unsigned char c)
734 {
735   fputc(c, fp);
736 }
737
738 void ascii_output_file::outs(const char *s)
739 {
740   fputc('<', fp);
741   if (s)
742     fputs(s, fp);
743   fputc('>', fp);
744 }
745
746 struct hvpair;
747
748 class troff_output_file : public real_output_file {
749   units hpos;
750   units vpos;
751   units output_vpos;
752   units output_hpos;
753   int force_motion;
754   int current_size;
755   int current_slant;
756   int current_height;
757   tfont *current_tfont;
758   color *current_fill_color;
759   color *current_glyph_color;
760   int current_font_number;
761   symbol *font_position;
762   int nfont_positions;
763   enum { TBUF_SIZE = 256 };
764   char tbuf[TBUF_SIZE];
765   int tbuf_len;
766   int tbuf_kern;
767   int begun_page;
768   int cur_div_level;
769   string tag_list;
770   void do_motion();
771   void put(char c);
772   void put(unsigned char c);
773   void put(int i);
774   void put(unsigned int i);
775   void put(const char *s);
776   void set_font(tfont *tf);
777   void flush_tbuf();
778 public:
779   troff_output_file();
780   ~troff_output_file();
781   void trailer(vunits page_length);
782   void put_char(charinfo *, tfont *, color *, color *);
783   void put_char_width(charinfo *, tfont *, color *, color *, hunits, hunits);
784   void right(hunits);
785   void down(vunits);
786   void moveto(hunits, vunits);
787   void start_special(tfont *, color *, color *, int = 0);
788   void start_special();
789   void special_char(unsigned char c);
790   void end_special();
791   void word_marker();
792   void really_transparent_char(unsigned char c);
793   void really_print_line(hunits x, vunits y, node *n, vunits before, vunits after, hunits width);
794   void really_begin_page(int pageno, vunits page_length);
795   void really_copy_file(hunits x, vunits y, const char *filename);
796   void really_put_filename(const char *filename);
797   void really_on();
798   void really_off();
799   void draw(char, hvpair *, int, font_size, color *, color *);
800   void determine_line_limits (char code, hvpair *point, int npoints);
801   void check_charinfo(tfont *tf, charinfo *ci);
802   void glyph_color(color *c);
803   void fill_color(color *c);
804   int get_hpos() { return hpos; }
805   int get_vpos() { return vpos; }
806   void add_to_tag_list(string s);
807   friend void space_char_hmotion_node::tprint(troff_output_file *);
808   friend void unbreakable_space_node::tprint(troff_output_file *);
809 };
810
811 static void put_string(const char *s, FILE *fp)
812 {
813   for (; *s != '\0'; ++s)
814     putc(*s, fp);
815 }
816
817 inline void troff_output_file::put(char c)
818 {
819   putc(c, fp);
820 }
821
822 inline void troff_output_file::put(unsigned char c)
823 {
824   putc(c, fp);
825 }
826
827 inline void troff_output_file::put(const char *s)
828 {
829   put_string(s, fp);
830 }
831
832 inline void troff_output_file::put(int i)
833 {
834   put_string(i_to_a(i), fp);
835 }
836
837 inline void troff_output_file::put(unsigned int i)
838 {
839   put_string(ui_to_a(i), fp);
840 }
841
842 void troff_output_file::start_special(tfont *tf, color *gcol, color *fcol,
843                                       int no_init_string)
844 {
845   set_font(tf);
846   glyph_color(gcol);
847   fill_color(fcol);
848   flush_tbuf();
849   do_motion();
850   if (!no_init_string)
851     put("x X ");
852 }
853
854 void troff_output_file::start_special()
855 {
856   flush_tbuf();
857   do_motion();
858   put("x X ");
859 }
860
861 void troff_output_file::special_char(unsigned char c)
862 {
863   put(c);
864   if (c == '\n')
865     put('+');
866 }
867
868 void troff_output_file::end_special()
869 {
870   put('\n');
871 }
872
873 inline void troff_output_file::moveto(hunits h, vunits v)
874 {
875   hpos = h.to_units();
876   vpos = v.to_units();
877 }
878
879 void troff_output_file::really_print_line(hunits x, vunits y, node *n,
880                                           vunits before, vunits after, hunits)
881 {
882   moveto(x, y);
883   while (n != 0) {
884     // Check whether we should push the current troff state and use
885     // the state at the start of the invocation of this diversion.
886     if (n->div_nest_level > cur_div_level && n->push_state) {
887       state.push_state(n->push_state);
888       cur_div_level = n->div_nest_level;
889     }
890     // Has the current diversion level decreased?  Then we must pop the
891     // troff state.
892     while (n->div_nest_level < cur_div_level) {
893       state.pop_state();
894       cur_div_level = n->div_nest_level;
895     }
896     // Now check whether the state has changed.
897     if ((is_on() || n->force_tprint())
898         && (state.changed(n->state) || n->is_tag() || n->is_special)) {
899       flush_tbuf();
900       do_motion();
901       force_motion = 1;
902       flush();
903       state.flush(fp, n->state, tag_list);
904       tag_list = string("");
905       flush();
906     }
907     n->tprint(this);
908     n = n->next;
909   }
910   flush_tbuf();
911   // This ensures that transparent throughput will have a more predictable
912   // position.
913   do_motion();
914   force_motion = 1;
915   hpos = 0;
916   put('n');
917   put(before.to_units());
918   put(' ');
919   put(after.to_units());
920   put('\n');
921 }
922
923 inline void troff_output_file::word_marker()
924 {
925   flush_tbuf();
926   if (is_on())
927     put('w');
928 }
929
930 inline void troff_output_file::right(hunits n)
931 {
932   hpos += n.to_units();
933 }
934
935 inline void troff_output_file::down(vunits n)
936 {
937   vpos += n.to_units();
938 }
939
940 void troff_output_file::do_motion()
941 {
942   if (force_motion) {
943     put('V');
944     put(vpos);
945     put('\n');
946     put('H');
947     put(hpos);
948     put('\n');
949   }
950   else {
951     if (hpos != output_hpos) {
952       units n = hpos - output_hpos;
953       if (n > 0 && n < hpos) {
954         put('h');
955         put(n);
956       }
957       else {
958         put('H');
959         put(hpos);
960       }
961       put('\n');
962     }
963     if (vpos != output_vpos) {
964       units n = vpos - output_vpos;
965       if (n > 0 && n < vpos) {
966         put('v');
967         put(n);
968       }
969       else {
970         put('V');
971         put(vpos);
972       }
973       put('\n');
974     }
975   }
976   output_vpos = vpos;
977   output_hpos = hpos;
978   force_motion = 0;
979 }
980
981 void troff_output_file::flush_tbuf()
982 {
983   if (!is_on()) {
984     tbuf_len = 0;
985     return;
986   }
987
988   if (tbuf_len == 0)
989     return;
990   if (tbuf_kern == 0)
991     put('t');
992   else {
993     put('u');
994     put(tbuf_kern);
995     put(' ');
996   }
997   check_output_limits(hpos, vpos);
998   check_output_limits(hpos, vpos - current_size);
999
1000   for (int i = 0; i < tbuf_len; i++)
1001     put(tbuf[i]);
1002   put('\n');
1003   tbuf_len = 0;
1004 }
1005
1006 void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
1007 {
1008   if (!is_on())
1009     return;
1010
1011   int height = tf->get_char_height(ci).to_units();
1012   int width = tf->get_width(ci).to_units()
1013               + tf->get_italic_correction(ci).to_units();
1014   int depth = tf->get_char_depth(ci).to_units();
1015   check_output_limits(output_hpos, output_vpos - height);
1016   check_output_limits(output_hpos + width, output_vpos + depth);
1017 }
1018
1019 void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
1020                                        color *gcol, color *fcol,
1021                                        hunits w, hunits k)
1022 {
1023   int kk = k.to_units();
1024   if (!is_on()) {
1025     flush_tbuf();
1026     hpos += w.to_units() + kk;
1027     return;
1028   }
1029   set_font(tf);
1030   unsigned char c = ci->get_ascii_code();
1031   if (c == '\0') {
1032     glyph_color(gcol);
1033     fill_color(fcol);
1034     flush_tbuf();
1035     do_motion();
1036     check_charinfo(tf, ci);
1037     if (ci->numbered()) {
1038       put('N');
1039       put(ci->get_number());
1040     }
1041     else {
1042       put('C');
1043       const char *s = ci->nm.contents();
1044       if (s[1] == 0) {
1045         put('\\');
1046         put(s[0]);
1047       }
1048       else
1049         put(s);
1050     }
1051     put('\n');
1052     hpos += w.to_units() + kk;
1053   }
1054   else if (tcommand_flag) {
1055     if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
1056         && (!gcol || gcol == current_glyph_color)
1057         && (!fcol || fcol == current_fill_color)
1058         && kk == tbuf_kern
1059         && tbuf_len < TBUF_SIZE) {
1060       check_charinfo(tf, ci);
1061       tbuf[tbuf_len++] = c;
1062       output_hpos += w.to_units() + kk;
1063       hpos = output_hpos;
1064       return;
1065     }
1066     glyph_color(gcol);
1067     fill_color(fcol);
1068     flush_tbuf();
1069     do_motion();
1070     check_charinfo(tf, ci);
1071     tbuf[tbuf_len++] = c;
1072     output_hpos += w.to_units() + kk;
1073     tbuf_kern = kk;
1074     hpos = output_hpos;
1075   }
1076   else {
1077     // flush_tbuf();
1078     int n = hpos - output_hpos;
1079     check_charinfo(tf, ci);
1080     // check_output_limits(output_hpos, output_vpos);
1081     if (vpos == output_vpos
1082         && (!gcol || gcol == current_glyph_color)
1083         && (!fcol || fcol == current_fill_color)
1084         && n > 0 && n < 100 && !force_motion) {
1085       put(char(n/10 + '0'));
1086       put(char(n%10 + '0'));
1087       put(c);
1088       output_hpos = hpos;
1089     }
1090     else {
1091       glyph_color(gcol);
1092       fill_color(fcol);
1093       do_motion();
1094       put('c');
1095       put(c);
1096     }
1097     hpos += w.to_units() + kk;
1098   }
1099 }
1100
1101 void troff_output_file::put_char(charinfo *ci, tfont *tf,
1102                                  color *gcol, color *fcol)
1103 {
1104   flush_tbuf();
1105   if (!is_on())
1106     return;
1107   set_font(tf);
1108   unsigned char c = ci->get_ascii_code();
1109   if (c == '\0') {
1110     glyph_color(gcol);
1111     fill_color(fcol);
1112     flush_tbuf();
1113     do_motion();
1114     if (ci->numbered()) {
1115       put('N');
1116       put(ci->get_number());
1117     }
1118     else {
1119       put('C');
1120       const char *s = ci->nm.contents();
1121       if (s[1] == 0) {
1122         put('\\');
1123         put(s[0]);
1124       }
1125       else
1126         put(s);
1127     }
1128     put('\n');
1129   }
1130   else {
1131     int n = hpos - output_hpos;
1132     if (vpos == output_vpos
1133         && (!gcol || gcol == current_glyph_color)
1134         && (!fcol || fcol == current_fill_color)
1135         && n > 0 && n < 100) {
1136       put(char(n/10 + '0'));
1137       put(char(n%10 + '0'));
1138       put(c);
1139       output_hpos = hpos;
1140     }
1141     else {
1142       glyph_color(gcol);
1143       fill_color(fcol);
1144       flush_tbuf();
1145       do_motion();
1146       put('c');
1147       put(c);
1148     }
1149   }
1150 }
1151
1152 // set_font calls `flush_tbuf' if necessary.
1153
1154 void troff_output_file::set_font(tfont *tf)
1155 {
1156   if (current_tfont == tf)
1157     return;
1158   flush_tbuf();
1159   int n = tf->get_input_position();
1160   symbol nm = tf->get_name();
1161   if (n >= nfont_positions || font_position[n] != nm) {
1162     put("x font ");
1163     put(n);
1164     put(' ');
1165     put(nm.contents());
1166     put('\n');
1167     if (n >= nfont_positions) {
1168       int old_nfont_positions = nfont_positions;
1169       symbol *old_font_position = font_position;
1170       nfont_positions *= 3;
1171       nfont_positions /= 2;
1172       if (nfont_positions <= n)
1173         nfont_positions = n + 10;
1174       font_position = new symbol[nfont_positions];
1175       memcpy(font_position, old_font_position,
1176              old_nfont_positions*sizeof(symbol));
1177       a_delete old_font_position;
1178     }
1179     font_position[n] = nm;
1180   }
1181   if (current_font_number != n) {
1182     put('f');
1183     put(n);
1184     put('\n');
1185     current_font_number = n;
1186   }
1187   int size = tf->get_size().to_scaled_points();
1188   if (current_size != size) {
1189     put('s');
1190     put(size);
1191     put('\n');
1192     current_size = size;
1193   }
1194   int slant = tf->get_slant();
1195   if (current_slant != slant) {
1196     put("x Slant ");
1197     put(slant);
1198     put('\n');
1199     current_slant = slant;
1200   }
1201   int height = tf->get_height();
1202   if (current_height != height) {
1203     put("x Height ");
1204     put(height == 0 ? current_size : height);
1205     put('\n');
1206     current_height = height;
1207   }
1208   current_tfont = tf;
1209 }
1210
1211 // fill_color calls `flush_tbuf' and `do_motion' if necessary.
1212
1213 void troff_output_file::fill_color(color *col)
1214 {
1215   if (!col || current_fill_color == col)
1216     return;
1217   current_fill_color = col;
1218   if (!color_flag)
1219     return;
1220   flush_tbuf();
1221   do_motion();
1222   put("DF");
1223   unsigned int components[4];
1224   color_scheme cs;
1225   cs = col->get_components(components);
1226   switch (cs) {
1227   case DEFAULT:
1228     put('d');
1229     break;
1230   case RGB:
1231     put("r ");
1232     put(Red);
1233     put(' ');
1234     put(Green);
1235     put(' ');
1236     put(Blue);
1237     break;
1238   case CMY:
1239     put("c ");
1240     put(Cyan);
1241     put(' ');
1242     put(Magenta);
1243     put(' ');
1244     put(Yellow);
1245     break;
1246   case CMYK:
1247     put("k ");
1248     put(Cyan);
1249     put(' ');
1250     put(Magenta);
1251     put(' ');
1252     put(Yellow);
1253     put(' ');
1254     put(Black);
1255     break;
1256   case GRAY:
1257     put("g ");
1258     put(Gray);
1259     break;
1260   }
1261   put('\n');
1262 }
1263
1264 // glyph_color calls `flush_tbuf' and `do_motion' if necessary.
1265
1266 void troff_output_file::glyph_color(color *col)
1267 {
1268   if (!col || current_glyph_color == col)
1269     return;
1270   current_glyph_color = col;
1271   if (!color_flag)
1272     return;
1273   flush_tbuf();
1274   // grotty doesn't like a color command if the vertical position is zero.
1275   do_motion();
1276   put("m");
1277   unsigned int components[4];
1278   color_scheme cs;
1279   cs = col->get_components(components);
1280   switch (cs) {
1281   case DEFAULT:
1282     put('d');
1283     break;
1284   case RGB:
1285     put("r ");
1286     put(Red);
1287     put(' ');
1288     put(Green);
1289     put(' ');
1290     put(Blue);
1291     break;
1292   case CMY:
1293     put("c ");
1294     put(Cyan);
1295     put(' ');
1296     put(Magenta);
1297     put(' ');
1298     put(Yellow);
1299     break;
1300   case CMYK:
1301     put("k ");
1302     put(Cyan);
1303     put(' ');
1304     put(Magenta);
1305     put(' ');
1306     put(Yellow);
1307     put(' ');
1308     put(Black);
1309     break;
1310   case GRAY:
1311     put("g ");
1312     put(Gray);
1313     break;
1314   }
1315   put('\n');
1316 }
1317
1318 void troff_output_file::add_to_tag_list(string s)
1319 {
1320   if (tag_list == string(""))
1321     tag_list = s;
1322   else {
1323     tag_list += string("\n");
1324     tag_list += s;
1325   }
1326 }
1327
1328 // determine_line_limits - works out the smallest box which will contain
1329 //                         the entity, code, built from the point array.
1330 void troff_output_file::determine_line_limits(char code, hvpair *point,
1331                                               int npoints)
1332 {
1333   int i, x, y;
1334
1335   if (!is_on())
1336     return;
1337
1338   switch (code) {
1339   case 'c':
1340   case 'C':
1341     // only the h field is used when defining a circle
1342     check_output_limits(output_hpos,
1343                         output_vpos - point[0].h.to_units()/2);
1344     check_output_limits(output_hpos + point[0].h.to_units(),
1345                         output_vpos + point[0].h.to_units()/2);
1346     break;
1347   case 'E':
1348   case 'e':
1349     check_output_limits(output_hpos,
1350                         output_vpos - point[0].v.to_units()/2);
1351     check_output_limits(output_hpos + point[0].h.to_units(),
1352                         output_vpos + point[0].v.to_units()/2);
1353     break;
1354   case 'P':
1355   case 'p':
1356     x = output_hpos;
1357     y = output_vpos;
1358     check_output_limits(x, y);
1359     for (i = 0; i < npoints; i++) {
1360       x += point[i].h.to_units();
1361       y += point[i].v.to_units();
1362       check_output_limits(x, y);
1363     }
1364     break;
1365   case 't':
1366     x = output_hpos;
1367     y = output_vpos;
1368     for (i = 0; i < npoints; i++) {
1369       x += point[i].h.to_units();
1370       y += point[i].v.to_units();
1371       check_output_limits(x, y);
1372     }
1373     break;
1374   case 'a':
1375     double c[2];
1376     int p[4];
1377     int minx, miny, maxx, maxy;
1378     x = output_hpos;
1379     y = output_vpos;
1380     p[0] = point[0].h.to_units();
1381     p[1] = point[0].v.to_units();
1382     p[2] = point[1].h.to_units();
1383     p[3] = point[1].v.to_units();
1384     if (adjust_arc_center(p, c)) {
1385       check_output_arc_limits(x, y,
1386                               p[0], p[1], p[2], p[3],
1387                               c[0], c[1],
1388                               &minx, &maxx, &miny, &maxy);
1389       check_output_limits(minx, miny);
1390       check_output_limits(maxx, maxy);
1391       break;
1392     }
1393     // fall through
1394   case 'l':
1395     x = output_hpos;
1396     y = output_vpos;
1397     check_output_limits(x, y);
1398     for (i = 0; i < npoints; i++) {
1399       x += point[i].h.to_units();
1400       y += point[i].v.to_units();
1401       check_output_limits(x, y);
1402     }
1403     break;
1404   default:
1405     x = output_hpos;
1406     y = output_vpos;
1407     for (i = 0; i < npoints; i++) {
1408       x += point[i].h.to_units();
1409       y += point[i].v.to_units();
1410       check_output_limits(x, y);
1411     }
1412   }
1413 }
1414
1415 void troff_output_file::draw(char code, hvpair *point, int npoints,
1416                              font_size fsize, color *gcol, color *fcol)
1417 {
1418   int i;
1419   glyph_color(gcol);
1420   fill_color(fcol);
1421   flush_tbuf();
1422   do_motion();
1423   if (is_on()) {
1424     int size = fsize.to_scaled_points();
1425     if (current_size != size) {
1426       put('s');
1427       put(size);
1428       put('\n');
1429       current_size = size;
1430       current_tfont = 0;
1431     }
1432     put('D');
1433     put(code);
1434     if (code == 'c') {
1435       put(' ');
1436       put(point[0].h.to_units());
1437     }
1438     else
1439       for (i = 0; i < npoints; i++) {
1440         put(' ');
1441         put(point[i].h.to_units());
1442         put(' ');
1443         put(point[i].v.to_units());
1444       }
1445     determine_line_limits(code, point, npoints);
1446   }
1447
1448   for (i = 0; i < npoints; i++)
1449     output_hpos += point[i].h.to_units();
1450   hpos = output_hpos;
1451   if (code != 'e') {
1452     for (i = 0; i < npoints; i++)
1453       output_vpos += point[i].v.to_units();
1454     vpos = output_vpos;
1455   }
1456   if (is_on())
1457     put('\n');
1458 }
1459
1460 void troff_output_file::really_on()
1461 {
1462   flush_tbuf();
1463   force_motion = 1;
1464   do_motion();
1465 }
1466
1467 void troff_output_file::really_off()
1468 {
1469   flush_tbuf();
1470 }
1471
1472 void troff_output_file::really_put_filename(const char *filename)
1473 {
1474   flush_tbuf();
1475   put("F ");
1476   put(filename);
1477   put('\n');
1478 }
1479
1480 void troff_output_file::really_begin_page(int pageno, vunits page_length)
1481 {
1482   flush_tbuf();
1483   if (begun_page) {
1484     if (page_length > V0) {
1485       put('V');
1486       put(page_length.to_units());
1487       put('\n');
1488     }
1489   }
1490   else
1491     begun_page = 1;
1492   current_tfont = 0;
1493   current_font_number = -1;
1494   current_size = 0;
1495   // current_height = 0;
1496   // current_slant = 0;
1497   hpos = 0;
1498   vpos = 0;
1499   output_hpos = 0;
1500   output_vpos = 0;
1501   force_motion = 1;
1502   for (int i = 0; i < nfont_positions; i++)
1503     font_position[i] = NULL_SYMBOL;
1504   put('p');
1505   put(pageno);
1506   put('\n');
1507 }
1508
1509 void troff_output_file::really_copy_file(hunits x, vunits y,
1510                                          const char *filename)
1511 {
1512   moveto(x, y);
1513   flush_tbuf();
1514   do_motion();
1515   errno = 0;
1516   FILE *ifp = include_search_path.open_file_cautious(filename);
1517   if (ifp == 0)
1518     error("can't open `%1': %2", filename, strerror(errno));
1519   else {
1520     int c;
1521     while ((c = getc(ifp)) != EOF)
1522       put(char(c));
1523     fclose(ifp);
1524   }
1525   force_motion = 1;
1526   current_size = 0;
1527   current_tfont = 0;
1528   current_font_number = -1;
1529   for (int i = 0; i < nfont_positions; i++)
1530     font_position[i] = NULL_SYMBOL;
1531 }
1532
1533 void troff_output_file::really_transparent_char(unsigned char c)
1534 {
1535   put(c);
1536 }
1537
1538 troff_output_file::~troff_output_file()
1539 {
1540   a_delete font_position;
1541 }
1542
1543 void troff_output_file::trailer(vunits page_length)
1544 {
1545   flush_tbuf();
1546   if (page_length > V0) {
1547     put("x trailer\n");
1548     put('V');
1549     put(page_length.to_units());
1550     put('\n');
1551   }
1552   put("x stop\n");
1553 }
1554
1555 troff_output_file::troff_output_file()
1556 : current_slant(0), current_height(0), current_fill_color(0),
1557   current_glyph_color(0), nfont_positions(10), tbuf_len(0), begun_page(0),
1558   cur_div_level(0)
1559 {
1560   font_position = new symbol[nfont_positions];
1561   put("x T ");
1562   put(device);
1563   put('\n');
1564   put("x res ");
1565   put(units_per_inch);
1566   put(' ');
1567   put(hresolution);
1568   put(' ');
1569   put(vresolution);
1570   put('\n');
1571   put("x init\n");
1572 }
1573
1574 /* output_file */
1575
1576 output_file *the_output = 0;
1577
1578 output_file::output_file()
1579 {
1580 }
1581
1582 output_file::~output_file()
1583 {
1584 }
1585
1586 void output_file::trailer(vunits)
1587 {
1588 }
1589
1590 void output_file::put_filename(const char *)
1591 {
1592 }
1593
1594 void output_file::on()
1595 {
1596 }
1597
1598 void output_file::off()
1599 {
1600 }
1601
1602 real_output_file::real_output_file()
1603 : printing(0), output_on(1)
1604 {
1605 #ifndef POPEN_MISSING
1606   if (pipe_command) {
1607     if ((fp = popen(pipe_command, POPEN_WT)) != 0) {
1608       piped = 1;
1609       return;
1610     }
1611     error("pipe open failed: %1", strerror(errno));
1612   }
1613   piped = 0;
1614 #endif /* not POPEN_MISSING */
1615   fp = stdout;
1616 }
1617
1618 real_output_file::~real_output_file()
1619 {
1620   if (!fp)
1621     return;
1622   // To avoid looping, set fp to 0 before calling fatal().
1623   if (ferror(fp) || fflush(fp) < 0) {
1624     fp = 0;
1625     fatal("error writing output file");
1626   }
1627 #ifndef POPEN_MISSING
1628   if (piped) {
1629     int result = pclose(fp);
1630     fp = 0;
1631     if (result < 0)
1632       fatal("pclose failed");
1633     if (!WIFEXITED(result))
1634       error("output process `%1' got fatal signal %2",
1635             pipe_command,
1636             WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
1637     else {
1638       int exit_status = WEXITSTATUS(result);
1639       if (exit_status != 0)
1640         error("output process `%1' exited with status %2",
1641               pipe_command, exit_status);
1642     }
1643   }
1644   else
1645 #endif /* not POPEN MISSING */
1646   if (fclose(fp) < 0) {
1647     fp = 0;
1648     fatal("error closing output file");
1649   }
1650 }
1651
1652 void real_output_file::flush()
1653 {
1654   if (fflush(fp) < 0)
1655     fatal("error writing output file");
1656 }
1657
1658 int real_output_file::is_printing()
1659 {
1660   return printing;
1661 }
1662
1663 void real_output_file::begin_page(int pageno, vunits page_length)
1664 {
1665   printing = in_output_page_list(pageno);
1666   if (printing)
1667     really_begin_page(pageno, page_length);
1668 }
1669
1670 void real_output_file::copy_file(hunits x, vunits y, const char *filename)
1671 {
1672   if (printing && output_on)
1673     really_copy_file(x, y, filename);
1674   check_output_limits(x.to_units(), y.to_units());
1675 }
1676
1677 void real_output_file::transparent_char(unsigned char c)
1678 {
1679   if (printing && output_on)
1680     really_transparent_char(c);
1681 }
1682
1683 void real_output_file::print_line(hunits x, vunits y, node *n,
1684                              vunits before, vunits after, hunits width)
1685 {
1686   if (printing)
1687     really_print_line(x, y, n, before, after, width);
1688   delete_node_list(n);
1689 }
1690
1691 void real_output_file::really_copy_file(hunits, vunits, const char *)
1692 {
1693   // do nothing
1694 }
1695
1696 void real_output_file::put_filename(const char *filename)
1697 {
1698   really_put_filename(filename);
1699 }
1700
1701 void real_output_file::really_put_filename(const char *)
1702 {
1703 }
1704
1705 void real_output_file::on()
1706 {
1707   really_on();
1708   if (output_on == 0)
1709     output_on = 1;
1710 }
1711
1712 void real_output_file::off()
1713 {
1714   really_off();
1715   output_on = 0;
1716 }
1717
1718 int real_output_file::is_on()
1719 {
1720   return output_on;
1721 }
1722
1723 void real_output_file::really_on()
1724 {
1725 }
1726
1727 void real_output_file::really_off()
1728 {
1729 }
1730
1731 /* ascii_output_file */
1732
1733 void ascii_output_file::really_transparent_char(unsigned char c)
1734 {
1735   putc(c, fp);
1736 }
1737
1738 void ascii_output_file::really_print_line(hunits, vunits, node *n,
1739                                           vunits, vunits, hunits)
1740 {
1741   while (n != 0) {
1742     n->ascii_print(this);
1743     n = n->next;
1744   }
1745   fputc('\n', fp);
1746 }
1747
1748 void ascii_output_file::really_begin_page(int /*pageno*/, vunits /*page_length*/)
1749 {
1750   fputs("<beginning of page>\n", fp);
1751 }
1752
1753 ascii_output_file::ascii_output_file()
1754 {
1755 }
1756
1757 /* suppress_output_file */
1758
1759 suppress_output_file::suppress_output_file()
1760 {
1761 }
1762
1763 void suppress_output_file::really_print_line(hunits, vunits, node *, vunits, vunits, hunits)
1764 {
1765 }
1766
1767 void suppress_output_file::really_begin_page(int, vunits)
1768 {
1769 }
1770
1771 void suppress_output_file::really_transparent_char(unsigned char)
1772 {
1773 }
1774
1775 /* glyphs, ligatures, kerns, discretionary breaks */
1776
1777 class charinfo_node : public node {
1778 protected:
1779   charinfo *ci;
1780 public:
1781   charinfo_node(charinfo *, statem *, int, node * = 0);
1782   int ends_sentence();
1783   int overlaps_vertically();
1784   int overlaps_horizontally();
1785 };
1786
1787 charinfo_node::charinfo_node(charinfo *c, statem *s, int pop, node *x)
1788 : node(x, s, pop), ci(c)
1789 {
1790 }
1791
1792 int charinfo_node::ends_sentence()
1793 {
1794   if (ci->ends_sentence())
1795     return 1;
1796   else if (ci->transparent())
1797     return 2;
1798   else
1799     return 0;
1800 }
1801
1802 int charinfo_node::overlaps_horizontally()
1803 {
1804   return ci->overlaps_horizontally();
1805 }
1806
1807 int charinfo_node::overlaps_vertically()
1808 {
1809   return ci->overlaps_vertically();
1810 }
1811
1812 class glyph_node : public charinfo_node {
1813   static glyph_node *free_list;
1814 protected:
1815   tfont *tf;
1816   color *gcol;
1817   color *fcol;          /* this is needed for grotty */
1818 #ifdef STORE_WIDTH
1819   hunits wid;
1820   glyph_node(charinfo *, tfont *, color *, color *, hunits,
1821              statem *, int, node * = 0);
1822 #endif
1823 public:
1824   void *operator new(size_t);
1825   void operator delete(void *);
1826   glyph_node(charinfo *, tfont *, color *, color *,
1827              statem *, int, node * = 0);
1828   ~glyph_node() {}
1829   node *copy();
1830   node *merge_glyph_node(glyph_node *);
1831   node *merge_self(node *);
1832   hunits width();
1833   node *last_char_node();
1834   units size();
1835   void vertical_extent(vunits *, vunits *);
1836   hunits subscript_correction();
1837   hunits italic_correction();
1838   hunits left_italic_correction();
1839   hunits skew();
1840   hyphenation_type get_hyphenation_type();
1841   tfont *get_tfont();
1842   color *get_glyph_color();
1843   color *get_fill_color();
1844   void tprint(troff_output_file *);
1845   void zero_width_tprint(troff_output_file *);
1846   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1847   node *add_self(node *, hyphen_list **);
1848   void ascii_print(ascii_output_file *);
1849   void asciify(macro *);
1850   int character_type();
1851   int same(node *);
1852   const char *type();
1853   int force_tprint();
1854   int is_tag();
1855   void debug_node();
1856 };
1857
1858 glyph_node *glyph_node::free_list = 0;
1859
1860 class ligature_node : public glyph_node {
1861   node *n1;
1862   node *n2;
1863 #ifdef STORE_WIDTH
1864   ligature_node(charinfo *, tfont *, color *, color *, hunits,
1865                 node *, node *, statem *, int, node * = 0);
1866 #endif
1867 public:
1868   void *operator new(size_t);
1869   void operator delete(void *);
1870   ligature_node(charinfo *, tfont *, color *, color *,
1871                 node *, node *, statem *, int, node * = 0);
1872   ~ligature_node();
1873   node *copy();
1874   node *add_self(node *, hyphen_list **);
1875   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1876   void ascii_print(ascii_output_file *);
1877   void asciify(macro *);
1878   int same(node *);
1879   const char *type();
1880   int force_tprint();
1881   int is_tag();
1882 };
1883
1884 class kern_pair_node : public node {
1885   hunits amount;
1886   node *n1;
1887   node *n2;
1888 public:
1889   kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1890   ~kern_pair_node();
1891   node *copy();
1892   node *merge_glyph_node(glyph_node *);
1893   node *add_self(node *, hyphen_list **);
1894   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1895   node *add_discretionary_hyphen();
1896   hunits width();
1897   node *last_char_node();
1898   hunits italic_correction();
1899   hunits subscript_correction();
1900   void tprint(troff_output_file *);
1901   hyphenation_type get_hyphenation_type();
1902   int ends_sentence();
1903   void ascii_print(ascii_output_file *);
1904   void asciify(macro *);
1905   int same(node *);
1906   const char *type();
1907   int force_tprint();
1908   int is_tag();
1909   void vertical_extent(vunits *, vunits *);
1910 };
1911
1912 class dbreak_node : public node {
1913   node *none;
1914   node *pre;
1915   node *post;
1916 public:
1917   dbreak_node(node *, node *, statem *, int, node * = 0);
1918   ~dbreak_node();
1919   node *copy();
1920   node *merge_glyph_node(glyph_node *);
1921   node *add_discretionary_hyphen();
1922   hunits width();
1923   node *last_char_node();
1924   hunits italic_correction();
1925   hunits subscript_correction();
1926   void tprint(troff_output_file *);
1927   breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1928                               int is_inner = 0);
1929   int nbreaks();
1930   int ends_sentence();
1931   void split(int, node **, node **);
1932   hyphenation_type get_hyphenation_type();
1933   void ascii_print(ascii_output_file *);
1934   void asciify(macro *);
1935   int same(node *);
1936   const char *type();
1937   int force_tprint();
1938   int is_tag();
1939 };
1940
1941 void *glyph_node::operator new(size_t n)
1942 {
1943   assert(n == sizeof(glyph_node));
1944   if (!free_list) {
1945     const int BLOCK = 1024;
1946     free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1947     for (int i = 0; i < BLOCK - 1; i++)
1948       free_list[i].next = free_list + i + 1;
1949     free_list[BLOCK-1].next = 0;
1950   }
1951   glyph_node *p = free_list;
1952   free_list = (glyph_node *)(free_list->next);
1953   p->next = 0;
1954   return p;
1955 }
1956
1957 void *ligature_node::operator new(size_t n)
1958 {
1959   return new char[n];
1960 }
1961
1962 void glyph_node::operator delete(void *p)
1963 {
1964   if (p) {
1965     ((glyph_node *)p)->next = free_list;
1966     free_list = (glyph_node *)p;
1967   }
1968 }
1969
1970 void ligature_node::operator delete(void *p)
1971 {
1972   delete[] (char *)p;
1973 }
1974
1975 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
1976                        statem *s, int pop, node *x)
1977 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
1978 {
1979 #ifdef STORE_WIDTH
1980   wid = tf->get_width(ci);
1981 #endif
1982 }
1983
1984 #ifdef STORE_WIDTH
1985 glyph_node::glyph_node(charinfo *c, tfont *t,
1986                        color *gc, color *fc, hunits w,
1987                        statem *s, int pop, node *x)
1988 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
1989 {
1990 }
1991 #endif
1992
1993 node *glyph_node::copy()
1994 {
1995 #ifdef STORE_WIDTH
1996   return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
1997 #else
1998   return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
1999 #endif
2000 }
2001
2002 node *glyph_node::merge_self(node *nd)
2003 {
2004   return nd->merge_glyph_node(this);
2005 }
2006
2007 int glyph_node::character_type()
2008 {
2009   return tf->get_character_type(ci);
2010 }
2011
2012 node *glyph_node::add_self(node *n, hyphen_list **p)
2013 {
2014   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2015   next = 0;
2016   node *nn;
2017   if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2018     next = n;
2019     nn = this;
2020   }
2021   if ((*p)->hyphen)
2022     nn = nn->add_discretionary_hyphen();
2023   hyphen_list *pp = *p;
2024   *p = (*p)->next;
2025   delete pp;
2026   return nn;
2027 }
2028
2029 units glyph_node::size()
2030 {
2031   return tf->get_size().to_units();
2032 }
2033
2034 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2035 {
2036   (*count)++;
2037   return new hyphen_list(ci->get_hyphenation_code(), tail);
2038 }
2039
2040 tfont *node::get_tfont()
2041 {
2042   return 0;
2043 }
2044
2045 tfont *glyph_node::get_tfont()
2046 {
2047   return tf;
2048 }
2049
2050 color *node::get_glyph_color()
2051 {
2052   return 0;
2053 }
2054
2055 color *glyph_node::get_glyph_color()
2056 {
2057   return gcol;
2058 }
2059
2060 color *node::get_fill_color()
2061 {
2062   return 0;
2063 }
2064
2065 color *glyph_node::get_fill_color()
2066 {
2067   return fcol;
2068 }
2069
2070 node *node::merge_glyph_node(glyph_node *)
2071 {
2072   return 0;
2073 }
2074
2075 node *glyph_node::merge_glyph_node(glyph_node *gn)
2076 {
2077   if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2078     charinfo *lig;
2079     if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2080       node *next1 = next;
2081       next = 0;
2082       return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2083                                gn->div_nest_level, next1);
2084     }
2085     hunits kern;
2086     if (tf->get_kern(ci, gn->ci, &kern)) {
2087       node *next1 = next;
2088       next = 0;
2089       return new kern_pair_node(kern, this, gn, state,
2090                                 gn->div_nest_level, next1);
2091     }
2092   }
2093   return 0;
2094 }
2095
2096 #ifdef STORE_WIDTH
2097 inline
2098 #endif
2099 hunits glyph_node::width()
2100 {
2101 #ifdef STORE_WIDTH
2102   return wid;
2103 #else
2104   return tf->get_width(ci);
2105 #endif
2106 }
2107
2108 node *glyph_node::last_char_node()
2109 {
2110   return this;
2111 }
2112
2113 void glyph_node::vertical_extent(vunits *min, vunits *max)
2114 {
2115   *min = -tf->get_char_height(ci);
2116   *max = tf->get_char_depth(ci);
2117 }
2118
2119 hunits glyph_node::skew()
2120 {
2121   return tf->get_char_skew(ci);
2122 }
2123
2124 hunits glyph_node::subscript_correction()
2125 {
2126   return tf->get_subscript_correction(ci);
2127 }
2128
2129 hunits glyph_node::italic_correction()
2130 {
2131   return tf->get_italic_correction(ci);
2132 }
2133
2134 hunits glyph_node::left_italic_correction()
2135 {
2136   return tf->get_left_italic_correction(ci);
2137 }
2138
2139 hyphenation_type glyph_node::get_hyphenation_type()
2140 {
2141   return HYPHEN_MIDDLE;
2142 }
2143
2144 void glyph_node::ascii_print(ascii_output_file *ascii)
2145 {
2146   unsigned char c = ci->get_ascii_code();
2147   if (c != 0)
2148     ascii->outc(c);
2149   else
2150     ascii->outs(ci->nm.contents());
2151 }
2152
2153 void glyph_node::debug_node()
2154 {
2155   unsigned char c = ci->get_ascii_code();
2156   fprintf(stderr, "{ %s [", type());
2157   if (c)
2158     fprintf(stderr, "%c", c);
2159   else
2160     fprintf(stderr, ci->nm.contents());
2161   if (push_state)
2162     fprintf(stderr, " <push_state>");
2163   if (state)
2164     state->display_state();
2165   fprintf(stderr, " nest level %d", div_nest_level);
2166   fprintf(stderr, "]}\n");
2167   fflush(stderr);
2168 }
2169
2170 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2171                              node *gn1, node *gn2, statem *s,
2172                              int pop, node *x)
2173 : glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2174 {
2175 }
2176
2177 #ifdef STORE_WIDTH
2178 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2179                              hunits w, node *gn1, node *gn2, statem *s,
2180                              int pop, node *x)
2181 : glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2182 {
2183 }
2184 #endif
2185
2186 ligature_node::~ligature_node()
2187 {
2188   delete n1;
2189   delete n2;
2190 }
2191
2192 node *ligature_node::copy()
2193 {
2194 #ifdef STORE_WIDTH
2195   return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2196                            state, div_nest_level);
2197 #else
2198   return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2199                            state, div_nest_level);
2200 #endif
2201 }
2202
2203 void ligature_node::ascii_print(ascii_output_file *ascii)
2204 {
2205   n1->ascii_print(ascii);
2206   n2->ascii_print(ascii);
2207 }
2208
2209 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2210 {
2211   hyphen_list *hl = n2->get_hyphen_list(tail, count);
2212   return n1->get_hyphen_list(hl, count);
2213 }
2214
2215 node *ligature_node::add_self(node *n, hyphen_list **p)
2216 {
2217   n = n1->add_self(n, p);
2218   n = n2->add_self(n, p);
2219   n1 = n2 = 0;
2220   delete this;
2221   return n;
2222 }
2223
2224 kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2225                                statem* s, int pop, node *x)
2226 : node(x, s, pop), amount(n), n1(first), n2(second)
2227 {
2228 }
2229
2230 dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2231 : node(x, s, pop), none(n), pre(p), post(0)
2232 {
2233 }
2234
2235 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2236 {
2237   glyph_node *gn2 = (glyph_node *)gn->copy();
2238   node *new_none = none ? none->merge_glyph_node(gn) : 0;
2239   node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2240   if (new_none == 0 && new_post == 0) {
2241     delete gn2;
2242     return 0;
2243   }
2244   if (new_none != 0)
2245     none = new_none;
2246   else {
2247     gn->next = none;
2248     none = gn;
2249   }
2250   if (new_post != 0)
2251     post = new_post;
2252   else {
2253     gn2->next = post;
2254     post = gn2;
2255   }
2256   return this;
2257 }
2258
2259 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2260 {
2261   node *nd = n2->merge_glyph_node(gn);
2262   if (nd == 0)
2263     return 0;
2264   n2 = nd;
2265   nd = n2->merge_self(n1);
2266   if (nd) {
2267     nd->next = next;
2268     n1 = 0;
2269     n2 = 0;
2270     delete this;
2271     return nd;
2272   }
2273   return this;
2274 }
2275
2276 hunits kern_pair_node::italic_correction()
2277 {
2278   return n2->italic_correction();
2279 }
2280
2281 hunits kern_pair_node::subscript_correction()
2282 {
2283   return n2->subscript_correction();
2284 }
2285
2286 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2287 {
2288   n1->vertical_extent(min, max);
2289   vunits min2, max2;
2290   n2->vertical_extent(&min2, &max2);
2291   if (min2 < *min)
2292     *min = min2;
2293   if (max2 > *max)
2294     *max = max2;
2295 }
2296
2297 node *kern_pair_node::add_discretionary_hyphen()
2298 {
2299   tfont *tf = n2->get_tfont();
2300   if (tf) {
2301     if (tf->contains(soft_hyphen_char)) {
2302       color *gcol = n2->get_glyph_color();
2303       color *fcol = n2->get_fill_color();
2304       node *next1 = next;
2305       next = 0;
2306       node *n = copy();
2307       glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2308                                       state, div_nest_level);
2309       node *nn = n->merge_glyph_node(gn);
2310       if (nn == 0) {
2311         gn->next = n;
2312         nn = gn;
2313       }
2314       return new dbreak_node(this, nn, state, div_nest_level, next1);
2315     }
2316   }
2317   return this;
2318 }
2319
2320 kern_pair_node::~kern_pair_node()
2321 {
2322   if (n1 != 0)
2323     delete n1;
2324   if (n2 != 0)
2325     delete n2;
2326 }
2327
2328 dbreak_node::~dbreak_node()
2329 {
2330   delete_node_list(pre);
2331   delete_node_list(post);
2332   delete_node_list(none);
2333 }
2334
2335 node *kern_pair_node::copy()
2336 {
2337   return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2338                             div_nest_level);
2339 }
2340
2341 node *copy_node_list(node *n)
2342 {
2343   node *p = 0;
2344   while (n != 0) {
2345     node *nn = n->copy();
2346     nn->next = p;
2347     p = nn;
2348     n = n->next;
2349   }
2350   while (p != 0) {
2351     node *pp = p->next;
2352     p->next = n;
2353     n = p;
2354     p = pp;
2355   }
2356   return n;
2357 }
2358
2359 void delete_node_list(node *n)
2360 {
2361   while (n != 0) {
2362     node *tem = n;
2363     n = n->next;
2364     delete tem;
2365   }
2366 }
2367
2368 node *dbreak_node::copy()
2369 {
2370   dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2371                                    state, div_nest_level);
2372   p->post = copy_node_list(post);
2373   return p;
2374 }
2375
2376 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2377 {
2378   return tail;
2379 }
2380
2381 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2382 {
2383   hyphen_list *hl = n2->get_hyphen_list(tail, count);
2384   return n1->get_hyphen_list(hl, count);
2385 }
2386
2387 class hyphen_inhibitor_node : public node {
2388 public:
2389   hyphen_inhibitor_node(node * = 0);
2390   node *copy();
2391   int same(node *);
2392   const char *type();
2393   int force_tprint();
2394   int is_tag();
2395   hyphenation_type get_hyphenation_type();
2396 };
2397
2398 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2399 {
2400 }
2401
2402 node *hyphen_inhibitor_node::copy()
2403 {
2404   return new hyphen_inhibitor_node;
2405 }
2406
2407 int hyphen_inhibitor_node::same(node *)
2408 {
2409   return 1;
2410 }
2411
2412 const char *hyphen_inhibitor_node::type()
2413 {
2414   return "hyphen_inhibitor_node";
2415 }
2416
2417 int hyphen_inhibitor_node::force_tprint()
2418 {
2419   return 0;
2420 }
2421
2422 int hyphen_inhibitor_node::is_tag()
2423 {
2424   return 0;
2425 }
2426
2427 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2428 {
2429   return HYPHEN_INHIBIT;
2430 }
2431
2432 /* add_discretionary_hyphen methods */
2433
2434 node *dbreak_node::add_discretionary_hyphen()
2435 {
2436   if (post)
2437     post = post->add_discretionary_hyphen();
2438   if (none)
2439     none = none->add_discretionary_hyphen();
2440   return this;
2441 }
2442
2443 node *node::add_discretionary_hyphen()
2444 {
2445   tfont *tf = get_tfont();
2446   if (!tf)
2447     return new hyphen_inhibitor_node(this);
2448   if (tf->contains(soft_hyphen_char)) {
2449     color *gcol = get_glyph_color();
2450     color *fcol = get_fill_color();
2451     node *next1 = next;
2452     next = 0;
2453     node *n = copy();
2454     glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2455                                     state, div_nest_level);
2456     node *n1 = n->merge_glyph_node(gn);
2457     if (n1 == 0) {
2458       gn->next = n;
2459       n1 = gn;
2460     }
2461     return new dbreak_node(this, n1, state, div_nest_level, next1);
2462   }
2463   return this;
2464 }
2465
2466 node *node::merge_self(node *)
2467 {
2468   return 0;
2469 }
2470
2471 node *node::add_self(node *n, hyphen_list ** /*p*/)
2472 {
2473   next = n;
2474   return this;
2475 }
2476
2477 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2478 {
2479   n = n1->add_self(n, p);
2480   n = n2->add_self(n, p);
2481   n1 = n2 = 0;
2482   delete this;
2483   return n;
2484 }
2485
2486 hunits node::width()
2487 {
2488   return H0;
2489 }
2490
2491 node *node::last_char_node()
2492 {
2493   return 0;
2494 }
2495
2496 int node::force_tprint()
2497 {
2498   return 0;
2499 }
2500
2501 int node::is_tag()
2502 {
2503   return 0;
2504 }
2505
2506 hunits hmotion_node::width()
2507 {
2508   return n;
2509 }
2510
2511 units node::size()
2512 {
2513   return points_to_units(10);
2514 }
2515
2516 void node::debug_node()
2517 {
2518   fprintf(stderr, "{ %s ", type());
2519   if (push_state)
2520     fprintf(stderr, " <push_state>");
2521   if (state)
2522     fprintf(stderr, " <state>");
2523   fprintf(stderr, " nest level %d", div_nest_level);
2524   fprintf(stderr, " }\n");
2525   fflush(stderr);
2526 }
2527
2528 void node::debug_node_list()
2529 {
2530   node *n = next;
2531
2532   debug_node();
2533   while (n != 0) {
2534     n->debug_node();
2535     n = n->next;
2536   }
2537 }
2538
2539 hunits kern_pair_node::width()
2540 {
2541   return n1->width() + n2->width() + amount;
2542 }
2543
2544 node *kern_pair_node::last_char_node()
2545 {
2546   node *nd = n2->last_char_node();
2547   if (nd)
2548     return nd;
2549   return n1->last_char_node();
2550 }
2551
2552 hunits dbreak_node::width()
2553 {
2554   hunits x = H0;
2555   for (node *n = none; n != 0; n = n->next)
2556     x += n->width();
2557   return x;
2558 }
2559
2560 node *dbreak_node::last_char_node()
2561 {
2562   for (node *n = none; n; n = n->next) {
2563     node *last_node = n->last_char_node();
2564     if (last_node)
2565       return last_node;
2566   }
2567   return 0;
2568 }
2569
2570 hunits dbreak_node::italic_correction()
2571 {
2572   return none ? none->italic_correction() : H0;
2573 }
2574
2575 hunits dbreak_node::subscript_correction()
2576 {
2577   return none ? none->subscript_correction() : H0;
2578 }
2579
2580 class italic_corrected_node : public node {
2581   node *n;
2582   hunits x;
2583 public:
2584   italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2585   ~italic_corrected_node();
2586   node *copy();
2587   void ascii_print(ascii_output_file *);
2588   void asciify(macro *);
2589   hunits width();
2590   node *last_char_node();
2591   void vertical_extent(vunits *, vunits *);
2592   int ends_sentence();
2593   int overlaps_horizontally();
2594   int overlaps_vertically();
2595   int same(node *);
2596   hyphenation_type get_hyphenation_type();
2597   tfont *get_tfont();
2598   hyphen_list *get_hyphen_list(hyphen_list *, int *);
2599   int character_type();
2600   void tprint(troff_output_file *);
2601   hunits subscript_correction();
2602   hunits skew();
2603   node *add_self(node *, hyphen_list **);
2604   const char *type();
2605   int force_tprint();
2606   int is_tag();
2607 };
2608
2609 node *node::add_italic_correction(hunits *wd)
2610 {
2611   hunits ic = italic_correction();
2612   if (ic.is_zero())
2613     return this;
2614   else {
2615     node *next1 = next;
2616     next = 0;
2617     *wd += ic;
2618     return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2619   }
2620 }
2621
2622 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2623                                              int pop, node *p)
2624 : node(p, s, pop), n(nn), x(xx)
2625 {
2626   assert(n != 0);
2627 }
2628
2629 italic_corrected_node::~italic_corrected_node()
2630 {
2631   delete n;
2632 }
2633
2634 node *italic_corrected_node::copy()
2635 {
2636   return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2637 }
2638
2639 hunits italic_corrected_node::width()
2640 {
2641   return n->width() + x;
2642 }
2643
2644 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2645 {
2646   n->vertical_extent(min, max);
2647 }
2648
2649 void italic_corrected_node::tprint(troff_output_file *out)
2650 {
2651   n->tprint(out);
2652   out->right(x);
2653 }
2654
2655 hunits italic_corrected_node::skew()
2656 {
2657   return n->skew() - x/2;
2658 }
2659
2660 hunits italic_corrected_node::subscript_correction()
2661 {
2662   return n->subscript_correction() - x;
2663 }
2664
2665 void italic_corrected_node::ascii_print(ascii_output_file *out)
2666 {
2667   n->ascii_print(out);
2668 }
2669
2670 int italic_corrected_node::ends_sentence()
2671 {
2672   return n->ends_sentence();
2673 }
2674
2675 int italic_corrected_node::overlaps_horizontally()
2676 {
2677   return n->overlaps_horizontally();
2678 }
2679
2680 int italic_corrected_node::overlaps_vertically()
2681 {
2682   return n->overlaps_vertically();
2683 }
2684
2685 node *italic_corrected_node::last_char_node()
2686 {
2687   return n->last_char_node();
2688 }
2689
2690 tfont *italic_corrected_node::get_tfont()
2691 {
2692   return n->get_tfont();
2693 }
2694
2695 hyphenation_type italic_corrected_node::get_hyphenation_type()
2696 {
2697   return n->get_hyphenation_type();
2698 }
2699
2700 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2701 {
2702   nd = n->add_self(nd, p);
2703   hunits not_interested;
2704   nd = nd->add_italic_correction(&not_interested);
2705   n = 0;
2706   delete this;
2707   return nd;
2708 }
2709
2710 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2711                                                     int *count)
2712 {
2713   return n->get_hyphen_list(tail, count);
2714 }
2715
2716 int italic_corrected_node::character_type()
2717 {
2718   return n->character_type();
2719 }
2720
2721 class break_char_node : public node {
2722   node *ch;
2723   char break_code;
2724   color *col;
2725 public:
2726   break_char_node(node *, int, color *, node * = 0);
2727   break_char_node(node *, int, color *, statem *, int, node * = 0);
2728   ~break_char_node();
2729   node *copy();
2730   hunits width();
2731   vunits vertical_width();
2732   node *last_char_node();
2733   int character_type();
2734   int ends_sentence();
2735   node *add_self(node *, hyphen_list **);
2736   hyphen_list *get_hyphen_list(hyphen_list *, int *);
2737   void tprint(troff_output_file *);
2738   void zero_width_tprint(troff_output_file *);
2739   void ascii_print(ascii_output_file *);
2740   void asciify(macro *);
2741   hyphenation_type get_hyphenation_type();
2742   int overlaps_vertically();
2743   int overlaps_horizontally();
2744   units size();
2745   tfont *get_tfont();
2746   int same(node *);
2747   const char *type();
2748   int force_tprint();
2749   int is_tag();
2750 };
2751
2752 break_char_node::break_char_node(node *n, int bc, color *c, node *x)
2753 : node(x), ch(n), break_code(bc), col(c)
2754 {
2755 }
2756
2757 break_char_node::break_char_node(node *n, int bc, color *c, statem *s,
2758                                  int pop, node *x)
2759 : node(x, s, pop), ch(n), break_code(bc), col(c)
2760 {
2761 }
2762
2763 break_char_node::~break_char_node()
2764 {
2765   delete ch;
2766 }
2767
2768 node *break_char_node::copy()
2769 {
2770   return new break_char_node(ch->copy(), break_code, col, state,
2771                              div_nest_level);
2772 }
2773
2774 hunits break_char_node::width()
2775 {
2776   return ch->width();
2777 }
2778
2779 vunits break_char_node::vertical_width()
2780 {
2781   return ch->vertical_width();
2782 }
2783
2784 node *break_char_node::last_char_node()
2785 {
2786   return ch->last_char_node();
2787 }
2788
2789 int break_char_node::character_type()
2790 {
2791   return ch->character_type();
2792 }
2793
2794 int break_char_node::ends_sentence()
2795 {
2796   return ch->ends_sentence();
2797 }
2798
2799 node *break_char_node::add_self(node *n, hyphen_list **p)
2800 {
2801   assert((*p)->hyphenation_code == 0);
2802   if ((*p)->breakable && (break_code & 1)) {
2803     n = new space_node(H0, col, n);
2804     n->freeze_space();
2805   }
2806   next = n;
2807   n = this;
2808   if ((*p)->breakable && (break_code & 2)) {
2809     n = new space_node(H0, col, n);
2810     n->freeze_space();
2811   }
2812   hyphen_list *pp = *p;
2813   *p = (*p)->next;
2814   delete pp;
2815   return n;
2816 }
2817
2818 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2819 {
2820   return new hyphen_list(0, tail);
2821 }
2822
2823 hyphenation_type break_char_node::get_hyphenation_type()
2824 {
2825   return HYPHEN_MIDDLE;
2826 }
2827
2828 void break_char_node::ascii_print(ascii_output_file *ascii)
2829 {
2830   ch->ascii_print(ascii);
2831 }
2832
2833 int break_char_node::overlaps_vertically()
2834 {
2835   return ch->overlaps_vertically();
2836 }
2837
2838 int break_char_node::overlaps_horizontally()
2839 {
2840   return ch->overlaps_horizontally();
2841 }
2842
2843 units break_char_node::size()
2844 {
2845   return ch->size();
2846 }
2847
2848 tfont *break_char_node::get_tfont()
2849 {
2850   return ch->get_tfont();
2851 }
2852
2853 node *extra_size_node::copy()
2854 {
2855   return new extra_size_node(n, state, div_nest_level);
2856 }
2857
2858 extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2859 : node(0, s, pop), n(i)
2860 {
2861 }
2862
2863 extra_size_node::extra_size_node(vunits i)
2864 : n(i)
2865 {
2866 }
2867
2868 node *vertical_size_node::copy()
2869 {
2870   return new vertical_size_node(n, state, div_nest_level);
2871 }
2872
2873 vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2874 : node(0, s, pop), n(i)
2875 {
2876 }
2877
2878 vertical_size_node::vertical_size_node(vunits i)
2879 : n(i)
2880 {
2881 }
2882
2883 node *hmotion_node::copy()
2884 {
2885   return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2886 }
2887
2888 node *space_char_hmotion_node::copy()
2889 {
2890   return new space_char_hmotion_node(n, col, state, div_nest_level);
2891 }
2892
2893 vmotion_node::vmotion_node(vunits i, color *c)
2894 : n(i), col(c)
2895 {
2896 }
2897
2898 vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2899 : node(0, s, pop), n(i), col(c)
2900 {
2901 }
2902
2903 node *vmotion_node::copy()
2904 {
2905   return new vmotion_node(n, col, state, div_nest_level);
2906 }
2907
2908 node *dummy_node::copy()
2909 {
2910   return new dummy_node;
2911 }
2912
2913 node *transparent_dummy_node::copy()
2914 {
2915   return new transparent_dummy_node;
2916 }
2917
2918 hline_node::~hline_node()
2919 {
2920   if (n)
2921     delete n;
2922 }
2923
2924 hline_node::hline_node(hunits i, node *c, node *nxt)
2925 : node(nxt), x(i), n(c)
2926 {
2927 }
2928
2929 hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
2930 : node(nxt, s, pop), x(i), n(c)
2931 {
2932 }
2933
2934 node *hline_node::copy()
2935 {
2936   return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
2937 }
2938
2939 hunits hline_node::width()
2940 {
2941   return x < H0 ? H0 : x;
2942 }
2943
2944 vline_node::vline_node(vunits i, node *c, node *nxt)
2945 : node(nxt), x(i), n(c)
2946 {
2947 }
2948
2949 vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
2950 : node(nxt, s, pop), x(i), n(c)
2951 {
2952 }
2953
2954 vline_node::~vline_node()
2955 {
2956   if (n)
2957     delete n;
2958 }
2959
2960 node *vline_node::copy()
2961 {
2962   return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
2963 }
2964
2965 hunits vline_node::width()
2966 {
2967   return n == 0 ? H0 : n->width();
2968 }
2969
2970 zero_width_node::zero_width_node(node *nd, statem *s, int pop)
2971 : node(0, s, pop), n(nd)
2972 {
2973 }
2974
2975 zero_width_node::zero_width_node(node *nd)
2976 : n(nd)
2977 {
2978 }
2979
2980 zero_width_node::~zero_width_node()
2981 {
2982   delete_node_list(n);
2983 }
2984
2985 node *zero_width_node::copy()
2986 {
2987   return new zero_width_node(copy_node_list(n), state, div_nest_level);
2988 }
2989
2990 int node_list_character_type(node *p)
2991 {
2992   int t = 0;
2993   for (; p; p = p->next)
2994     t |= p->character_type();
2995   return t;
2996 }
2997
2998 int zero_width_node::character_type()
2999 {
3000   return node_list_character_type(n);
3001 }
3002
3003 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3004 {
3005   *min = V0;
3006   *max = V0;
3007   vunits cur_vpos = V0;
3008   vunits v1, v2;
3009   for (; p; p = p->next) {
3010     p->vertical_extent(&v1, &v2);
3011     v1 += cur_vpos;
3012     if (v1 < *min)
3013       *min = v1;
3014     v2 += cur_vpos;
3015     if (v2 > *max)
3016       *max = v2;
3017     cur_vpos += p->vertical_width();
3018   }
3019 }
3020
3021 void zero_width_node::vertical_extent(vunits *min, vunits *max)
3022 {
3023   node_list_vertical_extent(n, min, max);
3024 }
3025
3026 overstrike_node::overstrike_node()
3027 : list(0), max_width(H0)
3028 {
3029 }
3030
3031 overstrike_node::overstrike_node(statem *s, int pop)
3032 : node(0, s, pop), list(0), max_width(H0)
3033 {
3034 }
3035
3036 overstrike_node::~overstrike_node()
3037 {
3038   delete_node_list(list);
3039 }
3040
3041 node *overstrike_node::copy()
3042 {
3043   overstrike_node *on = new overstrike_node(state, div_nest_level);
3044   for (node *tem = list; tem; tem = tem->next)
3045     on->overstrike(tem->copy());
3046   return on;
3047 }
3048
3049 void overstrike_node::overstrike(node *n)
3050 {
3051   if (n == 0)
3052     return;
3053   hunits w = n->width();
3054   if (w > max_width)
3055     max_width = w;
3056   node **p;
3057   for (p = &list; *p; p = &(*p)->next)
3058     ;
3059   n->next = 0;
3060   *p = n;
3061 }
3062
3063 hunits overstrike_node::width()
3064 {
3065   return max_width;
3066 }
3067
3068 bracket_node::bracket_node()
3069 : list(0), max_width(H0)
3070 {
3071 }
3072
3073 bracket_node::bracket_node(statem *s, int pop)
3074 : node(0, s, pop), list(0), max_width(H0)
3075 {
3076 }
3077
3078 bracket_node::~bracket_node()
3079 {
3080   delete_node_list(list);
3081 }
3082
3083 node *bracket_node::copy()
3084 {
3085   bracket_node *on = new bracket_node(state, div_nest_level);
3086   node *last_node = 0;
3087   node *tem;
3088   if (list)
3089     list->last = 0;
3090   for (tem = list; tem; tem = tem->next) {
3091     if (tem->next)
3092       tem->next->last = tem;
3093     last_node = tem;
3094   }
3095   for (tem = last_node; tem; tem = tem->last)
3096     on->bracket(tem->copy());
3097   return on;
3098 }
3099
3100 void bracket_node::bracket(node *n)
3101 {
3102   if (n == 0)
3103     return;
3104   hunits w = n->width();
3105   if (w > max_width)
3106     max_width = w;
3107   n->next = list;
3108   list = n;
3109 }
3110
3111 hunits bracket_node::width()
3112 {
3113   return max_width;
3114 }
3115
3116 int node::nspaces()
3117 {
3118   return 0;
3119 }
3120
3121 int node::merge_space(hunits, hunits, hunits)
3122 {
3123   return 0;
3124 }
3125
3126 #if 0
3127 space_node *space_node::free_list = 0;
3128
3129 void *space_node::operator new(size_t n)
3130 {
3131   assert(n == sizeof(space_node));
3132   if (!free_list) {
3133     free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3134     for (int i = 0; i < BLOCK - 1; i++)
3135       free_list[i].next = free_list + i + 1;
3136     free_list[BLOCK-1].next = 0;
3137   }
3138   space_node *p = free_list;
3139   free_list = (space_node *)(free_list->next);
3140   p->next = 0;
3141   return p;
3142 }
3143
3144 inline void space_node::operator delete(void *p)
3145 {
3146   if (p) {
3147     ((space_node *)p)->next = free_list;
3148     free_list = (space_node *)p;
3149   }
3150 }
3151 #endif
3152
3153 space_node::space_node(hunits nn, color *c, node *p)
3154 : node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3155 {
3156 }
3157
3158 space_node::space_node(hunits nn, color *c, statem *s, int pop, node *p)
3159 : node(p, s, pop), n(nn), set(0), was_escape_colon(0), col(c)
3160 {
3161 }
3162
3163 space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3164                        int pop, node *p)
3165 : node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3166 {
3167 }
3168
3169 #if 0
3170 space_node::~space_node()
3171 {
3172 }
3173 #endif
3174
3175 node *space_node::copy()
3176 {
3177   return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3178 }
3179
3180 int space_node::force_tprint()
3181 {
3182   return 0;
3183 }
3184
3185 int space_node::is_tag()
3186 {
3187   return 0;
3188 }
3189
3190 int space_node::nspaces()
3191 {
3192   return set ? 0 : 1;
3193 }
3194
3195 int space_node::merge_space(hunits h, hunits, hunits)
3196 {
3197   n += h;
3198   return 1;
3199 }
3200
3201 hunits space_node::width()
3202 {
3203   return n;
3204 }
3205
3206 void node::spread_space(int*, hunits*)
3207 {
3208 }
3209
3210 void space_node::spread_space(int *n_spaces, hunits *desired_space)
3211 {
3212   if (!set) {
3213     assert(*n_spaces > 0);
3214     if (*n_spaces == 1) {
3215       n += *desired_space;
3216       *desired_space = H0;
3217     }
3218     else {
3219       hunits extra = *desired_space / *n_spaces;
3220       *desired_space -= extra;
3221       n += extra;
3222     }
3223     *n_spaces -= 1;
3224     set = 1;
3225   }
3226 }
3227
3228 void node::freeze_space()
3229 {
3230 }
3231
3232 void space_node::freeze_space()
3233 {
3234   set = 1;
3235 }
3236
3237 void node::is_escape_colon()
3238 {
3239 }
3240
3241 void space_node::is_escape_colon()
3242 {
3243   was_escape_colon = 1;
3244 }
3245
3246 diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3247                                          node *p)
3248 : node(p, s, pop), n(d)
3249 {
3250 }
3251
3252 diverted_space_node::diverted_space_node(vunits d, node *p)
3253 : node(p), n(d)
3254 {
3255 }
3256
3257 node *diverted_space_node::copy()
3258 {
3259   return new diverted_space_node(n, state, div_nest_level);
3260 }
3261
3262 diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3263                                                  int pop, node *p)
3264 : node(p, st, pop), filename(s)
3265 {
3266 }
3267
3268 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3269 : node(p), filename(s)
3270 {
3271 }
3272
3273 node *diverted_copy_file_node::copy()
3274 {
3275   return new diverted_copy_file_node(filename, state, div_nest_level);
3276 }
3277
3278 int node::ends_sentence()
3279 {
3280   return 0;
3281 }
3282
3283 int kern_pair_node::ends_sentence()
3284 {
3285   switch (n2->ends_sentence()) {
3286   case 0:
3287     return 0;
3288   case 1:
3289     return 1;
3290   case 2:
3291     break;
3292   default:
3293     assert(0);
3294   }
3295   return n1->ends_sentence();
3296 }
3297
3298 int node_list_ends_sentence(node *n)
3299 {
3300   for (; n != 0; n = n->next)
3301     switch (n->ends_sentence()) {
3302     case 0:
3303       return 0;
3304     case 1:
3305       return 1;
3306     case 2:
3307       break;
3308     default:
3309       assert(0);
3310     }
3311   return 2;
3312 }
3313
3314 int dbreak_node::ends_sentence()
3315 {
3316   return node_list_ends_sentence(none);
3317 }
3318
3319 int node::overlaps_horizontally()
3320 {
3321   return 0;
3322 }
3323
3324 int node::overlaps_vertically()
3325 {
3326   return 0;
3327 }
3328
3329 int node::discardable()
3330 {
3331   return 0;
3332 }
3333
3334 int space_node::discardable()
3335 {
3336   return set ? 0 : 1;
3337 }
3338
3339 vunits node::vertical_width()
3340 {
3341   return V0;
3342 }
3343
3344 vunits vline_node::vertical_width()
3345 {
3346   return x;
3347 }
3348
3349 vunits vmotion_node::vertical_width()
3350 {
3351   return n;
3352 }
3353
3354 int node::set_unformat_flag()
3355 {
3356   return 1;
3357 }
3358
3359 int node::character_type()
3360 {
3361   return 0;
3362 }
3363
3364 hunits node::subscript_correction()
3365 {
3366   return H0;
3367 }
3368
3369 hunits node::italic_correction()
3370 {
3371   return H0;
3372 }
3373
3374 hunits node::left_italic_correction()
3375 {
3376   return H0;
3377 }
3378
3379 hunits node::skew()
3380 {
3381   return H0;
3382 }
3383
3384 /* vertical_extent methods */
3385
3386 void node::vertical_extent(vunits *min, vunits *max)
3387 {
3388   vunits v = vertical_width();
3389   if (v < V0) {
3390     *min = v;
3391     *max = V0;
3392   }
3393   else {
3394     *max = v;
3395     *min = V0;
3396   }
3397 }
3398
3399 void vline_node::vertical_extent(vunits *min, vunits *max)
3400 {
3401   if (n == 0)
3402     node::vertical_extent(min, max);
3403   else {
3404     vunits cmin, cmax;
3405     n->vertical_extent(&cmin, &cmax);
3406     vunits h = n->size();
3407     if (x < V0) {
3408       if (-x < h) {
3409         *min = x;
3410         *max = V0;
3411       }
3412       else {
3413         // we print the first character and then move up, so
3414         *max = cmax;
3415         // we print the last character and then move up h
3416         *min = cmin + h;
3417         if (*min > V0)
3418           *min = V0;
3419         *min += x;
3420       }
3421     }
3422     else {
3423       if (x < h) {
3424         *max = x;
3425         *min = V0;
3426       }
3427       else {
3428         // we move down by h and then print the first character, so
3429         *min = cmin + h;
3430         if (*min > V0)
3431           *min = V0;
3432         *max = x + cmax;
3433       }
3434     }
3435   }
3436 }
3437
3438 /* ascii_print methods */
3439
3440 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3441 {
3442   if (n == 0)
3443     return;
3444   ascii_print_reverse_node_list(ascii, n->next);
3445   n->ascii_print(ascii);
3446 }
3447
3448 void dbreak_node::ascii_print(ascii_output_file *ascii)
3449 {
3450   ascii_print_reverse_node_list(ascii, none);
3451 }
3452
3453 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3454 {
3455   n1->ascii_print(ascii);
3456   n2->ascii_print(ascii);
3457 }
3458
3459 void node::ascii_print(ascii_output_file *)
3460 {
3461 }
3462
3463 void space_node::ascii_print(ascii_output_file *ascii)
3464 {
3465   if (!n.is_zero())
3466     ascii->outc(' ');
3467 }
3468
3469 void hmotion_node::ascii_print(ascii_output_file *ascii)
3470 {
3471   // this is pretty arbitrary
3472   if (n >= points_to_units(2))
3473     ascii->outc(' ');
3474 }
3475
3476 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3477 {
3478   ascii->outc(' ');
3479 }
3480
3481 /* asciify methods */
3482
3483 void node::asciify(macro *m)
3484 {
3485   m->append(this);
3486 }
3487
3488 void glyph_node::asciify(macro *m)
3489 {
3490   unsigned char c = ci->get_asciify_code();
3491   if (c == 0)
3492     c = ci->get_ascii_code();
3493   if (c != 0) {
3494     m->append(c);
3495     delete this;
3496   }
3497   else
3498     m->append(this);
3499 }
3500
3501 void kern_pair_node::asciify(macro *m)
3502 {
3503   n1->asciify(m);
3504   n2->asciify(m);
3505   n1 = n2 = 0;
3506   delete this;
3507 }
3508
3509 static void asciify_reverse_node_list(macro *m, node *n)
3510 {
3511   if (n == 0)
3512     return;
3513   asciify_reverse_node_list(m, n->next);
3514   n->asciify(m);
3515 }
3516
3517 void dbreak_node::asciify(macro *m)
3518 {
3519   asciify_reverse_node_list(m, none);
3520   none = 0;
3521   delete this;
3522 }
3523
3524 void ligature_node::asciify(macro *m)
3525 {
3526   n1->asciify(m);
3527   n2->asciify(m);
3528   n1 = n2 = 0;
3529   delete this;
3530 }
3531
3532 void break_char_node::asciify(macro *m)
3533 {
3534   ch->asciify(m);
3535   ch = 0;
3536   delete this;
3537 }
3538
3539 void italic_corrected_node::asciify(macro *m)
3540 {
3541   n->asciify(m);
3542   n = 0;
3543   delete this;
3544 }
3545
3546 void left_italic_corrected_node::asciify(macro *m)
3547 {
3548   if (n) {
3549     n->asciify(m);
3550     n = 0;
3551   }
3552   delete this;
3553 }
3554
3555 void hmotion_node::asciify(macro *m)
3556 {
3557   if (was_tab) {
3558     m->append('\t');
3559     delete this;
3560   }
3561   else
3562     m->append(this);
3563 }
3564
3565 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3566                                                  statem *s, int pop,
3567                                                  node *nxt)
3568 : hmotion_node(i, c, s, pop, nxt)
3569 {
3570 }
3571
3572 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3573                                                  node *nxt)
3574 : hmotion_node(i, c, 0, 0, nxt)
3575 {
3576 }
3577
3578 void space_char_hmotion_node::asciify(macro *m)
3579 {
3580   m->append(ESCAPE_SPACE);
3581   delete this;
3582 }
3583
3584 void space_node::asciify(macro *m)
3585 {
3586   if (was_escape_colon) {
3587     m->append(ESCAPE_COLON);
3588     delete this;
3589   }
3590   else
3591     m->append(this);
3592 }
3593
3594 void word_space_node::asciify(macro *m)
3595 {
3596   for (width_list *w = orig_width; w; w = w->next)
3597     m->append(' ');
3598   delete this;
3599 }
3600
3601 void unbreakable_space_node::asciify(macro *m)
3602 {
3603   m->append(ESCAPE_TILDE);
3604   delete this;
3605 }
3606
3607 void line_start_node::asciify(macro *)
3608 {
3609   delete this;
3610 }
3611
3612 void vertical_size_node::asciify(macro *)
3613 {
3614   delete this;
3615 }
3616
3617 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3618                                   breakpoint *rest, int /*is_inner*/)
3619 {
3620   return rest;
3621 }
3622
3623 int node::nbreaks()
3624 {
3625   return 0;
3626 }
3627
3628 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3629                                         breakpoint *rest, int is_inner)
3630 {
3631   if (next && next->discardable())
3632     return rest;
3633   breakpoint *bp = new breakpoint;
3634   bp->next = rest;
3635   bp->width = wd;
3636   bp->nspaces = ns;
3637   bp->hyphenated = 0;
3638   if (is_inner) {
3639     assert(rest != 0);
3640     bp->index = rest->index + 1;
3641     bp->nd = rest->nd;
3642   }
3643   else {
3644     bp->nd = this;
3645     bp->index = 0;
3646   }
3647   return bp;
3648 }
3649
3650 int space_node::nbreaks()
3651 {
3652   if (next && next->discardable())
3653     return 0;
3654   else
3655     return 1;
3656 }
3657
3658 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3659                                              int ns, breakpoint *rest)
3660 {
3661   if (p != 0) {
3662     rest = p->get_breakpoints(*widthp,
3663                               ns,
3664                               node_list_get_breakpoints(p->next, widthp, ns,
3665                                                         rest),
3666                               1);
3667     *widthp += p->width();
3668   }
3669   return rest;
3670 }
3671
3672 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3673                                          breakpoint *rest, int is_inner)
3674 {
3675   breakpoint *bp = new breakpoint;
3676   bp->next = rest;
3677   bp->width = wd;
3678   for (node *tem = pre; tem != 0; tem = tem->next)
3679     bp->width += tem->width();
3680   bp->nspaces = ns;
3681   bp->hyphenated = 1;
3682   if (is_inner) {
3683     assert(rest != 0);
3684     bp->index = rest->index + 1;
3685     bp->nd = rest->nd;
3686   }
3687   else {
3688     bp->nd = this;
3689     bp->index = 0;
3690   }
3691   return node_list_get_breakpoints(none, &wd, ns, bp);
3692 }
3693
3694 int dbreak_node::nbreaks()
3695 {
3696   int i = 1;
3697   for (node *tem = none; tem != 0; tem = tem->next)
3698     i += tem->nbreaks();
3699   return i;
3700 }
3701
3702 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3703 {
3704   assert(0);
3705 }
3706
3707 void space_node::split(int where, node **pre, node **post)
3708 {
3709   assert(where == 0);
3710   *pre = next;
3711   *post = 0;
3712   delete this;
3713 }
3714
3715 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3716 {
3717   if (p == 0)
3718     return;
3719   int nb = p->nbreaks();
3720   node_list_split(p->next, wherep, prep, postp);
3721   if (*wherep < 0) {
3722     p->next = *postp;
3723     *postp = p;
3724   }
3725   else if (*wherep < nb) {
3726     p->next = *prep;
3727     p->split(*wherep, prep, postp);
3728   }
3729   else {
3730     p->next = *prep;
3731     *prep = p;
3732   }
3733   *wherep -= nb;
3734 }
3735
3736 void dbreak_node::split(int where, node **prep, node **postp)
3737 {
3738   assert(where >= 0);
3739   if (where == 0) {
3740     *postp = post;
3741     post = 0;
3742     if (pre == 0)
3743       *prep = next;
3744     else {
3745       node *tem;
3746       for (tem = pre; tem->next != 0; tem = tem->next)
3747         ;
3748       tem->next = next;
3749       *prep = pre;
3750     }
3751     pre = 0;
3752     delete this;
3753   }
3754   else {
3755     *prep = next;
3756     where -= 1;
3757     node_list_split(none, &where, prep, postp);
3758     none = 0;
3759     delete this;
3760   }
3761 }
3762
3763 hyphenation_type node::get_hyphenation_type()
3764 {
3765   return HYPHEN_BOUNDARY;
3766 }
3767
3768 hyphenation_type dbreak_node::get_hyphenation_type()
3769 {
3770   return HYPHEN_INHIBIT;
3771 }
3772
3773 hyphenation_type kern_pair_node::get_hyphenation_type()
3774 {
3775   return HYPHEN_MIDDLE;
3776 }
3777
3778 hyphenation_type dummy_node::get_hyphenation_type()
3779 {
3780   return HYPHEN_MIDDLE;
3781 }
3782
3783 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3784 {
3785   return HYPHEN_MIDDLE;
3786 }
3787
3788 hyphenation_type hmotion_node::get_hyphenation_type()
3789 {
3790   return HYPHEN_MIDDLE;
3791 }
3792
3793 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3794 {
3795   return HYPHEN_MIDDLE;
3796 }
3797
3798 hyphenation_type overstrike_node::get_hyphenation_type()
3799 {
3800   return HYPHEN_MIDDLE;
3801 }
3802
3803 hyphenation_type space_node::get_hyphenation_type()
3804 {
3805   if (was_escape_colon)
3806     return HYPHEN_MIDDLE;
3807   return HYPHEN_BOUNDARY;
3808 }
3809
3810 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3811 {
3812   return HYPHEN_MIDDLE;
3813 }
3814
3815 int node::interpret(macro *)
3816 {
3817   return 0;
3818 }
3819
3820 special_node::special_node(const macro &m, int n)
3821 : mac(m), no_init_string(n)
3822 {
3823   font_size fs = curenv->get_font_size();
3824   int char_height = curenv->get_char_height();
3825   int char_slant = curenv->get_char_slant();
3826   int fontno = env_definite_font(curenv);
3827   tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3828   if (curenv->is_composite())
3829     tf = tf->get_plain();
3830   gcol = curenv->get_glyph_color();
3831   fcol = curenv->get_fill_color();
3832   is_special = 1;
3833 }
3834
3835 special_node::special_node(const macro &m, tfont *t,
3836                            color *gc, color *fc,
3837                            statem *s, int pop,
3838                            int n)
3839 : node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3840 {
3841   is_special = 1;
3842 }
3843
3844 int special_node::same(node *n)
3845 {
3846   return mac == ((special_node *)n)->mac
3847          && tf == ((special_node *)n)->tf
3848          && gcol == ((special_node *)n)->gcol
3849          && fcol == ((special_node *)n)->fcol
3850          && no_init_string == ((special_node *)n)->no_init_string;
3851 }
3852
3853 const char *special_node::type()
3854 {
3855   return "special_node";
3856 }
3857
3858 int special_node::ends_sentence()
3859 {
3860   return 2;
3861 }
3862
3863 int special_node::force_tprint()
3864 {
3865   return 0;
3866 }
3867
3868 int special_node::is_tag()
3869 {
3870   return 0;
3871 }
3872
3873 node *special_node::copy()
3874 {
3875   return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3876                           no_init_string);
3877 }
3878
3879 void special_node::tprint_start(troff_output_file *out)
3880 {
3881   out->start_special(tf, gcol, fcol, no_init_string);
3882 }
3883
3884 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3885 {
3886   out->special_char(c);
3887 }
3888
3889 void special_node::tprint_end(troff_output_file *out)
3890 {
3891   out->end_special();
3892 }
3893
3894 tfont *special_node::get_tfont()
3895 {
3896   return tf;
3897 }
3898
3899 /* suppress_node */
3900
3901 suppress_node::suppress_node(int on_or_off, int issue_limits)
3902 : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3903   image_id(0)
3904 {
3905 }
3906
3907 suppress_node::suppress_node(symbol f, char p, int id)
3908 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3909 {
3910   is_special = 1;
3911 }
3912
3913 suppress_node::suppress_node(int issue_limits, int on_or_off,
3914                              symbol f, char p, int id,
3915                              statem *s, int pop)
3916 : node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3917   position(p), image_id(id)
3918 {
3919 }
3920
3921 int suppress_node::same(node *n)
3922 {
3923   return ((is_on == ((suppress_node *)n)->is_on)
3924           && (emit_limits == ((suppress_node *)n)->emit_limits)
3925           && (filename == ((suppress_node *)n)->filename)
3926           && (position == ((suppress_node *)n)->position)
3927           && (image_id == ((suppress_node *)n)->image_id));
3928 }
3929
3930 const char *suppress_node::type()
3931 {
3932   return "suppress_node";
3933 }
3934
3935 node *suppress_node::copy()
3936 {
3937   return new suppress_node(emit_limits, is_on, filename, position, image_id,
3938                            state, div_nest_level);
3939 }
3940
3941 /* tag_node */
3942
3943 tag_node::tag_node()
3944 : delayed(0)
3945 {
3946   is_special = 1;
3947 }
3948
3949 tag_node::tag_node(string s, int delay)
3950 : tag_string(s), delayed(delay)
3951 {
3952   is_special = !delay;
3953 }
3954
3955 tag_node::tag_node(string s, statem *st, int pop, int delay)
3956 : node(0, st, pop), tag_string(s), delayed(delay)
3957 {
3958   is_special = !delay;
3959 }
3960
3961 node *tag_node::copy()
3962 {
3963   return new tag_node(tag_string, state, div_nest_level, delayed);
3964 }
3965
3966 void tag_node::tprint(troff_output_file *out)
3967 {
3968   if (delayed)
3969     out->add_to_tag_list(tag_string);
3970   else
3971     out->state.add_tag(out->fp, tag_string);
3972 }
3973
3974 int tag_node::same(node *nd)
3975 {
3976   return tag_string == ((tag_node *)nd)->tag_string
3977          && delayed == ((tag_node *)nd)->delayed;
3978 }
3979
3980 const char *tag_node::type()
3981 {
3982   return "tag_node";
3983 }
3984
3985 int tag_node::force_tprint()
3986 {
3987   return !delayed;
3988 }
3989
3990 int tag_node::is_tag()
3991 {
3992   return !delayed;
3993 }
3994
3995 int tag_node::ends_sentence()
3996 {
3997   return 2;
3998 }
3999
4000 int get_reg_int(const char *p)
4001 {
4002   reg *r = (reg *)number_reg_dictionary.lookup(p);
4003   units prev_value;
4004   if (r && (r->get_value(&prev_value)))
4005     return (int)prev_value;
4006   else
4007     warning(WARN_REG, "number register `%1' not defined", p);
4008   return 0;
4009 }
4010
4011 const char *get_reg_str(const char *p)
4012 {
4013   reg *r = (reg *)number_reg_dictionary.lookup(p);
4014   if (r)
4015     return r->get_string();
4016   else
4017     warning(WARN_REG, "register `%1' not defined", p);
4018   return 0;
4019 }
4020
4021 void suppress_node::put(troff_output_file *out, const char *s)
4022 {
4023   int i = 0;
4024   while (s[i] != (char)0) {
4025     out->special_char(s[i]);
4026     i++;
4027   }
4028 }
4029
4030 /*
4031  *  We need to remember the start of the image and its name.
4032  */
4033
4034 static char last_position = 0;
4035 static const char *last_image_filename = 0;
4036 static int last_image_id = 0;
4037
4038 inline int min(int a, int b)
4039 {
4040   return a < b ? a : b;
4041 }
4042
4043 /*
4044  *  tprint - if (is_on == 2)
4045  *               remember current position (l, r, c, i) and filename
4046  *           else
4047  *               if (emit_limits)
4048  *                   if (html)
4049  *                      emit image tag
4050  *                   else
4051  *                      emit postscript bounds for image
4052  *               else
4053  *                  if (suppress boolean differs from current state)
4054  *                      alter state
4055  *                  reset registers
4056  *                  record current page
4057  *                  set low water mark.
4058  */
4059
4060 void suppress_node::tprint(troff_output_file *out)
4061 {
4062   int current_page = topdiv->get_page_number();
4063   // firstly check to see whether this suppress node contains
4064   // an image filename & position.
4065   if (is_on == 2) {
4066     // remember position and filename
4067     last_position = position;
4068     char *tem = (char *)last_image_filename;
4069     last_image_filename = strsave(filename.contents());
4070     if (tem)
4071       a_delete tem;
4072     last_image_id = image_id;
4073     // printf("start of image and page = %d\n", current_page);
4074   }
4075   else {
4076     // now check whether the suppress node requires us to issue limits.
4077     if (emit_limits) {
4078       char name[8192];
4079       // remember that the filename will contain a %d in which the
4080       // last_image_id is placed
4081       if (last_image_filename == (char *) 0)
4082         *name = '\0';
4083       else
4084         sprintf(name, last_image_filename, last_image_id);
4085       if (is_html) {
4086         switch (last_position) {
4087         case 'c':
4088           out->start_special();
4089           put(out, "devtag:.centered-image");
4090           break;
4091         case 'r':
4092           out->start_special();
4093           put(out, "devtag:.right-image");
4094           break;
4095         case 'l':
4096           out->start_special();
4097           put(out, "devtag:.left-image");
4098           break;
4099         case 'i':
4100           ;
4101         default:
4102           ;
4103         }
4104         out->end_special();
4105         out->start_special();
4106         put(out, "devtag:.auto-image ");
4107         put(out, name);
4108         out->end_special();
4109       }
4110       else {
4111         // postscript (or other device)
4112         if (suppress_start_page > 0 && current_page != suppress_start_page)
4113           error("suppression limit registers span more than one page;\n"
4114                 "image description %1 will be wrong", image_no);
4115         // if (topdiv->get_page_number() != suppress_start_page)
4116         //  fprintf(stderr, "end of image and topdiv page = %d   and  suppress_start_page = %d\n",
4117         //        topdiv->get_page_number(), suppress_start_page);
4118
4119         // remember that the filename will contain a %d in which the
4120         // image_no is placed
4121         fprintf(stderr,
4122                 "grohtml-info:page %d  %d  %d  %d  %d  %d  %s  %d  %d  %s\n",
4123                 topdiv->get_page_number(),
4124                 get_reg_int("opminx"), get_reg_int("opminy"),
4125                 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4126                 // page offset + line length
4127                 get_reg_int(".o") + get_reg_int(".l"),
4128                 name, hresolution, vresolution, get_reg_str(".F"));
4129         fflush(stderr);
4130       }
4131     }
4132     else {
4133       if (is_on) {
4134         out->on();
4135         // lastly we reset the output registers
4136         reset_output_registers();
4137       }
4138       else
4139         out->off();
4140       suppress_start_page = current_page;
4141     }
4142   }
4143 }
4144
4145 int suppress_node::force_tprint()
4146 {
4147   return is_on;
4148 }
4149
4150 int suppress_node::is_tag()
4151 {
4152   return is_on;
4153 }
4154
4155 hunits suppress_node::width()
4156 {
4157   return H0;
4158 }
4159
4160 /* composite_node */
4161
4162 class composite_node : public charinfo_node {
4163   node *n;
4164   tfont *tf;
4165 public:
4166   composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4167   ~composite_node();
4168   node *copy();
4169   hunits width();
4170   node *last_char_node();
4171   units size();
4172   void tprint(troff_output_file *);
4173   hyphenation_type get_hyphenation_type();
4174   void ascii_print(ascii_output_file *);
4175   void asciify(macro *);
4176   hyphen_list *get_hyphen_list(hyphen_list *, int *);
4177   node *add_self(node *, hyphen_list **);
4178   tfont *get_tfont();
4179   int same(node *);
4180   const char *type();
4181   int force_tprint();
4182   int is_tag();
4183   void vertical_extent(vunits *, vunits *);
4184   vunits vertical_width();
4185 };
4186
4187 composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4188                                int pop, node *x)
4189 : charinfo_node(c, s, pop, x), n(p), tf(t)
4190 {
4191 }
4192
4193 composite_node::~composite_node()
4194 {
4195   delete_node_list(n);
4196 }
4197
4198 node *composite_node::copy()
4199 {
4200   return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4201 }
4202
4203 hunits composite_node::width()
4204 {
4205   hunits x;
4206   if (tf->get_constant_space(&x))
4207     return x;
4208   x = H0;
4209   for (node *tem = n; tem; tem = tem->next)
4210     x += tem->width();
4211   hunits offset;
4212   if (tf->get_bold(&offset))
4213     x += offset;
4214   x += tf->get_track_kern();
4215   return x;
4216 }
4217
4218 node *composite_node::last_char_node()
4219 {
4220   return this;
4221 }
4222
4223 vunits composite_node::vertical_width()
4224 {
4225   vunits v = V0;
4226   for (node *tem = n; tem; tem = tem->next)
4227     v += tem->vertical_width();
4228   return v;
4229 }
4230
4231 units composite_node::size()
4232 {
4233   return tf->get_size().to_units();
4234 }
4235
4236 hyphenation_type composite_node::get_hyphenation_type()
4237 {
4238   return HYPHEN_MIDDLE;
4239 }
4240
4241 void composite_node::asciify(macro *m)
4242 {
4243   unsigned char c = ci->get_asciify_code();
4244   if (c == 0)
4245     c = ci->get_ascii_code();
4246   if (c != 0) {
4247     m->append(c);
4248     delete this;
4249   }
4250   else
4251     m->append(this);
4252 }
4253
4254 void composite_node::ascii_print(ascii_output_file *ascii)
4255 {
4256   unsigned char c = ci->get_ascii_code();
4257   if (c != 0)
4258     ascii->outc(c);
4259   else
4260     ascii->outs(ci->nm.contents());
4261
4262 }
4263
4264 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4265 {
4266   (*count)++;
4267   return new hyphen_list(ci->get_hyphenation_code(), tail);
4268 }
4269
4270 node *composite_node::add_self(node *nn, hyphen_list **p)
4271 {
4272   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4273   next = nn;
4274   nn = this;
4275   if ((*p)->hyphen)
4276     nn = nn->add_discretionary_hyphen();
4277   hyphen_list *pp = *p;
4278   *p = (*p)->next;
4279   delete pp;
4280   return nn;
4281 }
4282
4283 tfont *composite_node::get_tfont()
4284 {
4285   return tf;
4286 }
4287
4288 node *reverse_node_list(node *n)
4289 {
4290   node *r = 0;
4291   while (n) {
4292     node *tem = n;
4293     n = n->next;
4294     tem->next = r;
4295     r = tem;
4296   }
4297   return r;
4298 }
4299
4300 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4301 {
4302   n = reverse_node_list(n);
4303   node_list_vertical_extent(n, minimum, maximum);
4304   n = reverse_node_list(n);
4305 }
4306
4307 width_list::width_list(hunits w, hunits s)
4308 : width(w), sentence_width(s), next(0)
4309 {
4310 }
4311
4312 width_list::width_list(width_list *w)
4313 : width(w->width), sentence_width(w->sentence_width), next(0)
4314 {
4315 }
4316
4317 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4318 : space_node(d, c, x), orig_width(w), unformat(0)
4319 {
4320 }
4321
4322 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4323                                  int flag, statem *st, int pop, node *x)
4324 : space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4325 {
4326 }
4327
4328 word_space_node::~word_space_node()
4329 {
4330   width_list *w = orig_width;
4331   while (w != 0) {
4332     width_list *tmp = w;
4333     w = w->next;
4334     delete tmp;
4335   }
4336 }
4337
4338 node *word_space_node::copy()
4339 {
4340   assert(orig_width != 0);
4341   width_list *w_old_curr = orig_width;
4342   width_list *w_new_curr = new width_list(w_old_curr);
4343   width_list *w_new = w_new_curr;
4344   w_old_curr = w_old_curr->next;
4345   while (w_old_curr != 0) {
4346     w_new_curr->next = new width_list(w_old_curr);
4347     w_new_curr = w_new_curr->next;
4348     w_old_curr = w_old_curr->next;
4349   }
4350   return new word_space_node(n, set, col, w_new, unformat, state,
4351                              div_nest_level);
4352 }
4353
4354 int word_space_node::set_unformat_flag()
4355 {
4356   unformat = 1;
4357   return 1;
4358 }
4359
4360 void word_space_node::tprint(troff_output_file *out)
4361 {
4362   out->fill_color(col);
4363   out->word_marker();
4364   out->right(n);
4365 }
4366
4367 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4368 {
4369   n += h;
4370   assert(orig_width != 0);
4371   width_list *w = orig_width; 
4372   for (; w->next; w = w->next)
4373     ;
4374   w->next = new width_list(sw, ssw);
4375   return 1;
4376 }
4377
4378 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4379 : word_space_node(d, c, 0, x)
4380 {
4381 }
4382
4383 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4384                                                color *c, statem *st, int pop,
4385                                                node *x)
4386 : word_space_node(d, s, c, 0, 0, st, pop, x)
4387 {
4388 }
4389
4390 node *unbreakable_space_node::copy()
4391 {
4392   return new unbreakable_space_node(n, set, col, state, div_nest_level);
4393 }
4394
4395 int unbreakable_space_node::force_tprint()
4396 {
4397   return 0;
4398 }
4399
4400 int unbreakable_space_node::is_tag()
4401 {
4402   return 0;
4403 }
4404
4405 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4406                                                     breakpoint *rest, int)
4407 {
4408   return rest;
4409 }
4410
4411 int unbreakable_space_node::nbreaks()
4412 {
4413   return 0;
4414 }
4415
4416 void unbreakable_space_node::split(int, node **, node **)
4417 {
4418   assert(0);
4419 }
4420
4421 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4422 {
4423   return 0;
4424 }
4425
4426 hvpair::hvpair()
4427 {
4428 }
4429
4430 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4431                      color *gc, color *fc)
4432 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4433 {
4434   point = new hvpair[npoints];
4435   for (int i = 0; i < npoints; i++)
4436     point[i] = p[i];
4437 }
4438
4439 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4440                      color *gc, color *fc, statem *st, int pop)
4441 : node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4442 {
4443   point = new hvpair[npoints];
4444   for (int i = 0; i < npoints; i++)
4445     point[i] = p[i];
4446 }
4447
4448 int draw_node::same(node *n)
4449 {
4450   draw_node *nd = (draw_node *)n;
4451   if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4452       || gcol != nd->gcol || fcol != nd->fcol)
4453     return 0;
4454   for (int i = 0; i < npoints; i++)
4455     if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4456       return 0;
4457   return 1;
4458 }
4459
4460 const char *draw_node::type()
4461 {
4462   return "draw_node";
4463 }
4464
4465 int draw_node::force_tprint()
4466 {
4467   return 0;
4468 }
4469
4470 int draw_node::is_tag()
4471 {
4472   return 0;
4473 }
4474
4475 draw_node::~draw_node()
4476 {
4477   if (point)
4478     a_delete point;
4479 }
4480
4481 hunits draw_node::width()
4482 {
4483   hunits x = H0;
4484   for (int i = 0; i < npoints; i++)
4485     x += point[i].h;
4486   return x;
4487 }
4488
4489 vunits draw_node::vertical_width()
4490 {
4491   if (code == 'e')
4492     return V0;
4493   vunits x = V0;
4494   for (int i = 0; i < npoints; i++)
4495     x += point[i].v;
4496   return x;
4497 }
4498
4499 node *draw_node::copy()
4500 {
4501   return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4502                        div_nest_level);
4503 }
4504
4505 void draw_node::tprint(troff_output_file *out)
4506 {
4507   out->draw(code, point, npoints, sz, gcol, fcol);
4508 }
4509
4510 /* tprint methods */
4511
4512 void glyph_node::tprint(troff_output_file *out)
4513 {
4514   tfont *ptf = tf->get_plain();
4515   if (ptf == tf)
4516     out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4517   else {
4518     hunits offset;
4519     int bold = tf->get_bold(&offset);
4520     hunits w = ptf->get_width(ci);
4521     hunits k = H0;
4522     hunits x;
4523     int cs = tf->get_constant_space(&x);
4524     if (cs) {
4525       x -= w;
4526       if (bold)
4527         x -= offset;
4528       hunits x2 = x/2;
4529       out->right(x2);
4530       k = x - x2;
4531     }
4532     else
4533       k = tf->get_track_kern();
4534     if (bold) {
4535       out->put_char(ci, ptf, gcol, fcol);
4536       out->right(offset);
4537     }
4538     out->put_char_width(ci, ptf, gcol, fcol, w, k);
4539   }
4540 }
4541
4542 void glyph_node::zero_width_tprint(troff_output_file *out)
4543 {
4544   tfont *ptf = tf->get_plain();
4545   hunits offset;
4546   int bold = tf->get_bold(&offset);
4547   hunits x;
4548   int cs = tf->get_constant_space(&x);
4549   if (cs) {
4550     x -= ptf->get_width(ci);
4551     if (bold)
4552       x -= offset;
4553     x = x/2;
4554     out->right(x);
4555   }
4556   out->put_char(ci, ptf, gcol, fcol);
4557   if (bold) {
4558     out->right(offset);
4559     out->put_char(ci, ptf, gcol, fcol);
4560     out->right(-offset);
4561   }
4562   if (cs)
4563     out->right(-x);
4564 }
4565
4566 void break_char_node::tprint(troff_output_file *t)
4567 {
4568   ch->tprint(t);
4569 }
4570
4571 void break_char_node::zero_width_tprint(troff_output_file *t)
4572 {
4573   ch->zero_width_tprint(t);
4574 }
4575
4576 void hline_node::tprint(troff_output_file *out)
4577 {
4578   if (x < H0) {
4579     out->right(x);
4580     x = -x;
4581   }
4582   if (n == 0) {
4583     out->right(x);
4584     return;
4585   }
4586   hunits w = n->width();
4587   if (w <= H0) {
4588     error("horizontal line drawing character must have positive width");
4589     out->right(x);
4590     return;
4591   }
4592   int i = int(x/w);
4593   if (i == 0) {
4594     hunits xx = x - w;
4595     hunits xx2 = xx/2;
4596     out->right(xx2);
4597     if (out->is_on())
4598       n->tprint(out);
4599     out->right(xx - xx2);
4600   }
4601   else {
4602     hunits rem = x - w*i;
4603     if (rem > H0)
4604       if (n->overlaps_horizontally()) {
4605         if (out->is_on())
4606           n->tprint(out);
4607         out->right(rem - w);
4608       }
4609       else
4610         out->right(rem);
4611     while (--i >= 0)
4612       if (out->is_on())
4613         n->tprint(out);
4614   }
4615 }
4616
4617 void vline_node::tprint(troff_output_file *out)
4618 {
4619   if (n == 0) {
4620     out->down(x);
4621     return;
4622   }
4623   vunits h = n->size();
4624   int overlaps = n->overlaps_vertically();
4625   vunits y = x;
4626   if (y < V0) {
4627     y = -y;
4628     int i = y / h;
4629     vunits rem = y - i*h;
4630     if (i == 0) {
4631       out->right(n->width());
4632       out->down(-rem);
4633     }
4634     else {
4635       while (--i > 0) {
4636         n->zero_width_tprint(out);
4637         out->down(-h);
4638       }
4639       if (overlaps) {
4640         n->zero_width_tprint(out);
4641         out->down(-rem);
4642         if (out->is_on())
4643           n->tprint(out);
4644         out->down(-h);
4645       }
4646       else {
4647         if (out->is_on())
4648           n->tprint(out);
4649         out->down(-h - rem);
4650       }
4651     }
4652   }
4653   else {
4654     int i = y / h;
4655     vunits rem = y - i*h;
4656     if (i == 0) {
4657       out->down(rem);
4658       out->right(n->width());
4659     }
4660     else {
4661       out->down(h);
4662       if (overlaps)
4663         n->zero_width_tprint(out);
4664       out->down(rem);
4665       while (--i > 0) {
4666         n->zero_width_tprint(out);
4667         out->down(h);
4668       }
4669       if (out->is_on())
4670         n->tprint(out);
4671     }
4672   }
4673 }
4674
4675 void zero_width_node::tprint(troff_output_file *out)
4676 {
4677   if (!n)
4678     return;
4679   if (!n->next) {
4680     n->zero_width_tprint(out);
4681     return;
4682   }
4683   int hpos = out->get_hpos();
4684   int vpos = out->get_vpos();
4685   node *tem = n;
4686   while (tem) {
4687     tem->tprint(out);
4688     tem = tem->next;
4689   }
4690   out->moveto(hpos, vpos);
4691 }
4692
4693 void overstrike_node::tprint(troff_output_file *out)
4694 {
4695   hunits pos = H0;
4696   for (node *tem = list; tem; tem = tem->next) {
4697     hunits x = (max_width - tem->width())/2;
4698     out->right(x - pos);
4699     pos = x;
4700     tem->zero_width_tprint(out);
4701   }
4702   out->right(max_width - pos);
4703 }
4704
4705 void bracket_node::tprint(troff_output_file *out)
4706 {
4707   if (list == 0)
4708     return;
4709   int npieces = 0;
4710   node *tem;
4711   for (tem = list; tem; tem = tem->next)
4712     ++npieces;
4713   vunits h = list->size();
4714   vunits totalh = h*npieces;
4715   vunits y = (totalh - h)/2;
4716   out->down(y);
4717   for (tem = list; tem; tem = tem->next) {
4718     tem->zero_width_tprint(out);
4719     out->down(-h);
4720   }
4721   out->right(max_width);
4722   out->down(totalh - y);
4723 }
4724
4725 void node::tprint(troff_output_file *)
4726 {
4727 }
4728
4729 void node::zero_width_tprint(troff_output_file *out)
4730 {
4731   int hpos = out->get_hpos();
4732   int vpos = out->get_vpos();
4733   tprint(out);
4734   out->moveto(hpos, vpos);
4735 }
4736
4737 void space_node::tprint(troff_output_file *out)
4738 {
4739   out->fill_color(col);
4740   out->right(n);
4741 }
4742
4743 void hmotion_node::tprint(troff_output_file *out)
4744 {
4745   out->fill_color(col);
4746   out->right(n);
4747 }
4748
4749 void space_char_hmotion_node::tprint(troff_output_file *out)
4750 {
4751   out->fill_color(col);
4752   if (is_html) {
4753     // we emit the space width as a negative glyph index
4754     out->flush_tbuf();
4755     out->do_motion();
4756     out->put('N');
4757     out->put(-n.to_units());
4758     out->put('\n');
4759   }
4760   out->right(n);
4761 }
4762
4763 void vmotion_node::tprint(troff_output_file *out)
4764 {
4765   out->fill_color(col);
4766   out->down(n);
4767 }
4768
4769 void kern_pair_node::tprint(troff_output_file *out)
4770 {
4771   n1->tprint(out);
4772   out->right(amount);
4773   n2->tprint(out);
4774 }
4775
4776 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4777 {
4778   if (n == 0)
4779     return;
4780   tprint_reverse_node_list(out, n->next);
4781   n->tprint(out);
4782 }
4783
4784 void dbreak_node::tprint(troff_output_file *out)
4785 {
4786   tprint_reverse_node_list(out, none);
4787 }
4788
4789 void composite_node::tprint(troff_output_file *out)
4790 {
4791   hunits bold_offset;
4792   int is_bold = tf->get_bold(&bold_offset);
4793   hunits track_kern = tf->get_track_kern();
4794   hunits constant_space;
4795   int is_constant_spaced = tf->get_constant_space(&constant_space);
4796   hunits x = H0;
4797   if (is_constant_spaced) {
4798     x = constant_space;
4799     for (node *tem = n; tem; tem = tem->next)
4800       x -= tem->width();
4801     if (is_bold)
4802       x -= bold_offset;
4803     hunits x2 = x/2;
4804     out->right(x2);
4805     x -= x2;
4806   }
4807   if (is_bold) {
4808     int hpos = out->get_hpos();
4809     int vpos = out->get_vpos();
4810     tprint_reverse_node_list(out, n);
4811     out->moveto(hpos, vpos);
4812     out->right(bold_offset);
4813   }
4814   tprint_reverse_node_list(out, n);
4815   if (is_constant_spaced)
4816     out->right(x);
4817   else
4818     out->right(track_kern);
4819 }
4820
4821 node *make_composite_node(charinfo *s, environment *env)
4822 {
4823   int fontno = env_definite_font(env);
4824   if (fontno < 0) {
4825     error("no current font");
4826     return 0;
4827   }
4828   assert(fontno < font_table_size && font_table[fontno] != 0);
4829   node *n = charinfo_to_node_list(s, env);
4830   font_size fs = env->get_font_size();
4831   int char_height = env->get_char_height();
4832   int char_slant = env->get_char_slant();
4833   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4834                                             fontno);
4835   if (env->is_composite())
4836     tf = tf->get_plain();
4837   return new composite_node(n, s, tf, 0, 0, 0);
4838 }
4839
4840 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4841 {
4842   int fontno = env_definite_font(env);
4843   if (fontno < 0) {
4844     error("no current font");
4845     return 0;
4846   }
4847   assert(fontno < font_table_size && font_table[fontno] != 0);
4848   int fn = fontno;
4849   int found = font_table[fontno]->contains(s);
4850   if (!found) {
4851     macro *mac = s->get_macro();
4852     if (mac && s->is_fallback())
4853       return make_composite_node(s, env);
4854     if (s->numbered()) {
4855       if (!no_error_message)
4856         warning(WARN_CHAR, "can't find numbered character %1",
4857                 s->get_number());
4858       return 0;
4859     }
4860     special_font_list *sf = font_table[fontno]->sf;
4861     while (sf != 0 && !found) {
4862       fn = sf->n;
4863       if (font_table[fn])
4864         found = font_table[fn]->contains(s);
4865       sf = sf->next;
4866     }
4867     if (!found) {
4868       symbol f = font_table[fontno]->get_name();
4869       string gl(f.contents());
4870       gl += ' ';
4871       gl += s->nm.contents();
4872       gl += '\0';
4873       charinfo *ci = get_charinfo(symbol(gl.contents()));
4874       if (ci && ci->get_macro())
4875         return make_composite_node(ci, env);
4876     }
4877     if (!found) {
4878       sf = global_special_fonts;
4879       while (sf != 0 && !found) {
4880         fn = sf->n;
4881         if (font_table[fn])
4882           found = font_table[fn]->contains(s);
4883         sf = sf->next;
4884       }
4885     }
4886     if (!found)
4887       if (mac && s->is_special())
4888         return make_composite_node(s, env);
4889     if (!found) {
4890       for (fn = 0; fn < font_table_size; fn++)
4891         if (font_table[fn]
4892             && font_table[fn]->is_special()
4893             && font_table[fn]->contains(s)) {
4894           found = 1;
4895           break;
4896         }
4897     }
4898     if (!found) {
4899       if (!no_error_message && s->first_time_not_found()) {
4900         unsigned char input_code = s->get_ascii_code();
4901         if (input_code != 0) {
4902           if (csgraph(input_code))
4903             warning(WARN_CHAR, "can't find character `%1'", input_code);
4904           else
4905             warning(WARN_CHAR, "can't find character with input code %1",
4906                     int(input_code));
4907         }
4908         else if (s->nm.contents())
4909           warning(WARN_CHAR, "can't find special character `%1'",
4910                   s->nm.contents());
4911       }
4912       return 0;
4913     }
4914   }
4915   font_size fs = env->get_font_size();
4916   int char_height = env->get_char_height();
4917   int char_slant = env->get_char_slant();
4918   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4919   if (env->is_composite())
4920     tf = tf->get_plain();
4921   color *gcol = env->get_glyph_color();
4922   color *fcol = env->get_fill_color();
4923   return new glyph_node(s, tf, gcol, fcol, 0, 0);
4924 }
4925
4926 node *make_node(charinfo *ci, environment *env)
4927 {
4928   switch (ci->get_special_translation()) {
4929   case charinfo::TRANSLATE_SPACE:
4930     return new space_char_hmotion_node(env->get_space_width(),
4931                                        env->get_fill_color());
4932   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4933     return new unbreakable_space_node(env->get_space_width(),
4934                                       env->get_fill_color());
4935   case charinfo::TRANSLATE_DUMMY:
4936     return new dummy_node;
4937   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4938     error("translation to \\% ignored in this context");
4939     break;
4940   }
4941   charinfo *tem = ci->get_translation();
4942   if (tem)
4943     ci = tem;
4944   macro *mac = ci->get_macro();
4945   if (mac && ci->is_normal())
4946     return make_composite_node(ci, env);
4947   else
4948     return make_glyph_node(ci, env);
4949 }
4950
4951 int character_exists(charinfo *ci, environment *env)
4952 {
4953   if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
4954     return 1;
4955   charinfo *tem = ci->get_translation();
4956   if (tem)
4957     ci = tem;
4958   if (ci->get_macro())
4959     return 1;
4960   node *nd = make_glyph_node(ci, env, 1);
4961   if (nd) {
4962     delete nd;
4963     return 1;
4964   }
4965   return 0;
4966 }
4967
4968 node *node::add_char(charinfo *ci, environment *env,
4969                      hunits *widthp, int *spacep, node **glyph_comp_np)
4970 {
4971   node *res;
4972   switch (ci->get_special_translation()) {
4973   case charinfo::TRANSLATE_SPACE:
4974     res = new space_char_hmotion_node(env->get_space_width(),
4975                                       env->get_fill_color(), this);
4976     *widthp += res->width();
4977     return res;
4978   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
4979     res = new unbreakable_space_node(env->get_space_width(),
4980                                      env->get_fill_color(), this);
4981     res->freeze_space();
4982     *widthp += res->width();
4983     *spacep += res->nspaces();
4984     return res;
4985   case charinfo::TRANSLATE_DUMMY:
4986     return new dummy_node(this);
4987   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
4988     return add_discretionary_hyphen();
4989   }
4990   charinfo *tem = ci->get_translation();
4991   if (tem)
4992     ci = tem;
4993   macro *mac = ci->get_macro();
4994   if (mac && ci->is_normal()) {
4995     res = make_composite_node(ci, env);
4996     if (res) {
4997       res->next = this;
4998       *widthp += res->width();
4999       if (glyph_comp_np)
5000         *glyph_comp_np = res;
5001     }
5002     else {
5003       if (glyph_comp_np)
5004         *glyph_comp_np = res;
5005       return this;
5006     }
5007   }
5008   else {
5009     node *gn = make_glyph_node(ci, env);
5010     if (gn == 0)
5011       return this;
5012     else {
5013       hunits old_width = width();
5014       node *p = gn->merge_self(this);
5015       if (p == 0) {
5016         *widthp += gn->width();
5017         gn->next = this;
5018         res = gn;
5019       }
5020       else {
5021         *widthp += p->width() - old_width;
5022         res = p;
5023       }
5024       if (glyph_comp_np)
5025         *glyph_comp_np = res;
5026     }
5027   }
5028   int break_code = 0;
5029   if (ci->can_break_before())
5030     break_code = 1;
5031   if (ci->can_break_after())
5032     break_code |= 2;
5033   if (break_code) {
5034     node *next1 = res->next;
5035     res->next = 0;
5036     res = new break_char_node(res, break_code, env->get_fill_color(), next1);
5037   }
5038   return res;
5039 }
5040
5041 #ifdef __GNUG__
5042 inline
5043 #endif
5044 int same_node(node *n1, node *n2)
5045 {
5046   if (n1 != 0) {
5047     if (n2 != 0)
5048       return n1->type() == n2->type() && n1->same(n2);
5049     else
5050       return 0;
5051   }
5052   else
5053     return n2 == 0;
5054 }
5055
5056 int same_node_list(node *n1, node *n2)
5057 {
5058   while (n1 && n2) {
5059     if (n1->type() != n2->type() || !n1->same(n2))
5060       return 0;
5061     n1 = n1->next;
5062     n2 = n2->next;
5063   }
5064   return !n1 && !n2;
5065 }
5066
5067 int extra_size_node::same(node *nd)
5068 {
5069   return n == ((extra_size_node *)nd)->n;
5070 }
5071
5072 const char *extra_size_node::type()
5073 {
5074   return "extra_size_node";
5075 }
5076
5077 int extra_size_node::force_tprint()
5078 {
5079   return 0;
5080 }
5081
5082 int extra_size_node::is_tag()
5083 {
5084   return 0;
5085 }
5086
5087 int vertical_size_node::same(node *nd)
5088 {
5089   return n == ((vertical_size_node *)nd)->n;
5090 }
5091
5092 const char *vertical_size_node::type()
5093 {
5094   return "vertical_size_node";
5095 }
5096
5097 int vertical_size_node::set_unformat_flag()
5098 {
5099   return 0;
5100 }
5101
5102 int vertical_size_node::force_tprint()
5103 {
5104   return 0;
5105 }
5106
5107 int vertical_size_node::is_tag()
5108 {
5109   return 0;
5110 }
5111
5112 int hmotion_node::same(node *nd)
5113 {
5114   return n == ((hmotion_node *)nd)->n
5115          && col == ((hmotion_node *)nd)->col;
5116 }
5117
5118 const char *hmotion_node::type()
5119 {
5120   return "hmotion_node";
5121 }
5122
5123 int hmotion_node::set_unformat_flag()
5124 {
5125   unformat = 1;
5126   return 1;
5127 }
5128
5129 int hmotion_node::force_tprint()
5130 {
5131   return 0;
5132 }
5133
5134 int hmotion_node::is_tag()
5135 {
5136   return 0;
5137 }
5138
5139 node *hmotion_node::add_self(node *nd, hyphen_list **p)
5140 {
5141   next = nd;
5142   hyphen_list *pp = *p;
5143   *p = (*p)->next;
5144   delete pp;
5145   return this;
5146 }
5147
5148 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5149 {
5150   return new hyphen_list(0, tail);
5151 }
5152
5153 int space_char_hmotion_node::same(node *nd)
5154 {
5155   return n == ((space_char_hmotion_node *)nd)->n
5156          && col == ((space_char_hmotion_node *)nd)->col;
5157 }
5158
5159 const char *space_char_hmotion_node::type()
5160 {
5161   return "space_char_hmotion_node";
5162 }
5163
5164 int space_char_hmotion_node::force_tprint()
5165 {
5166   return 0;
5167 }
5168
5169 int space_char_hmotion_node::is_tag()
5170 {
5171   return 0;
5172 }
5173
5174 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5175 {
5176   next = nd;
5177   hyphen_list *pp = *p;
5178   *p = (*p)->next;
5179   delete pp;
5180   return this;
5181 }
5182
5183 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5184                                                       int *)
5185 {
5186   return new hyphen_list(0, tail);
5187 }
5188
5189 int vmotion_node::same(node *nd)
5190 {
5191   return n == ((vmotion_node *)nd)->n
5192          && col == ((vmotion_node *)nd)->col;
5193 }
5194
5195 const char *vmotion_node::type()
5196 {
5197   return "vmotion_node";
5198 }
5199
5200 int vmotion_node::force_tprint()
5201 {
5202   return 0;
5203 }
5204
5205 int vmotion_node::is_tag()
5206 {
5207   return 0;
5208 }
5209
5210 int hline_node::same(node *nd)
5211 {
5212   return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5213 }
5214
5215 const char *hline_node::type()
5216 {
5217   return "hline_node";
5218 }
5219
5220 int hline_node::force_tprint()
5221 {
5222   return 0;
5223 }
5224
5225 int hline_node::is_tag()
5226 {
5227   return 0;
5228 }
5229
5230 int vline_node::same(node *nd)
5231 {
5232   return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5233 }
5234
5235 const char *vline_node::type()
5236 {
5237   return "vline_node";
5238 }
5239
5240 int vline_node::force_tprint()
5241 {
5242   return 0;
5243 }
5244
5245 int vline_node::is_tag()
5246 {
5247   return 0;
5248 }
5249
5250 int dummy_node::same(node * /*nd*/)
5251 {
5252   return 1;
5253 }
5254
5255 const char *dummy_node::type()
5256 {
5257   return "dummy_node";
5258 }
5259
5260 int dummy_node::force_tprint()
5261 {
5262   return 0;
5263 }
5264
5265 int dummy_node::is_tag()
5266 {
5267   return 0;
5268 }
5269
5270 int transparent_dummy_node::same(node * /*nd*/)
5271 {
5272   return 1;
5273 }
5274
5275 const char *transparent_dummy_node::type()
5276 {
5277   return "transparent_dummy_node";
5278 }
5279
5280 int transparent_dummy_node::force_tprint()
5281 {
5282   return 0;
5283 }
5284
5285 int transparent_dummy_node::is_tag()
5286 {
5287   return 0;
5288 }
5289
5290 int transparent_dummy_node::ends_sentence()
5291 {
5292   return 2;
5293 }
5294
5295 int zero_width_node::same(node *nd)
5296 {
5297   return same_node_list(n, ((zero_width_node *)nd)->n);
5298 }
5299
5300 const char *zero_width_node::type()
5301 {
5302   return "zero_width_node";
5303 }
5304
5305 int zero_width_node::force_tprint()
5306 {
5307   return 0;
5308 }
5309
5310 int zero_width_node::is_tag()
5311 {
5312   return 0;
5313 }
5314
5315 int italic_corrected_node::same(node *nd)
5316 {
5317   return (x == ((italic_corrected_node *)nd)->x
5318           && same_node(n, ((italic_corrected_node *)nd)->n));
5319 }
5320
5321 const char *italic_corrected_node::type()
5322 {
5323   return "italic_corrected_node";
5324 }
5325
5326 int italic_corrected_node::force_tprint()
5327 {
5328   return 0;
5329 }
5330
5331 int italic_corrected_node::is_tag()
5332 {
5333   return 0;
5334 }
5335
5336 left_italic_corrected_node::left_italic_corrected_node(node *xx)
5337 : node(xx), n(0)
5338 {
5339 }
5340
5341 left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5342                                                        node *xx)
5343 : node(xx, s, pop), n(0)
5344 {
5345 }
5346
5347 left_italic_corrected_node::~left_italic_corrected_node()
5348 {
5349   delete n;
5350 }
5351
5352 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5353 {
5354   if (n == 0) {
5355     hunits lic = gn->left_italic_correction();
5356     if (!lic.is_zero()) {
5357       x = lic;
5358       n = gn;
5359       return this;
5360     }
5361   }
5362   else {
5363     node *nd = n->merge_glyph_node(gn);
5364     if (nd) {
5365       n = nd;
5366       x = n->left_italic_correction();
5367       return this;
5368     }
5369   }
5370   return 0;
5371 }
5372
5373 node *left_italic_corrected_node::copy()
5374 {
5375   left_italic_corrected_node *nd =
5376     new left_italic_corrected_node(state, div_nest_level);
5377   if (n) {
5378     nd->n = n->copy();
5379     nd->x = x;
5380   }
5381   return nd;
5382 }
5383
5384 void left_italic_corrected_node::tprint(troff_output_file *out)
5385 {
5386   if (n) {
5387     out->right(x);
5388     n->tprint(out);
5389   }
5390 }
5391
5392 const char *left_italic_corrected_node::type()
5393 {
5394   return "left_italic_corrected_node";
5395 }
5396
5397 int left_italic_corrected_node::force_tprint()
5398 {
5399   return 0;
5400 }
5401
5402 int left_italic_corrected_node::is_tag()
5403 {
5404   return 0;
5405 }
5406
5407 int left_italic_corrected_node::same(node *nd)
5408 {
5409   return (x == ((left_italic_corrected_node *)nd)->x
5410           && same_node(n, ((left_italic_corrected_node *)nd)->n));
5411 }
5412
5413 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5414 {
5415   if (n)
5416     n->ascii_print(out);
5417 }
5418
5419 hunits left_italic_corrected_node::width()
5420 {
5421   return n ? n->width() + x : H0;
5422 }
5423
5424 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5425                                                  vunits *maximum)
5426 {
5427   if (n)
5428     n->vertical_extent(minimum, maximum);
5429   else
5430     node::vertical_extent(minimum, maximum);
5431 }
5432
5433 hunits left_italic_corrected_node::skew()
5434 {
5435   return n ? n->skew() + x/2 : H0;
5436 }
5437
5438 hunits left_italic_corrected_node::subscript_correction()
5439 {
5440   return n ? n->subscript_correction() : H0;
5441 }
5442
5443 hunits left_italic_corrected_node::italic_correction()
5444 {
5445   return n ? n->italic_correction() : H0;
5446 }
5447
5448 int left_italic_corrected_node::ends_sentence()
5449 {
5450   return n ? n->ends_sentence() : 0;
5451 }
5452
5453 int left_italic_corrected_node::overlaps_horizontally()
5454 {
5455   return n ? n->overlaps_horizontally() : 0;
5456 }
5457
5458 int left_italic_corrected_node::overlaps_vertically()
5459 {
5460   return n ? n->overlaps_vertically() : 0;
5461 }
5462
5463 node *left_italic_corrected_node::last_char_node()
5464 {
5465   return n ? n->last_char_node() : 0;
5466 }
5467
5468 tfont *left_italic_corrected_node::get_tfont()
5469 {
5470   return n ? n->get_tfont() : 0;
5471 }
5472
5473 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5474 {
5475   if (n)
5476     return n->get_hyphenation_type();
5477   else
5478     return HYPHEN_MIDDLE;
5479 }
5480
5481 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5482                                                          int *count)
5483 {
5484   return n ? n->get_hyphen_list(tail, count) : tail;
5485 }
5486
5487 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5488 {
5489   if (n) {
5490     nd = new left_italic_corrected_node(state, div_nest_level, nd);
5491     nd = n->add_self(nd, p);
5492     n = 0;
5493     delete this;
5494   }
5495   return nd;
5496 }
5497
5498 int left_italic_corrected_node::character_type()
5499 {
5500   return n ? n->character_type() : 0;
5501 }
5502
5503 int overstrike_node::same(node *nd)
5504 {
5505   return same_node_list(list, ((overstrike_node *)nd)->list);
5506 }
5507
5508 const char *overstrike_node::type()
5509 {
5510   return "overstrike_node";
5511 }
5512
5513 int overstrike_node::force_tprint()
5514 {
5515   return 0;
5516 }
5517
5518 int overstrike_node::is_tag()
5519 {
5520   return 0;
5521 }
5522
5523 node *overstrike_node::add_self(node *n, hyphen_list **p)
5524 {
5525   next = n;
5526   hyphen_list *pp = *p;
5527   *p = (*p)->next;
5528   delete pp;
5529   return this;
5530 }
5531
5532 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5533 {
5534   return new hyphen_list(0, tail);
5535 }
5536
5537 int bracket_node::same(node *nd)
5538 {
5539   return same_node_list(list, ((bracket_node *)nd)->list);
5540 }
5541
5542 const char *bracket_node::type()
5543 {
5544   return "bracket_node";
5545 }
5546
5547 int bracket_node::force_tprint()
5548 {
5549   return 0;
5550 }
5551
5552 int bracket_node::is_tag()
5553 {
5554   return 0;
5555 }
5556
5557 int composite_node::same(node *nd)
5558 {
5559   return ci == ((composite_node *)nd)->ci
5560     && same_node_list(n, ((composite_node *)nd)->n);
5561 }
5562
5563 const char *composite_node::type()
5564 {
5565   return "composite_node";
5566 }
5567
5568 int composite_node::force_tprint()
5569 {
5570   return 0;
5571 }
5572
5573 int composite_node::is_tag()
5574 {
5575   return 0;
5576 }
5577
5578 int glyph_node::same(node *nd)
5579 {
5580   return ci == ((glyph_node *)nd)->ci
5581          && tf == ((glyph_node *)nd)->tf
5582          && gcol == ((glyph_node *)nd)->gcol
5583          && fcol == ((glyph_node *)nd)->fcol;
5584 }
5585
5586 const char *glyph_node::type()
5587 {
5588   return "glyph_node";
5589 }
5590
5591 int glyph_node::force_tprint()
5592 {
5593   return 0;
5594 }
5595
5596 int glyph_node::is_tag()
5597 {
5598   return 0;
5599 }
5600
5601 int ligature_node::same(node *nd)
5602 {
5603   return (same_node(n1, ((ligature_node *)nd)->n1)
5604           && same_node(n2, ((ligature_node *)nd)->n2)
5605           && glyph_node::same(nd));
5606 }
5607
5608 const char *ligature_node::type()
5609 {
5610   return "ligature_node";
5611 }
5612
5613 int ligature_node::force_tprint()
5614 {
5615   return 0;
5616 }
5617
5618 int ligature_node::is_tag()
5619 {
5620   return 0;
5621 }
5622
5623 int kern_pair_node::same(node *nd)
5624 {
5625   return (amount == ((kern_pair_node *)nd)->amount
5626           && same_node(n1, ((kern_pair_node *)nd)->n1)
5627           && same_node(n2, ((kern_pair_node *)nd)->n2));
5628 }
5629
5630 const char *kern_pair_node::type()
5631 {
5632   return "kern_pair_node";
5633 }
5634
5635 int kern_pair_node::force_tprint()
5636 {
5637   return 0;
5638 }
5639
5640 int kern_pair_node::is_tag()
5641 {
5642   return 0;
5643 }
5644
5645 int dbreak_node::same(node *nd)
5646 {
5647   return (same_node_list(none, ((dbreak_node *)nd)->none)
5648           && same_node_list(pre, ((dbreak_node *)nd)->pre)
5649           && same_node_list(post, ((dbreak_node *)nd)->post));
5650 }
5651
5652 const char *dbreak_node::type()
5653 {
5654   return "dbreak_node";
5655 }
5656
5657 int dbreak_node::force_tprint()
5658 {
5659   return 0;
5660 }
5661
5662 int dbreak_node::is_tag()
5663 {
5664   return 0;
5665 }
5666
5667 int break_char_node::same(node *nd)
5668 {
5669   return break_code == ((break_char_node *)nd)->break_code
5670          && col == ((break_char_node *)nd)->col
5671          && same_node(ch, ((break_char_node *)nd)->ch);
5672 }
5673
5674 const char *break_char_node::type()
5675 {
5676   return "break_char_node";
5677 }
5678
5679 int break_char_node::force_tprint()
5680 {
5681   return 0;
5682 }
5683
5684 int break_char_node::is_tag()
5685 {
5686   return 0;
5687 }
5688
5689 int line_start_node::same(node * /*nd*/)
5690 {
5691   return 1;
5692 }
5693
5694 const char *line_start_node::type()
5695 {
5696   return "line_start_node";
5697 }
5698
5699 int line_start_node::force_tprint()
5700 {
5701   return 0;
5702 }
5703
5704 int line_start_node::is_tag()
5705 {
5706   return 0;
5707 }
5708
5709 int space_node::same(node *nd)
5710 {
5711   return n == ((space_node *)nd)->n
5712               && set == ((space_node *)nd)->set
5713               && col == ((space_node *)nd)->col;
5714 }
5715
5716 const char *space_node::type()
5717 {
5718   return "space_node";
5719 }
5720
5721 int word_space_node::same(node *nd)
5722 {
5723   return n == ((word_space_node *)nd)->n
5724          && set == ((word_space_node *)nd)->set
5725          && col == ((word_space_node *)nd)->col;
5726 }
5727
5728 const char *word_space_node::type()
5729 {
5730   return "word_space_node";
5731 }
5732
5733 int word_space_node::force_tprint()
5734 {
5735   return 0;
5736 }
5737
5738 int word_space_node::is_tag()
5739 {
5740   return 0;
5741 }
5742
5743 void unbreakable_space_node::tprint(troff_output_file *out)
5744 {
5745   out->fill_color(col);
5746   if (is_html) {
5747     // we emit the space width as a negative glyph index
5748     out->flush_tbuf();
5749     out->do_motion();
5750     out->put('N');
5751     out->put(-n.to_units());
5752     out->put('\n');
5753   }
5754   out->right(n);
5755 }
5756
5757 int unbreakable_space_node::same(node *nd)
5758 {
5759   return n == ((unbreakable_space_node *)nd)->n
5760          && set == ((unbreakable_space_node *)nd)->set
5761          && col == ((unbreakable_space_node *)nd)->col;
5762 }
5763
5764 const char *unbreakable_space_node::type()
5765 {
5766   return "unbreakable_space_node";
5767 }
5768
5769 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5770 {
5771   next = nd;
5772   hyphen_list *pp = *p;
5773   *p = (*p)->next;
5774   delete pp;
5775   return this;
5776 }
5777
5778 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5779 {
5780   return new hyphen_list(0, tail);
5781 }
5782
5783 int diverted_space_node::same(node *nd)
5784 {
5785   return n == ((diverted_space_node *)nd)->n;
5786 }
5787
5788 const char *diverted_space_node::type()
5789 {
5790   return "diverted_space_node";
5791 }
5792
5793 int diverted_space_node::force_tprint()
5794 {
5795   return 0;
5796 }
5797
5798 int diverted_space_node::is_tag()
5799 {
5800   return 0;
5801 }
5802
5803 int diverted_copy_file_node::same(node *nd)
5804 {
5805   return filename == ((diverted_copy_file_node *)nd)->filename;
5806 }
5807
5808 const char *diverted_copy_file_node::type()
5809 {
5810   return "diverted_copy_file_node";
5811 }
5812
5813 int diverted_copy_file_node::force_tprint()
5814 {
5815   return 0;
5816 }
5817
5818 int diverted_copy_file_node::is_tag()
5819 {
5820   return 0;
5821 }
5822
5823 // Grow the font_table so that its size is > n.
5824
5825 static void grow_font_table(int n)
5826 {
5827   assert(n >= font_table_size);
5828   font_info **old_font_table = font_table;
5829   int old_font_table_size = font_table_size;
5830   font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5831   if (font_table_size <= n)
5832     font_table_size = n + 10;
5833   font_table = new font_info *[font_table_size];
5834   if (old_font_table_size)
5835     memcpy(font_table, old_font_table,
5836            old_font_table_size*sizeof(font_info *));
5837   a_delete old_font_table;
5838   for (int i = old_font_table_size; i < font_table_size; i++)
5839     font_table[i] = 0;
5840 }
5841
5842 dictionary font_translation_dictionary(17);
5843
5844 static symbol get_font_translation(symbol nm)
5845 {
5846   void *p = font_translation_dictionary.lookup(nm);
5847   return p ? symbol((char *)p) : nm;
5848 }
5849
5850 dictionary font_dictionary(50);
5851
5852 static int mount_font_no_translate(int n, symbol name, symbol external_name,
5853                                    int check_only = 0)
5854 {
5855   assert(n >= 0);
5856   // We store the address of this char in font_dictionary to indicate
5857   // that we've previously tried to mount the font and failed.
5858   static char a_char;
5859   font *fm = 0;
5860   void *p = font_dictionary.lookup(external_name);
5861   if (p == 0) {
5862     int not_found;
5863     fm = font::load_font(external_name.contents(), &not_found, check_only);
5864     if (check_only)
5865       return fm != 0;
5866     if (!fm) {
5867       if (not_found)
5868         warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5869       (void)font_dictionary.lookup(external_name, &a_char);
5870       return 0;
5871     }
5872     (void)font_dictionary.lookup(name, fm);
5873   }
5874   else if (p == &a_char) {
5875 #if 0
5876     error("invalid font `%1'", external_name.contents());
5877 #endif
5878     return 0;
5879   }
5880   else
5881     fm = (font*)p;
5882   if (check_only)
5883     return 1;
5884   if (n >= font_table_size) {
5885     if (n - font_table_size > 1000) {
5886       error("font position too much larger than first unused position");
5887       return 0;
5888     }
5889     grow_font_table(n);
5890   }
5891   else if (font_table[n] != 0)
5892     delete font_table[n];
5893   font_table[n] = new font_info(name, n, external_name, fm);
5894   font_family::invalidate_fontno(n);
5895   return 1;
5896 }
5897
5898 int mount_font(int n, symbol name, symbol external_name)
5899 {
5900   assert(n >= 0);
5901   name = get_font_translation(name);
5902   if (external_name.is_null())
5903     external_name = name;
5904   else
5905     external_name = get_font_translation(external_name);
5906   return mount_font_no_translate(n, name, external_name);
5907 }
5908
5909 int check_font(symbol fam, symbol name)
5910 {
5911   if (check_style(name))
5912     name = concat(fam, name);
5913   return mount_font_no_translate(0, name, name, 1);
5914 }
5915
5916 int check_style(symbol s)
5917 {
5918   int i = symbol_fontno(s);
5919   return i < 0 ? 0 : font_table[i]->is_style();
5920 }
5921
5922 void mount_style(int n, symbol name)
5923 {
5924   assert(n >= 0);
5925   if (n >= font_table_size) {
5926     if (n - font_table_size > 1000) {
5927       error("font position too much larger than first unused position");
5928       return;
5929     }
5930     grow_font_table(n);
5931   }
5932   else if (font_table[n] != 0)
5933     delete font_table[n];
5934   font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
5935   font_family::invalidate_fontno(n);
5936 }
5937
5938 /* global functions */
5939
5940 void font_translate()
5941 {
5942   symbol from = get_name(1);
5943   if (!from.is_null()) {
5944     symbol to = get_name();
5945     if (to.is_null() || from == to)
5946       font_translation_dictionary.remove(from);
5947     else
5948       (void)font_translation_dictionary.lookup(from, (void *)to.contents());
5949   }
5950   skip_line();
5951 }
5952
5953 void font_position()
5954 {
5955   int n;
5956   if (get_integer(&n)) {
5957     if (n < 0)
5958       error("negative font position");
5959     else {
5960       symbol internal_name = get_name(1);
5961       if (!internal_name.is_null()) {
5962         symbol external_name = get_long_name();
5963         mount_font(n, internal_name, external_name); // ignore error
5964       }
5965     }
5966   }
5967   skip_line();
5968 }
5969
5970 font_family::font_family(symbol s)
5971 : map_size(10), nm(s)
5972 {
5973   map = new int[map_size];
5974   for (int i = 0; i < map_size; i++)
5975     map[i] = -1;
5976 }
5977
5978 font_family::~font_family()
5979 {
5980   a_delete map;
5981 }
5982
5983 int font_family::make_definite(int i)
5984 {
5985   if (i >= 0) {
5986     if (i < map_size && map[i] >= 0)
5987       return map[i];
5988     else {
5989       if (i < font_table_size && font_table[i] != 0) {
5990         if (i >= map_size) {
5991           int old_map_size = map_size;
5992           int *old_map = map;
5993           map_size *= 3;
5994           map_size /= 2;
5995           if (i >= map_size)
5996             map_size = i + 10;
5997           map = new int[map_size];
5998           memcpy(map, old_map, old_map_size*sizeof(int));
5999           a_delete old_map;
6000           for (int j = old_map_size; j < map_size; j++)
6001             map[j] = -1;
6002         }
6003         if (font_table[i]->is_style()) {
6004           symbol sty = font_table[i]->get_name();
6005           symbol f = concat(nm, sty);
6006           int n;
6007           // don't use symbol_fontno, because that might return a style
6008           // and because we don't want to translate the name
6009           for (n = 0; n < font_table_size; n++)
6010             if (font_table[n] != 0 && font_table[n]->is_named(f)
6011                 && !font_table[n]->is_style())
6012               break;
6013           if (n >= font_table_size) {
6014             n = next_available_font_position();
6015             if (!mount_font_no_translate(n, f, f))
6016               return -1;
6017           }
6018           return map[i] = n;
6019         }
6020         else
6021           return map[i] = i;
6022       }
6023       else
6024         return -1;
6025     }
6026   }
6027   else
6028     return -1;
6029 }
6030
6031 dictionary family_dictionary(5);
6032
6033 font_family *lookup_family(symbol nm)
6034 {
6035   font_family *f = (font_family *)family_dictionary.lookup(nm);
6036   if (!f) {
6037     f = new font_family(nm);
6038     (void)family_dictionary.lookup(nm, f);
6039   }
6040   return f;
6041 }
6042
6043 void font_family::invalidate_fontno(int n)
6044 {
6045   assert(n >= 0 && n < font_table_size);
6046   dictionary_iterator iter(family_dictionary);
6047   symbol nam;
6048   font_family *fam;
6049   while (iter.get(&nam, (void **)&fam)) {
6050     int mapsize = fam->map_size;
6051     if (n < mapsize)
6052       fam->map[n] = -1;
6053     for (int i = 0; i < mapsize; i++)
6054       if (fam->map[i] == n)
6055         fam->map[i] = -1;
6056   }
6057 }
6058
6059 void style()
6060 {
6061   int n;
6062   if (get_integer(&n)) {
6063     if (n < 0)
6064       error("negative font position");
6065     else {
6066       symbol internal_name = get_name(1);
6067       if (!internal_name.is_null())
6068         mount_style(n, internal_name);
6069     }
6070   }
6071   skip_line();
6072 }
6073
6074 static int get_fontno()
6075 {
6076   int n;
6077   tok.skip();
6078   if (tok.delimiter()) {
6079     symbol s = get_name(1);
6080     if (!s.is_null()) {
6081       n = symbol_fontno(s);
6082       if (n < 0) {
6083         n = next_available_font_position();
6084         if (!mount_font(n, s))
6085           return -1;
6086       }
6087       return curenv->get_family()->make_definite(n);
6088     }
6089   }
6090   else if (get_integer(&n)) {
6091     if (n < 0 || n >= font_table_size || font_table[n] == 0)
6092       error("bad font number");
6093     else
6094       return curenv->get_family()->make_definite(n);
6095   }
6096   return -1;
6097 }
6098
6099 static int underline_fontno = 2;
6100
6101 void underline_font()
6102 {
6103   int n = get_fontno();
6104   if (n >= 0)
6105     underline_fontno = n;
6106   skip_line();
6107 }
6108
6109 int get_underline_fontno()
6110 {
6111   return underline_fontno;
6112 }
6113
6114 void define_font_special_character()
6115 {
6116   int n = get_fontno();
6117   if (n < 0) {
6118     skip_line();
6119     return;
6120   }
6121   symbol f = font_table[n]->get_name();
6122   do_define_character(CHAR_FONT_SPECIAL, f.contents());
6123 }
6124
6125 void remove_font_special_character()
6126 {
6127   int n = get_fontno();
6128   if (n < 0) {
6129     skip_line();
6130     return;
6131   }
6132   symbol f = font_table[n]->get_name();
6133   while (!tok.newline() && !tok.eof()) {
6134     if (!tok.space() && !tok.tab()) {
6135       charinfo *s = tok.get_char(1);
6136       string gl(f.contents());
6137       gl += ' ';
6138       gl += s->nm.contents();
6139       gl += '\0';
6140       charinfo *ci = get_charinfo(symbol(gl.contents()));
6141       if (!ci)
6142         break;
6143       macro *m = ci->set_macro(0);
6144       if (m)
6145         delete m;
6146     }
6147     tok.next();
6148   }
6149   skip_line();
6150 }
6151
6152 static void read_special_fonts(special_font_list **sp)
6153 {
6154   special_font_list *s = *sp;
6155   *sp = 0;
6156   while (s != 0) {
6157     special_font_list *tem = s;
6158     s = s->next;
6159     delete tem;
6160   }
6161   special_font_list **p = sp;
6162   while (has_arg()) {
6163     int i = get_fontno();
6164     if (i >= 0) {
6165       special_font_list *tem = new special_font_list;
6166       tem->n = i;
6167       tem->next = 0;
6168       *p = tem;
6169       p = &(tem->next);
6170     }
6171   }
6172 }
6173
6174 void font_special_request()
6175 {
6176   int n = get_fontno();
6177   if (n >= 0)
6178     read_special_fonts(&font_table[n]->sf);
6179   skip_line();
6180 }
6181
6182 void special_request()
6183 {
6184   read_special_fonts(&global_special_fonts);
6185   skip_line();
6186 }
6187
6188 int next_available_font_position()
6189 {
6190   int i;
6191   for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6192     ;
6193   return i;
6194 }
6195
6196 int symbol_fontno(symbol s)
6197 {
6198   s = get_font_translation(s);
6199   for (int i = 0; i < font_table_size; i++)
6200     if (font_table[i] != 0 && font_table[i]->is_named(s))
6201       return i;
6202   return -1;
6203 }
6204
6205 int is_good_fontno(int n)
6206 {
6207   return n >= 0 && n < font_table_size && font_table[n] != 0;
6208 }
6209
6210 int get_bold_fontno(int n)
6211 {
6212   if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6213     hunits offset;
6214     if (font_table[n]->get_bold(&offset))
6215       return offset.to_units() + 1;
6216     else
6217       return 0;
6218   }
6219   else
6220     return 0;
6221 }
6222
6223 hunits env_digit_width(environment *env)
6224 {
6225   node *n = make_glyph_node(charset_table['0'], env);
6226   if (n) {
6227     hunits x = n->width();
6228     delete n;
6229     return x;
6230   }
6231   else
6232     return H0;
6233 }
6234
6235 hunits env_space_width(environment *env)
6236 {
6237   int fn = env_definite_font(env);
6238   font_size fs = env->get_font_size();
6239   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6240     return scale(fs.to_units()/3, env->get_space_size(), 12);
6241   else
6242     return font_table[fn]->get_space_width(fs, env->get_space_size());
6243 }
6244
6245 hunits env_sentence_space_width(environment *env)
6246 {
6247   int fn = env_definite_font(env);
6248   font_size fs = env->get_font_size();
6249   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6250     return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6251   else
6252     return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6253 }
6254
6255 hunits env_half_narrow_space_width(environment *env)
6256 {
6257   int fn = env_definite_font(env);
6258   font_size fs = env->get_font_size();
6259   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6260     return 0;
6261   else
6262     return font_table[fn]->get_half_narrow_space_width(fs);
6263 }
6264
6265 hunits env_narrow_space_width(environment *env)
6266 {
6267   int fn = env_definite_font(env);
6268   font_size fs = env->get_font_size();
6269   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6270     return 0;
6271   else
6272     return font_table[fn]->get_narrow_space_width(fs);
6273 }
6274
6275 void bold_font()
6276 {
6277   int n = get_fontno();
6278   if (n >= 0) {
6279     if (has_arg()) {
6280       if (tok.delimiter()) {
6281         int f = get_fontno();
6282         if (f >= 0) {
6283           units offset;
6284           if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6285             font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6286           else
6287             font_table[f]->conditional_unbold(n);
6288         }
6289       }
6290       else {
6291         units offset;
6292         if (get_number(&offset, 'u') && offset >= 1)
6293           font_table[n]->set_bold(hunits(offset - 1));
6294         else
6295           font_table[n]->unbold();
6296       }
6297     }
6298     else
6299       font_table[n]->unbold();
6300   }
6301   skip_line();
6302 }
6303
6304 track_kerning_function::track_kerning_function() : non_zero(0)
6305 {
6306 }
6307
6308 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6309                                                int max_s, hunits max_a)
6310 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6311   max_amount(max_a)
6312 {
6313 }
6314
6315 int track_kerning_function::operator==(const track_kerning_function &tk)
6316 {
6317   if (non_zero)
6318     return (tk.non_zero
6319             && min_size == tk.min_size
6320             && min_amount == tk.min_amount
6321             && max_size == tk.max_size
6322             && max_amount == tk.max_amount);
6323   else
6324     return !tk.non_zero;
6325 }
6326
6327 int track_kerning_function::operator!=(const track_kerning_function &tk)
6328 {
6329   if (non_zero)
6330     return (!tk.non_zero
6331             || min_size != tk.min_size
6332             || min_amount != tk.min_amount
6333             || max_size != tk.max_size
6334             || max_amount != tk.max_amount);
6335   else
6336     return tk.non_zero;
6337 }
6338
6339 hunits track_kerning_function::compute(int size)
6340 {
6341   if (non_zero) {
6342     if (max_size <= min_size)
6343       return min_amount;
6344     else if (size <= min_size)
6345       return min_amount;
6346     else if (size >= max_size)
6347       return max_amount;
6348     else
6349       return (scale(max_amount, size - min_size, max_size - min_size)
6350               + scale(min_amount, max_size - size, max_size - min_size));
6351   }
6352   else
6353     return H0;
6354 }
6355
6356 void track_kern()
6357 {
6358   int n = get_fontno();
6359   if (n >= 0) {
6360     int min_s, max_s;
6361     hunits min_a, max_a;
6362     if (has_arg()
6363         && get_number(&min_s, 'z')
6364         && get_hunits(&min_a, 'p')
6365         && get_number(&max_s, 'z')
6366         && get_hunits(&max_a, 'p')) {
6367       track_kerning_function tk(min_s, min_a, max_s, max_a);
6368       font_table[n]->set_track_kern(tk);
6369     }
6370     else {
6371       track_kerning_function tk;
6372       font_table[n]->set_track_kern(tk);
6373     }
6374   }
6375   skip_line();
6376 }
6377
6378 void constant_space()
6379 {
6380   int n = get_fontno();
6381   if (n >= 0) {
6382     int x, y;
6383     if (!has_arg() || !get_integer(&x))
6384       font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6385     else {
6386       if (!has_arg() || !get_number(&y, 'z'))
6387         font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6388       else
6389         font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6390                                           scale(y*x,
6391                                                 units_per_inch,
6392                                                 36*72*sizescale));
6393     }
6394   }
6395   skip_line();
6396 }
6397
6398 void ligature()
6399 {
6400   int lig;
6401   if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6402     global_ligature_mode = lig;
6403   else
6404     global_ligature_mode = 1;
6405   skip_line();
6406 }
6407
6408 void kern_request()
6409 {
6410   int k;
6411   if (has_arg() && get_integer(&k))
6412     global_kern_mode = k != 0;
6413   else
6414     global_kern_mode = 1;
6415   skip_line();
6416 }
6417
6418 void set_soft_hyphen_char()
6419 {
6420   soft_hyphen_char = get_optional_char();
6421   if (!soft_hyphen_char)
6422     soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6423   skip_line();
6424 }
6425
6426 void init_output()
6427 {
6428   if (suppress_output_flag)
6429     the_output = new suppress_output_file;
6430   else if (ascii_output_flag)
6431     the_output = new ascii_output_file;
6432   else
6433     the_output = new troff_output_file;
6434 }
6435
6436 class next_available_font_position_reg : public reg {
6437 public:
6438   const char *get_string();
6439 };
6440
6441 const char *next_available_font_position_reg::get_string()
6442 {
6443   return i_to_a(next_available_font_position());
6444 }
6445
6446 class printing_reg : public reg {
6447 public:
6448   const char *get_string();
6449 };
6450
6451 const char *printing_reg::get_string()
6452 {
6453   if (the_output)
6454     return the_output->is_printing() ? "1" : "0";
6455   else
6456     return "0";
6457 }
6458
6459 void init_node_requests()
6460 {
6461   init_request("bd", bold_font);
6462   init_request("cs", constant_space);
6463   init_request("fp", font_position);
6464   init_request("fschar", define_font_special_character);
6465   init_request("fspecial", font_special_request);
6466   init_request("ftr", font_translate);
6467   init_request("kern", kern_request);
6468   init_request("lg", ligature);
6469   init_request("rfschar", remove_font_special_character);
6470   init_request("shc", set_soft_hyphen_char);
6471   init_request("special", special_request);
6472   init_request("sty", style);
6473   init_request("tkf", track_kern);
6474   init_request("uf", underline_font);
6475   number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6476   number_reg_dictionary.define(".kern",
6477                                new constant_int_reg(&global_kern_mode));
6478   number_reg_dictionary.define(".lg",
6479                                new constant_int_reg(&global_ligature_mode));
6480   number_reg_dictionary.define(".P", new printing_reg);
6481   soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6482 }