LCOV - code coverage report
Current view: top level - roff/troff - div.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 635 725 87.6 %
Date: 2026-01-16 17:51:41 Functions: 80 92 87.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2020 Free Software Foundation, Inc.
       2             :              2020-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             : // diversions
      22             : 
      23             : #ifdef HAVE_CONFIG_H
      24             : #include <config.h>
      25             : #endif
      26             : 
      27             : #include <stdlib.h> // exit(), EXIT_SUCCESS
      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 "input.h" // was_invoked_with_regular_control_character
      41             : 
      42             : #include "nonposix.h"
      43             : 
      44             : bool is_exit_underway = false;
      45             : bool is_eoi_macro_finished = false;
      46             : bool seen_last_page_ejector = false;
      47             : static bool began_page_in_eoi_macro = false;
      48             : int last_page_number = 0;       // if > 0, the number of the last page
      49             :                                 // specified with -o
      50             : 
      51             : static int last_post_line_extra_space = 0; // needed for \n(.a
      52             : static int nl_reg_contents = -1;
      53             : static int dl_reg_contents = 0;
      54             : static int dn_reg_contents = 0;
      55             : static bool honor_vertical_position_traps = true;
      56             : static vunits truncated_space;
      57             : static vunits needed_space;
      58             : 
      59       53206 : diversion::diversion(symbol s, bool boxing)
      60             : : prev(0 /* nullptr */), nm(s), vertical_position(V0),
      61             :   high_water_mark(V0), is_box(boxing), is_in_no_space_mode(false),
      62             :   saved_seen_break(false), saved_seen_space(false),
      63             :   saved_seen_eol(false), saved_suppress_next_eol(false),
      64       53206 :   marked_place(V0)
      65             : {
      66       53206 : }
      67             : 
      68             : struct vertical_size {
      69             :   vunits pre_extra, post_extra, pre, post;
      70             :   vertical_size(vunits vs, vunits post_vs);
      71             : };
      72             : 
      73      530674 : vertical_size::vertical_size(vunits vs, vunits post_vs)
      74      530674 : : pre_extra(V0), post_extra(V0), pre(vs), post(post_vs)
      75             : {
      76      530674 : }
      77             : 
      78    10996988 : void node::set_vertical_size(vertical_size *)
      79             : {
      80    10996988 : }
      81             : 
      82         252 : void extra_size_node::set_vertical_size(vertical_size *v)
      83             : {
      84         252 :   if (n < V0) {
      85          27 :     if (-n > v->pre_extra)
      86          27 :       v->pre_extra = -n;
      87             :   }
      88         225 :   else if (n > v->post_extra)
      89          49 :     v->post_extra = n;
      90         252 : }
      91             : 
      92     3290858 : void vertical_size_node::set_vertical_size(vertical_size *v)
      93             : {
      94     3290858 :   if (n < V0)
      95     1645429 :     v->pre = -n;
      96             :   else
      97     1645429 :     v->post = n;
      98     3290858 : }
      99             : 
     100             : top_level_diversion *topdiv;
     101             : 
     102             : diversion *curdiv;
     103             : 
     104      103576 : void do_divert(bool appending, bool boxing)
     105             : {
     106      103576 :   tok.skip_spaces();
     107      103576 :   symbol nm = read_identifier();
     108      103576 :   if (nm.is_null()) {
     109       51788 :     if (curdiv->prev != 0 /* nullptr */) {
     110             :       // Why the asymmetric diagnostic severity?  See Savannah #67139.
     111       51788 :       if (!(curdiv->is_box) && boxing)
     112           1 :         fatal("cannot close ordinary diversion with box request");
     113       51787 :       if (curdiv->is_box && !boxing)
     114           0 :         error("cannot close box diversion with ordinary diversion"
     115             :               " request");
     116       51787 :       curenv->seen_break = curdiv->saved_seen_break;
     117       51787 :       curenv->seen_space = curdiv->saved_seen_space;
     118       51787 :       curenv->seen_eol = curdiv->saved_seen_eol;
     119       51787 :       curenv->suppress_next_eol = curdiv->saved_suppress_next_eol;
     120       51787 :       if (boxing) {
     121        2809 :         curenv->line = curdiv->saved_line;
     122        2809 :         curenv->width_total = curdiv->saved_width_total;
     123        2809 :         curenv->space_total = curdiv->saved_space_total;
     124        2809 :         curenv->saved_indent = curdiv->saved_saved_indent;
     125        2809 :         curenv->target_text_length = curdiv->saved_target_text_length;
     126        2809 :         curenv->was_previous_line_interrupted
     127        2809 :           = curdiv->saved_was_previous_line_interrupted;
     128             :       }
     129       51787 :       diversion *temp = curdiv;
     130       51787 :       curdiv = curdiv->prev;
     131       51787 :       delete temp;
     132             :     }
     133             :     else
     134           0 :       warning(WARN_DI, "diversion stack underflow");
     135             :   }
     136             :   else {
     137       51788 :     macro_diversion *md = new macro_diversion(nm, appending, boxing);
     138       51788 :     md->prev = curdiv;
     139       51788 :     curdiv = md;
     140       51788 :     curdiv->saved_seen_break = curenv->seen_break;
     141       51788 :     curdiv->saved_seen_space = curenv->seen_space;
     142       51788 :     curdiv->saved_seen_eol = curenv->seen_eol;
     143       51788 :     curdiv->saved_suppress_next_eol = curenv->suppress_next_eol;
     144       51788 :     curenv->seen_break = false;
     145       51788 :     curenv->seen_space = false;
     146       51788 :     curenv->seen_eol = false;
     147       51788 :     if (boxing) {
     148        2809 :       curdiv->saved_line = curenv->line;
     149        2809 :       curdiv->saved_width_total = curenv->width_total;
     150        2809 :       curdiv->saved_space_total = curenv->space_total;
     151        2809 :       curdiv->saved_saved_indent = curenv->saved_indent;
     152        2809 :       curdiv->saved_target_text_length = curenv->target_text_length;
     153        2809 :       curdiv->saved_was_previous_line_interrupted
     154        2809 :         = curenv->was_previous_line_interrupted;
     155        2809 :       curenv->line = 0 /* nullptr */;
     156        2809 :       curenv->start_line();
     157             :     }
     158             :   }
     159      103575 :   skip_line();
     160      103575 : }
     161             : 
     162       97279 : static void divert() // .di
     163             : {
     164       97279 :   do_divert(false /* appending */, false /* boxing */);
     165       97279 : }
     166             : 
     167         678 : static void divert_append() // .da
     168             : {
     169         678 :   do_divert(true /* appending */, false /* boxing */);
     170         678 : }
     171             : 
     172        5555 : static void box() // .box
     173             : {
     174        5555 :   do_divert(false /* appending */, true /* boxing */);
     175        5554 : }
     176             : 
     177          64 : static void box_append() // .boxa
     178             : {
     179          64 :   do_divert(true /* appending */, true /* boxing */);
     180          64 : }
     181             : 
     182       19978 : void diversion::need(vunits n)
     183             : {
     184       19978 :   vunits d = distance_to_next_trap();
     185       19978 :   if (d < n) {
     186         451 :     truncated_space = -d;
     187         451 :     needed_space = n;
     188         451 :     space(d, 1);
     189             :   }
     190       19978 : }
     191             : 
     192       51788 : macro_diversion::macro_diversion(symbol s, bool appending, bool boxing)
     193             : : diversion(s, boxing), max_width(H0), diversion_trap(0 /* nullptr */),
     194       51788 :   diversion_trap_pos(0)
     195             : {
     196             : #if 0
     197             :   if (append) {
     198             :     /* We don't allow recursive appends, e.g.:
     199             : 
     200             :       .da a
     201             :       .a
     202             :       .di
     203             : 
     204             :       This causes an infinite loop in troff anyway.
     205             :       This is because the user could do
     206             : 
     207             :       .as a foo
     208             : 
     209             :       in the diversion, and this would mess things up royally,
     210             :       since there would be two things appending to the same
     211             :       macro_header.
     212             :       To make it work, we would have to copy the _contents_
     213             :       of the macro into which we were diverting; this doesn't
     214             :       strike me as worthwhile.
     215             :       However,
     216             : 
     217             :       .di a
     218             :       .a
     219             :       .a
     220             :       .di
     221             : 
     222             :        will work and will make 'a' contain two copies of what it
     223             :        contained before; in troff, 'a' would contain nothing. */
     224             :     request_or_macro *rm
     225             :       = static_cast<request_or_macro *>(request_dictionary.remove(s));
     226             :     if (!rm || (0 /* nullptr */ == (mac = rm->to_macro()))
     227             :       mac = new macro;
     228             :   }
     229             :   else
     230             :     mac = new macro;
     231             : #endif
     232             :   // We can now catch the situation described above by comparing
     233             :   // the length of the charlist in the macro_header with the length
     234             :   // stored in the macro. When we detect this, we copy the contents.
     235       51788 :   mac = new macro(true /* is diversion */);
     236       51788 :   if (appending) {
     237             :     request_or_macro *rm
     238         419 :       = static_cast<request_or_macro *>(request_dictionary.lookup(s));
     239         419 :     if (rm) {
     240         321 :       macro *m = rm->to_macro();
     241         321 :       if (m)
     242         321 :         *mac = *m;
     243             :     }
     244             :   }
     245       51788 : }
     246             : 
     247      207148 : macro_diversion::~macro_diversion()
     248             : {
     249             :   request_or_macro *rm
     250       51787 :     = static_cast<request_or_macro *>(request_dictionary.lookup(nm));
     251       51787 :   macro *m = rm ? rm->to_macro() : 0 /* nullptr */;
     252       51787 :   if (m) {
     253        4927 :     *m = *mac;
     254        4927 :     delete mac;
     255             :   }
     256             :   else
     257       46860 :     request_dictionary.define(nm, mac);
     258       51787 :   mac = 0 /* nullptr */;
     259       51787 :   dl_reg_contents = max_width.to_units();
     260       51787 :   dn_reg_contents = vertical_position.to_units();
     261      103574 : }
     262             : 
     263             : static const int DIVERSION_LENGTH_MAX = INT_MAX;
     264             : 
     265         668 : vunits macro_diversion::distance_to_next_trap()
     266             : {
     267         668 :   vunits distance = 0;
     268         668 :   if (!diversion_trap.is_null()
     269         668 :       && (diversion_trap_pos > vertical_position))
     270           0 :     distance = diversion_trap_pos - vertical_position;
     271             :   else
     272             :     // Do the (saturating) arithmetic ourselves to avoid an error
     273             :     // diagnostic from constructor in number.cpp.
     274         668 :     distance = units(DIVERSION_LENGTH_MAX / vresolution);
     275         668 :   assert(distance >= 0);
     276         668 :   return distance;
     277             : }
     278             : 
     279           2 : const char *macro_diversion::get_next_trap_name()
     280             : {
     281           2 :   if (!diversion_trap.is_null()
     282           2 :       && (diversion_trap_pos > vertical_position))
     283           1 :     return diversion_trap.contents();
     284             :   else
     285           1 :     return "";
     286             : }
     287             : 
     288      233376 : void macro_diversion::transparent_output(unsigned char c)
     289             : {
     290      233376 :   mac->append(c);
     291      233376 : }
     292             : 
     293           0 : void macro_diversion::transparent_output(node *n)
     294             : {
     295           0 :   mac->append(n);
     296           0 : }
     297             : 
     298      315916 : void macro_diversion::output(node *nd, bool retain_size,
     299             :                              vunits vs, vunits post_vs, hunits width)
     300             : {
     301      315916 :   is_in_no_space_mode = false;
     302      315916 :   vertical_size v(vs, post_vs);
     303     8104840 :   while (nd != 0 /* nullptr */) {
     304     7788924 :     nd->set_vertical_size(&v);
     305     7788924 :     node *temp = nd;
     306     7788924 :     nd = nd->next;
     307     7788924 :     if (temp->interpret(mac))
     308         430 :       delete temp;
     309             :     else {
     310             : #if 1
     311     7788494 :       temp->freeze_space();
     312             : #endif
     313     7788494 :       mac->append(temp);
     314             :     }
     315             :   }
     316      315916 :   last_post_line_extra_space = v.post_extra.to_units();
     317      315916 :   if (!retain_size) {
     318       37277 :     v.pre = vs;
     319       37277 :     v.post = post_vs;
     320             :   }
     321      315916 :   if (width > max_width)
     322       64065 :     max_width = width;
     323      315916 :   vunits x = v.pre + v.pre_extra + v.post + v.post_extra;
     324      315916 :   int new_vpos = 0;
     325      315916 :   int vpos = vertical_position.to_units();
     326      315916 :   int lineht = x.to_units();
     327      315916 :   bool overflow = false;
     328      315916 :   if (ckd_add(&new_vpos, vpos, lineht))
     329           0 :     overflow = true;
     330             :   else if (new_vpos > DIVERSION_LENGTH_MAX)
     331             :     overflow = true;
     332      315916 :   if (overflow)
     333           0 :     fatal("diversion overflow (vertical position: %1u,"
     334           0 :           " next line height: %2u)", vpos, lineht);
     335      315916 :   if (honor_vertical_position_traps
     336      310717 :       && !diversion_trap.is_null()
     337           1 :       && (diversion_trap_pos > vertical_position)
     338      626633 :       && (diversion_trap_pos <= vertical_position + x)) {
     339           1 :     vunits trunc = vertical_position + x - diversion_trap_pos;
     340           1 :     if (trunc > v.post)
     341           0 :       trunc = v.post;
     342           1 :     v.post -= trunc;
     343           1 :     x -= trunc;
     344           1 :     truncated_space = trunc;
     345           1 :     spring_trap(diversion_trap);
     346             :   }
     347      315916 :   mac->append(new vertical_size_node(-v.pre));
     348      315916 :   mac->append(new vertical_size_node(v.post));
     349      315916 :   mac->append('\n');
     350      315916 :   vertical_position += x;
     351      315916 :   if (vertical_position - v.post > high_water_mark)
     352       72372 :     high_water_mark = vertical_position - v.post;
     353      315916 : }
     354             : 
     355      526275 : void macro_diversion::space(vunits n, bool /* forcing */)
     356             : {
     357      526275 :   if (honor_vertical_position_traps
     358      521970 :       && !diversion_trap.is_null()
     359           0 :       && diversion_trap_pos > vertical_position
     360     1048245 :       && diversion_trap_pos <= vertical_position + n) {
     361           0 :     truncated_space = vertical_position + n - diversion_trap_pos;
     362           0 :     n = diversion_trap_pos - vertical_position;
     363           0 :     spring_trap(diversion_trap);
     364             :   }
     365      526275 :   else if (n + vertical_position < V0)
     366          51 :     n = -vertical_position;
     367      526275 :   mac->append(new diverted_space_node(n));
     368      526275 :   vertical_position += n;
     369      526275 : }
     370             : 
     371           2 : void macro_diversion::copy_file(const char *filename)
     372             : {
     373           2 :   mac->append(new diverted_copy_file_node(filename));
     374           2 : }
     375             : 
     376        1418 : top_level_diversion::top_level_diversion()
     377             : : page_number(0), page_count(0), last_page_count(-1),
     378             :   page_length(units_per_inch*11),
     379             :   prev_page_offset(units_per_inch), page_offset(units_per_inch),
     380             :   page_trap_list(0 /* nullptr */), overriding_next_page_number(false),
     381        1418 :   ejecting_page(false), before_first_page_status(1)
     382             : {
     383        1418 : }
     384             : 
     385             : // find the next trap after pos
     386             : 
     387      524538 : trap *top_level_diversion::find_next_trap(vunits *next_trap_pos)
     388             : {
     389      524538 :   trap *next_trap = 0 /* nullptr */;
     390     1636852 :   for (trap *pt = page_trap_list; pt != 0 /* nullptr */; pt = pt->next)
     391     1112314 :     if (!pt->nm.is_null()) {
     392     1083041 :       if (pt->position >= V0) {
     393      473464 :         if ((pt->position > vertical_position)
     394        3736 :             && (pt->position < page_length)
     395      477655 :             && ((0 /* nullptr */ == next_trap)
     396         455 :                 || pt->position < *next_trap_pos)) {
     397        3677 :           next_trap = pt;
     398        3677 :           *next_trap_pos = pt->position;
     399             :         }
     400             :       }
     401             :       else {
     402      609577 :         vunits pos = pt->position;
     403      609577 :         pos += page_length;
     404      609577 :         if ((pos > 0)
     405      609575 :             && (pos > vertical_position)
     406     1363043 :             && ((0 /* nullptr */ == next_trap)
     407      143891 :                 || (pos < *next_trap_pos))) {
     408      551765 :           next_trap = pt;
     409      551765 :           *next_trap_pos = pos;
     410             :         }
     411             :       }
     412             :     }
     413      524538 :   return next_trap;
     414             : }
     415             : 
     416       55244 : vunits top_level_diversion::distance_to_next_trap()
     417             : {
     418       55244 :   vunits d;
     419       55244 :   if (!find_next_trap(&d))
     420       16072 :     return page_length - vertical_position;
     421             :   else
     422       39172 :     return d - vertical_position;
     423             : }
     424             : 
     425           2 : const char *top_level_diversion::get_next_trap_name()
     426             : {
     427           2 :   vunits next_trap_pos;
     428           2 :   trap *next_trap = find_next_trap(&next_trap_pos);
     429           2 :   if (0 /* nullptr */ == next_trap)
     430           1 :     return "";
     431             :   else
     432           1 :     return next_trap->nm.contents();
     433             : }
     434             : 
     435             : // This is used by more than just top-level diversions.
     436      214758 : void top_level_diversion::output(node *nd, bool retain_size,
     437             :                                  vunits vs, vunits post_vs,
     438             :                                  hunits width)
     439             : {
     440      214758 :   is_in_no_space_mode = false;
     441      214758 :   vunits next_trap_pos;
     442      214758 :   trap *next_trap = find_next_trap(&next_trap_pos);
     443      214758 :   if ((before_first_page_status > 0) && begin_page())
     444           0 :     fatal("attempting diversion output before first page has started,"
     445             :           " when a top-of-page trap is defined; invoke break or flush"
     446             :           " request beforehand");
     447      214758 :   vertical_size v(vs, post_vs);
     448     6713932 :   for (node *tem = nd; tem != 0 /* nullptr */; tem = tem->next)
     449     6499174 :     tem->set_vertical_size(&v);
     450      214758 :   last_post_line_extra_space = v.post_extra.to_units();
     451      214758 :   if (!retain_size) {
     452       62467 :     v.pre = vs;
     453       62467 :     v.post = post_vs;
     454             :   }
     455      214758 :   vertical_position += v.pre;
     456      214758 :   vertical_position += v.pre_extra;
     457      214758 :   the_output->print_line(page_offset, vertical_position, nd,
     458      214758 :                          v.pre + v.pre_extra, v.post_extra, width);
     459      214758 :   vertical_position += v.post_extra;
     460      214758 :   if (vertical_position > high_water_mark)
     461       85399 :     high_water_mark = vertical_position;
     462      214758 :   if (honor_vertical_position_traps && vertical_position >= page_length)
     463          30 :     begin_page();
     464      214728 :   else if (honor_vertical_position_traps
     465      205473 :            && (next_trap != 0 /* nullptr */)
     466      420201 :            && (vertical_position >= next_trap_pos)) {
     467         554 :     nl_reg_contents = vertical_position.to_units();
     468         554 :     truncated_space = v.post;
     469         554 :     spring_trap(next_trap->nm);
     470             :   }
     471      214174 :   else if (v.post > V0) {
     472           2 :     vertical_position += v.post;
     473           2 :     if (honor_vertical_position_traps
     474           2 :         && (next_trap != 0 /* nullptr */)
     475           4 :         && (vertical_position >= next_trap_pos)) {
     476           0 :       truncated_space = vertical_position - next_trap_pos;
     477           0 :       vertical_position = next_trap_pos;
     478           0 :       nl_reg_contents = vertical_position.to_units();
     479           0 :       spring_trap(next_trap->nm);
     480             :     }
     481           2 :     else if (honor_vertical_position_traps
     482           2 :              && (vertical_position >= page_length))
     483           0 :       begin_page();
     484             :     else
     485           2 :       nl_reg_contents = vertical_position.to_units();
     486             :   }
     487             :   else
     488      214172 :     nl_reg_contents = vertical_position.to_units();
     489      214743 : }
     490             : 
     491             : // The next two member functions implement the internals of `.output`
     492             : // and `\!`.
     493             : 
     494      356631 : void top_level_diversion::transparent_output(unsigned char c)
     495             : {
     496      356631 :   if ((before_first_page_status > 0) && begin_page())
     497           0 :     fatal("attempting transparent output from top-level diversion"
     498             :           " before first page has started, when a top-of-page trap is"
     499             :           " defined; invoke break or flush request beforehand");
     500      356631 :   const char *s = encode_for_stream_output(c);
     501      713426 :   while (*s)
     502      356795 :     the_output->transparent_char(*s++);
     503      356631 : }
     504             : 
     505           0 : void top_level_diversion::transparent_output(node * /*n*/)
     506             : {
     507             :   // TODO: When Savannah #63074 is fixed, the user will have a way to
     508             :   // avoid this error.
     509           0 :   error("cannot write a node to device-independent output");
     510           0 : }
     511             : 
     512             : // Implement the internals of `.cf`.
     513           2 : void top_level_diversion::copy_file(const char *filename)
     514             : {
     515           2 :   if ((before_first_page_status > 0) && begin_page())
     516           0 :     fatal("attempting transparent copy of file to top-level diversion"
     517             :           " before first page has started, when a top-of-page trap is"
     518             :           " defined; invoke break or flush request beforehand");
     519           2 :   the_output->copy_file(page_offset, vertical_position, filename);
     520           2 : }
     521             : 
     522      251997 : void top_level_diversion::space(vunits n, bool forcing)
     523             : {
     524      251997 :   if (is_in_no_space_mode) {
     525         374 :     if (!forcing)
     526         118 :       return;
     527             :     else
     528         257 :       is_in_no_space_mode = false;
     529             :   }
     530      251880 :   if (before_first_page_status > 0) {
     531           1 :     begin_page(n);
     532           1 :     return;
     533             :   }
     534      251879 :   vunits next_trap_pos;
     535      251879 :   trap *next_trap = find_next_trap(&next_trap_pos);
     536      251879 :   vunits y = vertical_position + n;
     537      251879 :   if (curenv->get_vertical_spacing().to_units())
     538      251777 :     curenv->seen_space += n.to_units()
     539      251777 :                           / curenv->get_vertical_spacing().to_units();
     540      251879 :   if (honor_vertical_position_traps
     541      242744 :       && (next_trap != 0 /* nullptr */)
     542      494623 :       && (y >= next_trap_pos)) {
     543        2264 :     vertical_position = next_trap_pos;
     544        2264 :     nl_reg_contents = vertical_position.to_units();
     545        2264 :     truncated_space = y - vertical_position;
     546        2264 :     spring_trap(next_trap->nm);
     547             :   }
     548      249615 :   else if (y < V0) {
     549          21 :     vertical_position = V0;
     550          21 :     nl_reg_contents = vertical_position.to_units();
     551             :   }
     552      249594 :   else if (honor_vertical_position_traps
     553      249594 :            && (y >= page_length && n >= V0))
     554        2621 :     begin_page(y - page_length);
     555             :   else {
     556      246973 :     vertical_position = y;
     557      246973 :     nl_reg_contents = vertical_position.to_units();
     558             :   }
     559             : }
     560             : 
     561         612 : trap::trap(symbol s, vunits n, trap *p)
     562         612 : : next(p), position(n), nm(s)
     563             : {
     564         612 : }
     565             : 
     566        2093 : void top_level_diversion::add_trap(symbol nam, vunits pos)
     567             : {
     568        2093 :   trap *first_free_slot = 0 /* nullptr*/;
     569             :   trap **p;
     570        6747 :   for (p = &page_trap_list; *p; p = &(*p)->next) {
     571        5168 :     if ((*p)->nm.is_null()) {
     572        1856 :       if (0 /* nullptr*/ == first_free_slot)
     573         967 :         first_free_slot = *p;
     574             :     }
     575        3312 :     else if ((*p)->position == pos) {
     576         514 :       (*p)->nm = nam;
     577         514 :       return;
     578             :     }
     579             :   }
     580        1579 :   if (first_free_slot) {
     581         967 :     first_free_slot->nm = nam;
     582         967 :     first_free_slot->position = pos;
     583             :   }
     584             :   else
     585         612 :     *p = new trap(nam, pos, 0 /* nullptr*/);
     586             : }
     587             : 
     588        2396 : void top_level_diversion::remove_trap(symbol nam)
     589             : {
     590        7459 :   for (trap *p = page_trap_list; p; p = p->next)
     591        6204 :     if (p->nm == nam) {
     592        1141 :       p->nm = NULL_SYMBOL;
     593        1141 :       return;
     594             :     }
     595             : }
     596             : 
     597          11 : void top_level_diversion::remove_trap_at(vunits pos)
     598             : {
     599          11 :   for (trap *p = page_trap_list; p; p = p->next)
     600          11 :     if (p->position == pos) {
     601          11 :       p->nm = NULL_SYMBOL;
     602          11 :       return;
     603             :     }
     604             : }
     605             : 
     606         811 : void top_level_diversion::change_trap(symbol nam, vunits pos)
     607             : {
     608        1873 :   for (trap *p = page_trap_list; p; p = p->next)
     609        1867 :     if (p->nm == nam) {
     610         805 :       p->position = pos;
     611         805 :       return;
     612             :     }
     613           6 :   warning(WARN_MAC, "cannot move unplanted trap macro '%1'",
     614          12 :           nam.contents());
     615             : }
     616             : 
     617           1 : void top_level_diversion::print_traps()
     618             : {
     619           2 :   for (trap *p = page_trap_list; p; p = p->next)
     620           1 :     if (p->nm.is_null())
     621           0 :       fprintf(stderr, "  empty\n");
     622             :     else
     623           1 :       fprintf(stderr, "%s\t%d\n", p->nm.contents(),
     624             :               p->position.to_units());
     625           1 :   fflush(stderr);
     626           1 : }
     627             : 
     628        1184 : void end_diversions()
     629             : {
     630        1184 :   while (curdiv != topdiv) {
     631           0 :     error("automatically ending diversion '%1' on exit",
     632           0 :             curdiv->nm.contents());
     633           0 :     diversion *tem = curdiv;
     634           0 :     curdiv = curdiv->prev;
     635           0 :     delete tem;
     636             :   }
     637        1184 : }
     638             : 
     639             : // input.cpp:exit_troff() cleans up most formatter state.
     640        1419 : void write_any_trailer_and_exit(int exit_code)
     641             : {
     642             :   // If output was never initialized, there is no trailer to write.
     643        1419 :   if (the_output != 0 /* nullptr */) {
     644        1280 :     the_output->trailer(topdiv->get_page_length());
     645             :     // If we're already dying, don't call the_output's destructor.  See
     646             :     // node.cpp:real_output_file::~real_output_file().
     647        1280 :     if (!the_output->is_dying)
     648        1279 :       delete the_output;
     649             :   }
     650             :   FLUSH_INPUT_PIPE(STDIN_FILENO);
     651        1418 :   exit(exit_code);
     652             : }
     653             : 
     654             : // Returns `true` if beginning the page sprung a top-of-page trap.
     655             : // The optional parameter is for the .trunc register.
     656        4058 : bool top_level_diversion::begin_page(vunits n)
     657             : {
     658        4058 :   if (is_exit_underway) {
     659        2810 :     if (page_count == last_page_count
     660        1407 :         ? curenv->is_empty()
     661           3 :         : (is_eoi_macro_finished && (seen_last_page_ejector
     662           1 :                                       || began_page_in_eoi_macro)))
     663        1403 :       write_any_trailer_and_exit(EXIT_SUCCESS);
     664           2 :     if (!is_eoi_macro_finished)
     665           2 :       began_page_in_eoi_macro = true;
     666             :   }
     667        2655 :   if (last_page_number > 0 && page_number == last_page_number)
     668           0 :     write_any_trailer_and_exit(EXIT_SUCCESS);
     669        2655 :   if (0 /* nullptr */ == the_output)
     670        1234 :     init_output();
     671        2655 :   ++page_count;
     672        2655 :   if (overriding_next_page_number) {
     673         160 :     page_number = next_page_number;
     674         160 :     overriding_next_page_number = false;
     675             :   }
     676        2495 :   else if (before_first_page_status == 1)
     677        1275 :     page_number = 1;
     678             :   else
     679        1220 :     page_number++;
     680             :   // spring the top of page trap if there is one
     681        2655 :   vunits next_trap_pos;
     682        2655 :   vertical_position = -vresolution;
     683        2655 :   trap *next_trap = find_next_trap(&next_trap_pos);
     684        2655 :   vertical_position = V0;
     685        2655 :   high_water_mark = V0;
     686        2655 :   ejecting_page = false;
     687             :   // If before_first_page_status was 2, then the top of page transition
     688             :   // was undone using ".nr nl 0-1" or similar.  See nl_reg::set_value.
     689        2655 :   if (before_first_page_status != 2)
     690        2655 :     the_output->begin_page(page_number, page_length);
     691        2655 :   before_first_page_status = 0;
     692        2655 :   nl_reg_contents = vertical_position.to_units();
     693        2655 :   if ((honor_vertical_position_traps && (next_trap != 0 /* nullptr */))
     694        5310 :       && (next_trap_pos == V0)) {
     695        1301 :     truncated_space = n;
     696        1301 :     spring_trap(next_trap->nm);
     697        1301 :     return true;
     698             :   }
     699             :   else
     700        1354 :     return false;
     701             : }
     702             : 
     703        4856 : void continue_page_eject()
     704             : {
     705        4856 :   if (topdiv->get_ejecting()) {
     706        3052 :     if (curdiv != topdiv)
     707           0 :       error("cannot continue page ejection while diverting output");
     708        3052 :     else if (!honor_vertical_position_traps)
     709           0 :       error("cannot continue page ejection while vertical position"
     710             :             " traps disabled");
     711             :     else {
     712        3052 :       push_page_ejector();
     713        3052 :       topdiv->space(topdiv->get_page_length(), true /* forcing */);
     714             :     }
     715             :   }
     716        4659 : }
     717             : 
     718         212 : void top_level_diversion::set_next_page_number(int n)
     719             : {
     720         212 :   next_page_number = n;
     721         212 :   overriding_next_page_number = true;
     722         212 : }
     723             : 
     724           1 : int top_level_diversion::get_next_page_number()
     725             : {
     726           1 :   return overriding_next_page_number ? next_page_number
     727           1 :                                      : (page_number + 1);
     728             : }
     729             : 
     730         934 : void top_level_diversion::set_page_length(vunits n)
     731             : {
     732         934 :   page_length = n;
     733         934 : }
     734             : 
     735       51787 : diversion::~diversion()
     736             : {
     737       51787 : }
     738             : 
     739        5078 : void configure_page_offset_request()
     740             : {
     741        5078 :   hunits n;
     742             :   // The troff manual says that the default scaling indicator is v,
     743             :   // but it is in fact m: v wouldn't make sense for a horizontally
     744             :   // oriented request.
     745        5078 :   if (!has_arg() || !read_hunits(&n, 'm', topdiv->get_page_offset()))
     746          42 :     n = topdiv->get_previous_page_offset();
     747        5078 :   topdiv->set_page_offset(n);
     748        5078 :   topdiv->modified_tag.incl(MTSM_PO);
     749        5078 :   skip_line();
     750        5078 : }
     751             : 
     752         934 : static void configure_page_length_request() // .pl
     753             : {
     754         934 :   vunits temp;
     755         934 :   if (has_arg() && read_vunits(&temp, 'v', topdiv->get_page_length())) {
     756         932 :     if (temp < vresolution) {
     757           4 :       warning(WARN_RANGE, "setting computed page length %1u to device"
     758             :                           " vertical motion quantum",
     759           4 :                           temp.to_units());
     760           4 :       temp = vresolution;
     761             :     }
     762         932 :     topdiv->set_page_length(temp);
     763             :   }
     764             :   else
     765           2 :     topdiv->set_page_length(11 * units_per_inch);
     766         934 :   skip_line();
     767         934 : }
     768             : 
     769        2104 : static void when_request() // .wh
     770             : {
     771        2104 :   vunits n;
     772        2104 :   if (read_vunits(&n, 'v')) {
     773        2104 :     symbol s = read_identifier();
     774        2104 :     if (s.is_null())
     775          11 :       topdiv->remove_trap_at(n);
     776             :     else
     777        2093 :       topdiv->add_trap(s, n);
     778             :   }
     779        2104 :   skip_line();
     780        2104 : }
     781             : 
     782        2030 : static void begin_page() // .bp
     783             : {
     784        2030 :   bool got_arg = false;
     785        2030 :   int n = 0;
     786        2030 :   if (has_arg() && read_integer(&n, topdiv->get_page_number()))
     787         204 :     got_arg = true;
     788        2168 :   while (!tok.is_newline() && !tok.is_eof())
     789         138 :     tok.next();
     790        2030 :   if (curdiv == topdiv) {
     791        2030 :     if (topdiv->before_first_page_status > 0) {
     792           3 :       if (!was_invoked_with_regular_control_character) {
     793           0 :         if (got_arg)
     794           0 :           topdiv->set_next_page_number(n);
     795           0 :         if (got_arg || !topdiv->is_in_no_space_mode)
     796           0 :           topdiv->begin_page();
     797             :       }
     798           3 :       else if (topdiv->is_in_no_space_mode && !got_arg)
     799           0 :         topdiv->begin_page();
     800             :       else {
     801             :         /* Given this
     802             : 
     803             :          .wh 0 x
     804             :          .de x
     805             :          .tm \\n%
     806             :          ..
     807             :          .bp 3
     808             : 
     809             :          troff prints
     810             : 
     811             :          1
     812             :          3
     813             : 
     814             :          This code makes groff do the same. */
     815             : 
     816           3 :         push_page_ejector();
     817           3 :         topdiv->begin_page();
     818           3 :         if (got_arg)
     819           1 :           topdiv->set_next_page_number(n);
     820           3 :         topdiv->set_ejecting();
     821             :       }
     822             :     }
     823             :     else {
     824        2027 :       push_page_ejector();
     825        2027 :       if (was_invoked_with_regular_control_character)
     826         708 :         curenv->do_break();
     827        2027 :       if (got_arg)
     828         203 :         topdiv->set_next_page_number(n);
     829        2027 :       if (!(topdiv->is_in_no_space_mode && !got_arg))
     830        2008 :         topdiv->set_ejecting();
     831             :     }
     832             :   }
     833        2030 :   tok.next();
     834        2030 : }
     835             : 
     836       24848 : static void no_space() // .ns
     837             : {
     838       24848 :   curdiv->is_in_no_space_mode = true;
     839       24848 :   skip_line();
     840       24848 : }
     841             : 
     842        1091 : static void restore_spacing() // .re
     843             : {
     844        1091 :   curdiv->is_in_no_space_mode = false;
     845        1091 :   skip_line();
     846        1091 : }
     847             : 
     848             : /* It is necessary to generate a break before reading the argument,
     849             : because otherwise arguments using | will be wrong.  But if we just
     850             : generate a break as usual, then the line forced out may spring a trap
     851             : and thus push a macro onto the input stack before we have had a chance
     852             : to read the argument to the sp request.  We resolve this dilemma by
     853             : setting, before generating the break, a flag which will postpone the
     854             : actual pushing of the macro associated with the trap sprung by the
     855             : outputting of the line forced out by the break till after we have read
     856             : the argument to the request.  If the break did cause a trap to be
     857             : sprung, then we don't actually do the space. */
     858             : 
     859      249530 : static void space_request() // .sp
     860             : {
     861      249530 :   postpone_traps();
     862      249530 :   if (was_invoked_with_regular_control_character)
     863      249118 :     curenv->do_break();
     864      249530 :   vunits n;
     865      249530 :   if (!has_arg() || !read_vunits(&n, 'v'))
     866         450 :     n = curenv->get_vertical_spacing();
     867      349185 :   while (!tok.is_newline() && !tok.is_eof())
     868       99655 :     tok.next();
     869      249530 :   if (!unpostpone_traps() && !curdiv->is_in_no_space_mode)
     870      246651 :     curdiv->space(n);
     871             :   else
     872             :     // The line might have had line spacing that was truncated.
     873        2879 :     truncated_space += n;
     874             : 
     875      249530 :   tok.next();
     876      249530 : }
     877             : 
     878          80 : void blank_line()
     879             : {
     880          80 :   curenv->do_break();
     881          80 :   if (!was_trap_sprung && !curdiv->is_in_no_space_mode)
     882          80 :     curdiv->space(curenv->get_vertical_spacing());
     883             :   else
     884           0 :     truncated_space += curenv->get_vertical_spacing();
     885          80 : }
     886             : 
     887             : /* need_space might spring a trap and so we must be careful that the
     888             : BEGIN_TRAP token is not skipped over. */
     889             : 
     890       19978 : static void need_space() // .ne
     891             : {
     892       19978 :   vunits n;
     893       19978 :   if (!has_arg() || !read_vunits(&n, 'v'))
     894           0 :     n = curenv->get_vertical_spacing();
     895       20394 :   while (!tok.is_newline() && !tok.is_eof())
     896         416 :     tok.next();
     897       19978 :   curdiv->need(n);
     898       19978 :   tok.next();
     899       19978 : }
     900             : 
     901           8 : static void page_number() // .pn
     902             : {
     903           8 :   if (!has_arg()) {
     904           0 :     warning(WARN_MISSING, "page number assignment request expects an"
     905             :             " argument");
     906           0 :     skip_line();
     907           0 :     return;
     908             :   }
     909           8 :   int n = 0;
     910             :   // the ps4html register is set if we are using -Tps
     911             :   // to generate images for html
     912             :   // XXX: Yuck!  Get rid of this; macro packages already test the
     913             :   // register before invoking .pn.
     914           8 :   reg *r = static_cast<reg *>(register_dictionary.lookup("ps4html"));
     915           8 :   if (0 /* nullptr */ == r)
     916           8 :     if (has_arg() && read_integer(&n, topdiv->get_page_number()))
     917           8 :       topdiv->set_next_page_number(n);
     918           8 :   skip_line();
     919             : }
     920             : 
     921             : vunits saved_space;
     922             : 
     923           0 : static void save_vertical_space() // .sv
     924             : {
     925           0 :   vunits x;
     926           0 :   if (!has_arg() || !read_vunits(&x, 'v'))
     927           0 :     x = curenv->get_vertical_spacing();
     928           0 :   if (curdiv->distance_to_next_trap() > x)
     929           0 :     curdiv->space(x, true /* forcing */);
     930             :   else
     931           0 :     saved_space = x;
     932           0 :   skip_line();
     933           0 : }
     934             : 
     935          85 : static void output_saved_vertical_space() // .os
     936             : {
     937          85 :   while (!tok.is_newline() && !tok.is_eof())
     938           0 :     tok.next();
     939          85 :   if (saved_space > V0)
     940           0 :     curdiv->space(saved_space, true /* forcing */);
     941          85 :   saved_space = V0;
     942          85 :   tok.next();
     943          85 : }
     944             : 
     945          15 : static void flush_request() // .fl
     946             : {
     947          15 :   while (!tok.is_newline() && !tok.is_eof())
     948           0 :     tok.next();
     949          15 :   if (was_invoked_with_regular_control_character)
     950          15 :     curenv->do_break();
     951          15 :   if (the_output != 0 /* nullptr */)
     952          15 :     the_output->flush();
     953          15 :   tok.next();
     954          15 : }
     955             : 
     956           1 : void macro_diversion::set_diversion_trap(symbol s, vunits n)
     957             : {
     958           1 :   diversion_trap = s;
     959           1 :   diversion_trap_pos = n;
     960           1 : }
     961             : 
     962           0 : void macro_diversion::clear_diversion_trap()
     963             : {
     964           0 :   diversion_trap = NULL_SYMBOL;
     965           0 : }
     966             : 
     967           0 : void top_level_diversion::set_diversion_trap(symbol, vunits)
     968             : {
     969           0 :   error("cannot set diversion trap when not diverting output");
     970           0 : }
     971             : 
     972           0 : void top_level_diversion::clear_diversion_trap()
     973             : {
     974           0 :   error("cannot clear diversion trap when not diverting output");
     975           0 : }
     976             : 
     977           1 : static void diversion_trap() // .dt
     978             : {
     979           1 :   vunits n;
     980           1 :   if (has_arg() && read_vunits(&n, 'v')) {
     981           1 :     symbol s = read_identifier();
     982           1 :     if (!s.is_null())
     983           1 :       curdiv->set_diversion_trap(s, n);
     984             :     else
     985           0 :       curdiv->clear_diversion_trap();
     986             :   }
     987             :   else
     988           0 :     curdiv->clear_diversion_trap();
     989           1 :   skip_line();
     990           1 : }
     991             : 
     992        3207 : static void change_trap() // .ch
     993             : {
     994        3207 :   symbol s = read_identifier(true /* required */);
     995        3207 :   if (!s.is_null()) {
     996        3207 :     vunits x;
     997        3207 :     if (has_arg() && read_vunits(&x, 'v'))
     998         811 :       topdiv->change_trap(s, x);
     999             :     else
    1000        2396 :       topdiv->remove_trap(s);
    1001             :   }
    1002        3207 :   skip_line();
    1003        3207 : }
    1004             : 
    1005           1 : static void print_traps() // .pwh
    1006             : {
    1007           1 :   topdiv->print_traps();
    1008           1 :   skip_line();
    1009           1 : }
    1010             : 
    1011       21791 : static void mark() // .mk
    1012             : {
    1013       21791 :   symbol s = read_identifier();
    1014       21791 :   if (s.is_null())
    1015         852 :     curdiv->marked_place = curdiv->get_vertical_position();
    1016       20939 :   else if (curdiv == topdiv)
    1017        9457 :     set_register(s, nl_reg_contents);
    1018             :   else
    1019       11482 :     set_register(s, curdiv->get_vertical_position().to_units());
    1020       21791 :   skip_line();
    1021       21791 : }
    1022             : 
    1023             : // This is truly bizarre.  It is documented in the SQ manual.
    1024             : 
    1025        1083 : static void return_request() // .rt
    1026             : {
    1027        1083 :   vunits dist = curdiv->marked_place - curdiv->get_vertical_position();
    1028        1083 :   if (has_arg()) {
    1029         225 :     if (tok.ch() == int('-')) { // TODO: grochar
    1030           0 :       tok.next();
    1031           0 :       vunits x;
    1032           0 :       if (read_vunits(&x, 'v'))
    1033           0 :         dist = -x;
    1034             :     }
    1035             :     else {
    1036         225 :       vunits x;
    1037         225 :       if (read_vunits(&x, 'v'))
    1038         225 :         dist = x >= V0 ? x - curdiv->get_vertical_position() : V0;
    1039             :     }
    1040             :   }
    1041        1083 :   if (dist < V0)
    1042         579 :     curdiv->space(dist);
    1043        1083 :   skip_line();
    1044        1083 : }
    1045             : 
    1046       22753 : static void vertical_position_traps() // .vpt
    1047             : {
    1048       22753 :   int n = 0;
    1049       22753 :   if (has_arg() && read_integer(&n))
    1050       21526 :     honor_vertical_position_traps = (n > 0);
    1051             :   else
    1052        1227 :     honor_vertical_position_traps = true;
    1053       22753 :   skip_line();
    1054       22753 : }
    1055             : 
    1056             : class page_offset_reg : public reg {
    1057             : public:
    1058             :   bool get_value(units *);
    1059             :   const char *get_string();
    1060             : };
    1061             : 
    1062          59 : bool page_offset_reg::get_value(units *res)
    1063             : {
    1064          59 :   *res = topdiv->get_page_offset().to_units();
    1065          59 :   return true;
    1066             : }
    1067             : 
    1068         363 : const char *page_offset_reg::get_string()
    1069             : {
    1070         363 :   return i_to_a(topdiv->get_page_offset().to_units());
    1071             : }
    1072             : 
    1073             : class page_length_reg : public reg {
    1074             : public:
    1075             :   bool get_value(units *);
    1076             :   const char *get_string();
    1077             : };
    1078             : 
    1079           0 : bool page_length_reg::get_value(units *res)
    1080             : {
    1081           0 :   *res = topdiv->get_page_length().to_units();
    1082           0 :   return true;
    1083             : }
    1084             : 
    1085        1612 : const char *page_length_reg::get_string()
    1086             : {
    1087        1612 :   return i_to_a(topdiv->get_page_length().to_units());
    1088             : }
    1089             : 
    1090             : class vertical_position_reg : public reg {
    1091             : public:
    1092             :   bool get_value(units *);
    1093             :   const char *get_string();
    1094             : };
    1095             : 
    1096           0 : bool vertical_position_reg::get_value(units *res)
    1097             : {
    1098           0 :   if ((curdiv == topdiv) && (topdiv->before_first_page_status > 0))
    1099           0 :     *res = -1;
    1100             :   else
    1101           0 :     *res = curdiv->get_vertical_position().to_units();
    1102           0 :   return true;
    1103             : }
    1104             : 
    1105        7329 : const char *vertical_position_reg::get_string()
    1106             : {
    1107        7329 :   if ((curdiv == topdiv) && (topdiv->before_first_page_status > 0))
    1108           0 :     return "-1";
    1109             :   else
    1110        7329 :     return i_to_a(curdiv->get_vertical_position().to_units());
    1111             : }
    1112             : 
    1113             : class high_water_mark_reg : public reg {
    1114             : public:
    1115             :   bool get_value(units *);
    1116             :   const char *get_string();
    1117             : };
    1118             : 
    1119           0 : bool high_water_mark_reg::get_value(units *res)
    1120             : {
    1121           0 :   *res = curdiv->get_high_water_mark().to_units();
    1122           0 :   return true;
    1123             : }
    1124             : 
    1125         216 : const char *high_water_mark_reg::get_string()
    1126             : {
    1127         216 :   return i_to_a(curdiv->get_high_water_mark().to_units());
    1128             : }
    1129             : 
    1130             : class distance_to_next_trap_reg : public reg {
    1131             : public:
    1132             :   bool get_value(units *);
    1133             :   const char *get_string();
    1134             : };
    1135             : 
    1136           0 : bool distance_to_next_trap_reg::get_value(units *res)
    1137             : {
    1138           0 :   *res = curdiv->distance_to_next_trap().to_units();
    1139           0 :   return true;
    1140             : }
    1141             : 
    1142        8358 : const char *distance_to_next_trap_reg::get_string()
    1143             : {
    1144        8358 :   return i_to_a(curdiv->distance_to_next_trap().to_units());
    1145             : }
    1146             : 
    1147             : class diversion_name_reg : public reg {
    1148             : public:
    1149             :   const char *get_string();
    1150             : };
    1151             : 
    1152      127653 : const char *diversion_name_reg::get_string()
    1153             : {
    1154      127653 :   return curdiv->get_diversion_name();
    1155             : }
    1156             : 
    1157             : class next_trap_name_reg : public reg {
    1158             : public:
    1159             :   const char *get_string();
    1160             : };
    1161             : 
    1162           4 : const char *next_trap_name_reg::get_string()
    1163             : {
    1164           4 :   return curdiv->get_next_trap_name();
    1165             : }
    1166             : 
    1167             : class page_number_reg : public general_reg {
    1168             : public:
    1169             :   page_number_reg();
    1170             :   bool get_value(units *);
    1171             :   void set_value(units);
    1172             : };
    1173             : 
    1174        1418 : page_number_reg::page_number_reg()
    1175             : {
    1176        1418 : }
    1177             : 
    1178          35 : void page_number_reg::set_value(units n)
    1179             : {
    1180          35 :   topdiv->set_page_number(n);
    1181          35 : }
    1182             : 
    1183        3504 : bool page_number_reg::get_value(units *res)
    1184             : {
    1185        3504 :   *res = topdiv->get_page_number();
    1186        3504 :   return true;
    1187             : }
    1188             : 
    1189             : class next_page_number_reg : public reg {
    1190             : public:
    1191             :   const char *get_string();
    1192             : };
    1193             : 
    1194           1 : const char *next_page_number_reg::get_string()
    1195             : {
    1196           1 :   return i_to_a(topdiv->get_next_page_number());
    1197             : }
    1198             : 
    1199             : class page_ejecting_reg : public reg {
    1200             : public:
    1201             :   const char *get_string();
    1202             : };
    1203             : 
    1204          12 : const char *page_ejecting_reg::get_string()
    1205             : {
    1206          12 :   return i_to_a(topdiv->get_ejecting());
    1207             : }
    1208             : 
    1209             : class constant_vunits_reg : public reg {
    1210             :   vunits *p;
    1211             : public:
    1212             :   constant_vunits_reg(vunits *);
    1213             :   const char *get_string();
    1214             : };
    1215             : 
    1216        2836 : constant_vunits_reg::constant_vunits_reg(vunits *q) : p(q)
    1217             : {
    1218        2836 : }
    1219             : 
    1220          12 : const char *constant_vunits_reg::get_string()
    1221             : {
    1222          12 :   return i_to_a(p->to_units());
    1223             : }
    1224             : 
    1225             : class nl_reg : public variable_reg {
    1226             : public:
    1227             :   nl_reg();
    1228             :   void set_value(units);
    1229             : };
    1230             : 
    1231        1418 : nl_reg::nl_reg() : variable_reg(&nl_reg_contents)
    1232             : {
    1233        1418 : }
    1234             : 
    1235          24 : void nl_reg::set_value(units n)
    1236             : {
    1237          24 :   variable_reg::set_value(n);
    1238             :   // Setting nl to a negative value when the vertical position in
    1239             :   // the top-level diversion is 0 undoes the top of page transition,
    1240             :   // so that the header macro will be called as if the top of page
    1241             :   // transition hasn't happened.  This is used by Larry Wall's
    1242             :   // wrapman program.  Setting before_first_page_status to 2 rather than
    1243             :   // 1, tells top_level_diversion::begin_page not to call
    1244             :   // output_file::begin_page again.
    1245          24 :   if (n < 0 && topdiv->get_vertical_position() == V0)
    1246           0 :     topdiv->before_first_page_status = 2;
    1247          24 : }
    1248             : 
    1249             : class no_space_mode_reg : public reg {
    1250             : public:
    1251             :   bool get_value(units *);
    1252             :   const char *get_string();
    1253             : };
    1254             : 
    1255           0 : bool no_space_mode_reg::get_value(units *val)
    1256             : {
    1257           0 :   *val = curdiv->is_in_no_space_mode;
    1258           0 :   return true;
    1259             : }
    1260             : 
    1261         883 : const char *no_space_mode_reg::get_string()
    1262             : {
    1263         883 :   return curdiv->is_in_no_space_mode ? "1" : "0";
    1264             : }
    1265             : 
    1266        1418 : void init_div_requests()
    1267             : {
    1268        1418 :   init_request("box", box);
    1269        1418 :   init_request("boxa", box_append);
    1270        1418 :   init_request("bp", begin_page);
    1271        1418 :   init_request("ch", change_trap);
    1272        1418 :   init_request("da", divert_append);
    1273        1418 :   init_request("di", divert);
    1274        1418 :   init_request("dt", diversion_trap);
    1275        1418 :   init_request("fl", flush_request);
    1276        1418 :   init_request("mk", mark);
    1277        1418 :   init_request("ne", need_space);
    1278        1418 :   init_request("ns", no_space);
    1279        1418 :   init_request("os", output_saved_vertical_space);
    1280        1418 :   init_request("pl", configure_page_length_request);
    1281        1418 :   init_request("pn", page_number);
    1282        1418 :   init_request("po", configure_page_offset_request);
    1283        1418 :   init_request("pwh", print_traps);
    1284        1418 :   init_request("rs", restore_spacing);
    1285        1418 :   init_request("rt", return_request);
    1286        1418 :   init_request("sp", space_request);
    1287        1418 :   init_request("sv", save_vertical_space);
    1288        1418 :   init_request("vpt", vertical_position_traps);
    1289        1418 :   init_request("wh", when_request);
    1290        1418 :   register_dictionary.define(".a",
    1291        1418 :       new readonly_register(&last_post_line_extra_space));
    1292        1418 :   register_dictionary.define(".d", new vertical_position_reg);
    1293        1418 :   register_dictionary.define(".h", new high_water_mark_reg);
    1294        1418 :   register_dictionary.define(".ne",
    1295        1418 :       new constant_vunits_reg(&needed_space));
    1296        1418 :   register_dictionary.define(".ns", new no_space_mode_reg);
    1297        1418 :   register_dictionary.define(".o", new page_offset_reg);
    1298        1418 :   register_dictionary.define(".p", new page_length_reg);
    1299        1418 :   register_dictionary.define(".pe", new page_ejecting_reg);
    1300        1418 :   register_dictionary.define(".pn", new next_page_number_reg);
    1301        1418 :   register_dictionary.define(".t", new distance_to_next_trap_reg);
    1302        1418 :   register_dictionary.define(".trap", new next_trap_name_reg);
    1303        1418 :   register_dictionary.define(".trunc",
    1304        1418 :       new constant_vunits_reg(&truncated_space));
    1305        1418 :   register_dictionary.define(".vpt",
    1306        1418 :       new readonly_boolean_register(&honor_vertical_position_traps));
    1307        1418 :   register_dictionary.define(".z", new diversion_name_reg);
    1308        1418 :   register_dictionary.define("dl", new variable_reg(&dl_reg_contents));
    1309        1418 :   register_dictionary.define("dn", new variable_reg(&dn_reg_contents));
    1310        1418 :   register_dictionary.define("nl", new nl_reg);
    1311        1418 :   register_dictionary.define("%", new page_number_reg);
    1312        1418 : }
    1313             : 
    1314             : // Local Variables:
    1315             : // fill-column: 72
    1316             : // mode: C++
    1317             : // End:
    1318             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14