LCOV - code coverage report
Current view: top level - devices/grohtml - post-html.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 1819 2869 63.4 %
Date: 2026-01-16 17:51:41 Functions: 186 231 80.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2000-2025 Free Software Foundation, Inc.
       2             :  *
       3             :  *  Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
       4             :  *  but it owes a huge amount of ideas and raw code from
       5             :  *  James Clark (jjc@jclark.com) grops/ps.cpp.
       6             :  */
       7             : 
       8             : /*
       9             : This file is part of groff, the GNU roff typesetting system.
      10             : 
      11             : groff is free software; you can redistribute it and/or modify it under
      12             : the terms of the GNU General Public License as published by the Free
      13             : Software Foundation, either version 3 of the License, or
      14             : (at your option) any later version.
      15             : 
      16             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      17             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      18             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      19             : for more details.
      20             : 
      21             : You should have received a copy of the GNU General Public License
      22             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      23             : 
      24             : #ifdef HAVE_CONFIG_H
      25             : #include <config.h>
      26             : #endif
      27             : 
      28             : #include <assert.h>
      29             : #include <errno.h>
      30             : #include <stdio.h> // EOF, FILE, fclose(), fflush(), fopen(), freopen(),
      31             :                    // fseek(), SEEK_SET, setbuf(), stderr, stdout
      32             : #include <stdlib.h> // abs(), atoi(), EXIT_SUCCESS, exit()
      33             : #include <string.h> // strcasecmp(), strcmp(), strerror(), strlen(),
      34             :                     // strncmp()
      35             : #include <time.h> // asctime(), tm
      36             : 
      37             : #include <getopt.h> // getopt_long()
      38             : 
      39             : #include "cset.h" // csspace()
      40             : #include "curtime.h"
      41             : #include "driver.h"
      42             : #include "lib.h" // strsave(), xtmpfile()
      43             : #include "stringclass.h"
      44             : #include "unicode.h"
      45             : 
      46             : #include "html.h"
      47             : #include "html-text.h"
      48             : #include "html-table.h"
      49             : 
      50             : extern "C" const char *Version_string;
      51             : 
      52             : #if !defined(TRUE)
      53             : #   define TRUE  (1==1)
      54             : #endif
      55             : #if !defined(FALSE)
      56             : #   define FALSE (1==0)
      57             : #endif
      58             : 
      59             : #define MAX_LINE_LENGTH    60        /* maximum characters we want in a line      */
      60             : #define SIZE_INCREMENT     2         /* font size increment <big> = +2            */
      61             : #define CENTER_TOLERANCE   2         /* how many pixels off center do we allow    */
      62             : #define ANCHOR_TEMPLATE    "heading" /* if simple anchor is set we use this       */
      63             : #define UNICODE_DESC_START 0x80      /* all character entities above this are     */
      64             :                                      /* either encoded by their glyph names or if */
      65             :                                      /* there is no name then we use &#nnn;       */
      66             : typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
      67             : typedef enum {col_tag, tab_tag, tab0_tag, none_tag} colType;
      68             : 
      69             : #undef DEBUG_TABLES
      70             : // #define DEBUG_TABLES
      71             : 
      72             : /*
      73             :  *  prototypes
      74             :  */
      75             : 
      76             : const char *get_html_translation (font *f, const string &name);
      77             : static const char *get_html_entity(unsigned int code);
      78             : int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
      79             : 
      80             : 
      81             : static int auto_links = TRUE;               /* by default we enable automatic links at  */
      82             :                                             /* top of the document.                     */
      83             : static int auto_rule  = TRUE;               /* by default we enable an automatic rule   */
      84             :                                             /* at the top and bottom of the document    */
      85             : static int simple_anchors = FALSE;          /* default to anchors with heading text     */
      86             : static int manufacture_headings = FALSE;    /* default is to use the Hn html headings,  */
      87             :                                             /* rather than manufacture our own.         */
      88             : static int do_write_creator_comment = TRUE; /* write Creator HTML comment               */
      89             : static int do_write_date_comment = TRUE;    /* write CreationDate HTML comment          */
      90             : /* has user requested initial bg color?     */
      91             : static color *default_background = 0 /* nullptr */;
      92             : static string job_name;                     /* if set then the output is split into     */
      93             :                                             /* multiple files with 'job_name'-%d.html   */
      94             : // TODO: boolify
      95             : static int multiple_files = FALSE;          /* must we the output be divided into       */
      96             :                                             /* multiple html files, one for each        */
      97             :                                             /* heading?                                 */
      98             : static int base_point_size = 0;             /* which troff font size maps onto html     */
      99             :                                             /* size 3?                                  */
     100             : static int split_level = 2;                 /* what heading level to split at?          */
     101             : static string head_info;                    /* user supplied information to be placed   */
     102             :                                             /* into <head> </head>                      */
     103             : static int valid_flag = FALSE;              /* has user requested a valid flag at the   */
     104             :                                             /* end of each page?                        */
     105             : static int groff_sig = FALSE;               /* "This document was produced using"       */
     106             : html_dialect dialect = html4;               /* which html dialect should grohtml output */
     107             : // TODO: Make this an enum.
     108             : static const int CHARSET_ASCII = 0;
     109             : static const int CHARSET_MIXED = 1;
     110             : static const int CHARSET_UTF8  = 2;
     111             : static int charset_encoding = CHARSET_MIXED;/* The character set may be plain ASCII,    */
     112             :                                             /* pure UTF-8, or a mixture of character    */
     113             :                                             /* entity references.                       */
     114             : /*
     115             :  * TODO: CHARSET_MIXED doesn't seem to govern the output in any way.
     116             :  * Everywhere `charset_encoding` is tested, it is cast to a `bool`, so
     117             :  * all we're really choosing between is ASCII and UTF-8.  Can we get rid
     118             :  * of CHARSET_MIXED?
     119             :  */
     120             : 
     121             : /*
     122             :  *  start with a few favorites
     123             :  */
     124             : 
     125           0 : void stop () {}
     126             : 
     127           0 : static int min (int a, int b)
     128             : {
     129           0 :   if (a < b)
     130           0 :     return a;
     131             :   else
     132           0 :     return b;
     133             : }
     134             : 
     135         838 : static int max (int a, int b)
     136             : {
     137         838 :   if (a > b)
     138           4 :     return a;
     139             :   else
     140         834 :     return b;
     141             : }
     142             : 
     143             : /*
     144             :  *  is_intersection - returns TRUE if range a1..a2 intersects with
     145             :  *                    b1..b2
     146             :  */
     147             : 
     148             : // TODO: boolify
     149       45886 : static int is_intersection (int a1, int a2, int b1, int b2)
     150             : {
     151             :   // easier to prove NOT outside limits
     152       45886 :   return ! ((a1 > b2) || (a2 < b1));
     153             : }
     154             : 
     155             : /*
     156             :  *  is_digit - returns TRUE if character, ch, is a digit.
     157             :  */
     158             : 
     159             : // TODO: boolify
     160           0 : static int is_digit (char ch)
     161             : {
     162           0 :   return (ch >= '0') && (ch <= '9');
     163             : }
     164             : 
     165             : /*
     166             :  *  the classes and methods for maintaining a list of files.
     167             :  */
     168             : 
     169             : struct file {
     170             :   FILE    *fp;
     171             :   file    *next;
     172             :   int      new_output_file;
     173             :   int      require_links;
     174             :   string   output_file_name;
     175             : 
     176             :   file     (FILE * /* f */);
     177             : };
     178             : 
     179             : /*
     180             :  *  file - initialize all fields to null pointers
     181             :  */
     182             : 
     183          75 : file::file (FILE * f)
     184             :   : fp(f), next(0 /* nullptr */), new_output_file(FALSE),
     185          75 :     require_links(FALSE), output_file_name("")
     186             : {
     187          75 : }
     188             : 
     189             : // TODO: Explicit `void` parameter lists are unnecessary and
     190             : // inconsistent with other groff code style.
     191             : class files {
     192             : public:
     193             :               files              ();
     194             :   FILE       *get_file           (void);
     195             :   void        start_of_list      (void);
     196             :   void        move_next          (void);
     197             :   void        add_new_file       (FILE * /* f */);
     198             :   void        set_file_name      (string /* name */);
     199             :   void        set_links_required (void);
     200             :   int         are_links_required (void);
     201             :   int         is_new_output_file (void);
     202             :   string      file_name          (void);
     203             :   string      next_file_name     (void);
     204             : private:
     205             :   file       *head;
     206             :   file       *tail;
     207             :   file       *ptr;
     208             : };
     209             : 
     210             : /*
     211             :  *  files - create an empty list of files.
     212             :  */
     213             : 
     214          17 : files::files ()
     215          17 :   : head(0 /* nullptr */), tail(0 /* nullptr */), ptr(0 /* nullptr */)
     216             : {
     217          17 : }
     218             : 
     219             : /*
     220             :  *  get_file - returns the FILE associated with ptr.
     221             :  */
     222             : 
     223         432 : FILE *files::get_file (void)
     224             : {
     225         432 :   if (ptr != 0 /* nullptr */)
     226         415 :     return ptr->fp;
     227             :   else
     228          17 :     return 0 /* nullptr */;
     229             : }
     230             : 
     231             : /*
     232             :  *  start_of_list - reset the ptr to the start of the list.
     233             :  */
     234             : 
     235          17 : void files::start_of_list (void)
     236             : {
     237          17 :   ptr = head;
     238          17 : }
     239             : 
     240             : /*
     241             :  *  move_next - moves the ptr to the next element on the list.
     242             :  */
     243             : 
     244          75 : void files::move_next (void)
     245             : {
     246          75 :   if (ptr != 0 /* nullptr */)
     247          75 :     ptr = ptr->next;
     248          75 : }
     249             : 
     250             : /*
     251             :  *  add_new_file - adds a new file, f, to the list.
     252             :  */
     253             : 
     254          75 : void files::add_new_file (FILE *f)
     255             : {
     256          75 :   assert(f != 0 /* nullptr */);
     257          75 :   if (0 /* nullptr */ == head) {
     258          17 :     head = new file(f);
     259          17 :     tail = head;
     260             :   } else {
     261          58 :     tail->next = new file(f);
     262          58 :     tail       = tail->next;
     263             :   }
     264          75 :   ptr = tail;
     265          75 : }
     266             : 
     267             : /*
     268             :  *  set_file_name - sets the final file name to contain the html
     269             :  *                  data to name.
     270             :  */
     271             : 
     272          40 : void files::set_file_name (string name)
     273             : {
     274          40 :   if (ptr != 0 /* nullptr */) {
     275          40 :     ptr->output_file_name = name;
     276          40 :     ptr->new_output_file = TRUE;
     277             :   }
     278          40 : }
     279             : 
     280             : /*
     281             :  *  set_links_required - issue links when processing this component
     282             :  *                       of the file.
     283             :  */
     284             : 
     285          18 : void files::set_links_required (void)
     286             : {
     287          18 :   if (ptr != 0 /* nullptr */)
     288          18 :     ptr->require_links = TRUE;
     289          18 : }
     290             : 
     291             : /*
     292             :  *  are_links_required - returns TRUE if this section of the file
     293             :  *                       requires that links should be issued.
     294             :  */
     295             : 
     296             : // TODO: boolify
     297          75 : int files::are_links_required (void)
     298             : {
     299          75 :   if (ptr != 0 /* nullptr */)
     300          58 :     return ptr->require_links;
     301          17 :   return FALSE;
     302             : }
     303             : 
     304             : /*
     305             :  *  is_new_output_file - returns TRUE if this component of the file
     306             :  *                       is the start of a new output file.
     307             :  */
     308             : 
     309             : // TODO: boolify
     310          75 : int files::is_new_output_file (void)
     311             : {
     312          75 :   if (ptr != 0 /* nullptr */)
     313          58 :     return ptr->new_output_file;
     314          17 :   return FALSE;
     315             : }
     316             : 
     317             : /*
     318             :  *  file_name - returns the name of the file.
     319             :  */
     320             : 
     321         232 : string files::file_name (void)
     322             : {
     323         232 :   if (ptr != 0 /* nullptr */)
     324         232 :     return ptr->output_file_name;
     325           0 :   return string("");
     326             : }
     327             : 
     328             : /*
     329             :  *  next_file_name - returns the name of the next file.
     330             :  */
     331             : 
     332          57 : string files::next_file_name (void)
     333             : {
     334          57 :   if ((ptr != 0 /* nullptr */) && (ptr->next != 0 /* nullptr */))
     335          41 :     return ptr->next->output_file_name;
     336          16 :   return string("");
     337             : }
     338             : 
     339             : /*
     340             :  *  the class and methods for styles
     341             :  */
     342             : 
     343             : struct style {
     344             :   font        *f;
     345             :   int          point_size;
     346             :   int          font_no;
     347             :   int          height;
     348             :   int          slant;
     349             :   color        col;
     350             :                style       ();
     351             :                style       (font * /* p */,
     352             :                             int /* sz */,
     353             :                             int /* h */,
     354             :                             int /* sl */,
     355             :                             int /* no */,
     356             :                             color /* c */);
     357             :   int          operator == (const style & /* s */) const;
     358             :   int          operator != (const style & /* s */) const;
     359             : };
     360             : 
     361       26048 : style::style()
     362       26048 :   : f(0 /* nullptr */), point_size(-1)
     363             : {
     364       26048 : }
     365             : 
     366      103190 : style::style(font *p, int sz, int h, int sl, int no, color c)
     367      103190 :   : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
     368             : {
     369      103190 : }
     370             : 
     371       95183 : int style::operator==(const style &s) const
     372             : {
     373       95183 :   return ((f == s.f)
     374       93939 :           && (point_size == s.point_size)
     375       93925 :           && (height == s.height)
     376       93925 :           && (slant == s.slant)
     377      189122 :           && (col == s.col));
     378             : }
     379             : 
     380           0 : int style::operator!=(const style &s) const
     381             : {
     382           0 :   return !(*this == s);
     383             : }
     384             : 
     385             : /*
     386             :  *  the class and methods for retaining ascii text
     387             :  */
     388             : 
     389             : struct char_block {
     390             :   enum { SIZE = 256 };
     391             :   char         *buffer;
     392             :   int           used;
     393             :   char_block   *next;
     394             : 
     395             :   char_block();
     396             :   char_block(int /* length */);
     397             :   ~char_block();
     398             : };
     399             : 
     400           0 : char_block::char_block()
     401           0 : : buffer(0 /* nullptr */), used(0), next(0 /* nullptr */)
     402             : {
     403           0 : }
     404             : 
     405         838 : char_block::char_block(int length)
     406         838 : : used(0), next(0 /* nullptr */)
     407             : {
     408         838 :   buffer = new char[max(length, char_block::SIZE)];
     409         838 :   if (0 /* nullptr */ == buffer)
     410           0 :     fatal("out of memory error");
     411         838 : }
     412             : 
     413        1676 : char_block::~char_block()
     414             : {
     415         838 :   if (buffer != 0 /* nullptr */)
     416         838 :     delete[] buffer;
     417         838 : }
     418             : 
     419             : class char_buffer {
     420             : public:
     421             :   char_buffer();
     422             :   ~char_buffer();
     423             :   char  *add_string(const char * /* s */,
     424             :                     unsigned int /* length */);
     425             :   char  *add_string(const string & /* s */);
     426             : private:
     427             :   char_block *head;
     428             :   char_block *tail;
     429             : };
     430             : 
     431          54 : char_buffer::char_buffer()
     432          54 : : head(0 /* nullptr */), tail(0 /* nullptr */)
     433             : {
     434          54 : }
     435             : 
     436         912 : char_buffer::~char_buffer()
     437             : {
     438         875 :   while (head != 0 /* nullptr */) {
     439         838 :     char_block *temp = head;
     440         838 :     head = head->next;
     441         838 :     delete temp;
     442             :   }
     443          37 : }
     444             : 
     445       25822 : char *char_buffer::add_string (const char *s, unsigned int length)
     446             : {
     447       25822 :   int i = 0;
     448             :   unsigned int old_used;
     449             : 
     450       25822 :   if ((0 /* nullptr */ == s) || (0 == length))
     451           8 :     return 0 /* nullptr */;
     452             : 
     453       25814 :   if (0 /* nullptr */ == tail) {
     454          20 :     tail = new char_block(length+1);
     455          20 :     head = tail;
     456             :   } else {
     457       25794 :     if (tail->used + length+1 > char_block::SIZE) {
     458         818 :       tail->next  = new char_block(length+1);
     459         818 :       tail        = tail->next;
     460             :     }
     461             :   }
     462             : 
     463       25814 :   old_used = tail->used;
     464      159391 :   do {
     465      185205 :     tail->buffer[tail->used] = s[i];
     466      185205 :     tail->used++;
     467      185205 :     i++;
     468      185205 :     length--;
     469      185205 :   } while (length>0);
     470             : 
     471             :   // add terminating nul character
     472             : 
     473       25814 :   tail->buffer[tail->used] = '\0';
     474       25814 :   tail->used++;
     475             : 
     476             :   // and return start of new string
     477             : 
     478       25814 :   return &tail->buffer[old_used];
     479             : }
     480             : 
     481       25822 : char *char_buffer::add_string (const string &s)
     482             : {
     483       25822 :   return add_string(s.contents(), s.length());
     484             : }
     485             : 
     486             : /*
     487             :  *  the classes and methods for maintaining glyph positions.
     488             :  */
     489             : 
     490             : class text_glob {
     491             : public:
     492             :   void text_glob_html      (style * /* s */,
     493             :                             char * /* str */,
     494             :                             int /* length */,
     495             :                             int /* min_vertical */,
     496             :                             int /* min_horizontal */,
     497             :                             int /* max_vertical */,
     498             :                             int /* max_horizontal */);
     499             :   void text_glob_special   (style * /* s */,
     500             :                             char * /* str */,
     501             :                             int /* length */,
     502             :                             int /* min_vertical */,
     503             :                             int /* min_horizontal */,
     504             :                             int /* max_vertical */,
     505             :                             int /* max_horizontal */);
     506             :   void text_glob_line      (style * /* s */,
     507             :                             int /* min_vertical */,
     508             :                             int /* min_horizontal */,
     509             :                             int /* max_vertical */,
     510             :                             int /* max_horizontal */,
     511             :                             int /* thickness */);
     512             :   void text_glob_auto_image(style * /* s */,
     513             :                             char * /* str */,
     514             :                             int /* length */,
     515             :                             int /* min_vertical */,
     516             :                             int /* min_horizontal */,
     517             :                             int /* max_vertical */,
     518             :                             int /* max_horizontal */);
     519             :   void text_glob_tag       (style * /* s */,
     520             :                             char * /* str */,
     521             :                             int /* length */,
     522             :                             int /* min_vertical */,
     523             :                             int /* min_horizontal */,
     524             :                             int /* max_vertical */,
     525             :                             int /* max_horizontal */);
     526             :   text_glob                (void);
     527             :   ~text_glob               (void);
     528             :   int  is_a_line           (void);
     529             :   int  is_a_tag            (void);
     530             :   int  is_eol              (void);
     531             :   int  is_auto_img         (void);
     532             :   int  is_br               (void);
     533             :   int  is_in               (void);
     534             :   int  is_po               (void);
     535             :   int  is_ti               (void);
     536             :   int  is_ll               (void);
     537             :   int  is_ce               (void);
     538             :   int  is_tl               (void);
     539             :   int  is_eo_tl            (void);
     540             :   int  is_eol_ce           (void);
     541             :   int  is_col              (void);
     542             :   int  is_tab              (void);
     543             :   int  is_tab0             (void);
     544             :   int  is_ta               (void);
     545             :   int  is_tab_ts           (void);
     546             :   int  is_tab_te           (void);
     547             :   int  is_nf               (void);
     548             :   int  is_fi               (void);
     549             :   int  is_eo_h             (void);
     550             :   int  get_arg             (void);
     551             :   int  get_tab_args        (char * /* align */);
     552             : 
     553             :   void        remember_table (html_table * /* t */);
     554             :   html_table *get_table      (void);
     555             : 
     556             :   style           text_style;
     557             :   const char     *text_string;
     558             :   unsigned int    text_length;
     559             :   int             minv, minh, maxv, maxh;
     560             :   int             is_tag;               // is this a .br, .sp, .tl etc
     561             :   int             is_img_auto;          // image created by eqn delim
     562             :   int             is_special;           // text has come via 'x X html:'
     563             :   int             is_line;              // is the command a <line>?
     564             :   int             thickness;            // the thickness of a line
     565             :   html_table     *tab;                  // table description
     566             : 
     567             : private:
     568             :   text_glob           (style * /* s */,
     569             :                        const char * /* str */,
     570             :                        int /* length */,
     571             :                        int /* min_vertical */,
     572             :                        int /* min_horizontal */,
     573             :                        int /* max_vertical */,
     574             :                        int /* max_horizontal */,
     575             :                        bool /* is_troff_command */,
     576             :                        bool /* is_auto_image */,
     577             :                        bool /* is_special_command */,
     578             :                        bool /* is_a_line */,
     579             :                        int  /* thickness */);
     580             : };
     581             : 
     582       25822 : text_glob::text_glob (style *s ,
     583             :                       const char *str,
     584             :                       int length,
     585             :                       int min_vertical,
     586             :                       int min_horizontal,
     587             :                       int max_vertical,
     588             :                       int max_horizontal,
     589             :                       bool is_troff_command,
     590             :                       bool is_auto_image,
     591             :                       bool is_special_command,
     592             :                       bool is_a_line_flag,
     593       25822 :                       int line_thickness)
     594       25822 :   : text_style(*s), text_string(str), text_length(length),
     595             :     minv(min_vertical), minh(min_horizontal), maxv(max_vertical),
     596             :     maxh(max_horizontal), is_tag(is_troff_command),
     597             :     is_img_auto(is_auto_image), is_special(is_special_command),
     598             :     is_line(is_a_line_flag), thickness(line_thickness),
     599       25822 :     tab(0 /* nullptr */)
     600             : {
     601       25822 : }
     602             : 
     603       25822 : text_glob::text_glob ()
     604             :   : text_string(0 /* nullptr */), text_length(0), minv(-1), minh(-1),
     605             :     maxv(-1), maxh(-1), is_tag(FALSE), is_special(FALSE),
     606       25822 :     is_line(FALSE), thickness(0), tab(0 /* nullptr */)
     607             : {
     608       25822 : }
     609             : 
     610       51650 : text_glob::~text_glob ()
     611             : {
     612       51650 :   if (tab != 0 /* nullptr */)
     613          20 :     delete tab;
     614       51650 : }
     615             : 
     616             : /*
     617             :  *  text_glob_html - used to place html text into the glob buffer.
     618             :  */
     619             : 
     620       20473 : void text_glob::text_glob_html (style *s, char *str, int length,
     621             :                                 int min_vertical , int min_horizontal,
     622             :                                 int max_vertical , int max_horizontal)
     623             : {
     624             :   text_glob *g = new text_glob(s, str, length,
     625             :                                min_vertical, min_horizontal,
     626             :                                max_vertical, max_horizontal,
     627       20473 :                                FALSE, FALSE, FALSE, FALSE, 0 /* nullptr */);
     628             :   // TODO: penultimate argument above is not really a Boolean
     629       20473 :   *this = *g;
     630       20473 :   delete g;
     631       20473 : }
     632             : 
     633             : /*
     634             :  *  text_glob_html - used to place html specials into the glob buffer.
     635             :  *                   This text is essentially html commands coming
     636             :  *                   through from the macro sets, with special
     637             :  *                   designated sequences of characters translated into
     638             :  *                   html. See add_and_encode.
     639             :  */
     640             : 
     641         563 : void text_glob::text_glob_special (style *s, char *str, int length,
     642             :                                    int min_vertical, int min_horizontal,
     643             :                                    int max_vertical, int max_horizontal)
     644             : {
     645             :   text_glob *g = new text_glob(s, str, length,
     646             :                                min_vertical, min_horizontal,
     647             :                                max_vertical, max_horizontal,
     648         563 :                                FALSE, FALSE, TRUE, FALSE, 0 /* nullptr */);
     649             :   // TODO: penultimate argument above is not really a Boolean
     650         563 :   *this = *g;
     651         563 :   delete g;
     652         563 : }
     653             : 
     654             : /*
     655             :  *  text_glob_line - record horizontal draw line commands.
     656             :  */
     657             : 
     658           0 : void text_glob::text_glob_line (style *s,
     659             :                                 int min_vertical, int min_horizontal,
     660             :                                 int max_vertical, int max_horizontal,
     661             :                                 int thickness_value)
     662             : {
     663             :   text_glob *g = new text_glob(s, "", 0,
     664             :                                min_vertical, min_horizontal,
     665             :                                max_vertical, max_horizontal,
     666             :                                FALSE, FALSE, FALSE, TRUE,
     667           0 :                                thickness_value);
     668           0 :   *this = *g;
     669           0 :   delete g;
     670           0 : }
     671             : 
     672             : /*
     673             :  *  text_glob_auto_image - record the presence of a .auto-image tag
     674             :  *                         command.  Used to mark that an image has been
     675             :  *                         created automatically by a preprocessor and
     676             :  *                         (pre-grohtml/troff) combination.  Under some
     677             :  *                         circumstances images may not be created.
     678             :  *                         (consider .EQ
     679             :  *                                   delim $$
     680             :  *                                   .EN
     681             :  *                                   .TS
     682             :  *                                   tab(!), center;
     683             :  *                                   l!l.
     684             :  *                                   $1 over x$!recripical of x
     685             :  *                                   .TE
     686             :  *                          the first auto-image marker is created via
     687             :  *                          .EQ/.EN pair and no image is created.  The
     688             :  *                          second auto-image marker occurs at $1 over
     689             :  *                          x$ Currently this image will not be created
     690             :  *                          as the whole of the table is created as an
     691             :  *                          image.  (Once html tables are handled by
     692             :  *                          grohtml this will change.  Shortly this will
     693             :  *                          be the case).
     694             :  */
     695             : 
     696          58 : void text_glob::text_glob_auto_image(style *s, char *str, int length,
     697             :                                      int min_vertical,
     698             :                                      int min_horizontal,
     699             :                                      int max_vertical,
     700             :                                      int max_horizontal)
     701             : {
     702             :   text_glob *g = new text_glob(s, str, length,
     703             :                                min_vertical, min_horizontal,
     704             :                                max_vertical, max_horizontal,
     705          58 :                                TRUE, TRUE, FALSE, FALSE, 0 /* nullptr */);
     706          58 :   *this = *g;
     707          58 :   delete g;
     708          58 : }
     709             : 
     710             : /*
     711             :  *  text_glob_tag - records a troff tag.
     712             :  */
     713             : 
     714        4728 : void text_glob::text_glob_tag (style *s, char *str, int length,
     715             :                                int min_vertical, int min_horizontal,
     716             :                                int max_vertical, int max_horizontal)
     717             : {
     718             :   text_glob *g = new text_glob(s, str, length,
     719             :                                min_vertical, min_horizontal,
     720             :                                max_vertical, max_horizontal,
     721        4728 :                                TRUE, FALSE, FALSE, FALSE, 0 /* nullptr */);
     722        4728 :   *this = *g;
     723        4728 :   delete g;
     724        4728 : }
     725             : 
     726             : /*
     727             :  *  is_a_line - returns TRUE if glob should be converted into an <hr>
     728             :  */
     729             : 
     730             : // TODO: boolify
     731       20923 : int text_glob::is_a_line (void)
     732             : {
     733       20923 :   return is_line;
     734             : }
     735             : 
     736             : /*
     737             :  *  is_a_tag - returns TRUE if glob contains a troff directive.
     738             :  */
     739             : 
     740             : // TODO: boolify
     741      272989 : int text_glob::is_a_tag (void)
     742             : {
     743      272989 :   return is_tag;
     744             : }
     745             : 
     746             : /*
     747             :  *  is_eol - returns TRUE if glob contains the tag eol
     748             :  */
     749             : 
     750             : // TODO: boolify
     751       26287 : int text_glob::is_eol (void)
     752             : {
     753       26287 :   return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
     754             : }
     755             : 
     756             : /*
     757             :  *  is_eol_ce - returns TRUE if glob contains the tag eol.ce
     758             :  */
     759             : 
     760             : // TODO: boolify
     761        3440 : int text_glob::is_eol_ce (void)
     762             : {
     763        3440 :   return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
     764             : }
     765             : 
     766             : /*
     767             :  *  is_tl - returns TRUE if glob contains the tag .tl
     768             :  */
     769             : 
     770             : // TODO: boolify
     771         974 : int text_glob::is_tl (void)
     772             : {
     773         974 :   return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
     774             : }
     775             : 
     776             : /*
     777             :  *  is_eo_tl - returns TRUE if glob contains the tag eo.tl
     778             :  */
     779             : 
     780             : // TODO: boolify
     781          24 : int text_glob::is_eo_tl (void)
     782             : {
     783          24 :   return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
     784             : }
     785             : 
     786             : /*
     787             :  *  is_nf - returns TRUE if glob contains the tag .fi 0
     788             :  */
     789             : 
     790             : // TODO: boolify
     791       76857 : int text_glob::is_nf (void)
     792             : {
     793       76857 :   return is_tag
     794       12654 :          && (strncmp(text_string, "devtag:.fi", strlen("devtag:.fi"))
     795             :              == 0)
     796       89511 :          && (get_arg() == 0);
     797             : }
     798             : 
     799             : /*
     800             :  *  is_fi - returns TRUE if glob contains the tag .fi 1
     801             :  */
     802             : 
     803             : // TODO: boolify
     804       77620 : int text_glob::is_fi (void)
     805             : {
     806       77620 :   return (is_tag
     807       13417 :           && (strncmp(text_string, "devtag:.fi", strlen("devtag:.fi"))
     808             :               == 0)
     809       91037 :           && (get_arg() == 1));
     810             : }
     811             : 
     812             : /*
     813             :  *  is_eo_h - returns TRUE if glob contains the tag .eo.h
     814             :  */
     815             : 
     816             : // TODO: boolify
     817         417 : int text_glob::is_eo_h (void)
     818             : {
     819         417 :   return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
     820             : }
     821             : 
     822             : /*
     823             :  *  is_ce - returns TRUE if glob contains the tag .ce
     824             :  */
     825             : 
     826             : // TODO: boolify
     827       22986 : int text_glob::is_ce (void)
     828             : {
     829       22986 :   return is_tag && (strncmp(text_string, "devtag:.ce",
     830       22986 :                             strlen("devtag:.ce")) == 0);
     831             : }
     832             : 
     833             : /*
     834             :  *  is_in - returns TRUE if glob contains the tag .in
     835             :  */
     836             : 
     837             : // TODO: boolify
     838       23006 : int text_glob::is_in (void)
     839             : {
     840       23006 :   return is_tag && (strncmp(text_string, "devtag:.in ",
     841       23006 :                             strlen("devtag:.in ")) == 0);
     842             : }
     843             : 
     844             : /*
     845             :  *  is_po - returns TRUE if glob contains the tag .po
     846             :  */
     847             : 
     848             : // TODO: boolify
     849       22699 : int text_glob::is_po (void)
     850             : {
     851       22699 :   return is_tag && (strncmp(text_string, "devtag:.po ",
     852       22699 :                             strlen("devtag:.po ")) == 0);
     853             : }
     854             : 
     855             : /*
     856             :  *  is_ti - returns TRUE if glob contains the tag .ti
     857             :  */
     858             : 
     859             : // TODO: boolify
     860       22687 : int text_glob::is_ti (void)
     861             : {
     862       22687 :   return is_tag && (strncmp(text_string, "devtag:.ti ",
     863       22687 :                             strlen("devtag:.ti ")) == 0);
     864             : }
     865             : 
     866             : /*
     867             :  *  is_ll - returns TRUE if glob contains the tag .ll
     868             :  */
     869             : 
     870             : // TODO: boolify
     871         420 : int text_glob::is_ll (void)
     872             : {
     873         420 :   return is_tag && (strncmp(text_string, "devtag:.ll ",
     874         420 :                             strlen("devtag:.ll ")) == 0);
     875             : }
     876             : 
     877             : /*
     878             :  *  is_col - returns TRUE if glob contains the tag .col
     879             :  */
     880             : 
     881             : // TODO: boolify
     882       67734 : int text_glob::is_col (void)
     883             : {
     884       67734 :   return is_tag && (strncmp(text_string, "devtag:.col",
     885       67734 :                             strlen("devtag:.col")) == 0);
     886             : }
     887             : 
     888             : /*
     889             :  *  is_tab_ts - returns TRUE if glob contains the tag .tab_ts
     890             :  */
     891             : 
     892             : // TODO: boolify
     893           0 : int text_glob::is_tab_ts (void)
     894             : {
     895           0 :   return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
     896             : }
     897             : 
     898             : /*
     899             :  *  is_tab_te - returns TRUE if glob contains the tag .tab_te
     900             :  */
     901             : 
     902             : // TODO: boolify
     903           0 : int text_glob::is_tab_te (void)
     904             : {
     905           0 :   return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
     906             : }
     907             : 
     908             : /*
     909             :  *  is_ta - returns TRUE if glob contains the tag .ta
     910             :  */
     911             : 
     912             : // TODO: boolify
     913       24673 : int text_glob::is_ta (void)
     914             : {
     915       24673 :   return is_tag && (strncmp(text_string, "devtag:.ta ",
     916       24673 :                             strlen("devtag:.ta ")) == 0);
     917             : }
     918             : 
     919             : /*
     920             :  *  is_tab - returns TRUE if glob contains the tag tab
     921             :  */
     922             : 
     923             : // TODO: boolify
     924       78308 : int text_glob::is_tab (void)
     925             : {
     926       78308 :   return is_tag && (strncmp(text_string, "devtag:tab ",
     927       78308 :                             strlen("devtag:tab ")) == 0);
     928             : }
     929             : 
     930             : /*
     931             :  *  is_tab0 - returns TRUE if glob contains the tag tab0
     932             :  */
     933             : 
     934             : // TODO: boolify
     935       45040 : int text_glob::is_tab0 (void)
     936             : {
     937       45040 :   return is_tag && (strncmp(text_string, "devtag:tab0",
     938       45040 :                             strlen("devtag:tab0")) == 0);
     939             : }
     940             : 
     941             : /*
     942             :  *  is_auto_img - returns TRUE if the glob contains an automatically
     943             :  *                generated image.
     944             :  */
     945             : 
     946             : // TODO: boolify
     947         441 : int text_glob::is_auto_img (void)
     948             : {
     949         441 :   return is_img_auto;
     950             : }
     951             : 
     952             : /*
     953             :  *  is_br - returns TRUE if the glob is a tag containing a .br
     954             :  *          or an implied .br. Note that we do not include .nf or .fi
     955             :  *          as grohtml will place a .br after these commands if they
     956             :  *          should break the line.
     957             :  */
     958             : 
     959             : // TODO: boolify
     960       99010 : int text_glob::is_br (void)
     961             : {
     962      110538 :   return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0)
     963       11528 :                         || (strncmp("devtag:.sp", text_string,
     964       99010 :                                     strlen("devtag:.sp")) == 0));
     965             : }
     966             : 
     967        1407 : int text_glob::get_arg (void)
     968             : {
     969        1407 :   if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
     970        1407 :     const char *p = text_string;
     971             : 
     972       15533 :     while ((*p != '\0') && (!csspace(*p)))
     973       14126 :       p++;
     974        2814 :     while ((*p != '\0') && (csspace(*p)))
     975        1407 :       p++;
     976        1407 :     if ('\0' == *p)
     977           0 :       return -1;
     978        1407 :     return atoi(p);
     979             :   }
     980           0 :   return -1;
     981             : }
     982             : 
     983             : /*
     984             :  *  get_tab_args - returns the tab position and alignment of the tab tag
     985             :  */
     986             : 
     987           9 : int text_glob::get_tab_args (char *align)
     988             : {
     989           9 :   assert(align != 0 /* nullptr */);
     990           9 :   if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
     991           9 :     const char *p = text_string;
     992             : 
     993             :     // firstly the alignment C|R|L
     994          99 :     while ((*p != '\0') && (!csspace(*p)))
     995          90 :       p++;
     996          18 :     while ((*p != '\0') && (csspace(*p)))
     997           9 :       p++;
     998           9 :     *align = *p;
     999             :     // now the int value
    1000          18 :     while ((*p != '\0') && (!csspace(*p)))
    1001           9 :       p++;
    1002          18 :     while ((*p != '\0') && (csspace(*p)))
    1003           9 :       p++;
    1004           9 :     if ('\0' == *p)
    1005           0 :       return -1;
    1006           9 :     return atoi(p);
    1007             :   }
    1008           0 :   return -1;
    1009             : }
    1010             : 
    1011             : /*
    1012             :  *  remember_table - saves table, t, in the text_glob.
    1013             :  */
    1014             : 
    1015          20 : void text_glob::remember_table (html_table *t)
    1016             : {
    1017          20 :   if (tab != 0 /* nullptr */)
    1018           0 :     delete tab;
    1019          20 :   tab = t;
    1020          20 : }
    1021             : 
    1022             : /*
    1023             :  *  get_table - returns the stored table description.
    1024             :  */
    1025             : 
    1026          20 : html_table *text_glob::get_table (void)
    1027             : {
    1028          20 :   return tab;
    1029             : }
    1030             : 
    1031             : /*
    1032             :  *  the class and methods used to construct ordered double linked
    1033             :  *  lists.  In a previous implementation we used templates via
    1034             :  *  #include "ordered-list.h", but this does assume that all C++
    1035             :  *  compilers can handle this feature. Pragmatically it is safer to
    1036             :  *  assume this is not the case.
    1037             :  */
    1038             : 
    1039             : struct element_list {
    1040             :   element_list *right;
    1041             :   element_list *left;
    1042             :   text_glob    *datum;
    1043             :   int           lineno;
    1044             :   int           minv, minh, maxv, maxh;
    1045             : 
    1046             :   element_list  (text_glob * /* d */,
    1047             :                  int /* line_number */,
    1048             :                  int /* min_vertical */,
    1049             :                  int /* min_horizontal */,
    1050             :                  int /* max_vertical */,
    1051             :                  int /* max_horizontal */);
    1052             :   element_list  ();
    1053             :   ~element_list ();
    1054             : };
    1055             : 
    1056           0 : element_list::element_list ()
    1057             :   : right(0 /* nullptr */), left(0 /* nullptr */),
    1058             :     datum(0 /* nullptr */), lineno(0),
    1059             :     minv(-1), minh(-1),
    1060           0 :     maxv(-1), maxh(-1)
    1061             : {
    1062           0 : }
    1063             : 
    1064             : /*
    1065             :  *  element_list - create a list element assigning the datum and region
    1066             :  *                 parameters.
    1067             :  */
    1068             : 
    1069       25822 : element_list::element_list (text_glob *in,
    1070             :                             int line_number,
    1071             :                             int min_vertical, int min_horizontal,
    1072       25822 :                             int max_vertical, int max_horizontal)
    1073             :   : right(0 /* nullptr */), left( /* nullptr */0), datum(in),
    1074             :     lineno(line_number),
    1075             :     minv(min_vertical), minh(min_horizontal),
    1076       25822 :     maxv(max_vertical), maxh(max_horizontal)
    1077             : {
    1078       25822 : }
    1079             : 
    1080       51544 : element_list::~element_list ()
    1081             : {
    1082       25772 :   if (datum != 0 /* nullptr */)
    1083       25772 :     delete datum;
    1084       25772 : }
    1085             : 
    1086             : class list {
    1087             : public:
    1088             :        list             ();
    1089             :       ~list             ();
    1090             :   int  is_less          (element_list * /* a */,
    1091             :                          element_list * /* b */);
    1092             :   void add              (text_glob * /* in */,
    1093             :                          int /* line_number */,
    1094             :                          int /* min_vertical */,
    1095             :                          int /* min_horizontal */,
    1096             :                          int /* max_vertical */,
    1097             :                          int /* max_horizontal */);
    1098             :   void                  sub_move_right      (void);
    1099             :   void                  move_right          (void);
    1100             :   void                  move_left           (void);
    1101             :   int                   is_empty            (void);
    1102             :   int                   is_equal_to_tail    (void);
    1103             :   int                   is_equal_to_head    (void);
    1104             :   void                  start_from_head     (void);
    1105             :   void                  start_from_tail     (void);
    1106             :   void                  insert              (text_glob * /* in */);
    1107             :   void                  move_to             (text_glob * /* in */);
    1108             :   text_glob            *move_right_get_data (void);
    1109             :   text_glob            *move_left_get_data  (void);
    1110             :   text_glob            *get_data            (void);
    1111             : private:
    1112             :   element_list *head;
    1113             :   element_list *tail;
    1114             :   element_list *ptr;
    1115             : };
    1116             : 
    1117             : /*
    1118             :  *  list - construct an empty list.
    1119             :  */
    1120             : 
    1121          71 : list::list ()
    1122          71 :   : head(0 /* nullptr */), tail(0 /* nullptr */), ptr(0 /* nullptr */)
    1123             : {
    1124          71 : }
    1125             : 
    1126             : /*
    1127             :  *  ~list - destroy a complete list.
    1128             :  */
    1129             : 
    1130         108 : list::~list()
    1131             : {
    1132          54 :   element_list *temp=head;
    1133             : 
    1134       25746 :   do {
    1135       25800 :     temp = head;
    1136       25800 :     if (temp != 0 /* nullptr */) {
    1137       25772 :       head = head->right;
    1138       25772 :       delete temp;
    1139             :     }
    1140       25800 :   } while ((head != 0 /* nullptr */) && (head != tail));
    1141          54 : }
    1142             : 
    1143             : /*
    1144             :  *  is_less - returns TRUE if a is left of b if on the same line or
    1145             :  *            if a is higher up the page than b.
    1146             :  */
    1147             : 
    1148             : // TODO: boolify
    1149       51480 : int list::is_less (element_list *a, element_list *b)
    1150             : {
    1151       51480 :   assert(a != 0 /* nullptr */);
    1152       51480 :   assert(b != 0 /* nullptr */);
    1153             :   // was:
    1154             :   // if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
    1155       51480 :   if (a->lineno < b->lineno) {
    1156           0 :     return  TRUE;
    1157       51480 :   } else if (a->lineno > b->lineno) {
    1158        5594 :     return  FALSE;
    1159       45886 :   } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
    1160       45886 :     return (a->minh < b->minh);
    1161             :   } else {
    1162           0 :     return (a->maxv < b->maxv);
    1163             :   }
    1164             : }
    1165             : 
    1166             : /*
    1167             :  *  add - adds a datum to the list in the order specified by the
    1168             :  *        region position.
    1169             :  */
    1170             : 
    1171       25779 : void list::add (text_glob *in, int line_number,
    1172             :                 int min_vertical, int min_horizontal,
    1173             :                 int max_vertical, int max_horizontal)
    1174             : {
    1175             :   // create a new list element with datum and position fields
    1176             :   // initialized
    1177             :   element_list *t    = new element_list(in, line_number,
    1178             :                                         min_vertical, min_horizontal,
    1179       25779 :                                         max_vertical, max_horizontal);
    1180             :   element_list *last;
    1181             : 
    1182             : #if 0
    1183             :   fprintf(stderr, "[%s %d,%d,%d,%d] ",
    1184             :           in->text_string, min_vertical, min_horizontal,
    1185             :           max_vertical, max_horizontal);
    1186             :   fflush(stderr);
    1187             : #endif
    1188             : 
    1189       25779 :   if (0 /* nullptr */ == head) {
    1190          26 :     head     = t;
    1191          26 :     tail     = t;
    1192          26 :     ptr      = t;
    1193          26 :     t->left  = t;
    1194          26 :     t->right = t;
    1195             :   } else {
    1196       25753 :     last = tail;
    1197             : 
    1198       25753 :     while ((last != head) && (is_less(t, last)))
    1199           0 :       last = last->left;
    1200             : 
    1201       25753 :     if (is_less(t, last)) {
    1202           0 :       t->right          = last;
    1203           0 :       last->left->right = t;
    1204           0 :       t->left           = last->left;
    1205           0 :       last->left        = t;
    1206             :       // now check for a new head
    1207           0 :       if (last == head)
    1208           0 :         head = t;
    1209             :     } else {
    1210             :       // add t beyond last
    1211       25753 :       t->right          = last->right;
    1212       25753 :       t->left           = last;
    1213       25753 :       last->right->left = t;
    1214       25753 :       last->right       = t;
    1215             :       // now check for a new tail
    1216       25753 :       if (last == tail)
    1217       25753 :         tail = t;
    1218             :     }
    1219             :   }
    1220       25779 : }
    1221             : 
    1222             : /*
    1223             :  *  sub_move_right - removes the element which is currently pointed to
    1224             :  *                   by ptr from the list and moves ptr to the right.
    1225             :  */
    1226             : 
    1227          24 : void list::sub_move_right (void)
    1228             : {
    1229          24 :   element_list *t=ptr->right;
    1230             : 
    1231          24 :   if (head == tail) {
    1232           0 :     head = 0;
    1233           0 :     if (tail != 0 /* nullptr */)
    1234           0 :       delete tail;
    1235             : 
    1236           0 :     tail = 0 /* nullptr */;
    1237           0 :     ptr  = 0 /* nullptr */;
    1238             :   } else {
    1239          24 :     if (head == ptr)
    1240           0 :       head = head->right;
    1241          24 :     if (tail == ptr)
    1242           0 :       tail = tail->left;
    1243          24 :     ptr->left->right = ptr->right;
    1244          24 :     ptr->right->left = ptr->left;
    1245          24 :     ptr = t;
    1246             :   }
    1247          24 : }
    1248             : 
    1249             : /*
    1250             :  *  start_from_head - assigns ptr to the head.
    1251             :  */
    1252             : 
    1253         117 : void list::start_from_head (void)
    1254             : {
    1255         117 :   ptr = head;
    1256         117 : }
    1257             : 
    1258             : /*
    1259             :  *  start_from_tail - assigns ptr to the tail.
    1260             :  */
    1261             : 
    1262           0 : void list::start_from_tail (void)
    1263             : {
    1264           0 :   ptr = tail;
    1265           0 : }
    1266             : 
    1267             : /*
    1268             :  *  is_empty - returns TRUE if the list has no elements.
    1269             :  */
    1270             : 
    1271             : // TODO: boolify
    1272       26124 : int list::is_empty (void)
    1273             : {
    1274       26124 :   return 0 /* nullptr */ == head;
    1275             : }
    1276             : 
    1277             : /*
    1278             :  *  is_equal_to_tail - returns TRUE if the ptr equals the tail.
    1279             :  */
    1280             : 
    1281             : // TODO: boolify
    1282        1530 : int list::is_equal_to_tail (void)
    1283             : {
    1284        1530 :   return ptr == tail;
    1285             : }
    1286             : 
    1287             : /*
    1288             :  *  is_equal_to_head - returns TRUE if the ptr equals the head.
    1289             :  */
    1290             : 
    1291             : // TODO: boolify
    1292      109255 : int list::is_equal_to_head (void)
    1293             : {
    1294      109255 :   return ptr == head;
    1295             : }
    1296             : 
    1297             : /*
    1298             :  *  move_left - moves the ptr left.
    1299             :  */
    1300             : 
    1301       11530 : void list::move_left (void)
    1302             : {
    1303       11530 :   ptr = ptr->left;
    1304       11530 : }
    1305             : 
    1306             : /*
    1307             :  *  move_right - moves the ptr right.
    1308             :  */
    1309             : 
    1310       89073 : void list::move_right (void)
    1311             : {
    1312       89073 :   ptr = ptr->right;
    1313       89073 : }
    1314             : 
    1315             : /*
    1316             :  *  get_datum - returns the datum referenced via ptr.
    1317             :  */
    1318             : 
    1319      103019 : text_glob* list::get_data (void)
    1320             : {
    1321      103019 :   return ptr->datum;
    1322             : }
    1323             : 
    1324             : /*
    1325             :  *  move_right_get_data - returns the datum referenced via ptr and moves
    1326             :  *                        ptr right.
    1327             :  */
    1328             : 
    1329       25608 : text_glob* list::move_right_get_data (void)
    1330             : {
    1331       25608 :   ptr = ptr->right;
    1332       25608 :   if (ptr == head)
    1333          14 :     return 0;
    1334             :   else
    1335       25594 :     return ptr->datum;
    1336             : }
    1337             : 
    1338             : /*
    1339             :  *  move_left_get_data - returns the datum referenced via ptr and moves
    1340             :  *                       ptr right.
    1341             :  */
    1342             : 
    1343           0 : text_glob* list::move_left_get_data (void)
    1344             : {
    1345           0 :   ptr = ptr->left;
    1346           0 :   if (ptr == tail)
    1347           0 :     return 0;
    1348             :   else
    1349           0 :     return ptr->datum;
    1350             : }
    1351             : 
    1352             : /*
    1353             :  *  insert - inserts data after the current position.
    1354             :  */
    1355             : 
    1356          43 : void list::insert (text_glob *in)
    1357             : {
    1358          43 :   if (is_empty())
    1359           0 :     fatal("list must not be empty if we are inserting data");
    1360             :   else {
    1361          43 :     if (0 /* nullptr */ == ptr)
    1362           0 :       ptr = head;
    1363             : 
    1364          43 :     element_list *t = new element_list(in, ptr->lineno,
    1365          43 :                                        ptr->minv, ptr->minh,
    1366          43 :                                        ptr->maxv, ptr->maxh);
    1367          43 :     if (ptr == tail)
    1368           1 :       tail = t;
    1369          43 :     ptr->right->left = t;
    1370          43 :     t->right = ptr->right;
    1371          43 :     ptr->right = t;
    1372          43 :     t->left = ptr;
    1373             :   }
    1374          43 : }
    1375             : 
    1376             : /*
    1377             :  *  move_to - moves the current position to the point where data, in,
    1378             :  *            exists.  This is an expensive method and should be used
    1379             :  *            sparingly.
    1380             :  */
    1381             : 
    1382          46 : void list::move_to (text_glob *in)
    1383             : {
    1384          46 :   ptr = head;
    1385      148090 :   while ((ptr != tail) && (ptr->datum != in))
    1386      148044 :     ptr = ptr->right;
    1387          46 : }
    1388             : 
    1389             : /*
    1390             :  *  page class and methods
    1391             :  */
    1392             : 
    1393             : class page {
    1394             : public:
    1395             :               page            (void);
    1396             :   void        add             (style * /* s */,
    1397             :                                const string & /* str */,
    1398             :                                int /* line_number */,
    1399             :                                int /* min_vertical */,
    1400             :                                int /* min_horizontal */,
    1401             :                                int /* max_vertical */,
    1402             :                                int /* max_horizontal */);
    1403             :   void        add_tag         (style * /* s */,
    1404             :                                const string & /* str */,
    1405             :                                int /* line_number */,
    1406             :                                int /* min_vertical */,
    1407             :                                int /* min_horizontal */,
    1408             :                                int /* max_vertical */,
    1409             :                                int /* max_horizontal */);
    1410             :   void        add_and_encode  (style * /* s */,
    1411             :                                const string & /* str */,
    1412             :                                int /* line_number */,
    1413             :                                int /* min_vertical */,
    1414             :                                int /* min_horizontal */,
    1415             :                                int /* max_vertical */,
    1416             :                                int /* max_horizontal */,
    1417             :                                int /* is_tag */); // TODO: boolify?
    1418             :   void        add_line        (style * /* s */,
    1419             :                                int /* line_number */,
    1420             :                                int /* x1 */,
    1421             :                                int /* y1 */,
    1422             :                                int /* x2 */,
    1423             :                                int /* y2 */,
    1424             :                                int /* thickness */);
    1425             :   void        insert_tag      (const string & /* str */);
    1426             :   void        dump_page       (void);   // debugging method
    1427             : 
    1428             :   // and the data
    1429             : 
    1430             :   list        glyphs;         // position of glyphs and specials on page
    1431             :   char_buffer buffer;         // all characters for this page
    1432             : };
    1433             : 
    1434          37 : page::page()
    1435             : {
    1436          37 : }
    1437             : 
    1438             : /*
    1439             :  *  insert_tag - inserts a tag after the current position.
    1440             :  */
    1441             : 
    1442          43 : void page::insert_tag (const string &str)
    1443             : {
    1444          43 :   if (str.length() > 0) {
    1445          43 :     text_glob *g=new text_glob();
    1446          43 :     text_glob *f=glyphs.get_data();
    1447          43 :     g->text_glob_tag(&f->text_style, buffer.add_string(str),
    1448             :                      str.length(), f->minv, f->minh, f->maxv, f->maxh);
    1449          43 :     glyphs.insert(g);
    1450             :   }
    1451          43 : }
    1452             : 
    1453             : /*
    1454             :  *  add - add html text to the list of glyphs.
    1455             :  */
    1456             : 
    1457       20281 : void page::add (style *s, const string &str,
    1458             :                 int line_number,
    1459             :                 int min_vertical, int min_horizontal,
    1460             :                 int max_vertical, int max_horizontal)
    1461             : {
    1462       20281 :   if (str.length() > 0) {
    1463       20281 :     text_glob *g=new text_glob();
    1464       20281 :     g->text_glob_html(s, buffer.add_string(str), str.length(),
    1465             :                       min_vertical, min_horizontal,
    1466             :                       max_vertical, max_horizontal);
    1467       20281 :     glyphs.add(g, line_number, min_vertical, min_horizontal,
    1468             :                                max_vertical, max_horizontal);
    1469             :   }
    1470       20281 : }
    1471             : 
    1472             : /*
    1473             :  *  add_tag - adds a troff tag, for example: .tl .sp .br
    1474             :  */
    1475             : 
    1476        4460 : void page::add_tag (style *s, const string &str,
    1477             :                     int line_number,
    1478             :                     int min_vertical, int min_horizontal,
    1479             :                     int max_vertical, int max_horizontal)
    1480             : {
    1481        4460 :   if (str.length() > 0) {
    1482             :     text_glob *g;
    1483             : 
    1484        8920 :     if (strncmp((str+'\0').contents(), "devtag:.auto-image",
    1485        4460 :                 strlen("devtag:.auto-image")) == 0) {
    1486          58 :       g = new text_glob();
    1487          58 :       g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
    1488             :                               min_vertical, min_horizontal,
    1489             :                               max_vertical, max_horizontal);
    1490             :     } else {
    1491        4402 :       g = new text_glob();
    1492        4402 :       g->text_glob_tag(s, buffer.add_string(str), str.length(),
    1493             :                        min_vertical, min_horizontal,
    1494             :                        max_vertical, max_horizontal);
    1495             :     }
    1496        4460 :     glyphs.add(g, line_number, min_vertical, min_horizontal,
    1497             :                                max_vertical, max_horizontal);
    1498             :   }
    1499        4460 : }
    1500             : 
    1501             : /*
    1502             :  *  add_line - adds the <line> primitive providing that y1==y2
    1503             :  */
    1504             : 
    1505           0 : void page::add_line (style *s,
    1506             :                      int line_number,
    1507             :                      int x_1, int y_1, int x_2, int y_2,
    1508             :                      int thickness)
    1509             : {
    1510           0 :   if (y_1 == y_2) {
    1511           0 :     text_glob *g = new text_glob();
    1512           0 :     g->text_glob_line(s,
    1513             :                       min(y_1, y_2), min(x_1, x_2),
    1514             :                       max(y_1, y_2), max(x_1, x_2),
    1515             :                       thickness);
    1516           0 :     glyphs.add(g, line_number,
    1517             :                min(y_1, y_2), min(x_1, x_2),
    1518             :                max(y_1, y_2), max(x_1, x_2));
    1519             :   }
    1520           0 : }
    1521             : 
    1522             : /*
    1523             :  *  to_numerical_char_ref - returns a numerical character reference of
    1524             :  *                          unicode character code `ch`.
    1525             :  */
    1526             : 
    1527           0 : static char *to_numerical_char_ref (unsigned int ch)
    1528             : {
    1529             :   // Make static buffer large enough for a 64-bit `int` type in
    1530             :   // hexadecimal (8 bytes) plus '&#x;' plus null terminator.
    1531             :   static char buf[8 + 4 + 1];
    1532           0 :   sprintf(buf, "&#x%X;", ch);
    1533           0 :   return buf;
    1534             : }
    1535             : 
    1536             : /*
    1537             :  *  add_and_encode - adds a special string to the page, it translates
    1538             :  *                   the string into html glyphs. The special string
    1539             :  *                   will have come from x X html: and can contain troff
    1540             :  *                   character encodings which appear as \[char]. A
    1541             :  *                   sequence of \\ represents \.
    1542             :  *                   So for example we can write:
    1543             :  *                      "cost = \[Po]3.00 file = \\foo\\bar"
    1544             :  *                   which is translated into:
    1545             :  *                      "cost = &pound;3.00 file = \foo\bar"
    1546             :  */
    1547             : 
    1548         846 : void page::add_and_encode (style *s,
    1549             :                            const string &str,
    1550             :                            int line_number,
    1551             :                            int min_vertical,
    1552             :                            int min_horizontal,
    1553             :                            int max_vertical,
    1554             :                            int max_horizontal,
    1555             :                            int is_tag)
    1556             : {
    1557         846 :   string html_string;
    1558             :   const char *html_glyph;
    1559         846 :   int i = 0;
    1560         846 :   const int len = str.length();
    1561             : 
    1562         846 :   assert(s != 0 /* nullptr */);
    1563         846 :   if (0 /* nullptr */ == s->f)
    1564           0 :     return;
    1565       11258 :   while (i < len) {
    1566       10412 :     if ((i + 1 < len) && (str.substring(i, 2) == string("\\["))) {
    1567             :       // start of escape
    1568           1 :       i += 2; // move over \[
    1569           1 :       int a = i;
    1570           3 :       while ((i < len) && (str[i] != ']'))
    1571           2 :         i++;
    1572           1 :       if (i > 0) {
    1573           2 :         string troff_charname = str.substring(a, i - a);
    1574           1 :         html_glyph = get_html_translation(s->f, troff_charname);
    1575           1 :         if (html_glyph != 0 /* nullptr */)
    1576           1 :           html_string += html_glyph;
    1577             :         else {
    1578           0 :           glyph *g = name_to_glyph((troff_charname + '\0').contents());
    1579           0 :           if (s->f->contains(g))
    1580           0 :             html_string += s->f->get_code(g);
    1581             :         }
    1582             :       }
    1583             :     }
    1584             :     else
    1585       10411 :       html_string += str[i];
    1586       10412 :     i++;
    1587             :   }
    1588         846 :   if (html_string.length() > 0) {
    1589         846 :     text_glob *g=new text_glob();
    1590         846 :     if (is_tag)
    1591         283 :       g->text_glob_tag(s, buffer.add_string(html_string),
    1592             :                        html_string.length(),
    1593             :                        min_vertical, min_horizontal,
    1594             :                        max_vertical, max_horizontal);
    1595             :     else
    1596         563 :       g->text_glob_special(s, buffer.add_string(html_string),
    1597             :                            html_string.length(),
    1598             :                            min_vertical, min_horizontal,
    1599             :                            max_vertical, max_horizontal);
    1600         846 :     glyphs.add(g, line_number, min_vertical,
    1601             :                min_horizontal, max_vertical, max_horizontal);
    1602             :   }
    1603             : }
    1604             : 
    1605             : /*
    1606             :  *  dump_page - dump the page contents for debugging purposes.
    1607             :  */
    1608             : 
    1609         120 : void page::dump_page(void)
    1610             : {
    1611             : #if defined(DEBUG_TABLES)
    1612             :   text_glob *old_pos = glyphs.get_data();
    1613             :   text_glob *g;
    1614             : 
    1615             :   printf("\n<!--\n");
    1616             :   printf("\n\ndebugging start\n");
    1617             :   glyphs.start_from_head();
    1618             :   do {
    1619             :     g = glyphs.get_data();
    1620             :     if (g->is_tab_ts()) {
    1621             :       printf("\n\n");
    1622             :       if (g->get_table() != 0 /* nullptr */)
    1623             :         g->get_table()->dump_table();
    1624             :     }
    1625             :     printf("%s ", g->text_string);
    1626             :     if (g->is_tab_te())
    1627             :       printf("\n\n");
    1628             :     glyphs.move_right();
    1629             :   } while (! glyphs.is_equal_to_head());
    1630             :   glyphs.move_to(old_pos);
    1631             :   printf("\ndebugging end\n\n");
    1632             :   printf("\n-->\n");
    1633             :   fflush(stdout);
    1634             : #endif
    1635         120 : }
    1636             : 
    1637             : /*
    1638             :  *  font classes and methods
    1639             :  */
    1640             : 
    1641             : class html_font : public font {
    1642             :   html_font(const char * /* nm */);
    1643             : public:
    1644             :   int encoding_index;
    1645             :   char *encoding;
    1646             :   char *reencoded_name;
    1647             :   ~html_font();
    1648             :   static html_font *load_html_font(const char * /* s */);
    1649             : };
    1650             : 
    1651          18 : html_font *html_font::load_html_font(const char *s)
    1652             : {
    1653          18 :   html_font *f = new html_font(s);
    1654          18 :   if (!f->load()) {
    1655           0 :     delete f;
    1656           0 :     return 0 /* nullptr */;
    1657             :   }
    1658          18 :   return f;
    1659             : }
    1660             : 
    1661          18 : html_font::html_font(const char *nm)
    1662          18 : : font(nm)
    1663             : {
    1664          18 : }
    1665             : 
    1666          36 : html_font::~html_font()
    1667             : {
    1668          36 : }
    1669             : 
    1670             : /*
    1671             :  *  a simple class to contain the header to this document
    1672             :  */
    1673             : 
    1674             : class title_desc {
    1675             : public:
    1676             :           title_desc ();
    1677             :          ~title_desc ();
    1678             : 
    1679             :   int     has_been_written;
    1680             :   int     has_been_found;
    1681             :   int     with_h1;
    1682             :   string  text;
    1683             : };
    1684             : 
    1685             : 
    1686          17 : title_desc::title_desc ()
    1687          17 :   : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
    1688             : {
    1689          17 : }
    1690             : 
    1691          17 : title_desc::~title_desc ()
    1692             : {
    1693          17 : }
    1694             : 
    1695             : class header_desc {
    1696             : public:
    1697             :               header_desc ();
    1698             :               ~header_desc ();
    1699             : 
    1700             :   int         no_of_level_one_headings; // how many .SH or .NH 1 have we found?
    1701             :   int         no_of_headings;           // how many headings have we found?
    1702             :   char_buffer headings;                 // all the headings used in the document
    1703             :   list        headers;                  // list of headers built from .NH and .SH
    1704             :   list        header_filename;          // in which file is this header?
    1705             :   int         header_level;             // current header level
    1706             :   int         written_header;           // have we written the header yet?
    1707             :                                         // TODO: boolify?
    1708             :   string      header_buffer;            // current header text
    1709             : 
    1710             :   void        write_headings (FILE * /* f */,
    1711             :                               int /* force */); // TODO: boolify
    1712             : };
    1713             : 
    1714          17 : header_desc::header_desc ()
    1715             :   :   no_of_level_one_headings(0), no_of_headings(0), header_level(2),
    1716          17 :        written_header(0)
    1717             : {
    1718          17 : }
    1719             : 
    1720          17 : header_desc::~header_desc ()
    1721             : {
    1722          17 : }
    1723             : 
    1724             : /*
    1725             :  *  write_headings - emits a list of links for the headings in this
    1726             :  *                   document
    1727             :  */
    1728             : 
    1729          35 : void header_desc::write_headings (FILE *f, int force)
    1730             : {
    1731             :   text_glob *g;
    1732             : 
    1733          35 :   assert(f != 0 /* nullptr */);
    1734          35 :   if (auto_links || force) {
    1735          34 :     if (! headers.is_empty()) {
    1736          23 :       int h=1;
    1737             : 
    1738          23 :       headers.start_from_head();
    1739          23 :       header_filename.start_from_head();
    1740          23 :       if (xhtml == dialect)
    1741           0 :         fputs("<p>", f);
    1742         362 :       do {
    1743         385 :         g = headers.get_data();
    1744         385 :         fputs("<a href=\"", f);
    1745         385 :         if (multiple_files && (! header_filename.is_empty())) {
    1746         377 :           text_glob *fn = header_filename.get_data();
    1747         377 :           fputs(fn->text_string, f);
    1748             :         }
    1749         385 :         fputs("#", f);
    1750         385 :         if (simple_anchors) {
    1751         612 :           string buffer(ANCHOR_TEMPLATE);
    1752             : 
    1753         306 :           buffer += as_string(h);
    1754         306 :           buffer += '\0';
    1755         306 :           fprintf(f, "%s", buffer.contents());
    1756             :         } else
    1757          79 :           fputs(g->text_string, f);
    1758         385 :         h++;
    1759         385 :         fputs("\">", f);
    1760         385 :         fputs(g->text_string, f);
    1761         385 :         fputs("</a>", f);
    1762         385 :         if (xhtml == dialect)
    1763           0 :           fputs("<br/>\n", f);
    1764             :         else
    1765         385 :           fputs("<br>\n", f);
    1766         385 :         headers.move_right();
    1767         385 :         if (multiple_files && (! header_filename.is_empty()))
    1768         377 :           header_filename.move_right();
    1769         385 :       } while (! headers.is_equal_to_head());
    1770          23 :       fputs("\n", f);
    1771          23 :       if (xhtml == dialect)
    1772           0 :         fputs("</p>\n", f);
    1773             :     }
    1774             :   }
    1775          35 : }
    1776             : 
    1777             : struct assert_pos {
    1778             :   assert_pos *next;
    1779             :   const char *val;
    1780             :   const char *id;
    1781             : };
    1782             : 
    1783             : class assert_state {
    1784             : public:
    1785             :         assert_state ();
    1786             :         ~assert_state ();
    1787             : 
    1788             :   void  addx (const char * /* c */,
    1789             :               const char * /* i */,
    1790             :               const char * /* v */,
    1791             :               const char * /* f */,
    1792             :               const char * /* l */);
    1793             :   void  addy (const char * /* c */,
    1794             :               const char * /* i */,
    1795             :               const char * /* v */,
    1796             :               const char * /* f */,
    1797             :               const char * /* l */);
    1798             :   void  build(const char * /* c */,
    1799             :               const char * /* v */,
    1800             :               const char * /* f */,
    1801             :               const char * /* l */);
    1802             :   void  check_br (int /* br */);
    1803             :   void  check_ce (int /* ce */);
    1804             :   void  check_fi (int /* fi */);
    1805             :   void  check_sp (int /* sp */);
    1806             :   void  reset    (void);
    1807             : 
    1808             : private:
    1809             :   int check_br_flag;
    1810             :   int check_ce_flag;
    1811             :   int check_fi_flag;
    1812             :   int check_sp_flag;
    1813             :   const char *val_br;
    1814             :   const char *val_ce;
    1815             :   const char *val_fi;
    1816             :   const char *val_sp;
    1817             :   const char *file_br;
    1818             :   const char *file_ce;
    1819             :   const char *file_fi;
    1820             :   const char *file_sp;
    1821             :   const char *line_br;
    1822             :   const char *line_ce;
    1823             :   const char *line_fi;
    1824             :   const char *line_sp;
    1825             : 
    1826             :   assert_pos *xhead;
    1827             :   assert_pos *yhead;
    1828             : 
    1829             :   void add             (assert_pos ** /* h */,
    1830             :                         const char * /* c */,
    1831             :                         const char * /* i */,
    1832             :                         const char * /* v */,
    1833             :                         const char * /* f */,
    1834             :                         const char * /* l */);
    1835             :   void compare         (assert_pos * /* t */,
    1836             :                         const char * /* v */,
    1837             :                         const char * /* f */,
    1838             :                         const char * /* l */);
    1839             :   void close           (const char * /* c */);
    1840             :   void set             (const char * /* c */,
    1841             :                         const char * /* v */,
    1842             :                         const char * /* f */,
    1843             :                         const char * /* l */);
    1844             :   void check_value     (const char * /* s */,
    1845             :                         int  /* v */,
    1846             :                         const char *nam /* e */,
    1847             :                         const char * /* f */,
    1848             :                         const char * /* l */,
    1849             :                         int * /* flag */); // TODO: boolify
    1850             :   int check_value_error(int  /* c */,
    1851             :                         int  /* v */,
    1852             :                         const char * /* s */,
    1853             :                         const char *nam /* e */,
    1854             :                         const char * /* f */,
    1855             :                         const char * /* l */,
    1856             :                         int /* flag */); // TODO: boolify
    1857             : };
    1858             : 
    1859          17 : assert_state::assert_state ()
    1860             : {
    1861          17 :   reset();
    1862          17 :   val_br   = 0;
    1863          17 :   val_ce   = 0;
    1864          17 :   val_fi   = 0;
    1865          17 :   val_sp   = 0;
    1866          17 :   file_br  = 0;
    1867          17 :   file_ce  = 0;
    1868          17 :   file_fi  = 0;
    1869          17 :   file_sp  = 0;
    1870          17 :   line_br  = 0;
    1871          17 :   line_ce  = 0;
    1872          17 :   line_fi  = 0;
    1873          17 :   line_sp  = 0;
    1874          17 :   xhead    = 0;
    1875          17 :   yhead    = 0;
    1876          17 : }
    1877             : 
    1878          34 : assert_state::~assert_state ()
    1879             : {
    1880             :   assert_pos *t;
    1881             : 
    1882          17 :   while (xhead != 0 /* nullptr */) {
    1883           0 :     t = xhead;
    1884           0 :     xhead = xhead->next;
    1885           0 :     delete[] t->val;
    1886           0 :     delete[] t->id;
    1887           0 :     delete t;
    1888             :   }
    1889          17 :   while (yhead != 0 /* nullptr */) {
    1890           0 :     t = yhead;
    1891           0 :     yhead = yhead->next;
    1892           0 :     delete[] t->val;
    1893           0 :     delete[] t->id;
    1894           0 :     delete t;
    1895             :   }
    1896          17 : }
    1897             : 
    1898          60 : void assert_state::reset (void)
    1899             : {
    1900          60 :   check_br_flag = 0;
    1901          60 :   check_ce_flag = 0;
    1902          60 :   check_fi_flag = 0;
    1903          60 :   check_sp_flag = 0;
    1904          60 : }
    1905             : 
    1906           0 : void assert_state::add (assert_pos **h,
    1907             :                         const char *c, const char *i, const char *v,
    1908             :                         const char *f, const char *l)
    1909             : {
    1910           0 :   assert(h != 0 /* nullptr */);
    1911           0 :   assert(*h != 0 /* nullptr */);
    1912             : 
    1913           0 :   assert_pos *t = *h;
    1914             : 
    1915           0 :   while (t != 0 /* nullptr */) {
    1916           0 :     if (strcmp(t->id, i) == 0)
    1917           0 :       break;
    1918           0 :     t = t->next;
    1919             :   }
    1920           0 :   if ((t != 0 /* nullptr */) && (v != 0 /* nullptr */) && (v[0] != '='))
    1921           0 :     compare(t, v, f, l);
    1922             :   else {
    1923           0 :     if (0 /* nullptr */ == t) {
    1924           0 :       t = new assert_pos;
    1925           0 :       t->next = *h;
    1926           0 :       (*h) = t;
    1927             :     }
    1928           0 :     if ((0 == v) || (v[0] != '=')) {
    1929           0 :       if (0 /* nullptr */ == f)
    1930           0 :         f = strsave("stdin");
    1931           0 :       if (0 /* nullptr */ == l)
    1932           0 :         l = strsave("<none>");
    1933           0 :       if (0 /* nullptr */ == v)
    1934           0 :         v = "no value at all";
    1935           0 :       fprintf(stderr, "%s:%s:%s: error in assertion format of id=%s;"
    1936             :               " expected value prefixed with an '=', got %s\n",
    1937             :               program_name, f, l, i, v);
    1938             :     }
    1939           0 :     t->id = i;
    1940           0 :     t->val = v;
    1941           0 :     delete[] c;
    1942           0 :     delete[] f;
    1943           0 :     delete[] l;
    1944             :   }
    1945           0 : }
    1946             : 
    1947           0 : void assert_state::addx (const char *c, const char *i, const char *v,
    1948             :                          const char *f, const char *l)
    1949             : {
    1950           0 :   add(&xhead, c, i, v, f, l);
    1951           0 : }
    1952             : 
    1953           0 : void assert_state::addy (const char *c, const char *i, const char *v,
    1954             :                          const char *f, const char *l)
    1955             : {
    1956           0 :   add(&yhead, c, i, v, f, l);
    1957           0 : }
    1958             : 
    1959           0 : void assert_state::compare(assert_pos *t,
    1960             :                            const char *v, const char *f, const char *l)
    1961             : {
    1962           0 :   assert(t != 0 /* nullptr */);
    1963           0 :   assert(v != 0 /* nullptr */);
    1964           0 :   const char *s=t->val;
    1965             : 
    1966           0 :   while ((*v) == '=')
    1967           0 :     v++;
    1968           0 :   while ((*s) == '=')
    1969           0 :     s++;
    1970             : 
    1971           0 :   if (strcmp(v, s) != 0) {
    1972           0 :     if (0 /* nullptr */ == f)
    1973           0 :       f = "stdin";
    1974           0 :     if (0 /* nullptr */ == l)
    1975           0 :       l = "<none>";
    1976           0 :     fprintf(stderr, "%s:%s:%s: grohtml assertion failed at id%s: "
    1977             :                     "expected %s, got %s\n", program_name, f, l, t->id,
    1978             :                     s, v);
    1979             :   }
    1980           0 : }
    1981             : 
    1982           0 : void assert_state::close (const char *c)
    1983             : {
    1984           0 :   assert(c != 0 /* nullptr */);
    1985           0 :   if (strcmp(c, "sp") == 0)
    1986           0 :     check_sp_flag = 0;
    1987           0 :   else if (strcmp(c, "br") == 0)
    1988           0 :     check_br_flag = 0;
    1989           0 :   else if (strcmp(c, "fi") == 0)
    1990           0 :     check_fi_flag = 0;
    1991           0 :   else if (strcmp(c, "nf") == 0)
    1992           0 :     check_fi_flag = 0;
    1993           0 :   else if (strcmp(c, "ce") == 0)
    1994           0 :     check_ce_flag = 0;
    1995             :   else
    1996           0 :     fprintf(stderr, "%s: ignoring unrecognized tag '%s'\n",
    1997             :             program_name, c);
    1998           0 : }
    1999             : 
    2000           0 : const char *replace_negate_str (const char *before, char *after)
    2001             : {
    2002           0 :   if (before != 0 /* nullptr */)
    2003           0 :     delete[] before;
    2004             : 
    2005           0 :   assert(after != 0 /* nullptr */);
    2006           0 :   if (strlen(after) > 0) {
    2007           0 :     int d = atoi(after);
    2008             : 
    2009           0 :     if ((d < 0) || (d > 1)) {
    2010           0 :       fprintf(stderr, "%s: expected nf/fi value of 0 or 1, got '%s';"
    2011             :               " ignoring\n", program_name, after);
    2012           0 :       d = 0;
    2013             :     }
    2014           0 :     if (0 == d)
    2015           0 :       after[0] = '1';
    2016             :     else
    2017           0 :       after[0] = '0';
    2018           0 :     after[1] = '\0';
    2019             :   }
    2020           0 :   return after;
    2021             : }
    2022             : 
    2023           0 : const char *replace_str (const char *before, const char *after)
    2024             : {
    2025           0 :   if (before != 0 /* nullptr */)
    2026           0 :     delete[] before;
    2027           0 :   return after;
    2028             : }
    2029             : 
    2030           0 : void assert_state::set (const char *c, const char *v,
    2031             :                         const char *f, const char *l)
    2032             : {
    2033           0 :   assert(c != 0 /* nullptr */);
    2034           0 :   assert(v != 0 /* nullptr */);
    2035             : 
    2036           0 :   if (0 /* nullptr */ == l)
    2037           0 :     l = "<none>";
    2038           0 :   if (0 /* nullptr */ == f)
    2039           0 :     f = "stdin";
    2040             : 
    2041             :   // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
    2042           0 :   if (strcmp(c, "sp") == 0) {
    2043           0 :     check_sp_flag = 1;
    2044           0 :     val_sp = replace_str(val_sp, strsave(v));
    2045           0 :     file_sp = replace_str(file_sp, strsave(f));
    2046           0 :     line_sp = replace_str(line_sp, strsave(l));
    2047           0 :   } else if (strcmp(c, "br") == 0) {
    2048           0 :     check_br_flag = 1;
    2049           0 :     val_br = replace_str(val_br, strsave(v));
    2050           0 :     file_br = replace_str(file_br, strsave(f));
    2051           0 :     line_br = replace_str(line_br, strsave(l));
    2052           0 :   } else if (strcmp(c, "fi") == 0) {
    2053           0 :     check_fi_flag = 1;
    2054           0 :     val_fi = replace_str(val_fi, strsave(v));
    2055           0 :     file_fi = replace_str(file_fi, strsave(f));
    2056           0 :     line_fi = replace_str(line_fi, strsave(l));
    2057           0 :   } else if (strcmp(c, "nf") == 0) {
    2058           0 :     check_fi_flag = 1;
    2059           0 :     val_fi = replace_negate_str(val_fi, strsave(v));
    2060           0 :     file_fi = replace_str(file_fi, strsave(f));
    2061           0 :     line_fi = replace_str(line_fi, strsave(l));
    2062           0 :   } else if (strcmp(c, "ce") == 0) {
    2063           0 :     check_ce_flag = 1;
    2064           0 :     val_ce = replace_str(val_ce, strsave(v));
    2065           0 :     file_ce = replace_str(file_ce, strsave(f));
    2066           0 :     line_ce = replace_str(line_ce, strsave(l));
    2067             :   }
    2068           0 : }
    2069             : 
    2070             : /*
    2071             :  *  build - builds the troff state assertion.
    2072             :  *          see tmac/www.tmac for cmd examples.
    2073             :  */
    2074             : 
    2075           0 : void assert_state::build (const char *c, const char *v,
    2076             :                           const char *f, const char *l)
    2077             : {
    2078           0 :   assert(c != 0 /* nullptr */);
    2079           0 :   if (c[0] == '{')
    2080           0 :     set(&c[1], v, f, l);
    2081           0 :   if (c[0] == '}')
    2082           0 :     close(&c[1]);
    2083           0 : }
    2084             : 
    2085             : // TODO: boolify
    2086           0 : int assert_state::check_value_error (int c, int v, const char *s,
    2087             :                                      const char *name, const char *f,
    2088             :                                      const char *l, int flag)
    2089             : {
    2090           0 :   if (0 /* nullptr */ == c) {
    2091           0 :     if (0 /* nullptr */ == f)
    2092           0 :       f = "stdin";
    2093           0 :     if (0 /* nullptr */ == l)
    2094           0 :       l = "<none>";
    2095           0 :     fprintf(stderr, "%s:%s:%s: troff state assertion failed; "
    2096             :             "expected %s to be %s, got %d\n", program_name, f, l, name,
    2097             :             s, v);
    2098           0 :     return 0;
    2099             :   }
    2100           0 :   return flag;
    2101             : }
    2102             : 
    2103           0 : void assert_state::check_value (const char *s, int v, const char *name,
    2104             :                                 const char *f, const char *l, int *flag)
    2105             : {
    2106           0 :   assert(s != 0 /* nullptr */);
    2107           0 :   assert(flag != 0 /* nullptr */);
    2108           0 :   if (strncmp(s, "<=", 2) == 0)
    2109           0 :     *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
    2110           0 :   else if (strncmp(s, ">=", 2) == 0)
    2111           0 :     *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
    2112           0 :   else if (strncmp(s, "==", 2) == 0)
    2113           0 :     *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
    2114           0 :   else if (strncmp(s, "!=", 2) == 0)
    2115           0 :     *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
    2116           0 :   else if (strncmp(s, "<", 1) == 0)
    2117           0 :     *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
    2118           0 :   else if (strncmp(s, ">", 1) == 0)
    2119           0 :     *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
    2120           0 :   else if (strncmp(s, "=", 1) == 0)
    2121           0 :     *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
    2122             :   else
    2123           0 :     *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
    2124           0 : }
    2125             : 
    2126       21463 : void assert_state::check_sp (int sp)
    2127             : {
    2128       21463 :   if (check_sp_flag)
    2129           0 :     check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
    2130       21463 : }
    2131             : 
    2132      101969 : void assert_state::check_fi (int fi)
    2133             : {
    2134      101969 :   if (check_fi_flag)
    2135           0 :     check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
    2136      101969 : }
    2137             : 
    2138       21774 : void assert_state::check_br (int br)
    2139             : {
    2140       21774 :   if (check_br_flag)
    2141           0 :     check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
    2142       21774 : }
    2143             : 
    2144       25189 : void assert_state::check_ce (int ce)
    2145             : {
    2146       25189 :   if (check_ce_flag)
    2147           0 :     check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
    2148       25189 : }
    2149             : 
    2150             : class html_printer : public printer {
    2151             :   files                file_list;
    2152             :   simple_output        html;
    2153             :   int                  res;
    2154             :   glyph               *space_glyph;
    2155             :   int                  space_width;
    2156             :   int                  no_of_printed_pages;
    2157             :   int                  paper_length;
    2158             :   string               sbuf;
    2159             :   int                  sbuf_start_hpos;
    2160             :   int                  sbuf_vpos;
    2161             :   int                  sbuf_end_hpos;
    2162             :   int                  sbuf_prev_hpos;
    2163             :   int                  sbuf_kern;
    2164             :   style                sbuf_style;
    2165             :   int                  last_sbuf_length;
    2166             :   int                  overstrike_detected;
    2167             :   style                output_style;
    2168             :   int                  output_hpos;
    2169             :   int                  output_vpos;
    2170             :   int                  output_vpos_max;
    2171             :   int                  output_draw_point_size;
    2172             :   int                  line_thickness;
    2173             :   int                  output_line_thickness;
    2174             :   unsigned char        output_space_code;
    2175             :   char                *inside_font_style;
    2176             :   int                  page_number;
    2177             :   title_desc           title;
    2178             :   header_desc          header;
    2179             :   int                  header_indent;
    2180             :   int                  suppress_sub_sup;
    2181             :   int                  cutoff_heading;
    2182             :   page                *page_contents;
    2183             :   html_text           *current_paragraph;
    2184             :   html_indent         *indent;
    2185             :   html_table          *table;
    2186             :   int                  end_center;
    2187             :   int                  end_tempindent;
    2188             :   TAG_ALIGNMENT        next_tag;
    2189             :   int                  fill_on;
    2190             :   int                  max_linelength;
    2191             :   int                  linelength;
    2192             :   int                  pageoffset;
    2193             :   int                  troff_indent;
    2194             :   int                  device_indent;
    2195             :   int                  temp_indent;
    2196             :   int                  pointsize;
    2197             :   int                  vertical_spacing;
    2198             :   int                  line_number;
    2199             :   color               *background;
    2200             :   int                  seen_indent;
    2201             :   int                  next_indent;
    2202             :   int                  seen_pageoffset;
    2203             :   int                  next_pageoffset;
    2204             :   int                  seen_linelength;
    2205             :   int                  next_linelength;
    2206             :   int                  seen_center;
    2207             :   int                  next_center;
    2208             :   int                  seen_space;
    2209             :   int                  seen_break;
    2210             :   int                  current_column;
    2211             :   int                  row_space;
    2212             :   assert_state         as;
    2213             : 
    2214             :   void  flush_sbuf            ();
    2215             :   void  set_style             (const style & /* sty */);
    2216             :   void  set_space_code        (unsigned char /* c */);
    2217             :   void  set_line_thickness    (const environment * /* env */);
    2218             :   void  terminate_current_font(void);
    2219             :   void  flush_font            (void);
    2220             :   void  add_to_sbuf           (glyph * /* g */,
    2221             :                                const string & /* s */);
    2222             :   void  write_title           (int /* in_head */); // TODO: boolify
    2223             :   int   sbuf_continuation     (glyph * /* g */,
    2224             :                                const char * /* name */,
    2225             :                                const environment * /* env */,
    2226             :                                int /* w */);
    2227             :   void  flush_page            (void);
    2228             :   void  troff_tag             (text_glob * /* g */);
    2229             :   void  flush_globs           (void);
    2230             :   void  emit_line             (text_glob * /* g */);
    2231             :   void  emit_raw              (text_glob * /* g */);
    2232             :   void  emit_html             (text_glob * /* g */);
    2233             :   void  determine_space       (text_glob * /* g */);
    2234             :   void  start_font            (const char * /* name */);
    2235             :   void  end_font              (const char * /* name */);
    2236             :   int   is_font_courier       (font * /* f */);
    2237             :   int   is_line_start         (int /* nf */); // TODO: boolify
    2238             :   int   is_courier_until_eol  (void);
    2239             :   void  start_size            (int /* from */,
    2240             :                                int /* to */);
    2241             :   void  do_font               (text_glob * /* g */);
    2242             :   void  do_center             (char * /* arg */);
    2243             :   void  do_check_center       (void);
    2244             :   void  do_break              (void);
    2245             :   void  do_space              (char * /* arg */);
    2246             :   void  do_eol                (void);
    2247             :   void  do_eol_ce             (void);
    2248             :   void  do_title              (void);
    2249             :   void  do_fill               (char * /* arg */);
    2250             :   void  do_heading            (char * /* arg */);
    2251             :   void  write_header          (void);
    2252             :   void  determine_header_level(int /* level */);
    2253             :   void  do_linelength         (char * /* arg */);
    2254             :   void  do_pageoffset         (char * /* arg */);
    2255             :   void  do_indentation        (char * /* arg */);
    2256             :   void  do_tempindent         (char * /* arg */);
    2257             :   void  do_indentedparagraph  (void);
    2258             :   void  do_verticalspacing    (char * /* arg */);
    2259             :   void  do_pointsize          (char * /* arg */);
    2260             :   void  do_centered_image     (void);
    2261             :   void  do_left_image         (void);
    2262             :   void  do_right_image        (void);
    2263             :   void  do_auto_image         (text_glob * /* g */,
    2264             :                                const char * /* filename */);
    2265             :   void  do_links              (void);
    2266             :   void  do_flush              (void);
    2267             :   void  do_job_name           (char * /* name */);
    2268             :   void  do_head               (char * /* name */);
    2269             :   void  insert_split_file     (void);
    2270             :   int   is_in_middle          (int /* left */,
    2271             :                                int /* right */);
    2272             :   void  do_sup_or_sub         (text_glob * /* g */);
    2273             :   int   start_subscript       (text_glob * /* g */);
    2274             :   int   end_subscript         (text_glob * /* g */);
    2275             :   int   start_superscript     (text_glob * /* g */);
    2276             :   int   end_superscript       (text_glob * /* g */);
    2277             :   void  outstanding_eol       (int /* n */);
    2278             :   int   is_bold               (font * /* f */);
    2279             :   font *make_bold             (font * /* f */);
    2280             :   int   overstrike            (glyph * /* g */,
    2281             :                                const char * /* name */,
    2282             :                                const environment * /* env */,
    2283             :                                int /* w */);
    2284             :   void  do_body               (void);
    2285             :   int   next_horiz_pos        (text_glob * /* g */,
    2286             :                                int /* nf */); // TODO: boolify
    2287             :   void  lookahead_for_tables  (void);
    2288             :   void  insert_tab_te         (void);
    2289             :   text_glob *insert_tab_ts    (text_glob * /* where */);
    2290             :   void insert_tab0_foreach_tab(void);
    2291             :   void insert_tab_0           (text_glob * /* where */);
    2292             :   void do_indent              (int /* in */,
    2293             :                                int /* pageoff */,
    2294             :                                int /* linelen */);
    2295             :   void shutdown_table         (void);
    2296             :   void do_tab_ts              (text_glob * /* g */);
    2297             :   void do_tab_te              (void);
    2298             :   void do_col                 (char * /* s */);
    2299             :   void do_tab                 (char * /* s */);
    2300             :   void do_tab0                (void);
    2301             :   int  calc_nf                (text_glob * /* g */,
    2302             :                                int nf); // TODO: boolify
    2303             :   void calc_po_in             (text_glob * /* g */,
    2304             :                                int nf); // TODO: boolify
    2305             :   void remove_tabs            (void);
    2306             :   void remove_courier_tabs    (void);
    2307             :   void update_min_max         (colType /* type_of_col */,
    2308             :                                int * /* minimum */,
    2309             :                                int * /* maximum */,
    2310             :                                text_glob * /* g */);
    2311             :   void add_table_end          (const char * /* debug_string */);
    2312             :   void do_file_components     (void);
    2313             :   void write_navigation       (const string & /* top */,
    2314             :                                const string & /* prev */,
    2315             :                                const string & /* next */,
    2316             :                                const string & /* current */);
    2317             :   void emit_link              (const string & /* to */,
    2318             :                                const char * /* name */);
    2319             :   int  get_troff_indent       (void);
    2320             :   void restore_troff_indent   (void);
    2321             :   void handle_assertion       (int /* minv */,
    2322             :                                int /* minh */,
    2323             :                                int /* maxv */,
    2324             :                                int /* maxh */,
    2325             :                                const char * /* s */);
    2326             :   void handle_state_assertion (text_glob * /* g */);
    2327             :   void do_end_para            (text_glob * /* g */);
    2328             :   int  round_width            (int /* x */);
    2329             :   void handle_tag_within_title(text_glob * /* g */);
    2330             :   void writeHeadMetaStyle     (void);
    2331             :   void handle_valid_flag      (int /* needs_para */); // TODO: boolify
    2332             :   void do_math                (text_glob * /* g */);
    2333             :   void write_html_anchor      (text_glob * /* h */);
    2334             :   void write_xhtml_anchor     (text_glob * /* h */);
    2335             :   // ADD HERE
    2336             : 
    2337             : public:
    2338             :   html_printer             ();
    2339             :   ~html_printer            ();
    2340             :   void set_char            (glyph * /*g */,
    2341             :                             font * /*f */,
    2342             :                             const environment * /* env */,
    2343             :                             int /* w */,
    2344             :                             const char * /* name */);
    2345             :   void set_numbered_char   (int /* num */,
    2346             :                             const environment * /* env */,
    2347             :                             int * /* widthp */);
    2348             :   glyph *set_char_and_width(const char * /* nm */,
    2349             :                             const environment * /* env */,
    2350             :                             int * /* widthp */,
    2351             :                             font ** /* f */);
    2352             :   void draw                (int /* code */,
    2353             :                             int * /* p */,
    2354             :                             int np, // TODO: -> npoints
    2355             :                             const environment * /* env */);
    2356             :   void begin_page          (int /* n */);
    2357             :   void end_page            (int); // XXX: unused argument
    2358             :   void special             (char * /* arg */,
    2359             :                             const environment * /* env */,
    2360             :                             char /* type */);
    2361             :   void devtag              (char * /* arg */,
    2362             :                             const environment * /* env */,
    2363             :                             char /* type */);
    2364             :   font *make_font          (const char * /* nm */);
    2365             :   void end_of_line         ();
    2366             : };
    2367             : 
    2368          17 : printer *make_printer()
    2369             : {
    2370          17 :   return new html_printer;
    2371             : }
    2372             : 
    2373             : static void usage(FILE *stream);
    2374             : 
    2375       20281 : void html_printer::set_style(const style &sty)
    2376             : {
    2377       20281 :   const char *fontname = sty.f->get_filename();
    2378       20281 :   if (0 /* nullptr */ == fontname)
    2379             :     // XXX: Is this the only circumstance that can cause a null font
    2380             :     // description file name?  ps.cpp uses sty.f->get_internal_name()...
    2381           0 :     fatal("cannot set style; font description file '%1' lacks an"
    2382           0 :           " 'internalname' directive", fontname);
    2383             : 
    2384             : #if 0
    2385             :   change_font(fontname, (font::res / (72 * font::sizescale))
    2386             :                         * sty.point_size);
    2387             : #endif
    2388       20281 : }
    2389             : 
    2390             : /*
    2391             :  *  is_bold - returns TRUE if font, f, is bold.
    2392             :  */
    2393             : 
    2394           0 : int html_printer::is_bold (font *f)
    2395             : {
    2396           0 :   assert(f != 0 /* nullptr */);
    2397             :   // XXX: This property should be inferred from font description data,
    2398             :   // not the file name.
    2399           0 :   const char *fontname = f->get_filename();
    2400           0 :   return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
    2401             : }
    2402             : 
    2403             : /*
    2404             :  *  make_bold - if a bold style for f exists, return it.
    2405             :  */
    2406             : 
    2407           0 : font *html_printer::make_bold (font *f)
    2408             : {
    2409           0 :   assert(f != 0 /* nullptr */);
    2410             :   // XXX: This logic locks us into a font description file naming
    2411             :   // scheme.
    2412           0 :   const char *fontname = f->get_filename();
    2413             : 
    2414           0 :   if (strcmp(fontname, "B") == 0)
    2415           0 :     return f;
    2416           0 :   if (strcmp(fontname, "I") == 0)
    2417           0 :     return font::load_font("BI");
    2418           0 :   if (strcmp(fontname, "BI") == 0)
    2419           0 :     return f;
    2420           0 :   return 0 /* nullptr */;
    2421             : }
    2422             : 
    2423        5452 : void html_printer::end_of_line()
    2424             : {
    2425        5452 :   flush_sbuf();
    2426        5452 :   line_number++;
    2427        5452 : }
    2428             : 
    2429             : /*
    2430             :  *  emit_line - writes out a horizontal rule.
    2431             :  */
    2432             : 
    2433           0 : void html_printer::emit_line (text_glob *)
    2434             : {
    2435             :   // --fixme-- needs to know the length in percentage
    2436           0 :   if (xhtml == dialect)
    2437           0 :     html.put_string("<hr/>");
    2438             :   else
    2439           0 :     html.put_string("<hr>");
    2440           0 : }
    2441             : 
    2442             : /*
    2443             :  *  restore_troff_indent - is called when we have temporarily shutdown
    2444             :  *                         indentation (typically done when we have
    2445             :  *                         centered an image).
    2446             :  */
    2447             : 
    2448          76 : void html_printer::restore_troff_indent (void)
    2449             : {
    2450          76 :   troff_indent = next_indent;
    2451          76 :   if (troff_indent > 0) {
    2452             :     /*
    2453             :      *  force device indentation
    2454             :      */
    2455          68 :     device_indent = 0;
    2456          68 :     do_indent(get_troff_indent(), pageoffset, linelength);
    2457             :   }
    2458          76 : }
    2459             : 
    2460             : /*
    2461             :  *  emit_raw - writes the raw html information directly to the device.
    2462             :  */
    2463             : 
    2464          56 : void html_printer::emit_raw (text_glob *g)
    2465             : {
    2466          56 :   assert(g != 0 /* nullptr */);
    2467          56 :   do_font(g);
    2468          56 :   if (next_tag == INLINE) {
    2469           0 :     determine_space(g);
    2470           0 :     current_paragraph->do_emittext(g->text_string, g->text_length);
    2471             :   } else {
    2472             :     // TODO: boolify
    2473          56 :     int space = current_paragraph->retrieve_para_space() || seen_space;
    2474             : 
    2475          56 :     current_paragraph->done_para();
    2476          56 :     shutdown_table();
    2477          56 :     switch (next_tag) {
    2478             : 
    2479          55 :     case CENTERED:
    2480          55 :       if (html4 == dialect)
    2481          55 :         current_paragraph->do_para("align=\"center\"", space);
    2482             :       else
    2483           0 :         current_paragraph->do_para("class=\"center\"", space);
    2484          55 :       break;
    2485           1 :     case LEFT:
    2486           1 :       if (html4 == dialect)
    2487           1 :         current_paragraph->do_para(&html, "align=\"left\"",
    2488             :                                    get_troff_indent(), pageoffset,
    2489             :                                    linelength, space);
    2490             :       else
    2491           0 :         current_paragraph->do_para(&html, "class=\"left\"",
    2492             :                                    get_troff_indent(), pageoffset,
    2493             :                                    linelength, space);
    2494           1 :       break;
    2495           0 :     case RIGHT:
    2496           0 :       if (html4 == dialect)
    2497           0 :         current_paragraph->do_para(&html, "align=\"right\"",
    2498             :                                    get_troff_indent(), pageoffset,
    2499             :                                    linelength, space);
    2500             :       else
    2501           0 :         current_paragraph->do_para(&html, "class=\"right\"",
    2502             :                                    get_troff_indent(), pageoffset,
    2503             :                                    linelength, space);
    2504           0 :       break;
    2505           0 :     default:
    2506           0 :       fatal("unknown enumeration");
    2507             :     }
    2508          56 :     current_paragraph->do_emittext(g->text_string, g->text_length);
    2509          56 :     current_paragraph->done_para();
    2510          56 :     next_tag        = INLINE;
    2511          56 :     suppress_sub_sup = TRUE;
    2512          56 :     seen_space      = FALSE;
    2513          56 :     restore_troff_indent();
    2514             :   }
    2515          56 : }
    2516             : 
    2517             : /*
    2518             :  *  handle_tag_within_title - handle a limited number of tags within
    2519             :  *                            the context of a table. Those tags which
    2520             :  *                            set values rather than generate spaces
    2521             :  *                            and paragraphs.
    2522             :  */
    2523             : 
    2524           4 : void html_printer::handle_tag_within_title (text_glob *g)
    2525             : {
    2526           4 :   assert(g != 0 /* nullptr */);
    2527           4 :   if (g->is_in()
    2528           4 :       || g->is_ti()
    2529           4 :       || g->is_po()
    2530           4 :       || g->is_ce()
    2531           3 :       || g->is_ll()
    2532           2 :       || g->is_fi()
    2533           8 :       || g->is_nf())
    2534           3 :     troff_tag(g);
    2535           4 : }
    2536             : 
    2537             : /*
    2538             :  *  do_center - handle the .ce commands from troff.
    2539             :  */
    2540             : 
    2541         125 : void html_printer::do_center (char *arg)
    2542             : {
    2543         125 :   assert(arg != 0 /* nullptr */);
    2544         125 :   next_center = atoi(arg);
    2545         125 :   seen_center = TRUE;
    2546         125 : }
    2547             : 
    2548             : /*
    2549             :  *  do_centered_image - set a flag such that the next devtag is
    2550             :  *                      placed inside a centered paragraph.
    2551             :  */
    2552             : 
    2553          57 : void html_printer::do_centered_image (void)
    2554             : {
    2555          57 :   next_tag = CENTERED;
    2556          57 : }
    2557             : 
    2558             : /*
    2559             :  *  do_right_image - set a flag such that the next devtag is
    2560             :  *                   placed inside a right aligned paragraph.
    2561             :  */
    2562             : 
    2563           0 : void html_printer::do_right_image (void)
    2564             : {
    2565           0 :   next_tag = RIGHT;
    2566           0 : }
    2567             : 
    2568             : /*
    2569             :  *  do_left_image - set a flag such that the next devtag is
    2570             :  *                  placed inside a left aligned paragraph.
    2571             :  */
    2572             : 
    2573           1 : void html_printer::do_left_image (void)
    2574             : {
    2575           1 :   next_tag = LEFT;
    2576           1 : }
    2577             : 
    2578             : /*
    2579             :  *  exists - returns TRUE if filename exists.
    2580             :  */
    2581             : 
    2582             : // TODO: boolify
    2583          58 : static int exists (const char *filename)
    2584             : {
    2585          58 :   assert(filename != 0 /* nullptr */);
    2586          58 :   FILE *fp = fopen(filename, "r");
    2587             : 
    2588          58 :   if (0 /* nullptr */ == fp) {
    2589           2 :     return FALSE;
    2590             :   } else {
    2591          56 :     fclose(fp);
    2592          56 :     return TRUE;
    2593             :   }
    2594             : }
    2595             : 
    2596             : /*
    2597             :  *  generate_img_src - returns a html image tag for the filename
    2598             :  *                     providing that the image exists.
    2599             :  */
    2600             : 
    2601          58 : static string &generate_img_src (const char *filename)
    2602             : {
    2603          58 :   assert(filename != 0 /* nullptr */);
    2604          58 :   string *s = new string("");
    2605             : 
    2606         116 :   while ((filename != 0 /* nullptr */) && (' ' == filename[0]))
    2607          58 :     filename++;
    2608          58 :   if (exists(filename)) {
    2609         112 :     *s += string("<img src=\"") + filename + "\" "
    2610          56 :           + "alt=\"Image " + filename + "\">";
    2611          56 :     if (xhtml == dialect)
    2612           0 :       *s += "</img>";
    2613             :   }
    2614          58 :   return *s;
    2615             : }
    2616             : 
    2617             : /*
    2618             :  *  do_auto_image - tests whether the image, indicated by filename,
    2619             :  *                  is present, if so then it emits an html image tag.
    2620             :  *                  An image tag may be passed through from pic, eqn
    2621             :  *                  but the corresponding image might not be created.
    2622             :  *                  Consider .EQ delim $$ .EN  or an empty .PS .PE.
    2623             :  */
    2624             : 
    2625          58 : void html_printer::do_auto_image (text_glob *g, const char *filename)
    2626             : {
    2627          58 :   assert(g != 0 /* nullptr */);
    2628          58 :   assert(filename != 0 /* nullptr */);
    2629         116 :   string buffer = generate_img_src(filename);
    2630             : 
    2631          58 :   if (! buffer.empty()) {
    2632             :     /*
    2633             :      *  utilize emit_raw by creating a new text_glob.
    2634             :      */
    2635          56 :     text_glob h = *g;
    2636             : 
    2637          56 :     h.text_string = buffer.contents();
    2638          56 :     h.text_length = buffer.length();
    2639          56 :     emit_raw(&h);
    2640             :   } else
    2641           2 :     next_tag = INLINE;
    2642          58 : }
    2643             : 
    2644             : /*
    2645             :  *  outstanding_eol - call do_eol, n, times.
    2646             :  */
    2647             : 
    2648           0 : void html_printer::outstanding_eol (int n)
    2649             : {
    2650           0 :   while (n > 0) {
    2651           0 :     do_eol();
    2652           0 :     n--;
    2653             :   }
    2654           0 : }
    2655             : 
    2656             : /*
    2657             :  *  do_title - handle the .tl commands from troff.
    2658             :  */
    2659             : 
    2660           6 : void html_printer::do_title (void)
    2661             : {
    2662             :   text_glob    *t;
    2663             :   int           removed_from_head;
    2664             : 
    2665           6 :   if (1 == page_number) {
    2666           6 :     int found_title_start  = FALSE;
    2667           6 :     if (! page_contents->glyphs.is_empty()) {
    2668           6 :       page_contents->glyphs.sub_move_right(); // move onto next word
    2669          18 :       do {
    2670          24 :         t = page_contents->glyphs.get_data();
    2671          24 :         removed_from_head = FALSE;
    2672          24 :         if (t->is_auto_img()) {
    2673           0 :           string img = generate_img_src((t->text_string + 20));
    2674             : 
    2675           0 :           if (! img.empty()) {
    2676           0 :             if (found_title_start)
    2677           0 :               title.text += " ";
    2678           0 :             found_title_start = TRUE;
    2679           0 :             title.has_been_found = TRUE;
    2680           0 :             title.text += img;
    2681             :           }
    2682           0 :           page_contents->glyphs.sub_move_right(); // move onto next word
    2683           0 :           removed_from_head = ((!page_contents->glyphs.is_empty())
    2684           0 :                                && (page_contents->glyphs
    2685           0 :                                    .is_equal_to_head()));
    2686          24 :         } else if (t->is_eo_tl()) {
    2687             :           // end of title found
    2688           6 :           title.has_been_found = TRUE;
    2689           6 :           return;
    2690          18 :         } else if (t->is_a_tag()) {
    2691           4 :           handle_tag_within_title(t);
    2692           4 :           page_contents->glyphs.sub_move_right(); // move onto next word
    2693           8 :           removed_from_head = ((!page_contents->glyphs.is_empty())
    2694           8 :                                && (page_contents->glyphs
    2695           4 :                                    .is_equal_to_head()));
    2696          14 :         } else if (found_title_start) {
    2697           8 :           title.text += " " + string(t->text_string, t->text_length);
    2698           8 :           page_contents->glyphs.sub_move_right(); // move onto next word
    2699          16 :           removed_from_head = ((!page_contents->glyphs.is_empty())
    2700          16 :                                && (page_contents->glyphs
    2701           8 :                                    .is_equal_to_head()));
    2702             :         } else {
    2703           6 :           title.text += string(t->text_string, t->text_length);
    2704           6 :           found_title_start    = TRUE;
    2705           6 :           title.has_been_found = TRUE;
    2706           6 :           page_contents->glyphs.sub_move_right(); // move onto next word
    2707          12 :           removed_from_head = ((!page_contents->glyphs.is_empty())
    2708          12 :                                && (page_contents->glyphs
    2709           6 :                                    .is_equal_to_head()));
    2710             :         }
    2711          18 :       } while ((! page_contents->glyphs.is_equal_to_head())
    2712          18 :                || removed_from_head);
    2713             :     }
    2714             :   }
    2715             : }
    2716             : 
    2717             : /*
    2718             :  *  write_html_anchor - writes out an anchor.  The style of the anchor
    2719             :  *                      dependent upon simple_anchor.
    2720             :  */
    2721             : 
    2722          96 : void html_printer::write_html_anchor (text_glob *h)
    2723             : {
    2724          96 :   if (html4 == dialect) {
    2725          96 :     if (h != 0 /* nullptr */) {
    2726          96 :       html.put_string("<a name=\"");
    2727          96 :       if (simple_anchors) {
    2728          34 :         string buffer(ANCHOR_TEMPLATE);
    2729             : 
    2730          17 :         buffer += as_string(header.no_of_headings);
    2731          17 :         buffer += '\0';
    2732          17 :         html.put_string(buffer.contents());
    2733             :       } else
    2734          79 :         html.put_string(header.header_buffer);
    2735          96 :       html.put_string("\"></a>").nl();
    2736             :     }
    2737             :   }
    2738          96 : }
    2739             : 
    2740             : /*
    2741             :  *  write_xhtml_anchor - writes out an anchor.  The style of the anchor
    2742             :  *                       dependent upon simple_anchor.
    2743             :  */
    2744             : 
    2745          96 : void html_printer::write_xhtml_anchor (text_glob *h)
    2746             : {
    2747          96 :   if (xhtml == dialect) {
    2748           0 :     if (h != 0 /* nullptr */) {
    2749           0 :       html.put_string(" id=\"");
    2750           0 :       if (simple_anchors) {
    2751           0 :         string buffer(ANCHOR_TEMPLATE);
    2752             : 
    2753           0 :         buffer += as_string(header.no_of_headings);
    2754           0 :         buffer += '\0';
    2755           0 :         html.put_string(buffer.contents());
    2756             :       } else
    2757           0 :         html.put_string(header.header_buffer);
    2758           0 :       html.put_string("\"");
    2759             :     }
    2760             :   }
    2761          96 : }
    2762             : 
    2763          96 : void html_printer::write_header (void)
    2764             : {
    2765          96 :   if (! header.header_buffer.empty()) {
    2766          96 :     text_glob *a = 0;
    2767             :     // TODO: boolify
    2768          96 :     int space = current_paragraph->retrieve_para_space() || seen_space;
    2769             : 
    2770          96 :     if (header.header_level > 7)
    2771           0 :       header.header_level = 7;
    2772             : 
    2773             :     // firstly we must terminate any font and type faces
    2774          96 :     current_paragraph->done_para();
    2775          96 :     suppress_sub_sup = TRUE;
    2776             : 
    2777          96 :     if (cutoff_heading+2 > header.header_level) {
    2778             :       // now we save the header so we can issue a list of links
    2779          96 :       header.no_of_headings++;
    2780         192 :       style st;
    2781             : 
    2782          96 :       a = new text_glob();
    2783         192 :       a->text_glob_html(&st,
    2784             :                         header.headings
    2785          96 :                           .add_string(header.header_buffer),
    2786             :                         header.header_buffer.length(),
    2787             :                         header.no_of_headings, header.header_level,
    2788             :                         header.no_of_headings, header.header_level);
    2789             : 
    2790             :       // and add this header to the header list
    2791          96 :       header.headers.add(a,
    2792             :                          header.no_of_headings,
    2793             :                          header.no_of_headings, header.no_of_headings,
    2794             :                          header.no_of_headings, header.no_of_headings);
    2795             :     }
    2796             : 
    2797          96 :     html.nl().nl();
    2798             : 
    2799          96 :     if (manufacture_headings) {
    2800             :       // line break before a header
    2801           0 :       if (!current_paragraph->emitted_text())
    2802           0 :         current_paragraph->do_space();
    2803             :       // user wants manufactured headings which look better than
    2804             :       // <Hn></Hn>
    2805           0 :       if (header.header_level<4) {
    2806           0 :         html.put_string("<b><font size=\"+1\">");
    2807           0 :         html.put_string(header.header_buffer);
    2808           0 :         html.put_string("</font>").nl();
    2809           0 :         write_html_anchor(a);
    2810           0 :         html.put_string("</b>").nl();
    2811             :       }
    2812             :       else {
    2813           0 :         html.put_string("<b>");
    2814           0 :         html.put_string(header.header_buffer).nl();
    2815           0 :         write_html_anchor(a);
    2816           0 :         html.put_string("</b>").nl();
    2817             :       }
    2818             :     }
    2819             :     else {
    2820             :       // and now we issue the real header
    2821          96 :       html.put_string("<h");
    2822          96 :       html.put_number(header.header_level);
    2823          96 :       write_xhtml_anchor(a);
    2824          96 :       html.put_string(">");
    2825          96 :       html.put_string(header.header_buffer).nl();
    2826          96 :       write_html_anchor(a);
    2827          96 :       html.put_string("</h");
    2828          96 :       html.put_number(header.header_level);
    2829          96 :       html.put_string(">").nl();
    2830             :     }
    2831             : 
    2832             :     /* and now we save the file name in which this header will occur */
    2833             : 
    2834         192 :     style st;   // fake style to enable us to use the list data structure
    2835             : 
    2836          96 :     text_glob *h=new text_glob();
    2837         192 :     h->text_glob_html(&st,
    2838         192 :                       header.headings.add_string(file_list.file_name()),
    2839         192 :                       file_list.file_name().length(),
    2840             :                       header.no_of_headings, header.header_level,
    2841             :                       header.no_of_headings, header.header_level);
    2842             : 
    2843          96 :     header.header_filename.add(h,
    2844             :                                header.no_of_headings,
    2845             :                                header.no_of_headings,
    2846             :                                header.no_of_headings,
    2847             :                                header.no_of_headings,
    2848             :                                header.no_of_headings);
    2849             : 
    2850          96 :     current_paragraph->do_para(&html, "", get_troff_indent(),
    2851             :                                pageoffset, linelength, space);
    2852             :   }
    2853          96 : }
    2854             : 
    2855          96 : void html_printer::determine_header_level (int level)
    2856             : {
    2857          96 :   if (0 == level) {
    2858             :     int i;
    2859             : 
    2860           0 :     for (i = 0; ((i<header.header_buffer.length())
    2861           0 :                  && (('.' == header.header_buffer[i])
    2862           0 :                      || is_digit(header.header_buffer[i]))) ; i++) {
    2863           0 :       if ('.' == header.header_buffer[i])
    2864           0 :         level++;
    2865             :     }
    2866             :   }
    2867          96 :   header.header_level = level+1;
    2868          96 :   if ((header.header_level >= 2)
    2869          96 :       && (header.header_level <= split_level)) {
    2870          48 :     header.no_of_level_one_headings++;
    2871          48 :     insert_split_file();
    2872             :   }
    2873          96 : }
    2874             : 
    2875             : /*
    2876             :  *  do_heading - handle the .SH and .NH and equivalent commands from
    2877             :  *               troff.
    2878             :  */
    2879             : 
    2880          96 : void html_printer::do_heading (char *arg)
    2881             : {
    2882          96 :   assert(arg != 0 /* nullptr */);
    2883             :   text_glob *g;
    2884          96 :   int  level=atoi(arg);
    2885             :   int  horiz;
    2886             : 
    2887          96 :   header.header_buffer.clear();
    2888          96 :   page_contents->glyphs.move_right();
    2889          96 :   if (! page_contents->glyphs.is_equal_to_head()) {
    2890          96 :     g = page_contents->glyphs.get_data();
    2891          96 :     horiz = g->minh;
    2892         321 :     do {
    2893         417 :       if (g->is_auto_img()) {
    2894           0 :         string img=generate_img_src((g->text_string + 20));
    2895             : 
    2896           0 :         if (! img.empty()) {
    2897             :           // we cannot use full heading anchors with images
    2898           0 :           simple_anchors = TRUE;
    2899           0 :           if (horiz < g->minh)
    2900           0 :             header.header_buffer += " ";
    2901             : 
    2902           0 :           header.header_buffer += img;
    2903             :         }
    2904             :       }
    2905         417 :       else if (g->is_in()
    2906         417 :                || g->is_ti()
    2907         417 :                || g->is_po()
    2908         417 :                || g->is_ce()
    2909         834 :                || g->is_ll())
    2910           0 :         troff_tag(g);
    2911         417 :       else if (g->is_fi())
    2912           0 :         fill_on = 1;
    2913         417 :       else if (g->is_nf())
    2914           0 :         fill_on = 0;
    2915         417 :       else if (! (g->is_a_line() || g->is_a_tag())) {
    2916             :         /*
    2917             :          *  we ignore the other tag commands when constructing a heading
    2918             :          */
    2919         324 :         if (horiz < g->minh)
    2920         228 :           header.header_buffer += " ";
    2921             : 
    2922         324 :         horiz = g->maxh;
    2923         324 :         header.header_buffer += string(g->text_string, g->text_length);
    2924             :       }
    2925         417 :       page_contents->glyphs.move_right();
    2926         417 :       g = page_contents->glyphs.get_data();
    2927         417 :     } while ((! page_contents->glyphs.is_equal_to_head())
    2928         417 :              && (! g->is_eo_h()));
    2929             :   }
    2930             : 
    2931          96 :   determine_header_level(level);
    2932          96 :   write_header();
    2933             : 
    2934             :   /*
    2935             :    *  finally set the output font to uninitialized, thus forcing
    2936             :    *  the new paragraph to start a new font block.
    2937             :    */
    2938             : 
    2939          96 :   output_style.f = 0;
    2940          96 :   g = page_contents->glyphs.get_data();
    2941          96 :   page_contents->glyphs.move_left(); // so that next time we use old g
    2942          96 : }
    2943             : 
    2944             : /*
    2945             :  *  is_courier_until_eol - returns TRUE if we can see a whole line which
    2946             :  *                         is courier
    2947             :  */
    2948             : 
    2949        1154 : int html_printer::is_courier_until_eol (void)
    2950             : {
    2951        1154 :   text_glob *orig = page_contents->glyphs.get_data();
    2952        1154 :   int result      = TRUE;
    2953             :   text_glob *g;
    2954             : 
    2955        1154 :   if (! page_contents->glyphs.is_equal_to_tail()) {
    2956        1154 :     page_contents->glyphs.move_right();
    2957          45 :     do {
    2958        1199 :       g = page_contents->glyphs.get_data();
    2959        1199 :       if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
    2960         778 :         result = FALSE;
    2961        1199 :       page_contents->glyphs.move_right();
    2962             :     } while (result
    2963         421 :              && (! page_contents->glyphs.is_equal_to_head())
    2964         421 :              && (! g->is_fi())
    2965        1620 :              && (! g->is_eol()));
    2966             : 
    2967             :     /*
    2968             :      *  now restore our previous position.
    2969             :      */
    2970        3507 :     while (page_contents->glyphs.get_data() != orig)
    2971        2353 :       page_contents->glyphs.move_left();
    2972             :   }
    2973        1154 :   return result;
    2974             : }
    2975             : 
    2976             : /*
    2977             :  *  do_linelength - handle the .ll command from troff.
    2978             :  */
    2979             : 
    2980          33 : void html_printer::do_linelength (char *arg)
    2981             : {
    2982          33 :   assert(arg != 0 /* nullptr */);
    2983          33 :   if (-1 == max_linelength)
    2984          11 :     max_linelength = atoi(arg);
    2985             : 
    2986          33 :   next_linelength = atoi(arg);
    2987          33 :   seen_linelength = TRUE;
    2988          33 : }
    2989             : 
    2990             : /*
    2991             :  *  do_pageoffset - handle the .po command from troff.
    2992             :  */
    2993             : 
    2994          12 : void html_printer::do_pageoffset (char *arg)
    2995             : {
    2996          12 :   assert(arg != 0 /* nullptr */);
    2997          12 :   next_pageoffset = atoi(arg);
    2998          12 :   seen_pageoffset = TRUE;
    2999          12 : }
    3000             : 
    3001             : /*
    3002             :  *  get_troff_indent - returns the indent value.
    3003             :  */
    3004             : 
    3005        3006 : int html_printer::get_troff_indent (void)
    3006             : {
    3007        3006 :   if (end_tempindent > 0)
    3008         162 :     return temp_indent;
    3009             :   else
    3010        2844 :     return troff_indent;
    3011             : }
    3012             : 
    3013             : /*
    3014             :  *  do_indentation - handle the .in command from troff.
    3015             :  */
    3016             : 
    3017         307 : void html_printer::do_indentation (char *arg)
    3018             : {
    3019         307 :   assert(arg != 0 /* nullptr */);
    3020         307 :   next_indent = atoi(arg);
    3021         307 :   seen_indent = TRUE;
    3022         307 : }
    3023             : 
    3024             : /*
    3025             :  *  do_tempindent - handle the .ti command from troff.
    3026             :  */
    3027             : 
    3028          36 : void html_printer::do_tempindent (char *arg)
    3029             : {
    3030          36 :   assert(arg != 0 /* nullptr */);
    3031          36 :   if (fill_on) {
    3032             :     /*
    3033             :      *  we set the end_tempindent to 2 as the first .br
    3034             :      *  activates the .ti and the second terminates it.
    3035             :      */
    3036          36 :     end_tempindent = 2;
    3037          36 :     temp_indent = atoi(arg);
    3038             :   }
    3039          36 : }
    3040             : 
    3041             : /*
    3042             :  *  shutdown_table - shuts down the current table.
    3043             :  */
    3044             : 
    3045          56 : void html_printer::shutdown_table (void)
    3046             : {
    3047          56 :   if (table != 0 /* nullptr */) {
    3048           0 :     current_paragraph->done_para();
    3049           0 :     table->emit_finish_table();
    3050             :     // don't delete this table as it will be deleted when we destroy the
    3051             :     // text_glob
    3052           0 :     table = 0 /* nullptr */;
    3053             :   }
    3054          56 : }
    3055             : 
    3056             : /*
    3057             :  *  do_indent - remember the indent parameters and if
    3058             :  *              indent is > pageoff and indent has changed
    3059             :  *              then we start a html table to implement the indentation.
    3060             :  */
    3061             : 
    3062         437 : void html_printer::do_indent (int in, int pageoff, int linelen)
    3063             : {
    3064         437 :   if ((device_indent != -1)
    3065         437 :       && ((pageoffset+device_indent) != (in+pageoff))) {
    3066             : 
    3067             :     // TODO: boolify
    3068         376 :     int space = current_paragraph->retrieve_para_space() || seen_space;
    3069         376 :     current_paragraph->done_para();
    3070             : 
    3071         376 :     device_indent = in;
    3072         376 :     pageoffset  = pageoff;
    3073         376 :     if (linelen <= max_linelength)
    3074          78 :       linelength  = linelen;
    3075             : 
    3076         376 :     current_paragraph->do_para(&html, "", device_indent,
    3077             :                                pageoffset, max_linelength, space);
    3078             :   }
    3079         437 : }
    3080             : 
    3081             : /*
    3082             :  *  do_verticalspacing - handle the .vs command from troff.
    3083             :  */
    3084             : 
    3085           0 : void html_printer::do_verticalspacing (char *arg)
    3086             : {
    3087           0 :   assert(arg != 0 /* nullptr */);
    3088           0 :   vertical_spacing = atoi(arg);
    3089           0 : }
    3090             : 
    3091             : /*
    3092             :  *  do_pointsize - handle the .ps command from troff.
    3093             :  */
    3094             : 
    3095           0 : void html_printer::do_pointsize (char *arg)
    3096             : {
    3097           0 :   assert(arg != 0 /* nullptr */);
    3098             :   /*
    3099             :    *  firstly check to see whether this point size is really associated
    3100             :    *  with a .tl tag
    3101             :    */
    3102             : 
    3103           0 :   if (! page_contents->glyphs.is_empty()) {
    3104           0 :     text_glob *g = page_contents->glyphs.get_data();
    3105           0 :     text_glob *t = page_contents->glyphs.get_data();
    3106             : 
    3107           0 :     while (t->is_a_tag() && (!page_contents->glyphs.is_equal_to_head()))
    3108             :     {
    3109           0 :       if (t->is_tl()) {
    3110             :         /*
    3111             :          *  found title therefore ignore this .ps tag
    3112             :          */
    3113           0 :         while (t != g) {
    3114           0 :           page_contents->glyphs.move_left();
    3115           0 :           t = page_contents->glyphs.get_data();
    3116             :         }
    3117           0 :         return;
    3118             :       }
    3119           0 :       page_contents->glyphs.move_right();
    3120           0 :       t = page_contents->glyphs.get_data();
    3121             :     }
    3122             :     /*
    3123             :      *  move back to original position
    3124             :      */
    3125           0 :     while (t != g) {
    3126           0 :       page_contents->glyphs.move_left();
    3127           0 :       t = page_contents->glyphs.get_data();
    3128             :     }
    3129             :     /*
    3130             :      *  collect valid pointsize
    3131             :      */
    3132           0 :     pointsize = atoi(arg);
    3133             :   }
    3134             : }
    3135             : 
    3136             : /*
    3137             :  *  do_fill - records whether troff has requested that text be filled.
    3138             :  */
    3139             : 
    3140         217 : void html_printer::do_fill (char *arg)
    3141             : {
    3142         217 :   assert(arg != 0 /* nullptr */);
    3143         217 :   int on = atoi(arg);
    3144             : 
    3145         217 :   output_hpos = get_troff_indent()+pageoffset;
    3146         217 :   suppress_sub_sup = TRUE;
    3147             : 
    3148         217 :   if (fill_on != on) {
    3149         206 :     if (on)
    3150         103 :       current_paragraph->do_para("", seen_space);
    3151         206 :     fill_on = on;
    3152             :   }
    3153         217 : }
    3154             : 
    3155             : /*
    3156             :  *  do_eol - handle the end of line
    3157             :  */
    3158             : 
    3159         963 : void html_printer::do_eol (void)
    3160             : {
    3161         963 :   if (! fill_on) {
    3162         710 :     if (current_paragraph->ever_emitted_text()) {
    3163         633 :       current_paragraph->do_newline();
    3164         633 :       current_paragraph->do_break();
    3165             :     }
    3166             :   }
    3167         963 :   output_hpos = get_troff_indent()+pageoffset;
    3168         963 : }
    3169             : 
    3170             : /*
    3171             :  *  do_check_center - checks to see whether we have seen a '.ce' tag
    3172             :  *                    during the previous line.
    3173             :  */
    3174             : 
    3175        1268 : void html_printer::do_check_center(void)
    3176             : {
    3177        1268 :   if (seen_center) {
    3178         112 :     seen_center = FALSE;
    3179         112 :     if (next_center > 0) {
    3180          55 :       if (0 == end_center) {
    3181             :         // TODO: boolify
    3182          52 :         int space = current_paragraph->retrieve_para_space()
    3183          52 :                     || seen_space;
    3184          52 :         current_paragraph->done_para();
    3185          52 :         suppress_sub_sup = TRUE;
    3186          52 :         if (html4 == dialect)
    3187          52 :           current_paragraph->do_para("align=\"center\"", space);
    3188             :         else
    3189           0 :           current_paragraph->do_para("class=\"center\"", space);
    3190             :       } else
    3191           6 :         if ((strcmp("align=\"center\"",
    3192           3 :                     current_paragraph->get_alignment()) != 0)
    3193           4 :             && (strcmp("class=\"center\"",
    3194           1 :                        current_paragraph->get_alignment()) != 0)) {
    3195             :           /*
    3196             :            *  different alignment, so shutdown paragraph and open
    3197             :            *  a new one.
    3198             :            */
    3199             :           // TODO: boolify
    3200           1 :           int space = current_paragraph->retrieve_para_space()
    3201           1 :                       || seen_space;
    3202           1 :           current_paragraph->done_para();
    3203           1 :           suppress_sub_sup = TRUE;
    3204           1 :           if (html4 == dialect)
    3205           1 :             current_paragraph->do_para("align=\"center\"", space);
    3206             :           else
    3207           0 :             current_paragraph->do_para("class=\"center\"", space);
    3208             :         } else
    3209             :            // same alignment; if we have emitted text, issue a break.
    3210           2 :           if (current_paragraph->emitted_text())
    3211           0 :             current_paragraph->do_break();
    3212             :     } else
    3213             :       /*
    3214             :        *  next_center == 0
    3215             :        */
    3216          57 :       if (end_center > 0) {
    3217             :         // TODO: boolify
    3218         104 :         seen_space = seen_space
    3219          52 :                      || current_paragraph->retrieve_para_space();
    3220          52 :         current_paragraph->done_para();
    3221          52 :         suppress_sub_sup = TRUE;
    3222          52 :         current_paragraph->do_para("", seen_space);
    3223             :       }
    3224         112 :     end_center = next_center;
    3225             :   }
    3226        1268 : }
    3227             : 
    3228             : /*
    3229             :  *  do_eol_ce - handle end of line specifically for a .ce
    3230             :  */
    3231             : 
    3232           0 : void html_printer::do_eol_ce (void)
    3233             : {
    3234           0 :   if (end_center > 0) {
    3235           0 :     if (end_center > 1)
    3236           0 :       if (current_paragraph->emitted_text())
    3237           0 :         current_paragraph->do_break();
    3238             : 
    3239           0 :     end_center--;
    3240           0 :     if (0 == end_center) {
    3241           0 :       current_paragraph->done_para();
    3242           0 :       suppress_sub_sup = TRUE;
    3243             :     }
    3244             :   }
    3245           0 : }
    3246             : 
    3247             : /*
    3248             :  *  do_flush - flushes all output and tags.
    3249             :  */
    3250             : 
    3251           0 : void html_printer::do_flush (void)
    3252             : {
    3253           0 :   current_paragraph->done_para();
    3254           0 : }
    3255             : 
    3256             : /*
    3257             :  *  do_links - moves onto a new temporary file and sets auto_links to
    3258             :  *             false.
    3259             :  */
    3260             : 
    3261          18 : void html_printer::do_links (void)
    3262             : {
    3263          18 :   html.end_line();  // flush line
    3264          18 :   auto_links = FALSE;  // from now on only emit under user request
    3265          18 :   file_list.add_new_file(xtmpfile());
    3266          18 :   file_list.set_links_required();
    3267          18 :   html.set_file(file_list.get_file());
    3268          18 : }
    3269             : 
    3270             : /*
    3271             :  *  insert_split_file -
    3272             :  */
    3273             : 
    3274          48 : void html_printer::insert_split_file (void)
    3275             : {
    3276          48 :   if (multiple_files) {
    3277          40 :     current_paragraph->done_para();       // flush paragraph
    3278          40 :     html.end_line();                      // flush line
    3279          40 :     html.set_file(file_list.get_file());  // flush current file
    3280          40 :     file_list.add_new_file(xtmpfile());
    3281          80 :     string split_file = job_name;
    3282             : 
    3283          40 :     split_file += string("-");
    3284          40 :     split_file += as_string(header.no_of_level_one_headings);
    3285          40 :     if (xhtml == dialect)
    3286           0 :       split_file += string(".xhtml");
    3287             :     else
    3288          40 :       split_file += string(".html");
    3289          40 :     split_file += '\0';
    3290             : 
    3291          40 :     file_list.set_file_name(split_file);
    3292          40 :     html.set_file(file_list.get_file());
    3293             :   }
    3294          48 : }
    3295             : 
    3296             : /*
    3297             :  *  do_job_name - assigns the job_name to name.
    3298             :  */
    3299             : 
    3300           0 : void html_printer::do_job_name (char *name)
    3301             : {
    3302           0 :   if (! multiple_files) {
    3303           0 :     multiple_files = TRUE;
    3304           0 :     while (name != 0 /* nullptr */ && (*name != '\0') && (' ' == *name))
    3305           0 :       name++;
    3306           0 :     job_name = name;
    3307             :   }
    3308           0 : }
    3309             : 
    3310             : /*
    3311             :  *  do_head - adds a string to head_info which is to be included into
    3312             :  *            the <head> </head> section of the html document.
    3313             :  */
    3314             : 
    3315           1 : void html_printer::do_head (char *name)
    3316             : {
    3317           1 :   assert(name != 0 /* nullptr */);
    3318           1 :   head_info += string(name);
    3319           1 :   head_info += '\n';
    3320           1 : }
    3321             : 
    3322             : /*
    3323             :  *  do_break - handles the ".br" request and also undoes an outstanding
    3324             :  *             ".ti" command and calls indent if the indentation related
    3325             :  *             registers have changed.
    3326             :  */
    3327             : 
    3328        1268 : void html_printer::do_break (void)
    3329             : {
    3330        1268 :   int seen_temp_indent = FALSE;
    3331             : 
    3332        1268 :   current_paragraph->do_break();
    3333        1268 :   if (end_tempindent > 0) {
    3334          60 :     end_tempindent--;
    3335          60 :     if (end_tempindent > 0)
    3336          36 :       seen_temp_indent = TRUE;
    3337             :   }
    3338        1268 :   if (seen_indent
    3339         939 :       || seen_pageoffset
    3340         938 :       || seen_linelength
    3341         934 :       || seen_temp_indent) {
    3342         349 :     if (seen_indent && (! seen_temp_indent))
    3343         308 :       troff_indent = next_indent;
    3344         349 :     if (! seen_pageoffset)
    3345         342 :       next_pageoffset = pageoffset;
    3346         349 :     if (! seen_linelength)
    3347         333 :       next_linelength = linelength;
    3348         349 :     do_indent(get_troff_indent(), next_pageoffset, next_linelength);
    3349             :   }
    3350        1268 :   seen_indent     = seen_temp_indent;
    3351        1268 :   seen_linelength = FALSE;
    3352        1268 :   seen_pageoffset = FALSE;
    3353        1268 :   do_check_center();
    3354        1268 :   output_hpos     = get_troff_indent()+pageoffset;
    3355        1268 :   suppress_sub_sup = TRUE;
    3356        1268 : }
    3357             : 
    3358         957 : void html_printer::do_space (char *arg)
    3359             : {
    3360         957 :   assert(arg != 0 /* nullptr */);
    3361         957 :   int n = atoi(arg);
    3362             : 
    3363         957 :   seen_space = atoi(arg);
    3364         957 :   as.check_sp(seen_space);
    3365             : #if 0
    3366             :   if ((n>0) && table)
    3367             :     table->set_space(TRUE);
    3368             : #endif
    3369             : 
    3370        1914 :   while (n>0) {
    3371         957 :     current_paragraph->do_space();
    3372         957 :     n--;
    3373             :   }
    3374         957 :   suppress_sub_sup = TRUE;
    3375         957 : }
    3376             : 
    3377             : /*
    3378             :  *  do_tab_ts - start a table, which will have already been defined.
    3379             :  */
    3380             : 
    3381          20 : void html_printer::do_tab_ts (text_glob *g)
    3382             : {
    3383          20 :   assert(g != 0 /* nullptr */);
    3384          20 :   html_table *t = g->get_table();
    3385             : 
    3386          20 :   if (t != 0 /* nullptr */) {
    3387          20 :     current_column = 0;
    3388          20 :     current_paragraph->done_pre();
    3389          20 :     current_paragraph->done_para();
    3390          20 :     current_paragraph->remove_para_space();
    3391             : 
    3392             : #if defined(DEBUG_TABLES)
    3393             :     html.simple_comment("TABS");
    3394             : #endif
    3395             : 
    3396          20 :     t->set_linelength(max_linelength);
    3397          20 :     t->add_indent(pageoffset);
    3398             : #if 0
    3399             :     t->emit_table_header(seen_space);
    3400             : #else
    3401          20 :     t->emit_table_header(FALSE);
    3402             :     // TODO: boolify
    3403          20 :     row_space = current_paragraph->retrieve_para_space() || seen_space;
    3404          20 :     seen_space = FALSE;
    3405             : #endif
    3406             :   }
    3407             : 
    3408          20 :   table = t;
    3409          20 : }
    3410             : 
    3411             : /*
    3412             :  *  do_tab_te - finish a table.
    3413             :  */
    3414             : 
    3415          20 : void html_printer::do_tab_te (void)
    3416             : {
    3417          20 :   if (table != 0 /* nullptr */) {
    3418          20 :     current_paragraph->done_para();
    3419          20 :     current_paragraph->remove_para_space();
    3420          20 :     table->emit_finish_table();
    3421             :   }
    3422             : 
    3423          20 :   table = 0 /* nullptr */;
    3424          20 :   restore_troff_indent();
    3425          20 : }
    3426             : 
    3427             : /*
    3428             :  *  do_tab - handle the "devtag:tab" tag
    3429             :  */
    3430             : 
    3431           9 : void html_printer::do_tab (char *s)
    3432             : {
    3433           9 :   assert(s != 0 /* nullptr */);
    3434           9 :   if (table != 0 /* nullptr */) {
    3435          18 :     while (csspace(*s))
    3436           9 :       s++;
    3437           9 :     s++;
    3438           9 :     int col = table->find_column(atoi(s) + pageoffset
    3439           9 :               + get_troff_indent());
    3440           9 :     if (col > 0) {
    3441           9 :       current_paragraph->done_para();
    3442           9 :       table->emit_col(col);
    3443             :     }
    3444             :   }
    3445           9 : }
    3446             : 
    3447             : /*
    3448             :  *  do_tab0 - handle the "devtag:tab0" tag
    3449             :  */
    3450             : 
    3451           2 : void html_printer::do_tab0 (void)
    3452             : {
    3453           2 :   if (table != 0 /* nullptr */) {
    3454           2 :     int col = table->find_column(pageoffset+get_troff_indent());
    3455           2 :     if (col > 0) {
    3456           2 :       current_paragraph->done_para();
    3457           2 :       table->emit_col(col);
    3458             :     }
    3459             :   }
    3460           2 : }
    3461             : 
    3462             : /*
    3463             :  *  do_col - start column, s.
    3464             :  */
    3465             : 
    3466          56 : void html_printer::do_col (char *s)
    3467             : {
    3468          56 :   assert(s != 0 /* nullptr */);
    3469          56 :   if (table != 0 /* nullptr */) {
    3470          45 :     if (atoi(s) < current_column)
    3471          11 :       row_space = seen_space;
    3472             : 
    3473          45 :     current_column = atoi(s);
    3474          45 :     current_paragraph->done_para();
    3475          45 :     table->emit_col(current_column);
    3476          45 :     current_paragraph->do_para("", row_space);
    3477             :   }
    3478          56 : }
    3479             : 
    3480             : /*
    3481             :  *  troff_tag - processes the troff tag and manipulates the troff
    3482             :  *              state machine.
    3483             :  */
    3484             : 
    3485        4686 : void html_printer::troff_tag (text_glob *g)
    3486             : {
    3487        4686 :   assert(g != 0 /* nullptr */);
    3488             :   /*
    3489             :    *  firstly skip over devtag:
    3490             :    */
    3491        4686 :   char *t=const_cast<char *>(g->text_string)+strlen("devtag:");
    3492        4686 :   if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
    3493         283 :     do_end_para(g);
    3494        4403 :   } else if (strncmp(g->text_string, "html<?p>:", strlen("html<?p>:"))
    3495             :              == 0) {
    3496           0 :     if (current_paragraph->emitted_text())
    3497           0 :       html.put_string(g->text_string+9);
    3498             :     else
    3499           0 :       do_end_para(g);
    3500        4403 :   } else if (strncmp(g->text_string, "math<?p>:", strlen("math<?p>:"))
    3501             :               == 0) {
    3502           0 :     do_math(g);
    3503        4403 :   } else if (g->is_eol()) {
    3504         963 :     do_eol();
    3505        3440 :   } else if (g->is_eol_ce()) {
    3506           0 :     do_eol_ce();
    3507        3440 :   } else if (strncmp(t, ".sp", 3) == 0) {
    3508         957 :     char *a = t+3;
    3509         957 :     do_space(a);
    3510        2483 :   } else if (strncmp(t, ".br", 3) == 0) {
    3511        1268 :     seen_break = 1;
    3512        1268 :     as.check_br(1);
    3513        1268 :     do_break();
    3514        1215 :   } else if (strcmp(t, ".centered-image") == 0) {
    3515          57 :     do_centered_image();
    3516        1158 :   } else if (strcmp(t, ".right-image") == 0) {
    3517           0 :     do_right_image();
    3518        1158 :   } else if (strcmp(t, ".left-image") == 0) {
    3519           1 :     do_left_image();
    3520        1157 :   } else if (strncmp(t, ".auto-image", 11) == 0) {
    3521          58 :     char *a = t+11;
    3522          58 :     do_auto_image(g, a);
    3523        1099 :   } else if (strncmp(t, ".ce", 3) == 0) {
    3524         125 :     char *a = t+3;
    3525         125 :     suppress_sub_sup = TRUE;
    3526         125 :     do_center(a);
    3527         974 :   } else if (g->is_tl()) {
    3528           5 :     suppress_sub_sup = TRUE;
    3529           5 :     title.with_h1 = TRUE;
    3530           5 :     do_title();
    3531         969 :   } else if (strncmp(t, ".html-tl", 8) == 0) {
    3532           1 :     suppress_sub_sup = TRUE;
    3533           1 :     title.with_h1 = FALSE;
    3534           1 :     do_title();
    3535         968 :   } else if (strncmp(t, ".fi", 3) == 0) {
    3536         217 :     char *a = t+3;
    3537         217 :     do_fill(a);
    3538         751 :   } else if ((strncmp(t, ".SH", 3) == 0)
    3539         751 :               || (strncmp(t, ".NH", 3) == 0)) {
    3540          96 :     char *a = t+3;
    3541          96 :     do_heading(a);
    3542         655 :   } else if (strncmp(t, ".ll", 3) == 0) {
    3543          33 :     char *a = t+3;
    3544          33 :     do_linelength(a);
    3545         622 :   } else if (strncmp(t, ".po", 3) == 0) {
    3546          12 :     char *a = t+3;
    3547          12 :     do_pageoffset(a);
    3548         610 :   } else if (strncmp(t, ".in", 3) == 0) {
    3549         307 :     char *a = t+3;
    3550         307 :     do_indentation(a);
    3551         303 :   } else if (strncmp(t, ".ti", 3) == 0) {
    3552          36 :     char *a = t+3;
    3553          36 :     do_tempindent(a);
    3554         267 :   } else if (strncmp(t, ".vs", 3) == 0) {
    3555           0 :     char *a = t+3;
    3556           0 :     do_verticalspacing(a);
    3557         267 :   } else if (strncmp(t, ".ps", 3) == 0) {
    3558           0 :     char *a = t+3;
    3559           0 :     do_pointsize(a);
    3560         267 :   } else if (strcmp(t, ".links") == 0) {
    3561          18 :     do_links();
    3562         249 :   } else if (strncmp(t, ".job-name", 9) == 0) {
    3563           0 :     char *a = t+9;
    3564           0 :     do_job_name(a);
    3565         249 :   } else if (strncmp(t, ".head", 5) == 0) {
    3566           1 :     char *a = t+5;
    3567           1 :     do_head(a);
    3568         248 :   } else if (strcmp(t, ".no-auto-rule") == 0) {
    3569           1 :     auto_rule = FALSE;
    3570         247 :   } else if (strcmp(t, ".tab-ts") == 0) {
    3571          20 :     do_tab_ts(g);
    3572         227 :   } else if (strcmp(t, ".tab-te") == 0) {
    3573          20 :     do_tab_te();
    3574         207 :   } else if (strncmp(t, ".col ", 5) == 0) {
    3575          56 :     char *a = t+4;
    3576          56 :     do_col(a);
    3577         151 :   } else if (strncmp(t, "tab ", 4) == 0) {
    3578           9 :     char *a = t+3;
    3579           9 :     do_tab(a);
    3580         142 :   } else if (strncmp(t, "tab0", 4) == 0) {
    3581           2 :     do_tab0();
    3582             :   }
    3583        4686 : }
    3584             : 
    3585             : /*
    3586             :  *  do_math - prints out the equation
    3587             :  */
    3588             : 
    3589           0 : void html_printer::do_math (text_glob *g)
    3590             : {
    3591           0 :   assert(g != 0 /* nullptr */);
    3592           0 :   do_font(g);
    3593           0 :   if (current_paragraph->emitted_text())
    3594           0 :     html.put_string(g->text_string+9);
    3595             :   else
    3596           0 :     do_end_para(g);
    3597           0 : }
    3598             : 
    3599             : /*
    3600             :  *  is_in_middle - returns TRUE if the positions left..right are in the
    3601             :  *                 center of the page.
    3602             :  */
    3603             : 
    3604             : // TODO: boolify
    3605           0 : int html_printer::is_in_middle (int left, int right)
    3606             : {
    3607           0 :   return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
    3608           0 :           <= CENTER_TOLERANCE );
    3609             : }
    3610             : 
    3611             : /*
    3612             :  *  flush_globs - runs through the text glob list and emits html.
    3613             :  */
    3614             : 
    3615          20 : void html_printer::flush_globs (void)
    3616             : {
    3617             :   text_glob *g;
    3618             : 
    3619          20 :   if (! page_contents->glyphs.is_empty()) {
    3620          14 :     page_contents->glyphs.start_from_head();
    3621       25175 :     do {
    3622       25189 :       g = page_contents->glyphs.get_data();
    3623             : #if 0
    3624             :       fprintf(stderr, "[%s:%d:%d:%d:%d]",
    3625             :               g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
    3626             :       fflush(stderr);
    3627             : #endif
    3628             : 
    3629       25189 :       handle_state_assertion(g);
    3630             : 
    3631       25189 :       if (strcmp(g->text_string, "XXXXXXX") == 0)
    3632           0 :         stop();
    3633             : 
    3634       25189 :       if (g->is_a_tag())
    3635        4683 :         troff_tag(g);
    3636       20506 :       else if (g->is_a_line())
    3637           0 :         emit_line(g);
    3638             :       else {
    3639       20506 :         as.check_sp(seen_space);
    3640       20506 :         as.check_br(seen_break);
    3641       20506 :         seen_break = 0;
    3642       20506 :         seen_space = 0;
    3643       20506 :         emit_html(g);
    3644             :       }
    3645             : 
    3646       25189 :       as.check_fi(fill_on);
    3647       25189 :       as.check_ce(end_center);
    3648             :       /*
    3649             :        *  after processing the title (and removing it) the glyph list
    3650             :        *  might be empty
    3651             :        */
    3652       25189 :       if (! page_contents->glyphs.is_empty()) {
    3653       25189 :         page_contents->glyphs.move_right();
    3654             :       }
    3655       25189 :     } while (! page_contents->glyphs.is_equal_to_head());
    3656             :   }
    3657          20 : }
    3658             : 
    3659             : /*
    3660             :  *  calc_nf - calculates the _no_ format flag, given the
    3661             :  *            text glob, g.
    3662             :  */
    3663             : 
    3664             : // TODO: boolify
    3665       76780 : int html_printer::calc_nf (text_glob *g, int nf)
    3666             : {
    3667       76780 :   if (g != 0 /* nullptr */) {
    3668       76780 :     if (g->is_fi()) {
    3669         342 :       as.check_fi(TRUE);
    3670         342 :       return FALSE;
    3671             :     }
    3672       76438 :     if (g->is_nf()) {
    3673         309 :       as.check_fi(FALSE);
    3674         309 :       return TRUE;
    3675             :     }
    3676             :   }
    3677       76129 :   as.check_fi(! nf);
    3678       76129 :   return nf;
    3679             : }
    3680             : 
    3681             : /*
    3682             :  *  calc_po_in - calculates the, in, po, registers
    3683             :  */
    3684             : 
    3685       22585 : void html_printer::calc_po_in (text_glob *g, int nf)
    3686             : {
    3687       22585 :   assert(g != 0 /* nullptr */);
    3688       22585 :   if (g->is_in())
    3689         307 :     troff_indent = g->get_arg();
    3690       22278 :   else if (g->is_po())
    3691          12 :     pageoffset = g->get_arg();
    3692       22266 :   else if (g->is_ti()) {
    3693          36 :     temp_indent = g->get_arg();
    3694          36 :     end_tempindent = 2;
    3695       22230 :   } else if (g->is_br() || (nf && g->is_eol())) {
    3696           0 :     if (end_tempindent > 0)
    3697           0 :       end_tempindent--;
    3698             :   }
    3699       22585 : }
    3700             : 
    3701             : /*
    3702             :  *  next_horiz_pos - returns the next horiz position.
    3703             :  *                   -1 is returned if it doesn't exist.
    3704             :  */
    3705             : 
    3706           0 : int html_printer::next_horiz_pos (text_glob *g, int nf)
    3707             : {
    3708           0 :   int next = -1;
    3709             : 
    3710           0 :   if ((g != 0 /* nullptr */) && (g->is_br() || (nf && g->is_eol())))
    3711           0 :     if (! page_contents->glyphs.is_empty()) {
    3712           0 :       page_contents->glyphs.move_right_get_data();
    3713           0 :       if (0 /* nullptr */ == g) {
    3714           0 :         page_contents->glyphs.start_from_head();
    3715           0 :         as.reset();
    3716             :       }
    3717             :       else {
    3718           0 :         next = g->minh;
    3719           0 :         page_contents->glyphs.move_left();
    3720             :       }
    3721             :     }
    3722           0 :   return next;
    3723             : }
    3724             : 
    3725             : /*
    3726             :  *  insert_tab_ts - inserts a tab-ts before, where.
    3727             :  */
    3728             : 
    3729          20 : text_glob *html_printer::insert_tab_ts (text_glob *where)
    3730             : {
    3731             :   text_glob *start_of_table;
    3732          20 :   text_glob *old_pos = page_contents->glyphs.get_data();
    3733          20 :   page_contents->glyphs.move_to(where);
    3734          20 :   page_contents->glyphs.move_left();
    3735             :   // tab table start
    3736          20 :   page_contents->insert_tag(string("devtag:.tab-ts"));
    3737          20 :   page_contents->glyphs.move_right();
    3738          20 :   start_of_table = page_contents->glyphs.get_data();
    3739          20 :   page_contents->glyphs.move_to(old_pos);
    3740          20 :   return start_of_table;
    3741             : }
    3742             : 
    3743             : /*
    3744             :  *  insert_tab_te - inserts a tab-te before the current position
    3745             :  *                  (it skips backward over .sp/.br)
    3746             :  */
    3747             : 
    3748          20 : void html_printer::insert_tab_te (void)
    3749             : {
    3750          20 :   text_glob *g = page_contents->glyphs.get_data();
    3751          20 :   page_contents->dump_page();
    3752          96 :   while (page_contents->glyphs.get_data()->is_a_tag())
    3753          76 :     page_contents->glyphs.move_left();
    3754             :   // tab table end
    3755          20 :   page_contents->insert_tag(string("devtag:.tab-te"));
    3756         116 :   while (g != page_contents->glyphs.get_data())
    3757          96 :     page_contents->glyphs.move_right();
    3758          20 :   page_contents->dump_page();
    3759          20 : }
    3760             : 
    3761             : /*
    3762             :  *  insert_tab_0 - inserts a tab0 before, where.
    3763             :  */
    3764             : 
    3765           2 : void html_printer::insert_tab_0 (text_glob *where)
    3766             : {
    3767           2 :   text_glob *old_pos = page_contents->glyphs.get_data();
    3768             : 
    3769           2 :   page_contents->glyphs.move_to(where);
    3770           2 :   page_contents->glyphs.move_left();
    3771             :   // tab0 start of line
    3772           2 :   page_contents->insert_tag(string("devtag:tab0"));
    3773           2 :   page_contents->glyphs.move_right();
    3774           2 :   page_contents->glyphs.move_to(old_pos);
    3775           2 : }
    3776             : 
    3777             : /*
    3778             :  *  remove_tabs - removes the tabs tags on this line.
    3779             :  */
    3780             : 
    3781         376 : void html_printer::remove_tabs (void)
    3782             : {
    3783         376 :   text_glob *orig = page_contents->glyphs.get_data();
    3784             :   text_glob *g;
    3785             : 
    3786         376 :   if (! page_contents->glyphs.is_equal_to_tail()) {
    3787        8588 :     do {
    3788        8964 :       g = page_contents->glyphs.get_data();
    3789        8964 :       if (g->is_tab()) {
    3790           0 :         page_contents->glyphs.sub_move_right();
    3791           0 :         if (g == orig)
    3792           0 :           orig = page_contents->glyphs.get_data();
    3793             :       }
    3794             :       else
    3795        8964 :         page_contents->glyphs.move_right();
    3796        8964 :     } while ((! page_contents->glyphs.is_equal_to_head())
    3797        8964 :              && (! g->is_eol()));
    3798             : 
    3799             :     /*
    3800             :      *  now restore our previous position.
    3801             :      */
    3802        9340 :     while (page_contents->glyphs.get_data() != orig)
    3803        8964 :       page_contents->glyphs.move_left();
    3804             :   }
    3805         376 : }
    3806             : 
    3807          20 : void html_printer::remove_courier_tabs (void)
    3808             : {
    3809             :   text_glob  *g;
    3810          20 :   int line_start = TRUE;
    3811          20 :   int nf         = FALSE;
    3812             : 
    3813          20 :   if (! page_contents->glyphs.is_empty()) {
    3814          14 :     page_contents->glyphs.start_from_head();
    3815          14 :     as.reset();
    3816          14 :     line_start = TRUE;
    3817       25573 :     do {
    3818       25587 :       g = page_contents->glyphs.get_data();
    3819       25587 :       handle_state_assertion(g);
    3820       25587 :       nf = calc_nf(g, nf);
    3821             : 
    3822       25587 :       if (line_start) {
    3823        3037 :         if (line_start && nf && is_courier_until_eol()) {
    3824         376 :           remove_tabs();
    3825         376 :           g = page_contents->glyphs.get_data();
    3826             :         }
    3827             :       }
    3828             : 
    3829             :       // line_start = g->is_br() || g->is_nf() || g->is_fi()
    3830             :       //             || (nf && g->is_eol());
    3831       25587 :       line_start = g->is_br() || (nf && g->is_eol());
    3832       25587 :       page_contents->glyphs.move_right();
    3833       25587 :     } while (! page_contents->glyphs.is_equal_to_head());
    3834             :   }
    3835          20 : }
    3836             : 
    3837          20 : void html_printer::insert_tab0_foreach_tab (void)
    3838             : {
    3839          20 :   text_glob  *start_of_line  = 0 /* nullptr */;
    3840          20 :   text_glob  *g              = 0 /* nullptr */;
    3841          20 :   int seen_tab               = FALSE;
    3842          20 :   int seen_col               = FALSE;
    3843          20 :   int nf                     = FALSE;
    3844             : 
    3845          20 :   if (! page_contents->glyphs.is_empty()) {
    3846          14 :     page_contents->glyphs.start_from_head();
    3847          14 :     as.reset();
    3848          14 :     start_of_line = page_contents->glyphs.get_data();
    3849       22550 :     do {
    3850       22564 :       g = page_contents->glyphs.get_data();
    3851       22564 :       handle_state_assertion(g);
    3852       22564 :       nf = calc_nf(g, nf);
    3853             : 
    3854       22564 :       if (g->is_tab())
    3855           2 :         seen_tab = TRUE;
    3856             : 
    3857       22564 :       if (g->is_col())
    3858          16 :         seen_col = TRUE;
    3859             : 
    3860       22564 :       if (g->is_br() || (nf && g->is_eol())) {
    3861         915 :         do {
    3862        3023 :           page_contents->glyphs.move_right();
    3863        3023 :           g = page_contents->glyphs.get_data();
    3864        3023 :           handle_state_assertion(g);
    3865        3023 :           nf = calc_nf(g, nf);
    3866        3023 :           if (page_contents->glyphs.is_equal_to_head()) {
    3867           0 :             if (seen_tab && !seen_col)
    3868           0 :               insert_tab_0(start_of_line);
    3869           0 :             return;
    3870             :           }
    3871        3023 :         } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
    3872             :         // printf("\nstart_of_line is: %s\n", g->text_string);
    3873        2108 :         if (seen_tab && !seen_col) {
    3874           2 :           insert_tab_0(start_of_line);
    3875           2 :           page_contents->glyphs.move_to(g);
    3876             :         }
    3877             : 
    3878        2108 :         seen_tab = FALSE;
    3879        2108 :         seen_col = FALSE;
    3880        2108 :         start_of_line = g;
    3881             :       }
    3882       22564 :       page_contents->glyphs.move_right();
    3883       22564 :     } while (! page_contents->glyphs.is_equal_to_head());
    3884          14 :     if (seen_tab && !seen_col)
    3885           0 :       insert_tab_0(start_of_line);
    3886             : 
    3887             :   }
    3888             : }
    3889             : 
    3890             : /*
    3891             :  *  update_min_max - updates the extent of a column, given the left and
    3892             :  *                   right extents of a glyph, g.
    3893             :  */
    3894             : 
    3895       20863 : void html_printer::update_min_max (colType type_of_col,
    3896             :                                    int *minimum, int *maximum,
    3897             :                                    text_glob *g)
    3898             : {
    3899       20863 :   switch (type_of_col) {
    3900             : 
    3901          40 :   case tab_tag:
    3902          40 :     break;
    3903           2 :   case tab0_tag:
    3904           2 :     *minimum = g->minh;
    3905           2 :     break;
    3906         327 :   case col_tag:
    3907         327 :     *minimum = g->minh;
    3908         327 :     *maximum = g->maxh;
    3909         327 :     break;
    3910       20494 :   default:
    3911       20494 :     break;
    3912             :   }
    3913       20863 : }
    3914             : 
    3915             : /*
    3916             :  *  add_table_end - moves left one glyph, adds a table end tag and adds
    3917             :  *                  a debugging string.
    3918             :  */
    3919             : 
    3920          19 : void html_printer::add_table_end (const char *
    3921             : #if defined(DEBUG_TABLES)
    3922             :   debug_string
    3923             : #endif
    3924             : )
    3925             : {
    3926          19 :   page_contents->glyphs.move_left();
    3927          19 :   insert_tab_te();
    3928             : #if defined(DEBUG_TABLES)
    3929             :   assert(debug_string != 0 /* nullptr */);
    3930             :   page_contents->insert_tag(string(debug_string));
    3931             : #endif
    3932          19 : }
    3933             : 
    3934             : /*
    3935             :  *  lookahead_for_tables - checks for .col tags and inserts table
    3936             :  *                         start/end tags
    3937             :  */
    3938             : 
    3939          20 : void html_printer::lookahead_for_tables (void)
    3940             : {
    3941             :   text_glob  *g;
    3942          20 :   text_glob  *start_of_line  = 0 /* nullptr */;
    3943          20 :   text_glob  *start_of_table = 0 /* nullptr */;
    3944          20 :   text_glob  *last           = 0 /* nullptr */;
    3945          20 :   colType     type_of_col    = none_tag;
    3946          20 :   int         found_col      = FALSE;
    3947          20 :   int         ncol           = 0;
    3948          20 :   int         colmin         = 0;               // pacify compiler
    3949          20 :   int         colmax         = 0;               // pacify compiler
    3950          20 :   html_table *tbl            = new html_table(&html, -1);
    3951          20 :   const char *tab_defs       = 0 /* nullptr */;
    3952          20 :   char        align          = 'L';
    3953          20 :   int         nf             = FALSE;
    3954          20 :   int         old_pageoffset = pageoffset;
    3955             : 
    3956          20 :   remove_courier_tabs();
    3957          20 :   page_contents->dump_page();
    3958          20 :   insert_tab0_foreach_tab();
    3959          20 :   page_contents->dump_page();
    3960          20 :   if (! page_contents->glyphs.is_empty()) {
    3961          14 :     page_contents->glyphs.start_from_head();
    3962          14 :     as.reset();
    3963          14 :     g = page_contents->glyphs.get_data();
    3964          14 :     if (g->is_br()) {
    3965           2 :       g = page_contents->glyphs.move_right_get_data();
    3966           2 :       handle_state_assertion(g);
    3967           2 :       if (page_contents->glyphs.is_equal_to_head()) {
    3968           0 :         if (tbl != 0 /* nullptr */) {
    3969           0 :           delete tbl;
    3970           0 :           tbl = 0 /* nullptr */;
    3971             :         }
    3972           0 :         return;
    3973             :       }
    3974             : 
    3975           2 :       start_of_line = g;
    3976           2 :       ncol = 0;
    3977           2 :       if (found_col)
    3978           0 :         last = g;
    3979           2 :       found_col = FALSE;
    3980             :     }
    3981             : 
    3982       22571 :     do {
    3983             : #if defined(DEBUG_TABLES)
    3984             :       fprintf(stderr, " [") ;
    3985             :       fprintf(stderr, g->text_string) ;
    3986             :       fprintf(stderr, "] ") ;
    3987             :       fflush(stderr);
    3988             :       if (strcmp(g->text_string, "XXXXXXX") == 0)
    3989             :         stop();
    3990             : #endif
    3991             : 
    3992       22585 :       nf = calc_nf(g, nf);
    3993       22585 :       calc_po_in(g, nf);
    3994       22585 :       if (g->is_col()) {
    3995          56 :         if ((tab_tag == type_of_col)
    3996           0 :             && (start_of_table != 0 /* nullptr */)) {
    3997           0 :           page_contents->glyphs.move_left();
    3998           0 :           insert_tab_te();
    3999           0 :           start_of_table->remember_table(tbl);
    4000           0 :           tbl = new html_table(&html, -1);
    4001           0 :           page_contents->insert_tag(string("*** TAB -> COL ***"));
    4002           0 :           if (tab_defs != 0 /* nullptr */)
    4003           0 :             tbl->tab_stops->init(tab_defs);
    4004           0 :           start_of_table = 0 /* nullptr */;
    4005           0 :           last = 0 /* nullptr */;
    4006             :         }
    4007          56 :         type_of_col = col_tag;
    4008          56 :         found_col = TRUE;
    4009          56 :         ncol = g->get_arg();
    4010          56 :         align = 'L';
    4011          56 :         colmin = 0;
    4012          56 :         colmax = 0;
    4013       22529 :       } else if (g->is_tab()) {
    4014           9 :         type_of_col = tab_tag;
    4015           9 :         colmin = g->get_tab_args(&align);
    4016           9 :         align = 'L'; // for now as 'C' and 'R' are broken
    4017           9 :         ncol = tbl->find_tab_column(colmin);
    4018           9 :         colmin += pageoffset + get_troff_indent();
    4019           9 :         colmax = tbl->get_tab_pos(ncol+1);
    4020           9 :         if (colmax > 0)
    4021           2 :           colmax += pageoffset + get_troff_indent();
    4022       22520 :       } else if (g->is_tab0()) {
    4023           2 :         if ((col_tag == type_of_col)
    4024           0 :             && (start_of_table != 0 /* nullptr */)) {
    4025           0 :           page_contents->glyphs.move_left();
    4026           0 :           insert_tab_te();
    4027           0 :           start_of_table->remember_table(tbl);
    4028           0 :           tbl = new html_table(&html, -1);
    4029           0 :           page_contents->insert_tag(string("*** COL -> TAB ***"));
    4030           0 :           start_of_table = 0 /* nullptr */;
    4031           0 :           last = 0 /* nullptr */;
    4032             :         }
    4033           2 :         if (tab_defs != 0 /* nullptr */)
    4034           2 :           tbl->tab_stops->init(tab_defs);
    4035           2 :         type_of_col = tab0_tag;
    4036           2 :         ncol = 1;
    4037           2 :         colmin = 0;
    4038           2 :         colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
    4039       22518 :       } else if (! g->is_a_tag())
    4040       20863 :         update_min_max(type_of_col, &colmin, &colmax, g);
    4041       45114 :       if ((g->is_col() || g->is_tab() || g->is_tab0())
    4042          67 :           && (start_of_line != 0 /* nullptr */)
    4043       45170 :           && (0 /* nullptr */ == start_of_table)) {
    4044          20 :         start_of_table = insert_tab_ts(start_of_line);
    4045          20 :         start_of_line = 0 /* nullptr */;
    4046       22565 :       } else if (g->is_ce()
    4047       22565 :           && (start_of_table != 0 /* nullptr */)) {
    4048           0 :         add_table_end("*** CE ***");
    4049           0 :         start_of_table->remember_table(tbl);
    4050           0 :         tbl = new html_table(&html, -1);
    4051           0 :         start_of_table = 0 /* nullptr */;
    4052           0 :         last = 0 /* nullptr */;
    4053       22565 :       } else if (g->is_ta()) {
    4054          33 :         tab_defs = g->text_string;
    4055          33 :         if (col_tag == type_of_col)
    4056           8 :           tbl->tab_stops->check_init(tab_defs);
    4057          33 :         if (!tbl->tab_stops->compatible(tab_defs)) {
    4058          14 :           if (start_of_table != 0 /* nullptr */) {
    4059           0 :             add_table_end("*** TABS ***");
    4060           0 :             start_of_table->remember_table(tbl);
    4061           0 :             tbl = new html_table(&html, -1);
    4062           0 :             start_of_table = 0 /* nullptr */;
    4063           0 :             type_of_col = none_tag;
    4064           0 :             last = 0 /* nullptr */;
    4065             :           }
    4066          14 :           tbl->tab_stops->init(tab_defs);
    4067             :         }
    4068             :       }
    4069       24307 :       if (((! g->is_a_tag()) || g->is_tab())
    4070       24307 :           && (start_of_table != 0 /* nullptr */)) {
    4071             :         // we are in a table and have a glyph
    4072         378 :         if ((0 == ncol)
    4073         378 :             || (! tbl->add_column(ncol, colmin, colmax, align))) {
    4074          19 :           if (0 == ncol)
    4075           8 :             add_table_end("*** NCOL == 0 ***");
    4076             :           else
    4077          11 :             add_table_end("*** CROSSED COLS ***");
    4078             : 
    4079          19 :           start_of_table->remember_table(tbl);
    4080          19 :           tbl = new html_table(&html, -1);
    4081          19 :           start_of_table = 0 /* nullptr */;
    4082          19 :           type_of_col = none_tag;
    4083          19 :           last = 0 /* nullptr */;
    4084             :         }
    4085             :       }
    4086             :       /*
    4087             :        *  move onto next glob, check whether we are starting a new line
    4088             :        */
    4089       22585 :       g = page_contents->glyphs.move_right_get_data();
    4090       22585 :       handle_state_assertion(g);
    4091       22585 :       if (0 /* nullptr */ == g) {
    4092          14 :         if (found_col) {
    4093           1 :           page_contents->glyphs.start_from_head();
    4094           1 :           as.reset();
    4095           1 :           last = g;
    4096           1 :           found_col = FALSE;
    4097             :         }
    4098       22571 :       } else if (g->is_br() || (nf && g->is_eol())) {
    4099         915 :         do {
    4100        3021 :           g = page_contents->glyphs.move_right_get_data();
    4101        3021 :           handle_state_assertion(g);
    4102        3021 :           nf = calc_nf(g, nf);
    4103             :         } while ((g != 0 /* nullptr */)
    4104        3021 :                  && (g->is_br() || (nf && g->is_eol())));
    4105        2106 :         start_of_line = g;
    4106        2106 :         ncol = 0;
    4107        2106 :         if (found_col)
    4108          39 :           last = g;
    4109        2106 :         found_col = FALSE;
    4110             :       }
    4111             :     } while ((g != 0 /* nullptr */)
    4112       22585 :              && (! page_contents->glyphs.is_equal_to_head()));
    4113             : 
    4114             : #if defined(DEBUG_TABLES)
    4115             :     fprintf(stderr, "finished scanning for tables\n");
    4116             : #endif
    4117             : 
    4118          14 :     page_contents->glyphs.start_from_head();
    4119          14 :     if (start_of_table != 0 /* nullptr */) {
    4120           1 :       if (last != 0 /* nullptr */)
    4121           0 :         while (last != page_contents->glyphs.get_data())
    4122           0 :           page_contents->glyphs.move_left();
    4123             : 
    4124           1 :       insert_tab_te();
    4125           1 :       start_of_table->remember_table(tbl);
    4126           1 :       tbl = 0 /* nullptr */;
    4127           1 :       page_contents->insert_tag(string("*** LAST ***"));
    4128             :     }
    4129             :   }
    4130          20 :   if (tbl != 0 /* nullptr */) {
    4131          19 :     delete tbl;
    4132          19 :     tbl = 0 /* nullptr */;
    4133             :   }
    4134             : 
    4135             :   // and reset the registers
    4136          20 :   pageoffset = old_pageoffset;
    4137          20 :   troff_indent = 0;
    4138          20 :   temp_indent = 0;
    4139          20 :   end_tempindent = 0;
    4140             : }
    4141             : 
    4142          20 : void html_printer::flush_page (void)
    4143             : {
    4144          20 :   suppress_sub_sup = TRUE;
    4145          20 :   flush_sbuf();
    4146          20 :   page_contents->dump_page();
    4147          20 :   lookahead_for_tables();
    4148          20 :   page_contents->dump_page();
    4149          20 :   flush_globs();
    4150          20 :   current_paragraph->done_para();
    4151          20 :   current_paragraph->flush_text();
    4152             :   // move onto a new page
    4153          20 :   delete page_contents;
    4154             : #if defined(DEBUG_TABLES)
    4155             :   fprintf(stderr, "\n\n*** flushed page ***\n\n");
    4156             :   html.simple_comment("new page called");
    4157             : #endif
    4158          20 :   page_contents = new page;
    4159          20 : }
    4160             : 
    4161             : /*
    4162             :  *  determine_space - works out whether we need to write a space.
    4163             :  *                    If last glyph is adjoining, then emit no space.
    4164             :  */
    4165             : 
    4166       20506 : void html_printer::determine_space (text_glob *g)
    4167             : {
    4168       20506 :   assert(g != 0 /* nullptr */);
    4169       20506 :   if (current_paragraph->is_in_pre()) {
    4170             :     /*
    4171             :      *  .nf has been specified
    4172             :      */
    4173           0 :     while (output_hpos < g->minh) {
    4174           0 :       output_hpos += space_width;
    4175           0 :       current_paragraph->emit_space();
    4176             :     }
    4177             :   } else {
    4178       20506 :     if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
    4179       19727 :       current_paragraph->emit_space();
    4180             :     }
    4181             :   }
    4182       20506 : }
    4183             : 
    4184             : /*
    4185             :  *  is_line_start - returns TRUE if we are at the start of a line.
    4186             :  */
    4187             : 
    4188           0 : int html_printer::is_line_start (int nf)
    4189             : {
    4190           0 :   int line_start  = FALSE; // TODO: boolify
    4191           0 :   int result      = TRUE;
    4192           0 :   text_glob *orig = page_contents->glyphs.get_data();
    4193             :   text_glob *g;
    4194             : 
    4195           0 :   if (! page_contents->glyphs.is_equal_to_head()) {
    4196           0 :     do {
    4197           0 :       page_contents->glyphs.move_left();
    4198           0 :       g = page_contents->glyphs.get_data();
    4199           0 :       result = g->is_a_tag();
    4200           0 :       if (g->is_fi())
    4201           0 :         nf = FALSE;
    4202           0 :       else if (g->is_nf())
    4203           0 :         nf = TRUE;
    4204           0 :       line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
    4205           0 :     } while ((!line_start) && (result));
    4206             :     /*
    4207             :      *  now restore our previous position.
    4208             :      */
    4209           0 :     while (page_contents->glyphs.get_data() != orig)
    4210           0 :       page_contents->glyphs.move_right();
    4211             :   }
    4212           0 :   return result;
    4213             : }
    4214             : 
    4215             : /*
    4216             :  *  is_font_courier - returns TRUE if the font, f, is courier.
    4217             :  */
    4218             : 
    4219             : // TODO: boolify
    4220         778 : int html_printer::is_font_courier (font *f)
    4221             : {
    4222             :   // XXX: This logic locks us into a font description file naming
    4223             :   // scheme.
    4224         778 :   if (f != 0 /* nullptr */) {
    4225         778 :     const char *fontname = f->get_filename();
    4226         778 :     return((fontname != 0 /* nullptr */) && ('C' == fontname[0]));
    4227             :   }
    4228           0 :   return FALSE;
    4229             : }
    4230             : 
    4231             : /*
    4232             :  *  end_font - shuts down the font corresponding to fontname.
    4233             :  */
    4234             : 
    4235        1349 : void html_printer::end_font (const char *fontname)
    4236             : {
    4237        1349 :   assert(fontname != 0 /* nullptr */);
    4238        1349 :   if (strcmp(fontname, "B") == 0) {
    4239         517 :     current_paragraph->done_bold();
    4240         832 :   } else if (strcmp(fontname, "I") == 0) {
    4241         207 :     current_paragraph->done_italic();
    4242         625 :   } else if (strcmp(fontname, "BI") == 0) {
    4243           0 :     current_paragraph->done_bold();
    4244           0 :     current_paragraph->done_italic();
    4245         625 :   } else if (strcmp(fontname, "CR") == 0) {
    4246           0 :     current_paragraph->done_tt();
    4247         625 :   } else if (strcmp(fontname, "CI") == 0) {
    4248           0 :     current_paragraph->done_italic();
    4249           0 :     current_paragraph->done_tt();
    4250         625 :   } else if (strcmp(fontname, "CB") == 0) {
    4251           0 :     current_paragraph->done_bold();
    4252           0 :     current_paragraph->done_tt();
    4253         625 :   } else if (strcmp(fontname, "CBI") == 0) {
    4254           0 :     current_paragraph->done_bold();
    4255           0 :     current_paragraph->done_italic();
    4256           0 :     current_paragraph->done_tt();
    4257             :   }
    4258        1349 : }
    4259             : 
    4260             : /*
    4261             :  *  start_font - starts the font corresponding to name.
    4262             :  */
    4263             : 
    4264        1447 : void html_printer::start_font (const char *fontname)
    4265             : {
    4266        1447 :   assert(fontname != 0 /* nullptr */);
    4267        1447 :   if (strcmp(fontname, "R") == 0) {
    4268         722 :     current_paragraph->done_bold();
    4269         722 :     current_paragraph->done_italic();
    4270         722 :     current_paragraph->done_tt();
    4271         725 :   } else if (strcmp(fontname, "B") == 0) {
    4272         517 :     current_paragraph->do_bold();
    4273         208 :   } else if (strcmp(fontname, "I") == 0) {
    4274         208 :     current_paragraph->do_italic();
    4275           0 :   } else if (strcmp(fontname, "BI") == 0) {
    4276           0 :     current_paragraph->do_bold();
    4277           0 :     current_paragraph->do_italic();
    4278           0 :   } else if (strcmp(fontname, "CR") == 0) {
    4279           0 :     if ((! fill_on)
    4280           0 :         && is_courier_until_eol()
    4281           0 :         && is_line_start(! fill_on)) {
    4282           0 :       current_paragraph->do_pre();
    4283             :     }
    4284           0 :     current_paragraph->do_tt();
    4285           0 :   } else if (strcmp(fontname, "CI") == 0) {
    4286           0 :     if ((! fill_on)
    4287           0 :         && is_courier_until_eol()
    4288           0 :         && is_line_start(! fill_on)) {
    4289           0 :       current_paragraph->do_pre();
    4290             :     }
    4291           0 :     current_paragraph->do_tt();
    4292           0 :     current_paragraph->do_italic();
    4293           0 :   } else if (strcmp(fontname, "CB") == 0) {
    4294           0 :     if ((! fill_on)
    4295           0 :         && is_courier_until_eol()
    4296           0 :         && is_line_start(! fill_on)) {
    4297           0 :       current_paragraph->do_pre();
    4298             :     }
    4299           0 :     current_paragraph->do_tt();
    4300           0 :     current_paragraph->do_bold();
    4301           0 :   } else if (strcmp(fontname, "CBI") == 0) {
    4302           0 :     if ((! fill_on)
    4303           0 :         && is_courier_until_eol()
    4304           0 :         && is_line_start(! fill_on)) {
    4305           0 :       current_paragraph->do_pre();
    4306             :     }
    4307           0 :     current_paragraph->do_tt();
    4308           0 :     current_paragraph->do_italic();
    4309           0 :     current_paragraph->do_bold();
    4310             :   }
    4311        1447 : }
    4312             : 
    4313             : /*
    4314             :  *  start_size - from is old font size, to is the new font size.
    4315             :  *               The HTML elements <big> and <small> respectively
    4316             :  *               increase and decrease the font size by 20%.  We try and
    4317             :  *               map these onto glyph sizes.
    4318             :  */
    4319             : 
    4320          47 : void html_printer::start_size (int from, int to)
    4321             : {
    4322          47 :   if (from < to) {
    4323          48 :     while (from < to) {
    4324          25 :       current_paragraph->do_big();
    4325          25 :       from += SIZE_INCREMENT;
    4326             :     }
    4327          24 :   } else if (from > to) {
    4328          50 :     while (from > to) {
    4329          26 :       current_paragraph->do_small();
    4330          26 :       from -= SIZE_INCREMENT;
    4331             :     }
    4332             :   }
    4333          47 : }
    4334             : 
    4335             : /*
    4336             :  *  do_font - checks to see whether we need to alter the html font.
    4337             :  */
    4338             : 
    4339       20845 : void html_printer::do_font (text_glob *g)
    4340             : {
    4341       20845 :   assert(g != 0 /* nullptr */);
    4342             :   /*
    4343             :    *  check if the output_style.point_size has not been set yet
    4344             :    *  this allow users to place .ps at the top of their troff files
    4345             :    *  and grohtml can then treat the .ps value as the base font size (3)
    4346             :    */
    4347       20845 :   if (-1 == output_style.point_size) {
    4348          13 :     output_style.point_size = pointsize;
    4349             :   }
    4350             : 
    4351       20845 :   if (g->text_style.f != output_style.f) {
    4352        1447 :     if (output_style.f != 0 /* nullptr */) {
    4353        1349 :       end_font(output_style.f->get_filename());
    4354             :     }
    4355        1447 :     output_style.f = g->text_style.f;
    4356        1447 :     if (output_style.f != 0 /* nullptr */) {
    4357        1447 :       start_font(output_style.f->get_filename());
    4358             :     }
    4359             :   }
    4360       20845 :   if (output_style.point_size != g->text_style.point_size) {
    4361          60 :     do_sup_or_sub(g);
    4362          60 :     if ((output_style.point_size > 0)
    4363          47 :         && (g->text_style.point_size > 0)) {
    4364          47 :       start_size(output_style.point_size, g->text_style.point_size);
    4365             :     }
    4366          60 :     if (g->text_style.point_size > 0) {
    4367          58 :       output_style.point_size = g->text_style.point_size;
    4368             :     }
    4369             :   }
    4370       20845 :   if (output_style.col != g->text_style.col) {
    4371           6 :     current_paragraph->done_color();
    4372           6 :     output_style.col = g->text_style.col;
    4373           6 :     current_paragraph->do_color(&output_style.col);
    4374             :   }
    4375       20845 : }
    4376             : 
    4377             : /*
    4378             :  *  start_subscript - returns TRUE if, g, looks like a subscript start.
    4379             :  */
    4380             : 
    4381             : // TODO: boolify
    4382          17 : int html_printer::start_subscript (text_glob *g)
    4383             : {
    4384          17 :   assert(g != 0 /* nullptr */);
    4385          17 :   int r        = font::res;
    4386          17 :   int height   = output_style.point_size*r/72;
    4387             : 
    4388          17 :   return ((output_style.point_size != 0)
    4389          17 :           && (output_vpos < g->minv)
    4390           9 :           && (output_vpos-height > g->maxv)
    4391          34 :           && (output_style.point_size > g->text_style.point_size));
    4392             : }
    4393             : 
    4394             : /*
    4395             :  *  start_superscript - returns TRUE if, g, looks like a superscript
    4396             :  *                      start.
    4397             :  */
    4398             : 
    4399             : // TODO: boolify
    4400          17 : int html_printer::start_superscript (text_glob *g)
    4401             : {
    4402          17 :   assert(g != 0 /* nullptr */);
    4403          17 :   int r        = font::res;
    4404          17 :   int height   = output_style.point_size*r/72;
    4405             : 
    4406          17 :   return ((output_style.point_size != 0)
    4407          17 :           && (output_vpos > g->minv)
    4408           8 :           && (output_vpos-height < g->maxv)
    4409          34 :           && (output_style.point_size > g->text_style.point_size));
    4410             : }
    4411             : 
    4412             : /*
    4413             :  *  end_subscript - returns TRUE if, g, looks like the end of a
    4414             :  *                  subscript.
    4415             :  */
    4416             : 
    4417             : // TODO: boolify
    4418          17 : int html_printer::end_subscript (text_glob *g)
    4419             : {
    4420          17 :   assert(g != 0 /* nullptr */);
    4421          17 :   int r        = font::res;
    4422          17 :   int height   = output_style.point_size*r/72;
    4423             : 
    4424          17 :   return ((output_style.point_size != 0)
    4425          17 :           && (g->minv < output_vpos)
    4426           8 :           && (output_vpos-height > g->maxv)
    4427          34 :           && (output_style.point_size < g->text_style.point_size));
    4428             : }
    4429             : 
    4430             : /*
    4431             :  *  end_superscript - returns TRUE if, g, looks like the end of a
    4432             :  *                    superscript.
    4433             :  */
    4434             : 
    4435             : // TODO: boolify
    4436          17 : int html_printer::end_superscript (text_glob *g)
    4437             : {
    4438          17 :   assert(g != 0 /* nullptr */);
    4439          17 :   int r        = font::res;
    4440          17 :   int height   = output_style.point_size*r/72;
    4441             : 
    4442          17 :   return ((output_style.point_size != 0)
    4443          17 :           && (g->minv > output_vpos)
    4444           9 :           && (output_vpos-height < g->maxv)
    4445          34 :           && (output_style.point_size < g->text_style.point_size));
    4446             : }
    4447             : 
    4448             : /*
    4449             :  *  do_sup_or_sub - checks to see whether the next glyph is a
    4450             :  *                  subscript/superscript start/end and it calls the
    4451             :  *                  services of html-text to issue the appropriate tags.
    4452             :  */
    4453             : 
    4454          60 : void html_printer::do_sup_or_sub (text_glob *g)
    4455             : {
    4456          60 :   if (! suppress_sub_sup) {
    4457          17 :     if (start_subscript(g)) {
    4458           0 :       current_paragraph->do_sub();
    4459          17 :     } else if (start_superscript(g)) {
    4460           0 :       current_paragraph->do_sup();
    4461          17 :     } else if (end_subscript(g)) {
    4462           0 :       current_paragraph->done_sub();
    4463          17 :     } else if (end_superscript(g)) {
    4464           0 :       current_paragraph->done_sup();
    4465             :     }
    4466             :   }
    4467          60 : }
    4468             : 
    4469             : /*
    4470             :  *  do_end_para - writes out the html text after shutting down the
    4471             :  *                current paragraph.
    4472             :  */
    4473             : 
    4474         283 : void html_printer::do_end_para (text_glob *g)
    4475             : {
    4476         283 :   assert(g != 0 /* nullptr */);
    4477         283 :   do_font(g);
    4478         283 :   current_paragraph->done_para();
    4479         283 :   current_paragraph->remove_para_space();
    4480         283 :   html.put_string(g->text_string+9);
    4481         283 :   output_vpos     = g->minv;
    4482         283 :   output_hpos     = g->maxh;
    4483         283 :   output_vpos_max = g->maxv;
    4484         283 :   suppress_sub_sup = FALSE;
    4485         283 : }
    4486             : 
    4487             : /*
    4488             :  *  emit_html - write out the html text
    4489             :  */
    4490             : 
    4491       20506 : void html_printer::emit_html (text_glob *g)
    4492             : {
    4493       20506 :   assert(g != 0 /* nullptr */);
    4494       20506 :   do_font(g);
    4495       20506 :   determine_space(g);
    4496       20506 :   current_paragraph->do_emittext(g->text_string, g->text_length);
    4497       20506 :   output_vpos     = g->minv;
    4498       20506 :   output_hpos     = g->maxh;
    4499       20506 :   output_vpos_max = g->maxv;
    4500       20506 :   suppress_sub_sup = FALSE;
    4501       20506 : }
    4502             : 
    4503             : /*
    4504             :  *  flush_sbuf - flushes the current sbuf into the list of glyphs.
    4505             :  */
    4506             : 
    4507       31080 : void html_printer::flush_sbuf()
    4508             : {
    4509       31080 :   if (sbuf.length() > 0) {
    4510       20281 :     int r=font::res;   // resolution of the device
    4511       20281 :     set_style(sbuf_style);
    4512             : 
    4513       20281 :     if (overstrike_detected && (! is_bold(sbuf_style.f))) {
    4514           0 :       font *bold_font = make_bold(sbuf_style.f);
    4515           0 :       if (bold_font != 0 /* nullptr */)
    4516           0 :         sbuf_style.f = bold_font;
    4517             :     }
    4518             : 
    4519       20281 :     page_contents->add(&sbuf_style, sbuf, line_number,
    4520       20281 :                        (sbuf_vpos - (sbuf_style.point_size * r / 72)),
    4521             :                        sbuf_start_hpos, sbuf_vpos, sbuf_end_hpos);
    4522       20281 :     output_hpos = sbuf_end_hpos;
    4523       20281 :     output_vpos = sbuf_vpos;
    4524       20281 :     last_sbuf_length = 0;
    4525       20281 :     sbuf_prev_hpos = sbuf_end_hpos;
    4526       20281 :     overstrike_detected = FALSE;
    4527       20281 :     sbuf.clear();
    4528             :   }
    4529       31080 : }
    4530             : 
    4531           0 : void html_printer::set_line_thickness(const environment *env)
    4532             : {
    4533           0 :   assert(env != 0 /* nullptr */);
    4534           0 :   line_thickness = env->size;
    4535           0 : }
    4536             : 
    4537           0 : void html_printer::draw(int code, int *p, int np,
    4538             :                         const environment *env)
    4539             : {
    4540           0 :   assert(p != 0 /* nullptr */);
    4541           0 :   assert(env != 0 /* nullptr */);
    4542           0 :   switch (code) {
    4543             : 
    4544           0 :   case 'l':
    4545             : # if 0
    4546             :     if (2 == np) {
    4547             :       page_contents->add_line(&sbuf_style,
    4548             :                               line_number,
    4549             :                               env->hpos, env->vpos,
    4550             :                               (env->hpos + p[0]), (env->vpos + p[1]),
    4551             :                               line_thickness);
    4552             :     } else {
    4553             :       error("2 arguments required for line");
    4554             :     }
    4555             : # endif
    4556           0 :     break;
    4557           0 :   case 't':
    4558             :     {
    4559           0 :       if (0 == np) {
    4560           0 :         line_thickness = -1;
    4561             :       } else {
    4562             :         // troff gratuitously adds an extra 0
    4563           0 :         if ((np != 1) && (np != 2)) {
    4564           0 :           error("0 or 1 argument required for thickness");
    4565           0 :           break;
    4566             :         }
    4567           0 :         line_thickness = p[0];
    4568             :       }
    4569           0 :       break;
    4570             :     }
    4571             : 
    4572           0 :   case 'P':
    4573           0 :     break;
    4574           0 :   case 'p':
    4575           0 :     break;
    4576           0 :   case 'E':
    4577           0 :     break;
    4578           0 :   case 'e':
    4579           0 :     break;
    4580           0 :   case 'C':
    4581           0 :     break;
    4582           0 :   case 'c':
    4583           0 :     break;
    4584           0 :   case 'a':
    4585           0 :     break;
    4586           0 :   case '~':
    4587           0 :     break;
    4588           0 :   case 'f':
    4589           0 :     break;
    4590           0 :   case 'F':
    4591             :     // fill with color env->fill
    4592           0 :     if (background != 0 /* nullptr */)
    4593           0 :       delete background;
    4594           0 :     background = new color;
    4595           0 :     *background = *env->fill;
    4596           0 :     break;
    4597             : 
    4598           0 :   default:
    4599           0 :     error("unrecognized drawing command '%1'", char(code));
    4600           0 :     break;
    4601             :   }
    4602           0 : }
    4603             : 
    4604          17 : html_printer::html_printer()
    4605             : : html(0, MAX_LINE_LENGTH),
    4606             :   no_of_printed_pages(0),
    4607             :   last_sbuf_length(0),
    4608             :   overstrike_detected(FALSE),
    4609             :   output_hpos(-1),
    4610             :   output_vpos(-1),
    4611             :   output_vpos_max(-1),
    4612             :   line_thickness(-1),
    4613             :   inside_font_style(0),
    4614             :   page_number(0),
    4615             :   header_indent(-1),
    4616             :   suppress_sub_sup(TRUE),
    4617             :   cutoff_heading(100),
    4618             :   indent(0),
    4619             :   table(0),
    4620             :   end_center(0),
    4621             :   end_tempindent(0),
    4622             :   next_tag(INLINE),
    4623             :   fill_on(TRUE),
    4624             :   max_linelength(-1),
    4625             :   linelength(0),
    4626             :   pageoffset(0),
    4627             :   troff_indent(0),
    4628             :   device_indent(0),
    4629             :   temp_indent(0),
    4630             :   pointsize(base_point_size),
    4631             :   line_number(0),
    4632             :   background(default_background),
    4633             :   seen_indent(FALSE),
    4634             :   next_indent(0),
    4635             :   seen_pageoffset(FALSE),
    4636             :   next_pageoffset(0),
    4637             :   seen_linelength(FALSE),
    4638             :   next_linelength(0),
    4639             :   seen_center(FALSE),
    4640             :   next_center(0),
    4641             :   seen_space(0),
    4642             :   seen_break(0),
    4643             :   current_column(0),
    4644          17 :   row_space(FALSE)
    4645             : {
    4646          17 :   file_list.add_new_file(xtmpfile());
    4647          17 :   html.set_file(file_list.get_file());
    4648          17 :   if (font::hor != 24)
    4649           0 :     fatal("horizontal motion quantum must be 24");
    4650          17 :   if (font::vert != 40)
    4651           0 :     fatal("vertical motion quantum must be 40");
    4652             : #if 0
    4653             :   // should be sorted html..
    4654             :   if ((font::res % (font::sizescale*72)) != 0)
    4655             :     fatal("res must be a multiple of 72*sizescale");
    4656             : #endif
    4657          17 :   int r = font::res;
    4658          17 :   int point = 0;
    4659          34 :   while ((r % 10) == 0) {
    4660          17 :     r /= 10;
    4661          17 :     point++;
    4662             :   }
    4663          17 :   res               = r;
    4664          17 :   html.set_fixed_point(point);
    4665          17 :   space_glyph       = name_to_glyph("space");
    4666          17 :   space_width       = font::hor;
    4667          17 :   paper_length      = font::paperlength;
    4668          17 :   linelength        = font::res*13/2;
    4669          17 :   if (0 == paper_length)
    4670          17 :     paper_length    = 11*font::res;
    4671             : 
    4672          17 :   page_contents = new page();
    4673          17 : }
    4674             : 
    4675             : /*
    4676             :  *  add_to_sbuf - adds character code or name to the sbuf.
    4677             :  */
    4678             : 
    4679       97980 : void html_printer::add_to_sbuf (glyph *g, const string &s)
    4680             : {
    4681       97980 :   if (0 /* nullptr */ == sbuf_style.f)
    4682           0 :     return;
    4683             : 
    4684       97980 :   const char *html_glyph = 0;
    4685       97980 :   unsigned int code = sbuf_style.f->get_code(g);
    4686             : 
    4687       97980 :   if (s.empty()) {
    4688       97652 :     if (sbuf_style.f->contains(g))
    4689       97652 :       html_glyph = get_html_entity(sbuf_style.f->get_code(g));
    4690             :     else
    4691           0 :       html_glyph = 0;
    4692             : 
    4693       97652 :     if ((0 /* nullptr */ == html_glyph) && (code >= UNICODE_DESC_START))
    4694           0 :       html_glyph = static_cast<bool>(charset_encoding)
    4695           0 :                      ? to_utf8_string(code)
    4696           0 :                      : to_numerical_char_ref(code);
    4697             :   } else
    4698         328 :     html_glyph = get_html_translation(sbuf_style.f, s);
    4699             : 
    4700       97980 :   last_sbuf_length = sbuf.length();
    4701       97980 :   if (0 /* nullptr */ == html_glyph)
    4702       95741 :     sbuf += ((char)code);
    4703             :   else
    4704        2239 :     sbuf += html_glyph;
    4705             : }
    4706             : 
    4707             : // TODO: boolify
    4708       93925 : int html_printer::sbuf_continuation (glyph *g, const char *name,
    4709             :                                      const environment *env, int w)
    4710             : {
    4711       93925 :   assert(env != 0 /* nullptr */);
    4712             :   /*
    4713             :    *  lets see whether the glyph is closer to the end of sbuf
    4714             :    */
    4715       93925 :   if ((sbuf_end_hpos == env->hpos)
    4716       16226 :       || ((sbuf_prev_hpos < sbuf_end_hpos)
    4717       16226 :           && (env->hpos < sbuf_end_hpos)
    4718           0 :           && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
    4719       77699 :     add_to_sbuf(g, name);
    4720       77699 :     sbuf_prev_hpos = sbuf_end_hpos;
    4721       77699 :     sbuf_end_hpos += w + sbuf_kern;
    4722       77699 :     return TRUE;
    4723             :   } else {
    4724       16226 :     if ((env->hpos >= sbuf_end_hpos)
    4725       16226 :         && ((0 == sbuf_kern)
    4726           0 :             || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
    4727             :       /*
    4728             :        *  lets see whether a space is needed or not
    4729             :        */
    4730             : 
    4731       16226 :       if (env->hpos-sbuf_end_hpos < space_width) {
    4732           0 :         add_to_sbuf(g, name);
    4733           0 :         sbuf_prev_hpos = sbuf_end_hpos;
    4734           0 :         sbuf_end_hpos = env->hpos + w;
    4735           0 :         return TRUE;
    4736             :       }
    4737             :     }
    4738             :   }
    4739       16226 :   return FALSE;
    4740             : }
    4741             : 
    4742             : /*
    4743             :  *  get_html_translation - given the position of the character and its
    4744             :  *                         name return the device encoding for such
    4745             :  *                         character.
    4746             :  */
    4747             : 
    4748         329 : const char *get_html_translation (font *f, const string &name)
    4749             : {
    4750         329 :   if ((0 /* nullptr */ == f) || name.empty())
    4751           0 :     return 0 /* nullptr */;
    4752             :   else {
    4753         329 :     glyph *g = name_to_glyph((name + '\0').contents());
    4754         329 :     if (f->contains(g))
    4755         329 :       return get_html_entity(f->get_code(g));
    4756             :     else
    4757           0 :       return 0 /* nullptr */;
    4758             :   }
    4759             : }
    4760             : 
    4761             : /*
    4762             :  * get_html_entity - given a Unicode character's code point, return an
    4763             :  *                   HTML entity that represents the character, if the
    4764             :  *                   character cannot represent itself in all contexts.
    4765             :  *                   the return value, if not a null pointer, is
    4766             :  *                   allocated in a static buffer and is only valid
    4767             :  *                   until the next call of this function.
    4768             :  */
    4769       97981 : static const char *get_html_entity (unsigned int code)
    4770             : {
    4771       97981 :   if (code < UNICODE_DESC_START) {
    4772       96337 :     switch (code) {
    4773         240 :       case 0x0022: return "&quot;";
    4774          10 :       case 0x0026: return "&amp;";
    4775         168 :       case 0x003C: return "&lt;";
    4776         178 :       case 0x003E: return "&gt;";
    4777       95741 :       default: return 0;
    4778             :     }
    4779        1644 :   } else if (CHARSET_UTF8 == charset_encoding) {
    4780           0 :       return to_utf8_string(code);
    4781             :   } else {
    4782        1644 :     switch (code) {
    4783         119 :       case 0x00A0: return "&nbsp;";
    4784           0 :       case 0x00A1: return "&iexcl;";
    4785           0 :       case 0x00A2: return "&cent;";
    4786           0 :       case 0x00A3: return "&pound;";
    4787           0 :       case 0x00A4: return "&curren;";
    4788           0 :       case 0x00A5: return "&yen;";
    4789           0 :       case 0x00A6: return "&brvbar;";
    4790           0 :       case 0x00A7: return "&sect;";
    4791           0 :       case 0x00A8: return "&uml;";
    4792           0 :       case 0x00A9: return "&copy;";
    4793           0 :       case 0x00AA: return "&ordf;";
    4794           0 :       case 0x00AB: return "&laquo;";
    4795           0 :       case 0x00AC: return "&not;";
    4796           0 :       case 0x00AE: return "&reg;";
    4797           0 :       case 0x00AF: return "&macr;";
    4798           0 :       case 0x00B0: return "&deg;";
    4799           0 :       case 0x00B1: return "&plusmn;";
    4800           0 :       case 0x00B2: return "&sup2;";
    4801           0 :       case 0x00B3: return "&sup3;";
    4802           0 :       case 0x00B4: return "&acute;";
    4803           0 :       case 0x00B5: return "&micro;";
    4804           0 :       case 0x00B6: return "&para;";
    4805           0 :       case 0x00B7: return "&middot;";
    4806           0 :       case 0x00B8: return "&cedil;";
    4807           0 :       case 0x00B9: return "&sup1;";
    4808           0 :       case 0x00BA: return "&ordm;";
    4809           0 :       case 0x00BB: return "&raquo;";
    4810           0 :       case 0x00BC: return "&frac14;";
    4811           0 :       case 0x00BD: return "&frac12;";
    4812           0 :       case 0x00BE: return "&frac34;";
    4813           0 :       case 0x00BF: return "&iquest;";
    4814           0 :       case 0x00C0: return "&Agrave;";
    4815           0 :       case 0x00C1: return "&Aacute;";
    4816           0 :       case 0x00C2: return "&Acirc;";
    4817           0 :       case 0x00C3: return "&Atilde;";
    4818           0 :       case 0x00C4: return "&Auml;";
    4819           0 :       case 0x00C5: return "&Aring;";
    4820           0 :       case 0x00C6: return "&AElig;";
    4821           0 :       case 0x00C7: return "&Ccedil;";
    4822           0 :       case 0x00C8: return "&Egrave;";
    4823           0 :       case 0x00C9: return "&Eacute;";
    4824           0 :       case 0x00CA: return "&Ecirc;";
    4825           0 :       case 0x00CB: return "&Euml;";
    4826           0 :       case 0x00CC: return "&Igrave;";
    4827           0 :       case 0x00CD: return "&Iacute;";
    4828           0 :       case 0x00CE: return "&Icirc;";
    4829           0 :       case 0x00CF: return "&Iuml;";
    4830           0 :       case 0x00D0: return "&ETH;";
    4831           0 :       case 0x00D1: return "&Ntilde;";
    4832           0 :       case 0x00D2: return "&Ograve;";
    4833           0 :       case 0x00D3: return "&Oacute;";
    4834           0 :       case 0x00D4: return "&Ocirc;";
    4835           0 :       case 0x00D5: return "&Otilde;";
    4836           1 :       case 0x00D6: return "&Ouml;";
    4837           0 :       case 0x00D7: return "&times;";
    4838           0 :       case 0x00D8: return "&Oslash;";
    4839           0 :       case 0x00D9: return "&Ugrave;";
    4840           0 :       case 0x00DA: return "&Uacute;";
    4841           0 :       case 0x00DB: return "&Ucirc;";
    4842           0 :       case 0x00DC: return "&Uuml;";
    4843           0 :       case 0x00DD: return "&Yacute;";
    4844           0 :       case 0x00DE: return "&THORN;";
    4845           0 :       case 0x00DF: return "&szlig;";
    4846           0 :       case 0x00E0: return "&agrave;";
    4847           3 :       case 0x00E1: return "&aacute;";
    4848           0 :       case 0x00E2: return "&acirc;";
    4849           0 :       case 0x00E3: return "&atilde;";
    4850           1 :       case 0x00E4: return "&auml;";
    4851           0 :       case 0x00E5: return "&aring;";
    4852           1 :       case 0x00E6: return "&aelig;";
    4853           0 :       case 0x00E7: return "&ccedil;";
    4854           0 :       case 0x00E8: return "&egrave;";
    4855           1 :       case 0x00E9: return "&eacute;";
    4856           0 :       case 0x00EA: return "&ecirc;";
    4857           0 :       case 0x00EB: return "&euml;";
    4858           0 :       case 0x00EC: return "&igrave;";
    4859           0 :       case 0x00ED: return "&iacute;";
    4860           0 :       case 0x00EE: return "&icirc;";
    4861           0 :       case 0x00EF: return "&iuml;";
    4862           0 :       case 0x00F0: return "&eth;";
    4863           0 :       case 0x00F1: return "&ntilde;";
    4864           0 :       case 0x00F2: return "&ograve;";
    4865           1 :       case 0x00F3: return "&oacute;";
    4866           0 :       case 0x00F4: return "&ocirc;";
    4867           0 :       case 0x00F5: return "&otilde;";
    4868           1 :       case 0x00F6: return "&ouml;";
    4869           0 :       case 0x00F7: return "&divide;";
    4870           0 :       case 0x00F8: return "&oslash;";
    4871           0 :       case 0x00F9: return "&ugrave;";
    4872           0 :       case 0x00FA: return "&uacute;";
    4873           0 :       case 0x00FB: return "&ucirc;";
    4874           2 :       case 0x00FC: return "&uuml;";
    4875           0 :       case 0x00FD: return "&yacute;";
    4876           0 :       case 0x00FE: return "&thorn;";
    4877           0 :       case 0x00FF: return "&yuml;";
    4878           0 :       case 0x0152: return "&OElig;";
    4879           0 :       case 0x0153: return "&oelig;";
    4880           0 :       case 0x0160: return "&Scaron;";
    4881           0 :       case 0x0161: return "&scaron;";
    4882           0 :       case 0x0178: return "&Yuml;";
    4883           0 :       case 0x0192: return "&fnof;";
    4884           0 :       case 0x0391: return "&Alpha;";
    4885           0 :       case 0x0392: return "&Beta;";
    4886           0 :       case 0x0393: return "&Gamma;";
    4887           0 :       case 0x0394: return "&Delta;";
    4888           0 :       case 0x0395: return "&Epsilon;";
    4889           0 :       case 0x0396: return "&Zeta;";
    4890           0 :       case 0x0397: return "&Eta;";
    4891           0 :       case 0x0398: return "&Theta;";
    4892           0 :       case 0x0399: return "&Iota;";
    4893           0 :       case 0x039A: return "&Kappa;";
    4894           0 :       case 0x039B: return "&Lambda;";
    4895           0 :       case 0x039C: return "&Mu;";
    4896           0 :       case 0x039D: return "&Nu;";
    4897           0 :       case 0x039E: return "&Xi;";
    4898           0 :       case 0x039F: return "&Omicron;";
    4899           0 :       case 0x03A0: return "&Pi;";
    4900           0 :       case 0x03A1: return "&Rho;";
    4901           0 :       case 0x03A3: return "&Sigma;";
    4902           0 :       case 0x03A4: return "&Tau;";
    4903           0 :       case 0x03A5: return "&Upsilon;";
    4904           0 :       case 0x03A6: return "&Phi;";
    4905           0 :       case 0x03A7: return "&Chi;";
    4906           0 :       case 0x03A8: return "&Psi;";
    4907           0 :       case 0x03A9: return "&Omega;";
    4908           0 :       case 0x03B1: return "&alpha;";
    4909           0 :       case 0x03B2: return "&beta;";
    4910           0 :       case 0x03B3: return "&gamma;";
    4911           0 :       case 0x03B4: return "&delta;";
    4912           0 :       case 0x03B5: return "&epsilon;";
    4913           0 :       case 0x03B6: return "&zeta;";
    4914           0 :       case 0x03B7: return "&eta;";
    4915           0 :       case 0x03B8: return "&theta;";
    4916           0 :       case 0x03B9: return "&iota;";
    4917           0 :       case 0x03BA: return "&kappa;";
    4918           0 :       case 0x03BB: return "&lambda;";
    4919           0 :       case 0x03BC: return "&mu;";
    4920           0 :       case 0x03BD: return "&nu;";
    4921           0 :       case 0x03BE: return "&xi;";
    4922           0 :       case 0x03BF: return "&omicron;";
    4923           0 :       case 0x03C0: return "&pi;";
    4924           0 :       case 0x03C1: return "&rho;";
    4925           0 :       case 0x03C2: return "&sigmaf;";
    4926           0 :       case 0x03C3: return "&sigma;";
    4927           0 :       case 0x03C4: return "&tau;";
    4928           0 :       case 0x03C5: return "&upsilon;";
    4929           0 :       case 0x03C6: return "&phi;";
    4930           0 :       case 0x03C7: return "&chi;";
    4931           0 :       case 0x03C8: return "&psi;";
    4932           0 :       case 0x03C9: return "&omega;";
    4933           0 :       case 0x03D1: return "&thetasym;";
    4934           0 :       case 0x03D6: return "&piv;";
    4935           5 :       case 0x2013: return "&ndash;";
    4936           9 :       case 0x2014: return "&mdash;";
    4937         547 :       case 0x2018: return "&lsquo;";
    4938         694 :       case 0x2019: return "&rsquo;";
    4939           0 :       case 0x201A: return "&sbquo;";
    4940          19 :       case 0x201C: return "&ldquo;";
    4941          19 :       case 0x201D: return "&rdquo;";
    4942           0 :       case 0x201E: return "&bdquo;";
    4943           0 :       case 0x2020: return "&dagger;";
    4944           0 :       case 0x2021: return "&Dagger;";
    4945           0 :       case 0x2022: return "&bull;";
    4946           0 :       case 0x2030: return "&permil;";
    4947           0 :       case 0x2032: return "&prime;";
    4948           0 :       case 0x2033: return "&Prime;";
    4949           0 :       case 0x2039: return "&lsaquo;";
    4950           0 :       case 0x203A: return "&rsaquo;";
    4951           0 :       case 0x203E: return "&oline;";
    4952           0 :       case 0x2044: return "&frasl;";
    4953           0 :       case 0x20AC: return "&euro;";
    4954           0 :       case 0x2111: return "&image;";
    4955           0 :       case 0x2118: return "&weierp;";
    4956           0 :       case 0x211C: return "&real;";
    4957           0 :       case 0x2122: return "&trade;";
    4958           0 :       case 0x2135: return "&alefsym;";
    4959           0 :       case 0x2190: return "&larr;";
    4960           0 :       case 0x2191: return "&uarr;";
    4961           0 :       case 0x2192: return "&rarr;";
    4962           0 :       case 0x2193: return "&darr;";
    4963           0 :       case 0x2194: return "&harr;";
    4964           0 :       case 0x21D0: return "&lArr;";
    4965           0 :       case 0x21D1: return "&uArr;";
    4966           0 :       case 0x21D2: return "&rArr;";
    4967           0 :       case 0x21D3: return "&dArr;";
    4968           0 :       case 0x21D4: return "&hArr;";
    4969           0 :       case 0x2200: return "&forall;";
    4970           0 :       case 0x2202: return "&part;";
    4971           0 :       case 0x2203: return "&exist;";
    4972           0 :       case 0x2205: return "&empty;";
    4973           0 :       case 0x2207: return "&nabla;";
    4974           0 :       case 0x2208: return "&isin;";
    4975           0 :       case 0x2209: return "&notin;";
    4976           0 :       case 0x220B: return "&ni;";
    4977           0 :       case 0x220F: return "&prod;";
    4978           0 :       case 0x2211: return "&sum;";
    4979         192 :       case 0x2212: return "&minus;";
    4980           0 :       case 0x2217: return "&lowast;";
    4981           0 :       case 0x221A: return "&radic;";
    4982           0 :       case 0x221D: return "&prop;";
    4983           0 :       case 0x221E: return "&infin;";
    4984           0 :       case 0x2220: return "&ang;";
    4985           0 :       case 0x2227: return "&and;";
    4986           0 :       case 0x2228: return "&or;";
    4987           0 :       case 0x2229: return "&cap;";
    4988           0 :       case 0x222A: return "&cup;";
    4989           0 :       case 0x222B: return "&int;";
    4990           0 :       case 0x2234: return "&there4;";
    4991           0 :       case 0x223C: return "&sim;";
    4992           0 :       case 0x2245: return "&cong;";
    4993           0 :       case 0x2248: return "&asymp;";
    4994           0 :       case 0x2260: return "&ne;";
    4995           0 :       case 0x2261: return "&equiv;";
    4996           0 :       case 0x2264: return "&le;";
    4997           2 :       case 0x2265: return "&ge;";
    4998           0 :       case 0x2282: return "&sub;";
    4999           0 :       case 0x2283: return "&sup;";
    5000           0 :       case 0x2284: return "&nsub;";
    5001           0 :       case 0x2286: return "&sube;";
    5002           0 :       case 0x2287: return "&supe;";
    5003           0 :       case 0x2295: return "&oplus;";
    5004           0 :       case 0x2297: return "&otimes;";
    5005           0 :       case 0x22A5: return "&perp;";
    5006           0 :       case 0x22C5: return "&sdot;";
    5007           0 :       case 0x2308: return "&lceil;";
    5008           0 :       case 0x2309: return "&rceil;";
    5009           0 :       case 0x230A: return "&lfloor;";
    5010           0 :       case 0x230B: return "&rfloor;";
    5011          11 :       case 0x2329: return "&lang;";
    5012          11 :       case 0x232A: return "&rang;";
    5013           0 :       case 0x25CA: return "&loz;";
    5014           0 :       case 0x2660: return "&spades;";
    5015           0 :       case 0x2663: return "&clubs;";
    5016           0 :       case 0x2665: return "&hearts;";
    5017           0 :       case 0x2666: return "&diams;";
    5018           0 :       case 0x27E8: return "&lang;";
    5019           0 :       case 0x27E9: return "&rang;";
    5020           5 :       default: return (static_cast<bool>(charset_encoding)
    5021           5 :                          ? to_utf8_string(code)
    5022          10 :                          : to_numerical_char_ref(code));
    5023             :     }
    5024             :   }
    5025             : }
    5026             : 
    5027             : /*
    5028             :  *  overstrike - returns TRUE if the glyph (i, name) is going to
    5029             :  *               overstrike a previous glyph in sbuf.  If TRUE the font
    5030             :  *               is changed to bold and the previous sbuf is flushed.
    5031             :  */
    5032             : 
    5033             : // TODO: boolify
    5034       16226 : int html_printer::overstrike(glyph *g, const char *name,
    5035             :                              const environment *env, int w)
    5036             : {
    5037       16226 :   assert(env != 0 /* nullptr */);
    5038       16226 :   if ((env->hpos < sbuf_end_hpos)
    5039       16226 :       || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos)))
    5040             :   {
    5041             :     /*
    5042             :      *  at this point we have detected an overlap
    5043             :      */
    5044           0 :     if (overstrike_detected) {
    5045             :       /* already detected, remove previous glyph and use this glyph */
    5046           0 :       sbuf.set_length(last_sbuf_length);
    5047           0 :       add_to_sbuf(g, name);
    5048           0 :       sbuf_end_hpos = env->hpos + w;
    5049           0 :       return TRUE;
    5050             :     } else {
    5051             :       /* first time we have detected an overstrike in the sbuf */
    5052           0 :       sbuf.set_length(last_sbuf_length); /* remove previous glyph */
    5053           0 :       if (! is_bold(sbuf_style.f))
    5054           0 :         flush_sbuf();
    5055           0 :       overstrike_detected = TRUE;
    5056           0 :       add_to_sbuf(g, name);
    5057           0 :       sbuf_end_hpos = env->hpos + w;
    5058           0 :       return TRUE;
    5059             :     }
    5060             :   }
    5061       16226 :   return FALSE;
    5062             : }
    5063             : 
    5064             : /*
    5065             :  *  set_char - adds a character into the sbuf if it is a continuation
    5066             :  *             with the previous word otherwise flush the current sbuf
    5067             :  *             and add character anew.
    5068             :  */
    5069             : 
    5070       97980 : void html_printer::set_char(glyph *g, font *f, const environment *env,
    5071             :                             int w, const char *name)
    5072             : {
    5073       97980 :   assert(env != 0 /* nullptr */);
    5074       97980 :   style sty(f, env->size, env->height, env->slant, env->fontno,
    5075       97980 :             *env->col);
    5076       97980 :   if (sty.slant != 0) {
    5077           0 :     if ((sty.slant > 80) || (sty.slant < -80)) {
    5078           0 :       error("slant of %1 degrees out of range", sty.slant);
    5079           0 :       sty.slant = 0;
    5080             :     }
    5081             :   }
    5082       97980 :   if (((!sbuf.empty())
    5083       95183 :        && (sty == sbuf_style)
    5084       93925 :        && (sbuf_vpos == env->vpos))
    5085      209389 :       && (sbuf_continuation(g, name, env, w)
    5086       16226 :           || overstrike(g, name, env, w)))
    5087       77699 :     return;
    5088             : 
    5089       20281 :   flush_sbuf();
    5090       20281 :   if (0 /* nullptr */ == sbuf_style.f)
    5091           7 :     sbuf_style = sty;
    5092       20281 :   add_to_sbuf(g, name);
    5093       20281 :   sbuf_end_hpos = env->hpos + w;
    5094       20281 :   sbuf_start_hpos = env->hpos;
    5095       20281 :   sbuf_prev_hpos = env->hpos;
    5096       20281 :   sbuf_vpos = env->vpos;
    5097       20281 :   sbuf_style = sty;
    5098       20281 :   sbuf_kern = 0;
    5099             : }
    5100             : 
    5101             : /*
    5102             :  *  set_numbered_char - handle numbered characters.  Negative values are
    5103             :  *                      interpreted as unbreakable spaces; the value
    5104             :  *                      (taken positive) gives the width.
    5105             :  */
    5106             : 
    5107         123 : void html_printer::set_numbered_char(int num, const environment *env,
    5108             :                                      int *widthp)
    5109             : {
    5110         123 :   assert(env != 0 /* nullptr */);
    5111         123 :   int nbsp_width = 0;
    5112         123 :   if (num < 0) {
    5113         119 :     nbsp_width = -num;
    5114         119 :     num = 160;          // &nbsp;
    5115             :   }
    5116         123 :   glyph *g = number_to_glyph(num);
    5117         123 :   int fn = env->fontno;
    5118         123 :   if ((fn < 0) || (fn >= nfonts)) {
    5119           0 :     error("invalid font position '%1'", fn);
    5120           0 :     return;
    5121             :   }
    5122         123 :   font *f = font_table[fn];
    5123         123 :   if (0 /* nullptr */ == f) {
    5124           0 :     error("no font mounted at position %1", fn);
    5125           0 :     return;
    5126             :   }
    5127         123 :   if (!f->contains(g)) {
    5128           0 :     error("font description file '%1' has no glyph at index %2",
    5129           0 :           f->get_filename(), num);
    5130           0 :     return;
    5131             :   }
    5132             :   int w;
    5133         123 :   if (nbsp_width > 0)
    5134         119 :     w = nbsp_width;
    5135             :   else
    5136           4 :     w = f->get_width(g, env->size);
    5137         123 :   w = round_width(w);
    5138         123 :   if (widthp != 0 /* nullptr */)
    5139           0 :     *widthp = w;
    5140         123 :   set_char(g, f, env, w, 0 /* nullptr */);
    5141             : }
    5142             : 
    5143             : // XXX: Except for `w = round_width(w);`, this seems to be identical
    5144             : // to the overridden `printer::set_char_and_width()`.
    5145           0 : glyph *html_printer::set_char_and_width(const char *nm,
    5146             :                                         const environment *env,
    5147             :                                         int *widthp, font **f)
    5148             : {
    5149           0 :   assert(nm != 0 /* nullptr */);
    5150           0 :   assert(env != 0 /* nullptr */);
    5151           0 :   assert(f != 0 /* nullptr */);
    5152           0 :   glyph *g = name_to_glyph(nm);
    5153           0 :   int fn = env->fontno;
    5154           0 :   if ((fn < 0) || (fn >= nfonts)) {
    5155           0 :     error("invalid font position '%1'", fn);
    5156           0 :     return UNDEFINED_GLYPH;
    5157             :   }
    5158           0 :   *f = font_table[fn];
    5159           0 :   if (0 /* nullptr */ == *f) {
    5160           0 :     error("no font mounted at position %1", fn);
    5161           0 :     return UNDEFINED_GLYPH;
    5162             :   }
    5163           0 :   if (!(*f)->contains(g)) {
    5164           0 :     if ((nm[0] != '\0') && ('\0' == nm[1]))
    5165           0 :       error("font description file '%1' lacks glyph for ordinary"
    5166           0 :             " character '%2'", (*f)->get_filename(), nm[0]);
    5167             :     else
    5168           0 :       error("font description file '%1' lacks glyph for special"
    5169           0 :             " character '%2'", (*f)->get_filename(), nm);
    5170           0 :     return UNDEFINED_GLYPH;
    5171             :   }
    5172           0 :   int w = (*f)->get_width(g, env->size);
    5173           0 :   w = round_width(w);
    5174           0 :   if (widthp != 0 /* nullptr */)
    5175           0 :     *widthp = w;
    5176           0 :   return g;
    5177             : }
    5178             : 
    5179             : /*
    5180             :  *  write_title - writes the title to this document
    5181             :  */
    5182             : 
    5183          34 : void html_printer::write_title (int in_head)
    5184             : {
    5185          34 :   if (title.has_been_found) {
    5186          12 :     if (in_head) {
    5187           6 :       html.put_string("<title>");
    5188           6 :       html.put_string(title.text);
    5189           6 :       html.put_string("</title>").nl().nl();
    5190             :     } else {
    5191           6 :       title.has_been_written = TRUE;
    5192           6 :       if (title.with_h1) {
    5193           5 :         if (xhtml == dialect)
    5194           0 :           html.put_string("<h1>");
    5195             :         else
    5196           5 :           html.put_string("<h1 align=\"center\">");
    5197           5 :         html.put_string(title.text);
    5198           5 :         html.put_string("</h1>").nl().nl();
    5199             :       }
    5200             :     }
    5201          22 :   } else if (in_head) {
    5202             :     // place empty title tags to help conform to 'tidy'
    5203          11 :     html.put_string("<title></title>").nl();
    5204             :   }
    5205          34 : }
    5206             : 
    5207             : /*
    5208             :  *  write_rule - emits HTML rule element if the auto_rule is TRUE.
    5209             :  */
    5210             : 
    5211         192 : static void write_rule (void)
    5212             : {
    5213         192 :   if (auto_rule) {
    5214         123 :     if (xhtml == dialect)
    5215           0 :       fputs("<hr/>\n", stdout);
    5216             :     else
    5217         123 :       fputs("<hr>\n", stdout);
    5218             :   }
    5219         192 : }
    5220             : 
    5221          20 : void html_printer::begin_page(int n)
    5222             : {
    5223          20 :   page_number            =  n;
    5224             : #if defined(DEBUGGING)
    5225             :   html.begin_comment("Page: ")
    5226             :     .put_string(i_to_a(page_number)).end_comment();;
    5227             : #endif
    5228          20 :   no_of_printed_pages++;
    5229             : 
    5230          20 :   output_style.f         =  0;
    5231          20 :   output_style.point_size= -1;
    5232          20 :   output_space_code      = 32;
    5233          20 :   output_draw_point_size = -1;
    5234          20 :   output_line_thickness  = -1;
    5235          20 :   output_hpos            = -1;
    5236          20 :   output_vpos            = -1;
    5237          20 :   output_vpos_max        = -1;
    5238          20 :   current_paragraph      = new html_text(&html, dialect);
    5239          20 :   do_indent(get_troff_indent(), pageoffset, linelength);
    5240          20 :   current_paragraph->do_para("", FALSE);
    5241          20 : }
    5242             : 
    5243          20 : void html_printer::end_page(int)
    5244             : {
    5245          20 :   flush_sbuf();
    5246          20 :   flush_page();
    5247          20 : }
    5248             : 
    5249          18 : font *html_printer::make_font(const char *nm)
    5250             : {
    5251          18 :   return html_font::load_html_font(nm);
    5252             : }
    5253             : 
    5254          17 : void html_printer::do_body (void)
    5255             : {
    5256          17 :   if (0 /* nullptr */ == background)
    5257          16 :     fputs("<body>\n\n", stdout);
    5258             :   else {
    5259             :     char buf[(INT_HEXDIGITS * 3) + 1];
    5260             :     unsigned int r, g, b;
    5261             : 
    5262           1 :     background->get_rgb(&r, &g, &b);
    5263             :     // we have to scale 0..0xFFFF to 0..0xFF
    5264           1 :     sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
    5265             : 
    5266           1 :     fputs("<body bgcolor=\"#", stdout);
    5267           1 :     fputs(buf, stdout);
    5268           1 :     fputs("\">\n\n", stdout);
    5269             :   }
    5270          17 : }
    5271             : 
    5272             : /*
    5273             :  *  emit_link - generates: <a href="to">name</a>
    5274             :  */
    5275             : 
    5276         168 : void html_printer::emit_link (const string &to, const char *name)
    5277             : {
    5278         168 :   assert(name != 0 /* nullptr */);
    5279         168 :   fputs("<a href=\"", stdout);
    5280         168 :   fputs(to.contents(), stdout);
    5281         168 :   fputs("\">", stdout);
    5282         168 :   fputs(name, stdout);
    5283         168 :   fputs("</a>", stdout);
    5284         168 : }
    5285             : 
    5286             : /*
    5287             :  *  write_navigation - writes out the links which navigate between
    5288             :  *                     file fragments.
    5289             :  */
    5290             : 
    5291          80 : void html_printer::write_navigation (const string &top,
    5292             :                                      const string &prev,
    5293             :                                      const string &next,
    5294             :                                      const string &current)
    5295             : {
    5296          80 :   int need_bar = FALSE;
    5297             : 
    5298          80 :   if (multiple_files) {
    5299          80 :     current_paragraph->done_para();
    5300          80 :     write_rule();
    5301          80 :     if (groff_sig)
    5302           0 :       fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
    5303             :             "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
    5304             :             "<colgroup><col class=\"left\"></col>"
    5305             :             "<col class=\"right\"></col></colgroup>\n"
    5306             :             "<tr><td class=\"left\">", stdout);
    5307          80 :     handle_valid_flag(FALSE);
    5308          80 :     fputs("[ ", stdout);
    5309          80 :     if ((strcmp(prev.contents(), "") != 0)
    5310          46 :         && prev != top
    5311         126 :         && prev != current) {
    5312          44 :       emit_link(prev, "prev");
    5313          44 :       need_bar = TRUE;
    5314             :     }
    5315          80 :     if ((strcmp(next.contents(), "") != 0)
    5316          44 :         && next != top
    5317         124 :         && next != current) {
    5318          44 :       if (need_bar)
    5319          42 :         fputs(" | ", stdout);
    5320          44 :       emit_link(next, "next");
    5321          44 :       need_bar = TRUE;
    5322             :     }
    5323          80 :     if (top != "<standard input>"
    5324          80 :         && (strcmp(top.contents(), "") != 0)
    5325         160 :         && top != current) {
    5326          80 :       if (need_bar)
    5327          46 :         fputs(" | ", stdout);
    5328          80 :       emit_link(top, "top");
    5329             :     }
    5330          80 :     fputs(" ]\n", stdout);
    5331          80 :     if (groff_sig) {
    5332           0 :       fputs("</td><td class=\"right\"><i><small>"
    5333             :             "This document was produced using "
    5334             :             "<a href=\"http://www.gnu.org/software/groff/\">"
    5335             :             "groff-", stdout);
    5336           0 :       fputs(Version_string, stdout);
    5337           0 :       fputs("</a>.</small></i></td></tr></table>\n", stdout);
    5338             :     }
    5339          80 :     write_rule();
    5340             :   }
    5341          80 : }
    5342             : 
    5343             : /*
    5344             :  *  do_file_components - scan the file list copying each temporary file
    5345             :  *                       in turn.  This has twofold use: firstly to emit
    5346             :  *                       section heading links, between file fragments
    5347             :  *                       if required and secondly to generate jobname
    5348             :  *                       file fragments if required.
    5349             :  */
    5350             : 
    5351          17 : void html_printer::do_file_components (void)
    5352             : {
    5353          17 :   int fragment_no = 1;
    5354          34 :   string top;
    5355          34 :   string prev;
    5356          34 :   string next;
    5357          34 :   string current;
    5358             : 
    5359          17 :   file_list.start_of_list();
    5360          17 :   top = string(job_name);
    5361          17 :   if (xhtml == dialect)
    5362           0 :     top += string(".xhtml");
    5363             :   else
    5364          17 :     top += string(".html");
    5365          17 :   top += '\0';
    5366          17 :   next = file_list.next_file_name();
    5367          17 :   next += '\0';
    5368          17 :   current = next;
    5369          92 :   while (file_list.get_file() != 0 /* nullptr */) {
    5370          75 :     if (fseek(file_list.get_file(), 0L, SEEK_SET) < 0)
    5371           0 :       fatal("unable to seek within temporary file: %1",
    5372           0 :             strerror(errno));
    5373          75 :     html.copy_file(file_list.get_file());
    5374          75 :     fclose(file_list.get_file());
    5375          75 :     file_list.move_next();
    5376          75 :     if (file_list.is_new_output_file()) {
    5377             :       struct tm *t;
    5378             : 
    5379          40 :       if (fragment_no > 1)
    5380          38 :         write_navigation(top, prev, next, current);
    5381          40 :       prev = current;
    5382          40 :       current = next;
    5383          40 :       next = file_list.next_file_name();
    5384          40 :       next += '\0';
    5385          80 :       string split_file = file_list.file_name();
    5386          40 :       split_file += '\0';
    5387          40 :       fflush(stdout);
    5388          40 :       if (!freopen(split_file.contents(), "w", stdout)) {
    5389           0 :         fatal("unable to reopen standard output stream: %1",
    5390           0 :               strerror(errno));
    5391             :       }
    5392          40 :       fragment_no++;
    5393          40 :       if (xhtml == dialect)
    5394           0 :         writeHeadMetaStyle();
    5395             : 
    5396          40 :       if (do_write_creator_comment) {
    5397          40 :         html.begin_comment("Creator     : ")
    5398          40 :           .put_string("groff ")
    5399          40 :           .put_string("version ")
    5400          80 :           .put_string(Version_string)
    5401          40 :           .end_comment();
    5402             :       }
    5403             : 
    5404          40 :       if (do_write_date_comment) {
    5405          40 :         t = current_time();
    5406          40 :         html.begin_comment("CreationDate: ")
    5407          40 :           .put_string(asctime(t), strlen(asctime(t))-1)
    5408          40 :           .end_comment();
    5409             :       }
    5410             : 
    5411          40 :       if (html4 == dialect)
    5412          40 :         writeHeadMetaStyle();
    5413             : 
    5414          40 :       html.put_string("<title>");
    5415          40 :       html.put_string(split_file.contents());
    5416          40 :       html.put_string("</title>").nl().nl();
    5417             : 
    5418          40 :       fputs(head_info.contents(), stdout);
    5419          40 :       fputs("</head>\n", stdout);
    5420          40 :       write_navigation(top, prev, next, current);
    5421             :     }
    5422          75 :     if (file_list.are_links_required())
    5423          18 :       header.write_headings(stdout, TRUE);
    5424             :   }
    5425          17 :   if (fragment_no > 1)
    5426           2 :     write_navigation(top, prev, next, current);
    5427             :   else {
    5428          15 :     assert(current_paragraph != 0 /* nullptr */);
    5429          15 :     current_paragraph->done_para();
    5430          15 :     write_rule();
    5431          15 :     if (valid_flag) {
    5432           0 :       if (groff_sig)
    5433           0 :         fputs("\n\n<table width=\"100%\" border=\"0\" rules=\"none\"\n"
    5434             :               "frame=\"void\" cellspacing=\"1\" cellpadding=\"0\">\n"
    5435             :               "<colgroup><col class=\"left\"></col>"
    5436             :               "<col class=\"right\"></col></colgroup>\n"
    5437             :               "<tr><td class=\"left\">", stdout);
    5438           0 :       handle_valid_flag(TRUE);
    5439           0 :       if (groff_sig) {
    5440           0 :         fputs("</td><td class=\"right\"><i><small>"
    5441             :               "This document was produced using "
    5442             :               "<a href=\"http://www.gnu.org/software/groff/\">"
    5443             :               "groff-", stdout);
    5444           0 :         fputs(Version_string, stdout);
    5445           0 :         fputs("</a>.</small></i></td></tr></table>\n", stdout);
    5446             :       }
    5447           0 :       write_rule();
    5448             :     }
    5449             :   }
    5450          17 : }
    5451             : 
    5452             : /*
    5453             :  *  writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
    5454             :  *                       related information.
    5455             :  */
    5456             : 
    5457          57 : void html_printer::writeHeadMetaStyle (void)
    5458             : {
    5459          57 :   if (html4 == dialect) {
    5460          57 :     fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional"
    5461             :           "//EN\"\n", stdout);
    5462          57 :     fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
    5463          57 :     fputs("<html>\n", stdout);
    5464          57 :     fputs("<head>\n", stdout);
    5465          57 :     fputs("<meta name=\"generator\" "
    5466             :           "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
    5467          57 :     fputs("<meta http-equiv=\"Content-Type\" "
    5468             :           "content=\"text/html; charset=", stdout);
    5469          57 :     fputs(static_cast<bool>(charset_encoding)
    5470             :             ? "UTF-8" : "US-ASCII", stdout);
    5471          57 :     fputs("\">\n", stdout);
    5472          57 :     fputs("<meta name=\"Content-Style\" content=\"text/css\">\n",
    5473             :           stdout);
    5474          57 :     fputs("<style type=\"text/css\">\n", stdout);
    5475             :   }
    5476             :   else {
    5477           0 :     fputs("<?xml version=\"1.0\" encoding=\"", stdout);
    5478           0 :     fputs(static_cast<bool>(charset_encoding)
    5479             :             ? "UTF-8" : "us-ascii", stdout);
    5480           0 :     fputs("\"?>\n", stdout);
    5481           0 :     fputs("<!DOCTYPE html PUBLIC \"-//W3C//"
    5482             :           "DTD XHTML 1.1 plus MathML 2.0//EN\"\n", stdout);
    5483           0 :     fputs(" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\"\n",
    5484             :           stdout);
    5485           0 :     fputs(" [<!ENTITY mathml \"http://www.w3.org/1998/Math/"
    5486             :           "MathML\">]>\n", stdout);
    5487             : 
    5488           0 :     fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\" "
    5489             :           "xml:lang=\"en\">\n", stdout);
    5490           0 :     fputs("<head>\n", stdout);
    5491           0 :     fputs("<meta name=\"generator\" "
    5492             :           "content=\"groff -Txhtml, see www.gnu.org\"/>\n", stdout);
    5493           0 :     fputs("<meta http-equiv=\"Content-Type\" "
    5494             :           "content=\"text/html; charset=", stdout);
    5495           0 :     fputs(static_cast<bool>(charset_encoding)
    5496             :             ? "UTF-8" : "US-ASCII", stdout);
    5497           0 :     fputs("\"/>\n", stdout);
    5498           0 :     fputs("<meta name=\"Content-Style\" content=\"text/css\"/>\n",
    5499             :           stdout);
    5500           0 :     fputs("<style type=\"text/css\">\n", stdout);
    5501           0 :     fputs("       .center { text-align: center }\n", stdout);
    5502           0 :     fputs("       .right  { text-align: right }\n", stdout);
    5503             :   }
    5504          57 :   fputs("       p       { margin-top: 0; margin-bottom: 0; "
    5505             :         "vertical-align: top }\n", stdout);
    5506          57 :   fputs("       pre     { margin-top: 0; margin-bottom: 0; "
    5507             :         "vertical-align: top }\n", stdout);
    5508          57 :   fputs("       table   { margin-top: 0; margin-bottom: 0; "
    5509             :         "vertical-align: top }\n", stdout);
    5510          57 :   fputs("       h1      { text-align: center }\n", stdout);
    5511          57 :   fputs("</style>\n", stdout);
    5512          57 : }
    5513             : 
    5514          34 : html_printer::~html_printer()
    5515             : {
    5516             :   struct tm *t;
    5517             : 
    5518          17 :   current_lineno = 0; // At this point, we've read all the input.
    5519             : 
    5520          17 :   if (current_paragraph)
    5521          17 :     current_paragraph->flush_text();
    5522          17 :   html.end_line();
    5523          17 :   html.set_file(stdout);
    5524             : 
    5525          17 :   if (xhtml == dialect)
    5526           0 :     writeHeadMetaStyle();
    5527             : 
    5528          17 :   if (do_write_creator_comment) {
    5529          15 :     html.begin_comment("Creator     : ")
    5530          15 :       .put_string("groff ")
    5531          15 :       .put_string("version ")
    5532          30 :       .put_string(Version_string)
    5533          15 :       .end_comment();
    5534             :   }
    5535             : 
    5536          17 :   if (do_write_date_comment) {
    5537          15 :     t = current_time();
    5538          15 :     html.begin_comment("CreationDate: ")
    5539          15 :       .put_string(asctime(t), strlen(asctime(t))-1)
    5540          15 :       .end_comment();
    5541             :   }
    5542             : 
    5543          17 :   if (html4 == dialect)
    5544          17 :     writeHeadMetaStyle();
    5545             : 
    5546          17 :   write_title(TRUE);
    5547          17 :   head_info += '\0';
    5548          17 :   fputs(head_info.contents(), stdout);
    5549          17 :   fputs("</head>\n", stdout);
    5550          17 :   do_body();
    5551             : 
    5552          17 :   write_title(FALSE);
    5553          17 :   header.write_headings(stdout, FALSE);
    5554          17 :   write_rule();
    5555             : #if defined(DEBUGGING)
    5556             :   html.begin_comment("Total number of pages: ")
    5557             :     .put_string(i_to_a(no_of_printed_pages)).end_comment();
    5558             : #endif
    5559          17 :   html.end_line();
    5560          17 :   html.end_line();
    5561             : 
    5562          17 :   if (multiple_files) {
    5563           2 :     fputs("</body>\n", stdout);
    5564           2 :     fputs("</html>\n", stdout);
    5565           2 :     do_file_components();
    5566             :   } else {
    5567          15 :     do_file_components();
    5568          15 :     fputs("</body>\n", stdout);
    5569          15 :     fputs("</html>\n", stdout);
    5570             :   }
    5571          34 : }
    5572             : 
    5573             : /*
    5574             :  *  get_str - returns a duplicate of string, s. The duplicate
    5575             :  *            string is terminated at the next ',' or ']'.
    5576             :  */
    5577             : 
    5578           0 : static char *get_str (const char *s, char **n)
    5579             : {
    5580           0 :   assert(s != 0 /* nullptr */);
    5581           0 :   assert(n != 0 /* nullptr */);
    5582           0 :   int i = 0;
    5583             :   char *v;
    5584             : 
    5585           0 :   while ((s[i] != '\0') && (s[i] != ',') && (s[i] != ']'))
    5586           0 :     i++;
    5587           0 :   if (i>0) {
    5588           0 :     v = new char[i+1];
    5589           0 :     memcpy(v, s, i+1);
    5590           0 :     v[i] = '\0';
    5591           0 :     if (',' == s[i])
    5592           0 :       (*n) = const_cast<char *>(&s[i+1]);
    5593             :     else
    5594           0 :       (*n) = const_cast<char *>(&s[i]);
    5595           0 :     return v;
    5596             :   }
    5597           0 :   if (',' == s[i])
    5598           0 :     (*n) = const_cast<char *>(&s[1]);
    5599             :   else
    5600           0 :     (*n) = const_cast<char *>(s);
    5601           0 :   return 0;
    5602             : }
    5603             : 
    5604             : /*
    5605             :  *  make_val - creates a string from `v` if `s` is a null pointer.
    5606             :  */
    5607             : 
    5608           0 : char *make_val (char *s, int v, char *id, char *f, char *l)
    5609             : {
    5610           0 :   assert(id != 0 /* nullptr */);
    5611           0 :   assert(f != 0 /* nullptr */);
    5612           0 :   assert(l != 0 /* nullptr */);
    5613           0 :   if (0 /* nullptr */ == s) {
    5614             :     char buf[30];
    5615             : 
    5616           0 :     sprintf(buf, "%d", v);
    5617           0 :     return strsave(buf);
    5618             :   }
    5619             :   else {
    5620             :     /*
    5621             :      *  check that value, s, is the same as, v.
    5622             :      */
    5623           0 :     char *t = s;
    5624             : 
    5625           0 :     while ('=' == *t)
    5626           0 :       t++;
    5627           0 :     if (atoi(t) != v) {
    5628           0 :       if (0 /* nullptr */ == f)
    5629           0 :         f = const_cast<char *>("stdin");
    5630           0 :       if (0 /* nullptr */ == l)
    5631           0 :         f = const_cast<char *>("<none>");
    5632           0 :       fprintf(stderr, "%s:%s:%s: assertion failed at id%s;"
    5633             :               " expected %d, got %s\n", program_name, f, l, id, v, s);
    5634             :     }
    5635           0 :     return s;
    5636             :   }
    5637             : }
    5638             : 
    5639             : /*
    5640             :  *  handle_assertion - handles the assertions created via .www:ASSERT
    5641             :  *                     in www.tmac.  See www.tmac for examples.  This
    5642             :  *                     method should be called as we are parsing the
    5643             :  *                     ditroff input.  It checks the x, y position
    5644             :  *                     assertions.  It does _not_ check the troff state
    5645             :  *                     assertions as these are unknown at this point.
    5646             :  */
    5647             : 
    5648           0 : void html_printer::handle_assertion (int minv, int minh,
    5649             :                                      int maxv, int maxh, const char *s)
    5650             : {
    5651             :   char *n;
    5652           0 :   char *cmd = get_str(s, &n);
    5653           0 :   char *id  = get_str(n, &n);
    5654           0 :   char *val = get_str(n, &n);
    5655           0 :   char *file= get_str(n, &n);
    5656           0 :   char *line= get_str(n, &n);
    5657             : 
    5658           0 :   if (strcmp(cmd, "assertion:[x") == 0)
    5659           0 :     as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
    5660           0 :   else if (strcmp(cmd, "assertion:[y") == 0)
    5661           0 :     as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
    5662             :   else
    5663           0 :     if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
    5664           0 :       page_contents->add_tag(&sbuf_style, string(s),
    5665             :                              line_number, minv, minh, maxv, maxh);
    5666           0 : }
    5667             : 
    5668             : /*
    5669             :  *  build_state_assertion - builds the troff state assertions.
    5670             :  */
    5671             : 
    5672      101971 : void html_printer::handle_state_assertion (text_glob *g)
    5673             : {
    5674      101971 :   if ((g != 0 /* nullptr */)
    5675      101957 :       && g->is_a_tag()
    5676      203928 :       && (strncmp(g->text_string, "assertion:[", 11) == 0)) {
    5677           0 :     char *n   = const_cast<char *>(&g->text_string[11]);
    5678           0 :     char *cmd = get_str(n, &n);
    5679           0 :     char *val = get_str(n, &n);
    5680           0 :     (void)get_str(n, &n);   // unused
    5681           0 :     char *file= get_str(n, &n);
    5682           0 :     char *line= get_str(n, &n);
    5683             : 
    5684           0 :     as.build(cmd, val, file, line);
    5685             :   }
    5686      101971 : }
    5687             : 
    5688             : /*
    5689             :  *  special - handle all x X requests from troff.  For post-html they
    5690             :  *            allow users to pass raw HTML commands, turn auto linked
    5691             :  *            headings off/on, and so forth.
    5692             :  */
    5693             : 
    5694         847 : void html_printer::special(char *s, const environment *env, char type)
    5695             : {
    5696         847 :   assert(env != 0 /* nullptr */);
    5697         847 :   if (type != 'p')
    5698           0 :     return;
    5699         847 :   if (s != 0 /* nullptr */) {
    5700         847 :     flush_sbuf();
    5701         847 :     if (env->fontno >= 0) {
    5702         847 :       style sty(get_font_from_index(env->fontno), env->size,
    5703         847 :                 env->height, env->slant, env->fontno, *env->col);
    5704         847 :       sbuf_style = sty;
    5705             :     }
    5706             : 
    5707         847 :     if (strncmp(s, "html:", 5) == 0) {
    5708         563 :       int r=font::res;   /* resolution of the device */
    5709         563 :       font *f=sbuf_style.f;
    5710             : 
    5711         563 :       if (0 /* nullptr */ == f)
    5712           0 :         f = font::load_font("TR");
    5713             : 
    5714             :       /*
    5715             :        *  pass rest of string through to html output during flush
    5716             :        */
    5717         563 :       page_contents->add_and_encode(&sbuf_style, string(&s[5]),
    5718             :                                     line_number,
    5719         563 :                                     env->vpos-env->size*r/72, env->hpos,
    5720         563 :                                     env->vpos               , env->hpos,
    5721             :                                     FALSE);
    5722             : 
    5723             :       /*
    5724             :        * assume that the html command has no width, if it does then
    5725             :        * hopefully troff will have fudged this in a macro by requesting
    5726             :        * that the formatting move right by the appropriate amount.
    5727             :        */
    5728         284 :     } else if ((strncmp(s, "html</p>:", 9) == 0)
    5729           1 :                || (strncmp(s, "html<?p>:", 9) == 0)
    5730           1 :                || (strncmp(s, "math<?p>:", 9) == 0)) {
    5731         283 :       int r=font::res;   /* resolution of the device */
    5732         283 :       font *f=sbuf_style.f;
    5733         283 :       string t;
    5734             : 
    5735         283 :       if (0 /* nullptr */ == f)
    5736           0 :         f = font::load_font("TR");
    5737             : 
    5738         283 :       if (strncmp(s, "math<?p>:", 9) == 0) {
    5739           0 :         if (strncmp(&s[9], "<math>", 6) == 0) {
    5740           0 :           s[9] = '\0';
    5741           0 :           t = s;
    5742           0 :           t += "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
    5743           0 :           t += &s[15];
    5744           0 :           t += '\0';
    5745           0 :           s = &t[0];
    5746             :         }
    5747             :       }
    5748             : 
    5749             :       /*
    5750             :        *  need to pass all of string through to html output during flush
    5751             :        */
    5752         283 :       page_contents->add_and_encode(&sbuf_style, string(s),
    5753             :                                     line_number,
    5754         283 :                                     env->vpos-env->size*r/72, env->hpos,
    5755         283 :                                     env->vpos               , env->hpos,
    5756         283 :                                     TRUE);
    5757             : 
    5758             :       /*
    5759             :        * assume that the html command has no width, if it does then
    5760             :        * hopefully troff will have fudged this in a macro by
    5761             :        * requesting that the formatting move right by the appropriate
    5762             :        * amount.
    5763             :        */
    5764             : 
    5765           1 :     } else if (strncmp(s, "index:", 6) == 0) {
    5766           1 :       cutoff_heading = atoi(&s[6]);
    5767           0 :     } else if (strncmp(s, "assertion:[", 11) == 0) {
    5768           0 :       int r=font::res;   /* resolution of the device */
    5769             : 
    5770           0 :       handle_assertion(env->vpos-env->size*r/72, env->hpos,
    5771           0 :                        env->vpos, env->hpos, s);
    5772             :     }
    5773             :   }
    5774             : }
    5775             : 
    5776             : /*
    5777             :  *  devtag - handles device troff tags sent from the 'troff'.
    5778             :  *           These include the troff state machine tags:
    5779             :  *           .br, .sp, .in, .tl, .ll etc
    5780             :  *
    5781             :  *           (see man 5 grohtml_tags).
    5782             :  */
    5783             : 
    5784        4460 : void html_printer::devtag (char *s, const environment *env, char type)
    5785             : {
    5786        4460 :   assert(env != 0 /* nullptr */);
    5787        4460 :   if (type != 'p')
    5788           0 :     return;
    5789             : 
    5790        4460 :   if (s != 0 /* nullptr */) {
    5791        4460 :     flush_sbuf();
    5792        4460 :     if (env->fontno >= 0) {
    5793        4363 :       style sty(get_font_from_index(env->fontno), env->size,
    5794        4363 :                 env->height, env->slant, env->fontno, *env->col);
    5795        4363 :       sbuf_style = sty;
    5796             :     }
    5797             : 
    5798        4460 :     if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
    5799        4460 :       int r=font::res;   /* resolution of the device */
    5800             : 
    5801        4460 :       page_contents->add_tag(&sbuf_style, string(s),
    5802             :                              line_number,
    5803        4460 :                              env->vpos-env->size*r/72, env->hpos,
    5804        4460 :                              env->vpos               , env->hpos);
    5805             :     }
    5806             :   }
    5807             : }
    5808             : 
    5809             : 
    5810             : /*
    5811             :  *  taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
    5812             :  */
    5813             : 
    5814         123 : int html_printer::round_width(int x)
    5815             : {
    5816         123 :   int r = font::hor;
    5817             :   int n;
    5818             : 
    5819             :   // don't depend on rounding direction for division of negative ints
    5820         123 :   if (1 == r)
    5821           0 :     n = x;
    5822             :   else
    5823         123 :     n = (x < 0
    5824         123 :          ? -((-x + r/2 - 1)/r)
    5825         123 :          : (x + r/2 - 1)/r);
    5826         123 :   return n * r;
    5827             : }
    5828             : 
    5829             : /*
    5830             :  *  handle_valid_flag - emits a valid XHTML 1.1 or HTML 4.01 button,
    5831             :  *                      provided -V was supplied on the command line.
    5832             :  */
    5833             : 
    5834          80 : void html_printer::handle_valid_flag (int needs_para)
    5835             : {
    5836          80 :   if (valid_flag) {
    5837           0 :     if (needs_para)
    5838           0 :       fputs("<p>", stdout);
    5839           0 :     if (xhtml == dialect)
    5840           0 :       fputs("<a href=\"http://validator.w3.org/check?uri=referer\">"
    5841             :             "<img src=\"http://www.w3.org/Icons/valid-xhtml11-blue\" "
    5842             :             "alt=\"Valid XHTML 1.1 Transitional\" "
    5843             :             "height=\"31\" width=\"88\" /></a>\n", stdout);
    5844             :     else
    5845           0 :       fputs("<a href=\"http://validator.w3.org/check?uri=referer\">"
    5846             :             "<img src=\"http://www.w3.org/Icons/valid-html401-blue\" "
    5847             :             "alt=\"Valid HTML 4.01 Transitional\" "
    5848             :             "height=\"31\" width=\"88\"></a>\n", stdout);
    5849           0 :     if (needs_para)
    5850           0 :       fputs("</p>", stdout);
    5851             :   }
    5852          80 : }
    5853             : 
    5854          17 : int main(int argc, char **argv)
    5855             : {
    5856          17 :   program_name = argv[0];
    5857             :   static char stderr_buf[BUFSIZ];
    5858          17 :   setbuf(stderr, stderr_buf);
    5859             :   int c;
    5860             :   static const struct option long_options[] = {
    5861             :     { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
    5862             :     { "version", no_argument, 0 /* nullptr */, 'v' },
    5863             :     { 0 /* nullptr */, 0, 0, 0 }
    5864             :   };
    5865          31 :   while ((c = getopt_long(argc, argv, ":a:bCdD:eF:g:Ghi:I:j:k:l"
    5866             :                                       "no:prs:S:vVx:y",
    5867             :                           long_options, 0 /* nullptr */))
    5868          31 :          != EOF)
    5869          14 :     switch (c) {
    5870           0 :     case 'a':
    5871             :       /* text antialiasing bits - handled by pre-html */
    5872           0 :       break;
    5873           1 :     case 'b':
    5874             :       // set background color to white
    5875           1 :       default_background = new color;
    5876           1 :       default_background->set_gray(color::MAX_COLOR_VAL);
    5877           1 :       break;
    5878           2 :     case 'C':
    5879             :       // Don't write CreationDate HTML comments.
    5880           2 :       do_write_date_comment = FALSE;
    5881           2 :       break;
    5882           0 :     case 'd':
    5883             :       /* handled by pre-html */
    5884           0 :       break;
    5885           2 :     case 'D':
    5886             :       /* handled by pre-html */
    5887           2 :       break;
    5888           0 :     case 'e':
    5889             :       /* handled by pre-html */
    5890           0 :       break;
    5891           0 :     case 'F':
    5892           0 :       font::command_line_font_dir(optarg);
    5893           0 :       break;
    5894           0 :     case 'g':
    5895             :       /* graphic antialiasing bits - handled by pre-html */
    5896           0 :       break;
    5897           2 :     case 'G':
    5898             :       // Don't write Creator HTML comments.
    5899           2 :       do_write_creator_comment = FALSE;
    5900           2 :       break;
    5901           0 :     case 'h':
    5902             :       /* do not use the Hn headings of html, but manufacture our own */
    5903           0 :       manufacture_headings = TRUE;
    5904           0 :       break;
    5905           0 :     case 'i':
    5906             :       /* handled by pre-html */
    5907           0 :       break;
    5908           3 :     case 'I':
    5909             :       /* handled by pre-html */
    5910           3 :       break;
    5911           2 :     case 'j':
    5912           2 :       multiple_files = TRUE;
    5913           2 :       job_name = optarg;
    5914           2 :       break;
    5915           0 :     case 'k':
    5916           0 :       if (strcasecmp(optarg, "ascii") == 0)
    5917           0 :         charset_encoding = CHARSET_ASCII;
    5918           0 :       else if (strcasecmp(optarg, "mixed") == 0)
    5919           0 :         charset_encoding = CHARSET_MIXED;
    5920           0 :       else if ((strcasecmp(optarg, "utf8") == 0)
    5921           0 :                || (strcasecmp(optarg, "utf-8") == 0))
    5922           0 :         charset_encoding = CHARSET_UTF8;
    5923             :       else {
    5924           0 :         warning("unsupported character encoding '%1'; assuming UTF-8",
    5925           0 :                 optarg);
    5926           0 :         charset_encoding = CHARSET_UTF8;
    5927             :       }
    5928           0 :       break;
    5929           0 :     case 'l':
    5930           0 :       auto_links = FALSE;
    5931           0 :       break;
    5932           1 :     case 'n':
    5933           1 :       simple_anchors = TRUE;
    5934           1 :       break;
    5935           0 :     case 'o':
    5936             :       /* handled by pre-html */
    5937           0 :       break;
    5938           0 :     case 'p':
    5939             :       /* handled by pre-html */
    5940           0 :       break;
    5941           1 :     case 'r':
    5942           1 :       auto_rule = FALSE;
    5943           1 :       break;
    5944           0 :     case 's':
    5945           0 :       base_point_size = atoi(optarg);
    5946           0 :       break;
    5947           0 :     case 'S':
    5948           0 :       split_level = atoi(optarg) + 1;
    5949           0 :       break;
    5950           0 :     case 'v':
    5951           0 :       printf("GNU post-grohtml (groff) version %s\n", Version_string);
    5952           0 :       exit(EXIT_SUCCESS);
    5953             :       break;
    5954           0 :     case 'V':
    5955           0 :       valid_flag = TRUE;
    5956           0 :       break;
    5957           0 :     case 'x':
    5958           0 :       if (strcmp(optarg, "x") == 0) {
    5959           0 :         dialect = xhtml;
    5960           0 :         simple_anchors = TRUE;
    5961           0 :       } else if (strcmp(optarg, "4") == 0)
    5962           0 :         dialect = html4;
    5963             :       else
    5964           0 :         warning("unsupported HTML dialect: '%1'", optarg);
    5965           0 :       break;
    5966           0 :     case 'y':
    5967           0 :       groff_sig = TRUE;
    5968           0 :       break;
    5969           0 :     case CHAR_MAX + 1: // --help
    5970           0 :       usage(stdout);
    5971           0 :       exit(EXIT_SUCCESS);
    5972             :       break;
    5973           0 :     case '?':
    5974           0 :       if (optopt != 0)
    5975           0 :         error("unrecognized command-line option '%1'", char(optopt));
    5976             :       else
    5977           0 :         error("unrecognized command-line option '%1'",
    5978           0 :               argv[(optind - 1)]);
    5979           0 :       usage(stderr);
    5980           0 :       exit(2);
    5981             :       break;
    5982           0 :     case ':':
    5983           0 :       error("command-line option '%1' requires an argument",
    5984           0 :             char(optopt));
    5985           0 :       usage(stderr);
    5986           0 :       exit(2);
    5987             :       break;
    5988           0 :     default:
    5989           0 :       assert(0 == "unhandled getopt_long return value");
    5990             :     }
    5991          17 :   if (optind >= argc) {
    5992          17 :     do_file("-");
    5993             :   } else {
    5994           0 :     for (int i = optind; i < argc; i++)
    5995           0 :       do_file(argv[i]);
    5996             :   }
    5997          17 :   return 0;
    5998             : }
    5999             : 
    6000           0 : static void usage(FILE *stream)
    6001             : {
    6002           0 :   assert(stream != 0 /* nullptr */);
    6003           0 :   fprintf(stream,
    6004             : "usage: %s [-bCGhlnrVy] [-F font-directory] [-j output-stem]"
    6005             : " [-k encoding] [-s base-type-size] [-S heading-level]"
    6006             : " [-x html-dialect] [file ...]\n"
    6007             : "usage: %s {-v | --version}\n"
    6008             : "usage: %s --help\n",
    6009             :           program_name, program_name, program_name);
    6010           0 :   if (stdout == stream)
    6011           0 :     fputs("\n"
    6012             : "Translate the output of troff(1) into (X)HTML.  See the grohtml(1)\n"
    6013             : "manual page.\n", stream);
    6014           0 : }
    6015             : 
    6016             : // Local Variables:
    6017             : // fill-column: 72
    6018             : // mode: C++
    6019             : // End:
    6020             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14