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