LCOV - code coverage report
Current view: top level - preproc/html - pre-html.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 529 751 70.4 %
Date: 2026-01-16 17:51:41 Functions: 45 48 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2000-2025 Free Software Foundation, Inc.
       2             :  * Written by Gaius Mulley (gaius@glam.ac.uk).
       3             :  *
       4             :  * This file is part of groff, the GNU roff typesetting system.
       5             :  *
       6             :  * groff is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the
       8             :  * Free Software Foundation, either version 3 of the License, or (at
       9             :  * your option) any later version.
      10             :  *
      11             :  * groff is distributed in the hope that it will be useful, but WITHOUT
      12             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             :  * for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #ifdef HAVE_CONFIG_H
      21             : #include <config.h>
      22             : #endif
      23             : 
      24             : #include <assert.h>
      25             : #include <ctype.h> // isspace()
      26             : #include <errno.h>
      27             : #include <stdarg.h> // va_list, va_end(), va_start(), vsnprintf()
      28             : #include <stdio.h> // EOF, FILE, fclose(), feof(), fflush(), fopen(),
      29             :                    // fprintf(), fputc(), fread(), getc(), printf(),
      30             :                    // stderr, stdin, stdout, ungetc()
      31             : #include <stdlib.h> // atexit(), atoi(), exit(), free(), getenv(),
      32             :                     // malloc(), system()
      33             : #include <string.h> // memcpy(), strchr(), strcmp(), strcpy(),
      34             :                     // strerror(), strlen(), strncmp(), strsignal()
      35             : 
      36             : #include <getopt.h> // getopt_long()
      37             : 
      38             : #include <new> // std::bad_alloc
      39             : 
      40             : // needed for close(), creat(), dup(), dup2(), execvp(), fork(),
      41             : // getpid(), mkdir(), open(), pipe(), unlink(), wait(), write()
      42             : #include "posix.h"
      43             : #include "nonposix.h"
      44             : 
      45             : #define PREHTMLC
      46             : 
      47             : #include "lib.h"
      48             : 
      49             : #include "errarg.h"
      50             : #include "error.h"
      51             : #include "stringclass.h"
      52             : #include "defs.h"
      53             : #include "searchpath.h"
      54             : #include "paper.h"
      55             : #include "device.h"
      56             : #include "font.h"
      57             : 
      58             : #ifdef _POSIX_VERSION
      59             : # include <sys/wait.h>
      60             : # define PID_T pid_t
      61             : #else /* not _POSIX_VERSION */
      62             : # define PID_T int
      63             : #endif /* not _POSIX_VERSION */
      64             : 
      65             : /* Establish some definitions to facilitate discrimination between
      66             :    differing runtime environments. */
      67             : 
      68             : #undef MAY_FORK_CHILD_PROCESS
      69             : #undef MAY_SPAWN_ASYNCHRONOUS_CHILD
      70             : 
      71             : #if defined(__MSDOS__) || defined(_WIN32)
      72             : 
      73             : // Most MS-DOS and Win32 environments will be missing the 'fork'
      74             : // capability (some, like Cygwin, have it, but it is better avoided).
      75             : //
      76             : // Bruno Haible adds in 2023:
      77             : //   However, because on Cygwin, neither ... __MSDOS__ [nor] _WIN32 is
      78             : //   defined, the code [here] *will* use fork() on Cygwin.  Which works
      79             : //   fine, but is known to be slow.  However, I don't know whether the
      80             : //   native Windows code (_WIN32) will work on Cygwin: many native
      81             : //   Windows APIs don't work right from within Cygwin, because the
      82             : //   worlds inside and outside a Cygwin process are quite different.
      83             : 
      84             : # define MAY_FORK_CHILD_PROCESS 0
      85             : 
      86             : // On these systems, we use 'spawn...', instead of 'fork' ... 'exec...'.
      87             : # include <process.h>     // for 'spawn...'
      88             : # include <fcntl.h>       // for attributes of pipes
      89             : 
      90             : # if defined(__CYGWIN__) || defined(_UWIN) || defined(_WIN32)
      91             : 
      92             : // These Win32 implementations allow parent and 'spawn...'ed child to
      93             : // multitask asynchronously.
      94             : 
      95             : #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
      96             : 
      97             : # else
      98             : 
      99             : // Others may adopt MS-DOS behaviour where parent must sleep,
     100             : // from 'spawn...' until child terminates.
     101             : 
     102             : #  define MAY_SPAWN_ASYNCHRONOUS_CHILD 0
     103             : 
     104             : # endif /* not defined __CYGWIN__, _UWIN, or _WIN32 */
     105             : 
     106             : # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
     107             : /* When we are building a DEBUGGING version we need to tell pre-grohtml
     108             :    where to put intermediate files (the DEBUGGING version will preserve
     109             :    these on exit).
     110             : 
     111             :    On a Unix host, we might simply use '/tmp', but MS-DOS and Win32 will
     112             :    probably not have this on all disk drives, so default to using
     113             :    'c:/temp' instead.  (Note that user may choose to override this by
     114             :    supplying a definition such as
     115             : 
     116             :      -DDEBUG_FILE_DIR=d:/path/to/debug/files
     117             : 
     118             :    in the CPPFLAGS to 'make'.) */
     119             : 
     120             : #  define DEBUG_FILE_DIR c:/temp
     121             : # endif
     122             : 
     123             : #else /* not __MSDOS__ or _WIN32 */
     124             : 
     125             : // For non-Microsoft environments assume Unix conventions,
     126             : // so 'fork' is required and child processes are asynchronous.
     127             : # define MAY_FORK_CHILD_PROCESS 1
     128             : # define MAY_SPAWN_ASYNCHRONOUS_CHILD 1
     129             : 
     130             : # if defined(DEBUGGING) && !defined(DEBUG_FILE_DIR)
     131             : /* For a DEBUGGING version, on the Unix host, we can also usually rely
     132             :    on being able to use '/tmp' for temporary file storage.  (Note that,
     133             :    as in the __MSDOS__ or _WIN32 case above, the user may override this
     134             :    by defining
     135             : 
     136             :      -DDEBUG_FILE_DIR=/path/to/debug/files
     137             : 
     138             :    in the CPPFLAGS.) */
     139             : 
     140             : #  define DEBUG_FILE_DIR /tmp
     141             : # endif
     142             : 
     143             : #endif /* not __MSDOS__ or _WIN32 */
     144             : 
     145             : // For a DEBUGGING version, we need some additional macros,
     146             : // to direct the captured debugging mode output to appropriately named
     147             : // files in the specified DEBUG_FILE_DIR.
     148             : 
     149             : # define DEBUG_TEXT(text) #text
     150             : # define DEBUG_NAME(text) DEBUG_TEXT(text)
     151             : # define DEBUG_FILE(name) DEBUG_NAME(DEBUG_FILE_DIR) "/" name
     152             : 
     153             : extern "C" const char *Version_string;
     154             : 
     155             : #include "pre-html.h"
     156             : #include "pushback.h"
     157             : #include "html-strings.h"
     158             : 
     159             : #define DEFAULT_LINE_LENGTH 7   // inches wide
     160             : #define DEFAULT_IMAGE_RES 100   // number of pixels per inch resolution
     161             : #define IMAGE_BORDER_PIXELS 0
     162             : #define INLINE_LEADER_CHAR '\\'
     163             : 
     164             : // Don't use colour names here!  Otherwise there is a dependency on
     165             : // a file called 'rgb.txt' which maps names to colours.
     166             : #define TRANSPARENT "-background rgb:f/f/f -transparent rgb:f/f/f"
     167             : #define MIN_ALPHA_BITS 0
     168             : #define MAX_ALPHA_BITS 4
     169             : 
     170             : #define PAGE_TEMPLATE_SHORT "pg"
     171             : #define PAGE_TEMPLATE_LONG "-page-"
     172             : #define PS_TEMPLATE_SHORT "ps"
     173             : #define PS_TEMPLATE_LONG "-ps-"
     174             : #define REGION_TEMPLATE_SHORT "rg"
     175             : #define REGION_TEMPLATE_LONG "-regions-"
     176             : 
     177             : typedef enum {
     178             :   CENTERED, LEFT, RIGHT, INLINE
     179             : } IMAGE_ALIGNMENT;
     180             : 
     181             : typedef enum {xhtml, html4} html_dialect;
     182             : 
     183             : static int postscriptRes = -1;          // PostScript resolution,
     184             :                                         // dots per inch
     185             : static int stdoutfd = 1;                // output file descriptor -
     186             :                                         // normally 1 but might move
     187             :                                         // -1 means closed
     188             : static const char *psFileName = 0 /* nullptr */;        // PostScript
     189             :                                                         // file name
     190             : static const char *psPageName = 0 /* nullptr */;        // name of file
     191             :                                                         // containing
     192             :                                                         // current
     193             :                                                         // PostScript
     194             :                                                         // page
     195             : static const char *regionFileName = 0 /* nullptr */;    // name of file
     196             :                                                         // containing
     197             :                                                         // all image
     198             :                                                         // regions
     199             : static const char *imagePageName = 0 /* nullptr */;     // name of
     200             :                                                         // bitmap image
     201             :                                                         // file
     202             :                                                         // containing
     203             :                                                         // current page
     204             : static const char *image_device = "pnmraw";
     205             : static int image_res = DEFAULT_IMAGE_RES;
     206             : static int vertical_offset = 0;
     207             : static char *image_template = 0 /* nullptr */;  // image file name
     208             :                                                 // template
     209             : static const char *macroset_template= 0 /* nullptr */;  // image file
     210             :                                                         // name template
     211             :                                                         // passed to
     212             :                                                         // troff by -D
     213             : static int troff_arg = 0;               // troff arg index
     214             : static const char *image_dir = 0 /* nullptr */; // user-specified image
     215             :                                                 // directory
     216             : static int textAlphaBits = MAX_ALPHA_BITS;
     217             : static int graphicAlphaBits = MAX_ALPHA_BITS;
     218             : static const char *antiAlias = 0 /* nullptr */; // anti-alias arguments
     219             :                                                 // to be passed to gs
     220             : static bool want_progress_report = false;       // display page numbers
     221             :                                                 // as they are processed
     222             : static int currentPageNo = -1;          // current image page number
     223             : static bool debugging = false;
     224             : static const char *troffFileName = 0 /* nullptr */;     // pre-html
     225             :                                                         // output sent
     226             :                                                         // to troff -Tps
     227             : static const char *htmlFileName = 0 /* nullptr */;      // pre-html
     228             :                                                         // output sent
     229             :                                                         // to troff
     230             :                                                         // -Thtml
     231             : static bool need_eqn = false;           // must we preprocess via eqn?
     232             : 
     233             : static char *linebuf = 0 /* nullptr */; // for scanning devps/DESC
     234             : static int linebufsize = 0;
     235             : static const char *image_gen = 0 /* nullptr */; // the 'gs' program
     236             : 
     237             : static const char devhtml_desc[] = "devhtml/DESC";
     238             : static const char devps_desc[] = "devps/DESC";
     239             : 
     240             : const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
     241             : static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
     242             : static html_dialect dialect = html4;
     243             : 
     244             : 
     245             : /*
     246             :  *  Images are generated via PostScript, gs, and the pnm utilities.
     247             :  */
     248             : #define IMAGE_DEVICE "-Tps"
     249             : 
     250             : 
     251             : /*
     252             :  *  sys_fatal - Write a fatal error message.
     253             :  *              Taken from src/roff/groff/pipeline.c.
     254             :  */
     255             : 
     256           0 : void sys_fatal(const char *s)
     257             : {
     258           0 :   fatal("%1: %2", s, strerror(errno));
     259           0 : }
     260             : 
     261             : /*
     262             :  *  get_line - Copy a line (w/o newline) from a file to the
     263             :  *             global line buffer.
     264             :  *
     265             :  * TODO: Discard; migrate callers to POSIX `getline()`.
     266             :  * https://pubs.opengroup.org/onlinepubs/9799919799/functions/\
     267             :  *   getline.html
     268             :  */
     269             : 
     270         510 : static bool get_line(FILE *f, const char *file_name, int lineno)
     271             : {
     272         510 :   if (0 /* nullptr */ == f)
     273           0 :     return false;
     274         510 :   if (0 /* nullptr */ == linebuf) {
     275          17 :     linebufsize = 128;
     276             :     try {
     277          17 :       linebuf = new char[linebufsize]; // C++03: new int[linebufsize]();
     278          17 :       (void) memset(linebuf, '\0', (linebufsize * sizeof(char)));
     279             :     }
     280           0 :     catch (std::bad_alloc &e) {
     281           0 :       fatal_with_file_and_line(file_name, lineno, "cannot allocate %1"
     282             :                                " bytes to read line; aborting",
     283           0 :                                linebufsize);
     284             :     }
     285             :   }
     286         510 :   int i = 0;
     287             :   // skip leading whitespace
     288             :   for (;;) {
     289         510 :     int c = getc(f);
     290         510 :     if (EOF == c)
     291          34 :       return false;
     292         476 :     if (c != ' ' && c != '\t') {
     293         476 :       ungetc(c, f);
     294         476 :       break;
     295             :     }
     296           0 :   }
     297             :   for (;;) {
     298        7514 :     int c = getc(f);
     299        7514 :     if (EOF == c)
     300           0 :       break;
     301        7514 :     if (i + 1 >= linebufsize) {
     302           0 :       int newbufsize = linebufsize * 2;
     303           0 :       char *old_linebuf = linebuf;
     304             :       try {
     305           0 :         linebuf = new char[newbufsize]; // C++03: new int[newbufsize]();
     306           0 :         (void) memset(linebuf, '\0', (newbufsize * sizeof(char)));
     307             :       }
     308           0 :       catch (std::bad_alloc &e) {
     309           0 :         fatal_with_file_and_line(file_name, lineno, "cannot allocate"
     310             :                                  " more than %1 bytes to read line;"
     311           0 :                                  " aborting", linebufsize);
     312             :       }
     313           0 :       memcpy(linebuf, old_linebuf, linebufsize);
     314           0 :       delete[] old_linebuf;
     315           0 :       linebufsize = newbufsize;
     316             :     }
     317        7514 :     linebuf[i++] = c;
     318        7514 :     if ('\n' == c) {
     319         476 :       i--;
     320         476 :       break;
     321             :     }
     322        7038 :   }
     323         476 :   linebuf[i] = '\0';
     324         476 :   return true;
     325             : }
     326             : 
     327             : /*
     328             :  *  get_resolution - Return the PostScript device resolution.
     329             :  */
     330             : 
     331          17 : static unsigned int get_resolution(void)
     332             : {
     333             :   char *pathp;
     334             :   FILE *f;
     335          17 :   unsigned int res = 0;
     336          17 :   f = font_path.open_file(devps_desc, &pathp);
     337          17 :   if (0 /* nullptr */ == f)
     338           0 :     fatal("cannot open file '%1': %2", devps_desc, strerror(errno));
     339          17 :   int lineno = 0;
     340         255 :   while (get_line(f, pathp, lineno++)) {
     341         238 :     (void) sscanf(linebuf, "res %u", &res);
     342             :     // We must stop reading at a "charset" line; see groff_font(5).
     343         238 :     if (strncmp(linebuf, "charset", sizeof "charset") == 0) {
     344             :       // Don't be fooled by non-groff extensions.
     345           0 :       char trailing_char = linebuf[(sizeof "charset") - 1];
     346           0 :       if (isspace(trailing_char) || '\0' == trailing_char)
     347             :         break;
     348             :     }
     349             :   }
     350          17 :   free(pathp);
     351          17 :   fclose(f);
     352          17 :   return res;
     353             : }
     354             : 
     355             : 
     356             : /*
     357             :  *  get_image_generator - Return the declared program from the HTML
     358             :  *                        device description.
     359             :  */
     360             : 
     361          17 : static char *get_image_generator(void)
     362             : {
     363             :   char *pathp;
     364             :   FILE *f;
     365          17 :   char *generator = 0 /* nullptr */;
     366          17 :   const char keyword[] = "image_generator";
     367          17 :   const size_t keyword_len = strlen(keyword);
     368          17 :   f = font_path.open_file(devhtml_desc, &pathp);
     369          17 :   if (0 /* nullptr */ == f)
     370           0 :     fatal("cannot open file '%1': %2", devhtml_desc, strerror(errno));
     371          17 :   int lineno = 0;
     372         255 :   while (get_line(f, pathp, lineno++)) {
     373         238 :     char *cursor = linebuf;
     374         238 :     size_t limit = strlen(linebuf);
     375         238 :     char *end = linebuf + limit;
     376         238 :     if (0 == (strncmp(linebuf, keyword, keyword_len))) {
     377          17 :       cursor += keyword_len;
     378             :       // At least one space or tab is required.
     379          17 :       if(!(' ' == *cursor) || ('\t' == *cursor))
     380           0 :         continue;
     381          17 :       cursor++;
     382          17 :       while((cursor < end) && ((' ' == *cursor) || ('\t' == *cursor)))
     383           0 :         cursor++;
     384          17 :       if (cursor == end)
     385           0 :         continue;
     386          17 :       generator = cursor;
     387             :     }
     388             :     // We must stop reading at a "charset" line; see groff_font(5).
     389         238 :     if (strncmp(linebuf, "charset", sizeof "charset") == 0) {
     390             :       // Don't be fooled by non-groff extensions.
     391           0 :       char trailing_char = linebuf[(sizeof "charset") - 1];
     392           0 :       if (isspace(trailing_char) || '\0' == trailing_char)
     393             :         break;
     394             :     }
     395             :   }
     396          17 :   free(pathp);
     397          17 :   fclose(f);
     398          17 :   return generator;
     399             : }
     400             : 
     401             : /*
     402             :  *  html_system - A wrapper for system().
     403             :  */
     404             : 
     405         123 : void html_system(const char *s, int redirect_stdout)
     406             : {
     407         123 :   if (debugging) {
     408           0 :     fprintf(stderr, "%s: debug: executing: ", program_name);
     409           0 :     fwrite(s, sizeof(char), strlen(s), stderr);
     410           0 :     fflush(stderr);
     411             :   }
     412             :   {
     413         123 :     int saved_stdout = dup(STDOUT_FILENO);
     414         123 :     int fdnull = open(NULL_DEV, O_WRONLY|O_BINARY, 0666);
     415         123 :     if (redirect_stdout && (saved_stdout > STDOUT_FILENO)
     416          66 :         && (fdnull > STDOUT_FILENO))
     417          66 :       dup2(fdnull, STDOUT_FILENO);
     418         123 :     if (fdnull >= 0)
     419         123 :       close(fdnull);
     420         123 :     int status = system(s);
     421         123 :     if (redirect_stdout)
     422          66 :       dup2(saved_stdout, STDOUT_FILENO);
     423         123 :     if (-1 == status)
     424           0 :       fprintf(stderr, "%s: unable to execute command '%s': %s\n",
     425           0 :               program_name, s, strerror(errno));
     426         123 :     else if (status > 0) {
     427           0 :       if (WIFEXITED(status))
     428           0 :         fprintf(stderr, "%s: command '%s' returned status %d\n",
     429           0 :                 program_name, s, WEXITSTATUS(status));
     430           0 :       else if (WIFSIGNALED(status))
     431           0 :         fprintf(stderr, "%s: command '%s' exited by signal: %s\n",
     432             :                 program_name, s, strsignal(WTERMSIG(status)));
     433           0 :       else if (WIFSTOPPED(status))
     434           0 :         fprintf(stderr, "%s: command '%s' stopped: %s\n",
     435           0 :                 program_name, s, strsignal(WSTOPSIG(status)));
     436             :       else
     437           0 :         fprintf(stderr, "%s: command '%s' exited abnormally\n",
     438             :                 program_name, s);
     439             :     }
     440         123 :     close(saved_stdout);
     441             :   }
     442         123 : }
     443             : 
     444             : /*
     445             :  *  make_string - Create a string via `malloc()`, place the variadic
     446             :  *                arguments as formatted by `fmt` into it, and return
     447             :  *                it.  Adapted from Linux man-pages' printf(3) example.
     448             :  *                We never return a null pointer, instead treating
     449             :  *                failure as invariably fatal.
     450             :  */
     451             : 
     452         159 : const char *make_string(const char *fmt, ...)
     453             : {
     454         159 :   size_t size = 0;
     455         159 :   char *p = 0 /* nullptr */;
     456             :   va_list ap;
     457         159 :   va_start(ap, fmt);
     458         159 :   int n = vsnprintf(p, size, fmt, ap);
     459         159 :   va_end(ap);
     460         159 :   if (n < 0)
     461           0 :     sys_fatal("vsnprintf");
     462         159 :   size = static_cast<size_t>(n) + 1 /* '\0' */;
     463         159 :   p = static_cast<char *>(malloc(size));
     464         159 :   if (0 /* nullptr */ == p)
     465           0 :     sys_fatal("vsnprintf");
     466         159 :   va_start(ap, fmt);
     467         159 :   n = vsnprintf(p, size, fmt, ap);
     468         159 :   va_end(ap);
     469         159 :   if (n < 0)
     470           0 :     sys_fatal("vsnprintf");
     471         159 :   assert(p != 0 /* nullptr */);
     472         159 :   return p;
     473             : }
     474             : 
     475             : /*
     476             :  *  classes and methods for retaining ascii text
     477             :  */
     478             : 
     479             : struct char_block {
     480             :   enum { SIZE = 256 };
     481             :   char buffer[SIZE];
     482             :   int used;
     483             :   char_block *next;
     484             : 
     485             :   char_block();
     486             : };
     487             : 
     488        1408 : char_block::char_block()
     489        1408 : : used(0), next(0)
     490             : {
     491      361856 :   for (int i = 0; i < SIZE; i++)
     492      360448 :     buffer[i] = 0;
     493        1408 : }
     494             : 
     495             : class char_buffer {
     496             : public:
     497             :   char_buffer();
     498             :   ~char_buffer();
     499             :   void read_file(FILE *fp);
     500             :   int do_html(int argc, char *argv[]);
     501             :   int do_image(int argc, char *argv[]);
     502             :   void emit_troff_output(int device_format_selector);
     503             :   void write_upto_newline(char_block **t, int *i, int is_html);
     504             :   bool can_see(char_block **t, int *i, const char *string);
     505             :   void skip_until_newline(char_block **t, int *i);
     506             : private:
     507             :   char_block *head;
     508             :   char_block *tail;
     509             :   int run_output_filter(int device_format_selector, int argc,
     510             :                         char *argv[]);
     511             : };
     512             : 
     513          17 : char_buffer::char_buffer()
     514          17 : : head(0), tail(0)
     515             : {
     516          17 : }
     517             : 
     518        1442 : char_buffer::~char_buffer()
     519             : {
     520        1425 :   while (head != 0 /* nullptr */) {
     521        1408 :     char_block *temp = head;
     522        1408 :     head = head->next;
     523        1408 :     delete temp;
     524             :   }
     525          17 : }
     526             : 
     527             : /*
     528             :  *  read_file - Read file `fp` into char_blocks.
     529             :  */
     530             : 
     531        1425 : void char_buffer::read_file(FILE *fp)
     532             : {
     533             :   int n;
     534        1425 :   while (!feof(fp)) {
     535        1408 :     if (0 /* nullptr */ == tail) {
     536          17 :       tail = new char_block;
     537          17 :       head = tail;
     538             :     }
     539             :     else {
     540        1391 :       if (tail->used == char_block::SIZE) {
     541        1391 :         tail->next = new char_block;
     542        1391 :         tail = tail->next;
     543             :       }
     544             :     }
     545             :     // We now have a tail ready for the next `SIZE` bytes of the file.
     546        1408 :     n = fread(tail->buffer, sizeof(char), char_block::SIZE-tail->used,
     547             :               fp);
     548        1408 :     if ((n < 0) || ((0 == n) && !feof(fp)))
     549           0 :       sys_fatal("fread");
     550        1408 :     tail->used += n * sizeof(char);
     551             :   }
     552          17 : }
     553             : 
     554             : /*
     555             :  *  writeNbytes - Write n bytes to stdout.
     556             :  */
     557             : 
     558       86278 : static void writeNbytes(const char *s, int l)
     559             : {
     560       86278 :   int n = 0;
     561             :   int r;
     562             : 
     563      172376 :   while (n < l) {
     564       86098 :     r = write(stdoutfd, s, l - n);
     565       86098 :     if (r < 0)
     566           0 :       sys_fatal("write");
     567       86098 :     n += r;
     568       86098 :     s += r;
     569             :   }
     570       86278 : }
     571             : 
     572             : /*
     573             :  *  writeString - Write a string to stdout.
     574             :  */
     575             : 
     576          28 : static void writeString(const char *s)
     577             : {
     578          28 :   writeNbytes(s, strlen(s));
     579          28 : }
     580             : 
     581             : /*
     582             :  *  makeFileName - Create the image filename template
     583             :  *                 and the macroset image template.
     584             :  */
     585             : 
     586          17 : static void makeFileName(void)
     587             : {
     588          17 :   if ((image_dir != 0 /* nullptr */)
     589           2 :       && (strchr(image_dir, '%') != 0 /* nullptr */))
     590           0 :     fatal("'%%' is prohibited within the image directory name");
     591          17 :   if ((image_template != 0 /* nullptr */)
     592           2 :       && (strchr(image_template, '%') != 0 /* nullptr */))
     593           0 :     fatal("'%%' is prohibited within the image template");
     594          17 :   if (0 /* nullptr */ == image_dir)
     595          15 :     image_dir = (char *)"";
     596           2 :   else if (strlen(image_dir) > 0
     597           2 :            && image_dir[strlen(image_dir) - 1] != '/')
     598           2 :     image_dir = make_string("%s/", image_dir);
     599          17 :   if (0 /* nullptr */ == image_template)
     600          15 :     macroset_template = make_string("%sgrohtml-%d-", image_dir,
     601          15 :                                      int(getpid()));
     602             :   else
     603           2 :     macroset_template = make_string("%s%s-", image_dir,
     604             :                                      image_template);
     605          17 :   size_t mtlen = strlen(macroset_template);
     606          17 :   image_template = (char *)malloc(strlen("%d") + mtlen + 1);
     607          17 :   if (0 /* nullptr */ == image_template)
     608           0 :     sys_fatal("malloc");
     609          17 :   char *s = strcpy(image_template, macroset_template);
     610          17 :   s += mtlen;
     611             :   // Keep this format string synced with troff:suppress_node::tprint().
     612          17 :   strcpy(s, "%d");
     613          17 : }
     614             : 
     615             : /*
     616             :  *  setupAntiAlias - Set up the antialias string, used when we call gs.
     617             :  */
     618             : 
     619          17 : static void setupAntiAlias(void)
     620             : {
     621          17 :   if (textAlphaBits == 0 && graphicAlphaBits == 0)
     622           0 :     antiAlias = make_string(" ");
     623          17 :   else if (textAlphaBits == 0)
     624           0 :     antiAlias = make_string("-dGraphicsAlphaBits=%d ",
     625             :                             graphicAlphaBits);
     626          17 :   else if (graphicAlphaBits == 0)
     627           0 :     antiAlias = make_string("-dTextAlphaBits=%d ", textAlphaBits);
     628             :   else
     629          17 :     antiAlias = make_string("-dTextAlphaBits=%d"
     630             :                             " -dGraphicsAlphaBits=%d ", textAlphaBits,
     631             :                             graphicAlphaBits);
     632          17 : }
     633             : 
     634             : /*
     635             :  *  checkImageDir - Check whether the image directory is available.
     636             :  */
     637             : 
     638          17 : static void checkImageDir(void)
     639             : {
     640          17 :   if (image_dir != 0 /* nullptr */ && strcmp(image_dir, "") != 0)
     641           2 :     if (!(mkdir(image_dir, 0777) == 0 || errno == EEXIST))
     642           0 :       fatal("cannot create directory '%1': %2", image_dir,
     643           0 :             strerror(errno));
     644          17 : }
     645             : 
     646             : /*
     647             :  *  write_end_image - End the image.  Write out the image extents if we
     648             :  *                    are using -Tps.
     649             :  */
     650             : 
     651           4 : static void write_end_image(int is_html)
     652             : {
     653             :   /*
     654             :    *  if we are producing html then these
     655             :    *    emit image name and enable output
     656             :    *  else
     657             :    *    we are producing images
     658             :    *    in which case these generate image
     659             :    *    boundaries
     660             :    */
     661           4 :   writeString("\\O[4]\\O[2]");
     662           4 :   if (is_html)
     663           2 :     writeString("\\O[1]");
     664             :   else
     665           2 :     writeString("\\O[0]");
     666           4 : }
     667             : 
     668             : /*
     669             :  *  write_start_image - Write troff code which will:
     670             :  *
     671             :  *                      (i)  disable html output for the following image
     672             :  *                      (ii) reset the max/min x/y registers during
     673             :  *                           Postscript Rendering.
     674             :  */
     675             : 
     676           4 : static void write_start_image(IMAGE_ALIGNMENT pos, int is_html)
     677             : {
     678           4 :   writeString("\\O[5");
     679           4 :   switch (pos) {
     680           4 :   case INLINE:
     681           4 :     writeString("i");
     682           4 :     break;
     683           0 :   case LEFT:
     684           0 :     writeString("l");
     685           0 :     break;
     686           0 :   case RIGHT:
     687           0 :     writeString("r");
     688           0 :     break;
     689           0 :   case CENTERED:
     690             :   default:
     691           0 :     writeString("c");
     692           0 :     break;
     693             :   }
     694           4 :   writeString(image_template);
     695           4 :   writeString(".png]");
     696           4 :   if (is_html)
     697           2 :     writeString("\\O[0]\\O[3]");
     698             :   else
     699             :     // reset min/max registers
     700           2 :     writeString("\\O[1]\\O[3]");
     701           4 : }
     702             : 
     703             : /*
     704             :  *  write_upto_newline - Write the contents of the buffer until a
     705             :  *                       newline is seen.  Check for
     706             :  *                       HTML_IMAGE_INLINE_BEGIN and
     707             :  *                       HTML_IMAGE_INLINE_END; process them if they are
     708             :  *                       present.
     709             :  */
     710             : 
     711       56392 : void char_buffer::write_upto_newline(char_block **t, int *i,
     712             :                                      int is_html)
     713             : {
     714       56392 :   int j = *i;
     715             : 
     716       56392 :   if (*t) {
     717     1365492 :     while (j < (*t)->used
     718      708396 :            && (*t)->buffer[j] != '\n'
     719     1389074 :            && (*t)->buffer[j] != INLINE_LEADER_CHAR)
     720      654550 :       j++;
     721       56392 :     if (j < (*t)->used
     722       53846 :         && (*t)->buffer[j] == '\n')
     723       30264 :       j++;
     724       56392 :     writeNbytes((*t)->buffer + (*i), j - (*i));
     725       56392 :     if (j < char_block::SIZE && (*t)->buffer[j] == INLINE_LEADER_CHAR) {
     726       29866 :       if (can_see(t, &j, HTML_IMAGE_INLINE_BEGIN))
     727           4 :         write_start_image(INLINE, is_html);
     728       29862 :       else if (can_see(t, &j, HTML_IMAGE_INLINE_END))
     729           4 :         write_end_image(is_html);
     730             :       else {
     731       29858 :         if (j < (*t)->used) {
     732       29858 :           *i = j;
     733       29858 :           j++;
     734       29858 :           writeNbytes((*t)->buffer + (*i), j - (*i));
     735             :         }
     736             :       }
     737             :     }
     738       56392 :     if (j == (*t)->used) {
     739        2814 :       *i = 0;
     740        2814 :       *t = (*t)->next;
     741        2814 :       if (*t && (*t)->buffer[j - 1] != '\n')
     742        2694 :         write_upto_newline(t, i, is_html);
     743             :     }
     744             :     else
     745             :       // newline was seen
     746       53578 :       *i = j;
     747             :   }
     748       56392 : }
     749             : 
     750             : /*
     751             :  *  can_see - Return true if we can see string in t->buffer[i] onward.
     752             :  */
     753             : 
     754       59728 : bool char_buffer::can_see(char_block **t, int *i, const char *str)
     755             : {
     756       59728 :   size_t j = 0;
     757       59728 :   size_t l = strlen(str);
     758       59728 :   int k = *i;
     759       59728 :   char_block *s = *t;
     760             : 
     761       60024 :   while (s) {
     762      120032 :     while (k < s->used && j < l && s->buffer[k] == str[j]) {
     763       60008 :       j++;
     764       60008 :       k++;
     765             :     }
     766       60024 :     if (j == l) {
     767           8 :       *i = k;
     768           8 :       *t = s;
     769           8 :       return true;
     770             :     }
     771       60016 :     else if (k < s->used && s->buffer[k] != str[j])
     772       59720 :       return false;
     773         296 :     s = s->next;
     774         296 :     k = 0;
     775             :   }
     776           0 :   return false;
     777             : }
     778             : 
     779             : /*
     780             :  *  skip_until_newline - Skip all characters until a newline is seen.
     781             :  *                       The newline is not consumed.
     782             :  */
     783             : 
     784           0 : void char_buffer::skip_until_newline(char_block **t, int *i)
     785             : {
     786           0 :   int j = *i;
     787             : 
     788           0 :   if (*t) {
     789           0 :     while (j < (*t)->used && (*t)->buffer[j] != '\n')
     790           0 :       j++;
     791           0 :     if (j == (*t)->used) {
     792           0 :       *i = 0;
     793           0 :       *t = (*t)->next;
     794           0 :       skip_until_newline(t, i);
     795             :     }
     796             :     else
     797             :       // newline was seen
     798           0 :       *i = j;
     799             :   }
     800           0 : }
     801             : 
     802             : #define DEVICE_FORMAT(filter) (filter == HTML_OUTPUT_FILTER)
     803             : #define HTML_OUTPUT_FILTER     0
     804             : #define IMAGE_OUTPUT_FILTER    1
     805             : #define OUTPUT_STREAM(name)   creat((name), S_IWUSR | S_IRUSR)
     806             : #define PS_OUTPUT_STREAM      OUTPUT_STREAM(psFileName)
     807             : #define REGION_OUTPUT_STREAM  OUTPUT_STREAM(regionFileName)
     808             : 
     809             : /*
     810             :  *  emit_troff_output - Write formatted buffer content to the troff
     811             :  *                      post-processor data pipeline.
     812             :  */
     813             : 
     814          34 : void char_buffer::emit_troff_output(int device_format_selector)
     815             : {
     816             :   // Handle output for BOTH html and image device formats
     817             :   // if 'device_format_selector' is passed as
     818             :   //
     819             :   //   HTML_FORMAT(HTML_OUTPUT_FILTER)
     820             :   //     Buffer data is written to the output stream
     821             :   //     with template image names translated to actual image names.
     822             :   //
     823             :   //   HTML_FORMAT(IMAGE_OUTPUT_FILTER)
     824             :   //     Buffer data is written to the output stream
     825             :   //     with no translation, for image file creation in the
     826             :   //     post-processor.
     827             : 
     828          34 :   int idx = 0;
     829          34 :   char_block *element = head;
     830             : 
     831       53732 :   while (element != 0 /* nullptr */)
     832       53698 :     write_upto_newline(&element, &idx, device_format_selector);
     833             : 
     834             : #if 0
     835             :   if (close(stdoutfd) < 0)
     836             :     sys_fatal ("close");
     837             : 
     838             :   // now we grab fd=1 so that the next pipe cannot use fd=1
     839             :   if (stdoutfd == 1) {
     840             :     if (dup(2) != stdoutfd)
     841             :       sys_fatal ("dup failed to use fd=1");
     842             :   }
     843             : #endif /* 0 */
     844          34 : }
     845             : 
     846             : /*
     847             :  *  The image class remembers the position of all images in the
     848             :  *  PostScript file and assigns names for each image.
     849             :  */
     850             : 
     851             : struct imageItem {
     852             :   imageItem *next;
     853             :   int X1;
     854             :   int Y1;
     855             :   int X2;
     856             :   int Y2;
     857             :   char *imageName;
     858             :   int resolution;
     859             :   int maxx;
     860             :   int pageNo;
     861             : 
     862             :   imageItem(int x1, int y1, int x2, int y2,
     863             :             int page, int res, int max_width, char *name);
     864             :   ~imageItem();
     865             : };
     866             : 
     867             : /*
     868             :  *  imageItem - Constructor.
     869             :  */
     870             : 
     871          59 : imageItem::imageItem(int x1, int y1, int x2, int y2,
     872          59 :                      int page, int res, int max_width, char *name)
     873             : {
     874          59 :   X1 = x1;
     875          59 :   Y1 = y1;
     876          59 :   X2 = x2;
     877          59 :   Y2 = y2;
     878          59 :   pageNo = page;
     879          59 :   resolution = res;
     880          59 :   maxx = max_width;
     881          59 :   imageName = name;
     882          59 :   next = 0 /* nullptr */;
     883          59 : }
     884             : 
     885             : /*
     886             :  *  imageItem - Destructor.
     887             :  */
     888             : 
     889         118 : imageItem::~imageItem()
     890             : {
     891          59 :   if (imageName)
     892          59 :     free(imageName);
     893          59 : }
     894             : 
     895             : /*
     896             :  *  imageList - A class containing a list of imageItems.
     897             :  */
     898             : 
     899             : class imageList {
     900             : private:
     901             :   imageItem *head;
     902             :   imageItem *tail;
     903             :   int count;
     904             : public:
     905             :   imageList();
     906             :   ~imageList();
     907             :   void add(int x1, int y1, int x2, int y2,
     908             :            int page, int res, int maxx, char *name);
     909             :   void createImages(void);
     910             :   int createPage(int pageno);
     911             :   void createImage(imageItem *i);
     912             :   int getMaxX(int pageno);
     913             : };
     914             : 
     915             : /*
     916             :  *  imageList - Constructor.
     917             :  */
     918             : 
     919          17 : imageList::imageList()
     920          17 : : head(0), tail(0), count(0)
     921             : {
     922          17 : }
     923             : 
     924             : /*
     925             :  *  imageList - Destructor.
     926             :  */
     927             : 
     928          93 : imageList::~imageList()
     929             : {
     930          76 :   while (head != 0 /* nullptr */) {
     931          59 :     imageItem *i = head;
     932          59 :     head = head->next;
     933          59 :     delete i;
     934             :   }
     935          17 : }
     936             : 
     937             : /*
     938             :  *  createPage - Create image of page `pageno` from PostScript file.
     939             :  */
     940             : 
     941          57 : int imageList::createPage(int pageno)
     942             : {
     943          57 :   if (currentPageNo == pageno)
     944          24 :     return 0;
     945             : 
     946          33 :   if (currentPageNo >= 1) {
     947             :     /*
     948             :      *  We need to unlink the files which change each time a new page is
     949             :      *  processed.  The final unlink is done by xtmpfile when
     950             :      *  pre-grohtml exits.
     951             :      */
     952          29 :     unlink(imagePageName);
     953          29 :     unlink(psPageName);
     954             :   }
     955             : 
     956          33 :   if (want_progress_report) {
     957           0 :     fprintf(stderr, "[%d] ", pageno);
     958           0 :     fflush(stderr);
     959             :   }
     960             : 
     961          33 :   if (debugging)
     962           0 :     fprintf(stderr, "%s: debug: creating page %d\n", program_name,
     963             :             pageno);
     964             : 
     965          33 :   const char *s = make_string("ps2ps -sPageList=%d %s %s",
     966             :                               pageno, psFileName, psPageName);
     967          33 :   html_system(s, 1);
     968          33 :   assert(strlen(image_gen) > 0);
     969          99 :   s = make_string("echo showpage | "
     970             :                   "%s%s -q -dBATCH -dSAFER "
     971             :                   "-dDEVICEHEIGHTPOINTS=792 "
     972             :                   "-dDEVICEWIDTHPOINTS=%d -dFIXEDMEDIA=true "
     973             :                   "-sDEVICE=%s -r%d %s "
     974             :                   "-sOutputFile=%s %s -",
     975             :                   image_gen,
     976             :                   EXE_EXT,
     977          33 :                   (getMaxX(pageno) * image_res) / postscriptRes,
     978             :                   image_device,
     979             :                   image_res,
     980             :                   antiAlias,
     981             :                   imagePageName,
     982             :                   psPageName);
     983          33 :   html_system(s, 1);
     984          33 :   free(const_cast<char *>(s));
     985          33 :   currentPageNo = pageno;
     986          33 :   return 0;
     987             : }
     988             : 
     989             : /*
     990             :  *  min - Return the minimum of two numbers.
     991             :  */
     992             : 
     993         114 : int min(int x, int y)
     994             : {
     995         114 :   if (x < y)
     996         114 :     return x;
     997             :   else
     998           0 :     return y;
     999             : }
    1000             : 
    1001             : /*
    1002             :  *  max - Return the maximum of two numbers.
    1003             :  */
    1004             : 
    1005         286 : int max(int x, int y)
    1006             : {
    1007         286 :   if (x > y)
    1008         123 :     return x;
    1009             :   else
    1010         163 :     return y;
    1011             : }
    1012             : 
    1013             : /*
    1014             :  *  getMaxX - Return the largest right-hand position for any image
    1015             :  *            on `pageno`.
    1016             :  */
    1017             : 
    1018          33 : int imageList::getMaxX(int pageno)
    1019             : {
    1020          33 :   imageItem *h = head;
    1021          33 :   int x = postscriptRes * DEFAULT_LINE_LENGTH;
    1022             : 
    1023        1577 :   while (h != 0 /* nullptr */) {
    1024        1544 :     if (h->pageNo == pageno)
    1025          58 :       x = max(h->X2, x);
    1026        1544 :     h = h->next;
    1027             :   }
    1028          33 :   return x;
    1029             : }
    1030             : 
    1031             : /*
    1032             :  *  createImage - Generate a minimal PNG file from the set of page
    1033             :  *                images.
    1034             :  */
    1035             : 
    1036          59 : void imageList::createImage(imageItem *i)
    1037             : {
    1038          59 :   if (i->X1 != -1) {
    1039          57 :     int x1 = max(min(i->X1, i->X2) * image_res / postscriptRes
    1040             :                    - IMAGE_BORDER_PIXELS,
    1041             :                  0);
    1042         114 :     int y1 = max(image_res * vertical_offset / 72
    1043          57 :                    + min(i->Y1, i->Y2) * image_res / postscriptRes
    1044             :                    - IMAGE_BORDER_PIXELS,
    1045             :                  0);
    1046          57 :     int x2 = max(i->X1, i->X2) * image_res / postscriptRes
    1047          57 :              + IMAGE_BORDER_PIXELS;
    1048          57 :     int y2 = image_res * vertical_offset / 72
    1049          57 :              + max(i->Y1, i->Y2) * image_res / postscriptRes
    1050          57 :              + 1 + IMAGE_BORDER_PIXELS;
    1051          57 :     if (createPage(i->pageNo) == 0) {
    1052         114 :       const char *s = make_string("pamcut%s %d %d %d %d < %s "
    1053             :                                   "| pnmcrop%s " PNMTOOLS_QUIET
    1054             :                                   "| pnmtopng%s " PNMTOOLS_QUIET " %s"
    1055             :                                   "> %s",
    1056             :                                   EXE_EXT,
    1057          57 :                                   x1, y1, x2 - x1 + 1, y2 - y1 + 1,
    1058             :                                   imagePageName,
    1059             :                                   EXE_EXT,
    1060             :                                   EXE_EXT,
    1061             :                                   TRANSPARENT,
    1062             :                                   i->imageName);
    1063          57 :       html_system(s, 0);
    1064          57 :       free(const_cast<char *>(s));
    1065             :     }
    1066             :     else {
    1067           0 :       fprintf(stderr, "%s: failed to generate image of page %d\n",
    1068             :               program_name, i->pageNo);
    1069           0 :       fflush(stderr);
    1070             :     }
    1071             : #if defined(DEBUGGING)
    1072             :   }
    1073             :   else {
    1074             :     if (debugging) {
    1075             :       fprintf(stderr, "%s: debug: ignoring image as x1 coord is -1\n",
    1076             :               program_name);
    1077             :       fflush(stderr);
    1078             :     }
    1079             : #endif
    1080             :   }
    1081          59 : }
    1082             : 
    1083             : /*
    1084             :  *  add - Add an image description to the imageList.
    1085             :  */
    1086             : 
    1087          59 : void imageList::add(int x1, int y1, int x2, int y2,
    1088             :                     int page, int res, int maxx, char *name)
    1089             : {
    1090          59 :   imageItem *i = new imageItem(x1, y1, x2, y2, page, res, maxx, name);
    1091             : 
    1092          59 :   if (0 /* nullptr */ == head) {
    1093           5 :     head = i;
    1094           5 :     tail = i;
    1095             :   }
    1096             :   else {
    1097          54 :     tail->next = i;
    1098          54 :     tail = i;
    1099             :   }
    1100          59 : }
    1101             : 
    1102             : /*
    1103             :  *  createImages - For each image descriptor on the imageList,
    1104             :  *                 create the actual image.
    1105             :  */
    1106             : 
    1107          17 : void imageList::createImages(void)
    1108             : {
    1109          17 :   imageItem *h = head;
    1110             : 
    1111          76 :   while (h != 0 /* nullptr */) {
    1112          59 :     createImage(h);
    1113          59 :     h = h->next;
    1114             :   }
    1115          17 : }
    1116             : 
    1117             : static imageList listOfImages;  // list of images defined by region file
    1118             : 
    1119             : /*
    1120             :  *  generateImages - Parse the region file and generate images from the
    1121             :  *                   PostScript file.  The region file contains the
    1122             :  *                   x1,y1--x2,y2 extents of each image.
    1123             :  */
    1124             : 
    1125          17 : static void generateImages(const char *region_file_name)
    1126             : {
    1127          17 :   pushBackBuffer *f=new pushBackBuffer(region_file_name);
    1128             : 
    1129         128 :   while (f->putPB(f->getPB()) != eof) {
    1130         111 :     if (f->isString("grohtml-info:page")) {
    1131          59 :       int page = f->readInt();
    1132          59 :       int x1 = f->readInt();
    1133          59 :       int y1 = f->readInt();
    1134          59 :       int x2 = f->readInt();
    1135          59 :       int y2 = f->readInt();
    1136          59 :       int maxx = f->readInt();
    1137          59 :       char *name = f->readString();
    1138          59 :       int res = postscriptRes;
    1139          59 :       listOfImages.add(x1, y1, x2, y2, page, res, maxx, name);
    1140        3382 :       while (f->putPB(f->getPB()) != '\n'
    1141        3382 :              && f->putPB(f->getPB()) != eof)
    1142        3323 :         (void)f->getPB();
    1143          59 :       if (f->putPB(f->getPB()) == '\n')
    1144          59 :         (void)f->getPB();
    1145             :     }
    1146             :     else {
    1147             :       /* Write any error messages out to the user. */
    1148          52 :       fputc(f->getPB(), stderr);
    1149             :     }
    1150             :   }
    1151          17 :   fflush(stderr);
    1152             : 
    1153          17 :   listOfImages.createImages();
    1154          17 :   if (want_progress_report) {
    1155           0 :     fprintf(stderr, "done\n");
    1156           0 :     fflush(stderr);
    1157             :   }
    1158          17 :   delete f;
    1159          17 : }
    1160             : 
    1161             : /*
    1162             :  *  set_redirection - Redirect file descriptor `was` to file descriptor
    1163             :  *                    `willbe`.
    1164             :  */
    1165             : 
    1166         136 : static void set_redirection(int was, int willbe)
    1167             : {
    1168             :   // Nothing to do if 'was' and 'willbe' already have same handle.
    1169         136 :   if (was != willbe) {
    1170             :     // Otherwise attempt the specified redirection.
    1171         136 :     if (dup2(willbe, was) < 0) {
    1172             :       // Redirection failed, so issue diagnostic and bail out.
    1173           0 :       fprintf(stderr, "%s: unable to replace fd=%d with %d",
    1174             :               program_name, was, willbe);
    1175           0 :       if (willbe == STDOUT_FILENO)
    1176           0 :         fprintf(stderr,
    1177             :                 "; likely that stdout should be opened before %d", was);
    1178           0 :       fputc('\n', stderr);
    1179           0 :       sys_fatal("dup2");
    1180             :     }
    1181             : 
    1182             :     // When redirection has been successfully completed assume redundant
    1183             :     // handle 'willbe' is no longer required, so close it.
    1184         136 :     if (close(willbe) < 0)
    1185             :       // Issue diagnostic if 'close' fails.
    1186           0 :       sys_fatal("close");
    1187             :   }
    1188         136 : }
    1189             : 
    1190             : /*
    1191             :  *  save_and_redirect - Duplicate file descriptor for `was` on file
    1192             :  *                      descriptor `willbe`.
    1193             :  */
    1194             : 
    1195          34 : static int save_and_redirect(int was, int willbe)
    1196             : {
    1197          34 :   if (was == willbe)
    1198             :     // No redirection specified; silently bail out.
    1199           0 :     return (was);
    1200             : 
    1201             :   // Proceeding with redirection so first save and verify our duplicate
    1202             :   // handle for 'was'.
    1203          34 :   int saved = dup(was);
    1204          34 :   if (saved < 0) {
    1205           0 :     fprintf(stderr, "%s: unable to get duplicate file descriptor for"
    1206             :             " %d\n", program_name, was);
    1207           0 :     sys_fatal("dup");
    1208             :   }
    1209             : 
    1210             :   // Duplicate handle safely established so complete redirection.
    1211          34 :   set_redirection(was, willbe);
    1212             : 
    1213             :   // Finally return the saved duplicate descriptor for the original
    1214             :   // 'was' descriptor.
    1215          34 :   return saved;
    1216             : }
    1217             : 
    1218             : /*
    1219             :  *  alterDeviceTo - If toImage is set
    1220             :  *                     the argument list is altered to include
    1221             :  *                     IMAGE_DEVICE; we invoke groff rather than troff.
    1222             :  *                  Else
    1223             :  *                     set -Thtml and groff.
    1224             :  */
    1225             : 
    1226          34 : static void alterDeviceTo(int argc, char *argv[], int toImage)
    1227             : {
    1228          34 :   int i = 0;
    1229             : 
    1230          34 :   if (toImage) {
    1231         119 :     while (i < argc) {
    1232         102 :       if ((strcmp(argv[i], "-Thtml") == 0) ||
    1233          85 :           (strcmp(argv[i], "-Txhtml") == 0))
    1234          17 :         argv[i] = (char *)IMAGE_DEVICE;
    1235         102 :       i++;
    1236             :     }
    1237          17 :     argv[troff_arg] = (char *)"groff";        /* rather than troff */
    1238             :   }
    1239             :   else {
    1240         119 :     while (i < argc) {
    1241         102 :       if (strcmp(argv[i], IMAGE_DEVICE) == 0) {
    1242          17 :         if (dialect == xhtml)
    1243           0 :           argv[i] = (char *)"-Txhtml";
    1244             :         else
    1245          17 :           argv[i] = (char *)"-Thtml";
    1246             :       }
    1247         102 :       i++;
    1248             :     }
    1249          17 :     argv[troff_arg] = (char *)"groff";        /* use groff -Z */
    1250             :   }
    1251          34 : }
    1252             : 
    1253             : /*
    1254             :  *  addArg - Append newarg onto the command list for groff.
    1255             :  */
    1256             : 
    1257          17 : char **addArg(int argc, char *argv[], char *newarg)
    1258             : {
    1259          17 :   char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
    1260          17 :   int i = 0;
    1261             : 
    1262          17 :   if (0 /* nullptr */ == new_argv)
    1263           0 :     sys_fatal("malloc");
    1264             : 
    1265          17 :   if (argc > 0) {
    1266          17 :     new_argv[i] = argv[i];
    1267          17 :     i++;
    1268             :   }
    1269          17 :   new_argv[i] = newarg;
    1270          56 :   while (i < argc) {
    1271          39 :     new_argv[i + 1] = argv[i];
    1272          39 :     i++;
    1273             :   }
    1274          17 :   argc++;
    1275          17 :   new_argv[argc] = 0 /* nullptr */;
    1276          17 :   return new_argv;
    1277             : }
    1278             : 
    1279             : /*
    1280             :  *  addRegDef - Append a defined register or string onto the command
    1281             :  *              list for troff.
    1282             :  */
    1283             : 
    1284          68 : char **addRegDef(int argc, char *argv[], const char *numReg)
    1285             : {
    1286          68 :   char **new_argv = (char **)malloc((argc + 2) * sizeof(char *));
    1287          68 :   int i = 0;
    1288             : 
    1289          68 :   if (0 /* nullptr */ == new_argv)
    1290           0 :     sys_fatal("malloc");
    1291             : 
    1292         360 :   while (i < argc) {
    1293         292 :     new_argv[i] = argv[i];
    1294         292 :     i++;
    1295             :   }
    1296          68 :   new_argv[argc] = strsave(numReg);
    1297          68 :   argc++;
    1298          68 :   new_argv[argc] = 0 /* nullptr */;
    1299          68 :   return new_argv;
    1300             : }
    1301             : 
    1302             : #if 0
    1303             : /*
    1304             :  *  dump_args - Display the argument list.
    1305             :  */
    1306             : 
    1307             : void dump_args(int argc, char *argv[])
    1308             : {
    1309             :   fprintf(stderr, "  %d arguments:", argc);
    1310             :   for (int i = 0; i < argc; i++)
    1311             :     fprintf(stderr, " %s", argv[i]);
    1312             :   fprintf(stderr, "\n");
    1313             : }
    1314             : #endif
    1315             : 
    1316             : /*
    1317             :  *  print_args - Print arguments as if issued on the command line.
    1318             :  */
    1319             : 
    1320          34 : void print_args(int argc, char *argv[])
    1321             : {
    1322          34 :   if (debugging) {
    1323           0 :     fprintf(stderr, "%s: debug: executing: ", program_name);
    1324           0 :     for (int i = 0; i < argc; i++)
    1325           0 :       fprintf(stderr, "%s ", argv[i]);
    1326           0 :     fputc('\n', stderr);
    1327             :   }
    1328          34 : }
    1329             : 
    1330          34 : int char_buffer::run_output_filter(int filter, int argc, char **argv)
    1331             : {
    1332             :   int pipedes[2];
    1333             :   PID_T child_pid;
    1334             :   int wstatus;
    1335             : 
    1336          34 :   print_args(argc, argv);
    1337          34 :   if (pipe(pipedes) < 0)
    1338           0 :     sys_fatal("pipe");
    1339             : 
    1340             : #if MAY_FORK_CHILD_PROCESS
    1341             :   // This is the Unix process model.  To invoke our post-processor,
    1342             :   // we must 'fork' the current process.
    1343             : 
    1344          34 :   if ((child_pid = fork()) < 0)
    1345           0 :     sys_fatal("fork");
    1346             : 
    1347          68 :   else if (child_pid == 0) {
    1348             :     // This is the child process.  We redirect its input file descriptor
    1349             :     // to read data emerging from our pipe.  There is no point in
    1350             :     // saving, since we won't be able to restore later!
    1351             : 
    1352          34 :     set_redirection(STDIN_FILENO, pipedes[0]);
    1353             : 
    1354             :     // The parent process will be writing this data; release the child's
    1355             :     // writeable handle on the pipe since we have no use for it.
    1356             : 
    1357          34 :     if (close(pipedes[1]) < 0)
    1358           0 :       sys_fatal("close");
    1359             : 
    1360             :     // The IMAGE_OUTPUT_FILTER needs special output redirection...
    1361             : 
    1362          34 :     if (filter == IMAGE_OUTPUT_FILTER) {
    1363             :       // ...with BOTH 'stdout' AND 'stderr' diverted to files, the
    1364             :       // latter so that `generateImages()` can scrape "grohtml-info"
    1365             :       // from it.
    1366             : 
    1367          17 :       set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
    1368          17 :       set_redirection(STDERR_FILENO, REGION_OUTPUT_STREAM);
    1369             :     }
    1370             : 
    1371             :     // Now we are ready to launch the output filter.
    1372             : 
    1373          34 :     execvp(argv[0], argv); // does not return unless it fails
    1374          34 :     fatal("cannot execute '%1': %2", argv[0], strerror(errno));
    1375             :   }
    1376             : 
    1377             :   else {
    1378             :     // This is the parent process.  We write data to the filter pipeline
    1379             :     // where the child will read it.  We have no need to read from the
    1380             :     // input side ourselves, so close it.
    1381             : 
    1382          34 :     if (close(pipedes[0]) < 0)
    1383           0 :       sys_fatal("close");
    1384             : 
    1385             :     // Now redirect the standard output file descriptor to the inlet end
    1386             :     // of the pipe, and push the formatted data to the filter.
    1387             : 
    1388          34 :     pipedes[1] = save_and_redirect(STDOUT_FILENO, pipedes[1]);
    1389          34 :     emit_troff_output(DEVICE_FORMAT(filter));
    1390             : 
    1391             :     // After emitting all the data we close our connection to the inlet
    1392             :     // end of the pipe so the child process will detect end of data.
    1393             : 
    1394          34 :     set_redirection(STDOUT_FILENO, pipedes[1]);
    1395             : 
    1396             :     // Finally, we must wait for the child process to complete.
    1397             : 
    1398          34 :     if (WAIT(&wstatus, child_pid, _WAIT_CHILD) != child_pid)
    1399           0 :       sys_fatal("wait");
    1400             :   }
    1401             : 
    1402             : #elif MAY_SPAWN_ASYNCHRONOUS_CHILD
    1403             : 
    1404             :   // We do not have `fork` (or we prefer not to use it), but
    1405             :   // asynchronous processes are allowed, passing data through pipes.
    1406             :   // This should be okay for most Win32 systems and is preferred to
    1407             :   // `fork` for starting child processes under Cygwin.
    1408             : 
    1409             :   // Before we start the post-processor we bind its inherited standard
    1410             :   // input file descriptor to the readable end of our pipe, saving our
    1411             :   // own standard input file descriptor in `pipedes[0]`.
    1412             : 
    1413             :   pipedes[0] = save_and_redirect(STDIN_FILENO, pipedes[0]);
    1414             : 
    1415             :   // For the Win32 model,
    1416             :   // we need special provision for saving BOTH 'stdout' and 'stderr'.
    1417             : 
    1418             :   int saved_stdout = dup(STDOUT_FILENO);
    1419             :   int saved_stderr = STDERR_FILENO;
    1420             : 
    1421             :   // The IMAGE_OUTPUT_FILTER needs special output redirection...
    1422             : 
    1423             :   if (filter == IMAGE_OUTPUT_FILTER) {
    1424             :     // with BOTH 'stdout' AND 'stderr' diverted to files while saving a
    1425             :     // duplicate handle for 'stderr'.
    1426             : 
    1427             :     set_redirection(STDOUT_FILENO, PS_OUTPUT_STREAM);
    1428             :     saved_stderr = save_and_redirect(STDERR_FILENO,
    1429             :                                      REGION_OUTPUT_STREAM);
    1430             :   }
    1431             : 
    1432             :   // Use an asynchronous spawn request to start the post-processor.
    1433             : 
    1434             :   if ((child_pid = spawnvp(_P_NOWAIT, argv[0], argv)) < 0) {
    1435             :     fatal("cannot spawn %1: %2", argv[0], strerror(errno));
    1436             :   }
    1437             : 
    1438             :   // Once the post-processor has been started we revert our 'stdin'
    1439             :   // to its original saved source, which also closes the readable handle
    1440             :   // for the pipe.
    1441             : 
    1442             :   set_redirection(STDIN_FILENO, pipedes[0]);
    1443             : 
    1444             :   // if we redirected 'stderr', for use by the image post-processor,
    1445             :   // then we also need to reinstate its original assignment.
    1446             : 
    1447             :   if (filter == IMAGE_OUTPUT_FILTER)
    1448             :     set_redirection(STDERR_FILENO, saved_stderr);
    1449             : 
    1450             :   // Now we redirect the standard output to the inlet end of the pipe,
    1451             :   // and push out the appropriately formatted data to the filter.
    1452             : 
    1453             :   set_redirection(STDOUT_FILENO, pipedes[1]);
    1454             :   emit_troff_output(DEVICE_FORMAT(filter));
    1455             : 
    1456             :   // After emitting all the data we close our connection to the inlet
    1457             :   // end of the pipe so the child process will detect end of data.
    1458             : 
    1459             :   set_redirection(STDOUT_FILENO, saved_stdout);
    1460             : 
    1461             :   // And finally, we must wait for the child process to complete.
    1462             : 
    1463             :   if (WAIT(&wstatus, child_pid, _WAIT_CHILD) != child_pid)
    1464             :     sys_fatal("wait");
    1465             : 
    1466             : #else /* can't do asynchronous pipes! */
    1467             : 
    1468             :   // TODO: code to support an MS-DOS style process model should go here
    1469             :   fatal("output filtering not supported on this platform");
    1470             : 
    1471             : #endif /* MAY_FORK_CHILD_PROCESS or MAY_SPAWN_ASYNCHRONOUS_CHILD */
    1472             : 
    1473          34 :   return wstatus;
    1474             : }
    1475             : 
    1476             : /*
    1477             :  *  do_html - Set the troff number htmlflip and
    1478             :  *            write out the buffer to troff -Thtml.
    1479             :  */
    1480             : 
    1481          17 : int char_buffer::do_html(int argc, char *argv[])
    1482             : {
    1483          34 :   string s;
    1484             : 
    1485          17 :   alterDeviceTo(argc, argv, 0);
    1486          17 :   argv += troff_arg;            // skip all arguments up to groff
    1487          17 :   argc -= troff_arg;
    1488          17 :   argv = addArg(argc, argv, (char *)"-Z");
    1489          17 :   argc++;
    1490             : 
    1491          17 :   s = (char *)"-dwww-image-template=";
    1492          17 :   s += macroset_template;       // Do not combine these statements,
    1493             :                                 // otherwise they will not work.
    1494          17 :   s += '\0';                    // The trailing '\0' is ignored.
    1495          17 :   argv = addRegDef(argc, argv, s.contents());
    1496          17 :   argc++;
    1497             : 
    1498          17 :   if (dialect == xhtml) {
    1499           0 :     argv = addRegDef(argc, argv, "-rxhtml=1");
    1500           0 :     argc++;
    1501           0 :     if (need_eqn) {
    1502           0 :       argv = addRegDef(argc, argv, "-e");
    1503           0 :       argc++;
    1504             :     }
    1505             :   }
    1506             : 
    1507             : # define HTML_DEBUG_STREAM  OUTPUT_STREAM(htmlFileName)
    1508             :   // slight security risk: only enabled if defined(DEBUGGING)
    1509          17 :   if (debugging) {
    1510           0 :     int saved_stdout = save_and_redirect(STDOUT_FILENO,
    1511             :                                          HTML_DEBUG_STREAM);
    1512           0 :     emit_troff_output(DEVICE_FORMAT(HTML_OUTPUT_FILTER));
    1513           0 :     set_redirection(STDOUT_FILENO, saved_stdout);
    1514             :   }
    1515             : 
    1516          34 :   return run_output_filter(HTML_OUTPUT_FILTER, argc, argv);
    1517             : }
    1518             : 
    1519             : /*
    1520             :  *  do_image - Write out the buffer to troff -Tps.
    1521             :  */
    1522             : 
    1523          17 : int char_buffer::do_image(int argc, char *argv[])
    1524             : {
    1525          34 :   string s;
    1526             : 
    1527          17 :   alterDeviceTo(argc, argv, 1);
    1528          17 :   argv += troff_arg;            // skip all arguments up to troff/groff
    1529          17 :   argc -= troff_arg;
    1530          17 :   argv = addRegDef(argc, argv, "-rps4html=1");
    1531          17 :   argc++;
    1532             : 
    1533          17 :   s = "-dwww-image-template=";
    1534          17 :   s += macroset_template;
    1535          17 :   s += '\0';
    1536          17 :   argv = addRegDef(argc, argv, s.contents());
    1537          17 :   argc++;
    1538             : 
    1539             :   // Override local settings and produce a letter-size PostScript page
    1540             :   // file.
    1541          17 :   argv = addRegDef(argc, argv, "-P-pletter");
    1542          17 :   argc++;
    1543             : 
    1544          17 :   if (dialect == xhtml) {
    1545           0 :     if (need_eqn) {
    1546           0 :       argv = addRegDef(argc, argv, "-rxhtml=1");
    1547           0 :       argc++;
    1548             :     }
    1549           0 :     argv = addRegDef(argc, argv, "-e");
    1550           0 :     argc++;
    1551             :   }
    1552             : 
    1553             : # define IMAGE_DEBUG_STREAM  OUTPUT_STREAM(troffFileName)
    1554             :   // slight security risk: only enabled if defined(DEBUGGING)
    1555          17 :   if (debugging) {
    1556           0 :     int saved_stdout = save_and_redirect(STDOUT_FILENO,
    1557             :                                          IMAGE_DEBUG_STREAM);
    1558           0 :     emit_troff_output(DEVICE_FORMAT(IMAGE_OUTPUT_FILTER));
    1559           0 :     set_redirection(STDOUT_FILENO, saved_stdout);
    1560             :   }
    1561             : 
    1562          34 :   return run_output_filter(IMAGE_OUTPUT_FILTER, argc, argv);
    1563             : }
    1564             : 
    1565             : static char_buffer inputFile;
    1566             : 
    1567             : /*
    1568             :  *  usage - Emit usage message.
    1569             :  */
    1570             : 
    1571           0 : static void usage(FILE *stream)
    1572             : {
    1573           0 :   fprintf(stream,
    1574             : "usage: %s [-epV] [-a anti-aliasing-text-bits] [-D image-directory]"
    1575             : " [-F font-directory] [-g anti-aliasing-graphics-bits] [-i resolution]"
    1576             : " [-I image-stem] [-o image-vertical-offset] [-x html-dialect]"
    1577             : " troff-command troff-argument ...\n"
    1578             : "usage: %s {-v | --version}\n"
    1579             : "usage: %s --help\n",
    1580             :          program_name, program_name, program_name);
    1581           0 :   if (stdout == stream) {
    1582           0 :     fputs("\n"
    1583             : "Prepare a troff(1) document for HTML formatting.\n"
    1584             : "\n"
    1585             : "This program is not intended to be executed standalone; it is\n"
    1586             : "normally part of a groff pipeline.  If your need to run it manually\n"
    1587             : "(e.g., for debugging purposes), give the 'groff' program the\n"
    1588             : "command-line option '-V' to inspect the arguments with which\n",
    1589             :           stream);
    1590           0 :     fprintf(stream,
    1591             : "'%s' is called.  See the grohtml(1) manual page.\n",
    1592             :           program_name);
    1593             :   }
    1594           0 : }
    1595             : 
    1596             : /*
    1597             :  *  scanArguments - Scan for all arguments including -P-i, -P-o, -P-D,
    1598             :  *                  and -P-I.  Return the argument index of the first
    1599             :  *                  non-option.
    1600             :  */
    1601             : 
    1602          17 : static int scanArguments(int argc, char **argv)
    1603             : {
    1604          17 :   const char *cmdprefix = getenv("GROFF_COMMAND_PREFIX");
    1605          17 :   if (!cmdprefix)
    1606           0 :     cmdprefix = PROG_PREFIX;
    1607          17 :   size_t pfxlen = strlen(cmdprefix);
    1608          17 :   char *troff_name = new char[pfxlen + strlen("troff") + 1];
    1609          17 :   char *s = strcpy(troff_name, cmdprefix);
    1610          17 :   s += pfxlen;
    1611          17 :   strcpy(s, "troff");
    1612             :   int c, i;
    1613             :   static const struct option long_options[] = {
    1614             :     { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
    1615             :     { "version", no_argument, 0 /* nullptr */, 'v' },
    1616             :     { 0 /* nullptr */, 0, 0, 0 }
    1617             :   };
    1618          31 :   while ((c = getopt_long(argc, argv,
    1619             :                           "+:a:bCdD:eF:g:Ghi:I:j:k:lno:prs:S:vVx:y",
    1620             :                           long_options, 0 /* nullptr */))
    1621          31 :          != EOF)
    1622          14 :     switch (c) {
    1623           0 :     case 'a':
    1624           0 :       textAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
    1625             :                           MAX_ALPHA_BITS);
    1626           0 :       if (textAlphaBits == 3)
    1627           0 :         fatal("cannot use 3 bits of antialiasing information");
    1628           0 :       break;
    1629           1 :     case 'b':
    1630             :       // handled by post-grohtml (set background color to white)
    1631           1 :       break;
    1632           2 :     case 'C':
    1633             :       // handled by post-grohtml (don't write Creator HTML comment)
    1634           2 :       break;
    1635           0 :     case 'd':
    1636           0 :       debugging = true;
    1637           0 :       break;
    1638           2 :     case 'D':
    1639           2 :       image_dir = optarg;
    1640           2 :       break;
    1641           0 :     case 'e':
    1642           0 :       need_eqn = true;
    1643           0 :       break;
    1644           0 :     case 'F':
    1645           0 :       font_path.command_line_dir(optarg);
    1646           0 :       break;
    1647           0 :     case 'g':
    1648           0 :       graphicAlphaBits = min(max(MIN_ALPHA_BITS, atoi(optarg)),
    1649             :                              MAX_ALPHA_BITS);
    1650           0 :       if (graphicAlphaBits == 3)
    1651           0 :         fatal("cannot use 3 bits of antialiasing information");
    1652           0 :       break;
    1653           2 :     case 'G':
    1654             :       // handled by post-grohtml (don't write CreationDate HTML comment)
    1655           2 :       break;
    1656           0 :     case 'h':
    1657             :       // handled by post-grohtml (write headings with font size changes)
    1658           0 :       break;
    1659           0 :     case 'i':
    1660           0 :       image_res = atoi(optarg);
    1661           0 :       break;
    1662           3 :     case 'I':
    1663           3 :       image_template = optarg;
    1664           3 :       break;
    1665           2 :     case 'j':
    1666             :       // handled by post-grohtml (set job name for multiple file output)
    1667           2 :       break;
    1668           0 :     case 'k':
    1669             :       // handled by post-grohtml (charset ASCII/mixed/UTF-8)
    1670           0 :       break;
    1671           0 :     case 'l':
    1672             :       // handled by post-grohtml (no automatic section links)
    1673           0 :       break;
    1674           1 :     case 'n':
    1675             :       // handled by post-grohtml (generate simple heading anchors)
    1676           1 :       break;
    1677           0 :     case 'o':
    1678           0 :       vertical_offset = atoi(optarg);
    1679           0 :       break;
    1680           0 :     case 'p':
    1681           0 :       want_progress_report = true;
    1682           0 :       break;
    1683           1 :     case 'r':
    1684             :       // handled by post-grohtml (no header and footer lines)
    1685           1 :       break;
    1686           0 :     case 's':
    1687             :       // handled by post-grohtml (use font size n as the HTML base size)
    1688           0 :       break;
    1689           0 :     case 'S':
    1690             :       // handled by post-grohtml (set file split level)
    1691           0 :       break;
    1692           0 :     case 'v':
    1693           0 :       printf("GNU pre-grohtml (groff) version %s\n", Version_string);
    1694           0 :       exit(EXIT_SUCCESS);
    1695           0 :     case 'V':
    1696             :       // handled by post-grohtml (create validator button)
    1697           0 :       break;
    1698           0 :     case 'x':
    1699             :       // html dialect
    1700           0 :       if (strcmp(optarg, "x") == 0)
    1701           0 :         dialect = xhtml;
    1702           0 :       else if (strcmp(optarg, "4") == 0)
    1703           0 :         dialect = html4;
    1704             :       else
    1705           0 :         warning("unsupported HTML dialect: '%1'", optarg);
    1706           0 :       break;
    1707           0 :     case 'y':
    1708             :       // handled by post-grohtml (create groff signature)
    1709           0 :       break;
    1710           0 :     case CHAR_MAX + 1: // --help
    1711           0 :       usage(stdout);
    1712           0 :       exit(EXIT_SUCCESS);
    1713             :       break;
    1714           0 :     case '?':
    1715           0 :       if (optopt != 0)
    1716           0 :         error("unrecognized command-line option '%1'", char(optopt));
    1717             :       else
    1718           0 :         error("unrecognized command-line option '%1'",
    1719           0 :               argv[(optind - 1)]);
    1720           0 :       usage(stderr);
    1721           0 :       exit(2);
    1722             :       break;
    1723           0 :     case ':':
    1724           0 :       error("command-line option '%1' requires an argument",
    1725           0 :            char(optopt));
    1726           0 :       usage(stderr);
    1727           0 :       exit(2);
    1728             :       break;
    1729           0 :     default:
    1730           0 :       break;
    1731             :     }
    1732             : 
    1733          17 :   i = optind;
    1734          73 :   while (i < argc) {
    1735          56 :     if (strcmp(argv[i], troff_name) == 0)
    1736          17 :       troff_arg = i;
    1737          39 :     else if (argv[i][0] != '-')
    1738           0 :       return i;
    1739          56 :     i++;
    1740             :   }
    1741          17 :   delete[] troff_name;
    1742             : 
    1743          17 :   return argc;
    1744             : }
    1745             : 
    1746             : /*
    1747             :  *  makeTempFiles - Name the temporary files.
    1748             :  */
    1749             : 
    1750          17 : static void makeTempFiles(void)
    1751             : {
    1752          17 :   psFileName = DEBUG_FILE("prehtml-ps");
    1753          17 :   regionFileName = DEBUG_FILE("prehtml-region");
    1754          17 :   imagePageName = DEBUG_FILE("prehtml-page");
    1755          17 :   psPageName = DEBUG_FILE("prehtml-psn");
    1756          17 :   troffFileName = DEBUG_FILE("prehtml-troff");
    1757          17 :   htmlFileName = DEBUG_FILE("prehtml-html");
    1758             :   FILE *f;
    1759             : 
    1760             :   // psPageName contains a single page of PostScript.
    1761          17 :   f = xtmpfile(&psPageName, PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT);
    1762          17 :   if (0 /* nullptr */ == f)
    1763           0 :     sys_fatal("xtmpfile");
    1764          17 :   fclose(f);
    1765             : 
    1766             :   // imagePageName contains a bitmap image of a single PostScript page.
    1767          17 :   f = xtmpfile(&imagePageName, PAGE_TEMPLATE_LONG, PAGE_TEMPLATE_SHORT);
    1768          17 :   if (0 /* nullptr */ == f)
    1769           0 :     sys_fatal("xtmpfile");
    1770          17 :   fclose(f);
    1771             : 
    1772             :   // psFileName contains a PostScript file of the complete document.
    1773          17 :   f = xtmpfile(&psFileName, PS_TEMPLATE_LONG, PS_TEMPLATE_SHORT);
    1774          17 :   if (0 /* nullptr */ == f)
    1775           0 :     sys_fatal("xtmpfile");
    1776          17 :   fclose(f);
    1777             : 
    1778             :   // regionFileName contains a list of the images and their boxed
    1779             :   // coordinates.
    1780          17 :   f = xtmpfile(&regionFileName,
    1781             :                REGION_TEMPLATE_LONG, REGION_TEMPLATE_SHORT);
    1782          17 :   if (0 /* nullptr */ == f)
    1783           0 :     sys_fatal("xtmpfile");
    1784          17 :   fclose(f);
    1785          17 : }
    1786             : 
    1787          17 : static bool do_file(const char *filename)
    1788             : {
    1789             :   FILE *fp;
    1790             : 
    1791          17 :   current_filename = filename;
    1792          17 :   if (strcmp(filename, "-") == 0)
    1793          17 :     fp = stdin;
    1794             :   else {
    1795           0 :     fp = fopen(filename, "r");
    1796           0 :     if (0 /* nullptr*/ == fp) {
    1797           0 :       current_filename = 0 /* nullptr */;
    1798           0 :       error("unable to open '%1': %2", filename, strerror(errno));
    1799           0 :       return false;
    1800             :     }
    1801             :   }
    1802          17 :   inputFile.read_file(fp);
    1803          17 :   if (fp != stdin)
    1804           0 :     if (fclose(fp) != 0)
    1805           0 :       sys_fatal("fclose");
    1806          17 :   current_filename = 0 /* nullptr */;
    1807          17 :   return true;
    1808             : }
    1809             : 
    1810          17 : static void cleanup(void)
    1811             : {
    1812          17 :   free(const_cast<char *>(image_gen));
    1813          17 : }
    1814             : 
    1815          17 : int main(int argc, char **argv)
    1816             : {
    1817          17 :   program_name = argv[0];
    1818             : #ifdef CAPTURE_MODE
    1819             :   fprintf(stderr, "%s: invoked with %d arguments ...\n", program_name,
    1820             :           argc);
    1821             :   for (int i = 0; i < argc; i++)
    1822             :     fprintf(stderr, "%2d: %s\n", i, argv[i]);
    1823             :   FILE *dump = fopen(DEBUG_FILE("pre-html-data"), "wb");
    1824             :   if (dump != 0 /* nullptr */) {
    1825             :     while((int ch = fgetc(stdin)) >= 0)
    1826             :       fputc(ch, dump);
    1827             :     fclose(dump);
    1828             :   }
    1829             :   exit(EXIT_FAILURE);
    1830             : #endif /* CAPTURE_MODE */
    1831          17 :   if (atexit(&cleanup) != 0)
    1832           0 :     sys_fatal("atexit");
    1833          17 :   int operand_index = scanArguments(argc, argv);
    1834          17 :   image_gen = strsave(get_image_generator());
    1835          17 :   if (0 /* nullptr */ == image_gen)
    1836           0 :     fatal("'image_generator' directive not found in file '%1'",
    1837           0 :           devhtml_desc);
    1838          17 :   postscriptRes = get_resolution();
    1839          17 :   if (postscriptRes < 1) // TODO: what's a more sane minimum value?
    1840           0 :     fatal("'res' directive missing or invalid in file '%1'",
    1841           0 :           devps_desc);
    1842          17 :   setupAntiAlias();
    1843          17 :   checkImageDir();
    1844          17 :   makeFileName();
    1845          17 :   bool have_file_operand = false;
    1846          17 :   while (operand_index < argc) {
    1847           0 :     if (argv[operand_index][0] != '-') {
    1848           0 :       if(!do_file(argv[operand_index]))
    1849           0 :         exit(EXIT_FAILURE);
    1850           0 :       have_file_operand = true;
    1851             :     }
    1852           0 :     operand_index++;
    1853             :   }
    1854             : 
    1855          17 :   if (!have_file_operand)
    1856          17 :     do_file("-");
    1857          17 :   makeTempFiles();
    1858          17 :   int wstatus = inputFile.do_image(argc, argv);
    1859          17 :   if (wstatus == 0) {
    1860          17 :     generateImages(regionFileName);
    1861          17 :     wstatus = inputFile.do_html(argc, argv);
    1862             :   }
    1863             :   else
    1864           0 :     if (WEXITSTATUS(wstatus) != 0)
    1865             :       // XXX: This is a crappy suggestion.  See Savannah #62673.
    1866           0 :       fatal("'%1' exited with status %2; re-run with a different output"
    1867             :             " driver to see diagnostic messages", argv[0],
    1868           0 :             WEXITSTATUS(wstatus));
    1869          17 :   exit(EXIT_SUCCESS);
    1870             : }
    1871             : 
    1872             : // Local Variables:
    1873             : // fill-column: 72
    1874             : // mode: C++
    1875             : // End:
    1876             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14