LCOV - code coverage report
Current view: top level - libs/libdriver - input.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 535 730 73.3 %
Date: 2026-01-16 17:51:41 Functions: 52 56 92.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-2025 Free Software Foundation, Inc.
       2             : 
       3             :    Written by James Clark (jjc@jclark.com)
       4             :    Major rewrite 2001 by Bernd Warken <groff-bernd.warken-72@web.de>
       5             : 
       6             :    This file is part of groff, the GNU roff text processing system.
       7             : 
       8             :    groff is free software; you can redistribute it and/or modify it
       9             :    under the terms of the GNU General Public License as published by
      10             :    the Free 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
      14             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :    General Public License 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             : 
      22             : /* Description
      23             : 
      24             :    This file implements the parser for the intermediate groff output,
      25             :    see groff_out(5), and does the printout for the given device.
      26             : 
      27             :    All parsed information is processed within the function do_file().
      28             :    A device postprocessor just needs to fill in the methods for the class
      29             :    'printer' (or rather a derived class) without having to worry about
      30             :    the syntax of the intermediate output format.  Consequently, the
      31             :    programming of groff postprocessors is similar to the development of
      32             :    device drivers.
      33             : 
      34             :    The prototyping for this file is done in driver.h (and error.h).
      35             : */
      36             : 
      37             : /* Changes of the 2001 rewrite of this file.
      38             : 
      39             :    The interface to the outside and the handling of the global
      40             :    variables was not changed, but internally many necessary changes
      41             :    were performed.
      42             : 
      43             :    The main aim for this rewrite is to provide a first step toward
      44             :    making groff fully compatible with classical troff without pain.
      45             : 
      46             :    Bugs fixed
      47             :    - Unknown subcommands of 'D' and 'x' are now ignored like in the
      48             :      classical case, but a warning is issued.  This was also
      49             :      implemented for the other commands.
      50             :    - A warning is emitted if 'x stop' is missing.
      51             :    - 'DC' and 'DE' commands didn't position to the right end after
      52             :      drawing (now they do), see discussion below.
      53             :    - So far, 'x stop' was ignored.  Now it terminates the processing
      54             :      of the current intermediate output file like the classical troff.
      55             :    - The command 'c' didn't check correctly on white-space.
      56             :    - The environment stack wasn't suitable for the color extensions
      57             :      (replaced by a class).
      58             :    - The old groff parser could only handle a prologue with the first
      59             :      3 lines having a fixed structure, while classical troff specified
      60             :      the sequence of the first 3 commands without further
      61             :      restrictions.  Now the parser is smart about additional
      62             :      whitespace, comments, and empty lines in the prologue.
      63             :    - The old parser allowed space characters only as syntactical
      64             :      separators, while classical troff had tab characters as well.
      65             :      Now any sequence of tabs and/or spaces is a syntactical
      66             :      separator between commands and/or arguments.
      67             :    - Range checks for numbers implemented.
      68             : 
      69             :    New and improved features
      70             :    - The color commands 'm' and 'DF' are added.
      71             :    - The old color command 'Df' is now converted and delegated to 'DFg'.
      72             :    - The command 'F' is implemented as 'use intended file name'.  It
      73             :      checks whether its argument agrees with the file name used so far,
      74             :      otherwise a warning is issued.  Then the new name is remembered
      75             :      and used for the following error messages.
      76             :    - For the positioning after drawing commands, an alternative, easier
      77             :      scheme is provided, but not yet activated; it can be chosen by
      78             :      undefining the preprocessor macro STUPID_DRAWING_POSITIONING.
      79             :      It extends the rule of the classical troff output language in a
      80             :      logical way instead of the rather strange actual positioning.
      81             :      For details, see the discussion below.
      82             :    - For the 'D' commands that only set the environment, the calling of
      83             :      pr->send_draw() was removed because this doesn't make sense for
      84             :      the 'DF' commands; the (changed) environment is sent with the
      85             :      next command anyway.
      86             :    - Error handling was clearly separated into warnings and fatal.
      87             :    - The error behavior on additional arguments for 'D' and 'x'
      88             :      commands with a fixed number of arguments was changed from being
      89             :      ignored (former groff) to issue a warning and ignore (now), see
      90             :      skip_line_x().  No fatal was chosen because both string and
      91             :      integer arguments can occur.
      92             :    - The gtroff program issues a trailing dummy integer argument for
      93             :      some drawing commands with an odd number of arguments to make the
      94             :      number of arguments even, e.g. the DC and Dt commands; this is
      95             :      honored now.
      96             :    - All D commands with a variable number of args expect an even
      97             :      number of trailing integer arguments, so fatal on error was
      98             :      implemented.
      99             :    - Disable environment stack and the commands '{' and '}' by making
     100             :      them conditional on macro USE_ENV_STACK; actually, this is
     101             :      undefined by default.  There isn't any known application for these
     102             :      features.
     103             : 
     104             :    Cosmetics
     105             :    - Nested 'switch' commands are avoided by using more functions.
     106             :      Dangerous 'fall-through's avoided.
     107             :    - Commands and functions are sorted alphabetically (where possible).
     108             :    - Dynamic arrays/buffers are now implemented as container classes.
     109             :    - Some functions had an ugly return structure; this has been
     110             :      streamlined by using classes.
     111             :    - Use standard C math functions for number handling, so getting rid
     112             :      of differences to '0'.
     113             :    - The macro 'IntArg' has been created for an easier transition
     114             :      to guaranteed 32 bits integers ('int' is enough for GNU, while
     115             :      ANSI only guarantees 'long int' to have a length of 32 bits).
     116             :    - The many usages of type 'int' are differentiated by using 'Char',
     117             :      'bool', and 'IntArg' where appropriate.
     118             :    - To ease the calls of the local utility functions, the parser
     119             :      variables 'current_file', 'npages', and 'current_env'
     120             :      (formerly env) were made global to the file (formerly they were
     121             :      local to the do_file() function)
     122             :    - Various comments were added.
     123             : 
     124             :    TODO
     125             :    - Get rid of the stupid drawing positioning.
     126             :    - Can the 'Dt' command be completely handled by setting environment
     127             :      within do_file() instead of sending to pr?
     128             :    - Integer arguments must be >= 32 bits, use conditional #define.
     129             :    - Add scaling facility for classical device independence and
     130             :      non-groff devices.  Classical troff output had a quasi device
     131             :      independence by scaling the intermediate output to the resolution
     132             :      of the postprocessor device if different from the one specified
     133             :      with 'x T', groff have not.  So implement full quasi device
     134             :      independence, including the mapping of the strange classical
     135             :      devices to the postprocessor device (seems to be reasonably
     136             :      easy).
     137             :    - The external, global pointer variables are not optimally handled.
     138             :      - The global variables 'current_filename',
     139             :        'current_source_filename', and 'current_lineno' are only used for
     140             :        error reporting.  So implement a static class 'Error'
     141             :        ('::' calls).
     142             :      - The global 'device' is the name used during the formatting
     143             :        process; there should be a new variable for the device name used
     144             :        during the postprocessing.
     145             :   - Implement the B-spline drawing 'D~' for all graphical devices.
     146             :   - Make 'environment' a class with an overflow check for its members
     147             :     and a delete method to get rid of delete_current_env().
     148             :   - Implement the 'EnvStack' to use 'new' instead of 'malloc'.
     149             :   - The class definitions of this document could go into a new file.
     150             :   - The comments in this section should go to a 'Changelog' or some
     151             :     'README' file in this directory.
     152             : */
     153             : 
     154             : /*
     155             :   Discussion of the positioning by drawing commands
     156             : 
     157             :   There was some confusion about the positioning of the graphical
     158             :   pointer at the printout after having executed a 'D' command.
     159             :   The classical troff manual of Ossanna & Kernighan specified,
     160             : 
     161             :     'The position after a graphical object has been drawn is
     162             :      at its end; for circles and ellipses, the "end" is at the
     163             :      right side.'
     164             : 
     165             :   From this, it follows that
     166             :   - all open figures (args, splines, and lines) should position at their
     167             :     final point.
     168             :   - all circles and ellipses should position at their right-most point
     169             :     (as if 2 halves had been drawn).
     170             :   - all closed figures apart from circles and ellipses shouldn't change
     171             :     the position because they return to their origin.
     172             :   - all setting commands should not change position because they do not
     173             :     draw any graphical object.
     174             : 
     175             :   In the case of the open figures, this means that the horizontal
     176             :   displacement is the sum of all odd arguments and the vertical offset
     177             :   the sum of all even arguments, called the alternate arguments sum
     178             :   displacement in the following.
     179             : 
     180             :   Unfortunately, groff did not implement this simple rule.  The former
     181             :   documentation in groff_out(5) differed from the source code, and
     182             :   neither of them is compatible with the classical rule.
     183             : 
     184             :   The former groff_out(5) specified to use the alternative arguments
     185             :   sum displacement for calculating the drawing positioning of
     186             :   non-classical commands, including the 'Dt' command (setting-only)
     187             :   and closed polygons.  Applying this to the new groff color commands
     188             :   will lead to disaster.  For their arguments can take large values (>
     189             :   65000).  On low resolution devices, the displacement of such large
     190             :   values will corrupt the display or kill the printer.  So the
     191             :   nonsense specification has come to a natural end anyway.
     192             : 
     193             :   The groff source code, however, had no positioning for the
     194             :   setting-only commands (esp. 'Dt'), the right-end positioning for
     195             :   outlined circles and ellipses, and the alternative argument sum
     196             :   displacement for all other commands (including filled circles and
     197             :   ellipses).
     198             : 
     199             :   The reason why no one seems to have suffered from this mayhem so
     200             :   far is that the graphical objects are usually generated by
     201             :   preprocessors like pic that do not depend on the automatic
     202             :   positioning.  When using the low level '\D' escape sequences or 'D'
     203             :   output commands, the strange positionings can be circumvented by
     204             :   absolute positionings or by tricks like '\Z'.
     205             : 
     206             :   So doing an exorcism on the strange, incompatible displacements might
     207             :   not harm any existing documents, but will make the usage of the
     208             :   graphical escape sequences and commands natural.
     209             : 
     210             :   That's why the rewrite of this file returned to the reasonable,
     211             :   classical specification with its clear end-of-drawing rule that is
     212             :   suitable for all cases.  But a macro STUPID_DRAWING_POSITIONING is
     213             :   provided for testing the funny former behavior.
     214             : 
     215             :   The new rule implies the following behavior.
     216             :   - Setting commands ('Dt', 'Df', 'DF') and polygons ('Dp' and 'DP')
     217             :     do not change position now.
     218             :   - Filled circles and ellipses ('DC' and 'DE') position at their
     219             :     most right point (outlined ones 'Dc' and 'De' did this anyway).
     220             :   - As before, all open graphical objects position to their final
     221             :     drawing point (alternate sum of the command arguments).
     222             : 
     223             : */
     224             : 
     225             : #ifndef STUPID_DRAWING_POSITIONING
     226             : // uncomment next line if all non-classical D commands shall position
     227             : // to the strange alternate sum of args displacement
     228             : #define STUPID_DRAWING_POSITIONING
     229             : #endif
     230             : 
     231             : // Decide whether the commands '{' and '}' for different environments
     232             : // should be used.
     233             : #undef USE_ENV_STACK
     234             : 
     235             : #ifdef HAVE_CONFIG_H
     236             : #include <config.h>
     237             : #endif
     238             : 
     239             : #include <ctype.h> // isdigit()
     240             : #include <errno.h>
     241             : #include <stdio.h> // EOF, FILE, fclose(), fopen(), getc(), stdin,
     242             :                    // ungetc()
     243             : #include <stdlib.h> // strtol()
     244             : #include <string.h> // strcmp(), strlen(), strncmp(), strncpy()
     245             : 
     246             : #include "driver.h"
     247             : #include "device.h"
     248             : 
     249             : 
     250             : /**********************************************************************
     251             :                            local types
     252             :  **********************************************************************/
     253             : 
     254             : // integer type used in the fields of struct environment (see printer.h)
     255             : typedef int EnvInt;
     256             : 
     257             : // integer arguments of groff_out commands, must be >= 32 bits
     258             : typedef int IntArg;
     259             : 
     260             : // color components of groff_out color commands, must be >= 32 bits
     261             : typedef unsigned int ColorArg;
     262             : 
     263             : // Array for IntArg values.
     264             : class IntArray {
     265             :   size_t num_allocated;
     266             :   size_t num_stored;
     267             :   IntArg *data;
     268             : public:
     269             :   IntArray(void);
     270             :   IntArray(const size_t);
     271             :   ~IntArray(void);
     272      465986 :   IntArg operator[](const size_t i) const
     273             :   {
     274      465986 :     if (i >= num_stored)
     275           0 :       fatal("index out of range");
     276      465986 :     return (IntArg) data[i];
     277             :   }
     278             :   void append(IntArg);
     279      227990 :   IntArg *get_data(void) const { return (IntArg *)data; }
     280      610030 :   size_t len(void) const { return num_stored; }
     281             : };
     282             : 
     283             : // Characters read from the input queue.
     284             : class Char {
     285             :   int data;
     286             : public:
     287   323528025 :   Char(void) : data('\0') {}
     288    27517937 :   Char(const int c) : data(c) {}
     289      319904 :   bool operator==(char c) const { return (data == c) ? true : false; }
     290     4642890 :   bool operator==(int c) const { return (data == c) ? true : false; }
     291     8906522 :   bool operator==(const Char c) const
     292     8906522 :                   { return (data == c.data) ? true : false; }
     293        1004 :   bool operator!=(char c) const { return !(*this == c); }
     294     2489132 :   bool operator!=(int c) const { return !(*this == c); }
     295     3466540 :   bool operator!=(const Char c) const { return !(*this == c); }
     296    29641510 :   operator int() const { return (int) data; }
     297         216 :   operator unsigned char() const { return (unsigned char) data; }
     298    10478944 :   operator char() const { return (char) data; }
     299             : };
     300             : 
     301             : // Buffer for string arguments (Char, not char).
     302             : class StringBuf {
     303             :   size_t num_allocated;
     304             :   size_t num_stored;
     305             :   Char *data;                   // not terminated by '\0'
     306             : public:
     307             :   StringBuf(void);              // allocate without storing
     308             :   ~StringBuf(void);
     309             :   void append(const Char);      // append character to 'data'
     310             :   char *make_string(void);      // return new copy of 'data' with '\0'
     311      318684 :   bool is_empty(void) {         // true if none stored
     312      318684 :     return (num_stored > 0) ? false : true;
     313             :   }
     314             :   void reset(void);             // set 'num_stored' to 0
     315             : };
     316             : 
     317             : #ifdef USE_ENV_STACK
     318             : class EnvStack {
     319             :   environment **data;
     320             :   size_t num_allocated;
     321             :   size_t num_stored;
     322             : public:
     323             :   EnvStack(void);
     324             :   ~EnvStack(void);
     325             :   environment *pop(void);
     326             :   void push(environment *e);
     327             : };
     328             : #endif // USE_ENV_STACK
     329             : 
     330             : 
     331             : /**********************************************************************
     332             :                           external variables
     333             :  **********************************************************************/
     334             : 
     335             : // exported as extern by error.h (called from driver.h)
     336             : // needed for error messages (see ../libgroff/error.cpp)
     337             : const char *current_filename = 0; // printable name of the current file
     338             :                                   // printable name of current source file
     339             : const char *current_source_filename = 0;
     340             : int current_lineno = 0;           // current line number of printout
     341             : 
     342             : // exported as extern by device.h;
     343             : const char *device = 0;           // cancel former init with literal
     344             : 
     345             : printer *pr;
     346             : 
     347             : // Note:
     348             : //
     349             : //   We rely on an implementation of the 'new' operator which aborts
     350             : //   gracefully if it can't allocate memory (e.g. from libgroff/new.cpp).
     351             : 
     352             : 
     353             : /**********************************************************************
     354             :                         static local variables
     355             :  **********************************************************************/
     356             : 
     357             : FILE *current_file = 0;         // current input stream for parser
     358             : 
     359             : // npages: number of pages processed so far (including current page),
     360             : //         _not_ the page number in the printout (can be set with 'p').
     361             : int npages = 0;
     362             : 
     363             : const ColorArg
     364             : COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
     365             : 
     366             : const IntArg
     367             : INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
     368             : 
     369             : // parser environment, created and deleted by each run of do_file()
     370             : environment *current_env = 0;
     371             : 
     372             : #ifdef USE_ENV_STACK
     373             : const size_t
     374             : envp_size = sizeof(environment *);
     375             : #endif // USE_ENV_STACK
     376             : 
     377             : 
     378             : /**********************************************************************
     379             :                         function declarations
     380             :  **********************************************************************/
     381             : 
     382             : // utility functions
     383             : ColorArg color_from_Df_command(IntArg);
     384             :                                 // transform old color into new
     385             : void delete_current_env(void);  // delete global var current_env
     386             : void fatal_command(char);       // abort for invalid command
     387             : inline Char get_char(void);     // read next character from input stream
     388             : ColorArg get_color_arg(void);   // read in argument for new color cmds
     389             : IntArray *get_D_fixed_args(const size_t);
     390             :                                 // read in fixed number of integer
     391             :                                 // arguments
     392             : IntArray *get_D_fixed_args_odd_dummy(const size_t);
     393             :                                 // read in a fixed number of integer
     394             :                                 // arguments plus optional dummy
     395             : IntArray *get_D_variable_args(void);
     396             :                                 // variable, even number of int args
     397             : char *get_extended_arg(void);   // argument for 'x X' (several lines)
     398             : IntArg get_integer_arg(void);   // read in next integer argument
     399             : IntArray *get_possibly_integer_args();
     400             :                                 // 0 or more integer arguments
     401             : char *get_string_arg(void);     // read in next string arg, ended by WS
     402             : inline bool is_space_or_tab(const Char);
     403             :                                 // test on space/tab char
     404             : Char next_arg_begin(void);      // skip whitespace on current line
     405             : Char next_command(void);        // go to next command, evt. diff. line
     406             : inline bool odd(const int);     // test if integer is odd
     407             : void position_to_end_of_args(const IntArray * const);
     408             :                                 // positioning after drawing
     409             : void remember_filename(const char *);
     410             :                                 // set global current_filename
     411             : void remember_source_filename(const char *);
     412             :                                 // set global current_source_filename
     413             : void send_draw(const Char, const IntArray * const);
     414             :                                 // call pr->draw
     415             : void skip_line(void);           // unconditionally skip to next line
     416             : bool skip_line_checked(void);   // skip line, false if args are left
     417             : void skip_line_fatal(void);     // skip line, fatal if args are left
     418             : void skip_line_warn(void);      // skip line, warn if args are left
     419             : void skip_line_D(void);         // skip line in D commands
     420             : void skip_line_x(void);         // skip line in x commands
     421             : void skip_to_end_of_line(void); // skip to the end of the current line
     422             : inline void unget_char(const Char);
     423             :                                 // restore character onto input
     424             : 
     425             : // parser subcommands
     426             : void parse_color_command(color *);
     427             :                                 // color sub(sub)commands m and DF
     428             : void parse_D_command(void);     // graphical subcommands
     429             : bool parse_x_command(void);     // device control commands
     430             : 
     431             : 
     432             : /**********************************************************************
     433             :                          class methods
     434             :  **********************************************************************/
     435             : 
     436             : #ifdef USE_ENV_STACK
     437             : EnvStack::EnvStack(void)
     438             : {
     439             :   num_allocated = 4;
     440             :   // allocate pointer to array of num_allocated pointers to environment
     441             :   data = (environment **)malloc(envp_size * num_allocated);
     442             :   if (data == 0)
     443             :     fatal("could not allocate environment data");
     444             :   num_stored = 0;
     445             : }
     446             : 
     447             : EnvStack::~EnvStack(void)
     448             : {
     449             :   for (size_t i = 0; i < num_stored; i++)
     450             :     delete data[i];
     451             :   free(data);
     452             : }
     453             : 
     454             : // return top element from stack and decrease stack pointer
     455             : //
     456             : // the calling function must take care of properly deleting the result
     457             : environment *
     458             : EnvStack::pop(void)
     459             : {
     460             :   num_stored--;
     461             :   environment *result = data[num_stored];
     462             :   data[num_stored] = 0;
     463             :   return result;
     464             : }
     465             : 
     466             : // copy argument and push this onto the stack
     467             : void
     468             : EnvStack::push(environment *e)
     469             : {
     470             :   environment *e_copy = new environment;
     471             :   if (num_stored >= num_allocated) {
     472             :     environment **old_data = data;
     473             :     num_allocated *= 2;
     474             :     data = (environment **)malloc(envp_size * num_allocated);
     475             :     if (data == 0)
     476             :       fatal("could not allocate data");
     477             :     for (size_t i = 0; i < num_stored; i++)
     478             :       data[i] = old_data[i];
     479             :     free(old_data);
     480             :   }
     481             :   e_copy->col = new color;
     482             :   e_copy->fill = new color;
     483             :   *e_copy->col = *e->col;
     484             :   *e_copy->fill = *e->fill;
     485             :   e_copy->fontno = e->fontno;
     486             :   e_copy->height = e->height;
     487             :   e_copy->hpos = e->hpos;
     488             :   e_copy->size = e->size;
     489             :   e_copy->slant = e->slant;
     490             :   e_copy->vpos = e->vpos;
     491             :   data[num_stored] = e_copy;
     492             :   num_stored++;
     493             : }
     494             : #endif // USE_ENV_STACK
     495             : 
     496      154384 : IntArray::IntArray(void)
     497             : {
     498      154384 :   num_allocated = 4;
     499      154384 :   data = new IntArg[num_allocated];
     500      154384 :   num_stored = 0;
     501      154384 : }
     502             : 
     503      195052 : IntArray::IntArray(const size_t n)
     504             : {
     505      195052 :   if (n <= 0)
     506           0 :     fatal("number of integers to be allocated must be > 0");
     507      195052 :   num_allocated = n;
     508      195052 :   data = new IntArg[num_allocated];
     509      195052 :   num_stored = 0;
     510      195052 : }
     511             : 
     512      698872 : IntArray::~IntArray(void)
     513             : {
     514      349436 :   delete[] data;
     515      349436 : }
     516             : 
     517             : void
     518      587458 : IntArray::append(IntArg x)
     519             : {
     520      587458 :   if (num_stored >= num_allocated) {
     521       32706 :     IntArg *old_data = data;
     522       32706 :     num_allocated *= 2;
     523       32706 :     data = new IntArg[num_allocated];
     524      163658 :     for (size_t i = 0; i < num_stored; i++)
     525      130952 :       data[i] = old_data[i];
     526       32706 :     delete[] old_data;
     527             :   }
     528      587458 :   data[num_stored] = x;
     529      587458 :   num_stored++;
     530      587458 : }
     531             : 
     532     2489132 : StringBuf::StringBuf(void)
     533             : {
     534     2489132 :   num_stored = 0;
     535     2489132 :   num_allocated = 128;
     536   321098028 :   data = new Char[num_allocated];
     537     2489132 : }
     538             : 
     539     4978264 : StringBuf::~StringBuf(void)
     540             : {
     541     2489132 :   delete[] data;
     542     2489132 : }
     543             : 
     544             : void
     545    10478942 : StringBuf::append(const Char c)
     546             : {
     547    10478942 :   if (num_stored >= num_allocated) {
     548          25 :     Char *old_data = data;
     549          25 :     num_allocated *= 2;
     550       17689 :     data = new Char[num_allocated];
     551        8857 :     for (size_t i = 0; i < num_stored; i++)
     552        8832 :       data[i] = old_data[i];
     553          25 :     delete[] old_data;
     554             :   }
     555    10478942 :   data[num_stored] = c;
     556    10478942 :   num_stored++;
     557    10478942 : }
     558             : 
     559             : char *
     560     2653432 : StringBuf::make_string(void)
     561             : {
     562     2653432 :   char *result = new char[num_stored + 1];
     563    13132374 :   for (size_t i = 0; i < num_stored; i++)
     564    10478942 :     result[i] = (char) data[i];
     565     2653432 :   result[num_stored] = '\0';
     566     2653432 :   return result;
     567             : }
     568             : 
     569             : void
     570      318684 : StringBuf::reset(void)
     571             : {
     572      318684 :   num_stored = 0;
     573      318684 : }
     574             : 
     575             : /**********************************************************************
     576             :                         utility functions
     577             :  **********************************************************************/
     578             : 
     579             : //////////////////////////////////////////////////////////////////////
     580             : /* color_from_Df_command:
     581             :    Process the gray shade setting command Df.
     582             : 
     583             :    Transform Df style color into DF style color.
     584             :    Df color: 0-1000, 0 is white
     585             :    DF color: 0-65536, 0 is black
     586             : 
     587             :    The Df command is obsoleted by command DFg, but kept for
     588             :    compatibility.
     589             : */
     590             : ColorArg
     591           0 : color_from_Df_command(IntArg Df_gray)
     592             : {
     593           0 :   return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
     594             : }
     595             : 
     596             : //////////////////////////////////////////////////////////////////////
     597             : /* delete_current_env():
     598             :    Delete global variable current_env and its pointer members.
     599             : 
     600             :    This should be a class method of environment.
     601             : */
     602        1004 : void delete_current_env(void)
     603             : {
     604        1004 :   delete current_env->col;
     605        1004 :   delete current_env->fill;
     606        1004 :   delete current_env;
     607        1004 :   current_env = 0;
     608        1004 : }
     609             : 
     610             : //////////////////////////////////////////////////////////////////////
     611             : /* fatal_command():
     612             :    Emit error message about invalid command and abort.
     613             : */
     614             : void
     615           0 : fatal_command(char command)
     616             : {
     617           0 :   fatal("'%1' command invalid before first 'p' command", command);
     618           0 : }
     619             : 
     620             : //////////////////////////////////////////////////////////////////////
     621             : /* get_char():
     622             :    Retrieve the next character from the input queue.
     623             : 
     624             :    Return: The retrieved character (incl. EOF), converted to Char.
     625             : */
     626             : inline Char
     627    18611415 : get_char(void)
     628             : {
     629    18611415 :   return (Char) getc(current_file);
     630             : }
     631             : 
     632             : //////////////////////////////////////////////////////////////////////
     633             : /* get_color_arg():
     634             :    Retrieve an argument suitable for the color commands m and DF.
     635             : 
     636             :    Return: The retrieved color argument.
     637             : */
     638             : ColorArg
     639      239278 : get_color_arg(void)
     640             : {
     641      239278 :   IntArg x = get_integer_arg();
     642      239278 :   if (x < 0 || x > (IntArg)COLORARG_MAX) {
     643           0 :     error("color component argument out of range");
     644           0 :     x = 0;
     645             :   }
     646      239278 :   return (ColorArg) x;
     647             : }
     648             : 
     649             : //////////////////////////////////////////////////////////////////////
     650             : /* get_D_fixed_args():
     651             :    Get a fixed number of integer arguments for D commands.
     652             : 
     653             :    Fatal if wrong number of arguments.
     654             :    Too many arguments on the line raise a warning.
     655             :    A line skip is done.
     656             : 
     657             :    number: In-parameter, the number of arguments to be retrieved.
     658             :    ignore: In-parameter, ignore next argument -- GNU troff always emits
     659             :            pairs of parameters for 'D' extensions added by groff.
     660             :            Default is 'false'.
     661             : 
     662             :    Return: New IntArray containing the arguments.
     663             : */
     664             : IntArray *
     665       73606 : get_D_fixed_args(const size_t number)
     666             : {
     667       73606 :   if (number <= 0)
     668           0 :     fatal("requested number of arguments must be > 0");
     669       73606 :   IntArray *args = new IntArray(number);
     670      220934 :   for (size_t i = 0; i < number; i++)
     671      147328 :     args->append(get_integer_arg());
     672       73606 :   skip_line_D();
     673       73606 :   return args;
     674             : }
     675             : 
     676             : //////////////////////////////////////////////////////////////////////
     677             : /* get_D_fixed_args_odd_dummy():
     678             :    Get a fixed number of integer arguments for D commands and optionally
     679             :    ignore a dummy integer argument if the requested number is odd.
     680             : 
     681             :    The gtroff program adds a dummy argument to some commands to get
     682             :    an even number of arguments.
     683             :    Error if the number of arguments differs from the scheme above.
     684             :    A line skip is done.
     685             : 
     686             :    number: In-parameter, the number of arguments to be retrieved.
     687             : 
     688             :    Return: New IntArray containing the arguments.
     689             : */
     690             : IntArray *
     691      121446 : get_D_fixed_args_odd_dummy(const size_t number)
     692             : {
     693      121446 :   if (number <= 0)
     694           0 :     fatal("requested number of arguments must be > 0");
     695      121446 :   IntArray *args = new IntArray(number);
     696      242892 :   for (size_t i = 0; i < number; i++)
     697      121446 :     args->append(get_integer_arg());
     698      121446 :   if (odd(number)) {
     699      121446 :     IntArray *a = get_possibly_integer_args();
     700      121446 :     if (a->len() > 1)
     701           0 :       error("too many arguments");
     702      121446 :     delete a;
     703             :   }
     704      121446 :   skip_line_D();
     705      121446 :   return args;
     706             : }
     707             : 
     708             : //////////////////////////////////////////////////////////////////////
     709             : /* get_D_variable_args():
     710             :    Get a variable even number of integer arguments for D commands.
     711             : 
     712             :    Get as many integer arguments as possible from the rest of the
     713             :    current line.
     714             :    - The arguments are separated by an arbitrary sequence of space or
     715             :      tab characters.
     716             :    - A comment, a newline, or EOF indicates the end of processing.
     717             :    - Error on non-digit characters different from these.
     718             :    - A final line skip is performed (except for EOF).
     719             : 
     720             :    Return: New IntArray of the retrieved arguments.
     721             : */
     722             : IntArray *
     723       32938 : get_D_variable_args()
     724             : {
     725       32938 :   IntArray *args = get_possibly_integer_args();
     726       32938 :   size_t n = args->len();
     727       32938 :   if (n <= 0)
     728           0 :     error("no arguments found");
     729       32938 :   if (odd(n))
     730           0 :     error("even number of arguments expected");
     731       32938 :   skip_line_D();
     732       32938 :   return args;
     733             : }
     734             : 
     735             : //////////////////////////////////////////////////////////////////////
     736             : /* get_extended_arg():
     737             :    Retrieve extended arg for 'x X' command.
     738             : 
     739             :    - Skip leading spaces and tabs, error on EOL or newline.
     740             :    - Return everything before the next NL or EOF ('#' is not a comment);
     741             :      as long as the following line starts with '+' this is returned
     742             :      as well, with the '+' replaced by a newline.
     743             :    - Final line skip is always performed.
     744             : 
     745             :    Return: Allocated (new) string of retrieved text argument.
     746             : */
     747             : char *
     748       74254 : get_extended_arg(void)
     749             : {
     750      148508 :   StringBuf buf = StringBuf();
     751       74254 :   Char c = next_arg_begin();
     752     1543068 :   while ((int) c != EOF) {
     753     1543068 :     if ((int) c == '\n') {
     754       74254 :       current_lineno++;
     755       74254 :       c = get_char();
     756       74254 :       if ((int) c == '+')
     757           0 :         buf.append((Char) '\n');
     758             :       else {
     759       74254 :         unget_char(c);          // first character of next line
     760       74254 :         break;
     761             :       }
     762             :     }
     763             :     else
     764     1468814 :       buf.append(c);
     765     1468814 :     c = get_char();
     766             :   }
     767      148508 :   return buf.make_string();
     768             : }
     769             : 
     770             : //////////////////////////////////////////////////////////////////////
     771             : /* get_integer_arg(): Retrieve integer argument.
     772             : 
     773             :    Skip leading spaces and tabs, collect an optional '-' and all
     774             :    following decimal digits (at least one) up to the next non-digit,
     775             :    which is restored onto the input queue.
     776             : 
     777             :    Fatal error on all other situations.
     778             : 
     779             :    Return: Retrieved integer.
     780             : */
     781             : IntArg
     782     1846288 : get_integer_arg(void)
     783             : {
     784     1846288 :   StringBuf buf = StringBuf();
     785     1846288 :   Char c = next_arg_begin();
     786     1846288 :   if ((int) c == '-') {
     787        7448 :     buf.append(c);
     788        7448 :     c = get_char();
     789             :   }
     790     1846288 :   if (!isdigit((int) c))
     791           0 :     fatal("integer argument expected");
     792     8535331 :   while (isdigit((int) c)) {
     793     6689043 :     buf.append(c);
     794     6689043 :     c = get_char();
     795             :   }
     796             :   // c is not a digit
     797     1846288 :   unget_char(c);
     798     1846288 :   char *s = buf.make_string();
     799     1846288 :   errno = 0;
     800     1846288 :   long int number = strtol(s, 0, 10);
     801     1846288 :   if (errno != 0
     802     1846288 :       || number > INTARG_MAX || number < -INTARG_MAX) {
     803           0 :     error("integer argument too large");
     804           0 :     number = 0;
     805             :   }
     806     1846288 :   delete[] s;
     807     3692576 :   return (IntArg) number;
     808             : }
     809             : 
     810             : //////////////////////////////////////////////////////////////////////
     811             : /* get_possibly_integer_args():
     812             :    Parse the rest of the input line as a list of integer arguments.
     813             : 
     814             :    Get as many integer arguments as possible from the rest of the
     815             :    current line, even none.
     816             :    - The arguments are separated by an arbitrary sequence of space or
     817             :      tab characters.
     818             :    - A comment, a newline, or EOF indicates the end of processing.
     819             :    - Error on non-digit characters different from these.
     820             :    - No line skip is performed.
     821             : 
     822             :    Return: New IntArray of the retrieved arguments.
     823             : */
     824             : IntArray *
     825      154384 : get_possibly_integer_args()
     826             : {
     827      154384 :   bool done = false;
     828      154384 :   StringBuf buf = StringBuf();
     829      154384 :   Char c = get_char();
     830      154384 :   IntArray *args = new IntArray();
     831      473068 :   while (!done) {
     832      318684 :     buf.reset();
     833      637368 :     while (is_space_or_tab(c))
     834      318684 :       c = get_char();
     835      318684 :     if (c == '-') {
     836       33460 :       Char c1 = get_char();
     837       33460 :       if (isdigit((int) c1)) {
     838       33460 :         buf.append(c);
     839       33460 :         c = c1;
     840             :       }
     841             :       else
     842           0 :         unget_char(c1);
     843             :     }
     844     1033387 :     while (isdigit((int) c)) {
     845      714703 :       buf.append(c);
     846      714703 :       c = get_char();
     847             :     }
     848      318684 :     if (!buf.is_empty()) {
     849      318684 :       char *s = buf.make_string();
     850      318684 :       errno = 0;
     851      318684 :       long int x = strtol(s, 0, 10);
     852      318684 :       if (errno
     853      318684 :           || x > INTARG_MAX || x < -INTARG_MAX) {
     854           0 :         error("invalid integer argument, set to 0");
     855           0 :         x = 0;
     856             :       }
     857      318684 :       args->append((IntArg) x);
     858      318684 :       delete[] s;
     859             :     }
     860             :     // Here, c is not a digit.
     861             :     // Terminate on comment, end of line, or end of file, while
     862             :     // space or tab indicate continuation; otherwise error.
     863      318684 :     switch((int) c) {
     864           0 :     case '#':
     865           0 :       skip_to_end_of_line();
     866           0 :       done = true;
     867           0 :       break;
     868      154384 :     case '\n':
     869      154384 :       done = true;
     870      154384 :       unget_char(c);
     871      154384 :       break;
     872           0 :     case EOF:
     873           0 :       done = true;
     874           0 :       break;
     875      164300 :     case ' ':
     876             :     case '\t':
     877      164300 :       break;
     878           0 :     default:
     879           0 :       error("integer argument expected");
     880           0 :       done = true;
     881           0 :       break;
     882             :     }
     883             :   }
     884      308768 :   return args;
     885             : }
     886             : 
     887             : //////////////////////////////////////////////////////////////////////
     888             : /* get_string_arg():
     889             :    Retrieve string arg.
     890             : 
     891             :    - Skip leading spaces and tabs; error on EOL or newline.
     892             :    - Return all following characters before the next space, tab,
     893             :      newline, or EOF character (in-word '#' is not a comment character).
     894             :    - The terminating space, tab, newline, or EOF character is restored
     895             :      onto the input queue, so no line skip.
     896             : 
     897             :    Return: Retrieved string as char *, allocated by 'new'.
     898             : */
     899             : char *
     900      414206 : get_string_arg(void)
     901             : {
     902      828412 :   StringBuf buf = StringBuf();
     903      414206 :   Char c = next_arg_begin();
     904     1979680 :   while (!is_space_or_tab(c)
     905     1979680 :          && c != Char('\n') && c != Char(EOF)) {
     906     1565474 :     buf.append(c);
     907     1565474 :     c = get_char();
     908             :   }
     909      414206 :   unget_char(c);                // restore whitespace
     910      828412 :   return buf.make_string();
     911             : }
     912             : 
     913             : //////////////////////////////////////////////////////////////////////
     914             : /* is_space_or_tab():
     915             :    Test a character if it is a space or tab.
     916             : 
     917             :    c: In-parameter, character to be tested.
     918             : 
     919             :    Return: True, if c is a space or tab character, false otherwise.
     920             : */
     921             : inline bool
     922     2918651 : is_space_or_tab(const Char c)
     923             : {
     924     2918651 :   return (c == Char(' ') || c == Char('\t')) ? true : false;
     925             : }
     926             : 
     927             : //////////////////////////////////////////////////////////////////////
     928             : /* next_arg_begin():
     929             :    Return first character of next argument.
     930             : 
     931             :    Skip space and tab characters; error on newline or EOF.
     932             : 
     933             :    Return: The first character different from these (including '#').
     934             : */
     935             : Char
     936     2743772 : next_arg_begin(void)
     937             : {
     938     2743772 :   Char c;
     939             :   while (1) {
     940     3582668 :     c = get_char();
     941     3582668 :     switch ((int) c) {
     942      838896 :     case ' ':
     943             :     case '\t':
     944      838896 :       break;
     945           0 :     case '\n':
     946             :     case EOF:
     947           0 :       error("missing argument");
     948           0 :       return c;
     949     2743772 :     default:                    // first essential character
     950     2743772 :       return c;
     951             :     }
     952             :   }
     953             : }
     954             : 
     955             : //////////////////////////////////////////////////////////////////////
     956             : /* next_command():
     957             :    Find the first character of the next command.
     958             : 
     959             :    Skip spaces, tabs, comments (introduced by #), and newlines.
     960             : 
     961             :    Return: The first character different from these (including EOF).
     962             : */
     963             : Char
     964     2156622 : next_command(void)
     965             : {
     966     2156622 :   Char c;
     967             :   while (1) {
     968     3700879 :     c = get_char();
     969     3700879 :     switch ((int) c) {
     970           0 :     case ' ':
     971             :     case '\t':
     972           0 :       break;
     973     1544256 :     case '\n':
     974     1544256 :       current_lineno++;
     975     1544256 :       break;
     976           1 :     case '#':                   // comment
     977           1 :       skip_line();
     978           1 :       break;
     979     2156622 :     default:                    // EOF or first essential character
     980     2156622 :       return c;
     981             :     }
     982             :   }
     983             : }
     984             : 
     985             : //////////////////////////////////////////////////////////////////////
     986             : /* odd():
     987             :    Test whether argument is an odd number.
     988             : 
     989             :    n: In-parameter, the integer to be tested.
     990             : 
     991             :    Return: True if odd, false otherwise.
     992             : */
     993             : inline bool
     994      154384 : odd(const int n)
     995             : {
     996      154384 :   return ((n & 1) == 1) ? true : false;
     997             : }
     998             : 
     999             : //////////////////////////////////////////////////////////////////////
    1000             : /* position_to_end_of_args():
    1001             :    Move graphical pointer to end of drawn figure.
    1002             : 
    1003             :    This is used by the D commands that draw open geometrical figures.
    1004             :    The algorithm simply sums up all horizontal displacements (arguments
    1005             :    with even number) for the horizontal component.  Similarly, the
    1006             :    vertical component is the sum of the odd arguments.
    1007             : 
    1008             :    args: In-parameter, the arguments of a former drawing command.
    1009             : */
    1010             : void
    1011      227656 : position_to_end_of_args(const IntArray * const args)
    1012             : {
    1013             :   size_t i;
    1014      227656 :   const size_t n = args->len();
    1015      521133 :   for (i = 0; i < n; i += 2)
    1016      293477 :     current_env->hpos += (*args)[i];
    1017      399831 :   for (i = 1; i < n; i += 2)
    1018      172175 :     current_env->vpos += (*args)[i];
    1019      227656 : }
    1020             : 
    1021             : //////////////////////////////////////////////////////////////////////
    1022             : /* remember_filename():
    1023             :    Set global variable current_filename.
    1024             : 
    1025             :    The actual filename is stored in current_filename.  This is used by
    1026             :    the postprocessors, expecting the name "<standard input>" for stdin.
    1027             : 
    1028             :    filename: In-out-parameter; is changed to the new value also.
    1029             : */
    1030             : void
    1031        1071 : remember_filename(const char *filename)
    1032             : {
    1033             :   char *fname;
    1034        1071 :   if (strcmp(filename, "-") == 0)
    1035        1071 :     fname = (char *)"<standard input>";
    1036             :   else
    1037           0 :     fname = (char *)filename;
    1038        1071 :   size_t len = strlen(fname) + 1;
    1039        1071 :   if (current_filename != 0)
    1040           0 :     free((char *)current_filename);
    1041        1071 :   current_filename = (const char *)malloc(len);
    1042        1071 :   if (current_filename == 0)
    1043           0 :     fatal("can't malloc space for filename");
    1044        1071 :   strncpy((char *)current_filename, (char *)fname, len);
    1045        1071 : }
    1046             : 
    1047             : //////////////////////////////////////////////////////////////////////
    1048             : /* remember_source_filename():
    1049             :    Set global variable current_source_filename.
    1050             : 
    1051             :    The actual filename is stored in current_filename.  This is used by
    1052             :    the postprocessors, expecting the name "<standard input>" for stdin.
    1053             : 
    1054             :    filename: In-out-parameter; is changed to the new value also.
    1055             : */
    1056             : void
    1057         250 : remember_source_filename(const char *filename)
    1058             : {
    1059             :   char *fname;
    1060         250 :   if (strcmp(filename, "-") == 0)
    1061          17 :     fname = (char *)"<standard input>";
    1062             :   else
    1063         233 :     fname = (char *)filename;
    1064         250 :   size_t len = strlen(fname) + 1;
    1065         250 :   if (current_source_filename != 0)
    1066         233 :     free((char *)current_source_filename);
    1067         250 :   current_source_filename = (const char *)malloc(len);
    1068         250 :   if (current_source_filename == 0)
    1069           0 :     fatal("can't malloc space for filename");
    1070         250 :   strncpy((char *)current_source_filename, (char *)fname, len);
    1071         250 : }
    1072             : 
    1073             : //////////////////////////////////////////////////////////////////////
    1074             : /* send_draw():
    1075             :    Call draw method of printer class.
    1076             : 
    1077             :    subcmd: Letter of actual D subcommand.
    1078             :    args: Array of integer arguments of actual D subcommand.
    1079             : */
    1080             : void
    1081      227990 : send_draw(const Char subcmd, const IntArray * const args)
    1082             : {
    1083      227990 :   EnvInt n = (EnvInt) args->len();
    1084      227990 :   pr->draw((int) subcmd, (IntArg *)args->get_data(), n, current_env);
    1085      227990 : }
    1086             : 
    1087             : //////////////////////////////////////////////////////////////////////
    1088             : /* skip_line():
    1089             :    Go to next line within the input queue.
    1090             : 
    1091             :    Skip the rest of the current line, including the newline character.
    1092             :    The global variable current_lineno is adjusted.
    1093             :    No errors are raised.
    1094             : */
    1095             : void
    1096           1 : skip_line(void)
    1097             : {
    1098           1 :   Char c = get_char();
    1099             :   while (1) {
    1100           1 :     if (c == '\n') {
    1101           1 :       current_lineno++;
    1102           1 :       break;
    1103             :     }
    1104           0 :     if (c == EOF)
    1105           0 :       break;
    1106           0 :     c = get_char();
    1107             :   }
    1108           1 : }
    1109             : 
    1110             : //////////////////////////////////////////////////////////////////////
    1111             : /* skip_line_checked ():
    1112             :    Check that there aren't any arguments left on the rest of the line,
    1113             :    then skip line.
    1114             : 
    1115             :    Spaces, tabs, and a comment are allowed before newline or EOF.
    1116             :    All other characters raise an error.
    1117             : */
    1118             : bool
    1119      301603 : skip_line_checked(void)
    1120             : {
    1121      301603 :   bool ok = true;
    1122      301603 :   Char c = get_char();
    1123      301603 :   while (is_space_or_tab(c))
    1124           0 :     c = get_char();
    1125      301603 :   switch((int) c) {
    1126           0 :   case '#':                     // comment
    1127           0 :     skip_line();
    1128           0 :     break;
    1129      301581 :   case '\n':
    1130      301581 :     current_lineno++;
    1131      301581 :     break;
    1132          22 :   case EOF:
    1133          22 :     break;
    1134           0 :   default:
    1135           0 :     ok = false;
    1136           0 :     skip_line();
    1137           0 :     break;
    1138             :   }
    1139      301603 :   return ok;
    1140             : }
    1141             : 
    1142             : //////////////////////////////////////////////////////////////////////
    1143             : /* skip_line_fatal ():
    1144             :    Fatal error if arguments left, otherwise skip line.
    1145             : 
    1146             :    Spaces, tabs, and a comment are allowed before newline or EOF.
    1147             :    All other characters trigger the error.
    1148             : */
    1149             : void
    1150           0 : skip_line_fatal(void)
    1151             : {
    1152           0 :   bool ok = skip_line_checked();
    1153           0 :   if (!ok) {
    1154           0 :     current_lineno--;
    1155           0 :     error("too many arguments");
    1156           0 :     current_lineno++;
    1157             :   }
    1158           0 : }
    1159             : 
    1160             : //////////////////////////////////////////////////////////////////////
    1161             : /* skip_line_warn ():
    1162             :    Skip line, but warn if arguments are left on actual line.
    1163             : 
    1164             :    Spaces, tabs, and a comment are allowed before newline or EOF.
    1165             :    All other characters raise a warning
    1166             : */
    1167             : void
    1168      301603 : skip_line_warn(void)
    1169             : {
    1170      301603 :   bool ok = skip_line_checked();
    1171      301603 :   if (!ok) {
    1172           0 :     current_lineno--;
    1173           0 :     warning("too many arguments on current line");
    1174           0 :     current_lineno++;
    1175             :   }
    1176      301603 : }
    1177             : 
    1178             : //////////////////////////////////////////////////////////////////////
    1179             : /* skip_line_D ():
    1180             :    Skip line in 'D' commands.
    1181             : 
    1182             :    Decide whether in case of an additional argument a fatal error is
    1183             :    raised (the documented classical behavior), only a warning is
    1184             :    issued, or the line is just skipped (former groff behavior).
    1185             :    Actually decided for the warning.
    1186             : */
    1187             : void
    1188      227990 : skip_line_D(void)
    1189             : {
    1190      227990 :   skip_line_warn();
    1191             :   // or: skip_line_fatal();
    1192             :   // or: skip_line();
    1193      227990 : }
    1194             : 
    1195             : //////////////////////////////////////////////////////////////////////
    1196             : /* skip_line_x ():
    1197             :    Skip line in 'x' commands.
    1198             : 
    1199             :    Decide whether in case of an additional argument a fatal error is
    1200             :    raised (the documented classical behavior), only a warning is
    1201             :    issued, or the line is just skipped (former groff behavior).
    1202             :    Actually decided for the warning.
    1203             : */
    1204             : void
    1205       73613 : skip_line_x(void)
    1206             : {
    1207       73613 :   skip_line_warn();
    1208             :   // or: skip_line_fatal();
    1209             :   // or: skip_line();
    1210       73613 : }
    1211             : 
    1212             : //////////////////////////////////////////////////////////////////////
    1213             : /* skip_to_end_of_line():
    1214             :    Go to the end of the current line.
    1215             : 
    1216             :    Skip the rest of the current line, excluding the newline character.
    1217             :    The global variable current_lineno is not changed.
    1218             :    No errors are raised.
    1219             : */
    1220             : void
    1221           0 : skip_to_end_of_line(void)
    1222             : {
    1223           0 :   Char c = get_char();
    1224             :   while (1) {
    1225           0 :     if (c == '\n') {
    1226           0 :       unget_char(c);
    1227           0 :       return;
    1228             :     }
    1229           0 :     if (c == EOF)
    1230           0 :       return;
    1231           0 :     c = get_char();
    1232             :   }
    1233             : }
    1234             : 
    1235             : //////////////////////////////////////////////////////////////////////
    1236             : /* unget_char(c):
    1237             :    Restore character c onto input queue.
    1238             : 
    1239             :    Write a character back onto the input stream.
    1240             :    EOF is gracefully handled.
    1241             : 
    1242             :    c: In-parameter; character to be pushed onto the input queue.
    1243             : */
    1244             : inline void
    1245     2489132 : unget_char(const Char c)
    1246             : {
    1247     2489132 :   if (c != EOF) {
    1248     2489110 :     int ch = (int) c;
    1249     2489110 :     if (ungetc(ch, current_file) == EOF)
    1250           0 :       fatal("could not unget character");
    1251             :   }
    1252     2489132 : }
    1253             : 
    1254             : 
    1255             : /**********************************************************************
    1256             :                        parser subcommands
    1257             :  **********************************************************************/
    1258             : 
    1259             : //////////////////////////////////////////////////////////////////////
    1260             : /* parse_color_command:
    1261             :    Process the commands m and DF, but not Df.
    1262             : 
    1263             :    col: In-out-parameter; the color object to be set, must have
    1264             :         been initialized before.
    1265             : */
    1266             : void
    1267      114598 : parse_color_command(color *col)
    1268             : {
    1269      114598 :   ColorArg gray = 0;
    1270      114598 :   ColorArg red = 0, green = 0, blue = 0;
    1271      114598 :   ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
    1272      114598 :   Char subcmd = next_arg_begin();
    1273      114598 :   switch((int) subcmd) {
    1274           0 :   case 'c':                     // DFc or mc: CMY
    1275           0 :     cyan = get_color_arg();
    1276           0 :     magenta = get_color_arg();
    1277           0 :     yellow = get_color_arg();
    1278           0 :     col->set_cmy(cyan, magenta, yellow);
    1279           0 :     break;
    1280       34672 :   case 'd':                     // DFd or md: set default color
    1281       34672 :     col->set_default();
    1282       34672 :     break;
    1283         250 :   case 'g':                     // DFg or mg: gray
    1284         250 :     gray = get_color_arg();
    1285         250 :     col->set_gray(gray);
    1286         250 :     break;
    1287           0 :   case 'k':                     // DFk or mk: CMYK
    1288           0 :     cyan = get_color_arg();
    1289           0 :     magenta = get_color_arg();
    1290           0 :     yellow = get_color_arg();
    1291           0 :     black = get_color_arg();
    1292           0 :     col->set_cmyk(cyan, magenta, yellow, black);
    1293           0 :     break;
    1294       79676 :   case 'r':                     // DFr or mr: RGB
    1295       79676 :     red = get_color_arg();
    1296       79676 :     green = get_color_arg();
    1297       79676 :     blue = get_color_arg();
    1298       79676 :     col->set_rgb(red, green, blue);
    1299       79676 :     break;
    1300           0 :   default:
    1301           0 :     error("invalid color scheme '%1'", (int) subcmd);
    1302           0 :     break;
    1303             :   } // end of color subcommands
    1304      114598 : }
    1305             : 
    1306             : //////////////////////////////////////////////////////////////////////
    1307             : /* parse_D_command():
    1308             :    Parse the subcommands of graphical command D.
    1309             : 
    1310             :    This is the part of the do_file() parser that scans the graphical
    1311             :    subcommands.
    1312             :    - Error on lacking or wrong arguments.
    1313             :    - Warning on too many arguments.
    1314             :    - Line is always skipped.
    1315             : */
    1316             : void
    1317      294209 : parse_D_command()
    1318             : {
    1319      294209 :   Char subcmd = next_arg_begin();
    1320      294209 :   switch((int) subcmd) {
    1321          10 :   case '~':                     // D~: draw B-spline
    1322             :     // actually, this isn't available for some postprocessors
    1323             :     // fall through
    1324             :   default:                      // unknown options are passed to device
    1325             :     {
    1326          10 :       IntArray *args = get_D_variable_args();
    1327          10 :       send_draw(subcmd, args);
    1328          10 :       position_to_end_of_args(args);
    1329          10 :       delete args;
    1330          10 :       break;
    1331             :     }
    1332         140 :   case 'a':                     // Da: draw arc
    1333             :     {
    1334         140 :       IntArray *args = get_D_fixed_args(4);
    1335         140 :       send_draw(subcmd, args);
    1336         140 :       position_to_end_of_args(args);
    1337         140 :       delete args;
    1338         140 :       break;
    1339             :     }
    1340         164 :   case 'c':                     // Dc: draw circle line
    1341             :     {
    1342         164 :       IntArray *args = get_D_fixed_args(1);
    1343         164 :       send_draw(subcmd, args);
    1344             :       // move to right end
    1345         164 :       current_env->hpos += (*args)[0];
    1346         164 :       delete args;
    1347         164 :       break;
    1348             :     }
    1349         144 :   case 'C':                     // DC: draw solid circle
    1350             :     {
    1351         144 :       IntArray *args = get_D_fixed_args_odd_dummy(1);
    1352         144 :       send_draw(subcmd, args);
    1353             :       // move to right end
    1354         144 :       current_env->hpos += (*args)[0];
    1355         144 :       delete args;
    1356         144 :       break;
    1357             :     }
    1358          26 :   case 'e':                     // De: draw ellipse line
    1359             :   case 'E':                     // DE: draw solid ellipse
    1360             :     {
    1361          26 :       IntArray *args = get_D_fixed_args(2);
    1362          26 :       send_draw(subcmd, args);
    1363             :       // move to right end
    1364          26 :       current_env->hpos += (*args)[0];
    1365          26 :       delete args;
    1366          26 :       break;
    1367             :     }
    1368           0 :   case 'f':                     // Df: set fill gray; obsoleted by DFg
    1369             :     {
    1370           0 :       IntArg arg = get_integer_arg();
    1371           0 :       if ((arg >= 0) && (arg <= 1000)) {
    1372             :         // convert arg and treat it like DFg
    1373           0 :         ColorArg gray = color_from_Df_command(arg);
    1374           0 :         current_env->fill->set_gray(gray);
    1375             :       }
    1376             :       else {
    1377             :         // set fill color to the same value as the current outline color
    1378           0 :         delete current_env->fill;
    1379           0 :         current_env->fill = new color(current_env->col);
    1380             :       }
    1381           0 :       pr->change_fill_color(current_env);
    1382             :       // skip unused 'vertical' component (\D'...' always emits pairs)
    1383           0 :       (void) get_integer_arg();
    1384             : #   ifdef STUPID_DRAWING_POSITIONING
    1385           0 :       current_env->hpos += arg;
    1386             : #   endif
    1387           0 :       skip_line_x();
    1388           0 :       break;
    1389             :     }
    1390       66219 :   case 'F':                     // DF: set fill color, several formats
    1391       66219 :     parse_color_command(current_env->fill);
    1392       66219 :     pr->change_fill_color(current_env);
    1393             :     // no positioning (setting-only command)
    1394       66219 :     skip_line_x();
    1395       66219 :     break;
    1396       73276 :   case 'l':                     // Dl: draw line
    1397             :     {
    1398       73276 :       IntArray *args = get_D_fixed_args(2);
    1399       73276 :       send_draw(subcmd, args);
    1400       73276 :       position_to_end_of_args(args);
    1401       73276 :       delete args;
    1402       73276 :       break;
    1403             :     }
    1404       32928 :   case 'p':                     // Dp: draw closed polygon line
    1405             :   case 'P':                     // DP: draw solid closed polygon
    1406             :     {
    1407       32928 :       IntArray *args = get_D_variable_args();
    1408       32928 :       send_draw(subcmd, args);
    1409             : #   ifdef STUPID_DRAWING_POSITIONING
    1410             :       // final args positioning
    1411       32928 :       position_to_end_of_args(args);
    1412             : #   endif
    1413       32928 :       delete args;
    1414       32928 :       break;
    1415             :     }
    1416      121302 :   case 't':                     // Dt: set line thickness
    1417             :     {
    1418      121302 :       IntArray *args = get_D_fixed_args_odd_dummy(1);
    1419      121302 :       send_draw(subcmd, args);
    1420             : #   ifdef STUPID_DRAWING_POSITIONING
    1421             :       // final args positioning
    1422      121302 :       position_to_end_of_args(args);
    1423             : #   endif
    1424      121302 :       delete args;
    1425      121302 :       break;
    1426             :     }
    1427             :   } // end of D subcommands
    1428      294209 : }
    1429             : 
    1430             : //////////////////////////////////////////////////////////////////////
    1431             : /* parse_x_command():
    1432             :    Parse subcommands of the device control command x.
    1433             : 
    1434             :    This is the part of the do_file() parser that scans the device
    1435             :    controlling commands.
    1436             :    - Error on duplicate prologue commands.
    1437             :    - Error on wrong or lacking arguments.
    1438             :    - Warning on too many arguments.
    1439             :    - Line is always skipped.
    1440             : 
    1441             :    Globals:
    1442             :    - current_env: is set by many subcommands.
    1443             :    - npages: page counting variable
    1444             : 
    1445             :    Return: boolean in the meaning of 'stopped'
    1446             :            - true if parsing should be stopped ('x stop').
    1447             :            - false if parsing should continue.
    1448             : */
    1449             : bool
    1450       78636 : parse_x_command(void)
    1451             : {
    1452       78636 :   bool stopped = false;
    1453       78636 :   char *subcmd_str = get_string_arg();
    1454       78636 :   char subcmd = subcmd_str[0];
    1455       78636 :   switch (subcmd) {
    1456        2374 :   case 'f':                     // x font: mount font
    1457             :     {
    1458        2374 :       IntArg n = get_integer_arg();
    1459        2374 :       char *name = get_string_arg();
    1460        2374 :       pr->load_font(n, name);
    1461        2374 :       delete[] name;
    1462        2374 :       skip_line_x();
    1463        2374 :       break;
    1464             :     }
    1465         250 :   case 'F':                     // x Filename: set filename for errors
    1466             :     {
    1467         250 :       char *str_arg = get_extended_arg();
    1468         250 :       if (str_arg == 0)
    1469           0 :         warning("empty argument for 'x F' command");
    1470             :       else {
    1471         250 :         remember_source_filename(str_arg);
    1472         250 :         delete[] str_arg;
    1473             :       }
    1474         250 :       break;
    1475             :     }
    1476           0 :   case 'H':                     // x Height: set character height
    1477           0 :     current_env->height = get_integer_arg();
    1478           0 :     if (current_env->height == current_env->size)
    1479           0 :       current_env->height = 0;
    1480           0 :     skip_line_x();
    1481           0 :     break;
    1482           0 :   case 'i':                     // x init: initialize device
    1483           0 :     error("duplicate 'x init' command");
    1484           0 :     skip_line_x();
    1485           0 :     break;
    1486           0 :   case 'p':                     // x pause: pause device
    1487           0 :     skip_line_x();
    1488           0 :     break;
    1489           0 :   case 'r':                     // x res: set resolution
    1490           0 :     error("duplicate 'x res' command");
    1491           0 :     skip_line_x();
    1492           0 :     break;
    1493        1004 :   case 's':                     // x stop: stop device
    1494        1004 :     stopped = true;
    1495        1004 :     skip_line_x();
    1496        1004 :     break;
    1497           0 :   case 'S':                     // x Slant: set slant
    1498           0 :     current_env->slant = get_integer_arg();
    1499           0 :     skip_line_x();
    1500           0 :     break;
    1501        1004 :   case 't':                     // x trailer: generate trailer info
    1502        1004 :     skip_line_x();
    1503        1004 :     break;
    1504           0 :   case 'T':                     // x Typesetter: set typesetter
    1505           0 :     error("duplicate 'x T' command");
    1506           0 :     skip_line();
    1507           0 :     break;
    1508           0 :   case 'u':                     // x underline: from .cu
    1509             :     {
    1510           0 :       char *str_arg = get_string_arg();
    1511           0 :       pr->special(str_arg, current_env, 'u');
    1512           0 :       delete[] str_arg;
    1513           0 :       skip_line_x();
    1514           0 :       break;
    1515             :     }
    1516       74004 :   case 'X':                     // x X: send uninterpretedly to device
    1517             :     {
    1518       74004 :       char *str_arg = get_extended_arg(); // includes line skip
    1519       74004 :       if (npages <= 0)
    1520           0 :         error("'x X' command invalid before first 'p' command");
    1521       74004 :       else if (str_arg && (strncmp(str_arg, "devtag:",
    1522             :                                    strlen("devtag:")) == 0))
    1523       10337 :         pr->devtag(str_arg, current_env);
    1524             :       else
    1525       63667 :         pr->special(str_arg, current_env);
    1526       74004 :       delete[] str_arg;
    1527       74004 :       break;
    1528             :     }
    1529           0 :   default:                      // ignore unknown x commands, but warn
    1530           0 :     warning("unknown command 'x %1'", subcmd);
    1531           0 :     skip_line();
    1532             :   }
    1533       78636 :   delete[] subcmd_str;
    1534       78636 :   return stopped;
    1535             : }
    1536             : 
    1537             : 
    1538             : /**********************************************************************
    1539             :                      exported part (by driver.h)
    1540             :  **********************************************************************/
    1541             : 
    1542             : ////////////////////////////////////////////////////////////////////////
    1543             : /* do_file():
    1544             :    Parse and postprocess groff intermediate output.
    1545             : 
    1546             :    filename: "-" for standard input, normal file name otherwise
    1547             : */
    1548             : void
    1549        1071 : do_file(const char *filename)
    1550             : {
    1551        1071 :   Char command;
    1552        1071 :   bool stopped = false;         // terminating condition
    1553             : 
    1554             : #ifdef USE_ENV_STACK
    1555             :   EnvStack env_stack = EnvStack();
    1556             : #endif // USE_ENV_STACK
    1557             : 
    1558             :   // setup of global variables
    1559        1071 :   npages = 0;
    1560        1071 :   current_lineno = 1;
    1561             :   // 'pr' is initialized after the prologue.
    1562             :   // 'device' is set by the 1st prologue command.
    1563             : 
    1564        1071 :   if (filename[0] == '-' && filename[1] == '\0')
    1565        1071 :     current_file = stdin;
    1566             :   else {
    1567           0 :     errno = 0;
    1568           0 :     current_file = fopen(filename, "r");
    1569           0 :     if (errno != 0 || current_file == 0) {
    1570           0 :       error("can't open file '%1'", filename);
    1571          67 :       return;
    1572             :     }
    1573             :   }
    1574        1071 :   remember_filename(filename);
    1575             : 
    1576        1071 :   if (current_env != 0)
    1577           0 :     delete_current_env();
    1578        1071 :   current_env = new environment;
    1579        1071 :   current_env->col = new color;
    1580        1071 :   current_env->fill = new color;
    1581        1071 :   current_env->fontno = -1;
    1582        1071 :   current_env->height = 0;
    1583        1071 :   current_env->hpos = -1;
    1584        1071 :   current_env->slant = 0;
    1585        1071 :   current_env->size = 0;
    1586        1071 :   current_env->vpos = -1;
    1587             : 
    1588             :   // parsing of prologue (first 3 commands)
    1589             :   {
    1590             :     char *str_arg;
    1591             :     IntArg int_arg;
    1592             : 
    1593             :     // 1st command 'x T'
    1594        1071 :     command = next_command();
    1595        1071 :     if ((int) command == EOF)
    1596          67 :       return;
    1597        1004 :     if ((int) command != 'x')
    1598           0 :       fatal("the first command must be 'x T'");
    1599        1004 :     str_arg = get_string_arg();
    1600        1004 :     if (str_arg[0] != 'T')
    1601           0 :       fatal("the first command must be 'x T'");
    1602        1004 :     delete[] str_arg;
    1603        1004 :     char *tmp_dev = get_string_arg();
    1604        1004 :     if (pr == 0) {              // note: 'pr' initialized after prologue
    1605        1004 :       device = tmp_dev;
    1606        1004 :       if (0 /* nullptr */ == font::load_desc())
    1607           0 :         fatal("cannot load description of '%1' device", tmp_dev);
    1608             :     }
    1609             :     else {
    1610           0 :       if (device == 0 || strcmp(device, tmp_dev) != 0)
    1611           0 :         fatal("all files must use the same device");
    1612           0 :       delete[] tmp_dev;
    1613             :     }
    1614        1004 :     skip_line_x();              // ignore further arguments
    1615        1004 :     current_env->size = 10 * font::sizescale;
    1616             : 
    1617             :     // 2nd command 'x res'
    1618        1004 :     command = next_command();
    1619        1004 :     if ((int) command != 'x')
    1620           0 :       fatal("the second command must be 'x res'");
    1621        1004 :     str_arg = get_string_arg();
    1622        1004 :     if (str_arg[0] != 'r')
    1623           0 :       fatal("the second command must be 'x res'");
    1624        1004 :     delete[] str_arg;
    1625        1004 :     int_arg = get_integer_arg();
    1626        1004 :     EnvInt font_res = font::res;
    1627        1004 :     if (int_arg != font_res)
    1628           0 :       fatal("resolution does not match");
    1629        1004 :     int_arg = get_integer_arg();
    1630        1004 :     if (int_arg != font::hor)
    1631           0 :       fatal("minimum horizontal motion does not match");
    1632        1004 :     int_arg = get_integer_arg();
    1633        1004 :     if (int_arg != font::vert)
    1634           0 :       fatal("minimum vertical motion does not match");
    1635        1004 :     skip_line_x();              // ignore further arguments
    1636             : 
    1637             :     // 3rd command 'x init'
    1638        1004 :     command = next_command();
    1639        1004 :     if (command != 'x')
    1640           0 :       fatal("the third command must be 'x init'");
    1641        1004 :     str_arg = get_string_arg();
    1642        1004 :     if (str_arg[0] != 'i')
    1643           0 :       fatal("the third command must be 'x init'");
    1644        1004 :     delete[] str_arg;
    1645        1004 :     skip_line_x();
    1646             :   }
    1647             : 
    1648             :   // parsing of body
    1649        1004 :   if (pr == 0)
    1650        1004 :     pr = make_printer();
    1651     2154547 :   while (!stopped) {
    1652     2153543 :     command = next_command();
    1653     2153543 :     if (command == EOF)
    1654           0 :       break;
    1655             :     // spaces, tabs, comments, and newlines are skipped here
    1656     2153543 :     switch ((int) command) {
    1657           0 :     case '#':                   // #: comment, ignore up to end of line
    1658           0 :       skip_line();
    1659           0 :       break;
    1660             : #ifdef USE_ENV_STACK
    1661             :     case '{':                   // {: start a new environment (a copy)
    1662             :       env_stack.push(current_env);
    1663             :       break;
    1664             :     case '}':                   // }: pop previous env from stack
    1665             :       delete_current_env();
    1666             :       current_env = env_stack.pop();
    1667             :       break;
    1668             : #endif // USE_ENV_STACK
    1669           1 :     case '0':                   // ddc: obsolete jump and print command
    1670             :     case '1':
    1671             :     case '2':
    1672             :     case '3':
    1673             :     case '4':
    1674             :     case '5':
    1675             :     case '6':
    1676             :     case '7':
    1677             :     case '8':
    1678             :     case '9':
    1679             :       {                         // expect 2 digits and a character
    1680             :         char s[3];
    1681           1 :         Char c = next_arg_begin();
    1682           1 :         if (npages <= 0)
    1683           0 :           fatal_command(command);
    1684           1 :         if (!isdigit((int) c)) {
    1685           0 :           error("digit expected");
    1686           0 :           c = 0;
    1687             :         }
    1688           1 :         s[0] = (char) command;
    1689           1 :         s[1] = (char) c;
    1690           1 :         s[2] = '\0';
    1691           1 :         errno = 0;
    1692           1 :         long int x = strtol(s, 0, 10);
    1693           1 :         if (errno != 0)
    1694           0 :           error("couldn't convert 2 digits");
    1695           1 :         EnvInt hor_pos = (EnvInt) x;
    1696           1 :         current_env->hpos += hor_pos;
    1697           1 :         c = next_arg_begin();
    1698           1 :         if ((int) c == '\n' || (int) c == EOF)
    1699           0 :           error("character argument expected");
    1700             :         else
    1701           1 :           pr->set_ascii_char((unsigned char) c, current_env);
    1702           1 :         break;
    1703             :       }
    1704         215 :     case 'c':                   // c: print ascii char without moving
    1705             :       {
    1706         215 :         if (npages <= 0)
    1707           0 :           fatal_command(command);
    1708         215 :         Char c = next_arg_begin();
    1709         215 :         if (c == '\n' || c == EOF)
    1710           0 :           error("missing argument to 'c' command");
    1711             :         else
    1712         215 :           pr->set_ascii_char((unsigned char) c, current_env);
    1713         215 :         break;
    1714             :       }
    1715       16637 :     case 'C':                   // C: print named special character
    1716             :       {
    1717       16637 :         if (npages <= 0)
    1718           0 :           fatal_command(command);
    1719       16637 :         char *str_arg = get_string_arg();
    1720       16637 :         pr->set_special_char(str_arg, current_env);
    1721       16637 :         delete[] str_arg;
    1722       16637 :         break;
    1723             :       }
    1724      294209 :     case 'D':                   // drawing commands
    1725      294209 :       if (npages <= 0)
    1726           0 :         fatal_command(command);
    1727      294209 :       parse_D_command();
    1728      294209 :       break;
    1729       86940 :     case 'f':                   // f: set font to number
    1730       86940 :       current_env->fontno = get_integer_arg();
    1731       86940 :       break;
    1732           0 :     case 'F':                   // F: obsolete, replaced by 'x F'
    1733             :       {
    1734           0 :         char *str_arg = get_extended_arg();
    1735           0 :         remember_source_filename(str_arg);
    1736           0 :         delete[] str_arg;
    1737           0 :         break;
    1738             :       }
    1739      308917 :     case 'h':                   // h: relative horizontal move
    1740      308917 :       if (npages <= 0)
    1741           0 :         fatal_command(command);
    1742      308917 :       current_env->hpos += (EnvInt) get_integer_arg();
    1743      308917 :       break;
    1744      279810 :     case 'H':                   // H: absolute horizontal positioning
    1745      279810 :       if (npages <= 0)
    1746           0 :         fatal_command(command);
    1747      279810 :       current_env->hpos = (EnvInt) get_integer_arg();
    1748      279810 :       break;
    1749       48379 :     case 'm':                   // m: stroke color
    1750       48379 :       parse_color_command(current_env->col);
    1751       48379 :       pr->change_color(current_env);
    1752       48379 :       break;
    1753      166155 :     case 'n':                   // n: print end of line
    1754             :                                 // ignore two arguments (historically)
    1755      166155 :       if (npages <= 0)
    1756           0 :         fatal_command(command);
    1757      166155 :       pr->end_of_line();
    1758      166155 :       (void) get_integer_arg();
    1759      166155 :       (void) get_integer_arg();
    1760      166155 :       break;
    1761       21261 :     case 'N':                   // N: print char with given int code
    1762       21261 :       if (npages <= 0)
    1763           0 :         fatal_command(command);
    1764       21261 :       pr->set_numbered_char(get_integer_arg(), current_env);
    1765       21261 :       break;
    1766        1411 :     case 'p':                   // p: start new page with given number
    1767        1411 :       if (npages > 0)
    1768         407 :         pr->end_page(current_env->vpos);
    1769        1411 :       npages++;                 // increment # of processed pages
    1770        1411 :       pr->begin_page(get_integer_arg());
    1771        1411 :       current_env->vpos = 0;
    1772        1411 :       break;
    1773        2186 :     case 's':                   // s: set point size
    1774        2186 :       current_env->size = get_integer_arg();
    1775        2186 :       if (current_env->height == current_env->size)
    1776           0 :         current_env->height = 0;
    1777        2186 :       break;
    1778      312543 :     case 't':                   // t: print a text word
    1779             :       {
    1780             :         char c;
    1781      312543 :         if (npages <= 0)
    1782           0 :           fatal_command(command);
    1783      312543 :         char *str_arg = get_string_arg();
    1784      312543 :         size_t i = 0;
    1785     1733150 :         while ((c = str_arg[i++]) != '\0') {
    1786             :           EnvInt w;
    1787     1420607 :           pr->set_ascii_char((unsigned char) c, current_env, &w);
    1788     1420607 :           current_env->hpos += w;
    1789             :         }
    1790      312543 :         delete[] str_arg;
    1791      312543 :         break;
    1792             :       }
    1793           0 :     case 'u':                   // u: print spaced word
    1794             :       {
    1795             :         char c;
    1796           0 :         if (npages <= 0)
    1797           0 :           fatal_command(command);
    1798           0 :         EnvInt kern = (EnvInt) get_integer_arg();
    1799           0 :         char *str_arg = get_string_arg();
    1800           0 :         size_t i = 0;
    1801           0 :         while ((c = str_arg[i++]) != '\0') {
    1802             :           EnvInt w;
    1803           0 :           pr->set_ascii_char((unsigned char) c, current_env, &w);
    1804           0 :           current_env->hpos += w + kern;
    1805             :         }
    1806           0 :         delete[] str_arg;
    1807           0 :         break;
    1808             :       }
    1809         250 :     case 'v':                   // v: relative vertical move
    1810         250 :       if (npages <= 0)
    1811           0 :         fatal_command(command);
    1812         250 :       current_env->vpos += (EnvInt) get_integer_arg();
    1813         250 :       break;
    1814      299765 :     case 'V':                   // V: absolute vertical positioning
    1815      299765 :       if (npages <= 0)
    1816           0 :         fatal_command(command);
    1817      299765 :       current_env->vpos = (EnvInt) get_integer_arg();
    1818      299765 :       break;
    1819      236228 :     case 'w':                   // w: inform about paddable space
    1820      236228 :       break;
    1821       78636 :     case 'x':                   // device control commands
    1822       78636 :       stopped = parse_x_command();
    1823       78636 :       break;
    1824           0 :     default:
    1825           0 :       warning("unrecognized command '%1'", (unsigned char) command);
    1826           0 :       skip_line();
    1827           0 :       break;
    1828             :     } // end of switch
    1829             :   } // end of while
    1830             : 
    1831             :   // end of file reached
    1832        1004 :   if (npages > 0)
    1833        1004 :     pr->end_page(current_env->vpos);
    1834        1004 :   delete pr;
    1835        1004 :   pr = 0;
    1836        1004 :   fclose(current_file);
    1837             :   // If 'stopped' is not 'true' here then there wasn't any 'x stop'.
    1838        1004 :   if (!stopped)
    1839           0 :     warning("no final 'x stop' command");
    1840        1004 :   delete_current_env();
    1841             : }
    1842             : 
    1843             : // Local Variables:
    1844             : // fill-column: 72
    1845             : // mode: C++
    1846             : // End:
    1847             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14