3eca5232ec2fc61f8fc746e5c694ef2555cf344e
[platform/upstream/groff.git] / src / roff / troff / node.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2014  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   static glyph_node *free_list;
1851 protected:
1852   tfont *tf;
1853   color *gcol;
1854   color *fcol;          /* this is needed for grotty */
1855 #ifdef STORE_WIDTH
1856   hunits wid;
1857   glyph_node(charinfo *, tfont *, color *, color *, hunits,
1858              statem *, int, node * = 0);
1859 #endif
1860 public:
1861   void *operator new(size_t);
1862   void operator delete(void *);
1863   glyph_node(charinfo *, tfont *, color *, color *,
1864              statem *, int, node * = 0);
1865   ~glyph_node() {}
1866   node *copy();
1867   node *merge_glyph_node(glyph_node *);
1868   node *merge_self(node *);
1869   hunits width();
1870   node *last_char_node();
1871   units size();
1872   void vertical_extent(vunits *, vunits *);
1873   hunits subscript_correction();
1874   hunits italic_correction();
1875   hunits left_italic_correction();
1876   hunits skew();
1877   hyphenation_type get_hyphenation_type();
1878   tfont *get_tfont();
1879   color *get_glyph_color();
1880   color *get_fill_color();
1881   void tprint(troff_output_file *);
1882   void zero_width_tprint(troff_output_file *);
1883   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1884   node *add_self(node *, hyphen_list **);
1885   void ascii_print(ascii_output_file *);
1886   void asciify(macro *);
1887   int character_type();
1888   int same(node *);
1889   const char *type();
1890   int force_tprint();
1891   int is_tag();
1892   void debug_node();
1893 };
1894
1895 glyph_node *glyph_node::free_list = 0;
1896
1897 class ligature_node : public glyph_node {
1898   node *n1;
1899   node *n2;
1900 #ifdef STORE_WIDTH
1901   ligature_node(charinfo *, tfont *, color *, color *, hunits,
1902                 node *, node *, statem *, int, node * = 0);
1903 #endif
1904 public:
1905   void *operator new(size_t);
1906   void operator delete(void *);
1907   ligature_node(charinfo *, tfont *, color *, color *,
1908                 node *, node *, statem *, int, node * = 0);
1909   ~ligature_node();
1910   node *copy();
1911   node *add_self(node *, hyphen_list **);
1912   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1913   void ascii_print(ascii_output_file *);
1914   void asciify(macro *);
1915   int same(node *);
1916   const char *type();
1917   int force_tprint();
1918   int is_tag();
1919 };
1920
1921 class kern_pair_node : public node {
1922   hunits amount;
1923   node *n1;
1924   node *n2;
1925 public:
1926   kern_pair_node(hunits, node *, node *, statem *, int, node * = 0);
1927   ~kern_pair_node();
1928   node *copy();
1929   node *merge_glyph_node(glyph_node *);
1930   node *add_self(node *, hyphen_list **);
1931   hyphen_list *get_hyphen_list(hyphen_list *, int *);
1932   node *add_discretionary_hyphen();
1933   hunits width();
1934   node *last_char_node();
1935   hunits italic_correction();
1936   hunits subscript_correction();
1937   void tprint(troff_output_file *);
1938   hyphenation_type get_hyphenation_type();
1939   int ends_sentence();
1940   void ascii_print(ascii_output_file *);
1941   void asciify(macro *);
1942   int same(node *);
1943   const char *type();
1944   int force_tprint();
1945   int is_tag();
1946   void vertical_extent(vunits *, vunits *);
1947 };
1948
1949 class dbreak_node : public node {
1950   node *none;
1951   node *pre;
1952   node *post;
1953 public:
1954   dbreak_node(node *, node *, statem *, int, node * = 0);
1955   ~dbreak_node();
1956   node *copy();
1957   node *merge_glyph_node(glyph_node *);
1958   node *add_discretionary_hyphen();
1959   hunits width();
1960   node *last_char_node();
1961   hunits italic_correction();
1962   hunits subscript_correction();
1963   void tprint(troff_output_file *);
1964   breakpoint *get_breakpoints(hunits width, int ns, breakpoint *rest = 0,
1965                               int is_inner = 0);
1966   int nbreaks();
1967   int ends_sentence();
1968   void split(int, node **, node **);
1969   hyphenation_type get_hyphenation_type();
1970   void ascii_print(ascii_output_file *);
1971   void asciify(macro *);
1972   int same(node *);
1973   const char *type();
1974   int force_tprint();
1975   int is_tag();
1976 };
1977
1978 void *glyph_node::operator new(size_t n)
1979 {
1980   assert(n == sizeof(glyph_node));
1981   if (!free_list) {
1982     const int BLOCK = 1024;
1983     free_list = (glyph_node *)new char[sizeof(glyph_node)*BLOCK];
1984     for (int i = 0; i < BLOCK - 1; i++)
1985       free_list[i].next = free_list + i + 1;
1986     free_list[BLOCK-1].next = 0;
1987   }
1988   glyph_node *p = free_list;
1989   free_list = (glyph_node *)(free_list->next);
1990   p->next = 0;
1991   return p;
1992 }
1993
1994 void *ligature_node::operator new(size_t n)
1995 {
1996   return new char[n];
1997 }
1998
1999 void glyph_node::operator delete(void *p)
2000 {
2001   if (p) {
2002     ((glyph_node *)p)->next = free_list;
2003     free_list = (glyph_node *)p;
2004   }
2005 }
2006
2007 void ligature_node::operator delete(void *p)
2008 {
2009   delete[] (char *)p;
2010 }
2011
2012 glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
2013                        statem *s, int pop, node *x)
2014 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc)
2015 {
2016 #ifdef STORE_WIDTH
2017   wid = tf->get_width(ci);
2018 #endif
2019 }
2020
2021 #ifdef STORE_WIDTH
2022 glyph_node::glyph_node(charinfo *c, tfont *t,
2023                        color *gc, color *fc, hunits w,
2024                        statem *s, int pop, node *x)
2025 : charinfo_node(c, s, pop, x), tf(t), gcol(gc), fcol(fc), wid(w)
2026 {
2027 }
2028 #endif
2029
2030 node *glyph_node::copy()
2031 {
2032 #ifdef STORE_WIDTH
2033   return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
2034 #else
2035   return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
2036 #endif
2037 }
2038
2039 node *glyph_node::merge_self(node *nd)
2040 {
2041   return nd->merge_glyph_node(this);
2042 }
2043
2044 int glyph_node::character_type()
2045 {
2046   return tf->get_character_type(ci);
2047 }
2048
2049 node *glyph_node::add_self(node *n, hyphen_list **p)
2050 {
2051   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
2052   next = 0;
2053   node *nn;
2054   if (n == 0 || (nn = n->merge_glyph_node(this)) == 0) {
2055     next = n;
2056     nn = this;
2057   }
2058   if ((*p)->hyphen)
2059     nn = nn->add_discretionary_hyphen();
2060   hyphen_list *pp = *p;
2061   *p = (*p)->next;
2062   delete pp;
2063   return nn;
2064 }
2065
2066 units glyph_node::size()
2067 {
2068   return tf->get_size().to_units();
2069 }
2070
2071 hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
2072 {
2073   (*count)++;
2074   return new hyphen_list(ci->get_hyphenation_code(), tail);
2075 }
2076
2077 tfont *node::get_tfont()
2078 {
2079   return 0;
2080 }
2081
2082 tfont *glyph_node::get_tfont()
2083 {
2084   return tf;
2085 }
2086
2087 color *node::get_glyph_color()
2088 {
2089   return 0;
2090 }
2091
2092 color *glyph_node::get_glyph_color()
2093 {
2094   return gcol;
2095 }
2096
2097 color *node::get_fill_color()
2098 {
2099   return 0;
2100 }
2101
2102 color *glyph_node::get_fill_color()
2103 {
2104   return fcol;
2105 }
2106
2107 node *node::merge_glyph_node(glyph_node *)
2108 {
2109   return 0;
2110 }
2111
2112 node *glyph_node::merge_glyph_node(glyph_node *gn)
2113 {
2114   if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
2115     charinfo *lig;
2116     if ((lig = tf->get_lig(ci, gn->ci)) != 0) {
2117       node *next1 = next;
2118       next = 0;
2119       return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
2120                                gn->div_nest_level, next1);
2121     }
2122     hunits kern;
2123     if (tf->get_kern(ci, gn->ci, &kern)) {
2124       node *next1 = next;
2125       next = 0;
2126       return new kern_pair_node(kern, this, gn, state,
2127                                 gn->div_nest_level, next1);
2128     }
2129   }
2130   return 0;
2131 }
2132
2133 #ifdef STORE_WIDTH
2134 inline
2135 #endif
2136 hunits glyph_node::width()
2137 {
2138 #ifdef STORE_WIDTH
2139   return wid;
2140 #else
2141   return tf->get_width(ci);
2142 #endif
2143 }
2144
2145 node *glyph_node::last_char_node()
2146 {
2147   return this;
2148 }
2149
2150 void glyph_node::vertical_extent(vunits *min, vunits *max)
2151 {
2152   *min = -tf->get_char_height(ci);
2153   *max = tf->get_char_depth(ci);
2154 }
2155
2156 hunits glyph_node::skew()
2157 {
2158   return tf->get_char_skew(ci);
2159 }
2160
2161 hunits glyph_node::subscript_correction()
2162 {
2163   return tf->get_subscript_correction(ci);
2164 }
2165
2166 hunits glyph_node::italic_correction()
2167 {
2168   return tf->get_italic_correction(ci);
2169 }
2170
2171 hunits glyph_node::left_italic_correction()
2172 {
2173   return tf->get_left_italic_correction(ci);
2174 }
2175
2176 hyphenation_type glyph_node::get_hyphenation_type()
2177 {
2178   return HYPHEN_MIDDLE;
2179 }
2180
2181 void glyph_node::ascii_print(ascii_output_file *ascii)
2182 {
2183   unsigned char c = ci->get_ascii_code();
2184   if (c != 0)
2185     ascii->outc(c);
2186   else
2187     ascii->outs(ci->nm.contents());
2188 }
2189
2190 void glyph_node::debug_node()
2191 {
2192   unsigned char c = ci->get_ascii_code();
2193   fprintf(stderr, "{ %s [", type());
2194   if (c)
2195     fprintf(stderr, "%c", c);
2196   else
2197     fprintf(stderr, "%s", ci->nm.contents());
2198   if (push_state)
2199     fprintf(stderr, " <push_state>");
2200   if (state)
2201     state->display_state();
2202   fprintf(stderr, " nest level %d", div_nest_level);
2203   fprintf(stderr, "]}\n");
2204   fflush(stderr);
2205 }
2206
2207 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2208                              node *gn1, node *gn2, statem *s,
2209                              int pop, node *x)
2210 : glyph_node(c, t, gc, fc, s, pop, x), n1(gn1), n2(gn2)
2211 {
2212 }
2213
2214 #ifdef STORE_WIDTH
2215 ligature_node::ligature_node(charinfo *c, tfont *t, color *gc, color *fc,
2216                              hunits w, node *gn1, node *gn2, statem *s,
2217                              int pop, node *x)
2218 : glyph_node(c, t, gc, fc, w, s, pop, x), n1(gn1), n2(gn2)
2219 {
2220 }
2221 #endif
2222
2223 ligature_node::~ligature_node()
2224 {
2225   delete n1;
2226   delete n2;
2227 }
2228
2229 node *ligature_node::copy()
2230 {
2231 #ifdef STORE_WIDTH
2232   return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(), n2->copy(),
2233                            state, div_nest_level);
2234 #else
2235   return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
2236                            state, div_nest_level);
2237 #endif
2238 }
2239
2240 void ligature_node::ascii_print(ascii_output_file *ascii)
2241 {
2242   n1->ascii_print(ascii);
2243   n2->ascii_print(ascii);
2244 }
2245
2246 hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail, int *count)
2247 {
2248   hyphen_list *hl = n2->get_hyphen_list(tail, count);
2249   return n1->get_hyphen_list(hl, count);
2250 }
2251
2252 node *ligature_node::add_self(node *n, hyphen_list **p)
2253 {
2254   n = n1->add_self(n, p);
2255   n = n2->add_self(n, p);
2256   n1 = n2 = 0;
2257   delete this;
2258   return n;
2259 }
2260
2261 kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
2262                                statem* s, int pop, node *x)
2263 : node(x, s, pop), amount(n), n1(first), n2(second)
2264 {
2265 }
2266
2267 dbreak_node::dbreak_node(node *n, node *p, statem *s, int pop, node *x)
2268 : node(x, s, pop), none(n), pre(p), post(0)
2269 {
2270 }
2271
2272 node *dbreak_node::merge_glyph_node(glyph_node *gn)
2273 {
2274   glyph_node *gn2 = (glyph_node *)gn->copy();
2275   node *new_none = none ? none->merge_glyph_node(gn) : 0;
2276   node *new_post = post ? post->merge_glyph_node(gn2) : 0;
2277   if (new_none == 0 && new_post == 0) {
2278     delete gn2;
2279     return 0;
2280   }
2281   if (new_none != 0)
2282     none = new_none;
2283   else {
2284     gn->next = none;
2285     none = gn;
2286   }
2287   if (new_post != 0)
2288     post = new_post;
2289   else {
2290     gn2->next = post;
2291     post = gn2;
2292   }
2293   return this;
2294 }
2295
2296 node *kern_pair_node::merge_glyph_node(glyph_node *gn)
2297 {
2298   node *nd = n2->merge_glyph_node(gn);
2299   if (nd == 0)
2300     return 0;
2301   n2 = nd;
2302   nd = n2->merge_self(n1);
2303   if (nd) {
2304     nd->next = next;
2305     n1 = 0;
2306     n2 = 0;
2307     delete this;
2308     return nd;
2309   }
2310   return this;
2311 }
2312
2313 hunits kern_pair_node::italic_correction()
2314 {
2315   return n2->italic_correction();
2316 }
2317
2318 hunits kern_pair_node::subscript_correction()
2319 {
2320   return n2->subscript_correction();
2321 }
2322
2323 void kern_pair_node::vertical_extent(vunits *min, vunits *max)
2324 {
2325   n1->vertical_extent(min, max);
2326   vunits min2, max2;
2327   n2->vertical_extent(&min2, &max2);
2328   if (min2 < *min)
2329     *min = min2;
2330   if (max2 > *max)
2331     *max = max2;
2332 }
2333
2334 node *kern_pair_node::add_discretionary_hyphen()
2335 {
2336   tfont *tf = n1->get_tfont();
2337   if (tf) {
2338     if (tf->contains(soft_hyphen_char)) {
2339       color *gcol = n2->get_glyph_color();
2340       color *fcol = n2->get_fill_color();
2341       node *next1 = next;
2342       next = 0;
2343       node *n = copy();
2344       glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2345                                       state, div_nest_level);
2346       node *nn = n->merge_glyph_node(gn);
2347       if (nn == 0) {
2348         gn->next = n;
2349         nn = gn;
2350       }
2351       return new dbreak_node(this, nn, state, div_nest_level, next1);
2352     }
2353   }
2354   return this;
2355 }
2356
2357 kern_pair_node::~kern_pair_node()
2358 {
2359   if (n1 != 0)
2360     delete n1;
2361   if (n2 != 0)
2362     delete n2;
2363 }
2364
2365 dbreak_node::~dbreak_node()
2366 {
2367   delete_node_list(pre);
2368   delete_node_list(post);
2369   delete_node_list(none);
2370 }
2371
2372 node *kern_pair_node::copy()
2373 {
2374   return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
2375                             div_nest_level);
2376 }
2377
2378 node *copy_node_list(node *n)
2379 {
2380   node *p = 0;
2381   while (n != 0) {
2382     node *nn = n->copy();
2383     nn->next = p;
2384     p = nn;
2385     n = n->next;
2386   }
2387   while (p != 0) {
2388     node *pp = p->next;
2389     p->next = n;
2390     n = p;
2391     p = pp;
2392   }
2393   return n;
2394 }
2395
2396 void delete_node_list(node *n)
2397 {
2398   while (n != 0) {
2399     node *tem = n;
2400     n = n->next;
2401     delete tem;
2402   }
2403 }
2404
2405 node *dbreak_node::copy()
2406 {
2407   dbreak_node *p = new dbreak_node(copy_node_list(none), copy_node_list(pre),
2408                                    state, div_nest_level);
2409   p->post = copy_node_list(post);
2410   return p;
2411 }
2412
2413 hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
2414 {
2415   return tail;
2416 }
2417
2418 hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail, int *count)
2419 {
2420   hyphen_list *hl = n2->get_hyphen_list(tail, count);
2421   return n1->get_hyphen_list(hl, count);
2422 }
2423
2424 class hyphen_inhibitor_node : public node {
2425 public:
2426   hyphen_inhibitor_node(node * = 0);
2427   node *copy();
2428   int same(node *);
2429   const char *type();
2430   int force_tprint();
2431   int is_tag();
2432   hyphenation_type get_hyphenation_type();
2433 };
2434
2435 hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
2436 {
2437 }
2438
2439 node *hyphen_inhibitor_node::copy()
2440 {
2441   return new hyphen_inhibitor_node;
2442 }
2443
2444 int hyphen_inhibitor_node::same(node *)
2445 {
2446   return 1;
2447 }
2448
2449 const char *hyphen_inhibitor_node::type()
2450 {
2451   return "hyphen_inhibitor_node";
2452 }
2453
2454 int hyphen_inhibitor_node::force_tprint()
2455 {
2456   return 0;
2457 }
2458
2459 int hyphen_inhibitor_node::is_tag()
2460 {
2461   return 0;
2462 }
2463
2464 hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
2465 {
2466   return HYPHEN_INHIBIT;
2467 }
2468
2469 /* add_discretionary_hyphen methods */
2470
2471 node *dbreak_node::add_discretionary_hyphen()
2472 {
2473   if (post)
2474     post = post->add_discretionary_hyphen();
2475   if (none)
2476     none = none->add_discretionary_hyphen();
2477   return this;
2478 }
2479
2480 node *node::add_discretionary_hyphen()
2481 {
2482   tfont *tf = get_tfont();
2483   if (!tf)
2484     return new hyphen_inhibitor_node(this);
2485   if (tf->contains(soft_hyphen_char)) {
2486     color *gcol = get_glyph_color();
2487     color *fcol = get_fill_color();
2488     node *next1 = next;
2489     next = 0;
2490     node *n = copy();
2491     glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
2492                                     state, div_nest_level);
2493     node *n1 = n->merge_glyph_node(gn);
2494     if (n1 == 0) {
2495       gn->next = n;
2496       n1 = gn;
2497     }
2498     return new dbreak_node(this, n1, state, div_nest_level, next1);
2499   }
2500   return this;
2501 }
2502
2503 node *node::merge_self(node *)
2504 {
2505   return 0;
2506 }
2507
2508 node *node::add_self(node *n, hyphen_list ** /*p*/)
2509 {
2510   next = n;
2511   return this;
2512 }
2513
2514 node *kern_pair_node::add_self(node *n, hyphen_list **p)
2515 {
2516   n = n1->add_self(n, p);
2517   n = n2->add_self(n, p);
2518   n1 = n2 = 0;
2519   delete this;
2520   return n;
2521 }
2522
2523 hunits node::width()
2524 {
2525   return H0;
2526 }
2527
2528 node *node::last_char_node()
2529 {
2530   return 0;
2531 }
2532
2533 int node::force_tprint()
2534 {
2535   return 0;
2536 }
2537
2538 int node::is_tag()
2539 {
2540   return 0;
2541 }
2542
2543 int node::get_break_code()
2544 {
2545   return 0;
2546 }
2547
2548 hunits hmotion_node::width()
2549 {
2550   return n;
2551 }
2552
2553 units node::size()
2554 {
2555   return points_to_units(10);
2556 }
2557
2558 void node::debug_node()
2559 {
2560   fprintf(stderr, "{ %s ", type());
2561   if (push_state)
2562     fprintf(stderr, " <push_state>");
2563   if (state)
2564     fprintf(stderr, " <state>");
2565   fprintf(stderr, " nest level %d", div_nest_level);
2566   fprintf(stderr, " }\n");
2567   fflush(stderr);
2568 }
2569
2570 void node::debug_node_list()
2571 {
2572   node *n = next;
2573
2574   debug_node();
2575   while (n != 0) {
2576     n->debug_node();
2577     n = n->next;
2578   }
2579 }
2580
2581 hunits kern_pair_node::width()
2582 {
2583   return n1->width() + n2->width() + amount;
2584 }
2585
2586 node *kern_pair_node::last_char_node()
2587 {
2588   node *nd = n2->last_char_node();
2589   if (nd)
2590     return nd;
2591   return n1->last_char_node();
2592 }
2593
2594 hunits dbreak_node::width()
2595 {
2596   hunits x = H0;
2597   for (node *n = none; n != 0; n = n->next)
2598     x += n->width();
2599   return x;
2600 }
2601
2602 node *dbreak_node::last_char_node()
2603 {
2604   for (node *n = none; n; n = n->next) {
2605     node *last_node = n->last_char_node();
2606     if (last_node)
2607       return last_node;
2608   }
2609   return 0;
2610 }
2611
2612 hunits dbreak_node::italic_correction()
2613 {
2614   return none ? none->italic_correction() : H0;
2615 }
2616
2617 hunits dbreak_node::subscript_correction()
2618 {
2619   return none ? none->subscript_correction() : H0;
2620 }
2621
2622 class italic_corrected_node : public node {
2623   node *n;
2624   hunits x;
2625 public:
2626   italic_corrected_node(node *, hunits, statem *, int, node * = 0);
2627   ~italic_corrected_node();
2628   node *copy();
2629   void ascii_print(ascii_output_file *);
2630   void asciify(macro *);
2631   hunits width();
2632   node *last_char_node();
2633   void vertical_extent(vunits *, vunits *);
2634   int ends_sentence();
2635   int overlaps_horizontally();
2636   int overlaps_vertically();
2637   int same(node *);
2638   hyphenation_type get_hyphenation_type();
2639   tfont *get_tfont();
2640   hyphen_list *get_hyphen_list(hyphen_list *, int *);
2641   int character_type();
2642   void tprint(troff_output_file *);
2643   hunits subscript_correction();
2644   hunits skew();
2645   node *add_self(node *, hyphen_list **);
2646   const char *type();
2647   int force_tprint();
2648   int is_tag();
2649 };
2650
2651 node *node::add_italic_correction(hunits *wd)
2652 {
2653   hunits ic = italic_correction();
2654   if (ic.is_zero())
2655     return this;
2656   else {
2657     node *next1 = next;
2658     next = 0;
2659     *wd += ic;
2660     return new italic_corrected_node(this, ic, state, div_nest_level, next1);
2661   }
2662 }
2663
2664 italic_corrected_node::italic_corrected_node(node *nn, hunits xx, statem *s,
2665                                              int pop, node *p)
2666 : node(p, s, pop), n(nn), x(xx)
2667 {
2668   assert(n != 0);
2669 }
2670
2671 italic_corrected_node::~italic_corrected_node()
2672 {
2673   delete n;
2674 }
2675
2676 node *italic_corrected_node::copy()
2677 {
2678   return new italic_corrected_node(n->copy(), x, state, div_nest_level);
2679 }
2680
2681 hunits italic_corrected_node::width()
2682 {
2683   return n->width() + x;
2684 }
2685
2686 void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
2687 {
2688   n->vertical_extent(min, max);
2689 }
2690
2691 void italic_corrected_node::tprint(troff_output_file *out)
2692 {
2693   n->tprint(out);
2694   out->right(x);
2695 }
2696
2697 hunits italic_corrected_node::skew()
2698 {
2699   return n->skew() - x/2;
2700 }
2701
2702 hunits italic_corrected_node::subscript_correction()
2703 {
2704   return n->subscript_correction() - x;
2705 }
2706
2707 void italic_corrected_node::ascii_print(ascii_output_file *out)
2708 {
2709   n->ascii_print(out);
2710 }
2711
2712 int italic_corrected_node::ends_sentence()
2713 {
2714   return n->ends_sentence();
2715 }
2716
2717 int italic_corrected_node::overlaps_horizontally()
2718 {
2719   return n->overlaps_horizontally();
2720 }
2721
2722 int italic_corrected_node::overlaps_vertically()
2723 {
2724   return n->overlaps_vertically();
2725 }
2726
2727 node *italic_corrected_node::last_char_node()
2728 {
2729   return n->last_char_node();
2730 }
2731
2732 tfont *italic_corrected_node::get_tfont()
2733 {
2734   return n->get_tfont();
2735 }
2736
2737 hyphenation_type italic_corrected_node::get_hyphenation_type()
2738 {
2739   return n->get_hyphenation_type();
2740 }
2741
2742 node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
2743 {
2744   nd = n->add_self(nd, p);
2745   hunits not_interested;
2746   nd = nd->add_italic_correction(&not_interested);
2747   n = 0;
2748   delete this;
2749   return nd;
2750 }
2751
2752 hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
2753                                                     int *count)
2754 {
2755   return n->get_hyphen_list(tail, count);
2756 }
2757
2758 int italic_corrected_node::character_type()
2759 {
2760   return n->character_type();
2761 }
2762
2763 class break_char_node : public node {
2764   node *ch;
2765   char break_code;
2766   char prev_break_code;
2767   color *col;
2768 public:
2769   break_char_node(node *, int, int, color *, node * = 0);
2770   break_char_node(node *, int, int, color *, statem *, int, node * = 0);
2771   ~break_char_node();
2772   node *copy();
2773   hunits width();
2774   vunits vertical_width();
2775   node *last_char_node();
2776   int character_type();
2777   int ends_sentence();
2778   node *add_self(node *, hyphen_list **);
2779   hyphen_list *get_hyphen_list(hyphen_list *, int *);
2780   void tprint(troff_output_file *);
2781   void zero_width_tprint(troff_output_file *);
2782   void ascii_print(ascii_output_file *);
2783   void asciify(macro *);
2784   hyphenation_type get_hyphenation_type();
2785   int overlaps_vertically();
2786   int overlaps_horizontally();
2787   units size();
2788   tfont *get_tfont();
2789   int same(node *);
2790   const char *type();
2791   int force_tprint();
2792   int is_tag();
2793   int get_break_code();
2794 };
2795
2796 break_char_node::break_char_node(node *n, int bc, int pbc, color *c, node *x)
2797 : node(x), ch(n), break_code(bc), prev_break_code(pbc), col(c)
2798 {
2799 }
2800
2801 break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
2802                                  statem *s, int pop, node *x)
2803 : node(x, s, pop), ch(n), break_code(bc), prev_break_code(pbc), col(c)
2804 {
2805 }
2806
2807 break_char_node::~break_char_node()
2808 {
2809   delete ch;
2810 }
2811
2812 node *break_char_node::copy()
2813 {
2814   return new break_char_node(ch->copy(), break_code, prev_break_code,
2815                              col, state, div_nest_level);
2816 }
2817
2818 hunits break_char_node::width()
2819 {
2820   return ch->width();
2821 }
2822
2823 vunits break_char_node::vertical_width()
2824 {
2825   return ch->vertical_width();
2826 }
2827
2828 node *break_char_node::last_char_node()
2829 {
2830   return ch->last_char_node();
2831 }
2832
2833 int break_char_node::character_type()
2834 {
2835   return ch->character_type();
2836 }
2837
2838 int break_char_node::ends_sentence()
2839 {
2840   return ch->ends_sentence();
2841 }
2842
2843 enum break_char_type {
2844   CAN_BREAK_BEFORE = 0x01,
2845   CAN_BREAK_AFTER = 0x02,
2846   IGNORE_HCODES = 0x04,
2847   PROHIBIT_BREAK_BEFORE = 0x08,
2848   PROHIBIT_BREAK_AFTER = 0x10,
2849   INTER_CHAR_SPACE = 0x20
2850 };
2851
2852 node *break_char_node::add_self(node *n, hyphen_list **p)
2853 {
2854   int have_space_node = 0;
2855   assert((*p)->hyphenation_code == 0);
2856   if (break_code & CAN_BREAK_BEFORE) {
2857     if ((*p)->breakable || break_code & IGNORE_HCODES) {
2858       n = new space_node(H0, col, n);
2859       n->freeze_space();
2860       have_space_node = 1;
2861     }
2862   }
2863   if (!have_space_node) {
2864     if (prev_break_code & INTER_CHAR_SPACE
2865         || prev_break_code & PROHIBIT_BREAK_AFTER) {
2866       if (break_code & PROHIBIT_BREAK_BEFORE)
2867         // stretchable zero-width space not implemented yet
2868         ;
2869       else {
2870         // breakable, stretchable zero-width space not implemented yet
2871         n = new space_node(H0, col, n);
2872         n->freeze_space();
2873       }
2874     }
2875   }
2876   next = n;
2877   n = this;
2878   if (break_code & CAN_BREAK_AFTER) {
2879     if ((*p)->breakable || break_code & IGNORE_HCODES) {
2880       n = new space_node(H0, col, n);
2881       n->freeze_space();
2882     }
2883   }
2884   hyphen_list *pp = *p;
2885   *p = (*p)->next;
2886   delete pp;
2887   return n;
2888 }
2889
2890 hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
2891 {
2892   return new hyphen_list(0, tail);
2893 }
2894
2895 hyphenation_type break_char_node::get_hyphenation_type()
2896 {
2897   return HYPHEN_MIDDLE;
2898 }
2899
2900 void break_char_node::ascii_print(ascii_output_file *ascii)
2901 {
2902   ch->ascii_print(ascii);
2903 }
2904
2905 int break_char_node::overlaps_vertically()
2906 {
2907   return ch->overlaps_vertically();
2908 }
2909
2910 int break_char_node::overlaps_horizontally()
2911 {
2912   return ch->overlaps_horizontally();
2913 }
2914
2915 units break_char_node::size()
2916 {
2917   return ch->size();
2918 }
2919
2920 tfont *break_char_node::get_tfont()
2921 {
2922   return ch->get_tfont();
2923 }
2924
2925 node *extra_size_node::copy()
2926 {
2927   return new extra_size_node(n, state, div_nest_level);
2928 }
2929
2930 extra_size_node::extra_size_node(vunits i, statem *s, int pop)
2931 : node(0, s, pop), n(i)
2932 {
2933 }
2934
2935 extra_size_node::extra_size_node(vunits i)
2936 : n(i)
2937 {
2938 }
2939
2940 node *vertical_size_node::copy()
2941 {
2942   return new vertical_size_node(n, state, div_nest_level);
2943 }
2944
2945 vertical_size_node::vertical_size_node(vunits i, statem *s, int pop)
2946 : node(0, s, pop), n(i)
2947 {
2948 }
2949
2950 vertical_size_node::vertical_size_node(vunits i)
2951 : n(i)
2952 {
2953 }
2954
2955 node *hmotion_node::copy()
2956 {
2957   return new hmotion_node(n, was_tab, unformat, col, state, div_nest_level);
2958 }
2959
2960 node *space_char_hmotion_node::copy()
2961 {
2962   return new space_char_hmotion_node(n, col, state, div_nest_level);
2963 }
2964
2965 vmotion_node::vmotion_node(vunits i, color *c)
2966 : n(i), col(c)
2967 {
2968 }
2969
2970 vmotion_node::vmotion_node(vunits i, color *c, statem *s, int pop)
2971 : node(0, s, pop), n(i), col(c)
2972 {
2973 }
2974
2975 node *vmotion_node::copy()
2976 {
2977   return new vmotion_node(n, col, state, div_nest_level);
2978 }
2979
2980 node *dummy_node::copy()
2981 {
2982   return new dummy_node;
2983 }
2984
2985 node *transparent_dummy_node::copy()
2986 {
2987   return new transparent_dummy_node;
2988 }
2989
2990 hline_node::~hline_node()
2991 {
2992   if (n)
2993     delete n;
2994 }
2995
2996 hline_node::hline_node(hunits i, node *c, node *nxt)
2997 : node(nxt), x(i), n(c)
2998 {
2999 }
3000
3001 hline_node::hline_node(hunits i, node *c, statem *s, int pop, node *nxt)
3002 : node(nxt, s, pop), x(i), n(c)
3003 {
3004 }
3005
3006 node *hline_node::copy()
3007 {
3008   return new hline_node(x, n ? n->copy() : 0, state, div_nest_level);
3009 }
3010
3011 hunits hline_node::width()
3012 {
3013   return x < H0 ? H0 : x;
3014 }
3015
3016 vline_node::vline_node(vunits i, node *c, node *nxt)
3017 : node(nxt), x(i), n(c)
3018 {
3019 }
3020
3021 vline_node::vline_node(vunits i, node *c, statem *s, int pop, node *nxt)
3022 : node(nxt, s, pop), x(i), n(c)
3023 {
3024 }
3025
3026 vline_node::~vline_node()
3027 {
3028   if (n)
3029     delete n;
3030 }
3031
3032 node *vline_node::copy()
3033 {
3034   return new vline_node(x, n ? n->copy() : 0, state, div_nest_level);
3035 }
3036
3037 hunits vline_node::width()
3038 {
3039   return n == 0 ? H0 : n->width();
3040 }
3041
3042 zero_width_node::zero_width_node(node *nd, statem *s, int pop)
3043 : node(0, s, pop), n(nd)
3044 {
3045 }
3046
3047 zero_width_node::zero_width_node(node *nd)
3048 : n(nd)
3049 {
3050 }
3051
3052 zero_width_node::~zero_width_node()
3053 {
3054   delete_node_list(n);
3055 }
3056
3057 node *zero_width_node::copy()
3058 {
3059   return new zero_width_node(copy_node_list(n), state, div_nest_level);
3060 }
3061
3062 int node_list_character_type(node *p)
3063 {
3064   int t = 0;
3065   for (; p; p = p->next)
3066     t |= p->character_type();
3067   return t;
3068 }
3069
3070 int zero_width_node::character_type()
3071 {
3072   return node_list_character_type(n);
3073 }
3074
3075 void node_list_vertical_extent(node *p, vunits *min, vunits *max)
3076 {
3077   *min = V0;
3078   *max = V0;
3079   vunits cur_vpos = V0;
3080   vunits v1, v2;
3081   for (; p; p = p->next) {
3082     p->vertical_extent(&v1, &v2);
3083     v1 += cur_vpos;
3084     if (v1 < *min)
3085       *min = v1;
3086     v2 += cur_vpos;
3087     if (v2 > *max)
3088       *max = v2;
3089     cur_vpos += p->vertical_width();
3090   }
3091 }
3092
3093 void zero_width_node::vertical_extent(vunits *min, vunits *max)
3094 {
3095   node_list_vertical_extent(n, min, max);
3096 }
3097
3098 overstrike_node::overstrike_node()
3099 : list(0), max_width(H0)
3100 {
3101 }
3102
3103 overstrike_node::overstrike_node(statem *s, int pop)
3104 : node(0, s, pop), list(0), max_width(H0)
3105 {
3106 }
3107
3108 overstrike_node::~overstrike_node()
3109 {
3110   delete_node_list(list);
3111 }
3112
3113 node *overstrike_node::copy()
3114 {
3115   overstrike_node *on = new overstrike_node(state, div_nest_level);
3116   for (node *tem = list; tem; tem = tem->next)
3117     on->overstrike(tem->copy());
3118   return on;
3119 }
3120
3121 void overstrike_node::overstrike(node *n)
3122 {
3123   if (n == 0)
3124     return;
3125   hunits w = n->width();
3126   if (w > max_width)
3127     max_width = w;
3128   node **p;
3129   for (p = &list; *p; p = &(*p)->next)
3130     ;
3131   n->next = 0;
3132   *p = n;
3133 }
3134
3135 hunits overstrike_node::width()
3136 {
3137   return max_width;
3138 }
3139
3140 bracket_node::bracket_node()
3141 : list(0), max_width(H0)
3142 {
3143 }
3144
3145 bracket_node::bracket_node(statem *s, int pop)
3146 : node(0, s, pop), list(0), max_width(H0)
3147 {
3148 }
3149
3150 bracket_node::~bracket_node()
3151 {
3152   delete_node_list(list);
3153 }
3154
3155 node *bracket_node::copy()
3156 {
3157   bracket_node *on = new bracket_node(state, div_nest_level);
3158   node *last_node = 0;
3159   node *tem;
3160   if (list)
3161     list->last = 0;
3162   for (tem = list; tem; tem = tem->next) {
3163     if (tem->next)
3164       tem->next->last = tem;
3165     last_node = tem;
3166   }
3167   for (tem = last_node; tem; tem = tem->last)
3168     on->bracket(tem->copy());
3169   return on;
3170 }
3171
3172 void bracket_node::bracket(node *n)
3173 {
3174   if (n == 0)
3175     return;
3176   hunits w = n->width();
3177   if (w > max_width)
3178     max_width = w;
3179   n->next = list;
3180   list = n;
3181 }
3182
3183 hunits bracket_node::width()
3184 {
3185   return max_width;
3186 }
3187
3188 int node::nspaces()
3189 {
3190   return 0;
3191 }
3192
3193 int node::merge_space(hunits, hunits, hunits)
3194 {
3195   return 0;
3196 }
3197
3198 #if 0
3199 space_node *space_node::free_list = 0;
3200
3201 void *space_node::operator new(size_t n)
3202 {
3203   assert(n == sizeof(space_node));
3204   if (!free_list) {
3205     free_list = (space_node *)new char[sizeof(space_node)*BLOCK];
3206     for (int i = 0; i < BLOCK - 1; i++)
3207       free_list[i].next = free_list + i + 1;
3208     free_list[BLOCK-1].next = 0;
3209   }
3210   space_node *p = free_list;
3211   free_list = (space_node *)(free_list->next);
3212   p->next = 0;
3213   return p;
3214 }
3215
3216 inline void space_node::operator delete(void *p)
3217 {
3218   if (p) {
3219     ((space_node *)p)->next = free_list;
3220     free_list = (space_node *)p;
3221   }
3222 }
3223 #endif
3224
3225 space_node::space_node(hunits nn, color *c, node *p)
3226 : node(p, 0, 0), n(nn), set(0), was_escape_colon(0), col(c)
3227 {
3228 }
3229
3230 space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
3231                        int pop, node *p)
3232 : node(p, st, pop), n(nn), set(s), was_escape_colon(flag), col(c)
3233 {
3234 }
3235
3236 #if 0
3237 space_node::~space_node()
3238 {
3239 }
3240 #endif
3241
3242 node *space_node::copy()
3243 {
3244   return new space_node(n, set, was_escape_colon, col, state, div_nest_level);
3245 }
3246
3247 int space_node::force_tprint()
3248 {
3249   return 0;
3250 }
3251
3252 int space_node::is_tag()
3253 {
3254   return 0;
3255 }
3256
3257 int space_node::nspaces()
3258 {
3259   return set ? 0 : 1;
3260 }
3261
3262 int space_node::merge_space(hunits h, hunits, hunits)
3263 {
3264   n += h;
3265   return 1;
3266 }
3267
3268 hunits space_node::width()
3269 {
3270   return n;
3271 }
3272
3273 void node::spread_space(int*, hunits*)
3274 {
3275 }
3276
3277 void space_node::spread_space(int *n_spaces, hunits *desired_space)
3278 {
3279   if (!set) {
3280     assert(*n_spaces > 0);
3281     if (*n_spaces == 1) {
3282       n += *desired_space;
3283       *desired_space = H0;
3284     }
3285     else {
3286       hunits extra = *desired_space / *n_spaces;
3287       *desired_space -= extra;
3288       n += extra;
3289     }
3290     *n_spaces -= 1;
3291     set = 1;
3292   }
3293 }
3294
3295 void node::freeze_space()
3296 {
3297 }
3298
3299 void space_node::freeze_space()
3300 {
3301   set = 1;
3302 }
3303
3304 void node::is_escape_colon()
3305 {
3306 }
3307
3308 void space_node::is_escape_colon()
3309 {
3310   was_escape_colon = 1;
3311 }
3312
3313 diverted_space_node::diverted_space_node(vunits d, statem *s, int pop,
3314                                          node *p)
3315 : node(p, s, pop), n(d)
3316 {
3317 }
3318
3319 diverted_space_node::diverted_space_node(vunits d, node *p)
3320 : node(p), n(d)
3321 {
3322 }
3323
3324 node *diverted_space_node::copy()
3325 {
3326   return new diverted_space_node(n, state, div_nest_level);
3327 }
3328
3329 diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
3330                                                  int pop, node *p)
3331 : node(p, st, pop), filename(s)
3332 {
3333 }
3334
3335 diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
3336 : node(p), filename(s)
3337 {
3338 }
3339
3340 node *diverted_copy_file_node::copy()
3341 {
3342   return new diverted_copy_file_node(filename, state, div_nest_level);
3343 }
3344
3345 int node::ends_sentence()
3346 {
3347   return 0;
3348 }
3349
3350 int kern_pair_node::ends_sentence()
3351 {
3352   switch (n2->ends_sentence()) {
3353   case 0:
3354     return 0;
3355   case 1:
3356     return 1;
3357   case 2:
3358     break;
3359   default:
3360     assert(0);
3361   }
3362   return n1->ends_sentence();
3363 }
3364
3365 int node_list_ends_sentence(node *n)
3366 {
3367   for (; n != 0; n = n->next)
3368     switch (n->ends_sentence()) {
3369     case 0:
3370       return 0;
3371     case 1:
3372       return 1;
3373     case 2:
3374       break;
3375     default:
3376       assert(0);
3377     }
3378   return 2;
3379 }
3380
3381 int dbreak_node::ends_sentence()
3382 {
3383   return node_list_ends_sentence(none);
3384 }
3385
3386 int node::overlaps_horizontally()
3387 {
3388   return 0;
3389 }
3390
3391 int node::overlaps_vertically()
3392 {
3393   return 0;
3394 }
3395
3396 int node::discardable()
3397 {
3398   return 0;
3399 }
3400
3401 int space_node::discardable()
3402 {
3403   return set ? 0 : 1;
3404 }
3405
3406 vunits node::vertical_width()
3407 {
3408   return V0;
3409 }
3410
3411 vunits vline_node::vertical_width()
3412 {
3413   return x;
3414 }
3415
3416 vunits vmotion_node::vertical_width()
3417 {
3418   return n;
3419 }
3420
3421 int node::set_unformat_flag()
3422 {
3423   return 1;
3424 }
3425
3426 int node::character_type()
3427 {
3428   return 0;
3429 }
3430
3431 hunits node::subscript_correction()
3432 {
3433   return H0;
3434 }
3435
3436 hunits node::italic_correction()
3437 {
3438   return H0;
3439 }
3440
3441 hunits node::left_italic_correction()
3442 {
3443   return H0;
3444 }
3445
3446 hunits node::skew()
3447 {
3448   return H0;
3449 }
3450
3451 /* vertical_extent methods */
3452
3453 void node::vertical_extent(vunits *min, vunits *max)
3454 {
3455   vunits v = vertical_width();
3456   if (v < V0) {
3457     *min = v;
3458     *max = V0;
3459   }
3460   else {
3461     *max = v;
3462     *min = V0;
3463   }
3464 }
3465
3466 void vline_node::vertical_extent(vunits *min, vunits *max)
3467 {
3468   if (n == 0)
3469     node::vertical_extent(min, max);
3470   else {
3471     vunits cmin, cmax;
3472     n->vertical_extent(&cmin, &cmax);
3473     vunits h = n->size();
3474     if (x < V0) {
3475       if (-x < h) {
3476         *min = x;
3477         *max = V0;
3478       }
3479       else {
3480         // we print the first character and then move up, so
3481         *max = cmax;
3482         // we print the last character and then move up h
3483         *min = cmin + h;
3484         if (*min > V0)
3485           *min = V0;
3486         *min += x;
3487       }
3488     }
3489     else {
3490       if (x < h) {
3491         *max = x;
3492         *min = V0;
3493       }
3494       else {
3495         // we move down by h and then print the first character, so
3496         *min = cmin + h;
3497         if (*min > V0)
3498           *min = V0;
3499         *max = x + cmax;
3500       }
3501     }
3502   }
3503 }
3504
3505 /* ascii_print methods */
3506
3507 static void ascii_print_reverse_node_list(ascii_output_file *ascii, node *n)
3508 {
3509   if (n == 0)
3510     return;
3511   ascii_print_reverse_node_list(ascii, n->next);
3512   n->ascii_print(ascii);
3513 }
3514
3515 void dbreak_node::ascii_print(ascii_output_file *ascii)
3516 {
3517   ascii_print_reverse_node_list(ascii, none);
3518 }
3519
3520 void kern_pair_node::ascii_print(ascii_output_file *ascii)
3521 {
3522   n1->ascii_print(ascii);
3523   n2->ascii_print(ascii);
3524 }
3525
3526 void node::ascii_print(ascii_output_file *)
3527 {
3528 }
3529
3530 void space_node::ascii_print(ascii_output_file *ascii)
3531 {
3532   if (!n.is_zero())
3533     ascii->outc(' ');
3534 }
3535
3536 void hmotion_node::ascii_print(ascii_output_file *ascii)
3537 {
3538   // this is pretty arbitrary
3539   if (n >= points_to_units(2))
3540     ascii->outc(' ');
3541 }
3542
3543 void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
3544 {
3545   ascii->outc(' ');
3546 }
3547
3548 /* asciify methods */
3549
3550 void node::asciify(macro *m)
3551 {
3552   m->append(this);
3553 }
3554
3555 void glyph_node::asciify(macro *m)
3556 {
3557   unsigned char c = ci->get_asciify_code();
3558   if (c == 0)
3559     c = ci->get_ascii_code();
3560   if (c != 0) {
3561     m->append(c);
3562     delete this;
3563   }
3564   else
3565     m->append(this);
3566 }
3567
3568 void kern_pair_node::asciify(macro *m)
3569 {
3570   n1->asciify(m);
3571   n2->asciify(m);
3572   n1 = n2 = 0;
3573   delete this;
3574 }
3575
3576 static void asciify_reverse_node_list(macro *m, node *n)
3577 {
3578   if (n == 0)
3579     return;
3580   asciify_reverse_node_list(m, n->next);
3581   n->asciify(m);
3582 }
3583
3584 void dbreak_node::asciify(macro *m)
3585 {
3586   asciify_reverse_node_list(m, none);
3587   none = 0;
3588   delete this;
3589 }
3590
3591 void ligature_node::asciify(macro *m)
3592 {
3593   n1->asciify(m);
3594   n2->asciify(m);
3595   n1 = n2 = 0;
3596   delete this;
3597 }
3598
3599 void break_char_node::asciify(macro *m)
3600 {
3601   ch->asciify(m);
3602   ch = 0;
3603   delete this;
3604 }
3605
3606 void italic_corrected_node::asciify(macro *m)
3607 {
3608   n->asciify(m);
3609   n = 0;
3610   delete this;
3611 }
3612
3613 void left_italic_corrected_node::asciify(macro *m)
3614 {
3615   if (n) {
3616     n->asciify(m);
3617     n = 0;
3618   }
3619   delete this;
3620 }
3621
3622 void hmotion_node::asciify(macro *m)
3623 {
3624   if (was_tab) {
3625     m->append('\t');
3626     delete this;
3627   }
3628   else
3629     m->append(this);
3630 }
3631
3632 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3633                                                  statem *s, int pop,
3634                                                  node *nxt)
3635 : hmotion_node(i, c, s, pop, nxt)
3636 {
3637 }
3638
3639 space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
3640                                                  node *nxt)
3641 : hmotion_node(i, c, 0, 0, nxt)
3642 {
3643 }
3644
3645 void space_char_hmotion_node::asciify(macro *m)
3646 {
3647   m->append(ESCAPE_SPACE);
3648   delete this;
3649 }
3650
3651 void space_node::asciify(macro *m)
3652 {
3653   if (was_escape_colon) {
3654     m->append(ESCAPE_COLON);
3655     delete this;
3656   }
3657   else
3658     m->append(this);
3659 }
3660
3661 void word_space_node::asciify(macro *m)
3662 {
3663   for (width_list *w = orig_width; w; w = w->next)
3664     m->append(' ');
3665   delete this;
3666 }
3667
3668 void unbreakable_space_node::asciify(macro *m)
3669 {
3670   m->append(ESCAPE_TILDE);
3671   delete this;
3672 }
3673
3674 void line_start_node::asciify(macro *)
3675 {
3676   delete this;
3677 }
3678
3679 void vertical_size_node::asciify(macro *)
3680 {
3681   delete this;
3682 }
3683
3684 breakpoint *node::get_breakpoints(hunits /*width*/, int /*nspaces*/,
3685                                   breakpoint *rest, int /*is_inner*/)
3686 {
3687   return rest;
3688 }
3689
3690 int node::nbreaks()
3691 {
3692   return 0;
3693 }
3694
3695 breakpoint *space_node::get_breakpoints(hunits wd, int ns,
3696                                         breakpoint *rest, int is_inner)
3697 {
3698   if (next && next->discardable())
3699     return rest;
3700   breakpoint *bp = new breakpoint;
3701   bp->next = rest;
3702   bp->width = wd;
3703   bp->nspaces = ns;
3704   bp->hyphenated = 0;
3705   if (is_inner) {
3706     assert(rest != 0);
3707     bp->index = rest->index + 1;
3708     bp->nd = rest->nd;
3709   }
3710   else {
3711     bp->nd = this;
3712     bp->index = 0;
3713   }
3714   return bp;
3715 }
3716
3717 int space_node::nbreaks()
3718 {
3719   if (next && next->discardable())
3720     return 0;
3721   else
3722     return 1;
3723 }
3724
3725 static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
3726                                              int ns, breakpoint *rest)
3727 {
3728   if (p != 0) {
3729     rest = p->get_breakpoints(*widthp,
3730                               ns,
3731                               node_list_get_breakpoints(p->next, widthp, ns,
3732                                                         rest),
3733                               1);
3734     *widthp += p->width();
3735   }
3736   return rest;
3737 }
3738
3739 breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
3740                                          breakpoint *rest, int is_inner)
3741 {
3742   breakpoint *bp = new breakpoint;
3743   bp->next = rest;
3744   bp->width = wd;
3745   for (node *tem = pre; tem != 0; tem = tem->next)
3746     bp->width += tem->width();
3747   bp->nspaces = ns;
3748   bp->hyphenated = 1;
3749   if (is_inner) {
3750     assert(rest != 0);
3751     bp->index = rest->index + 1;
3752     bp->nd = rest->nd;
3753   }
3754   else {
3755     bp->nd = this;
3756     bp->index = 0;
3757   }
3758   return node_list_get_breakpoints(none, &wd, ns, bp);
3759 }
3760
3761 int dbreak_node::nbreaks()
3762 {
3763   int i = 1;
3764   for (node *tem = none; tem != 0; tem = tem->next)
3765     i += tem->nbreaks();
3766   return i;
3767 }
3768
3769 void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
3770 {
3771   assert(0);
3772 }
3773
3774 void space_node::split(int where, node **pre, node **post)
3775 {
3776   assert(where == 0);
3777   *pre = next;
3778   *post = 0;
3779   delete this;
3780 }
3781
3782 static void node_list_split(node *p, int *wherep, node **prep, node **postp)
3783 {
3784   if (p == 0)
3785     return;
3786   int nb = p->nbreaks();
3787   node_list_split(p->next, wherep, prep, postp);
3788   if (*wherep < 0) {
3789     p->next = *postp;
3790     *postp = p;
3791   }
3792   else if (*wherep < nb) {
3793     p->next = *prep;
3794     p->split(*wherep, prep, postp);
3795   }
3796   else {
3797     p->next = *prep;
3798     *prep = p;
3799   }
3800   *wherep -= nb;
3801 }
3802
3803 void dbreak_node::split(int where, node **prep, node **postp)
3804 {
3805   assert(where >= 0);
3806   if (where == 0) {
3807     *postp = post;
3808     post = 0;
3809     if (pre == 0)
3810       *prep = next;
3811     else {
3812       node *tem;
3813       for (tem = pre; tem->next != 0; tem = tem->next)
3814         ;
3815       tem->next = next;
3816       *prep = pre;
3817     }
3818     pre = 0;
3819     delete this;
3820   }
3821   else {
3822     *prep = next;
3823     where -= 1;
3824     node_list_split(none, &where, prep, postp);
3825     none = 0;
3826     delete this;
3827   }
3828 }
3829
3830 hyphenation_type node::get_hyphenation_type()
3831 {
3832   return HYPHEN_BOUNDARY;
3833 }
3834
3835 hyphenation_type dbreak_node::get_hyphenation_type()
3836 {
3837   return HYPHEN_INHIBIT;
3838 }
3839
3840 hyphenation_type kern_pair_node::get_hyphenation_type()
3841 {
3842   return HYPHEN_MIDDLE;
3843 }
3844
3845 hyphenation_type dummy_node::get_hyphenation_type()
3846 {
3847   return HYPHEN_MIDDLE;
3848 }
3849
3850 hyphenation_type transparent_dummy_node::get_hyphenation_type()
3851 {
3852   return HYPHEN_MIDDLE;
3853 }
3854
3855 hyphenation_type hmotion_node::get_hyphenation_type()
3856 {
3857   return HYPHEN_MIDDLE;
3858 }
3859
3860 hyphenation_type space_char_hmotion_node::get_hyphenation_type()
3861 {
3862   return HYPHEN_MIDDLE;
3863 }
3864
3865 hyphenation_type overstrike_node::get_hyphenation_type()
3866 {
3867   return HYPHEN_MIDDLE;
3868 }
3869
3870 hyphenation_type space_node::get_hyphenation_type()
3871 {
3872   if (was_escape_colon)
3873     return HYPHEN_MIDDLE;
3874   return HYPHEN_BOUNDARY;
3875 }
3876
3877 hyphenation_type unbreakable_space_node::get_hyphenation_type()
3878 {
3879   return HYPHEN_MIDDLE;
3880 }
3881
3882 int node::interpret(macro *)
3883 {
3884   return 0;
3885 }
3886
3887 special_node::special_node(const macro &m, int n)
3888 : mac(m), no_init_string(n)
3889 {
3890   font_size fs = curenv->get_font_size();
3891   int char_height = curenv->get_char_height();
3892   int char_slant = curenv->get_char_slant();
3893   int fontno = env_definite_font(curenv);
3894   tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fontno);
3895   if (curenv->is_composite())
3896     tf = tf->get_plain();
3897   gcol = curenv->get_glyph_color();
3898   fcol = curenv->get_fill_color();
3899   is_special = 1;
3900 }
3901
3902 special_node::special_node(const macro &m, tfont *t,
3903                            color *gc, color *fc,
3904                            statem *s, int pop,
3905                            int n)
3906 : node(0, s, pop), mac(m), tf(t), gcol(gc), fcol(fc), no_init_string(n)
3907 {
3908   is_special = 1;
3909 }
3910
3911 int special_node::same(node *n)
3912 {
3913   return mac == ((special_node *)n)->mac
3914          && tf == ((special_node *)n)->tf
3915          && gcol == ((special_node *)n)->gcol
3916          && fcol == ((special_node *)n)->fcol
3917          && no_init_string == ((special_node *)n)->no_init_string;
3918 }
3919
3920 const char *special_node::type()
3921 {
3922   return "special_node";
3923 }
3924
3925 int special_node::ends_sentence()
3926 {
3927   return 2;
3928 }
3929
3930 int special_node::force_tprint()
3931 {
3932   return 0;
3933 }
3934
3935 int special_node::is_tag()
3936 {
3937   return 0;
3938 }
3939
3940 node *special_node::copy()
3941 {
3942   return new special_node(mac, tf, gcol, fcol, state, div_nest_level,
3943                           no_init_string);
3944 }
3945
3946 void special_node::tprint_start(troff_output_file *out)
3947 {
3948   out->start_special(tf, gcol, fcol, no_init_string);
3949 }
3950
3951 void special_node::tprint_char(troff_output_file *out, unsigned char c)
3952 {
3953   out->special_char(c);
3954 }
3955
3956 void special_node::tprint_end(troff_output_file *out)
3957 {
3958   out->end_special();
3959 }
3960
3961 tfont *special_node::get_tfont()
3962 {
3963   return tf;
3964 }
3965
3966 /* suppress_node */
3967
3968 suppress_node::suppress_node(int on_or_off, int issue_limits)
3969 : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
3970   image_id(0)
3971 {
3972 }
3973
3974 suppress_node::suppress_node(symbol f, char p, int id)
3975 : is_on(2), emit_limits(0), filename(f), position(p), image_id(id)
3976 {
3977   is_special = 1;
3978 }
3979
3980 suppress_node::suppress_node(int issue_limits, int on_or_off,
3981                              symbol f, char p, int id,
3982                              statem *s, int pop)
3983 : node(0, s, pop), is_on(on_or_off), emit_limits(issue_limits), filename(f),
3984   position(p), image_id(id)
3985 {
3986 }
3987
3988 int suppress_node::same(node *n)
3989 {
3990   return ((is_on == ((suppress_node *)n)->is_on)
3991           && (emit_limits == ((suppress_node *)n)->emit_limits)
3992           && (filename == ((suppress_node *)n)->filename)
3993           && (position == ((suppress_node *)n)->position)
3994           && (image_id == ((suppress_node *)n)->image_id));
3995 }
3996
3997 const char *suppress_node::type()
3998 {
3999   return "suppress_node";
4000 }
4001
4002 node *suppress_node::copy()
4003 {
4004   return new suppress_node(emit_limits, is_on, filename, position, image_id,
4005                            state, div_nest_level);
4006 }
4007
4008 /* tag_node */
4009
4010 tag_node::tag_node()
4011 : delayed(0)
4012 {
4013   is_special = 1;
4014 }
4015
4016 tag_node::tag_node(string s, int delay)
4017 : tag_string(s), delayed(delay)
4018 {
4019   is_special = !delay;
4020 }
4021
4022 tag_node::tag_node(string s, statem *st, int pop, int delay)
4023 : node(0, st, pop), tag_string(s), delayed(delay)
4024 {
4025   is_special = !delay;
4026 }
4027
4028 node *tag_node::copy()
4029 {
4030   return new tag_node(tag_string, state, div_nest_level, delayed);
4031 }
4032
4033 void tag_node::tprint(troff_output_file *out)
4034 {
4035   if (delayed)
4036     out->add_to_tag_list(tag_string);
4037   else
4038     out->state.add_tag(out->fp, tag_string);
4039 }
4040
4041 int tag_node::same(node *nd)
4042 {
4043   return tag_string == ((tag_node *)nd)->tag_string
4044          && delayed == ((tag_node *)nd)->delayed;
4045 }
4046
4047 const char *tag_node::type()
4048 {
4049   return "tag_node";
4050 }
4051
4052 int tag_node::force_tprint()
4053 {
4054   return !delayed;
4055 }
4056
4057 int tag_node::is_tag()
4058 {
4059   return !delayed;
4060 }
4061
4062 int tag_node::ends_sentence()
4063 {
4064   return 2;
4065 }
4066
4067 int get_reg_int(const char *p)
4068 {
4069   reg *r = (reg *)number_reg_dictionary.lookup(p);
4070   units prev_value;
4071   if (r && (r->get_value(&prev_value)))
4072     return (int)prev_value;
4073   else
4074     warning(WARN_REG, "number register `%1' not defined", p);
4075   return 0;
4076 }
4077
4078 const char *get_reg_str(const char *p)
4079 {
4080   reg *r = (reg *)number_reg_dictionary.lookup(p);
4081   if (r)
4082     return r->get_string();
4083   else
4084     warning(WARN_REG, "register `%1' not defined", p);
4085   return 0;
4086 }
4087
4088 void suppress_node::put(troff_output_file *out, const char *s)
4089 {
4090   int i = 0;
4091   while (s[i] != (char)0) {
4092     out->special_char(s[i]);
4093     i++;
4094   }
4095 }
4096
4097 /*
4098  *  We need to remember the start of the image and its name.
4099  */
4100
4101 static char last_position = 0;
4102 static const char *last_image_filename = 0;
4103 static int last_image_id = 0;
4104
4105 inline int min(int a, int b)
4106 {
4107   return a < b ? a : b;
4108 }
4109
4110 /*
4111  *  tprint - if (is_on == 2)
4112  *               remember current position (l, r, c, i) and filename
4113  *           else
4114  *               if (emit_limits)
4115  *                   if (html)
4116  *                      emit image tag
4117  *                   else
4118  *                      emit postscript bounds for image
4119  *               else
4120  *                  if (suppress boolean differs from current state)
4121  *                      alter state
4122  *                  reset registers
4123  *                  record current page
4124  *                  set low water mark.
4125  */
4126
4127 void suppress_node::tprint(troff_output_file *out)
4128 {
4129   int current_page = topdiv->get_page_number();
4130   // firstly check to see whether this suppress node contains
4131   // an image filename & position.
4132   if (is_on == 2) {
4133     // remember position and filename
4134     last_position = position;
4135     char *tem = (char *)last_image_filename;
4136     last_image_filename = strsave(filename.contents());
4137     if (tem)
4138       a_delete tem;
4139     last_image_id = image_id;
4140     // printf("start of image and page = %d\n", current_page);
4141   }
4142   else {
4143     // now check whether the suppress node requires us to issue limits.
4144     if (emit_limits) {
4145       char name[8192];
4146       // remember that the filename will contain a %d in which the
4147       // last_image_id is placed
4148       if (last_image_filename == (char *) 0)
4149         *name = '\0';
4150       else
4151         sprintf(name, last_image_filename, last_image_id);
4152       if (is_html) {
4153         switch (last_position) {
4154         case 'c':
4155           out->start_special();
4156           put(out, "devtag:.centered-image");
4157           break;
4158         case 'r':
4159           out->start_special();
4160           put(out, "devtag:.right-image");
4161           break;
4162         case 'l':
4163           out->start_special();
4164           put(out, "devtag:.left-image");
4165           break;
4166         case 'i':
4167           ;
4168         default:
4169           ;
4170         }
4171         out->end_special();
4172         out->start_special();
4173         put(out, "devtag:.auto-image ");
4174         put(out, name);
4175         out->end_special();
4176       }
4177       else {
4178         // postscript (or other device)
4179         if (suppress_start_page > 0 && current_page != suppress_start_page)
4180           error("suppression limit registers span more than one page;\n"
4181                 "image description %1 will be wrong", image_no);
4182         // if (topdiv->get_page_number() != suppress_start_page)
4183         //  fprintf(stderr, "end of image and topdiv page = %d   and  suppress_start_page = %d\n",
4184         //        topdiv->get_page_number(), suppress_start_page);
4185
4186         // remember that the filename will contain a %d in which the
4187         // image_no is placed
4188         fprintf(stderr,
4189                 "grohtml-info:page %d  %d  %d  %d  %d  %d  %s  %d  %d  %s\n",
4190                 topdiv->get_page_number(),
4191                 get_reg_int("opminx"), get_reg_int("opminy"),
4192                 get_reg_int("opmaxx"), get_reg_int("opmaxy"),
4193                 // page offset + line length
4194                 get_reg_int(".o") + get_reg_int(".l"),
4195                 name, hresolution, vresolution, get_reg_str(".F"));
4196         fflush(stderr);
4197       }
4198     }
4199     else {
4200       if (is_on) {
4201         out->on();
4202         // lastly we reset the output registers
4203         reset_output_registers();
4204       }
4205       else
4206         out->off();
4207       suppress_start_page = current_page;
4208     }
4209   }
4210 }
4211
4212 int suppress_node::force_tprint()
4213 {
4214   return is_on;
4215 }
4216
4217 int suppress_node::is_tag()
4218 {
4219   return is_on;
4220 }
4221
4222 hunits suppress_node::width()
4223 {
4224   return H0;
4225 }
4226
4227 /* composite_node */
4228
4229 class composite_node : public charinfo_node {
4230   node *n;
4231   tfont *tf;
4232 public:
4233   composite_node(node *, charinfo *, tfont *, statem *, int, node * = 0);
4234   ~composite_node();
4235   node *copy();
4236   hunits width();
4237   node *last_char_node();
4238   units size();
4239   void tprint(troff_output_file *);
4240   hyphenation_type get_hyphenation_type();
4241   void ascii_print(ascii_output_file *);
4242   void asciify(macro *);
4243   hyphen_list *get_hyphen_list(hyphen_list *, int *);
4244   node *add_self(node *, hyphen_list **);
4245   tfont *get_tfont();
4246   int same(node *);
4247   const char *type();
4248   int force_tprint();
4249   int is_tag();
4250   void vertical_extent(vunits *, vunits *);
4251   vunits vertical_width();
4252 };
4253
4254 composite_node::composite_node(node *p, charinfo *c, tfont *t, statem *s,
4255                                int pop, node *x)
4256 : charinfo_node(c, s, pop, x), n(p), tf(t)
4257 {
4258 }
4259
4260 composite_node::~composite_node()
4261 {
4262   delete_node_list(n);
4263 }
4264
4265 node *composite_node::copy()
4266 {
4267   return new composite_node(copy_node_list(n), ci, tf, state, div_nest_level);
4268 }
4269
4270 hunits composite_node::width()
4271 {
4272   hunits x;
4273   if (tf->get_constant_space(&x))
4274     return x;
4275   x = H0;
4276   for (node *tem = n; tem; tem = tem->next)
4277     x += tem->width();
4278   hunits offset;
4279   if (tf->get_bold(&offset))
4280     x += offset;
4281   x += tf->get_track_kern();
4282   return x;
4283 }
4284
4285 node *composite_node::last_char_node()
4286 {
4287   return this;
4288 }
4289
4290 vunits composite_node::vertical_width()
4291 {
4292   vunits v = V0;
4293   for (node *tem = n; tem; tem = tem->next)
4294     v += tem->vertical_width();
4295   return v;
4296 }
4297
4298 units composite_node::size()
4299 {
4300   return tf->get_size().to_units();
4301 }
4302
4303 hyphenation_type composite_node::get_hyphenation_type()
4304 {
4305   return HYPHEN_MIDDLE;
4306 }
4307
4308 void composite_node::asciify(macro *m)
4309 {
4310   unsigned char c = ci->get_asciify_code();
4311   if (c == 0)
4312     c = ci->get_ascii_code();
4313   if (c != 0) {
4314     m->append(c);
4315     delete this;
4316   }
4317   else
4318     m->append(this);
4319 }
4320
4321 void composite_node::ascii_print(ascii_output_file *ascii)
4322 {
4323   unsigned char c = ci->get_ascii_code();
4324   if (c != 0)
4325     ascii->outc(c);
4326   else
4327     ascii->outs(ci->nm.contents());
4328
4329 }
4330
4331 hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail, int *count)
4332 {
4333   (*count)++;
4334   return new hyphen_list(ci->get_hyphenation_code(), tail);
4335 }
4336
4337 node *composite_node::add_self(node *nn, hyphen_list **p)
4338 {
4339   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
4340   next = nn;
4341   nn = this;
4342   if ((*p)->hyphen)
4343     nn = nn->add_discretionary_hyphen();
4344   hyphen_list *pp = *p;
4345   *p = (*p)->next;
4346   delete pp;
4347   return nn;
4348 }
4349
4350 tfont *composite_node::get_tfont()
4351 {
4352   return tf;
4353 }
4354
4355 node *reverse_node_list(node *n)
4356 {
4357   node *r = 0;
4358   while (n) {
4359     node *tem = n;
4360     n = n->next;
4361     tem->next = r;
4362     r = tem;
4363   }
4364   return r;
4365 }
4366
4367 void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
4368 {
4369   n = reverse_node_list(n);
4370   node_list_vertical_extent(n, minimum, maximum);
4371   n = reverse_node_list(n);
4372 }
4373
4374 width_list::width_list(hunits w, hunits s)
4375 : width(w), sentence_width(s), next(0)
4376 {
4377 }
4378
4379 width_list::width_list(width_list *w)
4380 : width(w->width), sentence_width(w->sentence_width), next(0)
4381 {
4382 }
4383
4384 word_space_node::word_space_node(hunits d, color *c, width_list *w, node *x)
4385 : space_node(d, c, x), orig_width(w), unformat(0)
4386 {
4387 }
4388
4389 word_space_node::word_space_node(hunits d, int s, color *c, width_list *w,
4390                                  int flag, statem *st, int pop, node *x)
4391 : space_node(d, s, 0, c, st, pop, x), orig_width(w), unformat(flag)
4392 {
4393 }
4394
4395 word_space_node::~word_space_node()
4396 {
4397   width_list *w = orig_width;
4398   while (w != 0) {
4399     width_list *tmp = w;
4400     w = w->next;
4401     delete tmp;
4402   }
4403 }
4404
4405 node *word_space_node::copy()
4406 {
4407   assert(orig_width != 0);
4408   width_list *w_old_curr = orig_width;
4409   width_list *w_new_curr = new width_list(w_old_curr);
4410   width_list *w_new = w_new_curr;
4411   w_old_curr = w_old_curr->next;
4412   while (w_old_curr != 0) {
4413     w_new_curr->next = new width_list(w_old_curr);
4414     w_new_curr = w_new_curr->next;
4415     w_old_curr = w_old_curr->next;
4416   }
4417   return new word_space_node(n, set, col, w_new, unformat, state,
4418                              div_nest_level);
4419 }
4420
4421 int word_space_node::set_unformat_flag()
4422 {
4423   unformat = 1;
4424   return 1;
4425 }
4426
4427 void word_space_node::tprint(troff_output_file *out)
4428 {
4429   out->fill_color(col);
4430   out->word_marker();
4431   out->right(n);
4432 }
4433
4434 int word_space_node::merge_space(hunits h, hunits sw, hunits ssw)
4435 {
4436   n += h;
4437   assert(orig_width != 0);
4438   width_list *w = orig_width; 
4439   for (; w->next; w = w->next)
4440     ;
4441   w->next = new width_list(sw, ssw);
4442   return 1;
4443 }
4444
4445 unbreakable_space_node::unbreakable_space_node(hunits d, color *c, node *x)
4446 : word_space_node(d, c, 0, x)
4447 {
4448 }
4449
4450 unbreakable_space_node::unbreakable_space_node(hunits d, int s,
4451                                                color *c, statem *st, int pop,
4452                                                node *x)
4453 : word_space_node(d, s, c, 0, 0, st, pop, x)
4454 {
4455 }
4456
4457 node *unbreakable_space_node::copy()
4458 {
4459   return new unbreakable_space_node(n, set, col, state, div_nest_level);
4460 }
4461
4462 int unbreakable_space_node::force_tprint()
4463 {
4464   return 0;
4465 }
4466
4467 int unbreakable_space_node::is_tag()
4468 {
4469   return 0;
4470 }
4471
4472 breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
4473                                                     breakpoint *rest, int)
4474 {
4475   return rest;
4476 }
4477
4478 int unbreakable_space_node::nbreaks()
4479 {
4480   return 0;
4481 }
4482
4483 void unbreakable_space_node::split(int, node **, node **)
4484 {
4485   assert(0);
4486 }
4487
4488 int unbreakable_space_node::merge_space(hunits, hunits, hunits)
4489 {
4490   return 0;
4491 }
4492
4493 hvpair::hvpair()
4494 {
4495 }
4496
4497 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4498                      color *gc, color *fc)
4499 : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4500 {
4501   point = new hvpair[npoints];
4502   for (int i = 0; i < npoints; i++)
4503     point[i] = p[i];
4504 }
4505
4506 draw_node::draw_node(char c, hvpair *p, int np, font_size s,
4507                      color *gc, color *fc, statem *st, int pop)
4508 : node(0, st, pop), npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
4509 {
4510   point = new hvpair[npoints];
4511   for (int i = 0; i < npoints; i++)
4512     point[i] = p[i];
4513 }
4514
4515 int draw_node::same(node *n)
4516 {
4517   draw_node *nd = (draw_node *)n;
4518   if (code != nd->code || npoints != nd->npoints || sz != nd->sz
4519       || gcol != nd->gcol || fcol != nd->fcol)
4520     return 0;
4521   for (int i = 0; i < npoints; i++)
4522     if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
4523       return 0;
4524   return 1;
4525 }
4526
4527 const char *draw_node::type()
4528 {
4529   return "draw_node";
4530 }
4531
4532 int draw_node::force_tprint()
4533 {
4534   return 0;
4535 }
4536
4537 int draw_node::is_tag()
4538 {
4539   return 0;
4540 }
4541
4542 draw_node::~draw_node()
4543 {
4544   if (point)
4545     a_delete point;
4546 }
4547
4548 hunits draw_node::width()
4549 {
4550   hunits x = H0;
4551   for (int i = 0; i < npoints; i++)
4552     x += point[i].h;
4553   return x;
4554 }
4555
4556 vunits draw_node::vertical_width()
4557 {
4558   if (code == 'e')
4559     return V0;
4560   vunits x = V0;
4561   for (int i = 0; i < npoints; i++)
4562     x += point[i].v;
4563   return x;
4564 }
4565
4566 node *draw_node::copy()
4567 {
4568   return new draw_node(code, point, npoints, sz, gcol, fcol, state,
4569                        div_nest_level);
4570 }
4571
4572 void draw_node::tprint(troff_output_file *out)
4573 {
4574   out->draw(code, point, npoints, sz, gcol, fcol);
4575 }
4576
4577 /* tprint methods */
4578
4579 void glyph_node::tprint(troff_output_file *out)
4580 {
4581   tfont *ptf = tf->get_plain();
4582   if (ptf == tf)
4583     out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
4584   else {
4585     hunits offset;
4586     int bold = tf->get_bold(&offset);
4587     hunits w = ptf->get_width(ci);
4588     hunits k = H0;
4589     hunits x;
4590     int cs = tf->get_constant_space(&x);
4591     if (cs) {
4592       x -= w;
4593       if (bold)
4594         x -= offset;
4595       hunits x2 = x/2;
4596       out->right(x2);
4597       k = x - x2;
4598     }
4599     else
4600       k = tf->get_track_kern();
4601     if (bold) {
4602       out->put_char(ci, ptf, gcol, fcol);
4603       out->right(offset);
4604     }
4605     out->put_char_width(ci, ptf, gcol, fcol, w, k);
4606   }
4607 }
4608
4609 void glyph_node::zero_width_tprint(troff_output_file *out)
4610 {
4611   tfont *ptf = tf->get_plain();
4612   hunits offset;
4613   int bold = tf->get_bold(&offset);
4614   hunits x;
4615   int cs = tf->get_constant_space(&x);
4616   if (cs) {
4617     x -= ptf->get_width(ci);
4618     if (bold)
4619       x -= offset;
4620     x = x/2;
4621     out->right(x);
4622   }
4623   out->put_char(ci, ptf, gcol, fcol);
4624   if (bold) {
4625     out->right(offset);
4626     out->put_char(ci, ptf, gcol, fcol);
4627     out->right(-offset);
4628   }
4629   if (cs)
4630     out->right(-x);
4631 }
4632
4633 void break_char_node::tprint(troff_output_file *t)
4634 {
4635   ch->tprint(t);
4636 }
4637
4638 void break_char_node::zero_width_tprint(troff_output_file *t)
4639 {
4640   ch->zero_width_tprint(t);
4641 }
4642
4643 void hline_node::tprint(troff_output_file *out)
4644 {
4645   if (x < H0) {
4646     out->right(x);
4647     x = -x;
4648   }
4649   if (n == 0) {
4650     out->right(x);
4651     return;
4652   }
4653   hunits w = n->width();
4654   if (w <= H0) {
4655     error("horizontal line drawing character must have positive width");
4656     out->right(x);
4657     return;
4658   }
4659   int i = int(x/w);
4660   if (i == 0) {
4661     hunits xx = x - w;
4662     hunits xx2 = xx/2;
4663     out->right(xx2);
4664     if (out->is_on())
4665       n->tprint(out);
4666     out->right(xx - xx2);
4667   }
4668   else {
4669     hunits rem = x - w*i;
4670     if (rem > H0) {
4671       if (n->overlaps_horizontally()) {
4672         if (out->is_on())
4673           n->tprint(out);
4674         out->right(rem - w);
4675       }
4676       else
4677         out->right(rem);
4678     }
4679     while (--i >= 0)
4680       if (out->is_on())
4681         n->tprint(out);
4682   }
4683 }
4684
4685 void vline_node::tprint(troff_output_file *out)
4686 {
4687   if (n == 0) {
4688     out->down(x);
4689     return;
4690   }
4691   vunits h = n->size();
4692   int overlaps = n->overlaps_vertically();
4693   vunits y = x;
4694   if (y < V0) {
4695     y = -y;
4696     int i = y / h;
4697     vunits rem = y - i*h;
4698     if (i == 0) {
4699       out->right(n->width());
4700       out->down(-rem);
4701     }
4702     else {
4703       while (--i > 0) {
4704         n->zero_width_tprint(out);
4705         out->down(-h);
4706       }
4707       if (overlaps) {
4708         n->zero_width_tprint(out);
4709         out->down(-rem);
4710         if (out->is_on())
4711           n->tprint(out);
4712         out->down(-h);
4713       }
4714       else {
4715         if (out->is_on())
4716           n->tprint(out);
4717         out->down(-h - rem);
4718       }
4719     }
4720   }
4721   else {
4722     int i = y / h;
4723     vunits rem = y - i*h;
4724     if (i == 0) {
4725       out->down(rem);
4726       out->right(n->width());
4727     }
4728     else {
4729       out->down(h);
4730       if (overlaps)
4731         n->zero_width_tprint(out);
4732       out->down(rem);
4733       while (--i > 0) {
4734         n->zero_width_tprint(out);
4735         out->down(h);
4736       }
4737       if (out->is_on())
4738         n->tprint(out);
4739     }
4740   }
4741 }
4742
4743 void zero_width_node::tprint(troff_output_file *out)
4744 {
4745   if (!n)
4746     return;
4747   if (!n->next) {
4748     n->zero_width_tprint(out);
4749     return;
4750   }
4751   int hpos = out->get_hpos();
4752   int vpos = out->get_vpos();
4753   node *tem = n;
4754   while (tem) {
4755     tem->tprint(out);
4756     tem = tem->next;
4757   }
4758   out->moveto(hpos, vpos);
4759 }
4760
4761 void overstrike_node::tprint(troff_output_file *out)
4762 {
4763   hunits pos = H0;
4764   for (node *tem = list; tem; tem = tem->next) {
4765     hunits x = (max_width - tem->width())/2;
4766     out->right(x - pos);
4767     pos = x;
4768     tem->zero_width_tprint(out);
4769   }
4770   out->right(max_width - pos);
4771 }
4772
4773 void bracket_node::tprint(troff_output_file *out)
4774 {
4775   if (list == 0)
4776     return;
4777   int npieces = 0;
4778   node *tem;
4779   for (tem = list; tem; tem = tem->next)
4780     ++npieces;
4781   vunits h = list->size();
4782   vunits totalh = h*npieces;
4783   vunits y = (totalh - h)/2;
4784   out->down(y);
4785   for (tem = list; tem; tem = tem->next) {
4786     tem->zero_width_tprint(out);
4787     out->down(-h);
4788   }
4789   out->right(max_width);
4790   out->down(totalh - y);
4791 }
4792
4793 void node::tprint(troff_output_file *)
4794 {
4795 }
4796
4797 void node::zero_width_tprint(troff_output_file *out)
4798 {
4799   int hpos = out->get_hpos();
4800   int vpos = out->get_vpos();
4801   tprint(out);
4802   out->moveto(hpos, vpos);
4803 }
4804
4805 void space_node::tprint(troff_output_file *out)
4806 {
4807   out->fill_color(col);
4808   out->right(n);
4809 }
4810
4811 void hmotion_node::tprint(troff_output_file *out)
4812 {
4813   out->fill_color(col);
4814   out->right(n);
4815 }
4816
4817 void space_char_hmotion_node::tprint(troff_output_file *out)
4818 {
4819   out->fill_color(col);
4820   if (is_html) {
4821     // we emit the space width as a negative glyph index
4822     out->flush_tbuf();
4823     out->do_motion();
4824     out->put('N');
4825     out->put(-n.to_units());
4826     out->put('\n');
4827   }
4828   out->right(n);
4829 }
4830
4831 void vmotion_node::tprint(troff_output_file *out)
4832 {
4833   out->fill_color(col);
4834   out->down(n);
4835 }
4836
4837 void kern_pair_node::tprint(troff_output_file *out)
4838 {
4839   n1->tprint(out);
4840   out->right(amount);
4841   n2->tprint(out);
4842 }
4843
4844 static void tprint_reverse_node_list(troff_output_file *out, node *n)
4845 {
4846   if (n == 0)
4847     return;
4848   tprint_reverse_node_list(out, n->next);
4849   n->tprint(out);
4850 }
4851
4852 void dbreak_node::tprint(troff_output_file *out)
4853 {
4854   tprint_reverse_node_list(out, none);
4855 }
4856
4857 void composite_node::tprint(troff_output_file *out)
4858 {
4859   hunits bold_offset;
4860   int is_bold = tf->get_bold(&bold_offset);
4861   hunits track_kern = tf->get_track_kern();
4862   hunits constant_space;
4863   int is_constant_spaced = tf->get_constant_space(&constant_space);
4864   hunits x = H0;
4865   if (is_constant_spaced) {
4866     x = constant_space;
4867     for (node *tem = n; tem; tem = tem->next)
4868       x -= tem->width();
4869     if (is_bold)
4870       x -= bold_offset;
4871     hunits x2 = x/2;
4872     out->right(x2);
4873     x -= x2;
4874   }
4875   if (is_bold) {
4876     int hpos = out->get_hpos();
4877     int vpos = out->get_vpos();
4878     tprint_reverse_node_list(out, n);
4879     out->moveto(hpos, vpos);
4880     out->right(bold_offset);
4881   }
4882   tprint_reverse_node_list(out, n);
4883   if (is_constant_spaced)
4884     out->right(x);
4885   else
4886     out->right(track_kern);
4887 }
4888
4889 node *make_composite_node(charinfo *s, environment *env)
4890 {
4891   int fontno = env_definite_font(env);
4892   if (fontno < 0) {
4893     error("no current font");
4894     return 0;
4895   }
4896   assert(fontno < font_table_size && font_table[fontno] != 0);
4897   node *n = charinfo_to_node_list(s, env);
4898   font_size fs = env->get_font_size();
4899   int char_height = env->get_char_height();
4900   int char_slant = env->get_char_slant();
4901   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
4902                                             fontno);
4903   if (env->is_composite())
4904     tf = tf->get_plain();
4905   return new composite_node(n, s, tf, 0, 0, 0);
4906 }
4907
4908 node *make_glyph_node(charinfo *s, environment *env, int no_error_message = 0)
4909 {
4910   int fontno = env_definite_font(env);
4911   if (fontno < 0) {
4912     error("no current font");
4913     return 0;
4914   }
4915   assert(fontno < font_table_size && font_table[fontno] != 0);
4916   int fn = fontno;
4917   int found = font_table[fontno]->contains(s);
4918   if (!found) {
4919     macro *mac = s->get_macro();
4920     if (mac && s->is_fallback())
4921       return make_composite_node(s, env);
4922     if (s->numbered()) {
4923       if (!no_error_message)
4924         warning(WARN_CHAR, "can't find numbered character %1",
4925                 s->get_number());
4926       return 0;
4927     }
4928     special_font_list *sf = font_table[fontno]->sf;
4929     while (sf != 0 && !found) {
4930       fn = sf->n;
4931       if (font_table[fn])
4932         found = font_table[fn]->contains(s);
4933       sf = sf->next;
4934     }
4935     if (!found) {
4936       symbol f = font_table[fontno]->get_name();
4937       string gl(f.contents());
4938       gl += ' ';
4939       gl += s->nm.contents();
4940       gl += '\0';
4941       charinfo *ci = get_charinfo(symbol(gl.contents()));
4942       if (ci && ci->get_macro())
4943         return make_composite_node(ci, env);
4944     }
4945     if (!found) {
4946       sf = global_special_fonts;
4947       while (sf != 0 && !found) {
4948         fn = sf->n;
4949         if (font_table[fn])
4950           found = font_table[fn]->contains(s);
4951         sf = sf->next;
4952       }
4953     }
4954     if (!found)
4955       if (mac && s->is_special())
4956         return make_composite_node(s, env);
4957     if (!found) {
4958       for (fn = 0; fn < font_table_size; fn++)
4959         if (font_table[fn]
4960             && font_table[fn]->is_special()
4961             && font_table[fn]->contains(s)) {
4962           found = 1;
4963           break;
4964         }
4965     }
4966     if (!found) {
4967       if (!no_error_message && s->first_time_not_found()) {
4968         unsigned char input_code = s->get_ascii_code();
4969         if (input_code != 0) {
4970           if (csgraph(input_code))
4971             warning(WARN_CHAR, "can't find character `%1'", input_code);
4972           else
4973             warning(WARN_CHAR, "can't find character with input code %1",
4974                     int(input_code));
4975         }
4976         else if (s->nm.contents()) {
4977           const char *nm = s->nm.contents();
4978           const char *backslash = (nm[1] == 0) ? "\\" : "";
4979           warning(WARN_CHAR, "can't find special character `%1%2'",
4980                   backslash, nm);
4981         }
4982       }
4983       return 0;
4984     }
4985   }
4986   font_size fs = env->get_font_size();
4987   int char_height = env->get_char_height();
4988   int char_slant = env->get_char_slant();
4989   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant, fn);
4990   if (env->is_composite())
4991     tf = tf->get_plain();
4992   color *gcol = env->get_glyph_color();
4993   color *fcol = env->get_fill_color();
4994   return new glyph_node(s, tf, gcol, fcol, 0, 0);
4995 }
4996
4997 node *make_node(charinfo *ci, environment *env)
4998 {
4999   switch (ci->get_special_translation()) {
5000   case charinfo::TRANSLATE_SPACE:
5001     return new space_char_hmotion_node(env->get_space_width(),
5002                                        env->get_fill_color());
5003   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5004     return new unbreakable_space_node(env->get_space_width(),
5005                                       env->get_fill_color());
5006   case charinfo::TRANSLATE_DUMMY:
5007     return new dummy_node;
5008   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5009     error("translation to \\%% ignored in this context");
5010     break;
5011   }
5012   charinfo *tem = ci->get_translation();
5013   if (tem)
5014     ci = tem;
5015   macro *mac = ci->get_macro();
5016   if (mac && ci->is_normal())
5017     return make_composite_node(ci, env);
5018   else
5019     return make_glyph_node(ci, env);
5020 }
5021
5022 int character_exists(charinfo *ci, environment *env)
5023 {
5024   if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
5025     return 1;
5026   charinfo *tem = ci->get_translation();
5027   if (tem)
5028     ci = tem;
5029   if (ci->get_macro())
5030     return 1;
5031   node *nd = make_glyph_node(ci, env, 1);
5032   if (nd) {
5033     delete nd;
5034     return 1;
5035   }
5036   return 0;
5037 }
5038
5039 node *node::add_char(charinfo *ci, environment *env,
5040                      hunits *widthp, int *spacep, node **glyph_comp_np)
5041 {
5042   node *res;
5043   switch (ci->get_special_translation()) {
5044   case charinfo::TRANSLATE_SPACE:
5045     res = new space_char_hmotion_node(env->get_space_width(),
5046                                       env->get_fill_color(), this);
5047     *widthp += res->width();
5048     return res;
5049   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
5050     res = new unbreakable_space_node(env->get_space_width(),
5051                                      env->get_fill_color(), this);
5052     res->freeze_space();
5053     *widthp += res->width();
5054     *spacep += res->nspaces();
5055     return res;
5056   case charinfo::TRANSLATE_DUMMY:
5057     return new dummy_node(this);
5058   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
5059     return add_discretionary_hyphen();
5060   }
5061   charinfo *tem = ci->get_translation();
5062   if (tem)
5063     ci = tem;
5064   macro *mac = ci->get_macro();
5065   if (mac && ci->is_normal()) {
5066     res = make_composite_node(ci, env);
5067     if (res) {
5068       res->next = this;
5069       *widthp += res->width();
5070       if (glyph_comp_np)
5071         *glyph_comp_np = res;
5072     }
5073     else {
5074       if (glyph_comp_np)
5075         *glyph_comp_np = res;
5076       return this;
5077     }
5078   }
5079   else {
5080     node *gn = make_glyph_node(ci, env);
5081     if (gn == 0)
5082       return this;
5083     else {
5084       hunits old_width = width();
5085       node *p = gn->merge_self(this);
5086       if (p == 0) {
5087         *widthp += gn->width();
5088         gn->next = this;
5089         res = gn;
5090       }
5091       else {
5092         *widthp += p->width() - old_width;
5093         res = p;
5094       }
5095       if (glyph_comp_np)
5096         *glyph_comp_np = res;
5097     }
5098   }
5099   int break_code = 0;
5100   if (ci->can_break_before())
5101     break_code = CAN_BREAK_BEFORE;
5102   if (ci->can_break_after())
5103     break_code |= CAN_BREAK_AFTER;
5104   if (ci->ignore_hcodes())
5105     break_code |= IGNORE_HCODES;
5106   if (ci->prohibit_break_before())
5107     break_code = PROHIBIT_BREAK_BEFORE;
5108   if (ci->prohibit_break_after())
5109     break_code |= PROHIBIT_BREAK_AFTER;
5110   if (ci->inter_char_space())
5111     break_code |= INTER_CHAR_SPACE;
5112   if (break_code) {
5113     node *next1 = res->next;
5114     res->next = 0;
5115     res = new break_char_node(res, break_code, get_break_code(),
5116                               env->get_fill_color(), next1);
5117   }
5118   return res;
5119 }
5120
5121 #ifdef __GNUG__
5122 inline
5123 #endif
5124 int same_node(node *n1, node *n2)
5125 {
5126   if (n1 != 0) {
5127     if (n2 != 0)
5128       return n1->type() == n2->type() && n1->same(n2);
5129     else
5130       return 0;
5131   }
5132   else
5133     return n2 == 0;
5134 }
5135
5136 int same_node_list(node *n1, node *n2)
5137 {
5138   while (n1 && n2) {
5139     if (n1->type() != n2->type() || !n1->same(n2))
5140       return 0;
5141     n1 = n1->next;
5142     n2 = n2->next;
5143   }
5144   return !n1 && !n2;
5145 }
5146
5147 int extra_size_node::same(node *nd)
5148 {
5149   return n == ((extra_size_node *)nd)->n;
5150 }
5151
5152 const char *extra_size_node::type()
5153 {
5154   return "extra_size_node";
5155 }
5156
5157 int extra_size_node::force_tprint()
5158 {
5159   return 0;
5160 }
5161
5162 int extra_size_node::is_tag()
5163 {
5164   return 0;
5165 }
5166
5167 int vertical_size_node::same(node *nd)
5168 {
5169   return n == ((vertical_size_node *)nd)->n;
5170 }
5171
5172 const char *vertical_size_node::type()
5173 {
5174   return "vertical_size_node";
5175 }
5176
5177 int vertical_size_node::set_unformat_flag()
5178 {
5179   return 0;
5180 }
5181
5182 int vertical_size_node::force_tprint()
5183 {
5184   return 0;
5185 }
5186
5187 int vertical_size_node::is_tag()
5188 {
5189   return 0;
5190 }
5191
5192 int hmotion_node::same(node *nd)
5193 {
5194   return n == ((hmotion_node *)nd)->n
5195          && col == ((hmotion_node *)nd)->col;
5196 }
5197
5198 const char *hmotion_node::type()
5199 {
5200   return "hmotion_node";
5201 }
5202
5203 int hmotion_node::set_unformat_flag()
5204 {
5205   unformat = 1;
5206   return 1;
5207 }
5208
5209 int hmotion_node::force_tprint()
5210 {
5211   return 0;
5212 }
5213
5214 int hmotion_node::is_tag()
5215 {
5216   return 0;
5217 }
5218
5219 node *hmotion_node::add_self(node *nd, hyphen_list **p)
5220 {
5221   next = nd;
5222   hyphen_list *pp = *p;
5223   *p = (*p)->next;
5224   delete pp;
5225   return this;
5226 }
5227
5228 hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
5229 {
5230   return new hyphen_list(0, tail);
5231 }
5232
5233 int space_char_hmotion_node::same(node *nd)
5234 {
5235   return n == ((space_char_hmotion_node *)nd)->n
5236          && col == ((space_char_hmotion_node *)nd)->col;
5237 }
5238
5239 const char *space_char_hmotion_node::type()
5240 {
5241   return "space_char_hmotion_node";
5242 }
5243
5244 int space_char_hmotion_node::force_tprint()
5245 {
5246   return 0;
5247 }
5248
5249 int space_char_hmotion_node::is_tag()
5250 {
5251   return 0;
5252 }
5253
5254 node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
5255 {
5256   next = nd;
5257   hyphen_list *pp = *p;
5258   *p = (*p)->next;
5259   delete pp;
5260   return this;
5261 }
5262
5263 hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
5264                                                       int *)
5265 {
5266   return new hyphen_list(0, tail);
5267 }
5268
5269 int vmotion_node::same(node *nd)
5270 {
5271   return n == ((vmotion_node *)nd)->n
5272          && col == ((vmotion_node *)nd)->col;
5273 }
5274
5275 const char *vmotion_node::type()
5276 {
5277   return "vmotion_node";
5278 }
5279
5280 int vmotion_node::force_tprint()
5281 {
5282   return 0;
5283 }
5284
5285 int vmotion_node::is_tag()
5286 {
5287   return 0;
5288 }
5289
5290 int hline_node::same(node *nd)
5291 {
5292   return x == ((hline_node *)nd)->x && same_node(n, ((hline_node *)nd)->n);
5293 }
5294
5295 const char *hline_node::type()
5296 {
5297   return "hline_node";
5298 }
5299
5300 int hline_node::force_tprint()
5301 {
5302   return 0;
5303 }
5304
5305 int hline_node::is_tag()
5306 {
5307   return 0;
5308 }
5309
5310 int vline_node::same(node *nd)
5311 {
5312   return x == ((vline_node *)nd)->x && same_node(n, ((vline_node *)nd)->n);
5313 }
5314
5315 const char *vline_node::type()
5316 {
5317   return "vline_node";
5318 }
5319
5320 int vline_node::force_tprint()
5321 {
5322   return 0;
5323 }
5324
5325 int vline_node::is_tag()
5326 {
5327   return 0;
5328 }
5329
5330 int dummy_node::same(node * /*nd*/)
5331 {
5332   return 1;
5333 }
5334
5335 const char *dummy_node::type()
5336 {
5337   return "dummy_node";
5338 }
5339
5340 int dummy_node::force_tprint()
5341 {
5342   return 0;
5343 }
5344
5345 int dummy_node::is_tag()
5346 {
5347   return 0;
5348 }
5349
5350 int transparent_dummy_node::same(node * /*nd*/)
5351 {
5352   return 1;
5353 }
5354
5355 const char *transparent_dummy_node::type()
5356 {
5357   return "transparent_dummy_node";
5358 }
5359
5360 int transparent_dummy_node::force_tprint()
5361 {
5362   return 0;
5363 }
5364
5365 int transparent_dummy_node::is_tag()
5366 {
5367   return 0;
5368 }
5369
5370 int transparent_dummy_node::ends_sentence()
5371 {
5372   return 2;
5373 }
5374
5375 int zero_width_node::same(node *nd)
5376 {
5377   return same_node_list(n, ((zero_width_node *)nd)->n);
5378 }
5379
5380 const char *zero_width_node::type()
5381 {
5382   return "zero_width_node";
5383 }
5384
5385 int zero_width_node::force_tprint()
5386 {
5387   return 0;
5388 }
5389
5390 int zero_width_node::is_tag()
5391 {
5392   return 0;
5393 }
5394
5395 int italic_corrected_node::same(node *nd)
5396 {
5397   return (x == ((italic_corrected_node *)nd)->x
5398           && same_node(n, ((italic_corrected_node *)nd)->n));
5399 }
5400
5401 const char *italic_corrected_node::type()
5402 {
5403   return "italic_corrected_node";
5404 }
5405
5406 int italic_corrected_node::force_tprint()
5407 {
5408   return 0;
5409 }
5410
5411 int italic_corrected_node::is_tag()
5412 {
5413   return 0;
5414 }
5415
5416 left_italic_corrected_node::left_italic_corrected_node(node *xx)
5417 : node(xx), n(0)
5418 {
5419 }
5420
5421 left_italic_corrected_node::left_italic_corrected_node(statem *s, int pop,
5422                                                        node *xx)
5423 : node(xx, s, pop), n(0)
5424 {
5425 }
5426
5427 left_italic_corrected_node::~left_italic_corrected_node()
5428 {
5429   delete n;
5430 }
5431
5432 node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
5433 {
5434   if (n == 0) {
5435     hunits lic = gn->left_italic_correction();
5436     if (!lic.is_zero()) {
5437       x = lic;
5438       n = gn;
5439       return this;
5440     }
5441   }
5442   else {
5443     node *nd = n->merge_glyph_node(gn);
5444     if (nd) {
5445       n = nd;
5446       x = n->left_italic_correction();
5447       return this;
5448     }
5449   }
5450   return 0;
5451 }
5452
5453 node *left_italic_corrected_node::copy()
5454 {
5455   left_italic_corrected_node *nd =
5456     new left_italic_corrected_node(state, div_nest_level);
5457   if (n) {
5458     nd->n = n->copy();
5459     nd->x = x;
5460   }
5461   return nd;
5462 }
5463
5464 void left_italic_corrected_node::tprint(troff_output_file *out)
5465 {
5466   if (n) {
5467     out->right(x);
5468     n->tprint(out);
5469   }
5470 }
5471
5472 const char *left_italic_corrected_node::type()
5473 {
5474   return "left_italic_corrected_node";
5475 }
5476
5477 int left_italic_corrected_node::force_tprint()
5478 {
5479   return 0;
5480 }
5481
5482 int left_italic_corrected_node::is_tag()
5483 {
5484   return 0;
5485 }
5486
5487 int left_italic_corrected_node::same(node *nd)
5488 {
5489   return (x == ((left_italic_corrected_node *)nd)->x
5490           && same_node(n, ((left_italic_corrected_node *)nd)->n));
5491 }
5492
5493 void left_italic_corrected_node::ascii_print(ascii_output_file *out)
5494 {
5495   if (n)
5496     n->ascii_print(out);
5497 }
5498
5499 hunits left_italic_corrected_node::width()
5500 {
5501   return n ? n->width() + x : H0;
5502 }
5503
5504 void left_italic_corrected_node::vertical_extent(vunits *minimum,
5505                                                  vunits *maximum)
5506 {
5507   if (n)
5508     n->vertical_extent(minimum, maximum);
5509   else
5510     node::vertical_extent(minimum, maximum);
5511 }
5512
5513 hunits left_italic_corrected_node::skew()
5514 {
5515   return n ? n->skew() + x/2 : H0;
5516 }
5517
5518 hunits left_italic_corrected_node::subscript_correction()
5519 {
5520   return n ? n->subscript_correction() : H0;
5521 }
5522
5523 hunits left_italic_corrected_node::italic_correction()
5524 {
5525   return n ? n->italic_correction() : H0;
5526 }
5527
5528 int left_italic_corrected_node::ends_sentence()
5529 {
5530   return n ? n->ends_sentence() : 0;
5531 }
5532
5533 int left_italic_corrected_node::overlaps_horizontally()
5534 {
5535   return n ? n->overlaps_horizontally() : 0;
5536 }
5537
5538 int left_italic_corrected_node::overlaps_vertically()
5539 {
5540   return n ? n->overlaps_vertically() : 0;
5541 }
5542
5543 node *left_italic_corrected_node::last_char_node()
5544 {
5545   return n ? n->last_char_node() : 0;
5546 }
5547
5548 tfont *left_italic_corrected_node::get_tfont()
5549 {
5550   return n ? n->get_tfont() : 0;
5551 }
5552
5553 hyphenation_type left_italic_corrected_node::get_hyphenation_type()
5554 {
5555   if (n)
5556     return n->get_hyphenation_type();
5557   else
5558     return HYPHEN_MIDDLE;
5559 }
5560
5561 hyphen_list *left_italic_corrected_node::get_hyphen_list(hyphen_list *tail,
5562                                                          int *count)
5563 {
5564   return n ? n->get_hyphen_list(tail, count) : tail;
5565 }
5566
5567 node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
5568 {
5569   if (n) {
5570     nd = new left_italic_corrected_node(state, div_nest_level, nd);
5571     nd = n->add_self(nd, p);
5572     n = 0;
5573     delete this;
5574     return nd;
5575   }
5576   else {
5577     next = nd;
5578     return this;
5579   }
5580 }
5581
5582 int left_italic_corrected_node::character_type()
5583 {
5584   return n ? n->character_type() : 0;
5585 }
5586
5587 int overstrike_node::same(node *nd)
5588 {
5589   return same_node_list(list, ((overstrike_node *)nd)->list);
5590 }
5591
5592 const char *overstrike_node::type()
5593 {
5594   return "overstrike_node";
5595 }
5596
5597 int overstrike_node::force_tprint()
5598 {
5599   return 0;
5600 }
5601
5602 int overstrike_node::is_tag()
5603 {
5604   return 0;
5605 }
5606
5607 node *overstrike_node::add_self(node *n, hyphen_list **p)
5608 {
5609   next = n;
5610   hyphen_list *pp = *p;
5611   *p = (*p)->next;
5612   delete pp;
5613   return this;
5614 }
5615
5616 hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
5617 {
5618   return new hyphen_list(0, tail);
5619 }
5620
5621 int bracket_node::same(node *nd)
5622 {
5623   return same_node_list(list, ((bracket_node *)nd)->list);
5624 }
5625
5626 const char *bracket_node::type()
5627 {
5628   return "bracket_node";
5629 }
5630
5631 int bracket_node::force_tprint()
5632 {
5633   return 0;
5634 }
5635
5636 int bracket_node::is_tag()
5637 {
5638   return 0;
5639 }
5640
5641 int composite_node::same(node *nd)
5642 {
5643   return ci == ((composite_node *)nd)->ci
5644     && same_node_list(n, ((composite_node *)nd)->n);
5645 }
5646
5647 const char *composite_node::type()
5648 {
5649   return "composite_node";
5650 }
5651
5652 int composite_node::force_tprint()
5653 {
5654   return 0;
5655 }
5656
5657 int composite_node::is_tag()
5658 {
5659   return 0;
5660 }
5661
5662 int glyph_node::same(node *nd)
5663 {
5664   return ci == ((glyph_node *)nd)->ci
5665          && tf == ((glyph_node *)nd)->tf
5666          && gcol == ((glyph_node *)nd)->gcol
5667          && fcol == ((glyph_node *)nd)->fcol;
5668 }
5669
5670 const char *glyph_node::type()
5671 {
5672   return "glyph_node";
5673 }
5674
5675 int glyph_node::force_tprint()
5676 {
5677   return 0;
5678 }
5679
5680 int glyph_node::is_tag()
5681 {
5682   return 0;
5683 }
5684
5685 int ligature_node::same(node *nd)
5686 {
5687   return (same_node(n1, ((ligature_node *)nd)->n1)
5688           && same_node(n2, ((ligature_node *)nd)->n2)
5689           && glyph_node::same(nd));
5690 }
5691
5692 const char *ligature_node::type()
5693 {
5694   return "ligature_node";
5695 }
5696
5697 int ligature_node::force_tprint()
5698 {
5699   return 0;
5700 }
5701
5702 int ligature_node::is_tag()
5703 {
5704   return 0;
5705 }
5706
5707 int kern_pair_node::same(node *nd)
5708 {
5709   return (amount == ((kern_pair_node *)nd)->amount
5710           && same_node(n1, ((kern_pair_node *)nd)->n1)
5711           && same_node(n2, ((kern_pair_node *)nd)->n2));
5712 }
5713
5714 const char *kern_pair_node::type()
5715 {
5716   return "kern_pair_node";
5717 }
5718
5719 int kern_pair_node::force_tprint()
5720 {
5721   return 0;
5722 }
5723
5724 int kern_pair_node::is_tag()
5725 {
5726   return 0;
5727 }
5728
5729 int dbreak_node::same(node *nd)
5730 {
5731   return (same_node_list(none, ((dbreak_node *)nd)->none)
5732           && same_node_list(pre, ((dbreak_node *)nd)->pre)
5733           && same_node_list(post, ((dbreak_node *)nd)->post));
5734 }
5735
5736 const char *dbreak_node::type()
5737 {
5738   return "dbreak_node";
5739 }
5740
5741 int dbreak_node::force_tprint()
5742 {
5743   return 0;
5744 }
5745
5746 int dbreak_node::is_tag()
5747 {
5748   return 0;
5749 }
5750
5751 int break_char_node::same(node *nd)
5752 {
5753   return break_code == ((break_char_node *)nd)->break_code
5754          && col == ((break_char_node *)nd)->col
5755          && same_node(ch, ((break_char_node *)nd)->ch);
5756 }
5757
5758 const char *break_char_node::type()
5759 {
5760   return "break_char_node";
5761 }
5762
5763 int break_char_node::force_tprint()
5764 {
5765   return 0;
5766 }
5767
5768 int break_char_node::is_tag()
5769 {
5770   return 0;
5771 }
5772
5773 int break_char_node::get_break_code()
5774 {
5775   return break_code;
5776 }
5777
5778 int line_start_node::same(node * /*nd*/)
5779 {
5780   return 1;
5781 }
5782
5783 const char *line_start_node::type()
5784 {
5785   return "line_start_node";
5786 }
5787
5788 int line_start_node::force_tprint()
5789 {
5790   return 0;
5791 }
5792
5793 int line_start_node::is_tag()
5794 {
5795   return 0;
5796 }
5797
5798 int space_node::same(node *nd)
5799 {
5800   return n == ((space_node *)nd)->n
5801               && set == ((space_node *)nd)->set
5802               && col == ((space_node *)nd)->col;
5803 }
5804
5805 const char *space_node::type()
5806 {
5807   return "space_node";
5808 }
5809
5810 int word_space_node::same(node *nd)
5811 {
5812   return n == ((word_space_node *)nd)->n
5813          && set == ((word_space_node *)nd)->set
5814          && col == ((word_space_node *)nd)->col;
5815 }
5816
5817 const char *word_space_node::type()
5818 {
5819   return "word_space_node";
5820 }
5821
5822 int word_space_node::force_tprint()
5823 {
5824   return 0;
5825 }
5826
5827 int word_space_node::is_tag()
5828 {
5829   return 0;
5830 }
5831
5832 void unbreakable_space_node::tprint(troff_output_file *out)
5833 {
5834   out->fill_color(col);
5835   if (is_html) {
5836     // we emit the space width as a negative glyph index
5837     out->flush_tbuf();
5838     out->do_motion();
5839     out->put('N');
5840     out->put(-n.to_units());
5841     out->put('\n');
5842   }
5843   out->right(n);
5844 }
5845
5846 int unbreakable_space_node::same(node *nd)
5847 {
5848   return n == ((unbreakable_space_node *)nd)->n
5849          && set == ((unbreakable_space_node *)nd)->set
5850          && col == ((unbreakable_space_node *)nd)->col;
5851 }
5852
5853 const char *unbreakable_space_node::type()
5854 {
5855   return "unbreakable_space_node";
5856 }
5857
5858 node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
5859 {
5860   next = nd;
5861   hyphen_list *pp = *p;
5862   *p = (*p)->next;
5863   delete pp;
5864   return this;
5865 }
5866
5867 hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail, int *)
5868 {
5869   return new hyphen_list(0, tail);
5870 }
5871
5872 int diverted_space_node::same(node *nd)
5873 {
5874   return n == ((diverted_space_node *)nd)->n;
5875 }
5876
5877 const char *diverted_space_node::type()
5878 {
5879   return "diverted_space_node";
5880 }
5881
5882 int diverted_space_node::force_tprint()
5883 {
5884   return 0;
5885 }
5886
5887 int diverted_space_node::is_tag()
5888 {
5889   return 0;
5890 }
5891
5892 int diverted_copy_file_node::same(node *nd)
5893 {
5894   return filename == ((diverted_copy_file_node *)nd)->filename;
5895 }
5896
5897 const char *diverted_copy_file_node::type()
5898 {
5899   return "diverted_copy_file_node";
5900 }
5901
5902 int diverted_copy_file_node::force_tprint()
5903 {
5904   return 0;
5905 }
5906
5907 int diverted_copy_file_node::is_tag()
5908 {
5909   return 0;
5910 }
5911
5912 // Grow the font_table so that its size is > n.
5913
5914 static void grow_font_table(int n)
5915 {
5916   assert(n >= font_table_size);
5917   font_info **old_font_table = font_table;
5918   int old_font_table_size = font_table_size;
5919   font_table_size = font_table_size ? (font_table_size*3)/2 : 10;
5920   if (font_table_size <= n)
5921     font_table_size = n + 10;
5922   font_table = new font_info *[font_table_size];
5923   if (old_font_table_size)
5924     memcpy(font_table, old_font_table,
5925            old_font_table_size*sizeof(font_info *));
5926   a_delete old_font_table;
5927   for (int i = old_font_table_size; i < font_table_size; i++)
5928     font_table[i] = 0;
5929 }
5930
5931 dictionary font_translation_dictionary(17);
5932
5933 static symbol get_font_translation(symbol nm)
5934 {
5935   void *p = font_translation_dictionary.lookup(nm);
5936   return p ? symbol((char *)p) : nm;
5937 }
5938
5939 dictionary font_dictionary(50);
5940
5941 static int mount_font_no_translate(int n, symbol name, symbol external_name,
5942                                    int check_only = 0)
5943 {
5944   assert(n >= 0);
5945   // We store the address of this char in font_dictionary to indicate
5946   // that we've previously tried to mount the font and failed.
5947   static char a_char;
5948   font *fm = 0;
5949   void *p = font_dictionary.lookup(external_name);
5950   if (p == 0) {
5951     int not_found;
5952     fm = font::load_font(external_name.contents(), &not_found, check_only);
5953     if (check_only)
5954       return fm != 0;
5955     if (!fm) {
5956       if (not_found)
5957         warning(WARN_FONT, "can't find font `%1'", external_name.contents());
5958       (void)font_dictionary.lookup(external_name, &a_char);
5959       return 0;
5960     }
5961     (void)font_dictionary.lookup(name, fm);
5962   }
5963   else if (p == &a_char) {
5964 #if 0
5965     error("invalid font `%1'", external_name.contents());
5966 #endif
5967     return 0;
5968   }
5969   else
5970     fm = (font*)p;
5971   if (check_only)
5972     return 1;
5973   if (n >= font_table_size) {
5974     if (n - font_table_size > 1000) {
5975       error("font position too much larger than first unused position");
5976       return 0;
5977     }
5978     grow_font_table(n);
5979   }
5980   else if (font_table[n] != 0)
5981     delete font_table[n];
5982   font_table[n] = new font_info(name, n, external_name, fm);
5983   font_family::invalidate_fontno(n);
5984   return 1;
5985 }
5986
5987 int mount_font(int n, symbol name, symbol external_name)
5988 {
5989   assert(n >= 0);
5990   name = get_font_translation(name);
5991   if (external_name.is_null())
5992     external_name = name;
5993   else
5994     external_name = get_font_translation(external_name);
5995   return mount_font_no_translate(n, name, external_name);
5996 }
5997
5998 int check_font(symbol fam, symbol name)
5999 {
6000   if (check_style(name))
6001     name = concat(fam, name);
6002   return mount_font_no_translate(0, name, name, 1);
6003 }
6004
6005 int check_style(symbol s)
6006 {
6007   int i = symbol_fontno(s);
6008   return i < 0 ? 0 : font_table[i]->is_style();
6009 }
6010
6011 void mount_style(int n, symbol name)
6012 {
6013   assert(n >= 0);
6014   if (n >= font_table_size) {
6015     if (n - font_table_size > 1000) {
6016       error("font position too much larger than first unused position");
6017       return;
6018     }
6019     grow_font_table(n);
6020   }
6021   else if (font_table[n] != 0)
6022     delete font_table[n];
6023   font_table[n] = new font_info(get_font_translation(name), n, NULL_SYMBOL, 0);
6024   font_family::invalidate_fontno(n);
6025 }
6026
6027 /* global functions */
6028
6029 void font_translate()
6030 {
6031   symbol from = get_name(1);
6032   if (!from.is_null()) {
6033     symbol to = get_name();
6034     if (to.is_null() || from == to)
6035       font_translation_dictionary.remove(from);
6036     else
6037       (void)font_translation_dictionary.lookup(from, (void *)to.contents());
6038   }
6039   skip_line();
6040 }
6041
6042 void font_position()
6043 {
6044   int n;
6045   if (get_integer(&n)) {
6046     if (n < 0)
6047       error("negative font position");
6048     else {
6049       symbol internal_name = get_name(1);
6050       if (!internal_name.is_null()) {
6051         symbol external_name = get_long_name();
6052         mount_font(n, internal_name, external_name); // ignore error
6053       }
6054     }
6055   }
6056   skip_line();
6057 }
6058
6059 font_family::font_family(symbol s)
6060 : map_size(10), nm(s)
6061 {
6062   map = new int[map_size];
6063   for (int i = 0; i < map_size; i++)
6064     map[i] = -1;
6065 }
6066
6067 font_family::~font_family()
6068 {
6069   a_delete map;
6070 }
6071
6072 int font_family::make_definite(int i)
6073 {
6074   if (i >= 0) {
6075     if (i < map_size && map[i] >= 0)
6076       return map[i];
6077     else {
6078       if (i < font_table_size && font_table[i] != 0) {
6079         if (i >= map_size) {
6080           int old_map_size = map_size;
6081           int *old_map = map;
6082           map_size *= 3;
6083           map_size /= 2;
6084           if (i >= map_size)
6085             map_size = i + 10;
6086           map = new int[map_size];
6087           memcpy(map, old_map, old_map_size*sizeof(int));
6088           a_delete old_map;
6089           for (int j = old_map_size; j < map_size; j++)
6090             map[j] = -1;
6091         }
6092         if (font_table[i]->is_style()) {
6093           symbol sty = font_table[i]->get_name();
6094           symbol f = concat(nm, sty);
6095           int n;
6096           // don't use symbol_fontno, because that might return a style
6097           // and because we don't want to translate the name
6098           for (n = 0; n < font_table_size; n++)
6099             if (font_table[n] != 0 && font_table[n]->is_named(f)
6100                 && !font_table[n]->is_style())
6101               break;
6102           if (n >= font_table_size) {
6103             n = next_available_font_position();
6104             if (!mount_font_no_translate(n, f, f))
6105               return -1;
6106           }
6107           return map[i] = n;
6108         }
6109         else
6110           return map[i] = i;
6111       }
6112       else
6113         return -1;
6114     }
6115   }
6116   else
6117     return -1;
6118 }
6119
6120 dictionary family_dictionary(5);
6121
6122 font_family *lookup_family(symbol nm)
6123 {
6124   font_family *f = (font_family *)family_dictionary.lookup(nm);
6125   if (!f) {
6126     f = new font_family(nm);
6127     (void)family_dictionary.lookup(nm, f);
6128   }
6129   return f;
6130 }
6131
6132 void font_family::invalidate_fontno(int n)
6133 {
6134   assert(n >= 0 && n < font_table_size);
6135   dictionary_iterator iter(family_dictionary);
6136   symbol nam;
6137   font_family *fam;
6138   while (iter.get(&nam, (void **)&fam)) {
6139     int mapsize = fam->map_size;
6140     if (n < mapsize)
6141       fam->map[n] = -1;
6142     for (int i = 0; i < mapsize; i++)
6143       if (fam->map[i] == n)
6144         fam->map[i] = -1;
6145   }
6146 }
6147
6148 void style()
6149 {
6150   int n;
6151   if (get_integer(&n)) {
6152     if (n < 0)
6153       error("negative font position");
6154     else {
6155       symbol internal_name = get_name(1);
6156       if (!internal_name.is_null())
6157         mount_style(n, internal_name);
6158     }
6159   }
6160   skip_line();
6161 }
6162
6163 static int get_fontno()
6164 {
6165   int n;
6166   tok.skip();
6167   if (tok.delimiter()) {
6168     symbol s = get_name(1);
6169     if (!s.is_null()) {
6170       n = symbol_fontno(s);
6171       if (n < 0) {
6172         n = next_available_font_position();
6173         if (!mount_font(n, s))
6174           return -1;
6175       }
6176       return curenv->get_family()->make_definite(n);
6177     }
6178   }
6179   else if (get_integer(&n)) {
6180     if (n < 0 || n >= font_table_size || font_table[n] == 0)
6181       error("bad font number");
6182     else
6183       return curenv->get_family()->make_definite(n);
6184   }
6185   return -1;
6186 }
6187
6188 static int underline_fontno = 2;
6189
6190 void underline_font()
6191 {
6192   int n = get_fontno();
6193   if (n >= 0)
6194     underline_fontno = n;
6195   skip_line();
6196 }
6197
6198 int get_underline_fontno()
6199 {
6200   return underline_fontno;
6201 }
6202
6203 void define_font_special_character()
6204 {
6205   int n = get_fontno();
6206   if (n < 0) {
6207     skip_line();
6208     return;
6209   }
6210   symbol f = font_table[n]->get_name();
6211   do_define_character(CHAR_FONT_SPECIAL, f.contents());
6212 }
6213
6214 void remove_font_special_character()
6215 {
6216   int n = get_fontno();
6217   if (n < 0) {
6218     skip_line();
6219     return;
6220   }
6221   symbol f = font_table[n]->get_name();
6222   while (!tok.newline() && !tok.eof()) {
6223     if (!tok.space() && !tok.tab()) {
6224       charinfo *s = tok.get_char(1);
6225       string gl(f.contents());
6226       gl += ' ';
6227       gl += s->nm.contents();
6228       gl += '\0';
6229       charinfo *ci = get_charinfo(symbol(gl.contents()));
6230       if (!ci)
6231         break;
6232       macro *m = ci->set_macro(0);
6233       if (m)
6234         delete m;
6235     }
6236     tok.next();
6237   }
6238   skip_line();
6239 }
6240
6241 static void read_special_fonts(special_font_list **sp)
6242 {
6243   special_font_list *s = *sp;
6244   *sp = 0;
6245   while (s != 0) {
6246     special_font_list *tem = s;
6247     s = s->next;
6248     delete tem;
6249   }
6250   special_font_list **p = sp;
6251   while (has_arg()) {
6252     int i = get_fontno();
6253     if (i >= 0) {
6254       special_font_list *tem = new special_font_list;
6255       tem->n = i;
6256       tem->next = 0;
6257       *p = tem;
6258       p = &(tem->next);
6259     }
6260   }
6261 }
6262
6263 void font_special_request()
6264 {
6265   int n = get_fontno();
6266   if (n >= 0)
6267     read_special_fonts(&font_table[n]->sf);
6268   skip_line();
6269 }
6270
6271 void special_request()
6272 {
6273   read_special_fonts(&global_special_fonts);
6274   skip_line();
6275 }
6276
6277 void font_zoom_request()
6278 {
6279   int n = get_fontno();
6280   if (n >= 0) {
6281     if (font_table[n]->is_style())
6282       warning(WARN_FONT, "can't set zoom factor for a style");
6283     else {
6284       int zoom;
6285       if (has_arg() && get_integer(&zoom)) {
6286         if (zoom < 0)
6287           warning(WARN_FONT, "can't use negative zoom factor");
6288         else
6289           font_table[n]->set_zoom(zoom);
6290       }
6291       else
6292         font_table[n]->set_zoom(0);
6293     }
6294   }
6295   skip_line();
6296 }
6297
6298 int next_available_font_position()
6299 {
6300   int i;
6301   for (i = 1; i < font_table_size && font_table[i] != 0; i++)
6302     ;
6303   return i;
6304 }
6305
6306 int symbol_fontno(symbol s)
6307 {
6308   s = get_font_translation(s);
6309   for (int i = 0; i < font_table_size; i++)
6310     if (font_table[i] != 0 && font_table[i]->is_named(s))
6311       return i;
6312   return -1;
6313 }
6314
6315 int is_good_fontno(int n)
6316 {
6317   return n >= 0 && n < font_table_size && font_table[n] != 0;
6318 }
6319
6320 int get_bold_fontno(int n)
6321 {
6322   if (n >= 0 && n < font_table_size && font_table[n] != 0) {
6323     hunits offset;
6324     if (font_table[n]->get_bold(&offset))
6325       return offset.to_units() + 1;
6326     else
6327       return 0;
6328   }
6329   else
6330     return 0;
6331 }
6332
6333 hunits env_digit_width(environment *env)
6334 {
6335   node *n = make_glyph_node(charset_table['0'], env);
6336   if (n) {
6337     hunits x = n->width();
6338     delete n;
6339     return x;
6340   }
6341   else
6342     return H0;
6343 }
6344
6345 hunits env_space_width(environment *env)
6346 {
6347   int fn = env_definite_font(env);
6348   font_size fs = env->get_font_size();
6349   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6350     return scale(fs.to_units()/3, env->get_space_size(), 12);
6351   else
6352     return font_table[fn]->get_space_width(fs, env->get_space_size());
6353 }
6354
6355 hunits env_sentence_space_width(environment *env)
6356 {
6357   int fn = env_definite_font(env);
6358   font_size fs = env->get_font_size();
6359   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6360     return scale(fs.to_units()/3, env->get_sentence_space_size(), 12);
6361   else
6362     return font_table[fn]->get_space_width(fs, env->get_sentence_space_size());
6363 }
6364
6365 hunits env_half_narrow_space_width(environment *env)
6366 {
6367   int fn = env_definite_font(env);
6368   font_size fs = env->get_font_size();
6369   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6370     return 0;
6371   else
6372     return font_table[fn]->get_half_narrow_space_width(fs);
6373 }
6374
6375 hunits env_narrow_space_width(environment *env)
6376 {
6377   int fn = env_definite_font(env);
6378   font_size fs = env->get_font_size();
6379   if (fn < 0 || fn >= font_table_size || font_table[fn] == 0)
6380     return 0;
6381   else
6382     return font_table[fn]->get_narrow_space_width(fs);
6383 }
6384
6385 void bold_font()
6386 {
6387   int n = get_fontno();
6388   if (n >= 0) {
6389     if (has_arg()) {
6390       if (tok.delimiter()) {
6391         int f = get_fontno();
6392         if (f >= 0) {
6393           units offset;
6394           if (has_arg() && get_number(&offset, 'u') && offset >= 1)
6395             font_table[f]->set_conditional_bold(n, hunits(offset - 1));
6396           else
6397             font_table[f]->conditional_unbold(n);
6398         }
6399       }
6400       else {
6401         units offset;
6402         if (get_number(&offset, 'u') && offset >= 1)
6403           font_table[n]->set_bold(hunits(offset - 1));
6404         else
6405           font_table[n]->unbold();
6406       }
6407     }
6408     else
6409       font_table[n]->unbold();
6410   }
6411   skip_line();
6412 }
6413
6414 track_kerning_function::track_kerning_function() : non_zero(0)
6415 {
6416 }
6417
6418 track_kerning_function::track_kerning_function(int min_s, hunits min_a,
6419                                                int max_s, hunits max_a)
6420 : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
6421   max_amount(max_a)
6422 {
6423 }
6424
6425 int track_kerning_function::operator==(const track_kerning_function &tk)
6426 {
6427   if (non_zero)
6428     return (tk.non_zero
6429             && min_size == tk.min_size
6430             && min_amount == tk.min_amount
6431             && max_size == tk.max_size
6432             && max_amount == tk.max_amount);
6433   else
6434     return !tk.non_zero;
6435 }
6436
6437 int track_kerning_function::operator!=(const track_kerning_function &tk)
6438 {
6439   if (non_zero)
6440     return (!tk.non_zero
6441             || min_size != tk.min_size
6442             || min_amount != tk.min_amount
6443             || max_size != tk.max_size
6444             || max_amount != tk.max_amount);
6445   else
6446     return tk.non_zero;
6447 }
6448
6449 hunits track_kerning_function::compute(int size)
6450 {
6451   if (non_zero) {
6452     if (max_size <= min_size)
6453       return min_amount;
6454     else if (size <= min_size)
6455       return min_amount;
6456     else if (size >= max_size)
6457       return max_amount;
6458     else
6459       return (scale(max_amount, size - min_size, max_size - min_size)
6460               + scale(min_amount, max_size - size, max_size - min_size));
6461   }
6462   else
6463     return H0;
6464 }
6465
6466 void track_kern()
6467 {
6468   int n = get_fontno();
6469   if (n >= 0) {
6470     int min_s, max_s;
6471     hunits min_a, max_a;
6472     if (has_arg()
6473         && get_number(&min_s, 'z')
6474         && get_hunits(&min_a, 'p')
6475         && get_number(&max_s, 'z')
6476         && get_hunits(&max_a, 'p')) {
6477       track_kerning_function tk(min_s, min_a, max_s, max_a);
6478       font_table[n]->set_track_kern(tk);
6479     }
6480     else {
6481       track_kerning_function tk;
6482       font_table[n]->set_track_kern(tk);
6483     }
6484   }
6485   skip_line();
6486 }
6487
6488 void constant_space()
6489 {
6490   int n = get_fontno();
6491   if (n >= 0) {
6492     int x, y;
6493     if (!has_arg() || !get_integer(&x))
6494       font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
6495     else {
6496       if (!has_arg() || !get_number(&y, 'z'))
6497         font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
6498       else
6499         font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
6500                                           scale(y*x,
6501                                                 units_per_inch,
6502                                                 36*72*sizescale));
6503     }
6504   }
6505   skip_line();
6506 }
6507
6508 void ligature()
6509 {
6510   int lig;
6511   if (has_arg() && get_integer(&lig) && lig >= 0 && lig <= 2)
6512     global_ligature_mode = lig;
6513   else
6514     global_ligature_mode = 1;
6515   skip_line();
6516 }
6517
6518 void kern_request()
6519 {
6520   int k;
6521   if (has_arg() && get_integer(&k))
6522     global_kern_mode = k != 0;
6523   else
6524     global_kern_mode = 1;
6525   skip_line();
6526 }
6527
6528 void set_soft_hyphen_char()
6529 {
6530   soft_hyphen_char = get_optional_char();
6531   if (!soft_hyphen_char)
6532     soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6533   skip_line();
6534 }
6535
6536 void init_output()
6537 {
6538   if (suppress_output_flag)
6539     the_output = new suppress_output_file;
6540   else if (ascii_output_flag)
6541     the_output = new ascii_output_file;
6542   else
6543     the_output = new troff_output_file;
6544 }
6545
6546 class next_available_font_position_reg : public reg {
6547 public:
6548   const char *get_string();
6549 };
6550
6551 const char *next_available_font_position_reg::get_string()
6552 {
6553   return i_to_a(next_available_font_position());
6554 }
6555
6556 class printing_reg : public reg {
6557 public:
6558   const char *get_string();
6559 };
6560
6561 const char *printing_reg::get_string()
6562 {
6563   if (the_output)
6564     return the_output->is_printing() ? "1" : "0";
6565   else
6566     return "0";
6567 }
6568
6569 void init_node_requests()
6570 {
6571   init_request("bd", bold_font);
6572   init_request("cs", constant_space);
6573   init_request("fp", font_position);
6574   init_request("fschar", define_font_special_character);
6575   init_request("fspecial", font_special_request);
6576   init_request("fzoom", font_zoom_request);
6577   init_request("ftr", font_translate);
6578   init_request("kern", kern_request);
6579   init_request("lg", ligature);
6580   init_request("rfschar", remove_font_special_character);
6581   init_request("shc", set_soft_hyphen_char);
6582   init_request("special", special_request);
6583   init_request("sty", style);
6584   init_request("tkf", track_kern);
6585   init_request("uf", underline_font);
6586   number_reg_dictionary.define(".fp", new next_available_font_position_reg);
6587   number_reg_dictionary.define(".kern",
6588                                new constant_int_reg(&global_kern_mode));
6589   number_reg_dictionary.define(".lg",
6590                                new constant_int_reg(&global_ligature_mode));
6591   number_reg_dictionary.define(".P", new printing_reg);
6592   soft_hyphen_char = get_charinfo(HYPHEN_SYMBOL);
6593 }