LCOV - code coverage report
Current view: top level - roff/troff - node.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 2940 4142 71.0 %
Date: 2026-01-16 17:51:41 Functions: 564 723 78.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2020 Free Software Foundation, Inc.
       2             :              2021-2025 G. Branden Robinson
       3             : 
       4             :      Written by James Clark (jjc@jclark.com)
       5             : 
       6             : This file is part of groff, the GNU roff typesetting system.
       7             : 
       8             : groff is free software; you can redistribute it and/or modify it under
       9             : the terms of the GNU General Public License as published by the Free
      10             : Software Foundation, either version 3 of the License, or
      11             : (at your option) any later version.
      12             : 
      13             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      14             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      15             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      16             : for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <errno.h>
      26             : #include <stdlib.h> // free(), malloc()
      27             : #include <string.h> // strerror()
      28             : 
      29             : #include "troff.h"
      30             : #include "dictionary.h"
      31             : #include "hvunits.h"
      32             : #include "stringclass.h"
      33             : #include "mtsm.h"
      34             : #include "env.h"
      35             : #include "request.h"
      36             : #include "node.h"
      37             : #include "token.h"
      38             : #include "div.h"
      39             : #include "reg.h"
      40             : #include "font.h"
      41             : #include "charinfo.h"
      42             : #include "input.h"
      43             : #include "geometry.h"
      44             : #include "json-encode.h" // json_encode_char()
      45             : 
      46             : #include "posix.h"
      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             : #include <stack>
      67             : 
      68             : static bool is_output_supressed = false;
      69             : 
      70             : // declarations to avoid friend name injections
      71             : class tfont;
      72             : class tfont_spec;
      73             : tfont *make_tfont(tfont_spec &);
      74             : 
      75             : 
      76             : /*
      77             :  *  how many boundaries of images have been written? Useful for
      78             :  *  debugging grohtml
      79             :  */
      80             : 
      81             : int image_no = 0;
      82             : static int suppression_starting_page_number = 0;
      83             : static bool was_any_page_in_output_list = false;
      84             : 
      85             : #define STORE_WIDTH 1
      86             : 
      87             : symbol HYPHEN_SYMBOL("hy");
      88             : 
      89             : // Character used when a hyphen is inserted at a line break.
      90             : static charinfo *soft_hyphen_char;
      91             : 
      92             : enum constant_space_type {
      93             :   CONSTANT_SPACE_NONE,
      94             :   CONSTANT_SPACE_RELATIVE,
      95             :   CONSTANT_SPACE_ABSOLUTE
      96             :   };
      97             : 
      98             : struct special_font_list {
      99             :   int n;
     100             :   special_font_list *next;
     101             : };
     102             : 
     103             : special_font_list *global_special_fonts;
     104             : static int global_ligature_mode = 1; // three-valued Boolean :-|
     105             : static bool global_kern_mode = true;
     106             : // Font mounting positions are non-negative integers.
     107             : const int FONT_NOT_MOUNTED = -1;
     108             : 
     109             : class track_kerning_function {
     110             :   int non_zero;
     111             :   units min_size;
     112             :   hunits min_amount;
     113             :   units max_size;
     114             :   hunits max_amount;
     115             : public:
     116             :   track_kerning_function();
     117             :   track_kerning_function(units, hunits, units, hunits);
     118             :   int operator==(const track_kerning_function &);
     119             :   int operator!=(const track_kerning_function &);
     120             :   hunits compute(int point_size);
     121             : };
     122             : 
     123             : struct font_lookup_info {
     124             :   int position;
     125             :   int requested_position;
     126             :   char *requested_name;
     127             :   font_lookup_info();
     128             : };
     129             : 
     130       11015 : font_lookup_info::font_lookup_info() : position(FONT_NOT_MOUNTED),
     131       11015 :   requested_position(FONT_NOT_MOUNTED), requested_name(0)
     132             : {
     133       11015 : }
     134             : 
     135             : // embolden fontno when this is the current font
     136             : 
     137             : struct conditional_bold {
     138             :   conditional_bold *next;
     139             :   int fontno;
     140             :   hunits offset;
     141             :   conditional_bold(int, hunits, conditional_bold * = 0 /* nullptr */);
     142             : };
     143             : 
     144             : class font_info {
     145             :   tfont *last_tfont;
     146             :   int number;
     147             :   font_size last_size;
     148             :   int last_height;
     149             :   int last_slant;
     150             :   symbol internal_name;
     151             :   symbol external_name;
     152             :   font *fm;
     153             :   bool has_emboldening;
     154             :   hunits bold_offset;
     155             :   track_kerning_function track_kern;
     156             :   constant_space_type is_constant_spaced;
     157             :   units constant_space;
     158             :   int last_ligature_mode;
     159             :   int last_kern_mode;
     160             :   conditional_bold *cond_bold_list;
     161             :   void flush();
     162             : public:
     163             :   special_font_list *sf;
     164             :   font_info(symbol, int, symbol, font *);
     165             :   int contains(charinfo *);
     166             :   void set_bold(hunits);
     167             :   void unbold();
     168             :   void set_conditional_bold(int, hunits);
     169             :   void conditional_unbold(int);
     170             :   void set_track_kern(track_kerning_function &);
     171             :   void set_constant_space(constant_space_type, units = 0);
     172             :   int is_named(symbol);
     173             :   symbol get_name();
     174             :   tfont *get_tfont(font_size, int, int, int);
     175             :   hunits get_space_width(font_size, int);
     176             :   hunits get_narrow_space_width(font_size);
     177             :   hunits get_half_narrow_space_width(font_size);
     178             :   bool is_emboldened(hunits *); // "by how many hunits?" in argument
     179             :   int is_special();
     180             :   int is_style();
     181             :   void set_zoom(int);
     182             :   int get_zoom();
     183             :   font *get_font() const;
     184             :   friend symbol get_font_name(int, environment *);
     185             :   friend symbol get_style_name(int);
     186             : };
     187             : 
     188             : class tfont_spec {
     189             : protected:
     190             :   symbol name;
     191             :   int input_position;
     192             :   font *fm;
     193             :   font_size size;
     194             :   bool has_emboldening;
     195             :   bool has_constant_spacing;
     196             :   int ligature_mode;
     197             :   int kern_mode;
     198             :   hunits bold_offset;
     199             :   hunits track_kern;                    // add this to the width
     200             :   hunits constant_space_width;
     201             :   int height;
     202             :   int slant;
     203             : public:
     204             :   tfont_spec(symbol, int, font *, font_size, int, int);
     205             :   tfont_spec plain();
     206             :   bool operator==(const tfont_spec &);
     207             :   friend tfont *font_info::get_tfont(font_size fs, int, int, int);
     208             : };
     209             : 
     210             : class tfont : public tfont_spec {
     211             :   static tfont *tfont_list;
     212             :   tfont *next;
     213             :   tfont *plain_version;
     214             : public:
     215             :   tfont(tfont_spec &);
     216             :   int contains(charinfo *);
     217             :   hunits get_width(charinfo *c);
     218             :   bool is_emboldened(hunits *); // "by how many hunits?" in argument
     219             :   bool is_constantly_spaced(hunits *); // "by how many hunits?" in arg
     220             :   hunits get_track_kern();
     221             :   tfont *get_plain();
     222             :   font_size get_size();
     223             :   int get_zoom();
     224             :   symbol get_name();
     225             :   charinfo *get_lig(charinfo *c1, charinfo *c2);
     226             :   bool is_kerned(charinfo *c1, charinfo *c2, hunits *res);
     227             :   int get_input_position();
     228             :   int get_character_type(charinfo *);
     229             :   int get_height();
     230             :   int get_slant();
     231             :   vunits get_char_height(charinfo *);
     232             :   vunits get_char_depth(charinfo *);
     233             :   hunits get_char_skew(charinfo *);
     234             :   hunits get_italic_correction(charinfo *);
     235             :   hunits get_left_italic_correction(charinfo *);
     236             :   hunits get_subscript_correction(charinfo *);
     237             :   friend tfont *make_tfont(tfont_spec &);
     238             : };
     239             : 
     240    14598126 : static inline int env_resolve_font(environment *env)
     241             : {
     242    14598126 :   return env->get_family()->resolve(env->get_font());
     243             : }
     244             : 
     245             : /* font_info functions */
     246             : 
     247             : static font_info **font_table = 0 /* nullptr */;
     248             : static int font_table_size = 0;
     249             : 
     250       17341 : font_info::font_info(symbol nm, int n, symbol enm, font *f)
     251             : : last_tfont(0 /* nullptr */), number(n), last_size(0),
     252             :   internal_name(nm), external_name(enm), fm(f),
     253             :   has_emboldening(false), is_constant_spaced(CONSTANT_SPACE_NONE),
     254             :   last_ligature_mode(1), last_kern_mode(1),
     255       17341 :   cond_bold_list(0 /* nullptr */), sf(0 /* nullptr */)
     256             : {
     257       17341 : }
     258             : 
     259    12900708 : inline int font_info::contains(charinfo *ci)
     260             : {
     261    12900708 :   return (fm != 0 /* nullptr */) && fm->contains(ci->as_glyph());
     262             : }
     263             : 
     264      129302 : inline int font_info::is_special()
     265             : {
     266      129302 :   return (fm != 0 /* nullptr */) && fm->is_special();
     267             : }
     268             : 
     269       15417 : inline int font_info::is_style()
     270             : {
     271       15417 :   return (0 /* nullptr */ == fm);
     272             : }
     273             : 
     274           0 : void font_info::set_zoom(int zoom)
     275             : {
     276           0 :   assert(fm != 0 /* nullptr */);
     277           0 :   fm->set_zoom(zoom);
     278           0 : }
     279             : 
     280           0 : inline int font_info::get_zoom()
     281             : {
     282           0 :   if (is_style())
     283           0 :     return 0;
     284           0 :   return fm->get_zoom();
     285             : }
     286             : 
     287           0 : font *font_info::get_font() const
     288             : {
     289           0 :   return fm;
     290             : }
     291             : 
     292       19136 : tfont *make_tfont(tfont_spec &spec)
     293             : {
     294      750446 :   for (tfont *p = tfont::tfont_list; p != 0 /* nullptr */; p = p->next)
     295      746630 :     if (*p == spec)
     296       15320 :       return p;
     297        3816 :   return new tfont(spec);
     298             : }
     299             : 
     300           0 : int env_get_zoom(environment *env)
     301             : {
     302           0 :   int fontno = env->get_family()->resolve(env->get_font());
     303           0 :   return font_table[fontno]->get_zoom();
     304             : }
     305             : 
     306             : // this is the current_font, fontno is where we found the character,
     307             : // presumably a special font
     308             : 
     309    12991235 : tfont *font_info::get_tfont(font_size fs, int height, int slant,
     310             :                             int fontno)
     311             : {
     312    25982470 :   if (0 /* nullptr */ == last_tfont
     313    12988933 :       || fs != last_size
     314    12983334 :       || height != last_height
     315    12983334 :       || slant != last_slant
     316    12983334 :       || global_ligature_mode != last_ligature_mode
     317    12983334 :       || global_kern_mode != last_kern_mode
     318    25980168 :       || fontno != number) {
     319       19136 :         font_info *f = font_table[fontno];
     320             :         tfont_spec spec(f->external_name, f->number, f->fm, fs, height,
     321       19136 :                         slant);
     322       19136 :         for (conditional_bold *p = cond_bold_list;
     323       19137 :              p != 0 /* nullptr */;
     324           1 :              p = p->next)
     325           2 :           if (p->fontno == fontno) {
     326           1 :             spec.has_emboldening = true;
     327           1 :             spec.bold_offset = p->offset;
     328           1 :             break;
     329             :           }
     330       19136 :         if (!spec.has_emboldening && has_emboldening) {
     331           2 :           spec.has_emboldening = true;
     332           2 :           spec.bold_offset = bold_offset;
     333             :         }
     334       19136 :         spec.track_kern = track_kern.compute(fs.to_scaled_points());
     335       19136 :         spec.ligature_mode = global_ligature_mode;
     336       19136 :         spec.kern_mode = global_kern_mode;
     337       19136 :         switch (is_constant_spaced) {
     338       19136 :         case CONSTANT_SPACE_NONE:
     339       19136 :           break;
     340           0 :         case CONSTANT_SPACE_ABSOLUTE:
     341           0 :           spec.has_constant_spacing = true;
     342           0 :           spec.constant_space_width = constant_space;
     343           0 :           break;
     344           0 :         case CONSTANT_SPACE_RELATIVE:
     345           0 :           spec.has_constant_spacing = true;
     346             :           spec.constant_space_width
     347           0 :             = scale(constant_space * fs.to_scaled_points(),
     348             :                     units_per_inch,
     349           0 :                     36 * 72 * sizescale);
     350           0 :           break;
     351           0 :         default:
     352           0 :           assert(0 == "unhandled case of constant spacing mode");
     353             :         }
     354       19136 :         if (fontno != number)
     355       11089 :           return make_tfont(spec);
     356             :         // save font for comparison purposes
     357        8047 :         last_tfont = make_tfont(spec);
     358             :         // save font related values not contained in tfont
     359        8047 :         last_size = fs;
     360        8047 :         last_height = height;
     361        8047 :         last_slant = slant;
     362        8047 :         last_ligature_mode = global_ligature_mode;
     363        8047 :         last_kern_mode = global_kern_mode;
     364             :       }
     365    12980146 :   return last_tfont;
     366             : }
     367             : 
     368           1 : bool font_info::is_emboldened(hunits *res)
     369             : {
     370           1 :   if (has_emboldening) {
     371           1 :     *res = bold_offset;
     372           1 :     return true;
     373             :   }
     374             :   else
     375           0 :     return false;
     376             : }
     377             : 
     378           1 : void font_info::unbold()
     379             : {
     380           1 :   if (has_emboldening) {
     381           1 :     has_emboldening = false;
     382           1 :     flush();
     383             :   }
     384           1 : }
     385             : 
     386           3 : void font_info::set_bold(hunits offset)
     387             : {
     388           3 :   if (!has_emboldening || (offset != bold_offset)) {
     389           3 :     has_emboldening = true;
     390           3 :     bold_offset = offset;
     391           3 :     flush();
     392             :   }
     393           3 : }
     394             : 
     395           1 : void font_info::set_conditional_bold(int fontno, hunits offset)
     396             : {
     397           1 :   for (conditional_bold *p = cond_bold_list;
     398           1 :        p != 0 /* nullptr */;
     399           0 :        p = p->next)
     400           0 :     if (p->fontno == fontno) {
     401           0 :       if (offset != p->offset) {
     402           0 :         p->offset = offset;
     403           0 :         flush();
     404             :       }
     405           0 :       return;
     406             :     }
     407           1 :   cond_bold_list = new conditional_bold(fontno, offset, cond_bold_list);
     408             : }
     409             : 
     410           1 : conditional_bold::conditional_bold(int f, hunits h, conditional_bold *x)
     411           1 : : next(x), fontno(f), offset(h)
     412             : {
     413           1 : }
     414             : 
     415           1 : void font_info::conditional_unbold(int fontno)
     416             : {
     417           1 :   for (conditional_bold **p = &cond_bold_list;
     418           1 :        *p != 0 /* nullptr */;
     419           0 :        p = &(*p)->next)
     420           1 :     if ((*p)->fontno == fontno) {
     421           1 :       conditional_bold *tem = *p;
     422           1 :       *p = (*p)->next;
     423           1 :       delete tem;
     424           1 :       flush();
     425           1 :       return;
     426             :     }
     427             : }
     428             : 
     429           0 : void font_info::set_constant_space(constant_space_type type, units x)
     430             : {
     431           0 :   if (type != is_constant_spaced
     432           0 :       || (type != CONSTANT_SPACE_NONE && x != constant_space)) {
     433           0 :     flush();
     434           0 :     is_constant_spaced = type;
     435           0 :     constant_space = x;
     436             :   }
     437           0 : }
     438             : 
     439         162 : void font_info::set_track_kern(track_kerning_function &tk)
     440             : {
     441         162 :   if (track_kern != tk) {
     442         156 :     track_kern = tk;
     443         156 :     flush();
     444             :   }
     445         162 : }
     446             : 
     447         161 : void font_info::flush()
     448             : {
     449         161 :   last_tfont = 0;
     450         161 : }
     451             : 
     452     1600478 : int font_info::is_named(symbol s)
     453             : {
     454     1600478 :   return internal_name == s;
     455             : }
     456             : 
     457       26479 : symbol font_info::get_name()
     458             : {
     459       26479 :   return internal_name;
     460             : }
     461             : 
     462        1143 : symbol get_font_name(int fontno, environment *env)
     463             : {
     464        1143 :   symbol f = font_table[fontno]->get_name();
     465        1143 :   if (font_table[fontno]->is_style()) {
     466         935 :     return concat(env->get_family()->nm, f);
     467             :   }
     468         208 :   return f;
     469             : }
     470             : 
     471        2658 : symbol get_style_name(int fontno)
     472             : {
     473        2658 :   if (font_table[fontno]->is_style())
     474        2463 :     return font_table[fontno]->get_name();
     475             :   else
     476         195 :     return EMPTY_SYMBOL;
     477             : }
     478             : 
     479     1591979 : hunits font_info::get_space_width(font_size fs, int space_sz)
     480             : {
     481     1591979 :   if (is_constant_spaced == CONSTANT_SPACE_NONE)
     482     1591979 :     return scale(hunits(fm->get_space_width(fs.to_scaled_points())),
     483     1591979 :                         space_sz, 12);
     484           0 :   else if (is_constant_spaced == CONSTANT_SPACE_ABSOLUTE)
     485           0 :     return constant_space;
     486             :   else
     487           0 :     return scale(constant_space*fs.to_scaled_points(),
     488           0 :                  units_per_inch, 36 * 72 * sizescale);
     489             : }
     490             : 
     491        5349 : hunits font_info::get_narrow_space_width(font_size fs)
     492             : {
     493        5349 :   charinfo *ci = lookup_charinfo(symbol("|"));
     494        5349 :   if (fm->contains(ci->as_glyph()))
     495           0 :     return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
     496             :   else
     497        5349 :     return hunits(fs.to_units()/6);
     498             : }
     499             : 
     500         310 : hunits font_info::get_half_narrow_space_width(font_size fs)
     501             : {
     502         310 :   charinfo *ci = lookup_charinfo(symbol("^"));
     503         310 :   if (fm->contains(ci->as_glyph()))
     504           0 :     return hunits(fm->get_width(ci->as_glyph(), fs.to_scaled_points()));
     505             :   else
     506         310 :     return hunits(fs.to_units()/12);
     507             : }
     508             : 
     509             : /* tfont */
     510             : 
     511       22955 : tfont_spec::tfont_spec(symbol nm, int n, font *f,
     512       22955 :                        font_size s, int h, int sl)
     513             : : name(nm), input_position(n), fm(f), size(s),
     514             :   has_emboldening(false), has_constant_spacing(false), ligature_mode(1),
     515       22955 :   kern_mode(1), height(h), slant(sl)
     516             : {
     517       22955 :   if (height == size.to_scaled_points())
     518           0 :     height = 0;
     519       22955 : }
     520             : 
     521      753787 : bool tfont_spec::operator==(const tfont_spec &spec)
     522             : {
     523     1507574 :   if (fm == spec.fm
     524      251211 :       && size == spec.size
     525       20854 :       && input_position == spec.input_position
     526       20854 :       && name == spec.name
     527       20854 :       && height == spec.height
     528       20854 :       && slant == spec.slant
     529       20857 :       && (has_emboldening
     530           3 :           ? (spec.has_emboldening && bold_offset == spec.bold_offset)
     531       20851 :           : !spec.has_emboldening)
     532       20851 :       && track_kern == spec.track_kern
     533       19560 :       && (has_constant_spacing
     534           0 :           ? (spec.has_constant_spacing
     535           0 :              && constant_space_width == spec.constant_space_width)
     536       19560 :           : !spec.has_constant_spacing)
     537       19560 :       && ligature_mode == spec.ligature_mode
     538     1045412 :       && kern_mode == spec.kern_mode)
     539       19136 :     return true;
     540             :   else
     541      734651 :     return false;
     542             : }
     543             : 
     544        3819 : tfont_spec tfont_spec::plain()
     545             : {
     546        3819 :   return tfont_spec(name, input_position, fm, size, height, slant);
     547             : }
     548             : 
     549    15413897 : hunits tfont::get_width(charinfo *c)
     550             : {
     551    15413897 :   if (has_constant_spacing)
     552           0 :     return constant_space_width;
     553    15413897 :   else if (has_emboldening)
     554           0 :     return (hunits(fm->get_width(c->as_glyph(),
     555           6 :                    size.to_scaled_points()))
     556          12 :             + track_kern + bold_offset);
     557             :   else
     558    15413891 :     return (hunits(fm->get_width(c->as_glyph(),
     559    15413891 :                    size.to_scaled_points()))
     560    30827782 :             + track_kern);
     561             : }
     562             : 
     563     2701291 : vunits tfont::get_char_height(charinfo *c)
     564             : {
     565     2701291 :   vunits v = fm->get_height(c->as_glyph(), size.to_scaled_points());
     566     2701291 :   if (height != 0 && height != size.to_scaled_points())
     567        1556 :     return scale(v, height, size.to_scaled_points());
     568             :   else
     569     2699735 :     return v;
     570             : }
     571             : 
     572     2701291 : vunits tfont::get_char_depth(charinfo *c)
     573             : {
     574     2701291 :   vunits v = fm->get_depth(c->as_glyph(), size.to_scaled_points());
     575     2701291 :   if (height != 0 && height != size.to_scaled_points())
     576        1556 :     return scale(v, height, size.to_scaled_points());
     577             :   else
     578     2699735 :     return v;
     579             : }
     580             : 
     581       20778 : hunits tfont::get_char_skew(charinfo *c)
     582             : {
     583       41556 :   return hunits(fm->get_skew(c->as_glyph(), size.to_scaled_points(),
     584       20778 :                              slant));
     585             : }
     586             : 
     587     2539271 : hunits tfont::get_italic_correction(charinfo *c)
     588             : {
     589     5078542 :   return hunits(fm->get_italic_correction(c->as_glyph(),
     590     2539271 :                                           size.to_scaled_points()));
     591             : }
     592             : 
     593       32205 : hunits tfont::get_left_italic_correction(charinfo *c)
     594             : {
     595       64410 :   return hunits(fm->get_left_italic_correction(c->as_glyph(),
     596       32205 :                                                size.to_scaled_points()));
     597             : }
     598             : 
     599       21035 : hunits tfont::get_subscript_correction(charinfo *c)
     600             : {
     601       42070 :   return hunits(fm->get_subscript_correction(c->as_glyph(),
     602       21035 :                                              size.to_scaled_points()));
     603             : }
     604             : 
     605      136167 : inline int tfont::get_input_position()
     606             : {
     607      136167 :   return input_position;
     608             : }
     609             : 
     610       24563 : inline int tfont::contains(charinfo *ci)
     611             : {
     612       24563 :   return fm->contains(ci->as_glyph());
     613             : }
     614             : 
     615      183183 : inline int tfont::get_character_type(charinfo *ci)
     616             : {
     617      183183 :   return fm->get_character_type(ci->as_glyph());
     618             : }
     619             : 
     620      118502 : inline bool tfont::is_emboldened(hunits *res)
     621             : {
     622      118502 :   if (has_emboldening) {
     623           6 :     *res = bold_offset;
     624           6 :     return true;
     625             :   }
     626             :   else
     627      118496 :     return false;
     628             : }
     629             : 
     630      118502 : inline bool tfont::is_constantly_spaced(hunits *res)
     631             : {
     632      118502 :   if (has_constant_spacing) {
     633           0 :     *res = constant_space_width;
     634           0 :     return true;
     635             :   }
     636             :   else
     637      118502 :     return false;
     638             : }
     639             : 
     640      118263 : inline hunits tfont::get_track_kern()
     641             : {
     642      118263 :   return track_kern;
     643             : }
     644             : 
     645     2660259 : inline tfont *tfont::get_plain()
     646             : {
     647     2660259 :   return plain_version;
     648             : }
     649             : 
     650      136167 : inline font_size tfont::get_size()
     651             : {
     652      136167 :   return size;
     653             : }
     654             : 
     655      136167 : inline int tfont::get_zoom()
     656             : {
     657      136167 :   return fm->get_zoom();
     658             : }
     659             : 
     660      136167 : inline symbol tfont::get_name()
     661             : {
     662      136167 :   return name;
     663             : }
     664             : 
     665      136167 : inline int tfont::get_height()
     666             : {
     667      136167 :   return height;
     668             : }
     669             : 
     670      136167 : inline int tfont::get_slant()
     671             : {
     672      136167 :   return slant;
     673             : }
     674             : 
     675             : symbol SYMBOL_ff("ff");
     676             : symbol SYMBOL_fi("fi");
     677             : symbol SYMBOL_fl("fl");
     678             : symbol SYMBOL_Fi("Fi");
     679             : symbol SYMBOL_Fl("Fl");
     680             : 
     681    10468377 : charinfo *tfont::get_lig(charinfo *c1, charinfo *c2)
     682             : {
     683    10468377 :   if (0 == ligature_mode)
     684           0 :     return 0 /* nullptr */;
     685    10468377 :   charinfo *ci = 0 /* nullptr */;
     686    10468377 :   if (c1->get_ascii_code() == 'f') {
     687      427250 :     switch (c2->get_ascii_code()) {
     688      137782 :     case 'f':
     689      137782 :       if (fm->has_ligature(font::LIG_ff))
     690           5 :         ci = lookup_charinfo(SYMBOL_ff);
     691      137782 :       break;
     692       14991 :     case 'i':
     693       14991 :       if (fm->has_ligature(font::LIG_fi))
     694       10188 :         ci = lookup_charinfo(SYMBOL_fi);
     695       14991 :       break;
     696        1032 :     case 'l':
     697        1032 :       if (fm->has_ligature(font::LIG_fl))
     698         717 :         ci = lookup_charinfo(SYMBOL_fl);
     699        1032 :       break;
     700             :     }
     701             :   }
     702    10041127 :   else if (ligature_mode != 2 && c1->nm == SYMBOL_ff) {
     703           0 :     switch (c2->get_ascii_code()) {
     704           0 :     case 'i':
     705           0 :       if (fm->has_ligature(font::LIG_ffi))
     706           0 :         ci = lookup_charinfo(SYMBOL_Fi);
     707           0 :       break;
     708           0 :     case 'l':
     709           0 :       if (fm->has_ligature(font::LIG_ffl))
     710           0 :         ci = lookup_charinfo(SYMBOL_Fl);
     711           0 :       break;
     712             :     }
     713             :   }
     714    10468377 :   if (ci != 0 /* nullptr */ && fm->contains(ci->as_glyph()))
     715       10910 :     return ci;
     716    10457467 :   return 0 /* nullptr */;
     717             : }
     718             : 
     719    10457467 : inline bool tfont::is_kerned(charinfo *c1, charinfo *c2, hunits *res)
     720             : {
     721    10457467 :   if (0 == kern_mode)
     722        9079 :     return false;
     723             :   else {
     724    10448388 :     int n = fm->get_kern(c1->as_glyph(),
     725             :                          c2->as_glyph(),
     726             :                          size.to_scaled_points());
     727    10448388 :     if (n) {
     728      485400 :       *res = hunits(n);
     729      485400 :       return true;
     730             :     }
     731             :     else
     732     9962988 :       return false;
     733             :   }
     734             : }
     735             : 
     736             : tfont *tfont::tfont_list = 0 /* nullptr */;
     737             : 
     738        3819 : tfont::tfont(tfont_spec &spec) : tfont_spec(spec)
     739             : {
     740        3819 :   next = tfont_list;
     741        3819 :   tfont_list = this;
     742        3819 :   tfont_spec plain_spec = plain();
     743             :   tfont *p;
     744        7160 :   for (p = tfont_list; p != 0 /* nullptr */; p = p->next)
     745        7157 :     if (*p == plain_spec) {
     746        3816 :       plain_version = p;
     747        3816 :       break;
     748             :     }
     749        3819 :   if (0 /* nullptr */ == p)
     750           3 :     plain_version = new tfont(plain_spec);
     751        3819 : }
     752             : 
     753             : /* output_file */
     754             : 
     755             : class real_output_file : public output_file {
     756             :   bool is_output_piped;         // as with `pi` request
     757             :   bool want_page_printed;       // if selected with `troff -o`
     758             :   bool is_output_on;            // as by \O[0], \O[1] escape sequences
     759             :   virtual void really_transparent_char(unsigned char) = 0;
     760             :   virtual void really_print_line(hunits x, vunits y, node *n,
     761             :                                  vunits before, vunits after,
     762             :                                  hunits width) = 0;
     763             :   virtual void really_begin_page(int pageno, vunits page_length) = 0;
     764             :   virtual void really_copy_file(hunits x, vunits y,
     765             :                                 const char *filename);
     766             :   virtual void really_put_filename(const char *, int);
     767             :   virtual void really_on();
     768             :   virtual void really_off();
     769             : public:
     770             :   FILE *fp;
     771             :   real_output_file();
     772             :   ~real_output_file();
     773             :   void flush();
     774             :   void transparent_char(unsigned char);
     775             :   void print_line(hunits x, vunits y, node *n,
     776             :                   vunits before, vunits after, hunits width);
     777             :   void begin_page(int pageno, vunits page_length);
     778             :   void put_filename(const char *, int);
     779             :   void on();
     780             :   void off();
     781             :   bool is_on();
     782             :   bool is_selected_for_printing();
     783             :   void copy_file(hunits x, vunits y, const char *filename);
     784             : };
     785             : 
     786             : class suppress_output_file : public real_output_file {
     787             : public:
     788             :   suppress_output_file();
     789             :   void really_transparent_char(unsigned char);
     790             :   void really_print_line(hunits x, vunits y, node *n,
     791             :                          vunits, vunits, hunits width);
     792             :   void really_begin_page(int pageno, vunits page_length);
     793             : };
     794             : 
     795             : class ascii_output_file : public real_output_file {
     796             : public:
     797             :   ascii_output_file();
     798             :   void really_transparent_char(unsigned char);
     799             :   void really_print_line(hunits x, vunits y, node *n,
     800             :                          vunits, vunits, hunits width);
     801             :   void really_begin_page(int pageno, vunits page_length);
     802             :   void outc(unsigned char c);
     803             :   void outs(const char *s);
     804             : };
     805             : 
     806        1304 : void ascii_output_file::outc(unsigned char c)
     807             : {
     808        1304 :   if (fp != 0 /* nullptr */)
     809        1304 :     fputc(c, fp);
     810        1304 : }
     811             : 
     812          51 : void ascii_output_file::outs(const char *s)
     813             : {
     814          51 :   if (fp != 0 /* nullptr */) {
     815          51 :     fputc('<', fp);
     816          51 :     if (s != 0 /* nullptr */)
     817          51 :       fputs(s, fp);
     818          51 :     fputc('>', fp);
     819             :   }
     820          51 : }
     821             : 
     822             : struct hvpair;
     823             : 
     824             : class troff_output_file : public real_output_file {
     825             :   units hpos;
     826             :   units vpos;
     827             :   units output_vpos;
     828             :   units output_hpos;
     829             :   bool must_update_drawing_position;
     830             :   int current_size;
     831             :   int current_slant;
     832             :   int current_height;
     833             :   tfont *current_tfont;
     834             :   color *current_fill_color;
     835             :   color *current_stroke_color;
     836             :   int current_font_number;
     837             :   symbol *font_position;
     838             :   int nfont_positions;
     839             :   enum { TBUF_SIZE = 256 };
     840             :   char tbuf[TBUF_SIZE];
     841             :   int tbuf_len;
     842             :   int tbuf_kern;
     843             :   bool has_page_begun;
     844             :   int cur_div_level;
     845             :   string tag_list;
     846             :   void do_motion();
     847             :   void put(char c);
     848             :   void put(unsigned char c);
     849             :   void put(int i);
     850             :   void put(unsigned int i);
     851             :   void put(const char *s);
     852             :   void set_font(tfont *tf);
     853             :   void flush_tbuf();
     854             : public:
     855             :   troff_output_file();
     856             :   ~troff_output_file();
     857             :   void flush();
     858             :   void trailer(vunits page_length);
     859             :   void put_char(charinfo *, tfont *, color *, color *);
     860             :   void put_char_width(charinfo *, tfont *, color *, color *, hunits,
     861             :                       hunits);
     862             :   void right(hunits);
     863             :   void down(vunits);
     864             :   void moveto(hunits, vunits);
     865             :   void start_device_extension(tfont * /* tf */,
     866             :                               color * /* gcol */, color * /* fcol */,
     867             :                               bool /* omit_command_prefix */ = false);
     868             :   void start_device_extension();
     869             :   void write_device_extension_char(unsigned char c);
     870             :   void end_device_extension();
     871             :   void word_marker();
     872             :   void really_transparent_char(unsigned char c);
     873             :   void really_print_line(hunits x, vunits y, node *n,
     874             :                          vunits before, vunits after, hunits width);
     875             :   void really_begin_page(int pageno, vunits page_length);
     876             :   void really_copy_file(hunits x, vunits y, const char *filename);
     877             :   void really_put_filename(const char *, int);
     878             :   void really_on();
     879             :   void really_off();
     880             :   void draw(char, hvpair *, int, font_size, color *, color *);
     881             :   void determine_line_limits (char code, hvpair *point, int npoints);
     882             :   void check_charinfo(tfont *tf, charinfo *ci);
     883             :   void stroke_color(color *c);
     884             :   void fill_color(color *c);
     885         424 :   int get_hpos() { return hpos; }
     886         424 :   int get_vpos() { return vpos; }
     887             :   void add_to_tag_list(string s);
     888             :   void comment(string s);
     889             :   friend void space_char_hmotion_node::tprint(troff_output_file *);
     890             :   friend void unbreakable_space_node::tprint(troff_output_file *);
     891             : };
     892             : 
     893     2902837 : static void put_string(const char *s, FILE *fp)
     894             : {
     895     2902837 :   if (fp != 0 /* nullptr */) {
     896    12834681 :     for (; *s != '\0'; ++s)
     897     9931844 :       putc(*s, fp);
     898             :   }
     899     2902837 : }
     900             : 
     901     9065783 : inline void troff_output_file::put(char c)
     902             : {
     903     9065783 :   if (fp != 0 /* nullptr */)
     904     9065783 :     putc(c, fp);
     905     9065783 : }
     906             : 
     907     1689087 : inline void troff_output_file::put(unsigned char c)
     908             : {
     909     1689087 :   if (fp != 0 /* nullptr */)
     910     1689087 :     putc(c, fp);
     911     1689087 : }
     912             : 
     913      314911 : inline void troff_output_file::put(const char *s)
     914             : {
     915      314911 :   put_string(s, fp);
     916      314911 : }
     917             : 
     918     2344171 : inline void troff_output_file::put(int i)
     919             : {
     920     2344171 :   put_string(i_to_a(i), fp);
     921     2344171 : }
     922             : 
     923      243755 : inline void troff_output_file::put(unsigned int i)
     924             : {
     925      243755 :   put_string(ui_to_a(i), fp);
     926      243755 : }
     927             : 
     928       66977 : void troff_output_file::start_device_extension(tfont *tf, color *gcol,
     929             :                                                color *fcol,
     930             :                                                bool omit_command_prefix)
     931             : {
     932       66977 :   flush_tbuf();
     933       66977 :   set_font(tf);
     934       66977 :   stroke_color(gcol);
     935       66977 :   fill_color(fcol);
     936       66977 :   do_motion();
     937       66977 :   if (!omit_command_prefix)
     938       66977 :     put("x X ");
     939       66977 : }
     940             : 
     941         116 : void troff_output_file::start_device_extension()
     942             : {
     943         116 :   flush_tbuf();
     944         116 :   put("x X ");
     945         116 : }
     946             : 
     947     1519645 : void troff_output_file::write_device_extension_char(unsigned char c)
     948             : {
     949     1519645 :   put(c);
     950     1519645 :   if (c == '\n')
     951         348 :     put('+');
     952     1519645 : }
     953             : 
     954       67093 : void troff_output_file::end_device_extension()
     955             : {
     956       67093 :   put('\n');
     957       67093 : }
     958             : 
     959      190009 : inline void troff_output_file::moveto(hunits h, vunits v)
     960             : {
     961      190009 :   hpos = h.to_units();
     962      190009 :   vpos = v.to_units();
     963      190009 : }
     964             : 
     965      189584 : void troff_output_file::really_print_line(hunits x, vunits y, node *n,
     966             :                                           vunits before, vunits after,
     967             :                                           hunits)
     968             : {
     969      189584 :   moveto(x, y);
     970     5309955 :   while (n != 0 /* nullptr */) {
     971             :     // Check whether we should push the current troff state and use
     972             :     // the state at the start of the invocation of this diversion.
     973     5120371 :     if (n->div_nest_level > cur_div_level && n->push_state) {
     974        3535 :       state.push_state(n->push_state);
     975        3535 :       cur_div_level = n->div_nest_level;
     976             :     }
     977             :     // Has the current diversion level decreased?  Then we must pop the
     978             :     // troff state.
     979     5123906 :     while (n->div_nest_level < cur_div_level) {
     980        3535 :       state.pop_state();
     981        3535 :       cur_div_level = n->div_nest_level;
     982             :     }
     983             :     // Now check whether the state has changed.
     984     5266548 :     if ((is_on() || n->causes_tprint())
     985     5266548 :         && (state.changed(n->state) || n->is_tag() || n->is_special)) {
     986       79827 :       flush_tbuf();
     987       79827 :       do_motion();
     988       79827 :       must_update_drawing_position = true;
     989       79827 :       flush();
     990       79827 :       state.flush(fp, n->state, tag_list);
     991       79827 :       tag_list = string("");
     992       79827 :       flush();
     993             :     }
     994     5120371 :     n->tprint(this);
     995     5120371 :     n = n->next;
     996             :   }
     997      189584 :   flush_tbuf();
     998             :   // Ensure that transparent throughput (.output, \!) has a more
     999             :   // predictable position.
    1000      189584 :   do_motion();
    1001      189584 :   must_update_drawing_position = true;
    1002      189584 :   hpos = 0;
    1003      189584 :   put('n');
    1004      189584 :   put(before.to_units());
    1005      189584 :   put(' ');
    1006      189584 :   put(after.to_units());
    1007      189584 :   put('\n');
    1008      189584 : }
    1009             : 
    1010      429091 : inline void troff_output_file::word_marker()
    1011             : {
    1012      429091 :   flush_tbuf();
    1013      429091 :   if (is_on())
    1014      410630 :     put('w');
    1015      429091 : }
    1016             : 
    1017      996696 : inline void troff_output_file::right(hunits n)
    1018             : {
    1019      996696 :   hpos += n.to_units();
    1020      996696 : }
    1021             : 
    1022      102798 : inline void troff_output_file::down(vunits n)
    1023             : {
    1024      102798 :   vpos += n.to_units();
    1025      102798 : }
    1026             : 
    1027     1200403 : void troff_output_file::do_motion()
    1028             : {
    1029     1200403 :   if (must_update_drawing_position) {
    1030      269412 :     put('V');
    1031      269412 :     put(vpos);
    1032      269412 :     put('\n');
    1033      269412 :     put('H');
    1034      269412 :     put(hpos);
    1035      269412 :     put('\n');
    1036             :   }
    1037             :   else {
    1038      930991 :     if (hpos != output_hpos) {
    1039      600983 :       units n = hpos - output_hpos;
    1040      600983 :       if (n > 0 && n < hpos) {
    1041      515804 :         put('h');
    1042      515804 :         put(n);
    1043             :       }
    1044             :       else {
    1045       85179 :         put('H');
    1046       85179 :         put(hpos);
    1047             :       }
    1048      600983 :       put('\n');
    1049             :     }
    1050      930991 :     if (vpos != output_vpos) {
    1051       61547 :       units n = vpos - output_vpos;
    1052       61547 :       if (n > 0 && n < vpos) {
    1053         560 :         put('v');
    1054         560 :         put(n);
    1055             :       }
    1056             :       else {
    1057       60987 :         put('V');
    1058       60987 :         put(vpos);
    1059             :       }
    1060       61547 :       put('\n');
    1061             :     }
    1062             :   }
    1063     1200403 :   output_vpos = vpos;
    1064     1200403 :   output_hpos = hpos;
    1065     1200403 :   must_update_drawing_position = false;
    1066     1200403 : }
    1067             : 
    1068     2147915 : void troff_output_file::flush_tbuf()
    1069             : {
    1070     2147915 :   if (!is_on()) {
    1071      130081 :     tbuf_len = 0;
    1072      130081 :     return;
    1073             :   }
    1074             : 
    1075     2017834 :   if (0 == tbuf_len)
    1076     1444324 :     return;
    1077      573510 :   if (0 == tbuf_kern)
    1078      572669 :     put('t');
    1079             :   else {
    1080         841 :     put('u');
    1081         841 :     put(tbuf_kern);
    1082         841 :     put(' ');
    1083             :   }
    1084      573510 :   check_output_limits(hpos, vpos);
    1085      573510 :   assert(current_size > 0);
    1086      573510 :   check_output_limits(hpos, vpos - current_size);
    1087             : 
    1088     3022722 :   for (int i = 0; i < tbuf_len; i++)
    1089     2449212 :     put(tbuf[i]);
    1090      573510 :   put('\n');
    1091      573510 :   tbuf_len = 0;
    1092             : }
    1093             : 
    1094     2506105 : void troff_output_file::check_charinfo(tfont *tf, charinfo *ci)
    1095             : {
    1096     2506105 :   if (!is_on())
    1097           0 :     return;
    1098             : 
    1099     2506105 :   int height = tf->get_char_height(ci).to_units();
    1100     2506105 :   int width = tf->get_width(ci).to_units()
    1101     2506105 :               + tf->get_italic_correction(ci).to_units();
    1102     2506105 :   int depth = tf->get_char_depth(ci).to_units();
    1103     2506105 :   check_output_limits(output_hpos, output_vpos - height);
    1104     2506105 :   check_output_limits(output_hpos + width, output_vpos + depth);
    1105             : }
    1106             : 
    1107     2608611 : void troff_output_file::put_char_width(charinfo *ci, tfont *tf,
    1108             :                                        color *gcol, color *fcol,
    1109             :                                        hunits w, hunits k)
    1110             : {
    1111     2608611 :   int kk = k.to_units();
    1112     2608611 :   if (!is_on()) {
    1113      102506 :     flush_tbuf();
    1114      102506 :     hpos += w.to_units() + kk;
    1115      102506 :     return;
    1116             :   }
    1117     2506105 :   set_font(tf);
    1118     2506105 :   unsigned char c = ci->get_ascii_code();
    1119     2506105 :   if (0U == c) {
    1120       56893 :     stroke_color(gcol);
    1121       56893 :     fill_color(fcol);
    1122       56893 :     flush_tbuf();
    1123       56893 :     do_motion();
    1124       56893 :     check_charinfo(tf, ci);
    1125       56893 :     if (ci->is_numbered()) {
    1126       21144 :       put('N');
    1127       21144 :       put(ci->get_number());
    1128             :     }
    1129             :     else {
    1130       35749 :       put('C');
    1131       35749 :       const char *s = ci->nm.contents();
    1132       35749 :       if (0 == s[1]) {
    1133        5042 :         put('\\');
    1134        5042 :         put(s[0]);
    1135             :       }
    1136             :       else
    1137       30707 :         put(s);
    1138             :     }
    1139       56893 :     put('\n');
    1140       56893 :     hpos += w.to_units() + kk;
    1141             :   }
    1142     2449212 :   else if (device_has_tcommand) {
    1143     2449212 :     if (tbuf_len > 0 && hpos == output_hpos && vpos == output_vpos
    1144     1875707 :         && (!gcol || gcol == current_stroke_color)
    1145     1875702 :         && (!fcol || fcol == current_fill_color)
    1146     1875702 :         && kk == tbuf_kern
    1147     1875702 :         && tbuf_len < TBUF_SIZE) {
    1148     1875702 :       check_charinfo(tf, ci);
    1149     1875702 :       tbuf[tbuf_len++] = c;
    1150     1875702 :       output_hpos += w.to_units() + kk;
    1151     1875702 :       hpos = output_hpos;
    1152     1875702 :       return;
    1153             :     }
    1154      573510 :     stroke_color(gcol);
    1155      573510 :     fill_color(fcol);
    1156      573510 :     flush_tbuf();
    1157      573510 :     do_motion();
    1158      573510 :     check_charinfo(tf, ci);
    1159      573510 :     tbuf[tbuf_len++] = c;
    1160      573510 :     output_hpos += w.to_units() + kk;
    1161      573510 :     tbuf_kern = kk;
    1162      573510 :     hpos = output_hpos;
    1163             :   }
    1164             :   else {
    1165             :     // flush_tbuf();
    1166           0 :     int n = hpos - output_hpos;
    1167           0 :     check_charinfo(tf, ci);
    1168             :     // check_output_limits(output_hpos, output_vpos);
    1169           0 :     if (vpos == output_vpos
    1170           0 :         && (!gcol || gcol == current_stroke_color)
    1171           0 :         && (!fcol || fcol == current_fill_color)
    1172           0 :         && (n > 0) && (n < 100) && !must_update_drawing_position) {
    1173           0 :       put(char(n / 10 + '0'));
    1174           0 :       put(char(n % 10 + '0'));
    1175           0 :       put(c);
    1176           0 :       output_hpos = hpos;
    1177             :     }
    1178             :     else {
    1179           0 :       stroke_color(gcol);
    1180           0 :       fill_color(fcol);
    1181           0 :       do_motion();
    1182           0 :       put('c');
    1183           0 :       put(c);
    1184             :     }
    1185           0 :     hpos += w.to_units() + kk;
    1186             :   }
    1187             : }
    1188             : 
    1189         245 : void troff_output_file::put_char(charinfo *ci, tfont *tf,
    1190             :                                  color *gcol, color *fcol)
    1191             : {
    1192         245 :   flush_tbuf();
    1193         245 :   if (!is_on())
    1194           0 :     return;
    1195         245 :   set_font(tf);
    1196         245 :   unsigned char c = ci->get_ascii_code();
    1197         245 :   if (0U == c) {
    1198          20 :     stroke_color(gcol);
    1199          20 :     fill_color(fcol);
    1200          20 :     flush_tbuf();
    1201          20 :     do_motion();
    1202          20 :     if (ci->is_numbered()) {
    1203           0 :       put('N');
    1204           0 :       put(ci->get_number());
    1205             :     }
    1206             :     else {
    1207          20 :       put('C');
    1208          20 :       const char *s = ci->nm.contents();
    1209          20 :       if (0 == s[1]) {
    1210           0 :         put('\\');
    1211           0 :         put(s[0]);
    1212             :       }
    1213             :       else
    1214          20 :         put(s);
    1215             :     }
    1216          20 :     put('\n');
    1217             :   }
    1218             :   else {
    1219         225 :     int n = hpos - output_hpos;
    1220         225 :     if (vpos == output_vpos
    1221         116 :         && (!gcol || gcol == current_stroke_color)
    1222         116 :         && (!fcol || fcol == current_fill_color)
    1223         116 :         && n > 0 && n < 100) {
    1224           1 :       put(char(n/10 + '0'));
    1225           1 :       put(char(n%10 + '0'));
    1226           1 :       put(c);
    1227           1 :       output_hpos = hpos;
    1228             :     }
    1229             :     else {
    1230         224 :       stroke_color(gcol);
    1231         224 :       fill_color(fcol);
    1232         224 :       flush_tbuf();
    1233         224 :       do_motion();
    1234         224 :       put('c');
    1235         224 :       put(c);
    1236             :     }
    1237             :   }
    1238             : }
    1239             : 
    1240             : // set_font calls 'flush_tbuf' if necessary.
    1241             : 
    1242     2573327 : void troff_output_file::set_font(tfont *tf)
    1243             : {
    1244     2573327 :   if (current_tfont == tf)
    1245     2437160 :     return;
    1246      136167 :   flush_tbuf();
    1247      136167 :   int n = tf->get_input_position();
    1248      136167 :   symbol nm = tf->get_name();
    1249      136167 :   if (n >= nfont_positions || font_position[n] != nm) {
    1250        4668 :     put("x font ");
    1251        4668 :     put(n);
    1252        4668 :     put(' ');
    1253        4668 :     put(nm.contents());
    1254        4668 :     put('\n');
    1255        4668 :     if (n >= nfont_positions) {
    1256          50 :       int old_nfont_positions = nfont_positions;
    1257          50 :       symbol *old_font_position = font_position;
    1258          50 :       nfont_positions *= 3;
    1259          50 :       nfont_positions /= 2;
    1260          50 :       if (nfont_positions <= n)
    1261          40 :         nfont_positions = n + 10;
    1262        2385 :       font_position = new symbol[nfont_positions];
    1263          50 :       memcpy(font_position, old_font_position,
    1264          50 :              old_nfont_positions*sizeof(symbol));
    1265          50 :       delete[] old_font_position;
    1266             :     }
    1267        4668 :     font_position[n] = nm;
    1268             :   }
    1269      136167 :   if (current_font_number != n) {
    1270      135483 :     put('f');
    1271      135483 :     put(n);
    1272      135483 :     put('\n');
    1273      135483 :     current_font_number = n;
    1274             :   }
    1275      136167 :   int zoom = tf->get_zoom();
    1276             :   int size;
    1277      136167 :   if (zoom)
    1278           0 :     size = scale(tf->get_size().to_scaled_points(),
    1279             :                  zoom, 1000);
    1280             :   else
    1281      136167 :     size = tf->get_size().to_scaled_points();
    1282      136167 :   if (current_size != size) {
    1283        4323 :     put('s');
    1284        4323 :     put(size);
    1285        4323 :     put('\n');
    1286        4323 :     current_size = size;
    1287             :   }
    1288      136167 :   int slant = tf->get_slant();
    1289      136167 :   if (current_slant != slant) {
    1290           0 :     put("x Slant ");
    1291           0 :     put(slant);
    1292           0 :     put('\n');
    1293           0 :     current_slant = slant;
    1294             :   }
    1295      136167 :   int height = tf->get_height();
    1296      136167 :   if (current_height != height) {
    1297         148 :     put("x Height ");
    1298         148 :     put((0 == height) ? current_size : height);
    1299         148 :     put('\n');
    1300         148 :     current_height = height;
    1301             :   }
    1302      136167 :   current_tfont = tf;
    1303             : }
    1304             : 
    1305             : // fill_color calls 'flush_tbuf' and 'do_motion' if necessary.
    1306             : 
    1307     1933114 : void troff_output_file::fill_color(color *col)
    1308             : {
    1309     1933114 :   if ((0 /* nullptr */ == col) || current_fill_color == col)
    1310     1866695 :     return;
    1311       66419 :   current_fill_color = col;
    1312       66419 :   if (!want_color_output)
    1313           0 :     return;
    1314       66419 :   flush_tbuf();
    1315             :   // In nroff-mode devices (grotty), the fill color is a property of the
    1316             :   // character cell; our drawing position has to be on the page, lest
    1317             :   // grotty grouse "output above first line discarded".
    1318       66419 :   if (in_nroff_mode)
    1319        1085 :     do_motion();
    1320       66419 :   put("DF");
    1321             :   unsigned int components[4];
    1322             :   color_scheme scheme;
    1323       66419 :   scheme = col->get_components(components);
    1324       66419 :   switch (scheme) {
    1325       33706 :   case DEFAULT:
    1326       33706 :     put('d');
    1327       33706 :     break;
    1328       32461 :   case RGB:
    1329       32461 :     put("r ");
    1330       32461 :     put(Red);
    1331       32461 :     put(' ');
    1332       32461 :     put(Green);
    1333       32461 :     put(' ');
    1334       32461 :     put(Blue);
    1335       32461 :     break;
    1336           0 :   case CMY:
    1337           0 :     put("c ");
    1338           0 :     put(Cyan);
    1339           0 :     put(' ');
    1340           0 :     put(Magenta);
    1341           0 :     put(' ');
    1342           0 :     put(Yellow);
    1343           0 :     break;
    1344           0 :   case CMYK:
    1345           0 :     put("k ");
    1346           0 :     put(Cyan);
    1347           0 :     put(' ');
    1348           0 :     put(Magenta);
    1349           0 :     put(' ');
    1350           0 :     put(Yellow);
    1351           0 :     put(' ');
    1352           0 :     put(Black);
    1353           0 :     break;
    1354         252 :   case GRAY:
    1355         252 :     put("g ");
    1356         252 :     put(Gray);
    1357         252 :     break;
    1358             :   }
    1359       66419 :   put('\n');
    1360             : }
    1361             : 
    1362             : // stroke_color calls 'flush_tbuf' and 'do_motion' if necessary.
    1363             : 
    1364      928679 : void troff_output_file::stroke_color(color *col)
    1365             : {
    1366      928679 :   if (!col || (current_stroke_color == col))
    1367      877314 :     return;
    1368       51365 :   current_stroke_color = col;
    1369       51365 :   if (!want_color_output)
    1370           0 :     return;
    1371       51365 :   flush_tbuf();
    1372             :   // In nroff-mode devices (grotty), the stroke color is a property of
    1373             :   // the character cell; our drawing position has to be on the page,
    1374             :   // lest grotty grouse "output above first line discarded".
    1375       51365 :   if (in_nroff_mode)
    1376         990 :     do_motion();
    1377       51365 :   put("m");
    1378             :   unsigned int components[4];
    1379             :   color_scheme scheme;
    1380       51365 :   scheme = col->get_components(components);
    1381       51365 :   switch (scheme) {
    1382        2655 :   case DEFAULT:
    1383        2655 :     put('d');
    1384        2655 :     break;
    1385       48705 :   case RGB:
    1386       48705 :     put("r ");
    1387       48705 :     put(Red);
    1388       48705 :     put(' ');
    1389       48705 :     put(Green);
    1390       48705 :     put(' ');
    1391       48705 :     put(Blue);
    1392       48705 :     break;
    1393           0 :   case CMY:
    1394           0 :     put("c ");
    1395           0 :     put(Cyan);
    1396           0 :     put(' ');
    1397           0 :     put(Magenta);
    1398           0 :     put(' ');
    1399           0 :     put(Yellow);
    1400           0 :     break;
    1401           0 :   case CMYK:
    1402           0 :     put("k ");
    1403           0 :     put(Cyan);
    1404           0 :     put(' ');
    1405           0 :     put(Magenta);
    1406           0 :     put(' ');
    1407           0 :     put(Yellow);
    1408           0 :     put(' ');
    1409           0 :     put(Black);
    1410           0 :     break;
    1411           5 :   case GRAY:
    1412           5 :     put("g ");
    1413           5 :     put(Gray);
    1414           5 :     break;
    1415             :   }
    1416       51365 :   put('\n');
    1417             : }
    1418             : 
    1419          50 : void troff_output_file::add_to_tag_list(string s)
    1420             : {
    1421          50 :   if (tag_list == string(""))
    1422          50 :     tag_list = s;
    1423             :   else {
    1424           0 :     tag_list += string("\n");
    1425           0 :     tag_list += s;
    1426             :   }
    1427          50 : }
    1428             : 
    1429           0 : void troff_output_file::comment(string s)
    1430             : {
    1431           0 :   flush_tbuf();
    1432           0 :   assert(s.search('\n') == -1); // Don't write a multi-line comment.
    1433           0 :   put("# ");
    1434           0 :   string t = s + '\0';
    1435           0 :   put(t.contents());
    1436           0 :   put('\n');
    1437           0 : }
    1438             : 
    1439             : // determine_line_limits - works out the smallest box which will contain
    1440             : //                         the entity, code, built from the point array.
    1441      228693 : void troff_output_file::determine_line_limits(char code, hvpair *point,
    1442             :                                               int npoints)
    1443             : {
    1444             :   int i, x, y;
    1445             : 
    1446      228693 :   if (!is_on())
    1447           0 :     return;
    1448             : 
    1449      228693 :   switch (code) {
    1450         312 :   case 'c':
    1451             :   case 'C':
    1452             :     // only the h field is used when defining a circle
    1453         312 :     check_output_limits(output_hpos,
    1454         312 :                         output_vpos - point[0].h.to_units() / 2);
    1455         312 :     check_output_limits(output_hpos + point[0].h.to_units(),
    1456         312 :                         output_vpos + point[0].h.to_units() / 2);
    1457      228682 :     break;
    1458          34 :   case 'E':
    1459             :   case 'e':
    1460          34 :     check_output_limits(output_hpos,
    1461          34 :                         output_vpos - point[0].v.to_units() / 2);
    1462          34 :     check_output_limits(output_hpos + point[0].h.to_units(),
    1463          34 :                         output_vpos + point[0].v.to_units() / 2);
    1464          34 :     break;
    1465       32970 :   case 'P':
    1466             :   case 'p':
    1467       32970 :     x = output_hpos;
    1468       32970 :     y = output_vpos;
    1469       32970 :     check_output_limits(x, y);
    1470      131643 :     for (i = 0; i < npoints; i++) {
    1471       98673 :       x += point[i].h.to_units();
    1472       98673 :       y += point[i].v.to_units();
    1473       98673 :       check_output_limits(x, y);
    1474             :     }
    1475       32970 :     break;
    1476      121455 :   case 't':
    1477      121455 :     x = output_hpos;
    1478      121455 :     y = output_vpos;
    1479      242910 :     for (i = 0; i < npoints; i++) {
    1480      121455 :       x += point[i].h.to_units();
    1481      121455 :       y += point[i].v.to_units();
    1482      121455 :       check_output_limits(x, y);
    1483             :     }
    1484      121455 :     break;
    1485         141 :   case 'a':
    1486             :     double c[2];
    1487             :     int p[4];
    1488             :     int minx, miny, maxx, maxy;
    1489         141 :     x = output_hpos;
    1490         141 :     y = output_vpos;
    1491         141 :     p[0] = point[0].h.to_units();
    1492         141 :     p[1] = point[0].v.to_units();
    1493         141 :     p[2] = point[1].h.to_units();
    1494         141 :     p[3] = point[1].v.to_units();
    1495         141 :     if (adjust_arc_center(p, c)) {
    1496         141 :       check_output_arc_limits(x, y,
    1497             :                               p[0], p[1], p[2], p[3],
    1498             :                               c[0], c[1],
    1499             :                               &minx, &maxx, &miny, &maxy);
    1500         141 :       check_output_limits(minx, miny);
    1501         141 :       check_output_limits(maxx, maxy);
    1502         141 :       break;
    1503             :     }
    1504             :     // fall through
    1505             :   case 'l':
    1506       73770 :     x = output_hpos;
    1507       73770 :     y = output_vpos;
    1508       73770 :     check_output_limits(x, y);
    1509      147540 :     for (i = 0; i < npoints; i++) {
    1510       73770 :       x += point[i].h.to_units();
    1511       73770 :       y += point[i].v.to_units();
    1512       73770 :       check_output_limits(x, y);
    1513             :     }
    1514       73770 :     break;
    1515          11 :   default:
    1516          11 :     x = output_hpos;
    1517          11 :     y = output_vpos;
    1518          46 :     for (i = 0; i < npoints; i++) {
    1519          35 :       x += point[i].h.to_units();
    1520          35 :       y += point[i].v.to_units();
    1521          35 :       check_output_limits(x, y);
    1522             :     }
    1523             :   }
    1524             : }
    1525             : 
    1526      231055 : void troff_output_file::draw(char code, hvpair *point, int npoints,
    1527             :                              font_size fsize, color *gcol, color *fcol)
    1528             : {
    1529             :   int i;
    1530      231055 :   stroke_color(gcol);
    1531      231055 :   fill_color(fcol);
    1532      231055 :   flush_tbuf();
    1533      231055 :   do_motion();
    1534      231055 :   if (is_on()) {
    1535      228693 :     int size = fsize.to_scaled_points();
    1536      228693 :     if (current_size != size) {
    1537         259 :       put('s');
    1538         259 :       put(size);
    1539         259 :       put('\n');
    1540         259 :       current_size = size;
    1541         259 :       current_tfont = 0;
    1542             :     }
    1543      228693 :     put('D');
    1544      228693 :     put(code);
    1545      228693 :     if (code == 'c') {
    1546         167 :       put(' ');
    1547         167 :       put(point[0].h.to_units());
    1548             :     }
    1549             :     else
    1550      522920 :       for (i = 0; i < npoints; i++) {
    1551      294394 :         put(' ');
    1552      294394 :         put(point[i].h.to_units());
    1553      294394 :         put(' ');
    1554      294394 :         put(point[i].v.to_units());
    1555             :       }
    1556      228693 :     determine_line_limits(code, point, npoints);
    1557             :   }
    1558             : 
    1559      528414 :   for (i = 0; i < npoints; i++)
    1560      297359 :     output_hpos += point[i].h.to_units();
    1561      231055 :   hpos = output_hpos;
    1562      231055 :   if (code != 'e') {
    1563      528334 :     for (i = 0; i < npoints; i++)
    1564      297319 :       output_vpos += point[i].v.to_units();
    1565      231015 :     vpos = output_vpos;
    1566             :   }
    1567      231055 :   if (is_on())
    1568      228693 :     put('\n');
    1569      231055 : }
    1570             : 
    1571         117 : void troff_output_file::really_on()
    1572             : {
    1573         117 :   flush_tbuf();
    1574         117 :   must_update_drawing_position = true;
    1575         117 :   do_motion();
    1576         117 : }
    1577             : 
    1578         134 : void troff_output_file::really_off()
    1579             : {
    1580         134 :   flush_tbuf();
    1581         134 : }
    1582             : 
    1583         618 : void troff_output_file::really_put_filename(const char *filename,
    1584             :                                             int po)
    1585             : {
    1586         618 :   flush_tbuf();
    1587         618 :   put("x F ");
    1588         618 :   if (po)
    1589           0 :     put("<");
    1590         618 :   put(filename);
    1591         618 :   if (po)
    1592           0 :     put(">");
    1593         618 :   put('\n');
    1594         618 : }
    1595             : 
    1596        2063 : void troff_output_file::really_begin_page(int pageno,
    1597             :                                           vunits page_length)
    1598             : {
    1599        2063 :   flush_tbuf();
    1600        2063 :   if (has_page_begun) {
    1601         869 :     if (page_length > V0) {
    1602         869 :       put('V');
    1603         869 :       put(page_length.to_units());
    1604         869 :       put('\n');
    1605             :     }
    1606             :   }
    1607             :   else
    1608        1194 :     has_page_begun = true;
    1609        2063 :   current_tfont = 0;
    1610        2063 :   current_font_number = FONT_NOT_MOUNTED;
    1611        2063 :   current_size = 0;
    1612             :   // current_height = 0;
    1613             :   // current_slant = 0;
    1614        2063 :   hpos = 0;
    1615        2063 :   vpos = 0;
    1616        2063 :   output_hpos = 0;
    1617        2063 :   output_vpos = 0;
    1618        2063 :   must_update_drawing_position = true;
    1619       53250 :   for (int i = 0; i < nfont_positions; i++)
    1620       51187 :     font_position[i] = NULL_SYMBOL;
    1621        2063 :   put('p');
    1622        2063 :   put(pageno);
    1623        2063 :   put('\n');
    1624        2063 : }
    1625             : 
    1626           1 : void troff_output_file::really_copy_file(hunits x, vunits y,
    1627             :                                          const char *filename)
    1628             : {
    1629           1 :   moveto(x, y);
    1630           1 :   flush_tbuf();
    1631           1 :   do_motion();
    1632           1 :   errno = 0;
    1633           1 :   FILE *ifp = include_search_path.open_file_cautiously(filename);
    1634           1 :   if (0 /* nullptr */ == ifp)
    1635           0 :     error("cannot open '%1': %2", filename, strerror(errno));
    1636             :   else {
    1637             :     int c;
    1638          33 :     while ((c = getc(ifp)) != EOF)
    1639          32 :       put(char(c));
    1640           1 :     fclose(ifp);
    1641             :   }
    1642           1 :   must_update_drawing_position = true;
    1643           1 :   current_size = 0;
    1644           1 :   current_tfont = 0;
    1645           1 :   current_font_number = FONT_NOT_MOUNTED;
    1646          11 :   for (int i = 0; i < nfont_positions; i++)
    1647          10 :     font_position[i] = NULL_SYMBOL;
    1648           1 : }
    1649             : 
    1650      169217 : void troff_output_file::really_transparent_char(unsigned char c)
    1651             : {
    1652      169217 :   put(c);
    1653      169217 : }
    1654             : 
    1655        2388 : troff_output_file::~troff_output_file()
    1656             : {
    1657        1194 :   delete[] font_position;
    1658        2387 : }
    1659             : 
    1660        1194 : void troff_output_file::trailer(vunits page_length)
    1661             : {
    1662        1194 :   flush_tbuf();
    1663        1194 :   if (was_any_page_in_output_list) {
    1664        1194 :     if (page_length > V0) {
    1665        1194 :       put("x trailer\n");
    1666        1194 :       put('V');
    1667        1194 :       put(page_length.to_units());
    1668        1194 :       put('\n');
    1669             :     }
    1670             :   }
    1671             :   else
    1672           0 :     warning(WARN_RANGE, "no pages match output page selection list");
    1673        1194 :   put("x stop\n");
    1674        1194 : }
    1675             : 
    1676        1194 : troff_output_file::troff_output_file()
    1677             : : current_slant(0), current_height(0), current_fill_color(0),
    1678             :   current_stroke_color(0), nfont_positions(10), tbuf_len(0),
    1679        1194 :   has_page_begun(false), cur_div_level(0)
    1680             : {
    1681       13134 :   font_position = new symbol[nfont_positions];
    1682        1194 :   put("x T ");
    1683        1194 :   put(device);
    1684        1194 :   put('\n');
    1685        1194 :   put("x res ");
    1686        1194 :   put(units_per_inch);
    1687        1194 :   put(' ');
    1688        1194 :   put(hresolution);
    1689        1194 :   put(' ');
    1690        1194 :   put(vresolution);
    1691        1194 :   put('\n');
    1692        1194 :   put("x init\n");
    1693        1194 : }
    1694             : 
    1695      159669 : void troff_output_file::flush()
    1696             : {
    1697      159669 :   flush_tbuf();
    1698      159669 :   real_output_file::flush();
    1699      159669 : }
    1700             : 
    1701             : /* output_file */
    1702             : 
    1703             : output_file *the_output = 0 /* nullptr */;
    1704             : 
    1705        1279 : output_file::output_file()
    1706             : {
    1707        1279 :         is_dying = false;
    1708        1279 : }
    1709             : 
    1710        1278 : output_file::~output_file()
    1711             : {
    1712        1278 : }
    1713             : 
    1714          86 : void output_file::trailer(vunits)
    1715             : {
    1716          86 : }
    1717             : 
    1718           0 : void output_file::put_filename(const char *, int)
    1719             : {
    1720           0 : }
    1721             : 
    1722           0 : void output_file::on()
    1723             : {
    1724           0 : }
    1725             : 
    1726           0 : void output_file::off()
    1727             : {
    1728           0 : }
    1729             : 
    1730        1279 : real_output_file::real_output_file()
    1731        1279 : : want_page_printed(true), is_output_on(true)
    1732             : {
    1733        1279 :   if (pipe_command) {
    1734           1 :     if ((fp = popen(pipe_command, POPEN_WT)) != 0 /* nullptr */) {
    1735           1 :       is_output_piped = true;
    1736           1 :       return;
    1737             :     }
    1738           0 :     error("pipe open failed: %1", strerror(errno));
    1739             :   }
    1740        1278 :   is_output_piped = false;
    1741        1278 :   fp = stdout;
    1742             : }
    1743             : 
    1744        1278 : real_output_file::~real_output_file()
    1745             : {
    1746        1279 :   if (0 /* nullptr */ == fp)
    1747           0 :     return;
    1748             :   // Prevent destructor from recursing; see
    1749             :   // div.cpp:write_any_trailer_and_exit().
    1750        1279 :   is_dying = true;
    1751             :   // To avoid looping, set fp to 0 before calling fatal().
    1752        1279 :   if (ferror(fp)) {
    1753           0 :     fp = 0 /* nullptr */;
    1754           0 :     fatal("error on output file stream");
    1755             :   }
    1756        1279 :   else if (fflush(fp) < 0) {
    1757           1 :     fp = 0 /* nullptr */;
    1758           1 :     fatal("unable to flush output file: %1", strerror(errno));
    1759             :   }
    1760        1278 :   if (is_output_piped) {
    1761           1 :     int result = pclose(fp);
    1762           1 :     fp = 0 /* nullptr */;
    1763           1 :     if (result < 0)
    1764           0 :       fatal("unable to close pipe: %1", strerror(errno));
    1765           1 :     if (!WIFEXITED(result))
    1766           0 :       error("output process '%1' got fatal signal %2",
    1767             :             pipe_command,
    1768           0 :             WIFSIGNALED(result) ? WTERMSIG(result) : WSTOPSIG(result));
    1769             :     else {
    1770           1 :       int exit_status = WEXITSTATUS(result);
    1771           1 :       if (exit_status != 0)
    1772           1 :         error("output process '%1' exited with status %2",
    1773           2 :               pipe_command, exit_status);
    1774             :     }
    1775             :   }
    1776             :   else
    1777        1277 :   if (fclose(fp) < 0) {
    1778           0 :     fp = 0 /* nullptr */;
    1779           0 :     fatal("unable to close output file: %1", strerror(errno));
    1780             :   }
    1781        1278 : }
    1782             : 
    1783      159669 : void real_output_file::flush()
    1784             : {
    1785             :   // To avoid looping, set fp to 0 before calling fatal().
    1786      159669 :   if (fflush(fp) < 0) {
    1787           0 :     fp = 0 /* nullptr */;
    1788           0 :     fatal("unable to flush output file: %1", strerror(errno));
    1789             :   }
    1790      159669 : }
    1791             : 
    1792           0 : bool real_output_file::is_selected_for_printing()
    1793             : {
    1794           0 :   return want_page_printed;
    1795             : }
    1796             : 
    1797        2655 : void real_output_file::begin_page(int pageno, vunits page_length)
    1798             : {
    1799        2655 :   want_page_printed = in_output_page_list(pageno);
    1800        2655 :   if (want_page_printed) {
    1801        2655 :     was_any_page_in_output_list = true;
    1802        2655 :     really_begin_page(pageno, page_length);
    1803             :   }
    1804        2655 : }
    1805             : 
    1806           2 : void real_output_file::copy_file(hunits x, vunits y,
    1807             :                                  const char *filename)
    1808             : {
    1809           2 :   if (want_page_printed && is_output_on)
    1810           2 :     really_copy_file(x, y, filename);
    1811           2 :   check_output_limits(x.to_units(), y.to_units());
    1812           2 : }
    1813             : 
    1814      356795 : void real_output_file::transparent_char(unsigned char c)
    1815             : {
    1816      356795 :   if (want_page_printed && is_output_on)
    1817      356795 :     really_transparent_char(c);
    1818      356795 : }
    1819             : 
    1820      214758 : void real_output_file::print_line(hunits x, vunits y, node *n,
    1821             :                              vunits before, vunits after, hunits width)
    1822             : {
    1823      214758 :   if (want_page_printed)
    1824      214758 :     really_print_line(x, y, n, before, after, width);
    1825      214758 :   delete_node_list(n);
    1826      214758 : }
    1827             : 
    1828           1 : void real_output_file::really_copy_file(hunits, vunits, const char *)
    1829             : {
    1830             :   // do nothing
    1831           1 : }
    1832             : 
    1833         618 : void real_output_file::put_filename(const char *filename, int po)
    1834             : {
    1835         618 :   really_put_filename(filename, po);
    1836         618 : }
    1837             : 
    1838           0 : void real_output_file::really_put_filename(const char *, int)
    1839             : {
    1840           0 : }
    1841             : 
    1842         117 : void real_output_file::on()
    1843             : {
    1844         117 :   really_on();
    1845             :   // XXX: Assertion fails when generating pic.html.  Find out why.
    1846             :   //assert(!is_output_on);
    1847         117 :   is_output_on = true;
    1848         117 : }
    1849             : 
    1850         134 : void real_output_file::off()
    1851             : {
    1852         134 :   really_off();
    1853             :   // XXX: Assertion fails when generating ms.html.  Find out why.
    1854             :   //assert(is_output_on);
    1855         134 :   is_output_on = false;
    1856         134 : }
    1857             : 
    1858    13511360 : bool real_output_file::is_on()
    1859             : {
    1860    13511360 :   return is_output_on;
    1861             : }
    1862             : 
    1863           0 : void real_output_file::really_on()
    1864             : {
    1865           0 : }
    1866             : 
    1867           0 : void real_output_file::really_off()
    1868             : {
    1869           0 : }
    1870             : 
    1871             : /* ascii_output_file */
    1872             : 
    1873          32 : void ascii_output_file::really_transparent_char(unsigned char c)
    1874             : {
    1875          32 :   if (fp != 0 /* nullptr */)
    1876          32 :     putc(c, fp);
    1877          32 : }
    1878             : 
    1879        1515 : void ascii_output_file::really_print_line(hunits, vunits, node *n,
    1880             :                                           vunits, vunits, hunits)
    1881             : {
    1882        1515 :   while (n != 0 /* nullptr */) {
    1883        1438 :     n->ascii_print(this);
    1884        1438 :     n = n->next;
    1885             :   }
    1886          77 :   if (fp != 0 /* nullptr */)
    1887          77 :     fputc('\n', fp);
    1888          77 : }
    1889             : 
    1890          28 : void ascii_output_file::really_begin_page(int /* pageno */,
    1891             :                                           vunits /* page_length */)
    1892             : {
    1893          28 :   fputs("<beginning of page>\n", fp);
    1894          28 : }
    1895             : 
    1896          26 : ascii_output_file::ascii_output_file()
    1897             : {
    1898          26 : }
    1899             : 
    1900             : /* suppress_output_file */
    1901             : 
    1902          59 : suppress_output_file::suppress_output_file()
    1903             : {
    1904          59 : }
    1905             : 
    1906       25097 : void suppress_output_file::really_print_line(hunits, vunits, node *,
    1907             :                                              vunits, vunits, hunits)
    1908             : {
    1909       25097 : }
    1910             : 
    1911         564 : void suppress_output_file::really_begin_page(int, vunits)
    1912             : {
    1913         564 : }
    1914             : 
    1915      187546 : void suppress_output_file::really_transparent_char(unsigned char)
    1916             : {
    1917      187546 : }
    1918             : 
    1919             : /* glyphs, ligatures, kerns, discretionary breaks */
    1920             : 
    1921             : // abstract
    1922             : class charinfo_node : public node {
    1923             : protected:
    1924             :   charinfo *ci;
    1925             : public:
    1926             :   charinfo_node(charinfo *, statem *, int, node * = 0 /* nullptr */);
    1927             :   virtual int ends_sentence();
    1928             :   virtual bool overlaps_vertically();
    1929             :   virtual bool overlaps_horizontally();
    1930             :   virtual void dump_properties();
    1931             : };
    1932             : 
    1933    13555468 : charinfo_node::charinfo_node(charinfo *c, statem *s, int divlevel,
    1934    13555468 :                              node *x)
    1935    13555468 : : node(x, s, divlevel), ci(c)
    1936             : {
    1937    13555468 : }
    1938             : 
    1939      416225 : int charinfo_node::ends_sentence()
    1940             : {
    1941      416225 :   if (ci->ends_sentence())
    1942       81582 :     return 1;
    1943      334643 :   else if (ci->is_transparent_to_end_of_sentence())
    1944       18453 :     return 2;
    1945      316190 :   return 0;
    1946             : }
    1947             : 
    1948         144 : bool charinfo_node::overlaps_horizontally()
    1949             : {
    1950         144 :   return ci->overlaps_horizontally();
    1951             : }
    1952             : 
    1953           0 : bool charinfo_node::overlaps_vertically()
    1954             : {
    1955           0 :   return ci->overlaps_vertically();
    1956             : }
    1957             : 
    1958           0 : void charinfo_node::dump_properties()
    1959             : {
    1960           0 :   node::dump_properties();
    1961             :   // GNU troff multiplexes the distinction of ordinary vs. special
    1962             :   // characters though the special character code zero.
    1963           0 :   unsigned char c = ci->get_ascii_code();
    1964           0 :   if (c != 0U) {
    1965           0 :     fputs(", \"character\": ", stderr);
    1966             :     // It's not a `string` or `symbol` we can `.json_dump()`, so we have
    1967             :     // to write the quotation marks ourselves.
    1968           0 :     fputc('\"', stderr);
    1969           0 :     json_char jc = json_encode_char(c);
    1970             :     // Write out its JSON representation by character by character to
    1971             :     // keep libc string functions from interpreting C escape sequences.
    1972           0 :     for (size_t i = 0; i < jc.len; i++)
    1973           0 :       fputc(jc.buf[i], stderr);
    1974           0 :     fputc('\"', stderr);
    1975             :   }
    1976             :   else {
    1977           0 :     fputs(", \"special character\": ", stderr);
    1978           0 :     ci->nm.json_dump();
    1979             :   }
    1980           0 :   fflush(stderr);
    1981           0 : }
    1982             : 
    1983             : // A glyph node corresponds to a glyph supplied by a device font.
    1984             : 
    1985             : class glyph_node : public charinfo_node {
    1986             : protected:
    1987             :   tfont *tf;
    1988             :   color *gcol;
    1989             :   color *fcol;          /* this is needed for grotty */
    1990             : #ifdef STORE_WIDTH
    1991             :   hunits wid;
    1992             :   glyph_node(charinfo *, tfont *, color *, color *, hunits,
    1993             :              statem *, int, node * = 0 /* nullptr */);
    1994             : #endif
    1995             : public:
    1996             :   glyph_node(charinfo *, tfont *, color *, color *,
    1997             :              statem *, int, node * = 0 /* nullptr */);
    1998    26935158 :   ~glyph_node() {}
    1999             :   node *copy();
    2000             :   node *merge_glyph_node(glyph_node *);
    2001             :   node *merge_self(node *);
    2002             :   hunits width();
    2003             :   node *last_char_node();
    2004             :   units size();
    2005             :   void vertical_extent(vunits *, vunits *);
    2006             :   hunits subscript_correction();
    2007             :   hunits italic_correction();
    2008             :   hunits left_italic_correction();
    2009             :   hunits skew();
    2010             :   hyphenation_type get_hyphenation_type();
    2011             :   tfont *get_tfont();
    2012             :   color *get_stroke_color();
    2013             :   color *get_fill_color();
    2014             :   void tprint(troff_output_file *);
    2015             :   void zero_width_tprint(troff_output_file *);
    2016             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    2017             :   node *add_self(node *, hyphen_list **);
    2018             :   void ascii_print(ascii_output_file *);
    2019             :   void asciify(macro *);
    2020             :   int character_type();
    2021             :   bool is_same_as(node *);
    2022             :   const char *type();
    2023             :   bool causes_tprint();
    2024             :   bool is_tag();
    2025             : };
    2026             : 
    2027             : // Not derived from `container_node`; implements custom double container
    2028             : // dumper in dump_node().
    2029             : class ligature_node : public glyph_node {
    2030             :   node *n1;
    2031             :   node *n2;
    2032             : #ifdef STORE_WIDTH
    2033             :   ligature_node(charinfo *, tfont *, color *, color *, hunits,
    2034             :                 node *, node *, statem *, int,
    2035             :                 node * = 0 /* nullptr */);
    2036             : #endif
    2037             : public:
    2038             :   void *operator new(size_t);
    2039             :   void operator delete(void *);
    2040             :   ligature_node(charinfo *, tfont *, color *, color *,
    2041             :                 node *, node *, statem *, int,
    2042             :                 node * = 0 /* nullptr */);
    2043             :   ~ligature_node();
    2044             :   node *copy();
    2045             :   node *add_self(node *, hyphen_list **);
    2046             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    2047             :   void ascii_print(ascii_output_file *);
    2048             :   void asciify(macro *);
    2049             :   bool is_same_as(node *);
    2050             :   const char *type();
    2051             :   bool causes_tprint();
    2052             :   bool is_tag();
    2053             :   void dump_node();
    2054             : };
    2055             : 
    2056             : // Not derived from `container_node`; implements custom double container
    2057             : // dumper in dump_node().
    2058             : class kern_pair_node : public node {
    2059             :   hunits amount;
    2060             :   node *n1;
    2061             :   node *n2;
    2062             : public:
    2063             :   kern_pair_node(hunits, node *, node *, statem *, int,
    2064             :                  node * = 0 /* nullptr */);
    2065             :   ~kern_pair_node();
    2066             :   node *copy();
    2067             :   node *merge_glyph_node(glyph_node *);
    2068             :   node *add_self(node *, hyphen_list **);
    2069             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    2070             :   node *add_discretionary_hyphen();
    2071             :   hunits width();
    2072             :   node *last_char_node();
    2073             :   hunits italic_correction();
    2074             :   hunits subscript_correction();
    2075             :   void tprint(troff_output_file *);
    2076             :   hyphenation_type get_hyphenation_type();
    2077             :   int ends_sentence();
    2078             :   void ascii_print(ascii_output_file *);
    2079             :   void asciify(macro *);
    2080             :   bool is_same_as(node *);
    2081             :   const char *type();
    2082             :   bool causes_tprint();
    2083             :   bool is_tag();
    2084             :   void vertical_extent(vunits *, vunits *);
    2085             :   void dump_properties();
    2086             :   void dump_node();
    2087             : };
    2088             : 
    2089             : // Not derived from `container_node`; implements custom triple container
    2090             : // dumper in dump_node().
    2091             : class dbreak_node : public node {
    2092             :   node *none;
    2093             :   node *pre;
    2094             :   node *post;
    2095             : public:
    2096             :   dbreak_node(node *, node *, statem *, int, node * = 0 /* nullptr */);
    2097             :   ~dbreak_node();
    2098             :   node *copy();
    2099             :   node *merge_glyph_node(glyph_node *);
    2100             :   node *add_discretionary_hyphen();
    2101             :   hunits width();
    2102             :   node *last_char_node();
    2103             :   hunits italic_correction();
    2104             :   hunits subscript_correction();
    2105             :   void tprint(troff_output_file *);
    2106             :   breakpoint *get_breakpoints(hunits /* width */, int /* ns */,
    2107             :                               breakpoint * /* rest */ = 0 /* nullptr */,
    2108             :                               bool /* is_inner */ = false);
    2109             :   int nbreaks();
    2110             :   int ends_sentence();
    2111             :   void split(int, node **, node **);
    2112             :   hyphenation_type get_hyphenation_type();
    2113             :   void ascii_print(ascii_output_file *);
    2114             :   void asciify(macro *);
    2115             :   bool is_same_as(node *);
    2116             :   const char *type();
    2117             :   bool causes_tprint();
    2118             :   bool is_tag();
    2119             :   void dump_node();
    2120             : };
    2121             : 
    2122       11988 : void *ligature_node::operator new(size_t n)
    2123             : {
    2124       11988 :   return new char[n];
    2125             : }
    2126             : 
    2127       11920 : void ligature_node::operator delete(void *p)
    2128             : {
    2129       11920 :   delete[] (char *)p;
    2130       11920 : }
    2131             : 
    2132    12904721 : glyph_node::glyph_node(charinfo *c, tfont *t, color *gc, color *fc,
    2133    12904721 :                        statem *s, int divlevel, node *x)
    2134    12904721 : : charinfo_node(c, s, divlevel, x), tf(t), gcol(gc), fcol(fc)
    2135             : {
    2136             : #ifdef STORE_WIDTH
    2137    12904721 :   wid = tf->get_width(ci);
    2138             : #endif
    2139    12904721 : }
    2140             : 
    2141             : #ifdef STORE_WIDTH
    2142      596304 : glyph_node::glyph_node(charinfo *c, tfont *t,
    2143             :                        color *gc, color *fc, hunits w,
    2144      596304 :                        statem *s, int divlevel, node *x)
    2145      596304 : : charinfo_node(c, s, divlevel, x), tf(t), gcol(gc), fcol(fc), wid(w)
    2146             : {
    2147      596304 : }
    2148             : #endif
    2149             : 
    2150      595226 : node *glyph_node::copy()
    2151             : {
    2152             : #ifdef STORE_WIDTH
    2153      595226 :   return new glyph_node(ci, tf, gcol, fcol, wid, state, div_nest_level);
    2154             : #else
    2155             :   return new glyph_node(ci, tf, gcol, fcol, state, div_nest_level);
    2156             : #endif
    2157             : }
    2158             : 
    2159    12850473 : node *glyph_node::merge_self(node *nd)
    2160             : {
    2161    12850473 :   return nd->merge_glyph_node(this);
    2162             : }
    2163             : 
    2164      183183 : int glyph_node::character_type()
    2165             : {
    2166      183183 :   return tf->get_character_type(ci);
    2167             : }
    2168             : 
    2169      204097 : node *glyph_node::add_self(node *n, hyphen_list **p)
    2170             : {
    2171      204097 :   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
    2172      204097 :   next = 0 /* nullptr */;
    2173      204097 :   node *nn = 0 /* nullptr */;
    2174      408173 :   if ((0 /* nullptr */ == n) ||
    2175      204076 :       (0 /* nullptr */ == (nn = n->merge_glyph_node(this)))) {
    2176      198356 :     next = n;
    2177      198356 :     nn = this;
    2178             :   }
    2179      204097 :   if ((*p)->is_hyphen)
    2180       21404 :     nn = nn->add_discretionary_hyphen();
    2181      204097 :   hyphen_list *pp = *p;
    2182      204097 :   *p = (*p)->next;
    2183      204097 :   delete pp;
    2184      204097 :   assert(nn != 0 /* nullptr */);
    2185      204097 :   return nn;
    2186             : }
    2187             : 
    2188           0 : units glyph_node::size()
    2189             : {
    2190           0 :   return tf->get_size().to_units();
    2191             : }
    2192             : 
    2193      204097 : hyphen_list *glyph_node::get_hyphen_list(hyphen_list *tail, int *count)
    2194             : {
    2195      204097 :   (*count)++;
    2196      204097 :   return new hyphen_list(ci->get_hyphenation_code(), tail);
    2197             : }
    2198             : 
    2199       77166 : tfont *node::get_tfont()
    2200             : {
    2201       77166 :   return 0 /* nullptr */;
    2202             : }
    2203             : 
    2204       24563 : tfont *glyph_node::get_tfont()
    2205             : {
    2206       24563 :   return tf;
    2207             : }
    2208             : 
    2209          64 : color *node::get_stroke_color()
    2210             : {
    2211          64 :   return 0 /* nullptr */;
    2212             : }
    2213             : 
    2214       24499 : color *glyph_node::get_stroke_color()
    2215             : {
    2216       24499 :   return gcol;
    2217             : }
    2218             : 
    2219          64 : color *node::get_fill_color()
    2220             : {
    2221          64 :   return 0 /* nullptr */;
    2222             : }
    2223             : 
    2224       24499 : color *glyph_node::get_fill_color()
    2225             : {
    2226       24499 :   return fcol;
    2227             : }
    2228             : 
    2229     2539854 : node *node::merge_glyph_node(glyph_node *)
    2230             : {
    2231     2539854 :   return 0 /* nullptr */;
    2232             : }
    2233             : 
    2234    10508480 : node *glyph_node::merge_glyph_node(glyph_node *gn)
    2235             : {
    2236    10508480 :   if (tf == gn->tf && gcol == gn->gcol && fcol == gn->fcol) {
    2237             :     charinfo *lig;
    2238    10468377 :     if ((lig = tf->get_lig(ci, gn->ci)) != 0 /* nullptr */) {
    2239       10910 :       node *next1 = next;
    2240       10910 :       next = 0 /* nullptr */;
    2241             :       return new ligature_node(lig, tf, gcol, fcol, this, gn, state,
    2242      496310 :                                gn->div_nest_level, next1);
    2243             :     }
    2244    10457467 :     hunits kern;
    2245    10457467 :     if (tf->is_kerned(ci, gn->ci, &kern)) {
    2246      485400 :       node *next1 = next;
    2247      485400 :       next = 0 /* nullptr */;
    2248             :       return new kern_pair_node(kern, this, gn, state,
    2249      485400 :                                 gn->div_nest_level, next1);
    2250             :     }
    2251             :   }
    2252    10012170 :   return 0 /* nullptr */;
    2253             : }
    2254             : 
    2255             : #ifdef STORE_WIDTH
    2256             : inline
    2257             : #endif
    2258    27884326 : hunits glyph_node::width()
    2259             : {
    2260             : #ifdef STORE_WIDTH
    2261    27884326 :   return wid;
    2262             : #else
    2263             :   return tf->get_width(ci);
    2264             : #endif
    2265             : }
    2266             : 
    2267        3401 : node *glyph_node::last_char_node()
    2268             : {
    2269        3401 :   return this;
    2270             : }
    2271             : 
    2272      195186 : void glyph_node::vertical_extent(vunits *min, vunits *max)
    2273             : {
    2274      195186 :   *min = -tf->get_char_height(ci);
    2275      195186 :   *max = tf->get_char_depth(ci);
    2276      195186 : }
    2277             : 
    2278       20778 : hunits glyph_node::skew()
    2279             : {
    2280       20778 :   return tf->get_char_skew(ci);
    2281             : }
    2282             : 
    2283       21035 : hunits glyph_node::subscript_correction()
    2284             : {
    2285       21035 :   return tf->get_subscript_correction(ci);
    2286             : }
    2287             : 
    2288       33166 : hunits glyph_node::italic_correction()
    2289             : {
    2290       33166 :   return tf->get_italic_correction(ci);
    2291             : }
    2292             : 
    2293       32205 : hunits glyph_node::left_italic_correction()
    2294             : {
    2295       32205 :   return tf->get_left_italic_correction(ci);
    2296             : }
    2297             : 
    2298      194899 : hyphenation_type glyph_node::get_hyphenation_type()
    2299             : {
    2300      194899 :   return HYPHENATION_PERMITTED;
    2301             : }
    2302             : 
    2303        1191 : void glyph_node::ascii_print(ascii_output_file *ascii)
    2304             : {
    2305        1191 :   unsigned char c = ci->get_ascii_code();
    2306        1191 :   if (c != 0U)
    2307        1140 :     ascii->outc(c);
    2308             :   else
    2309          51 :     ascii->outs(ci->nm.contents());
    2310        1191 : }
    2311       10910 : ligature_node::ligature_node(charinfo *c, tfont *t,
    2312             :                              color *gc, color *fc,
    2313             :                              node *gn1, node *gn2, statem *s,
    2314       10910 :                              int divlevel, node *x)
    2315       10910 : : glyph_node(c, t, gc, fc, s, divlevel, x), n1(gn1), n2(gn2)
    2316             : {
    2317       10910 : }
    2318             : 
    2319             : #ifdef STORE_WIDTH
    2320        1078 : ligature_node::ligature_node(charinfo *c, tfont *t,
    2321             :                              color *gc, color *fc, hunits w,
    2322             :                              node *gn1, node *gn2, statem *s,
    2323        1078 :                              int divlevel, node *x)
    2324        1078 : : glyph_node(c, t, gc, fc, w, s, divlevel, x), n1(gn1), n2(gn2)
    2325             : {
    2326        1078 : }
    2327             : #endif
    2328             : 
    2329       23840 : ligature_node::~ligature_node()
    2330             : {
    2331       11920 :   delete n1;
    2332       11920 :   delete n2;
    2333       23840 : }
    2334             : 
    2335        1078 : node *ligature_node::copy()
    2336             : {
    2337             : #ifdef STORE_WIDTH
    2338        1078 :   return new ligature_node(ci, tf, gcol, fcol, wid, n1->copy(),
    2339        1078 :                            n2->copy(), state, div_nest_level);
    2340             : #else
    2341             :   return new ligature_node(ci, tf, gcol, fcol, n1->copy(), n2->copy(),
    2342             :                            state, div_nest_level);
    2343             : #endif
    2344             : }
    2345             : 
    2346           0 : void ligature_node::ascii_print(ascii_output_file *ascii)
    2347             : {
    2348           0 :   n1->ascii_print(ascii);
    2349           0 :   n2->ascii_print(ascii);
    2350           0 : }
    2351             : 
    2352         400 : hyphen_list *ligature_node::get_hyphen_list(hyphen_list *tail,
    2353             :                                             int *count)
    2354             : {
    2355         400 :   hyphen_list *hl = n2->get_hyphen_list(tail, count);
    2356         400 :   return n1->get_hyphen_list(hl, count);
    2357             : }
    2358             : 
    2359         400 : node *ligature_node::add_self(node *n, hyphen_list **p)
    2360             : {
    2361         400 :   n = n1->add_self(n, p);
    2362         400 :   n = n2->add_self(n, p);
    2363         400 :   n1 = n2 = 0 /* nullptr */;
    2364         400 :   delete this;
    2365         400 :   return n;
    2366             : }
    2367             : 
    2368           0 : void ligature_node::dump_node()
    2369             : {
    2370           0 :   fputc('{', stderr);
    2371             :   // Flush so that in case something goes wrong with property dumping,
    2372             :   // we know that we traversed to a new node.
    2373           0 :   fflush(stderr);
    2374           0 :   node::dump_properties();
    2375           0 :   if (n1 != 0 /* nullptr */) {
    2376           0 :     fputs(", \"n1\": ", stderr);
    2377           0 :     n1->dump_node();
    2378             :   }
    2379           0 :   if (n2 != 0 /* nullptr */) {
    2380           0 :     fputs(", \"n2\": ", stderr);
    2381           0 :     n2->dump_node();
    2382             :   }
    2383           0 :   fputc('}', stderr);
    2384           0 :   fflush(stderr);
    2385           0 : }
    2386             : 
    2387      499945 : kern_pair_node::kern_pair_node(hunits n, node *first, node *second,
    2388      499945 :                                statem* s, int divlevel, node *x)
    2389      499945 : : node(x, s, divlevel), amount(n), n1(first), n2(second)
    2390             : {
    2391      499945 : }
    2392             : 
    2393           0 : void kern_pair_node::dump_properties()
    2394             : {
    2395           0 :   node::dump_properties();
    2396           0 :   fprintf(stderr, ", \"amount\": %d", amount.to_units());
    2397           0 :   fflush(stderr);
    2398           0 : }
    2399             : 
    2400           0 : void kern_pair_node::dump_node()
    2401             : {
    2402           0 :   fputc('{', stderr);
    2403             :   // Flush so that in case something goes wrong with property dumping,
    2404             :   // we know that we traversed to a new node.
    2405           0 :   fflush(stderr);
    2406           0 :   dump_properties();
    2407           0 :   if (n1 != 0 /* nullptr */) {
    2408           0 :     fputs(", \"n1\": ", stderr);
    2409           0 :     n1->dump_node();
    2410             :   }
    2411           0 :   if (n2 != 0 /* nullptr */) {
    2412           0 :     fputs(", \"n2\": ", stderr);
    2413           0 :     n2->dump_node();
    2414             :   }
    2415           0 :   fputc('}', stderr);
    2416           0 :   fflush(stderr);
    2417           0 : }
    2418             : 
    2419       25115 : dbreak_node::dbreak_node(node *n, node *p, statem *s, int divlevel,
    2420       25115 :                          node *x)
    2421       25115 : : node(x, s, divlevel), none(n), pre(p), post(0 /* nullptr */)
    2422             : {
    2423       25115 : }
    2424             : 
    2425       25503 : node *dbreak_node::merge_glyph_node(glyph_node *gn)
    2426             : {
    2427       25503 :   glyph_node *gn2 = static_cast<glyph_node *>(gn->copy());
    2428       25503 :   node *new_none = none ? none->merge_glyph_node(gn) : 0 /* nullptr */;
    2429       25503 :   node *new_post = post ? post->merge_glyph_node(gn2) : 0 /* nullptr */;
    2430       25503 :   if ((0 /* nullptr */ == new_none) && (0 /* nullptr */ == new_post)) {
    2431       24681 :     delete gn2;
    2432       24681 :     return 0 /* nullptr */;
    2433             :   }
    2434         822 :   if (new_none != 0 /* nullptr */)
    2435         822 :     none = new_none;
    2436             :   else {
    2437           0 :     gn->next = none;
    2438           0 :     none = gn;
    2439             :   }
    2440         822 :   if (new_post != 0 /* nullptr */)
    2441         122 :     post = new_post;
    2442             :   else {
    2443         700 :     gn2->next = post;
    2444         700 :     post = gn2;
    2445             :   }
    2446         822 :   return this;
    2447             : }
    2448             : 
    2449      457287 : node *kern_pair_node::merge_glyph_node(glyph_node *gn)
    2450             : {
    2451      457287 :   node *nd = n2->merge_glyph_node(gn);
    2452      457287 :   if (0 /* nullptr */ == nd)
    2453      397399 :     return 0 /* nullptr */;
    2454       59888 :   n2 = nd;
    2455       59888 :   nd = n2->merge_self(n1);
    2456       59888 :   if (nd != 0 /* nullptr */) {
    2457           0 :     nd->next = next;
    2458           0 :     n1 = n2 = 0 /* nullptr */;
    2459           0 :     delete this;
    2460           0 :     return nd;
    2461             :   }
    2462       59888 :   return this;
    2463             : }
    2464             : 
    2465        5912 : hunits kern_pair_node::italic_correction()
    2466             : {
    2467        5912 :   return n2->italic_correction();
    2468             : }
    2469             : 
    2470         328 : hunits kern_pair_node::subscript_correction()
    2471             : {
    2472         328 :   return n2->subscript_correction();
    2473             : }
    2474             : 
    2475        5125 : void kern_pair_node::vertical_extent(vunits *min, vunits *max)
    2476             : {
    2477        5125 :   n1->vertical_extent(min, max);
    2478        5125 :   vunits min2, max2;
    2479        5125 :   n2->vertical_extent(&min2, &max2);
    2480        5125 :   if (min2 < *min)
    2481         442 :     *min = min2;
    2482        5125 :   if (max2 > *max)
    2483        2949 :     *max = max2;
    2484        5125 : }
    2485             : 
    2486         572 : node *kern_pair_node::add_discretionary_hyphen()
    2487             : {
    2488         572 :   tfont *tf = n1->get_tfont();
    2489         572 :   if (tf != 0 /* nullptr */) {
    2490         572 :     if (tf->contains(soft_hyphen_char)) {
    2491         572 :       color *gcol = n2->get_stroke_color();
    2492         572 :       color *fcol = n2->get_fill_color();
    2493         572 :       node *next1 = next;
    2494         572 :       next = 0 /* nullptr */;
    2495         572 :       node *n = copy();
    2496             :       glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
    2497         572 :                                       state, div_nest_level);
    2498         572 :       node *nn = n->merge_glyph_node(gn);
    2499         572 :       if (0 /* nullptr */ == nn) {
    2500         572 :         gn->next = n;
    2501         572 :         nn = gn;
    2502             :       }
    2503         572 :       return new dbreak_node(this, nn, state, div_nest_level, next1);
    2504             :     }
    2505             :   }
    2506           0 :   return this;
    2507             : }
    2508             : 
    2509      998084 : kern_pair_node::~kern_pair_node()
    2510             : {
    2511      499042 :   if (n1 != 0 /* nullptr */)
    2512      494326 :     delete n1;
    2513      499042 :   if (n2 != 0 /* nullptr */)
    2514      494326 :     delete n2;
    2515      998084 : }
    2516             : 
    2517       49884 : dbreak_node::~dbreak_node()
    2518             : {
    2519       24942 :   delete_node_list(pre);
    2520       24942 :   delete_node_list(post);
    2521       24942 :   delete_node_list(none);
    2522       49884 : }
    2523             : 
    2524       14545 : node *kern_pair_node::copy()
    2525             : {
    2526       14545 :   return new kern_pair_node(amount, n1->copy(), n2->copy(), state,
    2527       14545 :                             div_nest_level);
    2528             : }
    2529             : 
    2530        5452 : node *copy_node_list(node *n)
    2531             : {
    2532        5452 :   node *p = 0 /* nullptr */;
    2533       15964 :   while (n != 0 /* nullptr */) {
    2534       10512 :     node *nn = n->copy();
    2535       10512 :     nn->next = p;
    2536       10512 :     p = nn;
    2537       10512 :     n = n->next;
    2538             :   }
    2539       15964 :   while (p != 0 /* nullptr */) {
    2540       10512 :     node *pp = p->next;
    2541       10512 :     p->next = n;
    2542       10512 :     n = p;
    2543       10512 :     p = pp;
    2544             :   }
    2545        5452 :   return n;
    2546             : }
    2547             : 
    2548    39504514 : void delete_node_list(node *n)
    2549             : {
    2550    39504514 :   while (n != 0 /* nullptr */) {
    2551    27478131 :     node *tem = n;
    2552    27478131 :     n = n->next;
    2553    27478131 :     delete tem;
    2554             :   }
    2555    12026383 : }
    2556             : 
    2557           0 : void dump_node_list(node *n)
    2558             : {
    2559           0 :   bool need_comma = false;
    2560           0 :   fputc('[', stderr);
    2561           0 :   while (n != 0 /* nullptr */) {
    2562           0 :     if (need_comma)
    2563           0 :       fputs(", ", stderr);
    2564           0 :     n->dump_node();
    2565           0 :     need_comma = true;
    2566           0 :     n = n->next;
    2567             :   }
    2568             :   // !need_comma implies that the list was empty.  JSON convention is to
    2569             :   // put a space between an empty pair of square brackets.
    2570           0 :   if (!need_comma)
    2571           0 :     fputc(' ', stderr);
    2572           0 :   fputc(']', stderr);
    2573           0 :   fflush(stderr);
    2574           0 : }
    2575             : 
    2576         552 : node *dbreak_node::copy()
    2577             : {
    2578         552 :   dbreak_node *p = new dbreak_node(copy_node_list(none),
    2579         552 :                                    copy_node_list(pre), state,
    2580         552 :                                    div_nest_level);
    2581         552 :   p->post = copy_node_list(post);
    2582         552 :   return p;
    2583             : }
    2584             : 
    2585        4466 : hyphen_list *node::get_hyphen_list(hyphen_list *tail, int *)
    2586             : {
    2587        4466 :   return tail;
    2588             : }
    2589             : 
    2590        4716 : hyphen_list *kern_pair_node::get_hyphen_list(hyphen_list *tail,
    2591             :                                              int *count)
    2592             : {
    2593        4716 :   hyphen_list *hl = n2->get_hyphen_list(tail, count);
    2594        4716 :   return n1->get_hyphen_list(hl, count);
    2595             : }
    2596             : 
    2597             : class hyphen_inhibitor_node : public node {
    2598             : public:
    2599             :   hyphen_inhibitor_node(node * = 0 /* nullptr */);
    2600             :   void asciify(macro *);
    2601             :   node *copy();
    2602             :   bool causes_tprint();
    2603             :   bool is_tag();
    2604             :   bool is_same_as(node *);
    2605             :   const char *type();
    2606             :   hyphenation_type get_hyphenation_type();
    2607             : };
    2608             : 
    2609       91067 : hyphen_inhibitor_node::hyphen_inhibitor_node(node *nd) : node(nd)
    2610             : {
    2611       91067 : }
    2612             : 
    2613       11336 : node *hyphen_inhibitor_node::copy()
    2614             : {
    2615       11336 :   return new hyphen_inhibitor_node;
    2616             : }
    2617             : 
    2618          46 : bool hyphen_inhibitor_node::causes_tprint()
    2619             : {
    2620          46 :   return false;
    2621             : }
    2622             : 
    2623       11599 : bool hyphen_inhibitor_node::is_tag()
    2624             : {
    2625       11599 :   return false;
    2626             : }
    2627             : 
    2628         890 : bool hyphen_inhibitor_node::is_same_as(node *)
    2629             : {
    2630         890 :   return true;
    2631             : }
    2632             : 
    2633        1802 : const char *hyphen_inhibitor_node::type()
    2634             : {
    2635        1802 :   return "hyphenation inhibitor node";
    2636             : }
    2637             : 
    2638        1096 : hyphenation_type hyphen_inhibitor_node::get_hyphenation_type()
    2639             : {
    2640        1096 :   return HYPHENATION_INHIBITED;
    2641             : }
    2642             : 
    2643             : /* add_discretionary_hyphen methods */
    2644             : 
    2645          71 : node *dbreak_node::add_discretionary_hyphen()
    2646             : {
    2647          71 :   if (post)
    2648          71 :     post = post->add_discretionary_hyphen();
    2649          71 :   if (none)
    2650          71 :     none = none->add_discretionary_hyphen();
    2651          71 :   return this;
    2652             : }
    2653             : 
    2654      103722 : node *node::add_discretionary_hyphen()
    2655             : {
    2656      103722 :   tfont *tf = get_tfont();
    2657      103722 :   if (0 /* nullptr */ == tf)
    2658       79731 :     return new hyphen_inhibitor_node(this);
    2659       23991 :   if (tf->contains(soft_hyphen_char)) {
    2660       23991 :     color *gcol = get_stroke_color();
    2661       23991 :     color *fcol = get_fill_color();
    2662       23991 :     node *next1 = next;
    2663       23991 :     next = 0 /* nullptr */;
    2664       23991 :     node *n = copy();
    2665             :     glyph_node *gn = new glyph_node(soft_hyphen_char, tf, gcol, fcol,
    2666       23991 :                                     state, div_nest_level);
    2667       23991 :     node *n1 = n->merge_glyph_node(gn);
    2668       23991 :     if (0 /* nullptr */ == n1) {
    2669       22557 :       gn->next = n;
    2670       22557 :       n1 = gn;
    2671             :     }
    2672       23991 :     return new dbreak_node(this, n1, state, div_nest_level, next1);
    2673             :   }
    2674           0 :   return this;
    2675             : }
    2676             : 
    2677       60066 : node *node::merge_self(node *)
    2678             : {
    2679       60066 :   return 0 /* nullptr */;
    2680             : }
    2681             : 
    2682        4466 : node *node::add_self(node *n, hyphen_list ** /*p*/)
    2683             : {
    2684        4466 :   next = n;
    2685        4466 :   return this;
    2686             : }
    2687             : 
    2688        4716 : node *kern_pair_node::add_self(node *n, hyphen_list **p)
    2689             : {
    2690        4716 :   n = n1->add_self(n, p);
    2691        4716 :   n = n2->add_self(n, p);
    2692        4716 :   n1 = n2 = 0 /* nullptr */;
    2693        4716 :   delete this;
    2694        4716 :   return n;
    2695             : }
    2696             : 
    2697     8171552 : hunits node::width()
    2698             : {
    2699     8171552 :   return H0;
    2700             : }
    2701             : 
    2702           8 : node *node::last_char_node()
    2703             : {
    2704           8 :   return 0 /* nullptr */;
    2705             : }
    2706             : 
    2707           0 : bool node::causes_tprint()
    2708             : {
    2709           0 :   return false;
    2710             : }
    2711             : 
    2712           0 : bool node::is_tag()
    2713             : {
    2714           0 :   return false;
    2715             : }
    2716             : 
    2717             : // TODO: Figure out what a hyphenation code means in the UTF-8 future,
    2718             : // where a "grochar" is a vector of NFD decomposed code points.  Can it
    2719             : // be a scalar--a 32-bit int?
    2720      132199 : unsigned char node::get_break_code()
    2721             : {
    2722      132199 :   return 0U;
    2723             : }
    2724             : 
    2725     2000156 : hunits hmotion_node::width()
    2726             : {
    2727     2000156 :   return n;
    2728             : }
    2729             : 
    2730           0 : units node::size()
    2731             : {
    2732           0 :   return points_to_units(10);
    2733             : }
    2734             : 
    2735           0 : void node::dump_properties()
    2736             : {
    2737           0 :   fprintf(stderr, "\"type\": \"%s\"", type());
    2738           0 :   fprintf(stderr, ", \"diversion level\": %d", div_nest_level);
    2739           0 :   fprintf(stderr, ", \"is_special_node\": %s",
    2740           0 :           is_special ? "true" : "false");
    2741           0 :   if (push_state) {
    2742           0 :     fputs(", \"push_state\": ", stderr);
    2743           0 :     push_state->display_state();
    2744             :   }
    2745           0 :   if (state) {
    2746           0 :     fputs(", \"state\": ", stderr);
    2747           0 :     state->display_state();
    2748             :   }
    2749           0 :   fflush(stderr);
    2750           0 : }
    2751             : 
    2752           0 : void node::dump_node()
    2753             : {
    2754           0 :   fputc('{', stderr);
    2755             :   // Flush so that in case something goes wrong with property dumping,
    2756             :   // we know that we traversed to a new node.
    2757           0 :   fflush(stderr);
    2758           0 :   dump_properties();
    2759           0 :   fputc('}', stderr);
    2760           0 :   fflush(stderr);
    2761           0 : }
    2762             : 
    2763       34835 : container_node::container_node(node *contents)
    2764       34835 : : node(), nodes(contents)
    2765             : {
    2766       34835 : }
    2767             : 
    2768      138258 : container_node::container_node(node *nxt, node *contents)
    2769      138258 : : node(nxt), nodes(contents)
    2770             : {
    2771      138258 : }
    2772             : 
    2773             : // `left_italic_corrected_node` uses an initially empty container.
    2774        7292 : container_node::container_node(node *nxt, statem *s, int divl)
    2775        7292 : : node(nxt, s, divl), nodes(0 /* nullptr */)
    2776             : {
    2777        7292 : }
    2778             : 
    2779             : #if 0
    2780             : container_node::container_node(node *nxt, statem *s, node *contents)
    2781             : : node(nxt, s), nodes(contents)
    2782             : {
    2783             : }
    2784             : #endif
    2785             : 
    2786       29654 : container_node::container_node(node *nxt, statem *s, int divl,
    2787       29654 :                                node *contents)
    2788       29654 : : node(nxt, s, divl), nodes(contents)
    2789             : {
    2790       29654 : }
    2791             : 
    2792           0 : container_node::container_node(node *nxt, statem *s, int divl,
    2793           0 :                                bool special, node *contents)
    2794           0 : : node(nxt, s, divl, special), nodes(contents)
    2795             : {
    2796           0 : }
    2797             : 
    2798      209415 : container_node::~container_node()
    2799             : {
    2800      209415 :   delete_node_list(nodes);
    2801      209415 : }
    2802             : 
    2803           0 : void container_node::dump_node()
    2804             : {
    2805           0 :   fputc('{', stderr);
    2806           0 :   dump_properties();
    2807           0 :   fputs(", \"contains\": ", stderr);
    2808           0 :   dump_node_list(nodes);
    2809           0 :   fputc('}', stderr);
    2810           0 :   fflush(stderr);
    2811           0 : }
    2812             : 
    2813           0 : void dump_node_list_in_reverse(node *nlist)
    2814             : {
    2815             :   // It's stored in reverse order already; this puts it forward again.
    2816           0 :   std::stack<node *> reversed_node_list;
    2817           0 :   node *n = nlist;
    2818             : 
    2819           0 :   while (n != 0 /* nullptr */) {
    2820           0 :     reversed_node_list.push(n);
    2821           0 :     n = n->next;
    2822             :   }
    2823           0 :   fputc('[', stderr);
    2824           0 :   bool need_comma = false;
    2825           0 :   while (!reversed_node_list.empty()) {
    2826           0 :     if (need_comma)
    2827           0 :       fputs(",\n", stderr);
    2828           0 :     reversed_node_list.top()->dump_node();
    2829           0 :     reversed_node_list.pop();
    2830           0 :     need_comma = true;
    2831             :   }
    2832             :   // !need_comma implies that the list was empty.  JSON convention is to
    2833             :   // put a space between an empty pair of square brackets.
    2834           0 :   if (!need_comma)
    2835           0 :     fputc(' ', stderr);
    2836           0 :   fputs("]\n", stderr);
    2837           0 :   fflush(stderr);
    2838           0 : }
    2839             : 
    2840     1018548 : hunits kern_pair_node::width()
    2841             : {
    2842     2037096 :   return n1->width() + n2->width() + amount;
    2843             : }
    2844             : 
    2845           0 : node *kern_pair_node::last_char_node()
    2846             : {
    2847           0 :   node *nd = n2->last_char_node();
    2848           0 :   if (nd != 0 /* nullptr */)
    2849           0 :     return nd;
    2850           0 :   return n1->last_char_node();
    2851             : }
    2852             : 
    2853       34761 : hunits dbreak_node::width()
    2854             : {
    2855       34761 :   hunits x = H0;
    2856       69522 :   for (node *n = none; n != 0 /* nullptr */; n = n->next)
    2857       34761 :     x += n->width();
    2858       34761 :   return x;
    2859             : }
    2860             : 
    2861           0 : node *dbreak_node::last_char_node()
    2862             : {
    2863           0 :   for (node *n = none; n != 0 /* nullptr */; n = n->next) {
    2864           0 :     node *last_node = n->last_char_node();
    2865           0 :     if (last_node)
    2866           0 :       return last_node;
    2867             :   }
    2868           0 :   return 0 /* nullptr */;
    2869             : }
    2870             : 
    2871           0 : hunits dbreak_node::italic_correction()
    2872             : {
    2873           0 :   return none ? none->italic_correction() : H0;
    2874             : }
    2875             : 
    2876           0 : hunits dbreak_node::subscript_correction()
    2877             : {
    2878           0 :   return none ? none->subscript_correction() : H0;
    2879             : }
    2880             : 
    2881             : class italic_corrected_node : public container_node {
    2882             :   hunits x;
    2883             : public:
    2884             :   italic_corrected_node(node *, hunits, statem *, int,
    2885             :                         node * = 0 /* nullptr */);
    2886             :   node *copy();
    2887             :   void ascii_print(ascii_output_file *);
    2888             :   void asciify(macro *);
    2889             :   hunits width();
    2890             :   node *last_char_node();
    2891             :   void vertical_extent(vunits *, vunits *);
    2892             :   int ends_sentence();
    2893             :   bool overlaps_horizontally();
    2894             :   bool overlaps_vertically();
    2895             :   bool is_same_as(node *);
    2896             :   hyphenation_type get_hyphenation_type();
    2897             :   tfont *get_tfont();
    2898             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    2899             :   int character_type();
    2900             :   void tprint(troff_output_file *);
    2901             :   hunits subscript_correction();
    2902             :   hunits skew();
    2903             :   node *add_self(node *, hyphen_list **);
    2904             :   const char *type();
    2905             :   bool causes_tprint();
    2906             :   bool is_tag();
    2907             :   void dump_properties();
    2908             : };
    2909             : 
    2910       34465 : node *node::add_italic_correction(hunits *wd)
    2911             : {
    2912       34465 :   hunits ic = italic_correction();
    2913       34465 :   if (ic.is_zero())
    2914       13204 :     return this;
    2915             :   else {
    2916       21261 :     node *next1 = next;
    2917       21261 :     next = 0 /* nullptr */;
    2918       21261 :     *wd += ic;
    2919             :     return new italic_corrected_node(this, ic, state, div_nest_level,
    2920       21261 :                                      next1);
    2921             :   }
    2922             : }
    2923             : 
    2924       24779 : italic_corrected_node::italic_corrected_node(node *nn, hunits xx,
    2925             :                                              statem *s, int divlevel,
    2926       24779 :                                              node *p)
    2927       24779 : : container_node(p, s, divlevel, nn), x(xx)
    2928             : {
    2929       24779 :   assert(nodes != 0 /* nullptr */);
    2930       24779 : }
    2931             : 
    2932           0 : void italic_corrected_node::dump_properties()
    2933             : {
    2934           0 :   node::dump_properties();
    2935           0 :   fprintf(stderr, ", \"hunits\": %d", x.to_units());
    2936           0 :   fflush(stderr);
    2937           0 : }
    2938             : 
    2939        3518 : node *italic_corrected_node::copy()
    2940             : {
    2941        3518 :   return new italic_corrected_node(nodes->copy(), x, state,
    2942        3518 :                                    div_nest_level);
    2943             : }
    2944             : 
    2945       12916 : hunits italic_corrected_node::width()
    2946             : {
    2947       25832 :   return nodes->width() + x;
    2948             : }
    2949             : 
    2950         484 : void italic_corrected_node::vertical_extent(vunits *min, vunits *max)
    2951             : {
    2952         484 :   nodes->vertical_extent(min, max);
    2953         484 : }
    2954             : 
    2955       10566 : void italic_corrected_node::tprint(troff_output_file *out)
    2956             : {
    2957       10566 :   nodes->tprint(out);
    2958       10566 :   out->right(x);
    2959       10566 : }
    2960             : 
    2961         294 : hunits italic_corrected_node::skew()
    2962             : {
    2963         588 :   return nodes->skew() - (x / 2);
    2964             : }
    2965             : 
    2966         294 : hunits italic_corrected_node::subscript_correction()
    2967             : {
    2968         588 :   return nodes->subscript_correction() - x;
    2969             : }
    2970             : 
    2971          10 : void italic_corrected_node::ascii_print(ascii_output_file *out)
    2972             : {
    2973          10 :   nodes->ascii_print(out);
    2974          10 : }
    2975             : 
    2976       23698 : int italic_corrected_node::ends_sentence()
    2977             : {
    2978       23698 :   return nodes->ends_sentence();
    2979             : }
    2980             : 
    2981           0 : bool italic_corrected_node::overlaps_horizontally()
    2982             : {
    2983           0 :   return nodes->overlaps_horizontally();
    2984             : }
    2985             : 
    2986           0 : bool italic_corrected_node::overlaps_vertically()
    2987             : {
    2988           0 :   return nodes->overlaps_vertically();
    2989             : }
    2990             : 
    2991           0 : node *italic_corrected_node::last_char_node()
    2992             : {
    2993           0 :   return nodes->last_char_node();
    2994             : }
    2995             : 
    2996           0 : tfont *italic_corrected_node::get_tfont()
    2997             : {
    2998           0 :   return nodes->get_tfont();
    2999             : }
    3000             : 
    3001         668 : hyphenation_type italic_corrected_node::get_hyphenation_type()
    3002             : {
    3003         668 :   return nodes->get_hyphenation_type();
    3004             : }
    3005             : 
    3006         668 : node *italic_corrected_node::add_self(node *nd, hyphen_list **p)
    3007             : {
    3008         668 :   nd = nodes->add_self(nd, p);
    3009         668 :   hunits not_interested;
    3010         668 :   nd = nd->add_italic_correction(&not_interested);
    3011         668 :   nodes = 0 /* nullptr */;
    3012         668 :   delete this;
    3013         668 :   return nd;
    3014             : }
    3015             : 
    3016         668 : hyphen_list *italic_corrected_node::get_hyphen_list(hyphen_list *tail,
    3017             :                                                     int *count)
    3018             : {
    3019         668 :   return nodes->get_hyphen_list(tail, count);
    3020             : }
    3021             : 
    3022         484 : int italic_corrected_node::character_type()
    3023             : {
    3024         484 :   return nodes->character_type();
    3025             : }
    3026             : 
    3027             : class break_char_node : public container_node {
    3028             :   // TODO: Figure out what a hyphenation code means in the UTF-8 future,
    3029             :   // where a "grochar" is a vector of NFD decomposed code points.  Can
    3030             :   // it be a scalar--a 32-bit int?
    3031             :   unsigned char break_code;
    3032             :   unsigned char prev_break_code;
    3033             :   color *col;
    3034             : public:
    3035             :   break_char_node(node *, int, int, color *, node * = 0 /* nullptr */);
    3036             :   break_char_node(node *, int, int, color *, statem *, int,
    3037             :                   node * = 0 /* nullptr */);
    3038             :   node *copy();
    3039             :   hunits width();
    3040             :   vunits vertical_width();
    3041             :   node *last_char_node();
    3042             :   int character_type();
    3043             :   int ends_sentence();
    3044             :   node *add_self(node *, hyphen_list **);
    3045             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    3046             :   void tprint(troff_output_file *);
    3047             :   void zero_width_tprint(troff_output_file *);
    3048             :   void ascii_print(ascii_output_file *);
    3049             :   void asciify(macro *);
    3050             :   hyphenation_type get_hyphenation_type();
    3051             :   bool overlaps_vertically();
    3052             :   bool overlaps_horizontally();
    3053             :   units size();
    3054             :   tfont *get_tfont();
    3055             :   bool is_same_as(node *);
    3056             :   const char *type();
    3057             :   bool causes_tprint();
    3058             :   bool is_tag();
    3059             :   unsigned char get_break_code();
    3060             :   void dump_properties();
    3061             : };
    3062             : 
    3063      137765 : break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
    3064      137765 :                                  node *x)
    3065      137765 : : container_node(x, n), break_code(bc), prev_break_code(pbc), col(c)
    3066             : {
    3067      137765 : }
    3068             : 
    3069        4006 : break_char_node::break_char_node(node *n, int bc, int pbc, color *c,
    3070        4006 :                                  statem *s, int divlevel, node *x)
    3071             : : container_node(x, s, divlevel, n), break_code(bc),
    3072        4006 :   prev_break_code(pbc), col(c)
    3073             : {
    3074        4006 : }
    3075             : 
    3076           0 : void break_char_node::dump_properties()
    3077             : {
    3078           0 :   node::dump_properties();
    3079           0 :   fprintf(stderr, ", \"break code before\": %d", break_code);
    3080           0 :   fprintf(stderr, ", \"break code after\": %d", prev_break_code);
    3081           0 :   fputs(", \"terminal_color\": ", stderr);
    3082           0 :   col->nm.json_dump();
    3083           0 :   fflush(stderr);
    3084           0 : }
    3085             : 
    3086        4006 : node *break_char_node::copy()
    3087             : {
    3088        4006 :   return new break_char_node(nodes->copy(), break_code, prev_break_code,
    3089        4006 :                              col, state, div_nest_level);
    3090             : }
    3091             : 
    3092      172340 : hunits break_char_node::width()
    3093             : {
    3094      172340 :   return nodes->width();
    3095             : }
    3096             : 
    3097        5132 : vunits break_char_node::vertical_width()
    3098             : {
    3099        5132 :   return nodes->vertical_width();
    3100             : }
    3101             : 
    3102           0 : node *break_char_node::last_char_node()
    3103             : {
    3104           0 :   return nodes->last_char_node();
    3105             : }
    3106             : 
    3107        2199 : int break_char_node::character_type()
    3108             : {
    3109        2199 :   return nodes->character_type();
    3110             : }
    3111             : 
    3112         134 : int break_char_node::ends_sentence()
    3113             : {
    3114         134 :   return nodes->ends_sentence();
    3115             : }
    3116             : 
    3117             : // Keep these symbol names in sync with the superset used in an
    3118             : // anonymous `enum` in "charinfo.h".
    3119             : enum break_char_type {
    3120             :   ALLOWS_BREAK_BEFORE = 0x01,
    3121             :   ALLOWS_BREAK_AFTER = 0x02,
    3122             :   IGNORES_SURROUNDING_HYPHENATION_CODES = 0x04,
    3123             :   PROHIBITS_BREAK_BEFORE = 0x08,
    3124             :   PROHIBITS_BREAK_AFTER = 0x10,
    3125             :   IS_INTERWORD_SPACE = 0x20
    3126             : };
    3127             : 
    3128         917 : node *break_char_node::add_self(node *n, hyphen_list **p)
    3129             : {
    3130         917 :   bool have_space_node = false;
    3131         917 :   assert(0U == (*p)->hyphenation_code);
    3132         917 :   if (break_code & ALLOWS_BREAK_BEFORE) {
    3133           0 :     if (((*p)->is_breakable)
    3134           0 :         || (break_code & IGNORES_SURROUNDING_HYPHENATION_CODES)) {
    3135           0 :       n = new space_node(H0, col, n);
    3136           0 :       n->freeze_space();
    3137           0 :       have_space_node = true;
    3138             :     }
    3139             :   }
    3140         917 :   if (!have_space_node) {
    3141         917 :     if ((prev_break_code & IS_INTERWORD_SPACE)
    3142         915 :         || (prev_break_code & PROHIBITS_BREAK_AFTER)) {
    3143           2 :       if (break_code & PROHIBITS_BREAK_BEFORE)
    3144             :         // stretchable zero-width space not implemented yet
    3145             :         ;
    3146             :       else {
    3147             :         // breakable, stretchable zero-width space not implemented yet
    3148           2 :         n = new space_node(H0, col, n);
    3149           2 :         n->freeze_space();
    3150             :       }
    3151             :     }
    3152             :   }
    3153         917 :   next = n;
    3154         917 :   n = this;
    3155         917 :   if (break_code & ALLOWS_BREAK_AFTER) {
    3156         913 :     if (((*p)->is_breakable)
    3157         119 :         || (break_code & IGNORES_SURROUNDING_HYPHENATION_CODES)) {
    3158         794 :       n = new space_node(H0, col, n);
    3159         794 :       n->freeze_space();
    3160             :     }
    3161             :   }
    3162         917 :   hyphen_list *pp = *p;
    3163         917 :   *p = (*p)->next;
    3164         917 :   delete pp;
    3165         917 :   return n;
    3166             : }
    3167             : 
    3168         917 : hyphen_list *break_char_node::get_hyphen_list(hyphen_list *tail, int *)
    3169             : {
    3170         917 :   return new hyphen_list(0, tail);
    3171             : }
    3172             : 
    3173         917 : hyphenation_type break_char_node::get_hyphenation_type()
    3174             : {
    3175         917 :   return HYPHENATION_PERMITTED;
    3176             : }
    3177             : 
    3178          10 : void break_char_node::ascii_print(ascii_output_file *ascii)
    3179             : {
    3180          10 :   nodes->ascii_print(ascii);
    3181          10 : }
    3182             : 
    3183           0 : bool break_char_node::overlaps_vertically()
    3184             : {
    3185           0 :   return nodes->overlaps_vertically();
    3186             : }
    3187             : 
    3188           0 : bool break_char_node::overlaps_horizontally()
    3189             : {
    3190           0 :   return nodes->overlaps_horizontally();
    3191             : }
    3192             : 
    3193           0 : units break_char_node::size()
    3194             : {
    3195           0 :   return nodes->size();
    3196             : }
    3197             : 
    3198           6 : tfont *break_char_node::get_tfont()
    3199             : {
    3200           6 :   return nodes->get_tfont();
    3201             : }
    3202             : 
    3203          65 : node *extra_size_node::copy()
    3204             : {
    3205          65 :   return new extra_size_node(n, state, div_nest_level);
    3206             : }
    3207             : 
    3208          65 : extra_size_node::extra_size_node(vunits i, statem *s, int divlevel)
    3209          65 : : node(0 /* nullptr */, s, divlevel), n(i)
    3210             : {
    3211          65 : }
    3212             : 
    3213         192 : extra_size_node::extra_size_node(vunits i)
    3214         192 : : n(i)
    3215             : {
    3216         192 : }
    3217             : 
    3218           0 : void extra_size_node::dump_properties()
    3219             : {
    3220           0 :   node::dump_properties();
    3221           0 :   fprintf(stderr, ", \"vunits\": %d", n.to_units());
    3222           0 :   fflush(stderr);
    3223           0 : }
    3224             : 
    3225     3295228 : node *vertical_size_node::copy()
    3226             : {
    3227     3295228 :   return new vertical_size_node(n, state, div_nest_level);
    3228             : }
    3229             : 
    3230     3295228 : vertical_size_node::vertical_size_node(vunits i, statem *s,
    3231     3295228 :                                        int divlevel)
    3232     3295228 : : node(0 /* nullptr */, s, divlevel), n(i)
    3233             : {
    3234     3295228 : }
    3235             : 
    3236      631832 : vertical_size_node::vertical_size_node(vunits i)
    3237      631832 : : n(i)
    3238             : {
    3239      631832 : }
    3240             : 
    3241           0 : void vertical_size_node::dump_properties()
    3242             : {
    3243           0 :   node::dump_properties();
    3244           0 :   fprintf(stderr, ", \"vunits\": %d", n.to_units());
    3245           0 :   fflush(stderr);
    3246           0 : }
    3247             : 
    3248           0 : void hmotion_node::dump_properties()
    3249             : {
    3250           0 :   node::dump_properties();
    3251           0 :   fprintf(stderr, ", \"hunits\": %d", n.to_units());
    3252           0 :   fprintf(stderr, ", \"was_tab\": %s", was_tab ? "true" : "false");
    3253           0 :   fprintf(stderr, ", \"unformat\": %s", unformat ? "true" : "false");
    3254           0 :   fputs(", \"terminal_color\": ", stderr);
    3255           0 :   col->nm.json_dump();
    3256           0 :   fflush(stderr);
    3257           0 : }
    3258             : 
    3259     1835120 : node *hmotion_node::copy()
    3260             : {
    3261     5505360 :   return new hmotion_node(n, was_tab, unformat, col, state,
    3262     1835120 :                           div_nest_level);
    3263             : }
    3264             : 
    3265         415 : node *space_char_hmotion_node::copy()
    3266             : {
    3267         415 :   return new space_char_hmotion_node(n, col, state, div_nest_level);
    3268             : }
    3269             : 
    3270      104097 : vmotion_node::vmotion_node(vunits i, color *c)
    3271      104097 : : n(i), col(c)
    3272             : {
    3273      104097 : }
    3274             : 
    3275      220789 : vmotion_node::vmotion_node(vunits i, color *c, statem *s, int divlevel)
    3276      220789 : : node(0 /* nullptr */, s, divlevel), n(i), col(c)
    3277             : {
    3278      220789 : }
    3279             : 
    3280           0 : void vmotion_node::dump_properties()
    3281             : {
    3282           0 :   node::dump_properties();
    3283           0 :   fprintf(stderr, ", \"vunits\": %d", n.to_units());
    3284           0 :   fputs(", \"terminal_color\": ", stderr);
    3285           0 :   col->nm.json_dump();
    3286           0 :   fflush(stderr);
    3287           0 : }
    3288             : 
    3289      220789 : node *vmotion_node::copy()
    3290             : {
    3291      220789 :   return new vmotion_node(n, col, state, div_nest_level);
    3292             : }
    3293             : 
    3294       32474 : node *dummy_node::copy()
    3295             : {
    3296       32474 :   return new dummy_node;
    3297             : }
    3298             : 
    3299      161958 : node *transparent_dummy_node::copy()
    3300             : {
    3301      161958 :   return new transparent_dummy_node;
    3302             : }
    3303             : 
    3304         492 : hline_node::hline_node(hunits i, node *c, node *nxt)
    3305         492 : : container_node(nxt, c), x(i)
    3306             : {
    3307         492 : }
    3308             : 
    3309         614 : hline_node::hline_node(hunits i, node *c, statem *s, int divlevel,
    3310         614 :                        node *nxt)
    3311         614 : : container_node(nxt, s, divlevel, c), x(i)
    3312             : {
    3313         614 : }
    3314             : 
    3315           0 : void hline_node::dump_properties()
    3316             : {
    3317           0 :   node::dump_properties();
    3318           0 :   fprintf(stderr, ", \"hunits\": %d", x.to_units());
    3319           0 :   fflush(stderr);
    3320           0 : }
    3321             : 
    3322         614 : node *hline_node::copy()
    3323             : {
    3324         614 :   return new hline_node(x, (nodes != 0 /* nullptr */) ? nodes->copy()
    3325             :                                                       : 0 /* nullptr */,
    3326         614 :                         state, div_nest_level);
    3327             : }
    3328             : 
    3329         651 : hunits hline_node::width()
    3330             : {
    3331         651 :   return x < H0 ? H0 : x;
    3332             : }
    3333             : 
    3334           1 : vline_node::vline_node(vunits i, node *c, node *nxt)
    3335           1 : : container_node(nxt, c), x(i)
    3336             : {
    3337           1 : }
    3338             : 
    3339           2 : vline_node::vline_node(vunits i, node *c, statem *s,
    3340           2 :                        int divlevel, node *nxt)
    3341           2 : : container_node(nxt, s, divlevel, c), x(i)
    3342             : {
    3343           2 : }
    3344             : 
    3345           0 : void vline_node::dump_properties()
    3346             : {
    3347           0 :   node::dump_properties();
    3348           0 :   fprintf(stderr, ", \"vunits\": %d", x.to_units());
    3349           0 :   fflush(stderr);
    3350           0 : }
    3351             : 
    3352           2 : node *vline_node::copy()
    3353             : {
    3354           2 :   return new vline_node(x, (nodes != 0 /* nullptr */) ? nodes->copy()
    3355             :                                                       : 0 /* nullptr */,
    3356           2 :                         state, div_nest_level);
    3357             : }
    3358             : 
    3359           3 : hunits vline_node::width()
    3360             : {
    3361           3 :   return (0 /* nullptr */ == nodes) ? H0 : nodes->width();
    3362             : }
    3363             : 
    3364         253 : zero_width_node::zero_width_node(node *nd, statem *s, int divlevel)
    3365         253 : : container_node(0 /* nullptr */, s, divlevel, nd)
    3366             : {
    3367         253 : }
    3368             : 
    3369         929 : zero_width_node::zero_width_node(node *nd)
    3370         929 : : container_node(nd)
    3371             : {
    3372         929 : }
    3373             : 
    3374         253 : node *zero_width_node::copy()
    3375             : {
    3376         253 :   return new zero_width_node(copy_node_list(nodes), state,
    3377         253 :                              div_nest_level);
    3378             : }
    3379             : 
    3380          58 : int node_list_character_type(node *p)
    3381             : {
    3382          58 :   int t = 0;
    3383         379 :   for (; p != 0 /* nullptr */; p = p->next)
    3384         321 :     t |= p->character_type();
    3385          58 :   return t;
    3386             : }
    3387             : 
    3388          58 : int zero_width_node::character_type()
    3389             : {
    3390          58 :   return node_list_character_type(nodes);
    3391             : }
    3392             : 
    3393        1378 : void node_list_vertical_extent(node *p, vunits *min, vunits *max)
    3394             : {
    3395        1378 :   *min = V0;
    3396        1378 :   *max = V0;
    3397        1378 :   vunits cur_vpos = V0;
    3398        1378 :   vunits v1, v2;
    3399        5089 :   for (; p != 0 /* nullptr */; p = p->next) {
    3400        3711 :     p->vertical_extent(&v1, &v2);
    3401        3711 :     v1 += cur_vpos;
    3402        3711 :     if (v1 < *min)
    3403         501 :       *min = v1;
    3404        3711 :     v2 += cur_vpos;
    3405        3711 :     if (v2 > *max)
    3406         173 :       *max = v2;
    3407        3711 :     cur_vpos += p->vertical_width();
    3408             :   }
    3409        1378 : }
    3410             : 
    3411         140 : void zero_width_node::vertical_extent(vunits *min, vunits *max)
    3412             : {
    3413         140 :   node_list_vertical_extent(nodes, min, max);
    3414         140 : }
    3415             : 
    3416         110 : overstrike_node::overstrike_node()
    3417         110 : : container_node(0 /* nullptr */), max_width(H0)
    3418             : {
    3419         110 : }
    3420             : 
    3421         319 : overstrike_node::overstrike_node(statem *s, int divlevel)
    3422         319 : : container_node(0 /* nullptr */, s, divlevel), max_width(H0)
    3423             : {
    3424         319 : }
    3425             : 
    3426           0 : void overstrike_node::dump_properties()
    3427             : {
    3428           0 :   node::dump_properties();
    3429           0 :   fprintf(stderr, ", \"max_width\": %d", max_width.to_units());
    3430           0 :   fflush(stderr);
    3431           0 : }
    3432             : 
    3433         319 : node *overstrike_node::copy()
    3434             : {
    3435         319 :   overstrike_node *on = new overstrike_node(state, div_nest_level);
    3436         957 :   for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
    3437         638 :     on->overstrike(tem->copy());
    3438         319 :   return on;
    3439             : }
    3440             : 
    3441         858 : void overstrike_node::overstrike(node *n)
    3442             : {
    3443         858 :   if (0 /* nullptr */ == n)
    3444           0 :     return;
    3445         858 :   hunits w = n->width();
    3446         858 :   if (w > max_width)
    3447         432 :     max_width = w;
    3448             :   node **p;
    3449        1287 :   for (p = &nodes; *p != 0 /* nullptr */; p = &(*p)->next)
    3450             :     ;
    3451         858 :   n->next = 0 /* nullptr */;
    3452         858 :   *p = n;
    3453             : }
    3454             : 
    3455         328 : hunits overstrike_node::width()
    3456             : {
    3457         328 :   return max_width;
    3458             : }
    3459             : 
    3460           1 : bracket_node::bracket_node()
    3461           1 : : container_node(0 /* nullptr */), max_width(H0)
    3462             : {
    3463           1 : }
    3464             : 
    3465           2 : bracket_node::bracket_node(statem *s, int divlevel)
    3466           2 : : container_node(0 /* nullptr */, s, divlevel), max_width(H0)
    3467             : {
    3468           2 : }
    3469             : 
    3470           0 : void bracket_node::dump_properties()
    3471             : {
    3472           0 :   node::dump_properties();
    3473           0 :   fprintf(stderr, ", \"max_width\": %d", max_width.to_units());
    3474           0 :   fflush(stderr);
    3475           0 : }
    3476             : 
    3477           2 : node *bracket_node::copy()
    3478             : {
    3479           2 :   bracket_node *on = new bracket_node(state, div_nest_level);
    3480           2 :   node *last_node = 0 /* nullptr */;
    3481             :   node *tem;
    3482           2 :   if (nodes != 0 /* nullptr */)
    3483           2 :     nodes->last = 0 /* nullptr */;
    3484           8 :   for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
    3485           6 :     if (tem->next)
    3486           4 :       tem->next->last = tem;
    3487           6 :     last_node = tem;
    3488             :   }
    3489           8 :   for (tem = last_node; tem != 0 /* nullptr */; tem = tem->last)
    3490           6 :     on->bracket(tem->copy());
    3491           2 :   return on;
    3492             : }
    3493             : 
    3494           9 : void bracket_node::bracket(node *n)
    3495             : {
    3496           9 :   if (0 /* nullptr */ == n)
    3497           0 :     return;
    3498           9 :   hunits w = n->width();
    3499           9 :   if (w > max_width)
    3500           3 :     max_width = w;
    3501           9 :   n->next = nodes;
    3502           9 :   nodes = n;
    3503             : }
    3504             : 
    3505           3 : hunits bracket_node::width()
    3506             : {
    3507           3 :   return max_width;
    3508             : }
    3509             : 
    3510    10518884 : int node::nspaces()
    3511             : {
    3512    10518884 :   return 0;
    3513             : }
    3514             : 
    3515      742122 : bool node::did_space_merge(hunits, hunits, hunits)
    3516             : {
    3517      742122 :   return false;
    3518             : }
    3519             : 
    3520             : 
    3521      849980 : space_node::space_node(hunits nn, color *c, node *p)
    3522             : : node(p, 0 /* nullptr */, 0), n(nn), set('\0'),
    3523      849980 :   was_escape_colon(false), col(c)
    3524             : {
    3525      849980 : }
    3526             : 
    3527       55717 : space_node::space_node(hunits nn, int s, int flag, color *c, statem *st,
    3528       55717 :                        int divlevel, node *p)
    3529       55717 : : node(p, st, divlevel), n(nn), set(s), was_escape_colon(flag), col(c)
    3530             : {
    3531       55717 : }
    3532             : 
    3533           0 : void space_node::dump_properties()
    3534             : {
    3535           0 :   node::dump_properties();
    3536           0 :   fprintf(stderr, ", \"hunits\": %d", n.to_units());
    3537           0 :   fprintf(stderr, ", \"undiscardable\": %s", set ? "true" : "false");
    3538           0 :   fprintf(stderr, ", \"is hyphenless breakpoint\": %s",
    3539           0 :           was_escape_colon ? "true" : "false");
    3540           0 :   fputs(", \"terminal_color\": ", stderr);
    3541           0 :   col->nm.json_dump();
    3542           0 :   fflush(stderr);
    3543           0 : }
    3544             : 
    3545             : #if 0
    3546             : space_node::~space_node()
    3547             : {
    3548             : }
    3549             : #endif
    3550             : 
    3551        5892 : node *space_node::copy()
    3552             : {
    3553       17676 :   return new space_node(n, set, was_escape_colon, col, state,
    3554        5892 :                         div_nest_level);
    3555             : }
    3556             : 
    3557          80 : bool space_node::causes_tprint()
    3558             : {
    3559          80 :   return false;
    3560             : }
    3561             : 
    3562        4955 : bool space_node::is_tag()
    3563             : {
    3564        4955 :   return false;
    3565             : }
    3566             : 
    3567     1115240 : int space_node::nspaces()
    3568             : {
    3569     1115240 :   return set ? 0 : 1;
    3570             : }
    3571             : 
    3572         102 : bool space_node::did_space_merge(hunits h, hunits, hunits)
    3573             : {
    3574         102 :   n += h;
    3575         102 :   return true;
    3576             : }
    3577             : 
    3578     2511402 : hunits space_node::width()
    3579             : {
    3580     2511402 :   return n;
    3581             : }
    3582             : 
    3583     2033873 : void node::spread_space(int*, hunits*)
    3584             : {
    3585     2033873 : }
    3586             : 
    3587      377601 : void space_node::spread_space(int *n_spaces, hunits *desired_space)
    3588             : {
    3589      377601 :   if (!set) {
    3590      375173 :     assert(*n_spaces > 0);
    3591      375173 :     if (*n_spaces == 1) {
    3592       27867 :       n += *desired_space;
    3593       27867 :       *desired_space = H0;
    3594             :     }
    3595             :     else {
    3596      347306 :       hunits extra = *desired_space / *n_spaces;
    3597      347306 :       *desired_space -= extra;
    3598      347306 :       n += extra;
    3599             :     }
    3600      375173 :     *n_spaces -= 1;
    3601      375173 :     set = true;
    3602             :   }
    3603      377601 : }
    3604             : 
    3605     7735027 : void node::freeze_space()
    3606             : {
    3607     7735027 : }
    3608             : 
    3609      105973 : void space_node::freeze_space()
    3610             : {
    3611      105973 :   set = true;
    3612      105973 : }
    3613             : 
    3614           0 : void node::is_escape_colon()
    3615             : {
    3616           0 : }
    3617             : 
    3618       49441 : void space_node::is_escape_colon()
    3619             : {
    3620       49441 :   was_escape_colon = true;
    3621       49441 : }
    3622             : 
    3623      526272 : diverted_space_node::diverted_space_node(vunits d, statem *s,
    3624      526272 :                                          int divlevel, node *p)
    3625      526272 : : node(p, s, divlevel), n(d)
    3626             : {
    3627      526272 : }
    3628             : 
    3629      526275 : diverted_space_node::diverted_space_node(vunits d, node *p)
    3630      526275 : : node(p), n(d)
    3631             : {
    3632      526275 : }
    3633             : 
    3634           0 : void diverted_space_node::dump_properties()
    3635             : {
    3636           0 :   node::dump_properties();
    3637           0 :   fprintf(stderr, ", \"vunits\": %d", n.to_units());
    3638           0 :   fflush(stderr);
    3639           0 : }
    3640             : 
    3641      526272 : node *diverted_space_node::copy()
    3642             : {
    3643      526272 :   return new diverted_space_node(n, state, div_nest_level);
    3644             : }
    3645             : 
    3646           3 : diverted_copy_file_node::diverted_copy_file_node(symbol s, statem *st,
    3647           3 :                                                  int divlevel, node *p)
    3648           3 : : node(p, st, divlevel), filename(s)
    3649             : {
    3650           3 : }
    3651             : 
    3652           2 : diverted_copy_file_node::diverted_copy_file_node(symbol s, node *p)
    3653           2 : : node(p), filename(s)
    3654             : {
    3655           2 : }
    3656             : 
    3657           0 : void diverted_copy_file_node::dump_properties()
    3658             : {
    3659           0 :   node::dump_properties();
    3660           0 :   fputs(", \"filename\": ", stderr);
    3661           0 :   filename.json_dump();
    3662           0 :   fflush(stderr);
    3663           0 : }
    3664             : 
    3665           3 : node *diverted_copy_file_node::copy()
    3666             : {
    3667           3 :   return new diverted_copy_file_node(filename, state, div_nest_level);
    3668             : }
    3669             : 
    3670       18699 : int node::ends_sentence()
    3671             : {
    3672       18699 :   return 0;
    3673             : }
    3674             : 
    3675       20031 : int kern_pair_node::ends_sentence()
    3676             : {
    3677       20031 :   switch (n2->ends_sentence()) {
    3678       14210 :   case 0:
    3679       14210 :     return 0;
    3680        5785 :   case 1:
    3681        5785 :     return 1;
    3682          36 :   case 2:
    3683          36 :     break;
    3684           0 :   default:
    3685           0 :     assert(0 == "unhandled case of sentence ending status");
    3686             :   }
    3687          36 :   return n1->ends_sentence();
    3688             : }
    3689             : 
    3690      486309 : int node_list_ends_sentence(node *n)
    3691             : {
    3692      486309 :   for (; n != 0 /* nullptr */; n = n->next)
    3693      486245 :     switch (n->ends_sentence()) {
    3694      335193 :     case 0:
    3695      335193 :       return 0;
    3696       81582 :     case 1:
    3697       81582 :       return 1;
    3698       69470 :     case 2:
    3699       69470 :       break;
    3700           0 :     default:
    3701           0 :       assert(0 == "unhandled case of sentence ending status");
    3702             :     }
    3703          64 :   return 2;
    3704             : }
    3705             : 
    3706           0 : int dbreak_node::ends_sentence()
    3707             : {
    3708           0 :   return node_list_ends_sentence(none);
    3709             : }
    3710             : 
    3711           0 : bool node::overlaps_horizontally()
    3712             : {
    3713           0 :   return false;
    3714             : }
    3715             : 
    3716           0 : bool node::overlaps_vertically()
    3717             : {
    3718           0 :   return false;
    3719             : }
    3720             : 
    3721     1051971 : bool node::discardable()
    3722             : {
    3723     1051971 :   return false;
    3724             : }
    3725             : 
    3726       97741 : bool space_node::discardable()
    3727             : {
    3728       97741 :   return !set;
    3729             : }
    3730             : 
    3731      293920 : vunits node::vertical_width()
    3732             : {
    3733      293920 :   return V0;
    3734             : }
    3735             : 
    3736           0 : vunits vline_node::vertical_width()
    3737             : {
    3738           0 :   return x;
    3739             : }
    3740             : 
    3741         464 : vunits vmotion_node::vertical_width()
    3742             : {
    3743         464 :   return n;
    3744             : }
    3745             : 
    3746       45506 : bool node::set_unformat_flag()
    3747             : {
    3748       45506 :   return true;
    3749             : }
    3750             : 
    3751       53242 : int node::character_type()
    3752             : {
    3753       53242 :   return 0;
    3754             : }
    3755             : 
    3756        4565 : hunits node::subscript_correction()
    3757             : {
    3758        4565 :   return H0;
    3759             : }
    3760             : 
    3761        1132 : hunits node::italic_correction()
    3762             : {
    3763        1132 :   return H0;
    3764             : }
    3765             : 
    3766        1649 : hunits node::left_italic_correction()
    3767             : {
    3768        1649 :   return H0;
    3769             : }
    3770             : 
    3771        4822 : hunits node::skew()
    3772             : {
    3773        4822 :   return H0;
    3774             : }
    3775             : 
    3776             : /* vertical_extent methods */
    3777             : 
    3778       52318 : void node::vertical_extent(vunits *min, vunits *max)
    3779             : {
    3780       52318 :   vunits v = vertical_width();
    3781       52318 :   if (v < V0) {
    3782         152 :     *min = v;
    3783         152 :     *max = V0;
    3784             :   }
    3785             :   else {
    3786       52166 :     *max = v;
    3787       52166 :     *min = V0;
    3788             :   }
    3789       52318 : }
    3790             : 
    3791           0 : void vline_node::vertical_extent(vunits *min, vunits *max)
    3792             : {
    3793           0 :   if (0 /* nullptr */ == nodes)
    3794           0 :     node::vertical_extent(min, max);
    3795             :   else {
    3796           0 :     vunits cmin, cmax;
    3797           0 :     nodes->vertical_extent(&cmin, &cmax);
    3798           0 :     vunits h = nodes->size();
    3799           0 :     if (x < V0) {
    3800           0 :       if (-x < h) {
    3801           0 :         *min = x;
    3802           0 :         *max = V0;
    3803             :       }
    3804             :       else {
    3805             :         // we print the first character and then move up, so
    3806           0 :         *max = cmax;
    3807             :         // we print the last character and then move up h
    3808           0 :         *min = cmin + h;
    3809           0 :         if (*min > V0)
    3810           0 :           *min = V0;
    3811           0 :         *min += x;
    3812             :       }
    3813             :     }
    3814             :     else {
    3815           0 :       if (x < h) {
    3816           0 :         *max = x;
    3817           0 :         *min = V0;
    3818             :       }
    3819             :       else {
    3820             :         // we move down by h and then print the first character, so
    3821           0 :         *min = cmin + h;
    3822           0 :         if (*min > V0)
    3823           0 :           *min = V0;
    3824           0 :         *max = x + cmax;
    3825             :       }
    3826             :     }
    3827             :   }
    3828           0 : }
    3829             : 
    3830             : // `ascii_print()` represents a node for `troff -a`
    3831             : 
    3832           1 : static void ascii_print_node_list(ascii_output_file *ascii, node *n)
    3833             : {
    3834           1 :   node *nn = n;
    3835           2 :   while(nn != 0 /* nullptr */) {
    3836           1 :     nn->ascii_print(ascii);
    3837           1 :     nn = nn->next;
    3838             :   }
    3839           1 : }
    3840             : 
    3841          45 : static void ascii_print_reverse_node_list(ascii_output_file *ascii,
    3842             :                                           node *n)
    3843             : {
    3844          45 :   if (0 /* nullptr */ == n)
    3845          10 :     return;
    3846          35 :   ascii_print_reverse_node_list(ascii, n->next);
    3847          35 :   n->ascii_print(ascii);
    3848             : }
    3849             : 
    3850           0 : void bracket_node::ascii_print(ascii_output_file *ascii)
    3851             : {
    3852           0 :   ascii_print_reverse_node_list(ascii, nodes);
    3853           0 : }
    3854             : 
    3855           3 : void dbreak_node::ascii_print(ascii_output_file *ascii)
    3856             : {
    3857           3 :   ascii_print_reverse_node_list(ascii, none);
    3858           3 : }
    3859             : 
    3860          15 : void kern_pair_node::ascii_print(ascii_output_file *ascii)
    3861             : {
    3862          15 :   n1->ascii_print(ascii);
    3863          15 :   n2->ascii_print(ascii);
    3864          15 : }
    3865             : 
    3866         116 : void node::ascii_print(ascii_output_file *)
    3867             : {
    3868         116 : }
    3869             : 
    3870           0 : void overstrike_node::ascii_print(ascii_output_file *ascii)
    3871             : {
    3872           0 :   ascii_print_node_list(ascii, nodes);
    3873           0 : }
    3874             : 
    3875         120 : void space_node::ascii_print(ascii_output_file *ascii)
    3876             : {
    3877         120 :   if (!n.is_zero())
    3878         120 :     ascii->outc(' ');
    3879         120 : }
    3880             : 
    3881          49 : void hmotion_node::ascii_print(ascii_output_file *ascii)
    3882             : {
    3883             :   // this is pretty arbitrary
    3884          49 :   if (n >= points_to_units(2))
    3885          42 :     ascii->outc(' ');
    3886          49 : }
    3887             : 
    3888           2 : void space_char_hmotion_node::ascii_print(ascii_output_file *ascii)
    3889             : {
    3890           2 :   ascii->outc(' ');
    3891           2 : }
    3892             : 
    3893           1 : void zero_width_node::ascii_print(ascii_output_file *out)
    3894             : {
    3895           1 :   ascii_print_node_list(out, nodes);
    3896           1 : }
    3897             : 
    3898             : // `asciify()` extracts the simple character content of a node; this is
    3899             : // a plain text (but not necessarily plain "ASCII") representation
    3900             : // suitable for storage in a groff string or embedding in a device
    3901             : // extension command escape sequence (as for PDF metadata).
    3902             : 
    3903         114 : void glyph_node::asciify(macro *m)
    3904             : {
    3905         114 :   if (!is_output_supressed) {
    3906         109 :     unsigned char c = ci->get_asciify_code();
    3907         109 :     if (c != 0U)
    3908           2 :       m->append(c);
    3909             :     else {
    3910         107 :       c = ci->get_ascii_code();
    3911         107 :       if (c != 0U)
    3912         104 :         m->append(c);
    3913             :       else {
    3914             :         // Also see input.cpp::charinfo::dump().
    3915           3 :         int unicode_mapping = ci->get_unicode_mapping();
    3916           3 :         if (unicode_mapping >= 0) {
    3917             :           // We must write out an escape sequence.  Use the default
    3918             :           // escape character.  TODO: Make `escape_char` global?
    3919             :           //
    3920             :           // First, handle the Basic Latin characters that don't map to
    3921             :           // themselves.
    3922           3 :           switch (unicode_mapping) {
    3923           1 :           case 34:
    3924           1 :             m->append_str("\\[dq]");
    3925           1 :             break;
    3926           0 :           case 39:
    3927           0 :             m->append_str("\\[aq]");
    3928           0 :             break;
    3929           0 :           case 45:
    3930           0 :             m->append_str("\\[-]");
    3931           0 :             break;
    3932           0 :           case 92:
    3933           0 :             m->append_str("\\[rs]");
    3934           0 :             break;
    3935           0 :           case 94:
    3936           0 :             m->append_str("\\[ha]");
    3937           0 :             break;
    3938           0 :           case 96:
    3939           0 :             m->append_str("\\[ga]");
    3940           0 :             break;
    3941           0 :           case 126:
    3942           0 :             m->append_str("\\[ti]");
    3943           0 :             break;
    3944           2 :           default:
    3945           2 :             m->append_str("\\[u");
    3946           2 :             const size_t buflen = sizeof "10FFFF";
    3947             :             char hexbuf[buflen];
    3948           2 :             (void) memset(hexbuf, '\0', buflen);
    3949           2 :             (void) snprintf(hexbuf, buflen, "%.4X", unicode_mapping);
    3950           2 :             m->append_str(hexbuf);
    3951           2 :             m->append(']');
    3952           2 :             break;
    3953             :           }
    3954             :         }
    3955             :         else {
    3956           0 :           error("unable to asciify glyph; charinfo data follows");
    3957             :           // This is garrulous as hell, but by the time we have hold of
    3958             :           // a glyph's charinfo, it no longer has a "name"--it's already
    3959             :           // been looked up in the dictionary.  (Also, multiple names
    3960             :           // can refer to the same charinfo datum.)  And this racket
    3961             :           // beats telling the user nothing at all about the glyph: if
    3962             :           // the character was defined by request (`char` et al.), this
    3963             :           // dump reports the file name and line number of that request.
    3964           0 :           ci->dump();
    3965             :         }
    3966             :       }
    3967             :     }
    3968             :   }
    3969         114 : }
    3970             : 
    3971           1 : void kern_pair_node::asciify(macro *m)
    3972             : {
    3973           1 :   if (!is_output_supressed) {
    3974           1 :     if (n1 != 0 /* nullptr */)
    3975           1 :       n1->asciify(m);
    3976           1 :     if (n2 != 0 /* nullptr */)
    3977           1 :       n2->asciify(m);
    3978             :   }
    3979           1 : }
    3980             : 
    3981           0 : void dbreak_node::asciify(macro *m)
    3982             : {
    3983           0 :   assert(m != 0 /* nullptr */);
    3984           0 :   if (!is_output_supressed) {
    3985           0 :     if (m != 0 /* nullptr */)
    3986           0 :       none->asciify(m);
    3987           0 :     none = 0 /* nullptr */;
    3988             :   }
    3989           0 : }
    3990             : 
    3991           0 : void ligature_node::asciify(macro *m)
    3992             : {
    3993           0 :   assert(n1 != 0 /* nullptr */);
    3994           0 :   assert(n2 != 0 /* nullptr */);
    3995           0 :   if (!is_output_supressed) {
    3996           0 :     if (n1 != 0 /* nullptr */)
    3997           0 :       n1->asciify(m);
    3998           0 :     if (n2 != 0 /* nullptr */)
    3999           0 :       n2->asciify(m);
    4000             :   }
    4001           0 : }
    4002             : 
    4003           0 : void break_char_node::asciify(macro *m)
    4004             : {
    4005           0 :   assert(nodes != 0 /* nullptr */);
    4006           0 :   if (!is_output_supressed && (nodes != 0 /* nullptr */))
    4007           0 :     nodes->asciify(m);
    4008           0 :   nodes = 0 /* nullptr */;
    4009           0 : }
    4010             : 
    4011           0 : void italic_corrected_node::asciify(macro *m)
    4012             : {
    4013           0 :   assert(nodes != 0 /* nullptr */);
    4014           0 :   if (!is_output_supressed && (nodes != 0 /* nullptr */))
    4015           0 :     nodes->asciify(m);
    4016           0 :   nodes = 0 /* nullptr */;
    4017           0 : }
    4018             : 
    4019           0 : void left_italic_corrected_node::asciify(macro *m)
    4020             : {
    4021           0 :   assert(nodes != 0 /* nullptr */);
    4022           0 :   if (!is_output_supressed && (nodes != 0 /* nullptr */))
    4023           0 :     nodes->asciify(m);
    4024           0 :   nodes = 0 /* nullptr */;
    4025           0 : }
    4026             : 
    4027           3 : void hmotion_node::asciify(macro *)
    4028             : {
    4029           3 : }
    4030             : 
    4031         415 : space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
    4032             :                                                  statem *s,
    4033             :                                                  int divlevel,
    4034         415 :                                                  node *nxt)
    4035         415 : : hmotion_node(i, c, s, divlevel, nxt)
    4036             : {
    4037         415 : }
    4038             : 
    4039        5777 : space_char_hmotion_node::space_char_hmotion_node(hunits i, color *c,
    4040        5777 :                                                  node *nxt)
    4041        5777 : : hmotion_node(i, c, 0 /* nullptr */, 0, nxt)
    4042             : {
    4043        5777 : }
    4044             : 
    4045           1 : void space_char_hmotion_node::asciify(macro *m)
    4046             : {
    4047           1 :   if (!is_output_supressed)
    4048           1 :     m->append(' ');
    4049           1 : }
    4050             : 
    4051           1 : void space_node::asciify(macro *)
    4052             : {
    4053           1 : }
    4054             : 
    4055           7 : void word_space_node::asciify(macro *m)
    4056             : {
    4057           7 :   if (!is_output_supressed) {
    4058          14 :     for (width_list *w = orig_width; w != 0 /* nullptr */; w = w->next)
    4059           7 :       m->append(' ');
    4060             :   }
    4061           7 : }
    4062             : 
    4063           1 : void unbreakable_space_node::asciify(macro *m)
    4064             : {
    4065           1 :   if (!is_output_supressed)
    4066           1 :     m->append(' ');
    4067           1 : }
    4068             : 
    4069           5 : void line_start_node::asciify(macro *)
    4070             : {
    4071           5 : }
    4072             : 
    4073          10 : void vertical_size_node::asciify(macro *)
    4074             : {
    4075          10 : }
    4076             : 
    4077           2 : void dummy_node::asciify(macro *)
    4078             : {
    4079           2 : }
    4080             : 
    4081           9 : void transparent_dummy_node::asciify(macro *)
    4082             : {
    4083           9 : }
    4084             : 
    4085           1 : void tag_node::asciify(macro *)
    4086             : {
    4087           1 : }
    4088             : 
    4089           1 : void device_extension_node::asciify(macro *)
    4090             : {
    4091           1 : }
    4092             : 
    4093           1 : void vmotion_node::asciify(macro *)
    4094             : {
    4095           1 : }
    4096             : 
    4097           1 : void bracket_node::asciify(macro *)
    4098             : {
    4099           1 : }
    4100             : 
    4101           1 : void diverted_copy_file_node::asciify(macro *)
    4102             : {
    4103           1 : }
    4104             : 
    4105           1 : void diverted_space_node::asciify(macro *)
    4106             : {
    4107           1 : }
    4108             : 
    4109           1 : void draw_node::asciify(macro *)
    4110             : {
    4111           1 : }
    4112             : 
    4113           1 : void extra_size_node::asciify(macro *)
    4114             : {
    4115           1 : }
    4116             : 
    4117           3 : void hline_node::asciify(macro *)
    4118             : {
    4119           3 : }
    4120             : 
    4121           1 : void hyphen_inhibitor_node::asciify(macro *)
    4122             : {
    4123           1 : }
    4124             : 
    4125           1 : void overstrike_node::asciify(macro *)
    4126             : {
    4127           1 : }
    4128             : 
    4129           2 : void suppress_node::asciify(macro *)
    4130             : {
    4131           2 :   is_output_supressed = (is_on == 0); // it's a three-valued Boolean :-/
    4132           2 : }
    4133             : 
    4134           1 : void vline_node::asciify(macro *)
    4135             : {
    4136           1 : }
    4137             : 
    4138             : // We probably would asciify zero-width nodes as nothing, but they're
    4139             : // used internally to represent some forms of combining character, as
    4140             : // with \[u015E] -> S<ac>.
    4141           1 : void zero_width_node::asciify(macro *m)
    4142             : {
    4143           1 :   assert(nodes != 0 /* nullptr */);
    4144           1 :   if (!is_output_supressed) {
    4145           1 :     node *n = nodes;
    4146           9 :     while (n != 0 /* nullptr */) {
    4147           8 :       n->asciify(m);
    4148           8 :       n = n->next;
    4149             :     }
    4150           1 :     nodes = 0 /* nullptr */;
    4151             :   }
    4152           1 : }
    4153             : 
    4154      188905 : breakpoint *node::get_breakpoints(hunits /* width */, int /* nspaces */,
    4155             :                                   breakpoint *rest, bool /* is_inner */)
    4156             : {
    4157      188905 :   return rest;
    4158             : }
    4159             : 
    4160          13 : int node::nbreaks()
    4161             : {
    4162          13 :   return 0;
    4163             : }
    4164             : 
    4165       55358 : breakpoint *space_node::get_breakpoints(hunits wd, int ns,
    4166             :                                         breakpoint *rest, bool is_inner)
    4167             : {
    4168       55358 :   if (next && next->discardable())
    4169           1 :     return rest;
    4170       55357 :   breakpoint *bp = new breakpoint;
    4171       55357 :   bp->next = rest;
    4172       55357 :   bp->width = wd;
    4173       55357 :   bp->nspaces = ns;
    4174       55357 :   bp->hyphenated = 0;
    4175       55357 :   if (is_inner) {
    4176           0 :     assert(rest != 0);
    4177           0 :     bp->index = rest->index + 1;
    4178           0 :     bp->nd = rest->nd;
    4179             :   }
    4180             :   else {
    4181       55357 :     bp->nd = this;
    4182       55357 :     bp->index = 0;
    4183             :   }
    4184       55357 :   return bp;
    4185             : }
    4186             : 
    4187           0 : int space_node::nbreaks()
    4188             : {
    4189           0 :   if (next && next->discardable())
    4190           0 :     return 0;
    4191             :   else
    4192           0 :     return 1;
    4193             : }
    4194             : 
    4195       37910 : static breakpoint *node_list_get_breakpoints(node *p, hunits *widthp,
    4196             :                                              int ns, breakpoint *rest)
    4197             : {
    4198       37910 :   if (p != 0 /* nullptr */) {
    4199       18955 :     rest = p->get_breakpoints(*widthp,
    4200             :                               ns,
    4201             :                               node_list_get_breakpoints(p->next, widthp,
    4202             :                                                         ns, rest),
    4203       18955 :                               true);
    4204       18955 :     *widthp += p->width();
    4205             :   }
    4206       37910 :   return rest;
    4207             : }
    4208             : 
    4209       18955 : breakpoint *dbreak_node::get_breakpoints(hunits wd, int ns,
    4210             :                                          breakpoint *rest,
    4211             :                                          bool is_inner)
    4212             : {
    4213       18955 :   breakpoint *bp = new breakpoint;
    4214       18955 :   bp->next = rest;
    4215       18955 :   bp->width = wd;
    4216       55688 :   for (node *tem = pre; tem != 0 /* nullptr */; tem = tem->next)
    4217       36733 :     bp->width += tem->width();
    4218       18955 :   bp->nspaces = ns;
    4219       18955 :   bp->hyphenated = 1;
    4220       18955 :   if (is_inner) {
    4221          63 :     assert(rest != 0);
    4222          63 :     bp->index = rest->index + 1;
    4223          63 :     bp->nd = rest->nd;
    4224             :   }
    4225             :   else {
    4226       18892 :     bp->nd = this;
    4227       18892 :     bp->index = 0;
    4228             :   }
    4229       18955 :   return node_list_get_breakpoints(none, &wd, ns, bp);
    4230             : }
    4231             : 
    4232          13 : int dbreak_node::nbreaks()
    4233             : {
    4234          13 :   int i = 1;
    4235          26 :   for (node *tem = none; tem != 0 /* nullptr */; tem = tem->next)
    4236          13 :     i += tem->nbreaks();
    4237          13 :   return i;
    4238             : }
    4239             : 
    4240           0 : void node::split(int /*where*/, node ** /*prep*/, node ** /*postp*/)
    4241             : {
    4242           0 :   assert(0 == "node::split() unimplemented");
    4243             : }
    4244             : 
    4245       24081 : void space_node::split(int where, node **pre, node **post)
    4246             : {
    4247       24081 :   assert(0 == where);
    4248       24081 :   *pre = next;
    4249       24081 :   *post = 0 /* nullptr */;
    4250       24081 :   delete this;
    4251       24081 : }
    4252             : 
    4253          26 : static void node_list_split(node *p, int *wherep,
    4254             :                             node **prep, node **postp)
    4255             : {
    4256          26 :   if (0 /* nullptr */ == p)
    4257          13 :     return;
    4258          13 :   int nb = p->nbreaks();
    4259          13 :   node_list_split(p->next, wherep, prep, postp);
    4260          13 :   if (*wherep < 0) {
    4261           0 :     p->next = *postp;
    4262           0 :     *postp = p;
    4263             :   }
    4264          13 :   else if (*wherep < nb) {
    4265          13 :     p->next = *prep;
    4266          13 :     p->split(*wherep, prep, postp);
    4267             :   }
    4268             :   else {
    4269           0 :     p->next = *prep;
    4270           0 :     *prep = p;
    4271             :   }
    4272          13 :   *wherep -= nb;
    4273             : }
    4274             : 
    4275        6796 : void dbreak_node::split(int where, node **prep, node **postp)
    4276             : {
    4277        6796 :   assert(where >= 0);
    4278        6796 :   if (0 == where) {
    4279        6783 :     *postp = post;
    4280        6783 :     post = 0 /* nullptr */;
    4281        6783 :     if (0 /* nullptr */ == pre)
    4282           0 :       *prep = next;
    4283             :     else {
    4284             :       node *tem;
    4285       13154 :       for (tem = pre; tem->next != 0 /* nullptr */; tem = tem->next)
    4286             :         ;
    4287        6783 :       tem->next = next;
    4288        6783 :       *prep = pre;
    4289             :     }
    4290        6783 :     pre = 0 /* nullptr */;
    4291        6783 :     delete this;
    4292             :   }
    4293             :   else {
    4294          13 :     *prep = next;
    4295          13 :     where -= 1;
    4296          13 :     node_list_split(none, &where, prep, postp);
    4297          13 :     none = 0 /* nullptr */;
    4298          13 :     delete this;
    4299             :   }
    4300        6796 : }
    4301             : 
    4302             : // TODO: Make this member function pure virtual to force consideration
    4303             : // of this question for each node type.
    4304          74 : hyphenation_type node::get_hyphenation_type()
    4305             : {
    4306          74 :   return HYPHENATION_UNNECESSARY;
    4307             : }
    4308             : 
    4309         238 : hyphenation_type dbreak_node::get_hyphenation_type()
    4310             : {
    4311         238 :   return HYPHENATION_INHIBITED;
    4312             : }
    4313             : 
    4314        4082 : hyphenation_type kern_pair_node::get_hyphenation_type()
    4315             : {
    4316        4082 :   return HYPHENATION_PERMITTED;
    4317             : }
    4318             : 
    4319        1959 : hyphenation_type dummy_node::get_hyphenation_type()
    4320             : {
    4321        1959 :   return HYPHENATION_PERMITTED;
    4322             : }
    4323             : 
    4324        1726 : hyphenation_type transparent_dummy_node::get_hyphenation_type()
    4325             : {
    4326        1726 :   return HYPHENATION_PERMITTED;
    4327             : }
    4328             : 
    4329          90 : hyphenation_type hmotion_node::get_hyphenation_type()
    4330             : {
    4331          90 :   return HYPHENATION_PERMITTED;
    4332             : }
    4333             : 
    4334           8 : hyphenation_type space_char_hmotion_node::get_hyphenation_type()
    4335             : {
    4336           8 :   return HYPHENATION_PERMITTED;
    4337             : }
    4338             : 
    4339           0 : hyphenation_type overstrike_node::get_hyphenation_type()
    4340             : {
    4341           0 :   return HYPHENATION_PERMITTED;
    4342             : }
    4343             : 
    4344       90935 : hyphenation_type space_node::get_hyphenation_type()
    4345             : {
    4346       90935 :   if (was_escape_colon)
    4347         322 :     return HYPHENATION_PERMITTED;
    4348       90613 :   return HYPHENATION_UNNECESSARY;
    4349             : }
    4350             : 
    4351         363 : hyphenation_type unbreakable_space_node::get_hyphenation_type()
    4352             : {
    4353         363 :   return HYPHENATION_PERMITTED;
    4354             : }
    4355             : 
    4356     7788494 : bool node::interpret(macro *)
    4357             : {
    4358     7788494 :   return false;
    4359             : }
    4360             : 
    4361       71087 : device_extension_node::device_extension_node(const macro &m, bool b)
    4362             : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), mac(m),
    4363       71087 :   lacks_command_prefix(b)
    4364             : {
    4365       71087 :   font_size fs = curenv->get_font_size();
    4366       71087 :   int char_height = curenv->get_char_height();
    4367       71087 :   int char_slant = curenv->get_char_slant();
    4368       71087 :   int fontno = env_resolve_font(curenv);
    4369       71087 :   tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
    4370             :                                      fontno);
    4371       71087 :   if (curenv->is_composite())
    4372          16 :     tf = tf->get_plain();
    4373       71087 :   gcol = curenv->get_stroke_color();
    4374       71087 :   fcol = curenv->get_fill_color();
    4375       71087 : }
    4376             : 
    4377      133688 : device_extension_node::device_extension_node(const macro &m, tfont *t,
    4378             :                            color *gc, color *fc,
    4379             :                            statem *s, int divlevel,
    4380      133688 :                            bool b)
    4381             : : node(0 /* nullptr */, s, divlevel, true), mac(m), tf(t), gcol(gc),
    4382      133688 :   fcol(fc), lacks_command_prefix(b)
    4383             : {
    4384      133688 : }
    4385             : 
    4386           0 : void device_extension_node::dump_properties()
    4387             : {
    4388           0 :   node::dump_properties();
    4389           0 :   fputs(", \"macro\": ", stderr);
    4390           0 :   mac.json_dump();
    4391           0 :   fputs(", \"tfont\": ", stderr);
    4392           0 :   tf->get_name().json_dump();
    4393           0 :   fputs(", \"stroke_color\": ", stderr);
    4394           0 :   gcol->nm.json_dump();
    4395           0 :   fputs(", \"fill_color\": ", stderr);
    4396           0 :   fcol->nm.json_dump();
    4397           0 :   fflush(stderr);
    4398           0 : }
    4399             : 
    4400           0 : bool device_extension_node::is_same_as(node *n)
    4401             : {
    4402           0 :   return ((mac == static_cast<device_extension_node *>(n)->mac)
    4403           0 :           && (tf == static_cast<device_extension_node *>(n)->tf)
    4404           0 :           && (gcol == static_cast<device_extension_node *>(n)->gcol)
    4405           0 :           && (fcol == static_cast<device_extension_node *>(n)->fcol)
    4406           0 :           && (lacks_command_prefix
    4407             :               == static_cast<device_extension_node *>(n)
    4408           0 :                  ->lacks_command_prefix));
    4409             : }
    4410             : 
    4411           0 : const char *device_extension_node::type()
    4412             : {
    4413           0 :   return "device extension command node";
    4414             : }
    4415             : 
    4416        1618 : int device_extension_node::ends_sentence()
    4417             : {
    4418        1618 :   return 2;
    4419             : }
    4420             : 
    4421           0 : bool device_extension_node::causes_tprint()
    4422             : {
    4423           0 :   return false;
    4424             : }
    4425             : 
    4426         451 : hyphenation_type device_extension_node::get_hyphenation_type()
    4427             : {
    4428         451 :   return HYPHENATION_PERMITTED;
    4429             : }
    4430             : 
    4431       66666 : bool device_extension_node::is_tag()
    4432             : {
    4433       66666 :   return false;
    4434             : }
    4435             : 
    4436      133688 : node *device_extension_node::copy()
    4437             : {
    4438      133688 :   return new device_extension_node(mac, tf, gcol, fcol, state,
    4439             :                                    div_nest_level,
    4440      133688 :                                    lacks_command_prefix);
    4441             : }
    4442             : 
    4443       66977 : void device_extension_node::tprint_start(troff_output_file *out)
    4444             : {
    4445       66977 :   out->start_device_extension(tf, gcol, fcol, lacks_command_prefix);
    4446       66977 : }
    4447             : 
    4448     1516445 : void device_extension_node::tprint_char(troff_output_file *out,
    4449             :                                         unsigned char c)
    4450             : {
    4451     1516445 :   out->write_device_extension_char(c);
    4452     1516445 : }
    4453             : 
    4454       66977 : void device_extension_node::tprint_end(troff_output_file *out)
    4455             : {
    4456       66977 :   out->end_device_extension();
    4457       66977 : }
    4458             : 
    4459           0 : tfont *device_extension_node::get_tfont()
    4460             : {
    4461           0 :   return tf;
    4462             : }
    4463             : 
    4464             : /* suppress_node */
    4465             : 
    4466         370 : suppress_node::suppress_node(int on_or_off, int issue_limits)
    4467         370 : : is_on(on_or_off), emit_limits(issue_limits), filename(0), position(0),
    4468         370 :   image_id(0)
    4469             : {
    4470         370 : }
    4471             : 
    4472         117 : suppress_node::suppress_node(symbol f, char p, int id)
    4473             : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), is_on(2),
    4474         117 :   emit_limits(false), filename(f), position(p), image_id(id)
    4475             : {
    4476         117 : }
    4477             : 
    4478         471 : suppress_node::suppress_node(int issue_limits, int on_or_off,
    4479             :                              symbol f, char p, int id,
    4480         471 :                              statem *s, int divlevel)
    4481             : : node(0 /* nullptr */, s, divlevel), is_on(on_or_off),
    4482         471 :   emit_limits(issue_limits), filename(f), position(p), image_id(id)
    4483             : {
    4484         471 : }
    4485             : 
    4486           0 : void suppress_node::dump_properties()
    4487             : {
    4488           0 :   node::dump_properties();
    4489           0 :   fprintf(stderr, ", \"is_on\": %d", is_on);
    4490           0 :   fprintf(stderr, ", \"emit_limits\": %s",
    4491           0 :           emit_limits ? "true" : "false");
    4492           0 :   if (filename.contents() != 0 /* nullptr */) {
    4493           0 :     fputs(", \"filename\": ", stderr);
    4494           0 :     filename.json_dump();
    4495             :   }
    4496           0 :   fputs(", \"position\": \"", stderr);
    4497           0 :   json_char jc = json_encode_char(position);
    4498             :   // Write out its JSON representation by character by character to
    4499             :   // keep libc string functions from interpreting C escape sequences.
    4500           0 :   for (size_t i = 0; i < jc.len; i++)
    4501           0 :     fputc(jc.buf[i], stderr);
    4502           0 :   fputc('\"', stderr);
    4503           0 :   fprintf(stderr, ", \"image_id\": %d", image_id);
    4504           0 :   fflush(stderr);
    4505           0 : }
    4506             : 
    4507           0 : bool suppress_node::is_same_as(node *n)
    4508             : {
    4509           0 :   return ((is_on == static_cast<suppress_node *>(n)->is_on)
    4510           0 :           && (emit_limits == static_cast<suppress_node *>(n)->emit_limits)
    4511           0 :           && (filename == static_cast<suppress_node *>(n)->filename)
    4512           0 :           && (position == static_cast<suppress_node *>(n)->position)
    4513           0 :           && (image_id == static_cast<suppress_node *>(n)->image_id));
    4514             : }
    4515             : 
    4516           0 : const char *suppress_node::type()
    4517             : {
    4518           0 :   return "suppressed output node";
    4519             : }
    4520             : 
    4521         471 : node *suppress_node::copy()
    4522             : {
    4523        1413 :   return new suppress_node(emit_limits, is_on, filename, position,
    4524         471 :                            image_id, state, div_nest_level);
    4525             : }
    4526             : 
    4527             : /* tag_node */
    4528             : 
    4529           0 : tag_node::tag_node()
    4530           0 : : node(0 /* nullptr */, 0 /* nullptr */, 0, true), delayed(false)
    4531             : {
    4532           0 : }
    4533             : 
    4534       16214 : tag_node::tag_node(string s, int delay)
    4535       16214 : : tag_string(s), delayed(delay)
    4536             : {
    4537       16214 :   is_special = !delay;
    4538       16214 : }
    4539             : 
    4540         108 : tag_node::tag_node(string s, statem *st, int divlevel, int delay)
    4541         108 : : node(0 /* nullptr */, st, divlevel), tag_string(s), delayed(delay)
    4542             : {
    4543         108 :   is_special = !delay;
    4544         108 : }
    4545             : 
    4546           0 : void tag_node::dump_properties()
    4547             : {
    4548           0 :   node::dump_properties();
    4549           0 :   fputs(", \"string\": ", stderr);
    4550           0 :   tag_string.json_dump();
    4551           0 :   fprintf(stderr, ", \"delayed\": %s", delayed ? "true" : "false");
    4552           0 :   fflush(stderr);
    4553           0 : }
    4554             : 
    4555         108 : node *tag_node::copy()
    4556             : {
    4557         108 :   return new tag_node(tag_string, state, div_nest_level, delayed);
    4558             : }
    4559             : 
    4560       11208 : void tag_node::tprint(troff_output_file *out)
    4561             : {
    4562       11208 :   if (delayed)
    4563          50 :     out->add_to_tag_list(tag_string);
    4564             :   else
    4565       11158 :     out->state.add_tag(out->fp, tag_string);
    4566       11208 : }
    4567             : 
    4568           0 : bool tag_node::is_same_as(node *nd)
    4569             : {
    4570           0 :   return ((tag_string == static_cast<tag_node *>(nd)->tag_string)
    4571           0 :           && (delayed == static_cast<tag_node *>(nd)->delayed));
    4572             : }
    4573             : 
    4574           0 : const char *tag_node::type()
    4575             : {
    4576           0 :   return "tag node";
    4577             : }
    4578             : 
    4579         265 : bool tag_node::causes_tprint()
    4580             : {
    4581         265 :   return !delayed;
    4582             : }
    4583             : 
    4584       10957 : bool tag_node::is_tag()
    4585             : {
    4586       10957 :   return !delayed;
    4587             : }
    4588             : 
    4589           6 : int tag_node::ends_sentence()
    4590             : {
    4591           6 :   return 2;
    4592             : }
    4593             : 
    4594             : // Get contents of register `p` as integer.
    4595             : // Used only by suppress_node::tprint().
    4596         354 : static int get_register(const char *p)
    4597             : {
    4598         354 :   assert(p != 0 /* nullptr */);
    4599         354 :   reg *r = static_cast<reg *>(register_dictionary.lookup(p));
    4600         354 :   assert(r != 0 /* nullptr */);
    4601             :   units value;
    4602         354 :   assert(r->get_value(&value));
    4603         354 :   return int(value);
    4604             : }
    4605             : 
    4606             : // Get contents of register `p` as string.
    4607             : // Used only by suppress_node::tprint().
    4608         118 : static const char *get_string(const char *p)
    4609             : {
    4610         118 :   assert(p != 0 /* nullptr */);
    4611         118 :   reg *r = static_cast<reg *>(register_dictionary.lookup(p));
    4612         118 :   assert(r != 0 /* nullptr */);
    4613         118 :   return r->get_string();
    4614             : }
    4615             : 
    4616         174 : void suppress_node::put(troff_output_file *out, const char *s)
    4617             : {
    4618         174 :   int i = 0;
    4619        3374 :   while (s[i] != '\0') {
    4620        3200 :     out->write_device_extension_char(s[i]);
    4621        3200 :     i++;
    4622             :   }
    4623         174 : }
    4624             : 
    4625             : /*
    4626             :  *  We need to remember the start of the image and its name (\O5).  But
    4627             :  *  we won't always need this information; for instance, \O2 is used to
    4628             :  *  produce a bounding box with no associated image or position thereof.
    4629             :  */
    4630             : 
    4631             : static char last_position = 'i';
    4632             : static const char *image_filename = "";
    4633             : static size_t image_filename_len = 0;
    4634             : static int subimage_counter = 0;
    4635             : 
    4636             : /*
    4637             :  *  tprint - if (is_on == 2)
    4638             :  *               remember current position (l, r, c, i) and filename
    4639             :  *           else
    4640             :  *               if (emit_limits)
    4641             :  *                   if (html)
    4642             :  *                      emit image tag
    4643             :  *                   else
    4644             :  *                      emit postscript bounds for image
    4645             :  *               else
    4646             :  *                  if (suppress boolean differs from current state)
    4647             :  *                      alter state
    4648             :  *                  reset registers
    4649             :  *                  record current page
    4650             :  *                  set low water mark.
    4651             :  */
    4652             : 
    4653         485 : void suppress_node::tprint(troff_output_file *out)
    4654             : {
    4655         485 :   int page_number = topdiv->get_page_number();
    4656             :   // Does the node have an associated position and file name?
    4657         485 :   if (is_on == 2) {
    4658             :     // Save them for future bounding box limits.
    4659         117 :     last_position = position;
    4660         117 :     image_filename = strsave(filename.contents());
    4661         117 :     image_filename_len = strlen(image_filename);
    4662             :   }
    4663             :   else { // is_on = 0 or 1
    4664             :     // Now check whether the suppress node requires us to issue limits.
    4665         368 :     if (emit_limits) {
    4666         117 :       const size_t namebuflen = 8192;
    4667         117 :       char name[namebuflen] = { '\0' };
    4668             :       // Jump through a flaming hoop to avoid a "format nonliteral"
    4669             :       // warning from blindly using sprintf...and avoid trouble from
    4670             :       // mischievous image stems.
    4671             :       //
    4672             :       // Keep this format string synced with pre-html:makeFileName().
    4673         117 :       const char format[] = "%d";
    4674         117 :       const size_t format_len = strlen(format);
    4675         117 :       const char *percent_position = strstr(image_filename, format);
    4676         117 :       if (percent_position) {
    4677           0 :         subimage_counter++;
    4678             :         assert(sizeof subimage_counter <= 8);
    4679             :         // A 64-bit signed int produces up to 19 decimal digits.
    4680           0 :         const size_t ndigits = 19;
    4681             :         // Reserve enough for that plus null terminator.
    4682             :         char *subimage_number
    4683           0 :           = static_cast<char *>(malloc(ndigits + 1));
    4684           0 :         if (0 == subimage_number)
    4685           0 :           fatal("memory allocation failure");
    4686             :         // Replace the %d in the filename with this number.
    4687           0 :         size_t enough = image_filename_len + ndigits - format_len;
    4688           0 :         char *new_name = static_cast<char *>(malloc(enough));
    4689           0 :         if (0 == new_name)
    4690           0 :           fatal("memory allocation failure");
    4691           0 :         ptrdiff_t prefix_length = percent_position - image_filename;
    4692           0 :         strncpy(new_name, image_filename, prefix_length);
    4693           0 :         sprintf(subimage_number, "%d", subimage_counter);
    4694           0 :         size_t number_length = strlen(subimage_number);
    4695           0 :         strcpy(new_name + prefix_length, subimage_number);
    4696             :         // Skip over the format in the source string.
    4697           0 :         const char *suffix_src = image_filename + prefix_length
    4698           0 :           + format_len;
    4699           0 :         char *suffix_dst = new_name + prefix_length + number_length;
    4700           0 :         strcpy(suffix_dst, suffix_src);
    4701             :         // Ensure the new string fits with room for a terminal '\0'.
    4702           0 :         const size_t len = strlen(new_name);
    4703           0 :         if (len > (namebuflen - 1))
    4704           0 :           error("constructed file name in suppressed output escape"
    4705             :                 " sequence is too long (>= %1 bytes); skipping image",
    4706           0 :                 int(namebuflen));
    4707             :         else
    4708           0 :           strncpy(name, new_name, (namebuflen - 1));
    4709           0 :         free(new_name);
    4710           0 :         free(subimage_number);
    4711             :       }
    4712             :       else {
    4713         117 :         if (image_filename_len > (namebuflen - 1))
    4714           0 :           error("file name in suppressed output escape sequence is too"
    4715           0 :                 " long (>= %1 bytes); skipping image", int(namebuflen));
    4716             :         else
    4717         117 :           strcpy(name, image_filename);
    4718             :       }
    4719         117 :       if (is_writing_html) {
    4720          58 :         switch (last_position) {
    4721          57 :         case 'c':
    4722          57 :           out->start_device_extension();
    4723          57 :           put(out, "devtag:.centered-image");
    4724          57 :           break;
    4725           0 :         case 'r':
    4726           0 :           out->start_device_extension();
    4727           0 :           put(out, "devtag:.right-image");
    4728           0 :           break;
    4729           1 :         case 'l':
    4730           1 :           out->start_device_extension();
    4731           1 :           put(out, "devtag:.left-image");
    4732           1 :           break;
    4733          58 :         case 'i':
    4734             :           ;
    4735             :         default:
    4736             :           ;
    4737             :         }
    4738          58 :         out->end_device_extension();
    4739          58 :         out->start_device_extension();
    4740          58 :         put(out, "devtag:.auto-image ");
    4741          58 :         put(out, name);
    4742          58 :         out->end_device_extension();
    4743             :       }
    4744             :       else {
    4745             :         // postscript (or other device)
    4746          59 :         if ((suppression_starting_page_number > 0)
    4747          59 :             && (page_number != suppression_starting_page_number))
    4748           0 :           error("suppression limit registers span more than a page;"
    4749           0 :                 " grohtml-info for image %1 will be wrong", image_no);
    4750             :         //if (topdiv->get_page_number()
    4751             :         //    != suppression_starting_page_number)
    4752             :         //  fprintf(stderr, "end of image and topdiv page = %d   and"
    4753             :         //        " suppression_starting_page_number = %d\n",
    4754             :         //        topdiv->get_page_number(),
    4755             :         //        suppression_starting_page_number);
    4756             :         // `name` will contain a "%d" in which the image_no is placed.
    4757         118 :         fprintf(stderr,
    4758             :                 "grohtml-info:page %d  %d  %d  %d  %d  %d  %s  %d  %d"
    4759             :                 "  %s:%s\n",
    4760             :                 topdiv->get_page_number(),
    4761             :                 get_register("opminx"), get_register("opminy"),
    4762             :                 get_register("opmaxx"), get_register("opmaxy"),
    4763             :                 // page offset + line length
    4764          59 :                 get_register(".o") + get_register(".l"),
    4765             :                 name, hresolution, vresolution, get_string(".F"),
    4766             :                 get_string(".c"));
    4767          59 :         fflush(stderr);
    4768             :       }
    4769             :     }
    4770             :     else { // We are not emitting limits.
    4771         251 :       if (is_on) {
    4772         117 :         out->on();
    4773         117 :         reset_output_registers();
    4774             :       }
    4775             :       else
    4776         134 :         out->off();
    4777         251 :       suppression_starting_page_number = page_number;
    4778             :     }
    4779             :   } // is_on
    4780         485 : }
    4781             : 
    4782         234 : bool suppress_node::causes_tprint()
    4783             : {
    4784         234 :   return is_on;
    4785             : }
    4786             : 
    4787         429 : bool suppress_node::is_tag()
    4788             : {
    4789         429 :   return is_on;
    4790             : }
    4791             : 
    4792        1384 : hunits suppress_node::width()
    4793             : {
    4794        1384 :   return H0;
    4795             : }
    4796             : 
    4797             : /* composite_node */
    4798             : 
    4799             : // A composite (glyph) node corresponds to a user-defined GNU troff
    4800             : // character with a macro definition.
    4801             : 
    4802             : // Not derived from `container_node`; implements custom contained node
    4803             : // dumper in dump_node().
    4804             : class composite_node : public charinfo_node {
    4805             :   node *nodes;
    4806             :   tfont *tf;
    4807             : public:
    4808             :   composite_node(node *, charinfo *, tfont *, statem *, int,
    4809             :                  node * = 0 /* nullptr */);
    4810             :   ~composite_node();
    4811             :   node *copy();
    4812             :   hunits width();
    4813             :   node *last_char_node();
    4814             :   units size();
    4815             :   void tprint(troff_output_file *);
    4816             :   hyphenation_type get_hyphenation_type();
    4817             :   void ascii_print(ascii_output_file *);
    4818             :   void asciify(macro *);
    4819             :   hyphen_list *get_hyphen_list(hyphen_list *, int *);
    4820             :   node *add_self(node *, hyphen_list **);
    4821             :   tfont *get_tfont();
    4822             :   bool is_same_as(node *);
    4823             :   const char *type();
    4824             :   bool causes_tprint();
    4825             :   bool is_tag();
    4826             :   void vertical_extent(vunits *, vunits *);
    4827             :   vunits vertical_width();
    4828             :   void dump_properties();
    4829             :   void dump_node();
    4830             : };
    4831             : 
    4832       54443 : composite_node::composite_node(node *p, charinfo *c, tfont *t,
    4833       54443 :                                statem *s, int divlevel, node *x)
    4834       54443 : : charinfo_node(c, s, divlevel, x), nodes(p), tf(t)
    4835             : {
    4836       54443 : }
    4837             : 
    4838      108050 : composite_node::~composite_node()
    4839             : {
    4840       54025 :   delete_node_list(nodes);
    4841      108050 : }
    4842             : 
    4843        3543 : node *composite_node::copy()
    4844             : {
    4845        3543 :   return new composite_node(copy_node_list(nodes), ci, tf, state,
    4846        3543 :                             div_nest_level);
    4847             : }
    4848             : 
    4849       98939 : hunits composite_node::width()
    4850             : {
    4851       98939 :   hunits x;
    4852       98939 :   if (tf->is_constantly_spaced(&x))
    4853           0 :     return x;
    4854       98939 :   x = H0;
    4855      301005 :   for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
    4856      202066 :     x += tem->width();
    4857       98939 :   hunits offset;
    4858       98939 :   if (tf->is_emboldened(&offset))
    4859           0 :     x += offset;
    4860       98939 :   x += tf->get_track_kern();
    4861       98939 :   return x;
    4862             : }
    4863             : 
    4864           0 : node *composite_node::last_char_node()
    4865             : {
    4866           0 :   return this;
    4867             : }
    4868             : 
    4869        1956 : vunits composite_node::vertical_width()
    4870             : {
    4871        1956 :   vunits v = V0;
    4872        6677 :   for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
    4873        4721 :     v += tem->vertical_width();
    4874        1956 :   return v;
    4875             : }
    4876             : 
    4877           0 : units composite_node::size()
    4878             : {
    4879           0 :   return tf->get_size().to_units();
    4880             : }
    4881             : 
    4882         312 : hyphenation_type composite_node::get_hyphenation_type()
    4883             : {
    4884         312 :   return HYPHENATION_PERMITTED;
    4885             : }
    4886             : 
    4887           5 : void composite_node::asciify(macro *m)
    4888             : {
    4889           5 :   if (!is_output_supressed) {
    4890           5 :     unsigned char c = ci->get_asciify_code();
    4891           5 :     if (0U == c)
    4892           5 :       c = ci->get_ascii_code();
    4893           5 :     if (c != 0U)
    4894           0 :       m->append(c);
    4895             :     else
    4896           5 :       m->append(this);
    4897             :   }
    4898           5 : }
    4899             : 
    4900           7 : void composite_node::ascii_print(ascii_output_file *ascii)
    4901             : {
    4902           7 :   ascii_print_reverse_node_list(ascii, nodes);
    4903           7 : }
    4904             : 
    4905         312 : hyphen_list *composite_node::get_hyphen_list(hyphen_list *tail,
    4906             :                                              int *count)
    4907             : {
    4908         312 :   (*count)++;
    4909         312 :   return new hyphen_list(ci->get_hyphenation_code(), tail);
    4910             : }
    4911             : 
    4912         312 : node *composite_node::add_self(node *nn, hyphen_list **p)
    4913             : {
    4914         312 :   assert(ci->get_hyphenation_code() == (*p)->hyphenation_code);
    4915         312 :   next = nn;
    4916         312 :   nn = this;
    4917         312 :   if ((*p)->is_hyphen)
    4918           0 :     nn = nn->add_discretionary_hyphen();
    4919         312 :   hyphen_list *pp = *p;
    4920         312 :   *p = (*p)->next;
    4921         312 :   delete pp;
    4922         312 :   return nn;
    4923             : }
    4924             : 
    4925           0 : tfont *composite_node::get_tfont()
    4926             : {
    4927           0 :   return tf;
    4928             : }
    4929             : 
    4930       54494 : node *reverse_node_list(node *n)
    4931             : {
    4932       54494 :   node *r = 0 /* nullptr */;
    4933      534250 :   while (n != 0 /* nullptr */) {
    4934      479756 :     node *tem = n;
    4935      479756 :     n = n->next;
    4936      479756 :     tem->next = r;
    4937      479756 :     r = tem;
    4938             :   }
    4939       54494 :   return r;
    4940             : }
    4941             : 
    4942        1238 : void composite_node::vertical_extent(vunits *minimum, vunits *maximum)
    4943             : {
    4944        1238 :   nodes = reverse_node_list(nodes);
    4945        1238 :   node_list_vertical_extent(nodes, minimum, maximum);
    4946        1238 :   nodes = reverse_node_list(nodes);
    4947        1238 : }
    4948             : 
    4949      831816 : width_list::width_list(hunits w, hunits s)
    4950      831816 : : width(w), sentence_width(s), next(0 /* nullptr */)
    4951             : {
    4952      831816 : }
    4953             : 
    4954       48427 : width_list::width_list(width_list *w)
    4955       48427 : : width(w->width), sentence_width(w->sentence_width), next(0)
    4956             : {
    4957       48427 : }
    4958             : 
    4959           0 : void width_list::dump()
    4960             : {
    4961           0 :   fputc('[', stderr);
    4962           0 :   bool need_comma = false;
    4963           0 :   fprintf(stderr, "{ \"width\": %d", width.to_units());
    4964           0 :   fprintf(stderr, ", \"sentence_width\": %d }",
    4965             :           sentence_width.to_units());
    4966           0 :   fflush(stderr);
    4967           0 :   width_list *n = this;
    4968           0 :   while (n->next != 0 /* nullptr */) {
    4969           0 :     if (need_comma)
    4970           0 :       fputs(", ", stderr);
    4971           0 :     need_comma = true;
    4972           0 :     n = n->next;
    4973             :   }
    4974           0 :   fputc(']', stderr);
    4975           0 :   fflush(stderr);
    4976           0 : }
    4977             : 
    4978      793998 : word_space_node::word_space_node(hunits d, color *c, width_list *w,
    4979      793998 :                                  node *x)
    4980      793998 : : space_node(d, c, x), orig_width(w), unformat(false)
    4981             : {
    4982      793998 : }
    4983             : 
    4984       49825 : word_space_node::word_space_node(hunits d, int s, color *c,
    4985             :                                  width_list *w, bool flag, statem *st,
    4986       49825 :                                  int divlevel, node *x)
    4987       49825 : : space_node(d, s, 0, c, st, divlevel, x), orig_width(w), unformat(flag)
    4988             : {
    4989       49825 : }
    4990             : 
    4991           0 : void word_space_node::dump_properties()
    4992             : {
    4993           0 :   space_node::dump_properties();
    4994           0 :   if (orig_width != 0 /* nullptr */) {
    4995           0 :     fputs(", \"width_list\": ", stderr);
    4996           0 :     orig_width->dump();
    4997             :   }
    4998           0 :   fprintf(stderr, ", \"unformat\": %s", unformat ? "true" : "false");
    4999           0 :   fflush(stderr);
    5000           0 : }
    5001             : 
    5002     1667116 : word_space_node::~word_space_node()
    5003             : {
    5004      839570 :   width_list *w = orig_width;
    5005     1715516 :   while (w != 0) {
    5006      875946 :     width_list *tmp = w;
    5007      875946 :     w = w->next;
    5008      875946 :     delete tmp;
    5009             :   }
    5010     1667116 : }
    5011             : 
    5012       46175 : node *word_space_node::copy()
    5013             : {
    5014       46175 :   assert(orig_width != 0);
    5015       46175 :   width_list *w_old_curr = orig_width;
    5016       46175 :   width_list *w_new_curr = new width_list(w_old_curr);
    5017       46175 :   width_list *w_new = w_new_curr;
    5018       46175 :   w_old_curr = w_old_curr->next;
    5019       48427 :   while (w_old_curr != 0) {
    5020        2252 :     w_new_curr->next = new width_list(w_old_curr);
    5021        2252 :     w_new_curr = w_new_curr->next;
    5022        2252 :     w_old_curr = w_old_curr->next;
    5023             :   }
    5024      138525 :   return new word_space_node(n, set, col, w_new, unformat, state,
    5025       46175 :                              div_nest_level);
    5026             : }
    5027             : 
    5028        2451 : bool word_space_node::set_unformat_flag()
    5029             : {
    5030        2451 :   unformat = true;
    5031        2451 :   return true;
    5032             : }
    5033             : 
    5034      423391 : void word_space_node::tprint(troff_output_file *out)
    5035             : {
    5036      423391 :   out->fill_color(col);
    5037      423391 :   out->word_marker();
    5038      423391 :   out->right(n);
    5039      423391 : }
    5040             : 
    5041        6220 : bool word_space_node::did_space_merge(hunits h, hunits sw, hunits ssw)
    5042             : {
    5043        6220 :   n += h;
    5044        6220 :   assert(orig_width != 0);
    5045        6220 :   width_list *w = orig_width;
    5046       43796 :   for (; w->next != 0 /* nullptr */; w = w->next)
    5047             :     ;
    5048        6220 :   w->next = new width_list(sw, ssw);
    5049        6220 :   return true;
    5050             : }
    5051             : 
    5052        8431 : unbreakable_space_node::unbreakable_space_node(hunits d, color *c,
    5053        8431 :                                                node *x)
    5054        8431 : : word_space_node(d, c, 0, x)
    5055             : {
    5056        8431 : }
    5057             : 
    5058        3650 : unbreakable_space_node::unbreakable_space_node(hunits d, int s,
    5059             :                                                color *c, statem *st,
    5060             :                                                int divlevel,
    5061        3650 :                                                node *x)
    5062        3650 : : word_space_node(d, s, c, 0, 0, st, divlevel, x)
    5063             : {
    5064        3650 : }
    5065             : 
    5066        3650 : node *unbreakable_space_node::copy()
    5067             : {
    5068        3650 :   return new unbreakable_space_node(n, set, col, state, div_nest_level);
    5069             : }
    5070             : 
    5071         120 : bool unbreakable_space_node::causes_tprint()
    5072             : {
    5073         120 :   return false;
    5074             : }
    5075             : 
    5076        5579 : bool unbreakable_space_node::is_tag()
    5077             : {
    5078        5579 :   return false;
    5079             : }
    5080             : 
    5081         318 : breakpoint *unbreakable_space_node::get_breakpoints(hunits, int,
    5082             :                                                     breakpoint *rest,
    5083             :                                                     bool /* is_inner */)
    5084             : {
    5085         318 :   return rest;
    5086             : }
    5087             : 
    5088           0 : int unbreakable_space_node::nbreaks()
    5089             : {
    5090           0 :   return 0;
    5091             : }
    5092             : 
    5093           0 : void unbreakable_space_node::split(int, node **, node **)
    5094             : {
    5095           0 :   assert(0 == "unbreakable_space_node::split() unimplemented");
    5096             : }
    5097             : 
    5098           4 : bool unbreakable_space_node::did_space_merge(hunits, hunits, hunits)
    5099             : {
    5100           4 :   return false;
    5101             : }
    5102             : 
    5103     3286470 : hvpair::hvpair()
    5104             : {
    5105     3286470 : }
    5106             : 
    5107      230302 : draw_node::draw_node(char c, hvpair *p, int np, font_size s,
    5108      230302 :                      color *gc, color *fc)
    5109      230302 : : npoints(np), sz(s), gcol(gc), fcol(fc), code(c)
    5110             : {
    5111      526976 :   point = new hvpair[npoints];
    5112      526976 :   for (int i = 0; i < npoints; i++)
    5113      296674 :     point[i] = p[i];
    5114      230302 : }
    5115             : 
    5116      527367 : draw_node::draw_node(char c, hvpair *p, int np, font_size s,
    5117      527367 :                      color *gc, color *fc, statem *st, int divlevel)
    5118             : : node(0 /* nullptr */, st, divlevel), npoints(np), sz(s), gcol(gc),
    5119      527367 :   fcol(fc), code(c)
    5120             : {
    5121     1214143 :   point = new hvpair[npoints];
    5122     1214143 :   for (int i = 0; i < npoints; i++)
    5123      686776 :     point[i] = p[i];
    5124      527367 : }
    5125             : 
    5126           0 : void draw_node::dump_properties()
    5127             : {
    5128           0 :   node::dump_properties();
    5129           0 :   fprintf(stderr, ", \"code\": \"%c\"", code);
    5130           0 :   fprintf(stderr, ", \"npoints\": %d", npoints);
    5131           0 :   fprintf(stderr, ", \"font_size\": %d", sz.to_units());
    5132           0 :   fputs(", \"stroke color\": ", stderr);
    5133           0 :   gcol->nm.json_dump();
    5134           0 :   fputs(", \"fill color\": ", stderr);
    5135           0 :   fcol->nm.json_dump();
    5136           0 :   fprintf(stderr, ", \"point\": \"(%d, %d)\"",
    5137           0 :           point->h.to_units(), point->v.to_units());
    5138           0 :   fflush(stderr);
    5139           0 : }
    5140             : 
    5141           0 : bool draw_node::is_same_as(node *n)
    5142             : {
    5143           0 :   draw_node *nd = static_cast<draw_node *>(n);
    5144           0 :   if (code != nd->code || npoints != nd->npoints || sz != nd->sz
    5145           0 :       || gcol != nd->gcol || fcol != nd->fcol)
    5146           0 :     return false;
    5147           0 :   for (int i = 0; i < npoints; i++)
    5148           0 :     if (point[i].h != nd->point[i].h || point[i].v != nd->point[i].v)
    5149           0 :       return false;
    5150           0 :   return true;
    5151             : }
    5152             : 
    5153           0 : const char *draw_node::type()
    5154             : {
    5155           0 :   return "drawing command node";
    5156             : }
    5157             : 
    5158        2249 : bool draw_node::causes_tprint()
    5159             : {
    5160        2249 :   return false;
    5161             : }
    5162             : 
    5163      226478 : bool draw_node::is_tag()
    5164             : {
    5165      226478 :   return false;
    5166             : }
    5167             : 
    5168     1515002 : draw_node::~draw_node()
    5169             : {
    5170      757501 :   if (point)
    5171      757501 :     delete[] point;
    5172     1515002 : }
    5173             : 
    5174      758016 : hunits draw_node::width()
    5175             : {
    5176      758016 :   hunits x = H0;
    5177     1741803 :   for (int i = 0; i < npoints; i++)
    5178      983787 :     x += point[i].h;
    5179      758016 :   return x;
    5180             : }
    5181             : 
    5182        1063 : vunits draw_node::vertical_width()
    5183             : {
    5184        1063 :   if (code == 'e')
    5185           0 :     return V0;
    5186        1063 :   vunits x = V0;
    5187        2126 :   for (int i = 0; i < npoints; i++)
    5188        1063 :     x += point[i].v;
    5189        1063 :   return x;
    5190             : }
    5191             : 
    5192      527367 : node *draw_node::copy()
    5193             : {
    5194     1582101 :   return new draw_node(code, point, npoints, sz, gcol, fcol, state,
    5195      527367 :                        div_nest_level);
    5196             : }
    5197             : 
    5198      231055 : void draw_node::tprint(troff_output_file *out)
    5199             : {
    5200      231055 :   out->draw(code, point, npoints, sz, gcol, fcol);
    5201      231055 : }
    5202             : 
    5203             : /* tprint methods */
    5204             : 
    5205     2608611 : void glyph_node::tprint(troff_output_file *out)
    5206             : {
    5207     2608611 :   tfont *ptf = tf->get_plain();
    5208     2608611 :   if (ptf == tf)
    5209     2605540 :     out->put_char_width(ci, ptf, gcol, fcol, width(), H0);
    5210             :   else {
    5211        3071 :     hunits offset;
    5212        3071 :     bool is_emboldened = tf->is_emboldened(&offset);
    5213        3071 :     hunits w = ptf->get_width(ci);
    5214        3071 :     hunits k = H0;
    5215        3071 :     hunits x;
    5216        3071 :     bool is_constantly_spaced = tf->is_constantly_spaced(&x);
    5217        3071 :     if (is_constantly_spaced) {
    5218           0 :       x -= w;
    5219           0 :       if (is_emboldened)
    5220           0 :         x -= offset;
    5221           0 :       hunits x2 = (x / 2);
    5222           0 :       out->right(x2);
    5223           0 :       k = x - x2;
    5224             :     }
    5225             :     else
    5226        3071 :       k = tf->get_track_kern();
    5227        3071 :     if (is_emboldened) {
    5228           6 :       out->put_char(ci, ptf, gcol, fcol);
    5229           6 :       out->right(offset);
    5230             :     }
    5231        3071 :     out->put_char_width(ci, ptf, gcol, fcol, w, k);
    5232             :   }
    5233     2608611 : }
    5234             : 
    5235         239 : void glyph_node::zero_width_tprint(troff_output_file *out)
    5236             : {
    5237         239 :   tfont *ptf = tf->get_plain();
    5238         239 :   hunits offset;
    5239         239 :   bool is_emboldened = tf->is_emboldened(&offset);
    5240         239 :   hunits x;
    5241         239 :   bool is_constantly_spaced = tf->is_constantly_spaced(&x);
    5242         239 :   if (is_constantly_spaced) {
    5243           0 :     x -= ptf->get_width(ci);
    5244           0 :     if (is_emboldened)
    5245           0 :       x -= offset;
    5246           0 :     x = (x / 2);
    5247           0 :     out->right(x);
    5248             :   }
    5249         239 :   out->put_char(ci, ptf, gcol, fcol);
    5250         239 :   if (is_emboldened) {
    5251           0 :     out->right(offset);
    5252           0 :     out->put_char(ci, ptf, gcol, fcol);
    5253           0 :     out->right(-offset);
    5254             :   }
    5255         239 :   if (is_constantly_spaced)
    5256           0 :     out->right(-x);
    5257         239 : }
    5258             : 
    5259       13894 : void break_char_node::tprint(troff_output_file *t)
    5260             : {
    5261       13894 :   nodes->tprint(t);
    5262       13894 : }
    5263             : 
    5264           0 : void break_char_node::zero_width_tprint(troff_output_file *t)
    5265             : {
    5266           0 :   nodes->zero_width_tprint(t);
    5267           0 : }
    5268             : 
    5269         310 : void hline_node::tprint(troff_output_file *out)
    5270             : {
    5271         310 :   if (x < H0) {
    5272           8 :     out->right(x);
    5273           8 :     x = -x;
    5274             :   }
    5275         310 :   if (0 /* nullptr */ == nodes) {
    5276           0 :     out->right(x);
    5277           0 :     return;
    5278             :   }
    5279         310 :   hunits w = nodes->width();
    5280         310 :   if (w <= H0) {
    5281           0 :     error("horizontal line drawing character must have positive width");
    5282           0 :     out->right(x);
    5283           0 :     return;
    5284             :   }
    5285         310 :   int i = int(x / w);
    5286         310 :   if (0 == i) {
    5287           2 :     hunits xx = x - w;
    5288           2 :     hunits xx2 = (xx / 2);
    5289           2 :     out->right(xx2);
    5290           2 :     if (out->is_on())
    5291           2 :       nodes->tprint(out);
    5292           2 :     out->right(xx - xx2);
    5293             :   }
    5294             :   else {
    5295         308 :     hunits rem = x - (w * i);
    5296         308 :     if (rem > H0) {
    5297         144 :       if (nodes->overlaps_horizontally()) {
    5298          31 :         if (out->is_on())
    5299          31 :           nodes->tprint(out);
    5300          31 :         out->right(rem - w);
    5301             :       }
    5302             :       else
    5303         113 :         out->right(rem);
    5304             :     }
    5305        8494 :     while (--i >= 0)
    5306        8186 :       if (out->is_on())
    5307        8180 :         nodes->tprint(out);
    5308             :   }
    5309             : }
    5310             : 
    5311           0 : void vline_node::tprint(troff_output_file *out)
    5312             : {
    5313           0 :   if (0 /* nullptr */ == nodes) {
    5314           0 :     out->down(x);
    5315           0 :     return;
    5316             :   }
    5317           0 :   vunits h = nodes->size();
    5318           0 :   bool overlaps = nodes->overlaps_vertically();
    5319           0 :   vunits y = x;
    5320           0 :   if (y < V0) {
    5321           0 :     y = -y;
    5322           0 :     int i = y / h;
    5323           0 :     vunits rem = y - i*h;
    5324           0 :     if (0 == i) {
    5325           0 :       out->right(nodes->width());
    5326           0 :       out->down(-rem);
    5327             :     }
    5328             :     else {
    5329           0 :       while (--i > 0) {
    5330           0 :         nodes->zero_width_tprint(out);
    5331           0 :         out->down(-h);
    5332             :       }
    5333           0 :       if (overlaps) {
    5334           0 :         nodes->zero_width_tprint(out);
    5335           0 :         out->down(-rem);
    5336           0 :         if (out->is_on())
    5337           0 :           nodes->tprint(out);
    5338           0 :         out->down(-h);
    5339             :       }
    5340             :       else {
    5341           0 :         if (out->is_on())
    5342           0 :           nodes->tprint(out);
    5343           0 :         out->down(-h - rem);
    5344             :       }
    5345             :     }
    5346             :   }
    5347             :   else {
    5348           0 :     int i = y / h;
    5349           0 :     vunits rem = y - i*h;
    5350           0 :     if (0 == i) {
    5351           0 :       out->down(rem);
    5352           0 :       out->right(nodes->width());
    5353             :     }
    5354             :     else {
    5355           0 :       out->down(h);
    5356           0 :       if (overlaps)
    5357           0 :         nodes->zero_width_tprint(out);
    5358           0 :       out->down(rem);
    5359           0 :       while (--i > 0) {
    5360           0 :         nodes->zero_width_tprint(out);
    5361           0 :         out->down(h);
    5362             :       }
    5363           0 :       if (out->is_on())
    5364           0 :         nodes->tprint(out);
    5365             :     }
    5366             :   }
    5367             : }
    5368             : 
    5369         451 : void zero_width_node::tprint(troff_output_file *out)
    5370             : {
    5371         451 :   if (0 /* nullptr */ == nodes)
    5372           0 :     return;
    5373         451 :   if (0 /* nullptr */ == nodes->next) {
    5374          29 :     nodes->zero_width_tprint(out);
    5375          29 :     return;
    5376             :   }
    5377         422 :   int hpos = out->get_hpos();
    5378         422 :   int vpos = out->get_vpos();
    5379         422 :   node *tem = nodes;
    5380        2658 :   while (tem) {
    5381        2236 :     tem->tprint(out);
    5382        2236 :     tem = tem->next;
    5383             :   }
    5384         422 :   out->moveto(hpos, vpos);
    5385             : }
    5386             : 
    5387         106 : void overstrike_node::tprint(troff_output_file *out)
    5388             : {
    5389         106 :   hunits pos = H0;
    5390         318 :   for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
    5391         212 :     hunits x = (max_width - tem->width()) / 2;
    5392         212 :     out->right(x - pos);
    5393         212 :     pos = x;
    5394         212 :     tem->zero_width_tprint(out);
    5395             :   }
    5396         106 :   out->right(max_width - pos);
    5397         106 : }
    5398             : 
    5399           0 : void bracket_node::tprint(troff_output_file *out)
    5400             : {
    5401           0 :   if (0 /* nullptr */ == nodes)
    5402           0 :     return;
    5403           0 :   int npieces = 0;
    5404             :   node *tem;
    5405           0 :   for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
    5406           0 :     ++npieces;
    5407           0 :   vunits h = nodes->size();
    5408           0 :   vunits totalh = h*npieces;
    5409           0 :   vunits y = (totalh - h) / 2;
    5410           0 :   out->down(y);
    5411           0 :   for (tem = nodes; tem != 0 /* nullptr */; tem = tem->next) {
    5412           0 :     tem->zero_width_tprint(out);
    5413           0 :     out->down(-h);
    5414             :   }
    5415           0 :   out->right(max_width);
    5416           0 :   out->down(totalh - y);
    5417             : }
    5418             : 
    5419     1274706 : void node::tprint(troff_output_file *)
    5420             : {
    5421     1274706 : }
    5422             : 
    5423           2 : void node::zero_width_tprint(troff_output_file *out)
    5424             : {
    5425           2 :   int hpos = out->get_hpos();
    5426           2 :   int vpos = out->get_vpos();
    5427           2 :   tprint(out);
    5428           2 :   out->moveto(hpos, vpos);
    5429           2 : }
    5430             : 
    5431        5035 : void space_node::tprint(troff_output_file *out)
    5432             : {
    5433        5035 :   out->fill_color(col);
    5434        5035 :   out->right(n);
    5435        5035 : }
    5436             : 
    5437      463183 : void hmotion_node::tprint(troff_output_file *out)
    5438             : {
    5439      463183 :   out->fill_color(col);
    5440      463183 :   out->right(n);
    5441      463183 : }
    5442             : 
    5443        4328 : void space_char_hmotion_node::tprint(troff_output_file *out)
    5444             : {
    5445        4328 :   out->fill_color(col);
    5446        4328 :   if (is_writing_html) {
    5447             :     // we emit the space width as a negative glyph index
    5448           0 :     out->flush_tbuf();
    5449           0 :     out->do_motion();
    5450           0 :     out->put('N');
    5451           0 :     out->put(-n.to_units());
    5452           0 :     out->put('\n');
    5453             :   }
    5454        4328 :   out->right(n);
    5455        4328 : }
    5456             : 
    5457      102798 : void vmotion_node::tprint(troff_output_file *out)
    5458             : {
    5459      102798 :   out->fill_color(col);
    5460      102798 :   out->down(n);
    5461      102798 : }
    5462             : 
    5463       57525 : void kern_pair_node::tprint(troff_output_file *out)
    5464             : {
    5465       57525 :   n1->tprint(out);
    5466       57525 :   out->right(amount);
    5467       57525 :   n2->tprint(out);
    5468       57525 : }
    5469             : 
    5470       77120 : static void tprint_reverse_node_list(troff_output_file *out, node *n)
    5471             : {
    5472       77120 :   if (0 /* nullptr */ == n)
    5473       27586 :     return;
    5474       49534 :   tprint_reverse_node_list(out, n->next);
    5475       49534 :   n->tprint(out);
    5476             : }
    5477             : 
    5478       11333 : void dbreak_node::tprint(troff_output_file *out)
    5479             : {
    5480       11333 :   tprint_reverse_node_list(out, none);
    5481       11333 : }
    5482             : 
    5483       16253 : void composite_node::tprint(troff_output_file *out)
    5484             : {
    5485       16253 :   hunits bold_offset;
    5486       16253 :   bool is_emboldened = tf->is_emboldened(&bold_offset);
    5487       16253 :   hunits track_kern = tf->get_track_kern();
    5488       16253 :   hunits constant_space;
    5489       16253 :   bool is_constantly_spaced = tf->is_constantly_spaced(&constant_space);
    5490       16253 :   hunits x = H0;
    5491       16253 :   if (is_constantly_spaced) {
    5492           0 :     x = constant_space;
    5493           0 :     for (node *tem = nodes; tem != 0 /* nullptr */; tem = tem->next)
    5494           0 :       x -= tem->width();
    5495           0 :     if (is_emboldened)
    5496           0 :       x -= bold_offset;
    5497           0 :     hunits x2 = x / 2;
    5498           0 :     out->right(x2);
    5499           0 :     x -= x2;
    5500             :   }
    5501       16253 :   if (is_emboldened) {
    5502           0 :     int hpos = out->get_hpos();
    5503           0 :     int vpos = out->get_vpos();
    5504           0 :     tprint_reverse_node_list(out, nodes);
    5505           0 :     out->moveto(hpos, vpos);
    5506           0 :     out->right(bold_offset);
    5507             :   }
    5508       16253 :   tprint_reverse_node_list(out, nodes);
    5509       16253 :   if (is_constantly_spaced)
    5510           0 :     out->right(x);
    5511             :   else
    5512       16253 :     out->right(track_kern);
    5513       16253 : }
    5514             : 
    5515           0 : void composite_node::dump_properties()
    5516             : {
    5517           0 :   node::dump_properties();
    5518             :   // GNU troff multiplexes the distinction of ordinary vs. special
    5519             :   // characters though the special character code zero.
    5520           0 :   unsigned char c = ci->get_ascii_code();
    5521           0 :   if (c != 0U) {
    5522           0 :     fputs(", \"character\": ", stderr);
    5523             :     // It's not a `string` or `symbol` we can `.json_dump()`, so we have
    5524             :     // to write the quotation marks ourselves.
    5525           0 :     fputc('\"', stderr);
    5526           0 :     json_char jc = json_encode_char(c);
    5527             :     // Write out its JSON representation by character by character to
    5528             :     // keep libc string functions from interpreting C escape sequences.
    5529           0 :     for (size_t i = 0; i < jc.len; i++)
    5530           0 :       fputc(jc.buf[i], stderr);
    5531           0 :     fputc('\"', stderr);
    5532             :   }
    5533             :   else {
    5534           0 :     fputs(", \"special character\": ", stderr);
    5535           0 :     ci->nm.json_dump();
    5536             :   }
    5537           0 :   fflush(stderr);
    5538           0 : }
    5539             : 
    5540           0 : void composite_node::dump_node()
    5541             : {
    5542           0 :   fputc('{', stderr);
    5543           0 :   dump_properties();
    5544           0 :   fputs(", \"contents\": ", stderr);
    5545           0 :   dump_node_list_in_reverse(nodes);
    5546           0 :   fputc('}', stderr);
    5547           0 :   fflush(stderr);
    5548           0 : }
    5549             : 
    5550       50900 : static node *make_composite_node(charinfo *s, environment *env)
    5551             : {
    5552       50900 :   int fontno = env_resolve_font(env);
    5553       50900 :   if (fontno < 0) {
    5554           0 :     error("cannot format composite glyph: no current font");
    5555           0 :     return 0 /* nullptr */;
    5556             :   }
    5557       50900 :   assert((fontno < font_table_size)
    5558             :          && font_table[fontno] != 0 /* nullptr*/);
    5559       50900 :   node *n = charinfo_to_node_list(s, env);
    5560       50900 :   font_size fs = env->get_font_size();
    5561       50900 :   int char_height = env->get_char_height();
    5562       50900 :   int char_slant = env->get_char_slant();
    5563       50900 :   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
    5564             :                                             fontno);
    5565       50900 :   if (env->is_composite())
    5566          36 :     tf = tf->get_plain();
    5567       50900 :   return new composite_node(n, s, tf, 0, 0, 0);
    5568             : }
    5569             : 
    5570    12878501 : static node *make_glyph_node(charinfo *s, environment *env,
    5571             :                              bool want_warnings = true)
    5572             : {
    5573    12878501 :   int fontno = env_resolve_font(env);
    5574    12878501 :   if (fontno < 0) {
    5575           0 :     error("cannot format glyph: no current font");
    5576           0 :     return 0 /* nullptr */;
    5577             :   }
    5578    12878501 :   assert((fontno < font_table_size)
    5579             :          && font_table[fontno] != 0 /* nullptr*/);
    5580    12878501 :   int fn = fontno;
    5581    12878501 :   bool found = font_table[fontno]->contains(s);
    5582    12878501 :   if (!found) {
    5583       20342 :     macro *mac = s->get_macro();
    5584       20342 :     if ((mac != 0 /* nullptr */) && s->is_fallback())
    5585         204 :       return make_composite_node(s, env);
    5586       20138 :     if (s->is_numbered()) {
    5587        8222 :       if (want_warnings)
    5588           0 :         warning(WARN_CHAR, "character code %1 not defined in current"
    5589           0 :                 " font", s->get_number());
    5590        8222 :       return 0 /* nullptr */;
    5591             :     }
    5592       11916 :     special_font_list *sf = font_table[fontno]->sf;
    5593       11938 :     while ((sf != 0 /* nullptr */) && !found) {
    5594          22 :       fn = sf->n;
    5595          22 :       if (font_table[fn])
    5596          22 :         found = font_table[fn]->contains(s);
    5597          22 :       sf = sf->next;
    5598             :     }
    5599       11916 :     if (!found) {
    5600       11898 :       symbol f = font_table[fontno]->get_name();
    5601       11898 :       string gl(f.contents());
    5602       11898 :       gl += ' ';
    5603       11898 :       gl += s->nm.contents();
    5604       11898 :       gl += '\0';
    5605       11898 :       charinfo *ci = lookup_charinfo(symbol(gl.contents()));
    5606       11898 :       if (ci && ci->get_macro())
    5607           0 :         return make_composite_node(ci, env);
    5608             :     }
    5609       11916 :     if (!found) {
    5610       11898 :       sf = global_special_fonts;
    5611       11906 :       while ((sf != 0 /* nullptr */) && !found) {
    5612           8 :         fn = sf->n;
    5613           8 :         if (font_table[fn])
    5614           8 :           found = font_table[fn]->contains(s);
    5615           8 :         sf = sf->next;
    5616             :       }
    5617             :     }
    5618       11916 :     if (!found)
    5619       11897 :       if (mac && s->is_special())
    5620         214 :         return make_composite_node(s, env);
    5621       11702 :     if (!found) {
    5622      144472 :       for (fn = 0; fn < font_table_size; fn++)
    5623      287718 :         if (font_table[fn]
    5624      129302 :             && font_table[fn]->is_special()
    5625      273161 :             && font_table[fn]->contains(s)) {
    5626       11070 :           found = true;
    5627       11070 :           break;
    5628             :         }
    5629             :     }
    5630       11702 :     if (!found) {
    5631         613 :       if (want_warnings && s->first_time_not_found()) {
    5632          28 :         unsigned char input_code = s->get_ascii_code();
    5633          28 :         if (input_code != 0U) {
    5634           0 :           if (csgraph(input_code))
    5635           0 :             warning(WARN_CHAR, "character '%1' not defined",
    5636           0 :                     input_code);
    5637             :           else
    5638           0 :             warning(WARN_CHAR, "character with input code %1 not"
    5639           0 :                     " defined", int(input_code));
    5640             :         }
    5641          28 :         else if (s->nm.contents()) {
    5642          28 :           const char *nm = s->nm.contents();
    5643             :           // If the contents are empty, get_char_for_escape_parameter()
    5644             :           // should already have thrown an error.
    5645          28 :           if (nm[0] != '\0') {
    5646          28 :             const char *backslash = (nm[1] == '\0') ? "\\" : "";
    5647          28 :             warning(WARN_CHAR, "special character '%1%2' not defined",
    5648          56 :                     backslash, nm);
    5649             :           }
    5650             :         }
    5651             :       }
    5652         613 :       return 0 /* nullptr */;
    5653             :     }
    5654             :   }
    5655    12869248 :   font_size fs = env->get_font_size();
    5656    12869248 :   int char_height = env->get_char_height();
    5657    12869248 :   int char_slant = env->get_char_slant();
    5658    12869248 :   tfont *tf = font_table[fontno]->get_tfont(fs, char_height, char_slant,
    5659             :                                             fn);
    5660    12869248 :   if (env->is_composite())
    5661       51357 :     tf = tf->get_plain();
    5662    12869248 :   color *gcol = env->get_stroke_color();
    5663    12869248 :   color *fcol = env->get_fill_color();
    5664             :   return new glyph_node(s, tf, gcol, fcol, 0 /* nullptr */,
    5665    12869248 :                         0 /* nullptr */);
    5666             : }
    5667             : 
    5668        1007 : node *make_node(charinfo *ci, environment *env)
    5669             : {
    5670        1007 :   switch (ci->get_special_translation()) {
    5671           0 :   case charinfo::TRANSLATE_SPACE:
    5672           0 :     return new space_char_hmotion_node(env->get_space_width(),
    5673           0 :                                        env->get_fill_color());
    5674           0 :   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
    5675           0 :     return new unbreakable_space_node(env->get_space_width(),
    5676           0 :                                       env->get_fill_color());
    5677           0 :   case charinfo::TRANSLATE_DUMMY:
    5678           0 :     return new dummy_node;
    5679           0 :   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
    5680           0 :     error("translation to \\%% ignored in this context");
    5681           0 :     break;
    5682             :   }
    5683        1007 :   charinfo *tem = ci->get_translation();
    5684        1007 :   if (tem != 0 /* nullptr */)
    5685           0 :     ci = tem;
    5686        1007 :   macro *mac = ci->get_macro();
    5687        1007 :   if (mac && ci->is_normal())
    5688         347 :     return make_composite_node(ci, env);
    5689             :   else
    5690         660 :     return make_glyph_node(ci, env);
    5691             : }
    5692             : 
    5693       26117 : bool character_exists(charinfo *ci, environment *env)
    5694             : {
    5695       26117 :   if (ci->get_special_translation() != charinfo::TRANSLATE_NONE)
    5696           0 :     return true;
    5697       26117 :   charinfo *tem = ci->get_translation();
    5698       26117 :   if (tem != 0 /* nullptr */)
    5699           0 :     ci = tem;
    5700       26117 :   if (ci->get_macro())
    5701         262 :     return true;
    5702       25855 :   node *nd = make_glyph_node(ci, env, false /* don't want warnings */);
    5703       25855 :   if (nd) {
    5704       17123 :     delete nd;
    5705       17123 :     return true;
    5706             :   }
    5707        8732 :   return false;
    5708             : }
    5709             : 
    5710    12900886 : node *node::add_char(charinfo *ci, environment *env,
    5711             :                      hunits *widthp, int *spacep, node **glyph_comp_np)
    5712             : {
    5713             :   node *res;
    5714    12900886 :   switch (ci->get_special_translation()) {
    5715           0 :   case charinfo::TRANSLATE_SPACE:
    5716           0 :     res = new space_char_hmotion_node(env->get_space_width(),
    5717           0 :                                       env->get_fill_color(), this);
    5718           0 :     *widthp += res->width();
    5719           0 :     return res;
    5720           0 :   case charinfo::TRANSLATE_STRETCHABLE_SPACE:
    5721           0 :     res = new unbreakable_space_node(env->get_space_width(),
    5722           0 :                                      env->get_fill_color(), this);
    5723           0 :     res->freeze_space();
    5724           0 :     *widthp += res->width();
    5725           0 :     *spacep += res->nspaces();
    5726           0 :     return res;
    5727           0 :   case charinfo::TRANSLATE_DUMMY:
    5728           0 :     return new dummy_node(this);
    5729           0 :   case charinfo::TRANSLATE_HYPHEN_INDICATOR:
    5730           0 :     return add_discretionary_hyphen();
    5731             :   }
    5732    12900886 :   charinfo *tem = ci->get_translation();
    5733    12900886 :   if (tem != 0 /* nullptr */)
    5734       15896 :     ci = tem;
    5735    12900886 :   macro *mac = ci->get_macro();
    5736    12900886 :   if (mac && ci->is_normal()) {
    5737       50135 :     res = make_composite_node(ci, env);
    5738       50135 :     if (res) {
    5739       50135 :       res->next = this;
    5740       50135 :       *widthp += res->width();
    5741       50135 :       if (glyph_comp_np)
    5742       50075 :         *glyph_comp_np = res;
    5743             :     }
    5744             :     else {
    5745           0 :       if (glyph_comp_np)
    5746           0 :         *glyph_comp_np = res;
    5747           0 :       return this;
    5748             :     }
    5749             :   }
    5750             :   else {
    5751    12850751 :     node *gn = make_glyph_node(ci, env);
    5752    12850751 :     if (0 /* nullptr */ == gn)
    5753         100 :       return this;
    5754             :     else {
    5755    12850651 :       hunits old_width = width();
    5756    12850651 :       node *p = gn->merge_self(this);
    5757    12850651 :       if (0 /* nullptr */ == p) {
    5758    12341003 :         *widthp += gn->width();
    5759    12341003 :         gn->next = this;
    5760    12341003 :         res = gn;
    5761             :       }
    5762             :       else {
    5763      509648 :         *widthp += p->width() - old_width;
    5764      509648 :         res = p;
    5765             :       }
    5766    12850651 :       if (glyph_comp_np)
    5767    12848822 :         *glyph_comp_np = res;
    5768             :     }
    5769             :   }
    5770    12900786 :   int break_code = 0;
    5771    12900786 :   if (ci->allows_break_before())
    5772           0 :     break_code = ALLOWS_BREAK_BEFORE;
    5773    12900786 :   if (ci->allows_break_after())
    5774      137696 :     break_code |= ALLOWS_BREAK_AFTER;
    5775    12900786 :   if (ci->ignores_surrounding_hyphenation_codes())
    5776           0 :     break_code |= IGNORES_SURROUNDING_HYPHENATION_CODES;
    5777    12900786 :   if (ci->prohibits_break_before())
    5778          14 :     break_code = PROHIBITS_BREAK_BEFORE;
    5779    12900786 :   if (ci->prohibits_break_after())
    5780           0 :     break_code |= PROHIBITS_BREAK_AFTER;
    5781    12900786 :   if (ci->is_interword_space())
    5782          57 :     break_code |= IS_INTERWORD_SPACE;
    5783    12900786 :   if (break_code != 0) {
    5784      137765 :     node *next1 = res->next;
    5785      137765 :     res->next = 0 /* nullptr */;
    5786      275530 :     res = new break_char_node(res, break_code, get_break_code(),
    5787      137765 :                               env->get_fill_color(), next1);
    5788             :   }
    5789    12900786 :   return res;
    5790             : }
    5791             : 
    5792       62913 : static inline int same_node(node *n1, node *n2)
    5793             : {
    5794       62913 :   if (n1 != 0 /* nullptr */) {
    5795       62913 :     if (n2 != 0 /* nullptr */)
    5796       62913 :       return n1->type() == n2->type() && n1->is_same_as(n2);
    5797             :     else
    5798           0 :       return false;
    5799             :   }
    5800             :   else
    5801           0 :     return 0 /* nullptr */ == n2;
    5802             : }
    5803             : 
    5804     2112266 : int same_node_list(node *n1, node *n2)
    5805             : {
    5806     2112266 :   while ((n1 != 0 /* nullptr */) && (n2 != 0 /* nullptr */)) {
    5807     1535220 :     if (n1->type() != n2->type() || !n1->is_same_as(n2))
    5808      802847 :       return 0;
    5809      732373 :     n1 = n1->next;
    5810      732373 :     n2 = n2->next;
    5811             :   }
    5812      577046 :   return !n1 && !n2;
    5813             : }
    5814             : 
    5815           0 : bool extra_size_node::is_same_as(node *nd)
    5816             : {
    5817           0 :   return (n == static_cast<extra_size_node *>(nd)->n);
    5818             : }
    5819             : 
    5820           0 : const char *extra_size_node::type()
    5821             : {
    5822           0 :   return "extra vertical spacing node";
    5823             : }
    5824             : 
    5825           0 : bool extra_size_node::causes_tprint()
    5826             : {
    5827           0 :   return false;
    5828             : }
    5829             : 
    5830         133 : bool extra_size_node::is_tag()
    5831             : {
    5832         133 :   return false;
    5833             : }
    5834             : 
    5835           0 : bool vertical_size_node::is_same_as(node *nd)
    5836             : {
    5837           0 :   return (n == static_cast<vertical_size_node *>(nd)->n);
    5838             : }
    5839             : 
    5840           0 : const char *vertical_size_node::type()
    5841             : {
    5842           0 :   return "vertical spacing node";
    5843             : }
    5844             : 
    5845        4042 : bool vertical_size_node::set_unformat_flag()
    5846             : {
    5847        4042 :   return false;
    5848             : }
    5849             : 
    5850        7646 : bool vertical_size_node::causes_tprint()
    5851             : {
    5852        7646 :   return false;
    5853             : }
    5854             : 
    5855      602762 : bool vertical_size_node::is_tag()
    5856             : {
    5857      602762 :   return false;
    5858             : }
    5859             : 
    5860           0 : bool hmotion_node::is_same_as(node *nd)
    5861             : {
    5862           0 :   return ((n == static_cast<hmotion_node *>(nd)->n)
    5863           0 :           && (col == static_cast<hmotion_node *>(nd)->col));
    5864             : }
    5865             : 
    5866           0 : const char *hmotion_node::type()
    5867             : {
    5868           0 :   return "horizontal motion node";
    5869             : }
    5870             : 
    5871         293 : bool hmotion_node::set_unformat_flag()
    5872             : {
    5873         293 :   unformat = true;
    5874         293 :   return true;
    5875             : }
    5876             : 
    5877        6268 : bool hmotion_node::causes_tprint()
    5878             : {
    5879        6268 :   return false;
    5880             : }
    5881             : 
    5882      455162 : bool hmotion_node::is_tag()
    5883             : {
    5884      455162 :   return false;
    5885             : }
    5886             : 
    5887          90 : node *hmotion_node::add_self(node *nd, hyphen_list **p)
    5888             : {
    5889          90 :   next = nd;
    5890          90 :   hyphen_list *pp = *p;
    5891          90 :   *p = (*p)->next;
    5892          90 :   delete pp;
    5893          90 :   return this;
    5894             : }
    5895             : 
    5896          90 : hyphen_list *hmotion_node::get_hyphen_list(hyphen_list *tail, int *)
    5897             : {
    5898          90 :   return new hyphen_list(0, tail);
    5899             : }
    5900             : 
    5901           0 : bool space_char_hmotion_node::is_same_as(node *nd)
    5902             : {
    5903           0 :   return ((n == static_cast<space_char_hmotion_node *>(nd)->n
    5904           0 :           && col == static_cast<space_char_hmotion_node *>(nd)->col));
    5905             : }
    5906             : 
    5907          21 : const char *space_char_hmotion_node::type()
    5908             : {
    5909          21 :   return "space character horizontal motion node";
    5910             : }
    5911             : 
    5912         518 : bool space_char_hmotion_node::causes_tprint()
    5913             : {
    5914         518 :   return false;
    5915             : }
    5916             : 
    5917        1024 : bool space_char_hmotion_node::is_tag()
    5918             : {
    5919        1024 :   return false;
    5920             : }
    5921             : 
    5922           8 : node *space_char_hmotion_node::add_self(node *nd, hyphen_list **p)
    5923             : {
    5924           8 :   next = nd;
    5925           8 :   hyphen_list *pp = *p;
    5926           8 :   *p = (*p)->next;
    5927           8 :   delete pp;
    5928           8 :   return this;
    5929             : }
    5930             : 
    5931           8 : hyphen_list *space_char_hmotion_node::get_hyphen_list(hyphen_list *tail,
    5932             :                                                       int *)
    5933             : {
    5934           8 :   return new hyphen_list(0, tail);
    5935             : }
    5936             : 
    5937           0 : bool vmotion_node::is_same_as(node *nd)
    5938             : {
    5939           0 :   return ((n == static_cast<vmotion_node *>(nd)->n)
    5940           0 :           && col == static_cast<vmotion_node *>(nd)->col);
    5941             : }
    5942             : 
    5943           7 : const char *vmotion_node::type()
    5944             : {
    5945           7 :   return "vertical motion node";
    5946             : }
    5947             : 
    5948        2545 : bool vmotion_node::causes_tprint()
    5949             : {
    5950        2545 :   return false;
    5951             : }
    5952             : 
    5953       99703 : bool vmotion_node::is_tag()
    5954             : {
    5955       99703 :   return false;
    5956             : }
    5957             : 
    5958           0 : bool hline_node::is_same_as(node *nd)
    5959             : {
    5960           0 :   return ((x == static_cast<hline_node *>(nd)->x)
    5961           0 :           && same_node(nodes, static_cast<hline_node *>(nd)->nodes));
    5962             : }
    5963             : 
    5964           0 : const char *hline_node::type()
    5965             : {
    5966           0 :   return "horizontal rule node";
    5967             : }
    5968             : 
    5969           1 : bool hline_node::causes_tprint()
    5970             : {
    5971           1 :   return false;
    5972             : }
    5973             : 
    5974         303 : bool hline_node::is_tag()
    5975             : {
    5976         303 :   return false;
    5977             : }
    5978             : 
    5979           0 : bool vline_node::is_same_as(node *nd)
    5980             : {
    5981           0 :   return ((x == static_cast<vline_node *>(nd)->x)
    5982           0 :           && same_node(nodes, static_cast<vline_node *>(nd)->nodes));
    5983             : }
    5984             : 
    5985           0 : const char *vline_node::type()
    5986             : {
    5987           0 :   return "vertical rule node";
    5988             : }
    5989             : 
    5990           0 : bool vline_node::causes_tprint()
    5991             : {
    5992           0 :   return false;
    5993             : }
    5994             : 
    5995           0 : bool vline_node::is_tag()
    5996             : {
    5997           0 :   return false;
    5998             : }
    5999             : 
    6000           0 : bool dummy_node::is_same_as(node *)
    6001             : {
    6002           0 :   return true;
    6003             : }
    6004             : 
    6005          87 : const char *dummy_node::type()
    6006             : {
    6007          87 :   return "dummy node";
    6008             : }
    6009             : 
    6010         441 : bool dummy_node::causes_tprint()
    6011             : {
    6012         441 :   return false;
    6013             : }
    6014             : 
    6015       51897 : bool dummy_node::is_tag()
    6016             : {
    6017       51897 :   return false;
    6018             : }
    6019             : 
    6020           0 : bool transparent_dummy_node::is_same_as(node *)
    6021             : {
    6022           0 :   return true;
    6023             : }
    6024             : 
    6025           0 : const char *transparent_dummy_node::type()
    6026             : {
    6027           0 :   return "transparent dummy node";
    6028             : }
    6029             : 
    6030         296 : bool transparent_dummy_node::causes_tprint()
    6031             : {
    6032         296 :   return false;
    6033             : }
    6034             : 
    6035      106890 : bool transparent_dummy_node::is_tag()
    6036             : {
    6037      106890 :   return false;
    6038             : }
    6039             : 
    6040       49217 : int transparent_dummy_node::ends_sentence()
    6041             : {
    6042       49217 :   return 2;
    6043             : }
    6044             : 
    6045           0 : bool zero_width_node::is_same_as(node *nd)
    6046             : {
    6047           0 :   return same_node_list(nodes, ((zero_width_node *)nd)->nodes);
    6048             : }
    6049             : 
    6050           0 : const char *zero_width_node::type()
    6051             : {
    6052           0 :   return "zero-width output node";
    6053             : }
    6054             : 
    6055           7 : bool zero_width_node::causes_tprint()
    6056             : {
    6057           7 :   return false;
    6058             : }
    6059             : 
    6060         370 : bool zero_width_node::is_tag()
    6061             : {
    6062         370 :   return false;
    6063             : }
    6064             : 
    6065           0 : bool italic_corrected_node::is_same_as(node *nd)
    6066             : {
    6067           0 :   return ((x == static_cast<italic_corrected_node *>(nd)->x)
    6068           0 :           && same_node(nodes,
    6069           0 :                static_cast<italic_corrected_node *>(nd)->nodes));
    6070             : }
    6071             : 
    6072           0 : const char *italic_corrected_node::type()
    6073             : {
    6074           0 :   return "italic-corrected node";
    6075             : }
    6076             : 
    6077          99 : bool italic_corrected_node::causes_tprint()
    6078             : {
    6079          99 :   return false;
    6080             : }
    6081             : 
    6082       10369 : bool italic_corrected_node::is_tag()
    6083             : {
    6084       10369 :   return false;
    6085             : }
    6086             : 
    6087       33795 : left_italic_corrected_node::left_italic_corrected_node(node *xx)
    6088       33795 : : container_node(xx)
    6089             : {
    6090       33795 : }
    6091             : 
    6092        6971 : left_italic_corrected_node::left_italic_corrected_node(statem *s,
    6093             :                                                        int divlevel,
    6094        6971 :                                                        node *xx)
    6095        6971 : : container_node(xx, s, divlevel)
    6096             : {
    6097        6971 : }
    6098             : 
    6099           0 : void left_italic_corrected_node::dump_properties()
    6100             : {
    6101           0 :   node::dump_properties();
    6102           0 :   fprintf(stderr, ", \"hunits\": %d", x.to_units());
    6103           0 :   fflush(stderr);
    6104           0 : }
    6105             : 
    6106       51376 : node *left_italic_corrected_node::merge_glyph_node(glyph_node *gn)
    6107             : {
    6108       51376 :   if (0 /* nullptr */ == nodes) {
    6109       31600 :     hunits lic = gn->left_italic_correction();
    6110       31600 :     if (!lic.is_zero()) {
    6111       20635 :       x = lic;
    6112       20635 :       nodes = gn;
    6113       20635 :       return this;
    6114             :     }
    6115             :   }
    6116             :   else {
    6117       19776 :     node *nd = nodes->merge_glyph_node(gn);
    6118       19776 :     if (nd != 0 /* nullptr */) {
    6119        2254 :       nodes = nd;
    6120        2254 :       x = nodes->left_italic_correction();
    6121        2254 :       return this;
    6122             :     }
    6123             :   }
    6124       28487 :   return 0 /* nullptr */;
    6125             : }
    6126             : 
    6127        6346 : node *left_italic_corrected_node::copy()
    6128             : {
    6129             :   left_italic_corrected_node *nd =
    6130        6346 :     new left_italic_corrected_node(state, div_nest_level);
    6131        6346 :   if (nodes != 0 /* nullptr */) {
    6132        3792 :     nd->nodes = nodes->copy();
    6133        3792 :     nd->x = x;
    6134             :   }
    6135        6346 :   return nd;
    6136             : }
    6137             : 
    6138       22186 : void left_italic_corrected_node::tprint(troff_output_file *out)
    6139             : {
    6140       22186 :   if (nodes != 0 /* nullptr */) {
    6141       10235 :     out->right(x);
    6142       10235 :     nodes->tprint(out);
    6143             :   }
    6144       22186 : }
    6145             : 
    6146           0 : const char *left_italic_corrected_node::type()
    6147             : {
    6148           0 :   return "left italic-corrected node";
    6149             : }
    6150             : 
    6151          39 : bool left_italic_corrected_node::causes_tprint()
    6152             : {
    6153          39 :   return false;
    6154             : }
    6155             : 
    6156       20673 : bool left_italic_corrected_node::is_tag()
    6157             : {
    6158       20673 :   return false;
    6159             : }
    6160             : 
    6161           0 : bool left_italic_corrected_node::is_same_as(node *nd)
    6162             : {
    6163           0 :   return ((x == static_cast<left_italic_corrected_node *>(nd)->x)
    6164           0 :           && same_node(nodes,
    6165           0 :                static_cast<left_italic_corrected_node *>(nd)->nodes));
    6166             : }
    6167             : 
    6168          10 : void left_italic_corrected_node::ascii_print(ascii_output_file *out)
    6169             : {
    6170          10 :   if (nodes != 0 /* nullptr */)
    6171          10 :     nodes->ascii_print(out);
    6172          10 : }
    6173             : 
    6174      144671 : hunits left_italic_corrected_node::width()
    6175             : {
    6176      289342 :   return (nodes != 0 /* nullptr */) ? (nodes->width() + x) : H0;
    6177             : }
    6178             : 
    6179         961 : void left_italic_corrected_node::vertical_extent(vunits *minimum,
    6180             :                                                  vunits *maximum)
    6181             : {
    6182         961 :   if (nodes != 0 /* nullptr */)
    6183         470 :     nodes->vertical_extent(minimum, maximum);
    6184             :   else
    6185         491 :     node::vertical_extent(minimum, maximum);
    6186         961 : }
    6187             : 
    6188         190 : hunits left_italic_corrected_node::skew()
    6189             : {
    6190         380 :   return (nodes != 0 /* nullptr */) ? (nodes->skew() + x / 2) : H0;
    6191             : }
    6192             : 
    6193         190 : hunits left_italic_corrected_node::subscript_correction()
    6194             : {
    6195         190 :   return (nodes != 0 /* nullptr */) ? nodes->subscript_correction()
    6196         190 :                                     : H0;
    6197             : }
    6198             : 
    6199        2834 : hunits left_italic_corrected_node::italic_correction()
    6200             : {
    6201        2834 :   return (nodes != 0 /* nullptr */) ? nodes->italic_correction() : H0;
    6202             : }
    6203             : 
    6204        2902 : int left_italic_corrected_node::ends_sentence()
    6205             : {
    6206        2902 :   return (nodes != 0 /* nullptr */) ? nodes->ends_sentence() : 0;
    6207             : }
    6208             : 
    6209           0 : bool left_italic_corrected_node::overlaps_horizontally()
    6210             : {
    6211           0 :   return (nodes != 0 /* nullptr */) ? nodes->overlaps_horizontally()
    6212           0 :                                     : false;
    6213             : }
    6214             : 
    6215           0 : bool left_italic_corrected_node::overlaps_vertically()
    6216             : {
    6217           0 :   return (nodes != 0 /* nullptr */) ? nodes->overlaps_vertically()
    6218           0 :                                     : false;
    6219             : }
    6220             : 
    6221           0 : node *left_italic_corrected_node::last_char_node()
    6222             : {
    6223           0 :   return (nodes != 0 /* nullptr */) ? nodes->last_char_node()
    6224           0 :                                     : 0 /* nullptr */;
    6225             : }
    6226             : 
    6227        2583 : tfont *left_italic_corrected_node::get_tfont()
    6228             : {
    6229        2583 :   return (nodes != 0 /* nullptr */) ? nodes->get_tfont()
    6230        2583 :                                     : 0 /* nullptr */;
    6231             : }
    6232             : 
    6233        1319 : hyphenation_type left_italic_corrected_node::get_hyphenation_type()
    6234             : {
    6235        1319 :   if (nodes != 0 /* nullptr */)
    6236         625 :     return nodes->get_hyphenation_type();
    6237             :   else
    6238         694 :     return HYPHENATION_PERMITTED;
    6239             : }
    6240             : 
    6241        1319 : hyphen_list *left_italic_corrected_node::get_hyphen_list(
    6242             :     hyphen_list *tail, int *count)
    6243             : {
    6244        1319 :   return (nodes != 0 /* nullptr */)
    6245        1319 :           ? nodes->get_hyphen_list(tail, count) : tail;
    6246             : }
    6247             : 
    6248        1319 : node *left_italic_corrected_node::add_self(node *nd, hyphen_list **p)
    6249             : {
    6250        1319 :   if (nodes != 0 /* nullptr */) {
    6251         625 :     nd = new left_italic_corrected_node(state, div_nest_level, nd);
    6252         625 :     nd = nodes->add_self(nd, p);
    6253         625 :     nodes = 0 /* nullptr */;
    6254         625 :     delete this;
    6255         625 :     return nd;
    6256             :   }
    6257             :   else {
    6258         694 :     next = nd;
    6259         694 :     return this;
    6260             :   }
    6261             : }
    6262             : 
    6263         961 : int left_italic_corrected_node::character_type()
    6264             : {
    6265         961 :   return (nodes != 0 /* nullptr */) ? nodes->character_type() : 0;
    6266             : }
    6267             : 
    6268           0 : bool overstrike_node::is_same_as(node *nd)
    6269             : {
    6270           0 :   return same_node_list(nodes, ((overstrike_node *)nd)->nodes);
    6271             : }
    6272             : 
    6273           0 : const char *overstrike_node::type()
    6274             : {
    6275           0 :   return "overstricken node";
    6276             : }
    6277             : 
    6278           0 : bool overstrike_node::causes_tprint()
    6279             : {
    6280           0 :   return false;
    6281             : }
    6282             : 
    6283         105 : bool overstrike_node::is_tag()
    6284             : {
    6285         105 :   return false;
    6286             : }
    6287             : 
    6288           0 : node *overstrike_node::add_self(node *more_nodes, hyphen_list **p)
    6289             : {
    6290           0 :   next = more_nodes;
    6291           0 :   hyphen_list *pp = *p;
    6292           0 :   *p = (*p)->next;
    6293           0 :   delete pp;
    6294           0 :   return this;
    6295             : }
    6296             : 
    6297           0 : hyphen_list *overstrike_node::get_hyphen_list(hyphen_list *tail, int *)
    6298             : {
    6299           0 :   return new hyphen_list(0U, tail);
    6300             : }
    6301             : 
    6302           0 : bool bracket_node::is_same_as(node *nd)
    6303             : {
    6304           0 :   return same_node_list(nodes, ((bracket_node *)nd)->nodes);
    6305             : }
    6306             : 
    6307           0 : const char *bracket_node::type()
    6308             : {
    6309           0 :   return "bracket-building node";
    6310             : }
    6311             : 
    6312           0 : bool bracket_node::causes_tprint()
    6313             : {
    6314           0 :   return false;
    6315             : }
    6316             : 
    6317           0 : bool bracket_node::is_tag()
    6318             : {
    6319           0 :   return false;
    6320             : }
    6321             : 
    6322        6075 : bool composite_node::is_same_as(node *nd)
    6323             : {
    6324        6075 :   return ((ci == static_cast<composite_node *>(nd)->ci)
    6325        6075 :           && same_node(nodes,
    6326        6075 :                        static_cast<composite_node *>(nd)->nodes));
    6327             : }
    6328             : 
    6329       12203 : const char *composite_node::type()
    6330             : {
    6331       12203 :   return "composite node"; // XXX: composite WHAT? (character macro?)
    6332             : }
    6333             : 
    6334          28 : bool composite_node::causes_tprint()
    6335             : {
    6336          28 :   return false;
    6337             : }
    6338             : 
    6339        6169 : bool composite_node::is_tag()
    6340             : {
    6341        6169 :   return false;
    6342             : }
    6343             : 
    6344     1179015 : bool glyph_node::is_same_as(node *nd)
    6345             : {
    6346     1179015 :   return ((ci == static_cast<glyph_node *>(nd)->ci)
    6347      666694 :           && (tf == static_cast<glyph_node *>(nd)->tf)
    6348      666694 :           && (gcol == static_cast<glyph_node *>(nd)->gcol)
    6349     1845709 :           && (fcol == static_cast<glyph_node *>(nd)->fcol));
    6350             : }
    6351             : 
    6352     2435977 : const char *glyph_node::type()
    6353             : {
    6354     2435977 :   return "glyph node";
    6355             : }
    6356             : 
    6357       94645 : bool glyph_node::causes_tprint()
    6358             : {
    6359       94645 :   return false;
    6360             : }
    6361             : 
    6362     2349134 : bool glyph_node::is_tag()
    6363             : {
    6364     2349134 :   return false;
    6365             : }
    6366             : 
    6367           1 : bool ligature_node::is_same_as(node *nd)
    6368             : {
    6369           1 :   return (same_node(n1, ((ligature_node *)nd)->n1)
    6370           1 :           && same_node(n2, ((ligature_node *)nd)->n2)
    6371           2 :           && glyph_node::is_same_as(nd));
    6372             : }
    6373             : 
    6374           2 : const char *ligature_node::type()
    6375             : {
    6376           2 :   return "ligature node";
    6377             : }
    6378             : 
    6379         342 : bool ligature_node::causes_tprint()
    6380             : {
    6381         342 :   return false;
    6382             : }
    6383             : 
    6384        4324 : bool ligature_node::is_tag()
    6385             : {
    6386        4324 :   return false;
    6387             : }
    6388             : 
    6389       19812 : bool kern_pair_node::is_same_as(node *nd)
    6390             : {
    6391       19812 :   return ((amount == static_cast<kern_pair_node *>(nd)->amount)
    6392       19357 :           && same_node(n1, static_cast<kern_pair_node *>(nd)->n1)
    6393       39169 :           && same_node(n2, static_cast<kern_pair_node *>(nd)->n2));
    6394             : }
    6395             : 
    6396      109407 : const char *kern_pair_node::type()
    6397             : {
    6398      109407 :   return "kerned character pair node";
    6399             : }
    6400             : 
    6401        3121 : bool kern_pair_node::causes_tprint()
    6402             : {
    6403        3121 :   return false;
    6404             : }
    6405             : 
    6406       43710 : bool kern_pair_node::is_tag()
    6407             : {
    6408       43710 :   return false;
    6409             : }
    6410             : 
    6411           0 : bool dbreak_node::is_same_as(node *nd)
    6412             : {
    6413           0 :   return (same_node_list(none, ((dbreak_node *)nd)->none)
    6414           0 :           && same_node_list(pre, ((dbreak_node *)nd)->pre)
    6415           0 :           && same_node_list(post, ((dbreak_node *)nd)->post));
    6416             : }
    6417             : 
    6418           0 : const char *dbreak_node::type()
    6419             : {
    6420           0 :   return "discretionary breakpoint node";
    6421             : }
    6422             : 
    6423         215 : bool dbreak_node::causes_tprint()
    6424             : {
    6425         215 :   return false;
    6426             : }
    6427             : 
    6428       11093 : bool dbreak_node::is_tag()
    6429             : {
    6430       11093 :   return false;
    6431             : }
    6432             : 
    6433           0 : void dbreak_node::dump_node()
    6434             : {
    6435           0 :   fputc('{', stderr);
    6436             :   // Flush so that in case something goes wrong with property dumping,
    6437             :   // we know that we traversed to a new node.
    6438           0 :   fflush(stderr);
    6439           0 :   node::dump_properties();
    6440           0 :   if (none != 0 /* nullptr */) {
    6441           0 :     fputs(", \"none\": ", stderr);
    6442           0 :     none->dump_node();
    6443             :   }
    6444           0 :   if (pre != 0 /* nullptr */) {
    6445           0 :     fputs(", \"pre\": ", stderr);
    6446           0 :     pre->dump_node();
    6447             :   }
    6448           0 :   if (post != 0 /* nullptr */) {
    6449           0 :     fputs(", \"post\": ", stderr);
    6450           0 :     post->dump_node();
    6451             :   }
    6452           0 :   fputc('}', stderr);
    6453           0 :   fflush(stderr);
    6454           0 : }
    6455             : 
    6456       18560 : bool break_char_node::is_same_as(node *nd)
    6457             : {
    6458       18560 :   return (break_code == static_cast<break_char_node *>(nd)->break_code)
    6459       18560 :          && (col == static_cast<break_char_node *>(nd)->col)
    6460       37120 :          && (same_node(nodes,
    6461       18560 :                        static_cast<break_char_node *>(nd)->nodes));
    6462             : }
    6463             : 
    6464       39598 : const char *break_char_node::type()
    6465             : {
    6466       39598 :   return "breakpoint node";
    6467             : }
    6468             : 
    6469         369 : bool break_char_node::causes_tprint()
    6470             : {
    6471         369 :   return false;
    6472             : }
    6473             : 
    6474        9472 : bool break_char_node::is_tag()
    6475             : {
    6476        9472 :   return false;
    6477             : }
    6478             : 
    6479        5566 : unsigned char break_char_node::get_break_code()
    6480             : {
    6481        5566 :   return break_code;
    6482             : }
    6483             : 
    6484       66688 : bool line_start_node::is_same_as(node *)
    6485             : {
    6486       66688 :   return true;
    6487             : }
    6488             : 
    6489      136245 : const char *line_start_node::type()
    6490             : {
    6491      136245 :   return "output line start node";
    6492             : }
    6493             : 
    6494        8262 : bool line_start_node::causes_tprint()
    6495             : {
    6496        8262 :   return false;
    6497             : }
    6498             : 
    6499      467990 : bool line_start_node::is_tag()
    6500             : {
    6501      467990 :   return false;
    6502             : }
    6503             : 
    6504          90 : bool space_node::is_same_as(node *nd)
    6505             : {
    6506          90 :   return ((n == static_cast<space_node *>(nd)->n)
    6507          90 :           && (set == static_cast<space_node *>(nd)->set)
    6508         180 :           && (col == static_cast<space_node *>(nd)->col));
    6509             : }
    6510             : 
    6511         384 : const char *space_node::type()
    6512             : {
    6513         384 :   return "space node";
    6514             : }
    6515             : 
    6516           5 : bool word_space_node::is_same_as(node *nd)
    6517             : {
    6518           5 :   return ((n == static_cast<word_space_node *>(nd)->n)
    6519           5 :           && (set == static_cast<word_space_node *>(nd)->set)
    6520          10 :           && (col == static_cast<word_space_node *>(nd)->col));
    6521             : }
    6522             : 
    6523        2699 : const char *word_space_node::type()
    6524             : {
    6525        2699 :   return "word space node";
    6526             : }
    6527             : 
    6528       18341 : bool word_space_node::causes_tprint()
    6529             : {
    6530       18341 :   return false;
    6531             : }
    6532             : 
    6533      404786 : bool word_space_node::is_tag()
    6534             : {
    6535      404786 :   return false;
    6536             : }
    6537             : 
    6538        5700 : void unbreakable_space_node::tprint(troff_output_file *out)
    6539             : {
    6540        5700 :   out->fill_color(col);
    6541        5700 :   out->word_marker();
    6542        5700 :   if (is_writing_html) {
    6543             :     // we emit the space width as a negative glyph index
    6544         120 :     out->flush_tbuf();
    6545         120 :     out->do_motion();
    6546         120 :     out->put('N');
    6547         120 :     out->put(-n.to_units());
    6548         120 :     out->put('\n');
    6549             :   }
    6550        5700 :   out->right(n);
    6551        5700 : }
    6552             : 
    6553           1 : bool unbreakable_space_node::is_same_as(node *nd)
    6554             : {
    6555           1 :   return n == ((unbreakable_space_node *)nd)->n
    6556           1 :          && set == ((unbreakable_space_node *)nd)->set
    6557           2 :          && col == ((unbreakable_space_node *)nd)->col;
    6558             : }
    6559             : 
    6560           2 : const char *unbreakable_space_node::type()
    6561             : {
    6562           2 :   return "unbreakable space node";
    6563             : }
    6564             : 
    6565         363 : node *unbreakable_space_node::add_self(node *nd, hyphen_list **p)
    6566             : {
    6567         363 :   next = nd;
    6568         363 :   hyphen_list *pp = *p;
    6569         363 :   *p = (*p)->next;
    6570         363 :   delete pp;
    6571         363 :   return this;
    6572             : }
    6573             : 
    6574         363 : hyphen_list *unbreakable_space_node::get_hyphen_list(hyphen_list *tail,
    6575             :                                                      int *)
    6576             : {
    6577         363 :   return new hyphen_list(0, tail);
    6578             : }
    6579             : 
    6580           0 : bool diverted_space_node::is_same_as(node *nd)
    6581             : {
    6582           0 :   return n == ((diverted_space_node *)nd)->n;
    6583             : }
    6584             : 
    6585           0 : const char *diverted_space_node::type()
    6586             : {
    6587           0 :   return "diverted vertical space node";
    6588             : }
    6589             : 
    6590           0 : bool diverted_space_node::causes_tprint()
    6591             : {
    6592           0 :   return false;
    6593             : }
    6594             : 
    6595           0 : bool diverted_space_node::is_tag()
    6596             : {
    6597           0 :   return false;
    6598             : }
    6599             : 
    6600           0 : bool diverted_copy_file_node::is_same_as(node *nd)
    6601             : {
    6602           0 :   return filename == ((diverted_copy_file_node *)nd)->filename;
    6603             : }
    6604             : 
    6605           0 : const char *diverted_copy_file_node::type()
    6606             : {
    6607           0 :   return "diverted file throughput node";
    6608             : }
    6609             : 
    6610           0 : bool diverted_copy_file_node::causes_tprint()
    6611             : {
    6612           0 :   return false;
    6613             : }
    6614             : 
    6615           0 : bool diverted_copy_file_node::is_tag()
    6616             : {
    6617           0 :   return false;
    6618             : }
    6619             : 
    6620             : // Grow the font_table so that its size is > n.
    6621             : 
    6622        2567 : static void grow_font_table(int n)
    6623             : {
    6624        2567 :   assert(n >= font_table_size);
    6625        2567 :   font_info **old_font_table = font_table;
    6626        2567 :   int old_font_table_size = font_table_size;
    6627        2567 :   font_table_size = font_table_size ? ((font_table_size * 3) / 2) : 10;
    6628        2567 :   if (font_table_size <= n)
    6629           0 :     font_table_size = n + 10;
    6630        2567 :   font_table = new font_info *[font_table_size];
    6631        2567 :   if (old_font_table_size)
    6632        1149 :     memcpy(font_table, old_font_table,
    6633        1149 :            old_font_table_size*sizeof(font_info *));
    6634        2567 :   delete[] old_font_table;
    6635       29237 :   for (int i = old_font_table_size; i < font_table_size; i++)
    6636       26670 :     font_table[i] = 0 /* nullptr */;
    6637        2567 : }
    6638             : 
    6639             : dictionary font_translation_dictionary(17);
    6640             : 
    6641      272418 : static symbol get_font_translation(symbol nm)
    6642             : {
    6643      272418 :   void *p = font_translation_dictionary.lookup(nm);
    6644      272418 :   return p ? symbol(static_cast<char *>(p)) : nm;
    6645             : }
    6646             : 
    6647             : dictionary font_dictionary(50);
    6648             : // We store the address of this font in `font_dictionary` to indicate
    6649             : // that we've previously tried to mount the font and failed.
    6650             : font nonexistent_font = font("\0");
    6651             : 
    6652             : // Mount font at position `n` with troff identifier `name` and
    6653             : // description file name `filename` (these are often identical).  If
    6654             : // `check_only`, just look up `name` in the existing list of mounted
    6655             : // fonts.
    6656       21540 : static bool mount_font_no_translate(int n, symbol name, symbol filename,
    6657             :                                     bool check_only = false)
    6658             : {
    6659       21540 :   assert(n >= 0);
    6660       21540 :   font *fm = 0 /* nullptr */;
    6661       21540 :   void *p = font_dictionary.lookup(filename);
    6662       21540 :   if (0 /* nullptr */ == p) {
    6663       19018 :     fm = font::load_font(filename.contents(), check_only);
    6664       19018 :     if (check_only)
    6665        5447 :       return fm != 0 /* nullptr */;
    6666       13571 :     if (0 /* nullptr */ == fm) {
    6667           2 :       (void) font_dictionary.lookup(filename, &nonexistent_font);
    6668           2 :       return false;
    6669             :     }
    6670       13569 :     (void) font_dictionary.lookup(name, fm);
    6671             :   }
    6672        2522 :   else if (&nonexistent_font == p)
    6673           1 :     return false;
    6674             :   else
    6675        2521 :     fm = static_cast<font *>(p);
    6676       16090 :   if (check_only)
    6677        2493 :     return true;
    6678       13597 :   if (n >= font_table_size) {
    6679        2205 :     if ((n - font_table_size) > 1000) {
    6680           0 :       error("requested font mounting position %1 too much larger than"
    6681             :             " first unused position %2", n,
    6682           0 :             next_available_font_position());
    6683           0 :       return false;
    6684             :     }
    6685        2205 :     grow_font_table(n);
    6686             :   }
    6687       11392 :   else if (font_table[n] != 0 /* nullptr */)
    6688           0 :     delete font_table[n];
    6689       13597 :   font_table[n] = new font_info(name, n, filename, fm);
    6690       13597 :   font_family::invalidate_fontno(n);
    6691       13597 :   return true;
    6692             : }
    6693             : 
    6694           0 : void print_font_mounting_position_request()
    6695             : {
    6696           0 :   for (int i = 0; i < font_table_size; i++) {
    6697           0 :     font_info *fi = font_table[i];
    6698           0 :     if (0 /* nullptr */ == fi) // No font mounted here.
    6699           0 :       continue;
    6700           0 :     errprint("%1\t%2", i, fi->get_name().contents());
    6701           0 :     font *f = font_table[i]->get_font();
    6702           0 :     if (f != 0 /* nullptr */) { // It's not an abstract style.
    6703           0 :       errprint("\t%1", f->get_filename());
    6704           0 :       if (f->get_internal_name() != 0 /* nullptr */)
    6705           0 :         errprint("\t%1", f->get_internal_name());
    6706             :     }
    6707           0 :     errprint("\n");
    6708           0 :     fflush(stderr);
    6709             :   }
    6710           0 :   skip_line();
    6711           0 : }
    6712             : 
    6713       13307 : bool mount_font(int n, symbol name, symbol external_name)
    6714             : {
    6715       13307 :   assert(n >= 0);
    6716       13307 :   name = get_font_translation(name);
    6717       13307 :   if (external_name.is_null())
    6718       13278 :     external_name = name;
    6719             :   else
    6720          29 :     external_name = get_font_translation(external_name);
    6721       13307 :   return mount_font_no_translate(n, name, external_name);
    6722             : }
    6723             : 
    6724             : // True for abstract styles and resolved font names.
    6725        7940 : bool is_font_name(symbol fam, symbol name)
    6726             : {
    6727        7940 :   if (is_abstract_style(name))
    6728          24 :     name = concat(fam, name);
    6729        7940 :   return mount_font_no_translate(0, name, name, true /* check only */);
    6730             : }
    6731             : 
    6732        7942 : bool is_abstract_style(symbol s)
    6733             : {
    6734        7942 :   int i = symbol_fontno(s);
    6735        7942 :   return i < 0 ? 0 : font_table[i]->is_style();
    6736             : }
    6737             : 
    6738        3744 : bool mount_style(int n, symbol name)
    6739             : {
    6740        3744 :   assert(n >= 0);
    6741        3744 :   if (n >= font_table_size) {
    6742         362 :     if ((n - font_table_size) > 1000) {
    6743           0 :       error("font position too much larger than first unused position");
    6744           0 :       return false;
    6745             :     }
    6746         362 :     grow_font_table(n);
    6747             :   }
    6748        3382 :   else if (font_table[n] != 0 /* nullptr */)
    6749           0 :     delete font_table[n];
    6750        3744 :   font_table[n] = new font_info(get_font_translation(name), n,
    6751        3744 :                                 NULL_SYMBOL, 0);
    6752        3744 :   font_family::invalidate_fontno(n);
    6753        3744 :   return true;
    6754             : }
    6755             : 
    6756             : // True for valid (not necessarily used) font mounting positions.
    6757       21588 : static bool is_nonnegative_integer(const char *str)
    6758             : {
    6759       21588 :   return strspn(str, "0123456789") == strlen(str);
    6760             : }
    6761             : 
    6762       10794 : static void translate_font()
    6763             : {
    6764       10794 :   if (!has_arg()) {
    6765           0 :     warning(WARN_MISSING, "font translation request expects one or two"
    6766             :             " font name arguments");
    6767           0 :     skip_line();
    6768           0 :     return;
    6769             :   }
    6770       10794 :   symbol from = read_identifier(true /* required */);
    6771             :   // has_arg()+read_identifier() should ensure the assertion succeeds.
    6772       10794 :   assert(!from.is_null());
    6773       10794 :   if (is_nonnegative_integer(from.contents())) {
    6774           0 :     error("cannot translate a font mounting position");
    6775           0 :     skip_line();
    6776           0 :     return;
    6777             :   }
    6778       10794 :   symbol to = read_identifier();
    6779       10794 :   if ((!to.is_null()) && is_nonnegative_integer(to.contents())) {
    6780           0 :     error("cannot translate to a font mounting position");
    6781           0 :     skip_line();
    6782           0 :     return;
    6783             :   }
    6784       10794 :   if (to.is_null() || from == to)
    6785        2871 :     font_translation_dictionary.remove(from);
    6786             :   else
    6787        7923 :     (void) font_translation_dictionary.lookup(from,
    6788        7923 :                                               (void *)to.contents());
    6789       10794 :   skip_line();
    6790             : }
    6791             : 
    6792           0 : static void print_font_translation_request()
    6793             : {
    6794           0 :   dictionary_iterator iter(font_translation_dictionary);
    6795           0 :   symbol from, to;
    6796             :   // We must use the nuclear `reinterpret_cast` operator because GNU
    6797             :   // troff's dictionary types use a pre-STL approach to containers.
    6798           0 :   while (iter.get(&from, reinterpret_cast<void **>(&to)))
    6799           0 :     errprint("%1\t%2\n", from.contents(), to.contents());
    6800           0 :   fflush(stderr);
    6801           0 :   skip_line();
    6802           0 :   return;
    6803             : }
    6804             : 
    6805          30 : static void mount_font_at_position()
    6806             : {
    6807          30 :   if (!has_arg()) {
    6808           0 :     warning(WARN_MISSING, "font mounting request expects arguments");
    6809           0 :     skip_line();
    6810           0 :     return;
    6811             :   }
    6812             :   int n;
    6813          30 :   if (read_integer(&n)) {
    6814          30 :     if (n < 0)
    6815           0 :       error("font mounting position %1 is negative", n);
    6816             :     else {
    6817          30 :       symbol internal_name = read_identifier(true /* required */);
    6818          30 :       if (!internal_name.is_null()) {
    6819          30 :         symbol filename = read_long_identifier();
    6820          30 :         if (!mount_font(n, internal_name, filename)) {
    6821           2 :           string msg;
    6822           2 :           if (filename != 0 /* nullptr */)
    6823           2 :             msg += string(" from file '") + filename.contents()
    6824           3 :               + string("'");
    6825           2 :           msg += '\0';
    6826           2 :           error("cannot load font description '%1'%2 for mounting",
    6827           4 :                 internal_name.contents(), msg.contents());
    6828             :         }
    6829             :       }
    6830             :     }
    6831             :   }
    6832          30 :   skip_line();
    6833             : }
    6834             : 
    6835        1452 : font_family::font_family(symbol s)
    6836        1452 : : map_size(10), nm(s)
    6837             : {
    6838        1452 :   map = new int[map_size];
    6839       15972 :   for (int i = 0; i < map_size; i++)
    6840       14520 :     map[i] = FONT_NOT_MOUNTED;
    6841        1452 : }
    6842             : 
    6843           0 : font_family::~font_family()
    6844             : {
    6845           0 :   delete[] map;
    6846           0 : }
    6847             : 
    6848             : // Resolve a requested font mounting position to a mounting position
    6849             : // usable by the output driver.  (Positions 1 through 4 are typically
    6850             : // allocated to styles, and are not usable thus.)  A return value of
    6851             : // `FONT_NOT_MOUNTED` indicates failure.
    6852    14909627 : int font_family::resolve(int mounting_position)
    6853             : {
    6854    14909627 :   assert(mounting_position >= 0);
    6855    14909627 :   int pos = mounting_position;
    6856    14909627 :   assert((pos >= 0) || (FONT_NOT_MOUNTED == pos));
    6857    14909627 :   if (pos < 0)
    6858           0 :     return FONT_NOT_MOUNTED;
    6859    14909627 :   if (pos < map_size && map[pos] >= 0)
    6860    14900700 :     return map[pos];
    6861        8927 :   if (!((pos < font_table_size)
    6862        8927 :         && (font_table[pos] != 0 /* nullptr */)))
    6863           0 :     return FONT_NOT_MOUNTED;
    6864        8927 :   if (pos >= map_size) {
    6865         842 :     int old_map_size = map_size;
    6866         842 :     int *old_map = map;
    6867         842 :     map_size *= 3;
    6868         842 :     map_size /= 2;
    6869         842 :     if (pos >= map_size)
    6870          10 :       map_size = pos + 10;
    6871         842 :     map = new int[map_size];
    6872         842 :     memcpy(map, old_map, old_map_size * sizeof (int));
    6873         842 :     delete[] old_map;
    6874        9130 :     for (int j = old_map_size; j < map_size; j++)
    6875        8288 :       map[j] = FONT_NOT_MOUNTED;
    6876             :   }
    6877        8927 :   if (!(font_table[pos]->is_style()))
    6878        8444 :     return map[pos] = pos;
    6879         483 :   symbol sty = font_table[pos]->get_name();
    6880         483 :   symbol f = concat(nm, sty);
    6881             :   int n;
    6882             :   // Don't use symbol_fontno, because that might return a style and
    6883             :   // because we don't want to translate the name.
    6884       12776 :   for (n = 0; n < font_table_size; n++)
    6885        9728 :     if ((font_table[n] != 0 /* nullptr */) && font_table[n]->is_named(f)
    6886       22211 :         && !font_table[n]->is_style())
    6887         190 :       break;
    6888         483 :   if (n >= font_table_size) {
    6889         293 :     n = next_available_font_position();
    6890         293 :     if (!mount_font_no_translate(n, f, f))
    6891           0 :       return FONT_NOT_MOUNTED;
    6892             :   }
    6893         483 :   return map[pos] = n;
    6894             : }
    6895             : 
    6896             : dictionary family_dictionary(5);
    6897             : 
    6898       47326 : font_family *lookup_family(symbol nm)
    6899             : {
    6900             :   font_family *f
    6901       47326 :     = static_cast<font_family *>(family_dictionary.lookup(nm));
    6902       47326 :   if (0 /* nullptr */ == f) {
    6903        1452 :     f = new font_family(nm);
    6904        1452 :     (void) family_dictionary.lookup(nm, f);
    6905             :   }
    6906       47326 :   return f;
    6907             : }
    6908             : 
    6909       17341 : void font_family::invalidate_fontno(int n)
    6910             : {
    6911       17341 :   assert(n >= 0 && n < font_table_size);
    6912       17341 :   dictionary_iterator iter(family_dictionary);
    6913       17341 :   symbol nam;
    6914             :   font_family *fam;
    6915       26897 :   while (iter.get(&nam, (void **)&fam)) {
    6916        9556 :     int mapsize = fam->map_size;
    6917        9556 :     if (n < mapsize)
    6918        6316 :       fam->map[n] = FONT_NOT_MOUNTED;
    6919      333650 :     for (int i = 0; i < mapsize; i++)
    6920      324094 :       if (fam->map[i] == n)
    6921           0 :         fam->map[i] = FONT_NOT_MOUNTED;
    6922             :   }
    6923       17341 : }
    6924             : 
    6925        2632 : static void associate_style_with_font_position()
    6926             : {
    6927        2632 :   if (!has_arg()) {
    6928           0 :     warning(WARN_MISSING, "abstract style configuration request expects"
    6929             :             " arguments");
    6930           0 :     skip_line();
    6931           0 :     return;
    6932             :   }
    6933             :   int n;
    6934        2632 :   if (read_integer(&n)) {
    6935        2632 :     if (n < 0)
    6936           0 :       error("font mounting position %1 is negative", n);
    6937             :     else {
    6938        2632 :       if (!has_arg())
    6939           0 :         warning(WARN_MISSING, "abstract style configuration request"
    6940             :                 " expects a style name as second argument");
    6941             :       else {
    6942        2632 :         symbol internal_name = read_identifier(true /* required */);
    6943        2632 :         if (!internal_name.is_null())
    6944        2632 :           (void) mount_style(n, internal_name);
    6945             :       }
    6946             :     }
    6947             :   }
    6948        2632 :   skip_line();
    6949             : }
    6950             : 
    6951           0 : static void font_lookup_error(font_lookup_info& finfo,
    6952             :                               const char *msg)
    6953             : {
    6954           0 :   if (finfo.requested_name)
    6955           0 :     error("cannot select font '%1' %2", finfo.requested_name, msg);
    6956           0 :   else if (finfo.position == FONT_NOT_MOUNTED)
    6957           0 :     error("cannot select font %1", msg); // don't report position `-1`
    6958             :   else
    6959           0 :     error("cannot select font at position %1 %2",
    6960           0 :           finfo.requested_position, msg);
    6961           0 : }
    6962             : 
    6963     1668389 : bool is_valid_font_mounting_position(int n)
    6964             : {
    6965             :   return ((n >= 0)
    6966     1668389 :           && (n < font_table_size)
    6967     3336778 :           && (font_table[n] != 0 /* nullptr */));
    6968             : }
    6969             : 
    6970             : // Read the next token and look it up as a font name or position number.
    6971             : // Return lookup success.  Store, in the supplied struct argument, the
    6972             : // requested name or position, and the position actually resolved.
    6973       11015 : static bool read_font_identifier(font_lookup_info *finfo)
    6974             : {
    6975             :   int n;
    6976       11015 :   tok.skip_spaces();
    6977       11015 :   if (tok.is_usable_as_delimiter()) {
    6978       10850 :     symbol s = read_identifier(true /* required */);
    6979       10850 :     finfo->requested_name = const_cast<char *>(s.contents());
    6980       10850 :     if (!s.is_null()) {
    6981       10850 :       n = symbol_fontno(s);
    6982       10850 :       if (n < 0) {
    6983        6532 :         n = next_available_font_position();
    6984        6532 :         if (mount_font(n, s))
    6985        6532 :           finfo->position = n;
    6986             :       }
    6987       10850 :       finfo->position = curenv->get_family()->resolve(n);
    6988             :     }
    6989             :   }
    6990         165 :   else if (read_integer(&n)) {
    6991         165 :     finfo->requested_position = n;
    6992         165 :     if (is_valid_font_mounting_position(n))
    6993         165 :       finfo->position = curenv->get_family()->resolve(n);
    6994             :   }
    6995       11015 :   return (finfo->position != FONT_NOT_MOUNTED);
    6996             : }
    6997             : 
    6998             : static int underline_fontno = 2;
    6999             : 
    7000           0 : static void select_underline_font()
    7001             : {
    7002           0 :   if (!has_arg()) {
    7003           0 :     warning(WARN_MISSING, "underline font selection request expects an"
    7004             :             " argument");
    7005           0 :     skip_line();
    7006           0 :     return;
    7007             :   }
    7008           0 :   font_lookup_info finfo;
    7009           0 :   if (!read_font_identifier(&finfo))
    7010           0 :     font_lookup_error(finfo, "to make it the underline font");
    7011             :   else
    7012           0 :     underline_fontno = finfo.position;
    7013           0 :   skip_line();
    7014             : }
    7015             : 
    7016           9 : int get_underline_fontno()
    7017             : {
    7018           9 :   return underline_fontno;
    7019             : }
    7020             : 
    7021       10492 : static void define_font_specific_character()
    7022             : {
    7023       10492 :   if (!has_arg()) {
    7024           0 :     warning(WARN_MISSING, "font-specific fallback character definition"
    7025             :             " request expects arguments");
    7026           0 :     skip_line();
    7027           0 :     return;
    7028             :   }
    7029       10492 :   font_lookup_info finfo;
    7030       10492 :   if (!read_font_identifier(&finfo)) {
    7031           0 :     font_lookup_error(finfo, "to define font-specific fallback"
    7032             :                       " character");
    7033             :     // Normally we skip the remainder of the line unconditionally at the
    7034             :     // end of a request-implementing function, but define_character()
    7035             :     // will eat the rest of it for us.
    7036           0 :     skip_line();
    7037             :   }
    7038             :   else {
    7039       10492 :     symbol f = font_table[finfo.position]->get_name();
    7040       10492 :     define_character(CHAR_FONT_SPECIFIC_FALLBACK, f.contents());
    7041             :   }
    7042             : }
    7043             : 
    7044           0 : static void remove_font_specific_character()
    7045             : {
    7046           0 :   if (!has_arg()) {
    7047           0 :     warning(WARN_MISSING, "font-specific fallback character removal"
    7048             :             " request expects arguments");
    7049           0 :     skip_line();
    7050           0 :     return;
    7051             :   }
    7052           0 :   font_lookup_info finfo;
    7053           0 :   if (!read_font_identifier(&finfo))
    7054           0 :     font_lookup_error(finfo, "to remove font-specific fallback"
    7055             :                       " character");
    7056             :   else {
    7057           0 :     symbol f = font_table[finfo.position]->get_name();
    7058           0 :     while (!tok.is_newline() && !tok.is_eof()) {
    7059           0 :       if (!tok.is_space() && !tok.is_tab()) {
    7060           0 :         charinfo *s = tok.get_charinfo(true /* required */);
    7061           0 :         if (0 /* nullptr */ == s)
    7062           0 :           assert(0 == "attempted to use token without charinfo in"
    7063             :                  " font-specific character removal request");
    7064             :         else {
    7065           0 :           string gl(f.contents());
    7066           0 :           gl += ' ';
    7067           0 :           gl += s->nm.contents();
    7068           0 :           gl += '\0';
    7069           0 :           charinfo *ci = lookup_charinfo(symbol(gl.contents()));
    7070             :           // Expect a null pointer if the font-specific character was
    7071             :           // already removed or never existed.
    7072           0 :           if (ci != 0 /* nullptr */) {
    7073           0 :             macro *m = ci->set_macro(0 /* nullptr */);
    7074           0 :             if (m != 0 /* nullptr */)
    7075           0 :               delete m;
    7076             :           }
    7077             :         }
    7078             :       }
    7079           0 :       tok.next();
    7080             :     }
    7081             :   }
    7082           0 :   skip_line();
    7083             : }
    7084             : 
    7085         127 : static void read_special_fonts(special_font_list **sp)
    7086             : {
    7087         127 :   special_font_list *s = *sp;
    7088         127 :   *sp = 0 /* nullptr */;
    7089         141 :   while (s != 0 /* nullptr */) {
    7090          14 :     special_font_list *tem = s;
    7091          14 :     s = s->next;
    7092          14 :     delete tem;
    7093             :   }
    7094         127 :   special_font_list **p = sp;
    7095         370 :   while (has_arg()) {
    7096         243 :     font_lookup_info finfo;
    7097         243 :     if (!read_font_identifier(&finfo))
    7098           0 :       font_lookup_error(finfo, "to mark it as special");
    7099             :     else {
    7100         243 :       special_font_list *tem = new special_font_list;
    7101         243 :       tem->n = finfo.position;
    7102         243 :       tem->next = 0 /* nullptr */;
    7103         243 :       *p = tem;
    7104         243 :       p = &(tem->next);
    7105             :     }
    7106             :   }
    7107         127 : }
    7108             : 
    7109         110 : static void set_font_specific_special_fonts()
    7110             : {
    7111         110 :   if (!has_arg()) {
    7112           0 :     warning(WARN_MISSING, "font-specific special font selection request"
    7113             :             " expects at least one argument");
    7114           0 :     skip_line();
    7115           0 :     return;
    7116             :   }
    7117         110 :   font_lookup_info finfo;
    7118         110 :   if (!read_font_identifier(&finfo))
    7119           0 :     font_lookup_error(finfo, "to mark other fonts as special"
    7120             :                              " contingently upon it"); // a mouthful :-/
    7121             :   else
    7122         110 :     read_special_fonts(&font_table[finfo.position]->sf);
    7123         110 :   skip_line();
    7124             : }
    7125             : 
    7126          17 : static void set_special_fonts()
    7127             : {
    7128          17 :   read_special_fonts(&global_special_fonts);
    7129          17 :   skip_line();
    7130          17 : }
    7131             : 
    7132           0 : static void zoom_font()
    7133             : {
    7134           0 :   if (!(has_arg())) {
    7135           0 :     warning(WARN_MISSING, "font zoom factor request expects arguments");
    7136           0 :     skip_line();
    7137           0 :     return;
    7138             :   }
    7139           0 :   symbol font_name = read_identifier();
    7140             :   // has_arg()+read_identifier() should ensure the assertion succeeds.
    7141           0 :   assert(font_name != 0 /* nullptr */);
    7142           0 :   if (is_nonnegative_integer(font_name.contents())) {
    7143           0 :     warning(WARN_FONT, "cannot set zoom factor of a font mounting"
    7144             :             " position");
    7145           0 :     skip_line();
    7146           0 :     return;
    7147             :   }
    7148           0 :   if (!(has_arg())) {
    7149           0 :     warning(WARN_MISSING, "font zoom factor request expects zoom factor"
    7150             :             " argument");
    7151           0 :     skip_line();
    7152           0 :     return;
    7153             :   }
    7154           0 :   int fpos = next_available_font_position();
    7155           0 :   if (!(mount_font(fpos, font_name))) {
    7156           0 :     error("cannot mount font '%1' to set a zoom factor for it",
    7157           0 :           font_name.contents());
    7158           0 :     skip_line();
    7159           0 :     return;
    7160             :   }
    7161             : #if 0
    7162             :   // This would be a good diagnostic to have, but mount_font() is too
    7163             :   // formally complex to make it easy.  Instead it will fail in the
    7164             :   // above test on a font named "R", for instance, when that is
    7165             :   // literally true but might not help users who don't understand that
    7166             :   // "R", "I", "B", and "BI" are (by default) abstract styles, not fonts
    7167             :   // in the GNU troff sense.  It is a shame that a lot of our validation
    7168             :   // functions are willing only to handle arguments that they eat from
    7169             :   // the input stream (i.e., you can't pass them information you
    7170             :   // obtained elsewhere).  That design also forces us to validate
    7171             :   // request arguments in the order they appear in the input, and seems
    7172             :   // unnecessarily inflexible to me.  --GBR
    7173             :   if (font_table[fpos]->is_style()) {
    7174             :     warning(WARN_FONT, "ignoring request to set font zoom factor on an"
    7175             :             " abstract style");
    7176             :     skip_line();
    7177             :     return;
    7178             :   }
    7179             : #endif
    7180           0 :   int zoom = 0;
    7181           0 :   read_integer(&zoom);
    7182           0 :   if (zoom < 0) {
    7183           0 :     warning(WARN_RANGE, "ignoring negative font zoom factor '%1'",
    7184           0 :             zoom);
    7185           0 :     skip_line();
    7186           0 :     return;
    7187             :   }
    7188           0 :   font_table[fpos]->set_zoom(zoom);
    7189           0 :   skip_line();
    7190             : }
    7191             : 
    7192        9486 : int next_available_font_position()
    7193             : {
    7194             :   int i;
    7195      385920 :   for (i = 1;
    7196      385920 :        (i < font_table_size) && (font_table[i] != 0 /* nullptr */);
    7197             :        i++)
    7198             :     ;
    7199        9486 :   return i;
    7200             : }
    7201             : 
    7202      255338 : int symbol_fontno(symbol s)
    7203             : {
    7204      255338 :   s = get_font_translation(s);
    7205     1922118 :   for (int i = 0; i < font_table_size; i++)
    7206     3820228 :     if ((font_table[i] != 0 /* nullptr */)
    7207     1910114 :         && font_table[i]->is_named(s))
    7208      243334 :       return i;
    7209       12004 :   return FONT_NOT_MOUNTED;
    7210             : }
    7211             : 
    7212           1 : hunits env_font_emboldening_offset(environment *env, int n)
    7213             : {
    7214           1 :   if (is_valid_font_mounting_position(n)) {
    7215           1 :     hunits offset;
    7216           1 :     int fontno = env->get_family()->resolve(env->get_font());
    7217           1 :     if (font_table[fontno]->is_emboldened(&offset))
    7218           1 :       return offset.to_units() + 1;
    7219             :     else
    7220           0 :       return H0;
    7221             :   }
    7222             :   else
    7223           0 :     return H0;
    7224             : }
    7225             : 
    7226        1235 : hunits env_digit_width(environment *env)
    7227             : {
    7228        1235 :   node *n = make_glyph_node(charset_table['0'], env);
    7229        1235 :   if (n != 0 /* nullptr */) {
    7230        1235 :     hunits x = n->width();
    7231        1235 :     delete n;
    7232        1235 :     return x;
    7233             :   }
    7234             :   else
    7235           0 :     return H0;
    7236             : }
    7237             : 
    7238      803676 : hunits env_space_width(environment *env)
    7239             : {
    7240      803676 :   int fn = env_resolve_font(env);
    7241      803676 :   font_size fs = env->get_font_size();
    7242      803676 :   if (!is_valid_font_mounting_position(fn))
    7243           0 :     return scale(fs.to_units() / 3, env->get_space_size(), 12);
    7244             :   else
    7245      803676 :     return font_table[fn]->get_space_width(fs, env->get_space_size());
    7246             : }
    7247             : 
    7248      788303 : hunits env_sentence_space_width(environment *env)
    7249             : {
    7250      788303 :   int fn = env_resolve_font(env);
    7251      788303 :   font_size fs = env->get_font_size();
    7252      788303 :   units sss = env->get_sentence_space_size();
    7253      788303 :   if (!is_valid_font_mounting_position(fn))
    7254           0 :     return scale(fs.to_units() / 3, sss, 12);
    7255             :   else
    7256      788303 :     return font_table[fn]->get_space_width(fs, sss);
    7257             : }
    7258             : 
    7259         310 : hunits env_half_narrow_space_width(environment *env)
    7260             : {
    7261         310 :   int fn = env_resolve_font(env);
    7262         310 :   font_size fs = env->get_font_size();
    7263         310 :   if (!is_valid_font_mounting_position(fn))
    7264           0 :     return H0;
    7265             :   else
    7266         310 :     return font_table[fn]->get_half_narrow_space_width(fs);
    7267             : }
    7268             : 
    7269        5349 : hunits env_narrow_space_width(environment *env)
    7270             : {
    7271        5349 :   int fn = env_resolve_font(env);
    7272        5349 :   font_size fs = env->get_font_size();
    7273        5349 :   if (!is_valid_font_mounting_position(fn))
    7274           0 :     return H0;
    7275             :   else
    7276        5349 :     return font_table[fn]->get_narrow_space_width(fs);
    7277             : }
    7278             : 
    7279             : // XXX: We can only conditionally (un)embolden a font specified by name,
    7280             : // not position.  Does ".bd 1 2" mean "embolden font position 1 by 2
    7281             : // units" (really one unit), or "stop conditionally emboldening font 2
    7282             : // when font 1 is selected"?
    7283           6 : static void embolden_font()
    7284             : {
    7285           6 :   if (!(has_arg())) {
    7286           0 :     warning(WARN_MISSING, "emboldening request expects arguments");
    7287           0 :     skip_line();
    7288           0 :     return;
    7289             :   }
    7290           6 :   if (in_nroff_mode) {
    7291           0 :     skip_line();
    7292           0 :     return;
    7293             :   }
    7294             :   // XXX: Here's where `is_ordinary_character()` would be useful.
    7295             :   // (TOKEN_CHAR == type) means the same thing, but this file doesn't
    7296             :   // root around in token::token_type...
    7297           6 :   if ((!tok.is_any_character())
    7298           6 :       || tok.is_special_character()
    7299          12 :       || tok.is_indexed_character()) {
    7300           0 :     error("emboldening request expects font name or mounting position"
    7301           0 :           " as first argument, got %1", tok.description());
    7302           0 :     skip_line();
    7303           0 :     return;
    7304             :   }
    7305             :   // If the 1st argument starts with a non-numeral, we must be prepared
    7306             :   // to expect a third argument to specify an emboldening amount...
    7307             :   // .bd S TB 3 \" embolden S when `TB` selected
    7308             :   // ...or removal of conditional emboldening.
    7309             :   // .bd S TB \" don't embolden S when `TB` selected
    7310             :   //
    7311             :   // XXX: Our approach forecloses the emboldening of fonts whose names
    7312             :   // _start_ with numerals but _contain_ non-numerals, like '3foo'.  If
    7313             :   // one agrees that Kernighan's grammar for the extended `bd` request
    7314             :   // of device-independent troff is ambiguous, there may not be a
    7315             :   // perfect solution.  (This approach could be improved by scanning
    7316             :   // ahead in the input, but is it worth the trouble?)
    7317           6 :   bool emboldening_may_be_conditional = false;
    7318           6 :   if (!csdigit(tok.ch()))
    7319           3 :     emboldening_may_be_conditional = true;
    7320           6 :   font_lookup_info finfo;
    7321           6 :   if (!read_font_identifier(&finfo)) {
    7322           0 :     font_lookup_error(finfo, "for emboldening");
    7323           0 :     skip_line();
    7324           0 :     return;
    7325             :   }
    7326           6 :   int n = finfo.position;
    7327           6 :   if (has_arg()) {
    7328             :     // XXX: Here's another useful `is_ordinary_character()` spot.
    7329           5 :     if ((!tok.is_any_character())
    7330           5 :         || tok.is_special_character()
    7331          10 :         || tok.is_indexed_character()) {
    7332           0 :       error("emboldening request expects font name or emboldening"
    7333           0 :             " amount as second argument, got %1", tok.description());
    7334           0 :       skip_line();
    7335           0 :       return;
    7336             :     }
    7337             :     // If both arguments start with numerals, assume this form.
    7338             :     // .bd 2 4 \" embolden font position 2 by 3 (yes, 3) units
    7339             :     // ...otherwise...
    7340           5 :     if (emboldening_may_be_conditional && !(csdigit(tok.ch()))) {
    7341           2 :       font_lookup_info finfo2;
    7342           2 :       if (!read_font_identifier(&finfo2)) {
    7343           0 :         font_lookup_error(finfo2, "for conditional emboldening");
    7344           0 :         skip_line();
    7345           0 :         return;
    7346             :       }
    7347           2 :       int f = finfo2.position;
    7348             :       units offset;
    7349           2 :       if (has_arg()
    7350           1 :           && read_measurement(&offset, 'u')
    7351           3 :           && (offset >= 1))
    7352           1 :         font_table[f]->set_conditional_bold(n, hunits(offset - 1));
    7353             :       else
    7354           1 :         font_table[f]->conditional_unbold(n);
    7355             :     }
    7356             :     else {
    7357             :       // The second argument must be an emboldening amount.
    7358             :       units offset;
    7359           3 :       if (read_measurement(&offset, 'u') && (offset >= 1))
    7360           3 :         font_table[n]->set_bold(hunits(offset - 1));
    7361             :       else
    7362           0 :         font_table[n]->unbold();
    7363           3 :       if (has_arg())
    7364           1 :         warning(WARN_FONT, "ignoring third argument to font emboldening"
    7365             :                 " request when first interpreted as mounting position"
    7366             :                 " and second as emboldening amount");
    7367             :     }
    7368             :   }
    7369             :   else
    7370           1 :     font_table[n]->unbold();
    7371           6 :   skip_line();
    7372             : }
    7373             : 
    7374       17341 : track_kerning_function::track_kerning_function() : non_zero(0)
    7375             : {
    7376       17341 : }
    7377             : 
    7378         162 : track_kerning_function::track_kerning_function(int min_s, hunits min_a,
    7379         162 :                                                int max_s, hunits max_a)
    7380             : : non_zero(1), min_size(min_s), min_amount(min_a), max_size(max_s),
    7381         162 :   max_amount(max_a)
    7382             : {
    7383         162 : }
    7384             : 
    7385           0 : int track_kerning_function::operator==(const track_kerning_function &tk)
    7386             : {
    7387           0 :   if (non_zero)
    7388           0 :     return (tk.non_zero
    7389           0 :             && min_size == tk.min_size
    7390           0 :             && min_amount == tk.min_amount
    7391           0 :             && max_size == tk.max_size
    7392           0 :             && max_amount == tk.max_amount);
    7393             :   else
    7394           0 :     return !tk.non_zero;
    7395             : }
    7396             : 
    7397         162 : int track_kerning_function::operator!=(const track_kerning_function &tk)
    7398             : {
    7399         162 :   if (non_zero)
    7400         141 :     return (!tk.non_zero
    7401         141 :             || min_size != tk.min_size
    7402         141 :             || min_amount != tk.min_amount
    7403           6 :             || max_size != tk.max_size
    7404         282 :             || max_amount != tk.max_amount);
    7405             :   else
    7406          21 :     return tk.non_zero;
    7407             : }
    7408             : 
    7409       19136 : hunits track_kerning_function::compute(int size)
    7410             : {
    7411       19136 :   if (non_zero) {
    7412         560 :     if (max_size <= min_size)
    7413         560 :       return min_amount;
    7414           0 :     else if (size <= min_size)
    7415           0 :       return min_amount;
    7416           0 :     else if (size >= max_size)
    7417           0 :       return max_amount;
    7418             :     else
    7419           0 :       return (scale(max_amount, size - min_size, max_size - min_size)
    7420           0 :               + scale(min_amount, max_size - size, max_size - min_size));
    7421             :   }
    7422             :   else
    7423       18576 :     return H0;
    7424             : }
    7425             : 
    7426         162 : static void configure_track_kerning()
    7427             : {
    7428         162 :   if (!has_arg()) {
    7429           0 :     warning(WARN_MISSING, "track kerning request expects arguments");
    7430           0 :     skip_line();
    7431           0 :     return;
    7432             :   }
    7433         162 :   font_lookup_info finfo;
    7434         162 :   if (!read_font_identifier(&finfo))
    7435           0 :     font_lookup_error(finfo, "for track kerning");
    7436             :   else {
    7437         162 :     int n = finfo.position, min_s, max_s;
    7438         162 :     hunits min_a, max_a;
    7439         162 :     if (has_arg()
    7440         162 :         && read_measurement(&min_s, 'z')
    7441         162 :         && read_hunits(&min_a, 'p')
    7442         162 :         && read_measurement(&max_s, 'z')
    7443         324 :         && read_hunits(&max_a, 'p')) {
    7444         162 :       track_kerning_function tk(min_s, min_a, max_s, max_a);
    7445         162 :       font_table[n]->set_track_kern(tk);
    7446             :     }
    7447             :     else {
    7448           0 :       track_kerning_function tk;
    7449           0 :       font_table[n]->set_track_kern(tk);
    7450             :     }
    7451             :   }
    7452         162 :   skip_line();
    7453             : }
    7454             : 
    7455           0 : static void constantly_space_font()
    7456             : {
    7457           0 :   if (!has_arg()) {
    7458           0 :     warning(WARN_MISSING, "constant spacing request expects arguments");
    7459           0 :     skip_line();
    7460           0 :     return;
    7461             :   }
    7462           0 :   font_lookup_info finfo;
    7463           0 :   if (!read_font_identifier(&finfo))
    7464           0 :     font_lookup_error(finfo, "for constant spacing");
    7465             :   else {
    7466           0 :     int n = finfo.position, x, y;
    7467           0 :     if (!has_arg() || !read_integer(&x))
    7468           0 :       font_table[n]->set_constant_space(CONSTANT_SPACE_NONE);
    7469             :     else {
    7470           0 :       if (!has_arg() || !read_measurement(&y, 'z'))
    7471           0 :         font_table[n]->set_constant_space(CONSTANT_SPACE_RELATIVE, x);
    7472             :       else
    7473           0 :         font_table[n]->set_constant_space(CONSTANT_SPACE_ABSOLUTE,
    7474             :                                           scale(y*x,
    7475             :                                                 units_per_inch,
    7476             :                                                 36 * 72 * sizescale));
    7477             :     }
    7478             :   }
    7479           0 :   skip_line();
    7480             : }
    7481             : 
    7482          28 : static void set_ligature_mode()
    7483             : {
    7484             :   int lig;
    7485          28 :   if (has_arg() && read_integer(&lig) && lig >= 0 && lig <= 2)
    7486           0 :     global_ligature_mode = lig;
    7487             :   else
    7488          28 :     global_ligature_mode = 1;
    7489          28 :   skip_line();
    7490          28 : }
    7491             : 
    7492         352 : static void set_kerning_mode()
    7493             : {
    7494             :   int k;
    7495         352 :   if (has_arg() && read_integer(&k))
    7496         162 :     global_kern_mode = (k > 0);
    7497             :   else
    7498         190 :     global_kern_mode = true;
    7499         352 :   skip_line();
    7500         352 : }
    7501             : 
    7502             : // Set (soft) hyphenation character, used to mark where a discretionary
    7503             : // break ("dbreak") has occurred in formatted output, conventionally
    7504             : // within a word at a syllable boundary.
    7505             : //
    7506             : // XXX: The soft hyphen character is global; shouldn't it be
    7507             : // environmental?
    7508           0 : static void soft_hyphen_character_request()
    7509             : {
    7510           0 :   soft_hyphen_char = read_character();
    7511           0 :   if (0 /* nullptr */ == soft_hyphen_char)
    7512           0 :     soft_hyphen_char = lookup_charinfo(HYPHEN_SYMBOL);
    7513           0 :   skip_line();
    7514           0 : }
    7515             : 
    7516        1279 : void init_output()
    7517             : {
    7518        1279 :   if (want_output_suppressed)
    7519          59 :     the_output = new suppress_output_file;
    7520        1220 :   else if (want_abstract_output)
    7521          26 :     the_output = new ascii_output_file;
    7522             :   else
    7523        1194 :     the_output = new troff_output_file;
    7524        1279 : }
    7525             : 
    7526             : class next_available_font_position_reg : public reg {
    7527             : public:
    7528             :   const char *get_string();
    7529             : };
    7530             : 
    7531        2632 : const char *next_available_font_position_reg::get_string()
    7532             : {
    7533        2632 :   return i_to_a(next_available_font_position());
    7534             : }
    7535             : 
    7536             : class printing_reg : public reg {
    7537             : public:
    7538             :   const char *get_string();
    7539             : };
    7540             : 
    7541           0 : const char *printing_reg::get_string()
    7542             : {
    7543           0 :   if (the_output)
    7544           0 :     return the_output->is_selected_for_printing() ? "1" : "0";
    7545             :   else
    7546           0 :     return "0";
    7547             : }
    7548             : 
    7549        1418 : void init_node_requests()
    7550             : {
    7551        1418 :   init_request("bd", embolden_font);
    7552        1418 :   init_request("cs", constantly_space_font);
    7553        1418 :   init_request("fp", mount_font_at_position);
    7554        1418 :   init_request("fschar", define_font_specific_character);
    7555        1418 :   init_request("fspecial", set_font_specific_special_fonts);
    7556        1418 :   init_request("fzoom", zoom_font);
    7557        1418 :   init_request("ftr", translate_font);
    7558        1418 :   init_request("kern", set_kerning_mode);
    7559        1418 :   init_request("lg", set_ligature_mode);
    7560        1418 :   init_request("pfp", print_font_mounting_position_request);
    7561        1418 :   init_request("pftr", print_font_translation_request);
    7562        1418 :   init_request("rfschar", remove_font_specific_character);
    7563        1418 :   init_request("shc", soft_hyphen_character_request);
    7564        1418 :   init_request("special", set_special_fonts);
    7565        1418 :   init_request("sty", associate_style_with_font_position);
    7566        1418 :   init_request("tkf", configure_track_kerning);
    7567        1418 :   init_request("uf", select_underline_font);
    7568        1418 :   register_dictionary.define(".fp",
    7569        1418 :                              new next_available_font_position_reg);
    7570        1418 :   register_dictionary.define(".kern",
    7571        1418 :       new readonly_boolean_register(&global_kern_mode));
    7572        1418 :   register_dictionary.define(".lg",
    7573        1418 :       new readonly_register(&global_ligature_mode));
    7574        1418 :   register_dictionary.define(".P", new printing_reg);
    7575        1418 :   soft_hyphen_char = lookup_charinfo(HYPHEN_SYMBOL);
    7576        1418 : }
    7577             : 
    7578             : // Local Variables:
    7579             : // fill-column: 72
    7580             : // mode: C++
    7581             : // End:
    7582             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14