LCOV - code coverage report
Current view: top level - roff/groff - groff.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 338 534 63.3 %
Date: 2026-01-16 17:51:41 Functions: 17 25 68.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2024 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       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 under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : 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             : // A front end for GNU troff.
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include <config.h>
      23             : #endif
      24             : 
      25             : #include <assert.h>
      26             : #include <errno.h>
      27             : #include <stdio.h> // EOF, FILE, fflush(), setbuf(), stderr, stdout
      28             : #include <stdlib.h> // exit(), EXIT_SUCCESS, free(), getenv(), putenv()
      29             : #include <string.h> // strerror(), strsignal()
      30             : 
      31             : #include <getopt.h> // getopt_long()
      32             : //
      33             : // TODO: operating system services proper (cf. the standard C/C++
      34             : // runtime and libraries) should be abstracted through posix.h/
      35             : // nonposix.h (if gnulib doesn't handle it for us).
      36             : #include <sys/types.h> // pid_t
      37             : #include <signal.h> // kill()
      38             : 
      39             : // needed for close(), dup(), execvp(), _exit(), fork(), pipe(), wait()
      40             : #include "posix.h"
      41             : #include "nonposix.h"
      42             : 
      43             : #include "lib.h"
      44             : 
      45             : #include "errarg.h"
      46             : #include "error.h"
      47             : #include "stringclass.h"
      48             : #include "cset.h"
      49             : #include "font.h"
      50             : #include "device.h"
      51             : #include "pipeline.h"
      52             : #include "relocate.h"
      53             : #include "defs.h"
      54             : 
      55             : #define GXDITVIEW "gxditview"
      56             : 
      57             : // troff will be passed an argument of -rXREG=1 if the -X option is
      58             : // specified
      59             : #define XREG ".X"
      60             : 
      61             : // The number of commands must be in sync with MAX_COMMANDS in
      62             : // pipeline.h.
      63             : 
      64             : // grap, chem, and ideal must come before pic;
      65             : // tbl must come before eqn
      66             : const int PRECONV_INDEX = 0;
      67             : const int SOELIM_INDEX = PRECONV_INDEX + 1;
      68             : const int REFER_INDEX = SOELIM_INDEX + 1;
      69             : const int GRAP_INDEX = REFER_INDEX + 1;
      70             : const int CHEM_INDEX = GRAP_INDEX + 1;
      71             : const int IDEAL_INDEX = CHEM_INDEX + 1;
      72             : const int PIC_INDEX = IDEAL_INDEX + 1;
      73             : const int TBL_INDEX = PIC_INDEX + 1;
      74             : const int GRN_INDEX = TBL_INDEX + 1;
      75             : const int EQN_INDEX = GRN_INDEX + 1;
      76             : const int TROFF_INDEX = EQN_INDEX + 1;
      77             : const int POST_INDEX = TROFF_INDEX + 1;
      78             : const int SPOOL_INDEX = POST_INDEX + 1;
      79             : 
      80             : const int NCOMMANDS = SPOOL_INDEX + 1;
      81             : 
      82             : class possible_command {
      83             :   char *name;
      84             :   string args;
      85             :   char **argv;
      86             : 
      87             :   void build_argv();
      88             : public:
      89             :   possible_command();
      90             :   ~possible_command();
      91             :   void clear_name();
      92             :   void set_name(const char *);
      93             :   void set_name(const char *, const char *);
      94             :   const char *get_name();
      95             :   void append_arg(const char *, const char * = 0 /* nullptr */);
      96             :   void insert_arg(const char *);
      97             :   void insert_args(string s);
      98             :   void clear_args();
      99             :   char **get_argv();
     100             :   void print(int is_last, FILE *fp);
     101             : };
     102             : 
     103             : extern "C" const char *Version_string;
     104             : 
     105             : int lflag = 0;
     106             : char *spooler = 0 /* nullptr */;
     107             : char *postdriver = 0 /* nullptr */;
     108             : char *predriver = 0 /* nullptr */;
     109             : bool need_postdriver = true;
     110             : char *saved_path = 0 /* nullptr */;
     111             : char *groff_bin_path = 0 /* nullptr */;
     112             : char *groff_font_path = 0 /* nullptr */;
     113             : 
     114             : possible_command commands[NCOMMANDS];
     115             : 
     116             : int run_commands(bool no_pipe);
     117             : void print_commands(FILE *);
     118             : void append_arg_to_string(const char *arg, string &str);
     119             : void handle_unknown_desc_command(const char *command, const char *arg,
     120             :                                  const char *filename, int lineno);
     121             : const char *xbasename(const char *);
     122             : 
     123             : void usage(FILE *stream);
     124             : 
     125        6839 : static char *xstrdup(const char *s) {
     126        6839 :   if (0 /* nullptr */ == s)
     127         754 :     return const_cast<char *>(s);
     128        6085 :   char *str = strdup(s);
     129        6085 :   if (0 /* nullptr */ == str)
     130           0 :     fatal("unable to copy string: %1", strerror(errno));
     131        6085 :   return str;
     132             : }
     133             : 
     134        2926 : static void xputenv(const char *s) {
     135        2926 :   if (putenv(const_cast<char *>(s)) != 0)
     136           0 :     fatal("cannot update process environment: %1", strerror(errno));
     137        2926 :   return;
     138             : }
     139             : 
     140        1437 : static void xexit(int status) {
     141        1437 :   free(spooler);
     142        1437 :   free(predriver);
     143        1437 :   free(postdriver);
     144        1437 :   free(saved_path);
     145        1437 :   free(groff_bin_path);
     146        1437 :   free(groff_font_path);
     147        1437 :   exit(status);
     148             : }
     149             : 
     150        1437 : int main(int argc, char **argv)
     151             : {
     152        1437 :   program_name = argv[0];
     153             :   static char stderr_buf[BUFSIZ];
     154        1437 :   setbuf(stderr, stderr_buf);
     155             :   assert(NCOMMANDS <= MAX_COMMANDS);
     156        1437 :   string Pargs, Largs, Fargs;
     157        1437 :   int Kflag = 0;
     158        1437 :   bool want_version_info = false;
     159        1437 :   int Vflag = 0;
     160        1437 :   int zflag = 0;
     161        1437 :   int iflag = 0;
     162        1437 :   int Xflag = 0;
     163        1437 :   int oflag = 0;
     164        1437 :   bool is_safer_mode_locked = false; // made true if `-S` explicit
     165        1437 :   bool want_unsafe_mode = false;
     166        1437 :   int is_xhtml = 0;
     167        1437 :   int eflag = 0;
     168        1437 :   int need_pic = 0;
     169             :   int opt;
     170        1437 :   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
     171        1437 :   const char *encoding = getenv("GROFF_ENCODING");
     172        1437 :   if (!command_prefix)
     173           0 :     command_prefix = PROG_PREFIX;
     174        1437 :   commands[TROFF_INDEX].set_name(command_prefix, "troff");
     175             :   static const struct option long_options[] = {
     176             :     { "help", no_argument, 0 /* nullptr */, 'h' },
     177             :     { "version", no_argument, 0 /* nullptr */, 'v' },
     178             :     { 0 /* nullptr */, 0, 0 /* nullptr */, 0 }
     179             :   };
     180        6267 :   while ((opt = getopt_long(argc, argv,
     181             :                             ":abcCd:D:eEf:F:gGhiI:jJkK:lL:m:M:"
     182             :                             "n:No:pP:r:RsStT:UvVw:W:XzZ",
     183             :                             long_options, 0 /* nullptr */))
     184        6267 :          != EOF) {
     185             :     char buf[3];
     186        4830 :     buf[0] = '-';
     187        4830 :     buf[1] = opt;
     188        4830 :     buf[2] = '\0';
     189        4830 :     switch (opt) {
     190           0 :     case 'i':
     191           0 :       iflag = 1;
     192           0 :       break;
     193          27 :     case 'I':
     194          27 :       commands[GRN_INDEX].set_name(command_prefix, "grn");
     195          27 :       commands[GRN_INDEX].append_arg("-M", optarg);
     196          27 :       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
     197          27 :       commands[SOELIM_INDEX].append_arg(buf, optarg);
     198             :       // .psbb may need to search for files
     199          27 :       commands[TROFF_INDEX].append_arg(buf, optarg);
     200             :       // \X'ps:import' may need to search for files
     201          27 :       Pargs += buf;
     202          27 :       Pargs += optarg;
     203          27 :       Pargs += '\0';
     204          27 :       break;
     205           0 :     case 'D':
     206           0 :       commands[PRECONV_INDEX].set_name("preconv");
     207           0 :       commands[PRECONV_INDEX].append_arg("-D", optarg);
     208           0 :       break;
     209          31 :     case 'K':
     210          31 :       commands[PRECONV_INDEX].append_arg("-e", optarg);
     211          31 :       Kflag = 1;
     212             :       // fall through
     213          33 :     case 'k':
     214          33 :       commands[PRECONV_INDEX].set_name("preconv");
     215          33 :       break;
     216         131 :     case 't':
     217         131 :       commands[TBL_INDEX].set_name(command_prefix, "tbl");
     218         131 :       break;
     219           0 :     case 'J':
     220             :       // commands[IDEAL_INDEX].set_name(command_prefix, "gideal");
     221             :       // need_pic = 1;
     222           0 :       break;
     223           1 :     case 'j':
     224           1 :       commands[CHEM_INDEX].set_name(command_prefix, "chem");
     225           1 :       need_pic = 1;
     226           1 :       break;
     227          44 :     case 'p':
     228          44 :       commands[PIC_INDEX].set_name(command_prefix, "pic");
     229          44 :       break;
     230           2 :     case 'g':
     231           2 :       commands[GRN_INDEX].set_name(command_prefix, "grn");
     232           2 :       break;
     233           0 :     case 'G':
     234           0 :       commands[GRAP_INDEX].set_name(command_prefix, "grap");
     235           0 :       need_pic = 1;
     236           0 :       break;
     237          50 :     case 'e':
     238          50 :       eflag = 1;
     239          50 :       commands[EQN_INDEX].set_name(command_prefix, "eqn");
     240          50 :       break;
     241           0 :     case 's':
     242           0 :       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
     243           0 :       break;
     244           1 :     case 'R':
     245           1 :       commands[REFER_INDEX].set_name(command_prefix, "refer");
     246           1 :       break;
     247         134 :     case 'z':
     248             :     case 'a':
     249         134 :       commands[TROFF_INDEX].append_arg(buf);
     250             :       // fall through
     251         377 :     case 'Z':
     252         377 :       zflag++;
     253         377 :       need_postdriver = false;
     254         377 :       break;
     255           0 :     case 'l':
     256           0 :       lflag++;
     257           0 :       break;
     258           0 :     case 'V':
     259           0 :       Vflag++;
     260           0 :       break;
     261           2 :     case 'v':
     262           2 :       want_version_info = true;
     263           2 :       printf("GNU groff version %s\n", Version_string);
     264           2 :       puts(
     265             : "Copyright 1989-2026 Free Software Foundation, Inc. and others\n"
     266             : "\n"
     267             : "This is free software, distributed under the terms of the GNU General"
     268             : " Public\n"
     269             : "License, version 3, or any later version, at your option.  There is NO"
     270             : " warranty;\n"
     271             : "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
     272             : "\n"
     273             : "See the \"COPYING\", \"FDL\", and \"LICENSES\" files in the groff"
     274             : " distribution or\n"
     275             : "package for additional notices and permissions."
     276             :       );
     277           2 :       puts("\nprograms in constructed pipeline:\n");
     278           2 :       fflush(stdout);
     279             :       // Pass -v to all possible subprograms
     280           2 :       commands[PRECONV_INDEX].append_arg(buf);
     281           2 :       commands[CHEM_INDEX].append_arg(buf);
     282           2 :       commands[IDEAL_INDEX].append_arg(buf);
     283           2 :       commands[POST_INDEX].append_arg(buf);
     284             :       // fall through
     285         320 :     case 'C':
     286         320 :       commands[SOELIM_INDEX].append_arg(buf);
     287         320 :       commands[REFER_INDEX].append_arg(buf);
     288         320 :       commands[PIC_INDEX].append_arg(buf);
     289         320 :       commands[GRAP_INDEX].append_arg(buf);
     290         320 :       commands[TBL_INDEX].append_arg(buf);
     291         320 :       commands[GRN_INDEX].append_arg(buf);
     292         320 :       commands[EQN_INDEX].append_arg(buf);
     293         320 :       commands[TROFF_INDEX].append_arg(buf);
     294         320 :       break;
     295           0 :     case 'N':
     296           0 :       commands[EQN_INDEX].append_arg(buf);
     297           0 :       break;
     298           0 :     case 'h':
     299           0 :       usage(stdout);
     300           0 :       exit(EXIT_SUCCESS);
     301             :       break;
     302          82 :     case 'E':
     303             :     case 'b':
     304          82 :       commands[TROFF_INDEX].append_arg(buf);
     305          82 :       break;
     306           0 :     case 'c':
     307           0 :       commands[TROFF_INDEX].append_arg(buf);
     308           0 :       break;
     309           1 :     case 'S':
     310           1 :       is_safer_mode_locked = true;
     311           1 :       want_unsafe_mode = false;
     312           1 :       break;
     313          22 :     case 'U':
     314          22 :       if (is_safer_mode_locked)
     315           1 :         warning("ignoring '-U' option; '-S' already specified");
     316             :       else
     317          21 :         want_unsafe_mode = true;
     318          22 :       break;
     319        1382 :     case 'T':
     320        1382 :       if (strcmp(optarg, "xhtml") == 0) {
     321             :         // force soelim to aid the html preprocessor
     322          10 :         commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
     323          10 :         Pargs += "-x";
     324          10 :         Pargs += '\0';
     325          10 :         Pargs += 'x';
     326          10 :         Pargs += '\0';
     327          10 :         is_xhtml = 1;
     328          10 :         device = "html";
     329          10 :         break;
     330             :       }
     331        1372 :       if (strcmp(optarg, "html") == 0)
     332             :         // force soelim to aid the html preprocessor
     333          52 :         commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
     334        1372 :       if (strcmp(optarg, "Xps") == 0) {
     335           0 :         warning("-TXps option is obsolete: use -X -Tps instead");
     336           0 :         device = "ps";
     337           0 :         Xflag++;
     338             :       }
     339             :       else
     340        1372 :         device = optarg;
     341        1372 :       break;
     342         103 :     case 'F':
     343         103 :       font::command_line_font_dir(optarg);
     344         103 :       if (Fargs.length() > 0) {
     345          51 :         Fargs += PATH_SEP_CHAR;
     346          51 :         Fargs += optarg;
     347             :       }
     348             :       else
     349          52 :         Fargs = optarg;
     350         103 :       break;
     351           0 :     case 'o':
     352           0 :       oflag = 1;
     353             :       // fall through
     354        1596 :     case 'f':
     355             :     case 'm':
     356             :     case 'r':
     357             :     case 'd':
     358             :     case 'n':
     359             :     case 'w':
     360             :     case 'W':
     361        1596 :       commands[TROFF_INDEX].append_arg(buf, optarg);
     362        1596 :       break;
     363         150 :     case 'M':
     364         150 :       commands[EQN_INDEX].append_arg(buf, optarg);
     365         150 :       commands[GRAP_INDEX].append_arg(buf, optarg);
     366         150 :       commands[GRN_INDEX].append_arg(buf, optarg);
     367         150 :       commands[TROFF_INDEX].append_arg(buf, optarg);
     368         150 :       break;
     369         508 :     case 'P':
     370         508 :       Pargs += optarg;
     371         508 :       Pargs += '\0';
     372         508 :       break;
     373           0 :     case 'L':
     374           0 :       append_arg_to_string(optarg, Largs);
     375           0 :       break;
     376           0 :     case 'X':
     377           0 :       Xflag++;
     378           0 :       need_postdriver = false;
     379           0 :       break;
     380           0 :     case '?':
     381           0 :       if (optopt != 0)
     382           0 :         error("unrecognized command-line option '%1'", char(optopt));
     383             :       else
     384           0 :         error("unrecognized command-line option '%1'",
     385           0 :               argv[(optind - 1)]);
     386           0 :       usage(stderr);
     387           0 :       xexit(2);
     388           0 :       break;
     389           0 :     case ':':
     390           0 :       error("command-line option '%1' requires an argument",
     391           0 :             char(optopt));
     392           0 :       usage(stderr);
     393           0 :       xexit(2);
     394           0 :       break;
     395           0 :     default:
     396           0 :       assert(0 == "no case to handle option character");
     397             :       break;
     398             :     }
     399             :   }
     400        1437 :   if (need_pic)
     401           1 :     commands[PIC_INDEX].set_name(command_prefix, "pic");
     402        1437 :   if (encoding) {
     403           0 :     commands[PRECONV_INDEX].set_name("preconv");
     404           0 :     if (!Kflag && *encoding)
     405           0 :       commands[PRECONV_INDEX].append_arg("-e", encoding);
     406             :   }
     407        1437 :   if (is_safer_mode_locked) {
     408           1 :     commands[TROFF_INDEX].insert_arg("-S");
     409           1 :     commands[PIC_INDEX].append_arg("-S");
     410             :   }
     411        1436 :   else if (want_unsafe_mode) {
     412          21 :     commands[TROFF_INDEX].insert_arg("-U");
     413          21 :     commands[PIC_INDEX].append_arg("-U");
     414             :   }
     415        1437 :   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
     416        1437 :   const char *desc = font::load_desc();
     417        1437 :   if (0 /* nullptr */ == desc)
     418           0 :     fatal("cannot load 'DESC' description file for device '%1'",
     419           0 :           device);
     420        1437 :   if (need_postdriver && (0 /* nullptr */ == postdriver))
     421           0 :     fatal_with_file_and_line(desc, 0, "device description file missing"
     422             :                              " 'postpro' directive");
     423        1437 :   if (predriver && !zflag) {
     424          17 :     commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
     425          17 :     commands[TROFF_INDEX].insert_arg("--");
     426          17 :     commands[TROFF_INDEX].set_name(predriver);
     427             :     // pass the device arguments to the predrivers as well
     428          17 :     commands[TROFF_INDEX].insert_args(Pargs);
     429          17 :     if (eflag && is_xhtml)
     430           0 :       commands[TROFF_INDEX].insert_arg("-e");
     431          17 :     if (want_version_info)
     432           0 :       commands[TROFF_INDEX].insert_arg("-v");
     433             :   }
     434        1437 :   const char *real_driver = 0 /* nullptr */;
     435        1437 :   if (Xflag) {
     436           0 :     real_driver = postdriver;
     437           0 :     postdriver = xstrdup(GXDITVIEW); // so we can free() it in xexit()
     438           0 :     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
     439             :   }
     440        1437 :   if (postdriver)
     441        1437 :     commands[POST_INDEX].set_name(postdriver);
     442        1437 :   int gxditview_flag = postdriver
     443        1437 :                        && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
     444        1437 :   if (gxditview_flag && argc - optind == 1) {
     445           0 :     commands[POST_INDEX].append_arg("-title");
     446           0 :     commands[POST_INDEX].append_arg(argv[optind]);
     447           0 :     commands[POST_INDEX].append_arg("-xrm");
     448           0 :     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
     449           0 :     string filename_string("|");
     450           0 :     append_arg_to_string(argv[0], filename_string);
     451           0 :     append_arg_to_string("-Z", filename_string);
     452           0 :     for (int i = 1; i < argc; i++)
     453           0 :       append_arg_to_string(argv[i], filename_string);
     454           0 :     filename_string += '\0';
     455           0 :     commands[POST_INDEX].append_arg("-filename");
     456           0 :     commands[POST_INDEX].append_arg(filename_string.contents());
     457             :   }
     458        1437 :   if (gxditview_flag && Xflag) {
     459           0 :     string print_string(real_driver);
     460           0 :     if (spooler) {
     461           0 :       print_string += " | ";
     462           0 :       print_string += spooler;
     463           0 :       print_string += Largs;
     464             :     }
     465           0 :     print_string += '\0';
     466           0 :     commands[POST_INDEX].append_arg("-printCommand");
     467           0 :     commands[POST_INDEX].append_arg(print_string.contents());
     468             :   }
     469        1437 :   const char *p = Pargs.contents();
     470        1437 :   const char *end = p + Pargs.length();
     471        1992 :   while (p < end) {
     472         555 :     commands[POST_INDEX].append_arg(p);
     473         555 :     p = strchr(p, '\0') + 1;
     474             :   }
     475        1437 :   if (gxditview_flag)
     476          40 :     commands[POST_INDEX].append_arg("-");
     477        1437 :   if (lflag && !want_version_info && !Xflag && spooler) {
     478           0 :     commands[SPOOL_INDEX].set_name(BSHELL);
     479           0 :     commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
     480           0 :     Largs += '\0';
     481           0 :     Largs = spooler + Largs;
     482           0 :     commands[SPOOL_INDEX].append_arg(Largs.contents());
     483             :   }
     484        1437 :   if (zflag) {
     485         377 :     commands[POST_INDEX].set_name(0 /* nullptr */);
     486         377 :     commands[SPOOL_INDEX].set_name(0 /* nullptr */);
     487             :   }
     488        1437 :   commands[TROFF_INDEX].append_arg("-T", device);
     489        1437 :   if (strcmp(device, "html") == 0) {
     490          62 :     if (is_xhtml) {
     491          10 :       if (oflag)
     492           0 :         fatal("'-o' option is invalid with device 'xhtml'");
     493          10 :       if (zflag)
     494          10 :         commands[EQN_INDEX].append_arg("-Tmathml:xhtml");
     495           0 :       else if (eflag)
     496           0 :         commands[EQN_INDEX].clear_name();
     497             :     }
     498             :     else {
     499          52 :       if (oflag)
     500           0 :         fatal("'-o' option is invalid with device 'html'");
     501             :       // html renders equations as images via ps
     502          52 :       commands[EQN_INDEX].append_arg("-Tps:html");
     503             :     }
     504             :   }
     505             :   else
     506        1375 :     commands[EQN_INDEX].append_arg("-T", device);
     507             : 
     508        1437 :   commands[GRN_INDEX].append_arg("-T", device);
     509             : 
     510             :   int first_index;
     511       14508 :   for (first_index = 0; first_index < TROFF_INDEX; first_index++)
     512       13277 :     if (commands[first_index].get_name() != 0 /* nullptr */)
     513         206 :       break;
     514        1437 :   if (optind < argc) {
     515          74 :     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
     516           0 :       commands[first_index].append_arg("--");
     517         350 :     for (int i = optind; i < argc; i++)
     518         276 :       commands[first_index].append_arg(argv[i]);
     519          74 :     if (iflag)
     520           0 :       commands[first_index].append_arg("-");
     521             :   }
     522        1437 :   if (Fargs.length() > 0) {
     523         104 :     string e = "GROFF_FONT_PATH";
     524          52 :     e += '=';
     525          52 :     e += Fargs;
     526          52 :     char *fontpath = getenv("GROFF_FONT_PATH");
     527          52 :     if (fontpath && *fontpath) {
     528          30 :       e += PATH_SEP_CHAR;
     529          30 :       e += fontpath;
     530             :     }
     531          52 :     e += '\0';
     532          52 :     groff_font_path = xstrdup(e.contents());
     533          52 :     xputenv(groff_font_path);
     534             :   }
     535             :   {
     536             :     // we save the original path in GROFF_PATH__ and put it into the
     537             :     // environment -- troff will pick it up later.
     538        1437 :     char *path = getenv("PATH");
     539        2874 :     string g = "GROFF_PATH__";
     540        1437 :     g += '=';
     541        1437 :     if (path && *path)
     542        1437 :       g += path;
     543        1437 :     g += '\0';
     544        1437 :     saved_path = xstrdup(g.contents());
     545        1437 :     xputenv(saved_path);
     546        1437 :     char *binpath = getenv("GROFF_BIN_PATH");
     547        2874 :     string f = "PATH";
     548        1437 :     f += '=';
     549        1437 :     if (binpath && *binpath)
     550        1437 :       f += binpath;
     551             :     else {
     552           0 :       binpath = relocatep(BINPATH);
     553           0 :       f += binpath;
     554             :     }
     555        1437 :     if (path && *path) {
     556        1437 :       f += PATH_SEP_CHAR;
     557        1437 :       f += path;
     558             :     }
     559        1437 :     f += '\0';
     560        1437 :     groff_bin_path = xstrdup(f.contents());
     561        1437 :     xputenv(groff_bin_path);
     562             :   }
     563        1437 :   if (Vflag)
     564           0 :     print_commands(Vflag == 1 ? stdout : stderr);
     565        1437 :   if (Vflag == 1)
     566           0 :     xexit(EXIT_SUCCESS);
     567             :   // We need the lower two bits of the exit status for ourselves.
     568        1437 :   int status = run_commands(want_version_info) << 2;
     569        1437 :   assert(status < 65 || 0 == "run_commands() returned too many bits");
     570        1437 :   xexit(status);
     571           0 : }
     572             : 
     573        1437 : const char *xbasename(const char *s)
     574             : {
     575        1437 :   if (!s)
     576           0 :     return 0 /* nullptr */;
     577             :   // DIR_SEPS[] are possible directory separator characters; see
     578             :   // nonposix.h.  We want the rightmost separator of all possible ones.
     579             :   // Example: d:/foo\\bar.
     580        1437 :   const char *p = strrchr(s, DIR_SEPS[0]), *p1;
     581        1437 :   const char *sep = &DIR_SEPS[1];
     582             : 
     583        1437 :   while (*sep)
     584             :     {
     585           0 :       p1 = strrchr(s, *sep);
     586           0 :       if (p1 && (!p || p1 > p))
     587           0 :         p = p1;
     588           0 :       sep++;
     589             :     }
     590        1437 :   return p ? p + 1 : s;
     591             : }
     592             : 
     593        1858 : void handle_unknown_desc_command(const char *command, const char *arg,
     594             :                                  const char *filename, int lineno)
     595             : {
     596        1858 :   if (strcmp(command, "print") == 0) {
     597         173 :     if (arg == 0 /* nullptr */)
     598           0 :       error_with_file_and_line(filename, lineno, "'print' directive"
     599             :                                " requires an argument");
     600             :     else
     601         173 :       spooler = xstrdup(arg);
     602         173 :     return;
     603             :   }
     604        1685 :   if (strcmp(command, "prepro") == 0) {
     605          62 :     if (arg == 0 /* nullptr */)
     606           0 :       error("'prepro' directive requires an argument");
     607             :     else {
     608         744 :       for (const char *p = arg; *p; p++)
     609         682 :         if (csspace(*p)) {
     610           0 :           error_with_file_and_line(filename, lineno, "invalid 'prepro'"
     611             :                                    " directive argument '%1': program"
     612           0 :                                    " name required", arg);
     613             :         }
     614          62 :       predriver = xstrdup(arg);
     615             :     }
     616          62 :     return;
     617             :   }
     618        1623 :   if (strcmp(command, "postpro") == 0) {
     619        1437 :     if (arg == 0 /* nullptr */)
     620           0 :       error_with_file_and_line(filename, lineno, "'postpro' directive"
     621             :                                " requires an argument");
     622             :     else {
     623       10417 :       for (const char *p = arg; *p; p++)
     624        8980 :         if (csspace(*p)) {
     625           0 :           error_with_file_and_line(filename, lineno, "invalid 'postpro'"
     626             :                                    " directive argument '%1': program"
     627           0 :                                    " name required", arg);
     628           0 :           return;
     629             :         }
     630        1437 :       postdriver = xstrdup(arg);
     631             :     }
     632        1437 :     return;
     633             :   }
     634             : }
     635             : 
     636           0 : void print_commands(FILE *fp)
     637             : {
     638             :   int last;
     639           0 :   for (last = SPOOL_INDEX; last >= 0; last--)
     640           0 :     if (commands[last].get_name() != 0 /* nullptr */)
     641           0 :       break;
     642           0 :   for (int i = 0; i <= last; i++)
     643           0 :     if (commands[i].get_name() != 0 /* nullptr */)
     644           0 :       commands[i].print(i == last, fp);
     645           0 : }
     646             : 
     647             : // Run the commands. Return the code with which to exit.
     648             : 
     649        1437 : int run_commands(bool no_pipe)
     650             : {
     651             :   char **v[NCOMMANDS]; // vector of argv arrays to pipe together
     652        1437 :   int ncommands = 0;
     653       20118 :   for (int i = 0; i < NCOMMANDS; i++)
     654       18681 :     if (commands[i].get_name() != 0 /* nullptr */)
     655        2850 :       v[ncommands++] = commands[i].get_argv();
     656        2874 :   return run_pipeline(ncommands, v, no_pipe);
     657             : }
     658             : 
     659       18681 : possible_command::possible_command()
     660       18681 : : name(0), argv(0)
     661             : {
     662       18681 : }
     663             : 
     664       18681 : possible_command::~possible_command()
     665             : {
     666       18681 :   free(name);
     667       18681 :   delete[] argv;
     668       18681 : }
     669             : 
     670        2241 : void possible_command::set_name(const char *s)
     671             : {
     672        2241 :   free(name);
     673        2241 :   name = xstrdup(s);
     674        2241 : }
     675             : 
     676           0 : void possible_command::clear_name()
     677             : {
     678           0 :   delete[] name;
     679           0 :   delete[] argv;
     680           0 :   name = NULL;
     681           0 :   argv = NULL;
     682           0 : }
     683             : 
     684        1783 : void possible_command::set_name(const char *s1, const char *s2)
     685             : {
     686        1783 :   free(name);
     687        1783 :   name = (char*)malloc(strlen(s1) + strlen(s2) + 1);
     688        1783 :   strcpy(name, s1);
     689        1783 :   strcat(name, s2);
     690        1783 : }
     691             : 
     692       31975 : const char *possible_command::get_name()
     693             : {
     694       31975 :   return name;
     695             : }
     696             : 
     697           0 : void possible_command::clear_args()
     698             : {
     699           0 :   args.clear();
     700           0 : }
     701             : 
     702       10296 : void possible_command::append_arg(const char *s, const char *t)
     703             : {
     704       10296 :   args += s;
     705       10296 :   if (t)
     706        6557 :     args += t;
     707       10296 :   args += '\0';
     708       10296 : }
     709             : 
     710          68 : void possible_command::insert_arg(const char *s)
     711             : {
     712         136 :   string str(s);
     713          68 :   str += '\0';
     714          68 :   str += args;
     715          68 :   args = str;
     716          68 : }
     717             : 
     718          17 : void possible_command::insert_args(string s)
     719             : {
     720          17 :   const char *p = s.contents();
     721          17 :   const char *end = p + s.length();
     722          17 :   int l = 0;
     723          17 :   if (p >= end)
     724          11 :     return;
     725             :   // find the total number of arguments in our string
     726           6 :   do {
     727          12 :     l++;
     728          12 :     p = strchr(p, '\0') + 1;
     729          12 :   } while (p < end);
     730             :   // now insert each argument preserving the order
     731          18 :   for (int i = l - 1; i >= 0; i--) {
     732          12 :     p = s.contents();
     733          25 :     for (int j = 0; j < i; j++)
     734          13 :       p = strchr(p, '\0') + 1;
     735          12 :     insert_arg(p);
     736             :   }
     737             : }
     738             : 
     739        2850 : void possible_command::build_argv()
     740             : {
     741        2850 :   if (argv)
     742           0 :     return;
     743             :   // Count the number of arguments.
     744        2850 :   int len = args.length();
     745        2850 :   int argc = 1;
     746        2850 :   char *p = 0 /* nullptr */;
     747        2850 :   if (len > 0) {
     748        2047 :     p = &args[0];
     749       53078 :     for (int i = 0; i < len; i++)
     750       51031 :       if (p[i] == '\0')
     751        4920 :         argc++;
     752             :   }
     753             :   // Build an argument vector.
     754        2850 :   argv = new char *[argc + 1];
     755        2850 :   argv[0] = name;
     756        7770 :   for (int i = 1; i < argc; i++) {
     757        4920 :     argv[i] = p;
     758        4920 :     p = strchr(p, '\0') + 1;
     759             :   }
     760        2850 :   argv[argc] = 0 /* nullptr */;
     761             : }
     762             : 
     763           0 : void possible_command::print(int is_last, FILE *fp)
     764             : {
     765           0 :   build_argv();
     766           0 :   if (IS_BSHELL(argv[0])
     767           0 :       && argv[1] != 0 /* nullptr */
     768           0 :       && strcmp(argv[1], BSHELL_DASH_C) == 0
     769           0 :       && argv[2] != 0 /* nullptr */ && argv[3] == 0 /* nullptr */)
     770           0 :     fputs(argv[2], fp);
     771             :   else {
     772           0 :     fputs(argv[0], fp);
     773           0 :     string str;
     774           0 :     for (int i = 1; argv[i] != 0 /* nullptr */; i++) {
     775           0 :       str.clear();
     776           0 :       append_arg_to_string(argv[i], str);
     777           0 :       put_string(str, fp);
     778             :     }
     779             :   }
     780           0 :   if (is_last)
     781           0 :     putc('\n', fp);
     782             :   else
     783           0 :     fputs(" | ", fp);
     784           0 : }
     785             : 
     786           0 : void append_arg_to_string(const char *arg, string &str)
     787             : {
     788           0 :   str += ' ';
     789           0 :   int needs_quoting = 0;
     790             :   // Native Windows programs don't support '..' style of quoting, so
     791             :   // always behave as if ARG included the single quote character.
     792             : #if defined(_WIN32) && !defined(__CYGWIN__)
     793             :   int contains_single_quote = 1;
     794             : #else
     795           0 :   int contains_single_quote = 0;
     796             : #endif
     797             :   const char*p;
     798           0 :   for (p = arg; *p != '\0'; p++)
     799           0 :     switch (*p) {
     800           0 :     case ';':
     801             :     case '&':
     802             :     case '(':
     803             :     case ')':
     804             :     case '|':
     805             :     case '^':
     806             :     case '<':
     807             :     case '>':
     808             :     case '\n':
     809             :     case ' ':
     810             :     case '\t':
     811             :     case '\\':
     812             :     case '"':
     813             :     case '$':
     814             :     case '?':
     815             :     case '*':
     816           0 :       needs_quoting = 1;
     817           0 :       break;
     818           0 :     case '\'':
     819           0 :       contains_single_quote = 1;
     820           0 :       break;
     821             :     }
     822           0 :   if (contains_single_quote || arg[0] == '\0') {
     823           0 :     str += '"';
     824           0 :     for (p = arg; *p != '\0'; p++)
     825           0 :       switch (*p) {
     826             : #if !(defined(_WIN32) && !defined(__CYGWIN__))
     827           0 :       case '"':
     828             :       case '\\':
     829             :       case '$':
     830           0 :         str += '\\';
     831             : #else
     832             :       case '"':
     833             :       case '\\':
     834             :         if (*p == '"' || (*p == '\\' && p[1] == '"'))
     835             :           str += '\\';
     836             : #endif
     837             :         // fall through
     838           0 :       default:
     839           0 :         str += *p;
     840           0 :         break;
     841             :       }
     842           0 :     str += '"';
     843             :   }
     844           0 :   else if (needs_quoting) {
     845           0 :     str += '\'';
     846           0 :     str += arg;
     847           0 :     str += '\'';
     848             :   }
     849             :   else
     850           0 :     str += arg;
     851           0 : }
     852             : 
     853        2850 : char **possible_command::get_argv()
     854             : {
     855        2850 :   build_argv();
     856        2850 :   return argv;
     857             : }
     858             : 
     859           0 : void usage(FILE *stream)
     860             : {
     861             :   // Add `J` to the cluster if we ever get ideal(1) support.
     862           0 :   fprintf(stream,
     863             : "usage: %s [-abcCeEgGijklNpRsStUVXzZ] [-d ctext] [-d string=text]"
     864             : " [-D fallback-encoding] [-f font-family] [-F font-directory]"
     865             : " [-I inclusion-directory] [-K input-encoding] [-L spooler-argument]"
     866             : " [-m macro-package] [-M macro-directory] [-n page-number]"
     867             : " [-o page-list] [-P postprocessor-argument] [-r cnumeric-expression]"
     868             : " [-r register=numeric-expression] [-T output-device]"
     869             : " [-w warning-category] [-W warning-category]"
     870             : " [file ...]\n"
     871             : "usage: %s {-v | --version}\n"
     872             : "usage: %s {-h | --help}\n",
     873             :           program_name, program_name, program_name);
     874           0 :   if (stdout == stream)
     875           0 :     fputs("\n"
     876             : "groff (GNU roff) is a typesetting system that reads plain text input\n"
     877             : "files that include formatting commands to produce output in\n"
     878             : "PostScript, PDF, HTML, or DVI formats or for display to a terminal.\n"
     879             : "See the groff(1) manual page.\n",
     880             :           stream);
     881           0 : }
     882             : 
     883             : extern "C" {
     884             : 
     885           0 : void c_error(const char *format, const char *arg1, const char *arg2,
     886             :              const char *arg3)
     887             : {
     888           0 :   error(format, arg1, arg2, arg3);
     889           0 : }
     890             : 
     891           0 : void c_fatal(const char *format, const char *arg1, const char *arg2,
     892             :              const char *arg3)
     893             : {
     894           0 :   fatal(format, arg1, arg2, arg3);
     895           0 : }
     896             : 
     897             : }
     898             : 
     899             : // Local Variables:
     900             : // fill-column: 72
     901             : // mode: C++
     902             : // End:
     903             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14