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