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