home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / groff-1.09-src.lha / src / amiga / groff-1.09 / groff / groff.cc next >
C/C++ Source or Header  |  1994-02-21  |  14KB  |  589 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. // A front end for groff.
  22.  
  23. #include <stdio.h>
  24. #include </gnu/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 clear_args();
  78.   char **get_argv();
  79.   void print(int is_last, FILE *fp);
  80. };
  81.  
  82. int lflag = 0;
  83. char *spooler = 0;
  84. char *driver = 0;
  85.  
  86. possible_command commands[NCOMMANDS];
  87.  
  88. int run_commands();
  89. void print_commands();
  90. void append_arg_to_string(const char *arg, string &str);
  91. void handle_unknown_desc_command(const char *command, const char *arg,
  92.                  const char *filename, int lineno);
  93. const char *basename(const char *);
  94.  
  95. void usage();
  96. void help();
  97.  
  98. int main(int argc, char **argv)
  99. {
  100.   program_name = argv[0];
  101.   static char stderr_buf[BUFSIZ];
  102.   setbuf(stderr, stderr_buf);
  103.   assert(NCOMMANDS <= MAX_COMMANDS);
  104.   string Pargs, Largs, Fargs;
  105.   int Vflag = 0;
  106.   int zflag = 0;
  107.   int iflag = 0;
  108.   int Xflag = 0;
  109.   int opt;
  110.   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
  111.   if (!command_prefix)
  112.     command_prefix = PROG_PREFIX;
  113.   commands[TROFF_INDEX].set_name(command_prefix, "troff");
  114.   while ((opt = getopt(argc, argv,
  115.                "itpeRszavVhblCENXZF:m:T:f:w:W:M:d:r:n:o:P:L:"))
  116.      != EOF) {
  117.     char buf[3];
  118.     buf[0] = '-';
  119.     buf[1] = opt;
  120.     buf[2] = '\0';
  121.     switch (opt) {
  122.     case 'i':
  123.       iflag = 1;
  124.       break;
  125.     case 't':
  126.       commands[TBL_INDEX].set_name(command_prefix, "tbl");
  127.       break;
  128.     case 'p':
  129.       commands[PIC_INDEX].set_name(command_prefix, "pic");
  130.       break;
  131.     case 'e':
  132.       commands[EQN_INDEX].set_name(command_prefix, "eqn");
  133.       break;
  134.     case 's':
  135.       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
  136.       break;
  137.     case 'R':
  138.       commands[REFER_INDEX].set_name(command_prefix, "refer");
  139.       break;
  140.     case 'z':
  141.     case 'a':
  142.       commands[TROFF_INDEX].append_arg(buf);
  143.       // fall through
  144.     case 'Z':
  145.       zflag++;
  146.       break;
  147.     case 'l':
  148.       lflag++;
  149.       break;
  150.     case 'V':
  151.       Vflag++;
  152.       break;
  153.     case 'v':
  154.     case 'C':
  155.       commands[SOELIM_INDEX].append_arg(buf);
  156.       commands[PIC_INDEX].append_arg(buf);
  157.       commands[TBL_INDEX].append_arg(buf);
  158.       commands[EQN_INDEX].append_arg(buf);
  159.       commands[TROFF_INDEX].append_arg(buf);
  160.       break;
  161.     case 'N':
  162.       commands[EQN_INDEX].append_arg(buf);
  163.       break;
  164.     case 'h':
  165.       help();
  166.       break;
  167.     case 'E':
  168.     case 'b':
  169.       commands[TROFF_INDEX].append_arg(buf);
  170.       break;
  171.     case 'T':
  172.       if (strcmp(optarg, "Xps") == 0) {
  173.     warning("-TXps option is obsolete: use -X -Tps instead");
  174.     device = "ps";
  175.     Xflag++;
  176.       }
  177.       else
  178.     device = optarg;
  179.       break;
  180.     case 'F':
  181.       font::command_line_font_dir(optarg);
  182.       if (Fargs.length() > 0) {
  183.     Fargs += ':';
  184.     Fargs += optarg;
  185.       }
  186.       else
  187.     Fargs = optarg;
  188.       break;
  189.     case 'f':
  190.     case 'o':
  191.     case 'm':
  192.     case 'r':
  193.     case 'd':
  194.     case 'n':
  195.     case 'w':
  196.     case 'W':
  197.       commands[TROFF_INDEX].append_arg(buf, optarg);
  198.       break;
  199.     case 'M':
  200.       commands[EQN_INDEX].append_arg(buf, optarg);
  201.       commands[TROFF_INDEX].append_arg(buf, optarg);
  202.       break;
  203.     case 'P':
  204.       Pargs += optarg;
  205.       Pargs += '\0';
  206.       break;
  207.     case 'L':
  208.       append_arg_to_string(optarg, Largs);
  209.       break;
  210.     case 'X':
  211.       Xflag++;
  212.       break;
  213.     case '?':
  214.       usage();
  215.       break;
  216.     default:
  217.       assert(0);
  218.       break;
  219.     }
  220.   }
  221.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  222.   if (!font::load_desc())
  223.     fatal("invalid device `%1'", device);
  224.   if (!driver)
  225.     fatal("no `postpro' command in DESC file for device `%1'", device);
  226.   const char *real_driver = 0;
  227.   if (Xflag) {
  228.     real_driver = driver;
  229.     driver = GXDITVIEW;
  230.     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
  231.   }
  232.   if (driver)
  233.     commands[POST_INDEX].set_name(driver);
  234.   int gxditview_flag = driver && strcmp(basename(driver), GXDITVIEW) == 0;
  235.   if (gxditview_flag && argc - optind == 1) {
  236.     commands[POST_INDEX].append_arg("-title");
  237.     commands[POST_INDEX].append_arg(argv[optind]);
  238.     commands[POST_INDEX].append_arg("-xrm");
  239.     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
  240.     string filename_string("|");
  241.     append_arg_to_string(argv[0], filename_string);
  242.     append_arg_to_string("-Z", filename_string);
  243.     for (int i = 1; i < argc; i++)
  244.       append_arg_to_string(argv[i], filename_string);
  245.     filename_string += '\0';
  246.     commands[POST_INDEX].append_arg("-filename");
  247.     commands[POST_INDEX].append_arg(filename_string.contents());
  248.   }
  249.   if (gxditview_flag && Xflag) {
  250.     string print_string(real_driver);
  251.     if (spooler) {
  252.       print_string += " | ";
  253.       print_string += spooler;
  254.       print_string += Largs;
  255.     }
  256.     print_string += '\0';
  257.     commands[POST_INDEX].append_arg("-printCommand");
  258.     commands[POST_INDEX].append_arg(print_string.contents());
  259.   }
  260.   const char *p = Pargs.contents();
  261.   const char *end = p + Pargs.length();
  262.   while (p < end) {
  263.     commands[POST_INDEX].append_arg(p);
  264.     p = strchr(p, '\0') + 1;
  265.   }
  266.   if (gxditview_flag)
  267.     commands[POST_INDEX].append_arg("-");
  268.   if (lflag && !Xflag && spooler) {
  269.     commands[SPOOL_INDEX].set_name(BSHELL);
  270.     commands[SPOOL_INDEX].append_arg("-c");
  271.     Largs += '\0';
  272.     Largs = spooler + Largs;
  273.     commands[SPOOL_INDEX].append_arg(Largs.contents());
  274.   }
  275.   if (zflag) {
  276.     commands[POST_INDEX].set_name(0);
  277.     commands[SPOOL_INDEX].set_name(0);
  278.   }
  279.   commands[TROFF_INDEX].append_arg("-T", device);
  280.   commands[EQN_INDEX].append_arg("-T", device);
  281.  
  282.   for (int first_index = 0; first_index < TROFF_INDEX; first_index++)
  283.     if (commands[first_index].get_name() != 0)
  284.       break;
  285.   if (optind < argc) {
  286.     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
  287.       commands[first_index].append_arg("--");
  288.     for (int i = optind; i < argc; i++)
  289.       commands[first_index].append_arg(argv[i]);
  290.     if (iflag)
  291.       commands[first_index].append_arg("-");
  292.   }
  293.   if (Fargs.length() > 0) {
  294.     string e = "GROFF_FONT_PATH";
  295.     e += '=';
  296.     e += Fargs;
  297.     char *fontpath = getenv("GROFF_FONT_PATH");
  298.     if (fontpath && *fontpath) {
  299.       e += ':';
  300.       e += fontpath;
  301.     }
  302.     e += '\0';
  303.     if (putenv(strsave(e.contents())))
  304.       fatal("putenv failed");
  305.   }
  306.   if (Vflag) {
  307.     print_commands();
  308.     exit(0);
  309.   }
  310.   return run_commands();
  311. }
  312.  
  313. const char *basename(const char *s)
  314. {
  315.   if (!s)
  316.     return 0;
  317.   const char *p = strrchr(s, '/');
  318.   return p ? p + 1 : s;
  319. }
  320.  
  321. void handle_unknown_desc_command(const char *command, const char *arg,
  322.                  const char *filename, int lineno)
  323. {
  324.   if (strcmp(command, "print") == 0) {
  325.     if (arg == 0)
  326.       error_with_file_and_line(filename, lineno,
  327.                    "`print' command requires an argument");
  328.     else
  329.       spooler = strsave(arg);
  330.   }
  331.   if (strcmp(command, "postpro") == 0) {
  332.     if (arg == 0)
  333.       error_with_file_and_line(filename, lineno,
  334.                    "`postpro' command requires an argument");
  335.     else {
  336.       for (const char *p = arg; *p; p++)
  337.     if (csspace(*p)) {
  338.       error_with_file_and_line(filename, lineno,
  339.                    "invalid `postpro' argument `%1'"
  340.                    ": program name required", arg);
  341.       return;
  342.     }
  343.       driver = strsave(arg);
  344.     }
  345.   }
  346. }
  347.  
  348. void print_commands()
  349. {
  350.   for (int last = SPOOL_INDEX; last >= 0; last--)
  351.     if (commands[last].get_name() != 0)
  352.       break;
  353.   for (int i = 0; i <= last; i++)
  354.     if (commands[i].get_name() != 0)
  355.       commands[i].print(i == last, stdout);
  356. }
  357.  
  358. // Run the commands. Return the code with which to exit.
  359.  
  360. int run_commands()
  361. {
  362.   char **v[NCOMMANDS];
  363.   int j = 0;
  364.   for (int i = 0; i < NCOMMANDS; i++)
  365.     if (commands[i].get_name() != 0)
  366.       v[j++] = commands[i].get_argv();
  367.   return run_pipeline(j, v);
  368. }
  369.  
  370. possible_command::possible_command()
  371. : name(0), argv(0)
  372. {
  373. }
  374.  
  375. possible_command::~possible_command()
  376. {
  377.   a_delete name;
  378.   a_delete argv;
  379. }
  380.  
  381. void possible_command::set_name(const char *s)
  382. {
  383.   a_delete name;
  384.   name = strsave(s);
  385. }
  386.  
  387. void possible_command::set_name(const char *s1, const char *s2)
  388. {
  389.   a_delete name;
  390.   name = new char[strlen(s1) + strlen(s2) + 1];
  391.   strcpy(name, s1);
  392.   strcat(name, s2);
  393. }
  394.  
  395. const char *possible_command::get_name()
  396. {
  397.   return name;
  398. }
  399.  
  400. void possible_command::clear_args()
  401. {
  402.   args.clear();
  403. }
  404.  
  405. void possible_command::append_arg(const char *s, const char *t)
  406. {
  407.   args += s;
  408.   if (t)
  409.     args += t;
  410.   args += '\0';
  411. }
  412.  
  413. void possible_command::build_argv()
  414. {
  415.   if (argv)
  416.     return;
  417.   // Count the number of arguments.
  418.   int len = args.length();
  419.   int argc = 1;
  420.   char *p = 0;
  421.   if (len > 0) {
  422.     p = &args[0];
  423.     for (int i = 0; i < len; i++)
  424.       if (p[i] == '\0')
  425.     argc++;
  426.   }
  427.   // Build an argument vector.
  428.   argv = new char *[argc + 1];
  429.   argv[0] = name;
  430.   for (int i = 1; i < argc; i++) {
  431.     argv[i] = p;
  432.     p = strchr(p, '\0') + 1;
  433.   }
  434.   argv[argc] = 0;
  435. }
  436.  
  437. void possible_command::print(int is_last, FILE *fp)
  438. {
  439.   build_argv();
  440.   if (argv[0] != 0 && strcmp(argv[0], BSHELL) == 0
  441.       && argv[1] != 0 && strcmp(argv[1], "-c") == 0
  442.       && argv[2] != 0 && argv[3] == 0)
  443.     fputs(argv[2], fp);
  444.   else {
  445.     fputs(argv[0], fp);
  446.     string str;
  447.     for (int i = 1; argv[i] != 0; i++) {
  448.       str.clear();
  449.       append_arg_to_string(argv[i], str);
  450.       put_string(str, fp);
  451.     }
  452.   }
  453.   if (is_last)
  454.     putc('\n', fp);
  455.   else
  456.     fputs(" | ", fp);
  457. }
  458.  
  459. void append_arg_to_string(const char *arg, string &str)
  460. {
  461.   str += ' ';
  462.   int needs_quoting = 0;
  463.   int contains_single_quote = 0;
  464.   for (const char *p = arg; *p != '\0'; p++)
  465.     switch (*p) {
  466.     case ';':
  467.     case '&':
  468.     case '(':
  469.     case ')':
  470.     case '|':
  471.     case '^':
  472.     case '<':
  473.     case '>':
  474.     case '\n':
  475.     case ' ':
  476.     case '\t':
  477.     case '\\':
  478.     case '"':
  479.     case '$':
  480.     case '?':
  481.     case '*':
  482.       needs_quoting = 1;
  483.       break;
  484.     case '\'':
  485.       contains_single_quote = 1;
  486.       break;
  487.     }
  488.   if (contains_single_quote || arg[0] == '\0') {
  489.     str += '"';
  490.     for (p = arg; *p != '\0'; p++)
  491.       switch (*p) {
  492.       case '"':
  493.       case '\\':
  494.       case '$':
  495.     str += '\\';
  496.     // fall through
  497.       default:
  498.     str += *p;
  499.     break;
  500.       }
  501.     str += '"';
  502.   }
  503.   else if (needs_quoting) {
  504.     str += '\'';
  505.     str += arg;
  506.     str += '\'';
  507.   }
  508.   else
  509.     str += arg;
  510. }
  511.  
  512. char **possible_command::get_argv()
  513. {
  514.   build_argv();
  515.   return argv;
  516. }
  517.  
  518. void synopsis()
  519. {
  520.   fprintf(stderr,
  521. "usage: %s [-abehilpstvzCENRVXZ] [-Fdir] [-mname] [-Tdev] [-ffam] [-wname]\n"
  522. "       [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg] [-Larg]\n"
  523. "       [files...]\n",
  524.       program_name);
  525. }
  526.  
  527. void help()
  528. {
  529.   synopsis();
  530.   fputs("\n"
  531. "-h\tprint this message\n"
  532. "-t\tpreprocess with tbl\n"
  533. "-p\tpreprocess with pic\n"
  534. "-e\tpreprocess with eqn\n"
  535. "-s\tpreprocess with soelim\n"
  536. "-R\tpreprocess with refer\n"
  537. "-Tdev\tuse device dev\n"
  538. "-X\tuse X11 previewer rather than usual postprocessor\n"
  539. "-mname\tread macros tmac.name\n"
  540. "-dcs\tdefine a string c as s\n"
  541. "-rcn\tdefine a number register c as n\n"
  542. "-nnum\tnumber first page n\n"
  543. "-olist\toutput only pages in list\n"
  544. "-ffam\tuse fam as the default font family\n"
  545. "-Fdir\tsearch directory dir for device directories\n"
  546. "-Mdir\tsearch dir for macro files\n"
  547. "-v\tprint version number\n"
  548. "-z\tsuppress formatted output\n"
  549. "-Z\tdon't postprocess\n"
  550. "-a\tproduce ASCII description of output\n"
  551. "-i\tread standard input after named input files\n"
  552. "-wname\tenable warning name\n"
  553. "-Wname\tinhibit warning name\n"
  554. "-E\tinhibit all errors\n"
  555. "-b\tprint backtraces with errors or warnings\n"
  556. "-l\tspool the output\n"
  557. "-C\tenable compatibility mode\n"
  558. "-V\tprint commands on stdout instead of running them\n"
  559. "-Parg\tpass arg to the postprocessor\n"
  560. "-Larg\tpass arg to the spooler\n"
  561. "-N\tdon't allow newlines within eqn delimiters\n"
  562. "\n",
  563.     stderr);
  564.   exit(0);
  565. }
  566.  
  567. void usage()
  568. {
  569.   synopsis();
  570.   fprintf(stderr, "%s -h gives more help\n", program_name);
  571.   exit(1);
  572. }
  573.  
  574. extern "C" {
  575.  
  576. void c_error(const char *format, const char *arg1, const char *arg2,
  577.          const char *arg3)
  578. {
  579.   error(format, arg1, arg2, arg3);
  580. }
  581.  
  582. void c_fatal(const char *format, const char *arg1, const char *arg2,
  583.          const char *arg3)
  584. {
  585.   fatal(format, arg1, arg2, arg3);
  586. }
  587.  
  588. }
  589.