home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / groff / groff.cc next >
C/C++ Source or Header  |  1995-06-22  |  14KB  |  606 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  20.  
  21. // A front end for groff.
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <signal.h>
  27. #include <errno.h>
  28.  
  29. #include "lib.h"
  30. #include "assert.h"
  31. #include "errarg.h"
  32. #include "error.h"
  33. #include "stringclass.h"
  34. #include "cset.h"
  35. #include "font.h"
  36. #include "device.h"
  37. #include "pipeline.h"
  38. #include "defs.h"
  39.  
  40. #define BSHELL "/bin/sh"
  41. #define GXDITVIEW "gxditview"
  42.  
  43. // troff will be passed an argument of -rXREG=1 if the -X option is
  44. // specified
  45. #define XREG ".X"
  46.  
  47. #ifndef STDLIB_H_DECLARES_PUTENV
  48. extern "C" {
  49.   int putenv(const char *);
  50. }
  51. #endif /* not STDLIB_H_DECLARES_PUTENV */
  52.  
  53. const int SOELIM_INDEX = 0;
  54. const int REFER_INDEX = SOELIM_INDEX + 1;
  55. const int PIC_INDEX = REFER_INDEX + 1;
  56. const int TBL_INDEX = PIC_INDEX + 1;
  57. const int EQN_INDEX = TBL_INDEX + 1;
  58. const int TROFF_INDEX = EQN_INDEX + 1;
  59. const int POST_INDEX = TROFF_INDEX + 1;
  60. const int SPOOL_INDEX = POST_INDEX + 1;
  61.  
  62. const int NCOMMANDS = SPOOL_INDEX + 1;
  63.  
  64. class possible_command {
  65.   char *name;
  66.   string args;
  67.   char **argv;
  68.  
  69.   void build_argv();
  70. public:
  71.   possible_command();
  72.   ~possible_command();
  73.   void set_name(const char *);
  74.   void set_name(const char *, const char *);
  75.   const char *get_name();
  76.   void append_arg(const char *, const char * = 0);
  77.   void insert_arg(const char *);
  78.   void clear_args();
  79.   char **get_argv();
  80.   void print(int is_last, FILE *fp);
  81. };
  82.  
  83. int lflag = 0;
  84. char *spooler = 0;
  85. char *driver = 0;
  86.  
  87. possible_command commands[NCOMMANDS];
  88.  
  89. int run_commands();
  90. void print_commands();
  91. void append_arg_to_string(const char *arg, string &str);
  92. void handle_unknown_desc_command(const char *command, const char *arg,
  93.                  const char *filename, int lineno);
  94. const char *xbasename(const char *);
  95.  
  96. void usage();
  97. void help();
  98.  
  99. int main(int argc, char **argv)
  100. {
  101.   program_name = argv[0];
  102.   static char stderr_buf[BUFSIZ];
  103.   setbuf(stderr, stderr_buf);
  104.   assert(NCOMMANDS <= MAX_COMMANDS);
  105.   string Pargs, Largs, Fargs;
  106.   int Vflag = 0;
  107.   int zflag = 0;
  108.   int iflag = 0;
  109.   int Xflag = 0;
  110.   int opt;
  111.   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
  112.   if (!command_prefix)
  113.     command_prefix = PROG_PREFIX;
  114.   commands[TROFF_INDEX].set_name(command_prefix, "troff");
  115.   while ((opt = getopt(argc, argv,
  116.                "itpeRsSzavVhblCENXZF:m:T:f:w:W:M:d:r:n:o:P:L:"))
  117.      != EOF) {
  118.     char buf[3];
  119.     buf[0] = '-';
  120.     buf[1] = opt;
  121.     buf[2] = '\0';
  122.     switch (opt) {
  123.     case 'i':
  124.       iflag = 1;
  125.       break;
  126.     case 't':
  127.       commands[TBL_INDEX].set_name(command_prefix, "tbl");
  128.       break;
  129.     case 'p':
  130.       commands[PIC_INDEX].set_name(command_prefix, "pic");
  131.       break;
  132.     case 'e':
  133.       commands[EQN_INDEX].set_name(command_prefix, "eqn");
  134.       break;
  135.     case 's':
  136.       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
  137.       break;
  138.     case 'R':
  139.       commands[REFER_INDEX].set_name(command_prefix, "refer");
  140.       break;
  141.     case 'z':
  142.     case 'a':
  143.       commands[TROFF_INDEX].append_arg(buf);
  144.       // fall through
  145.     case 'Z':
  146.       zflag++;
  147.       break;
  148.     case 'l':
  149.       lflag++;
  150.       break;
  151.     case 'V':
  152.       Vflag++;
  153.       break;
  154.     case 'v':
  155.     case 'C':
  156.       commands[SOELIM_INDEX].append_arg(buf);
  157.       commands[PIC_INDEX].append_arg(buf);
  158.       commands[TBL_INDEX].append_arg(buf);
  159.       commands[EQN_INDEX].append_arg(buf);
  160.       commands[TROFF_INDEX].append_arg(buf);
  161.       break;
  162.     case 'N':
  163.       commands[EQN_INDEX].append_arg(buf);
  164.       break;
  165.     case 'h':
  166.       help();
  167.       break;
  168.     case 'E':
  169.     case 'b':
  170.       commands[TROFF_INDEX].append_arg(buf);
  171.       break;
  172.     case 'S':
  173.       commands[PIC_INDEX].append_arg(buf);
  174.       commands[TROFF_INDEX].insert_arg("-msafer");
  175.       break;
  176.     case 'T':
  177.       if (strcmp(optarg, "Xps") == 0) {
  178.     warning("-TXps option is obsolete: use -X -Tps instead");
  179.     device = "ps";
  180.     Xflag++;
  181.       }
  182.       else
  183.     device = optarg;
  184.       break;
  185.     case 'F':
  186.       font::command_line_font_dir(optarg);
  187.       if (Fargs.length() > 0) {
  188.     Fargs += ':';
  189.     Fargs += optarg;
  190.       }
  191.       else
  192.     Fargs = optarg;
  193.       break;
  194.     case 'f':
  195.     case 'o':
  196.     case 'm':
  197.     case 'r':
  198.     case 'd':
  199.     case 'n':
  200.     case 'w':
  201.     case 'W':
  202.       commands[TROFF_INDEX].append_arg(buf, optarg);
  203.       break;
  204.     case 'M':
  205.       commands[EQN_INDEX].append_arg(buf, optarg);
  206.       commands[TROFF_INDEX].append_arg(buf, optarg);
  207.       break;
  208.     case 'P':
  209.       Pargs += optarg;
  210.       Pargs += '\0';
  211.       break;
  212.     case 'L':
  213.       append_arg_to_string(optarg, Largs);
  214.       break;
  215.     case 'X':
  216.       Xflag++;
  217.       break;
  218.     case '?':
  219.       usage();
  220.       break;
  221.     default:
  222.       assert(0);
  223.       break;
  224.     }
  225.   }
  226.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  227.   if (!font::load_desc())
  228.     fatal("invalid device `%1'", device);
  229.   if (!driver)
  230.     fatal("no `postpro' command in DESC file for device `%1'", device);
  231.   const char *real_driver = 0;
  232.   if (Xflag) {
  233.     real_driver = driver;
  234.     driver = GXDITVIEW;
  235.     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
  236.   }
  237.   if (driver)
  238.     commands[POST_INDEX].set_name(driver);
  239.   int gxditview_flag = driver && strcmp(xbasename(driver), GXDITVIEW) == 0;
  240.   if (gxditview_flag && argc - optind == 1) {
  241.     commands[POST_INDEX].append_arg("-title");
  242.     commands[POST_INDEX].append_arg(argv[optind]);
  243.     commands[POST_INDEX].append_arg("-xrm");
  244.     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
  245.     string filename_string("|");
  246.     append_arg_to_string(argv[0], filename_string);
  247.     append_arg_to_string("-Z", filename_string);
  248.     for (int i = 1; i < argc; i++)
  249.       append_arg_to_string(argv[i], filename_string);
  250.     filename_string += '\0';
  251.     commands[POST_INDEX].append_arg("-filename");
  252.     commands[POST_INDEX].append_arg(filename_string.contents());
  253.   }
  254.   if (gxditview_flag && Xflag) {
  255.     string print_string(real_driver);
  256.     if (spooler) {
  257.       print_string += " | ";
  258.       print_string += spooler;
  259.       print_string += Largs;
  260.     }
  261.     print_string += '\0';
  262.     commands[POST_INDEX].append_arg("-printCommand");
  263.     commands[POST_INDEX].append_arg(print_string.contents());
  264.   }
  265.   const char *p = Pargs.contents();
  266.   const char *end = p + Pargs.length();
  267.   while (p < end) {
  268.     commands[POST_INDEX].append_arg(p);
  269.     p = strchr(p, '\0') + 1;
  270.   }
  271.   if (gxditview_flag)
  272.     commands[POST_INDEX].append_arg("-");
  273.   if (lflag && !Xflag && spooler) {
  274.     commands[SPOOL_INDEX].set_name(BSHELL);
  275.     commands[SPOOL_INDEX].append_arg("-c");
  276.     Largs += '\0';
  277.     Largs = spooler + Largs;
  278.     commands[SPOOL_INDEX].append_arg(Largs.contents());
  279.   }
  280.   if (zflag) {
  281.     commands[POST_INDEX].set_name(0);
  282.     commands[SPOOL_INDEX].set_name(0);
  283.   }
  284.   commands[TROFF_INDEX].append_arg("-T", device);
  285.   commands[EQN_INDEX].append_arg("-T", device);
  286.  
  287.   int first_index;
  288.   for (first_index = 0; first_index < TROFF_INDEX; first_index++)
  289.     if (commands[first_index].get_name() != 0)
  290.       break;
  291.   if (optind < argc) {
  292.     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
  293.       commands[first_index].append_arg("--");
  294.     for (int i = optind; i < argc; i++)
  295.       commands[first_index].append_arg(argv[i]);
  296.     if (iflag)
  297.       commands[first_index].append_arg("-");
  298.   }
  299.   if (Fargs.length() > 0) {
  300.     string e = "GROFF_FONT_PATH";
  301.     e += '=';
  302.     e += Fargs;
  303.     char *fontpath = getenv("GROFF_FONT_PATH");
  304.     if (fontpath && *fontpath) {
  305.       e += ':';
  306.       e += fontpath;
  307.     }
  308.     e += '\0';
  309.     if (putenv(strsave(e.contents())))
  310.       fatal("putenv failed");
  311.   }
  312.   if (Vflag) {
  313.     print_commands();
  314.     exit(0);
  315.   }
  316.   return run_commands();
  317. }
  318.  
  319. const char *xbasename(const char *s)
  320. {
  321.   if (!s)
  322.     return 0;
  323.   const char *p = strrchr(s, '/');
  324.   return p ? p + 1 : s;
  325. }
  326.  
  327. void handle_unknown_desc_command(const char *command, const char *arg,
  328.                  const char *filename, int lineno)
  329. {
  330.   if (strcmp(command, "print") == 0) {
  331.     if (arg == 0)
  332.       error_with_file_and_line(filename, lineno,
  333.                    "`print' command requires an argument");
  334.     else
  335.       spooler = strsave(arg);
  336.   }
  337.   if (strcmp(command, "postpro") == 0) {
  338.     if (arg == 0)
  339.       error_with_file_and_line(filename, lineno,
  340.                    "`postpro' command requires an argument");
  341.     else {
  342.       for (const char *p = arg; *p; p++)
  343.     if (csspace(*p)) {
  344.       error_with_file_and_line(filename, lineno,
  345.                    "invalid `postpro' argument `%1'"
  346.                    ": program name required", arg);
  347.       return;
  348.     }
  349.       driver = strsave(arg);
  350.     }
  351.   }
  352. }
  353.  
  354. void print_commands()
  355. {
  356.   int last;
  357.   for (last = SPOOL_INDEX; last >= 0; last--)
  358.     if (commands[last].get_name() != 0)
  359.       break;
  360.   for (int i = 0; i <= last; i++)
  361.     if (commands[i].get_name() != 0)
  362.       commands[i].print(i == last, stdout);
  363. }
  364.  
  365. // Run the commands. Return the code with which to exit.
  366.  
  367. int run_commands()
  368. {
  369.   char **v[NCOMMANDS];
  370.   int j = 0;
  371.   for (int i = 0; i < NCOMMANDS; i++)
  372.     if (commands[i].get_name() != 0)
  373.       v[j++] = commands[i].get_argv();
  374.   return run_pipeline(j, v);
  375. }
  376.  
  377. possible_command::possible_command()
  378. : name(0), argv(0)
  379. {
  380. }
  381.  
  382. possible_command::~possible_command()
  383. {
  384.   a_delete name;
  385.   a_delete argv;
  386. }
  387.  
  388. void possible_command::set_name(const char *s)
  389. {
  390.   a_delete name;
  391.   name = strsave(s);
  392. }
  393.  
  394. void possible_command::set_name(const char *s1, const char *s2)
  395. {
  396.   a_delete name;
  397.   name = new char[strlen(s1) + strlen(s2) + 1];
  398.   strcpy(name, s1);
  399.   strcat(name, s2);
  400. }
  401.  
  402. const char *possible_command::get_name()
  403. {
  404.   return name;
  405. }
  406.  
  407. void possible_command::clear_args()
  408. {
  409.   args.clear();
  410. }
  411.  
  412. void possible_command::append_arg(const char *s, const char *t)
  413. {
  414.   args += s;
  415.   if (t)
  416.     args += t;
  417.   args += '\0';
  418. }
  419.  
  420. void possible_command::insert_arg(const char *s)
  421. {
  422.   string str(s);
  423.   str += '\0';
  424.   str += args;
  425.   args = str;
  426. }
  427.  
  428. void possible_command::build_argv()
  429. {
  430.   if (argv)
  431.     return;
  432.   // Count the number of arguments.
  433.   int len = args.length();
  434.   int argc = 1;
  435.   char *p = 0;
  436.   if (len > 0) {
  437.     p = &args[0];
  438.     for (int i = 0; i < len; i++)
  439.       if (p[i] == '\0')
  440.     argc++;
  441.   }
  442.   // Build an argument vector.
  443.   argv = new char *[argc + 1];
  444.   argv[0] = name;
  445.   for (int i = 1; i < argc; i++) {
  446.     argv[i] = p;
  447.     p = strchr(p, '\0') + 1;
  448.   }
  449.   argv[argc] = 0;
  450. }
  451.  
  452. void possible_command::print(int is_last, FILE *fp)
  453. {
  454.   build_argv();
  455.   if (argv[0] != 0 && strcmp(argv[0], BSHELL) == 0
  456.       && argv[1] != 0 && strcmp(argv[1], "-c") == 0
  457.       && argv[2] != 0 && argv[3] == 0)
  458.     fputs(argv[2], fp);
  459.   else {
  460.     fputs(argv[0], fp);
  461.     string str;
  462.     for (int i = 1; argv[i] != 0; i++) {
  463.       str.clear();
  464.       append_arg_to_string(argv[i], str);
  465.       put_string(str, fp);
  466.     }
  467.   }
  468.   if (is_last)
  469.     putc('\n', fp);
  470.   else
  471.     fputs(" | ", fp);
  472. }
  473.  
  474. void append_arg_to_string(const char *arg, string &str)
  475. {
  476.   str += ' ';
  477.   int needs_quoting = 0;
  478.   int contains_single_quote = 0;
  479.   const char*p;
  480.   for (p = arg; *p != '\0'; p++)
  481.     switch (*p) {
  482.     case ';':
  483.     case '&':
  484.     case '(':
  485.     case ')':
  486.     case '|':
  487.     case '^':
  488.     case '<':
  489.     case '>':
  490.     case '\n':
  491.     case ' ':
  492.     case '\t':
  493.     case '\\':
  494.     case '"':
  495.     case '$':
  496.     case '?':
  497.     case '*':
  498.       needs_quoting = 1;
  499.       break;
  500.     case '\'':
  501.       contains_single_quote = 1;
  502.       break;
  503.     }
  504.   if (contains_single_quote || arg[0] == '\0') {
  505.     str += '"';
  506.     for (p = arg; *p != '\0'; p++)
  507.       switch (*p) {
  508.       case '"':
  509.       case '\\':
  510.       case '$':
  511.     str += '\\';
  512.     // fall through
  513.       default:
  514.     str += *p;
  515.     break;
  516.       }
  517.     str += '"';
  518.   }
  519.   else if (needs_quoting) {
  520.     str += '\'';
  521.     str += arg;
  522.     str += '\'';
  523.   }
  524.   else
  525.     str += arg;
  526. }
  527.  
  528. char **possible_command::get_argv()
  529. {
  530.   build_argv();
  531.   return argv;
  532. }
  533.  
  534. void synopsis()
  535. {
  536.   fprintf(stderr,
  537. "usage: %s [-abehilpstvzCENRSVXZ] [-Fdir] [-mname] [-Tdev] [-ffam] [-wname]\n"
  538. "       [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg] [-Larg]\n"
  539. "       [files...]\n",
  540.       program_name);
  541. }
  542.  
  543. void help()
  544. {
  545.   synopsis();
  546.   fputs("\n"
  547. "-h\tprint this message\n"
  548. "-t\tpreprocess with tbl\n"
  549. "-p\tpreprocess with pic\n"
  550. "-e\tpreprocess with eqn\n"
  551. "-s\tpreprocess with soelim\n"
  552. "-R\tpreprocess with refer\n"
  553. "-Tdev\tuse device dev\n"
  554. "-X\tuse X11 previewer rather than usual postprocessor\n"
  555. "-mname\tread macros tmac.name\n"
  556. "-dcs\tdefine a string c as s\n"
  557. "-rcn\tdefine a number register c as n\n"
  558. "-nnum\tnumber first page n\n"
  559. "-olist\toutput only pages in list\n"
  560. "-ffam\tuse fam as the default font family\n"
  561. "-Fdir\tsearch directory dir for device directories\n"
  562. "-Mdir\tsearch dir for macro files\n"
  563. "-v\tprint version number\n"
  564. "-z\tsuppress formatted output\n"
  565. "-Z\tdon't postprocess\n"
  566. "-a\tproduce ASCII description of output\n"
  567. "-i\tread standard input after named input files\n"
  568. "-wname\tenable warning name\n"
  569. "-Wname\tinhibit warning name\n"
  570. "-E\tinhibit all errors\n"
  571. "-b\tprint backtraces with errors or warnings\n"
  572. "-l\tspool the output\n"
  573. "-C\tenable compatibility mode\n"
  574. "-V\tprint commands on stdout instead of running them\n"
  575. "-Parg\tpass arg to the postprocessor\n"
  576. "-Larg\tpass arg to the spooler\n"
  577. "-N\tdon't allow newlines within eqn delimiters\n"
  578. "-S\tenable safer mode\n"
  579. "\n",
  580.     stderr);
  581.   exit(0);
  582. }
  583.  
  584. void usage()
  585. {
  586.   synopsis();
  587.   fprintf(stderr, "%s -h gives more help\n", program_name);
  588.   exit(1);
  589. }
  590.  
  591. extern "C" {
  592.  
  593. void c_error(const char *format, const char *arg1, const char *arg2,
  594.          const char *arg3)
  595. {
  596.   error(format, arg1, arg2, arg3);
  597. }
  598.  
  599. void c_fatal(const char *format, const char *arg1, const char *arg2,
  600.          const char *arg3)
  601. {
  602.   fatal(format, arg1, arg2, arg3);
  603. }
  604.  
  605. }
  606.