2 /* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 // A front end for groff.
33 #include "stringclass.h"
41 #define GXDITVIEW "gxditview"
43 // troff will be passed an argument of -rXREG=1 if the -X option is
47 #ifdef NEED_DECLARATION_PUTENV
49 int putenv(const char *);
51 #endif /* NEED_DECLARATION_PUTENV */
53 // The number of commands must be in sync with MAX_COMMANDS in pipeline.h
54 const int PRECONV_INDEX = 0;
55 const int SOELIM_INDEX = PRECONV_INDEX + 1;
56 const int REFER_INDEX = SOELIM_INDEX + 1;
57 const int GRAP_INDEX = REFER_INDEX + 1;
58 const int PIC_INDEX = GRAP_INDEX + 1;
59 const int TBL_INDEX = PIC_INDEX + 1;
60 const int GRN_INDEX = TBL_INDEX + 1;
61 const int EQN_INDEX = GRN_INDEX + 1;
62 const int TROFF_INDEX = EQN_INDEX + 1;
63 const int POST_INDEX = TROFF_INDEX + 1;
64 const int SPOOL_INDEX = POST_INDEX + 1;
66 const int NCOMMANDS = SPOOL_INDEX + 1;
68 class possible_command {
78 void set_name(const char *);
79 void set_name(const char *, const char *);
80 const char *get_name();
81 void append_arg(const char *, const char * = 0);
82 void insert_arg(const char *);
83 void insert_args(string s);
86 void print(int is_last, FILE *fp);
89 extern "C" const char *Version_string;
96 possible_command commands[NCOMMANDS];
98 int run_commands(int no_pipe);
99 void print_commands(FILE *);
100 void append_arg_to_string(const char *arg, string &str);
101 void handle_unknown_desc_command(const char *command, const char *arg,
102 const char *filename, int lineno);
103 const char *xbasename(const char *);
105 void usage(FILE *stream);
108 int main(int argc, char **argv)
110 program_name = argv[0];
111 static char stderr_buf[BUFSIZ];
112 setbuf(stderr, stderr_buf);
113 assert(NCOMMANDS <= MAX_COMMANDS);
114 string Pargs, Largs, Fargs;
126 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
127 const char *encoding = getenv("GROFF_ENCODING");
129 command_prefix = PROG_PREFIX;
130 commands[TROFF_INDEX].set_name(command_prefix, "troff");
131 static const struct option long_options[] = {
132 { "help", no_argument, 0, 'h' },
133 { "version", no_argument, 0, 'v' },
136 while ((opt = getopt_long(
138 "abcCd:D:eEf:F:gGhiI:lkK:L:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
150 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
151 commands[SOELIM_INDEX].append_arg(buf, optarg);
152 // .psbb may need to search for files
153 commands[TROFF_INDEX].append_arg(buf, optarg);
154 // \X'ps:import' may need to search for files
160 commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
161 commands[PRECONV_INDEX].append_arg("-D", optarg);
164 commands[PRECONV_INDEX].append_arg("-e", optarg);
168 commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
171 commands[TBL_INDEX].set_name(command_prefix, "tbl");
174 commands[PIC_INDEX].set_name(command_prefix, "pic");
177 commands[GRN_INDEX].set_name(command_prefix, "grn");
180 commands[GRAP_INDEX].set_name(command_prefix, "grap");
184 commands[EQN_INDEX].set_name(command_prefix, "eqn");
187 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
190 commands[REFER_INDEX].set_name(command_prefix, "refer");
194 commands[TROFF_INDEX].append_arg(buf);
207 printf("GNU groff version %s\n", Version_string);
209 "Copyright (C) 2009 Free Software Foundation, Inc.\n"
210 "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
211 "You may redistribute copies of groff and its subprograms\n"
212 "under the terms of the GNU General Public License.\n"
213 "For more information about these matters, see the file named COPYING.\n");
214 printf("\ncalled subprograms:\n\n");
216 commands[POST_INDEX].append_arg(buf);
219 commands[SOELIM_INDEX].append_arg(buf);
220 commands[REFER_INDEX].append_arg(buf);
221 commands[PIC_INDEX].append_arg(buf);
222 commands[GRAP_INDEX].append_arg(buf);
223 commands[TBL_INDEX].append_arg(buf);
224 commands[GRN_INDEX].append_arg(buf);
225 commands[EQN_INDEX].append_arg(buf);
226 commands[TROFF_INDEX].append_arg(buf);
229 commands[EQN_INDEX].append_arg(buf);
236 commands[TROFF_INDEX].append_arg(buf);
239 commands[TROFF_INDEX].append_arg(buf);
248 if (strcmp(optarg, "xhtml") == 0) {
249 // force soelim to aid the html preprocessor
250 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
259 if (strcmp(optarg, "html") == 0)
260 // force soelim to aid the html preprocessor
261 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
263 if (strcmp(optarg, "Xps") == 0) {
264 warning("-TXps option is obsolete: use -X -Tps instead");
272 font::command_line_font_dir(optarg);
273 if (Fargs.length() > 0) {
274 Fargs += PATH_SEP_CHAR;
289 commands[TROFF_INDEX].append_arg(buf, optarg);
292 commands[EQN_INDEX].append_arg(buf, optarg);
293 commands[GRAP_INDEX].append_arg(buf, optarg);
294 commands[GRN_INDEX].append_arg(buf, optarg);
295 commands[TROFF_INDEX].append_arg(buf, optarg);
302 append_arg_to_string(optarg, Largs);
317 commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
318 if (!Kflag && *encoding)
319 commands[PRECONV_INDEX].append_arg("-e", encoding);
322 commands[TROFF_INDEX].insert_arg("-U");
323 commands[PIC_INDEX].append_arg("-U");
325 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
326 if (!font::load_desc())
327 fatal("invalid device `%1'", device);
329 fatal("no `postpro' command in DESC file for device `%1'", device);
330 if (predriver && !zflag) {
331 commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
332 commands[TROFF_INDEX].set_name(predriver);
333 // pass the device arguments to the predrivers as well
334 commands[TROFF_INDEX].insert_args(Pargs);
335 if (eflag && is_xhtml)
336 commands[TROFF_INDEX].insert_arg("-e");
338 commands[TROFF_INDEX].insert_arg("-v");
340 const char *real_driver = 0;
342 real_driver = postdriver;
343 postdriver = (char *)GXDITVIEW;
344 commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
347 commands[POST_INDEX].set_name(postdriver);
348 int gxditview_flag = postdriver
349 && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
350 if (gxditview_flag && argc - optind == 1) {
351 commands[POST_INDEX].append_arg("-title");
352 commands[POST_INDEX].append_arg(argv[optind]);
353 commands[POST_INDEX].append_arg("-xrm");
354 commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
355 string filename_string("|");
356 append_arg_to_string(argv[0], filename_string);
357 append_arg_to_string("-Z", filename_string);
358 for (int i = 1; i < argc; i++)
359 append_arg_to_string(argv[i], filename_string);
360 filename_string += '\0';
361 commands[POST_INDEX].append_arg("-filename");
362 commands[POST_INDEX].append_arg(filename_string.contents());
364 if (gxditview_flag && Xflag) {
365 string print_string(real_driver);
367 print_string += " | ";
368 print_string += spooler;
369 print_string += Largs;
371 print_string += '\0';
372 commands[POST_INDEX].append_arg("-printCommand");
373 commands[POST_INDEX].append_arg(print_string.contents());
375 const char *p = Pargs.contents();
376 const char *end = p + Pargs.length();
378 commands[POST_INDEX].append_arg(p);
379 p = strchr(p, '\0') + 1;
382 commands[POST_INDEX].append_arg("-");
383 if (lflag && !vflag && !Xflag && spooler) {
384 commands[SPOOL_INDEX].set_name(BSHELL);
385 commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
387 Largs = spooler + Largs;
388 commands[SPOOL_INDEX].append_arg(Largs.contents());
391 commands[POST_INDEX].set_name(0);
392 commands[SPOOL_INDEX].set_name(0);
394 commands[TROFF_INDEX].append_arg("-T", device);
395 if (strcmp(device, "html") == 0) {
398 fatal("`-o' option is invalid with device `xhtml'");
400 commands[EQN_INDEX].append_arg("-Tmathml:xhtml");
402 commands[EQN_INDEX].clear_name();
406 fatal("`-o' option is invalid with device `html'");
407 // html renders equations as images via ps
408 commands[EQN_INDEX].append_arg("-Tps:html");
412 commands[EQN_INDEX].append_arg("-T", device);
414 commands[GRN_INDEX].append_arg("-T", device);
417 for (first_index = 0; first_index < TROFF_INDEX; first_index++)
418 if (commands[first_index].get_name() != 0)
421 if (argv[optind][0] == '-' && argv[optind][1] != '\0')
422 commands[first_index].append_arg("--");
423 for (int i = optind; i < argc; i++)
424 commands[first_index].append_arg(argv[i]);
426 commands[first_index].append_arg("-");
428 if (Fargs.length() > 0) {
429 string e = "GROFF_FONT_PATH";
432 char *fontpath = getenv("GROFF_FONT_PATH");
433 if (fontpath && *fontpath) {
438 if (putenv(strsave(e.contents())))
439 fatal("putenv failed");
442 // we save the original path in GROFF_PATH__ and put it into the
443 // environment -- troff will pick it up later.
444 char *path = getenv("PATH");
445 string e = "GROFF_PATH__";
450 if (putenv(strsave(e.contents())))
451 fatal("putenv failed");
452 char *binpath = getenv("GROFF_BIN_PATH");
455 if (binpath && *binpath)
464 if (putenv(strsave(f.contents())))
465 fatal("putenv failed");
468 print_commands(Vflag == 1 ? stdout : stderr);
471 return run_commands(vflag);
474 const char *xbasename(const char *s)
478 // DIR_SEPS[] are possible directory separator characters, see nonposix.h
479 // We want the rightmost separator of all possible ones.
480 // Example: d:/foo\\bar.
481 const char *p = strrchr(s, DIR_SEPS[0]), *p1;
482 const char *sep = &DIR_SEPS[1];
486 p1 = strrchr(s, *sep);
487 if (p1 && (!p || p1 > p))
491 return p ? p + 1 : s;
494 void handle_unknown_desc_command(const char *command, const char *arg,
495 const char *filename, int lineno)
497 if (strcmp(command, "print") == 0) {
499 error_with_file_and_line(filename, lineno,
500 "`print' command requires an argument");
502 spooler = strsave(arg);
504 if (strcmp(command, "prepro") == 0) {
506 error_with_file_and_line(filename, lineno,
507 "`prepro' command requires an argument");
509 for (const char *p = arg; *p; p++)
511 error_with_file_and_line(filename, lineno,
512 "invalid `prepro' argument `%1'"
513 ": program name required", arg);
516 predriver = strsave(arg);
519 if (strcmp(command, "postpro") == 0) {
521 error_with_file_and_line(filename, lineno,
522 "`postpro' command requires an argument");
524 for (const char *p = arg; *p; p++)
526 error_with_file_and_line(filename, lineno,
527 "invalid `postpro' argument `%1'"
528 ": program name required", arg);
531 postdriver = strsave(arg);
536 void print_commands(FILE *fp)
539 for (last = SPOOL_INDEX; last >= 0; last--)
540 if (commands[last].get_name() != 0)
542 for (int i = 0; i <= last; i++)
543 if (commands[i].get_name() != 0)
544 commands[i].print(i == last, fp);
547 // Run the commands. Return the code with which to exit.
549 int run_commands(int no_pipe)
553 for (int i = 0; i < NCOMMANDS; i++)
554 if (commands[i].get_name() != 0)
555 v[j++] = commands[i].get_argv();
556 return run_pipeline(j, v, no_pipe);
559 possible_command::possible_command()
564 possible_command::~possible_command()
570 void possible_command::set_name(const char *s)
576 void possible_command::clear_name()
584 void possible_command::set_name(const char *s1, const char *s2)
587 name = new char[strlen(s1) + strlen(s2) + 1];
592 const char *possible_command::get_name()
597 void possible_command::clear_args()
602 void possible_command::append_arg(const char *s, const char *t)
610 void possible_command::insert_arg(const char *s)
618 void possible_command::insert_args(string s)
620 const char *p = s.contents();
621 const char *end = p + s.length();
625 // find the total number of arguments in our string
628 p = strchr(p, '\0') + 1;
630 // now insert each argument preserving the order
631 for (int i = l - 1; i >= 0; i--) {
633 for (int j = 0; j < i; j++)
634 p = strchr(p, '\0') + 1;
639 void possible_command::build_argv()
643 // Count the number of arguments.
644 int len = args.length();
649 for (int i = 0; i < len; i++)
653 // Build an argument vector.
654 argv = new char *[argc + 1];
656 for (int i = 1; i < argc; i++) {
658 p = strchr(p, '\0') + 1;
663 void possible_command::print(int is_last, FILE *fp)
666 if (IS_BSHELL(argv[0])
667 && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
668 && argv[2] != 0 && argv[3] == 0)
673 for (int i = 1; argv[i] != 0; i++) {
675 append_arg_to_string(argv[i], str);
685 void append_arg_to_string(const char *arg, string &str)
688 int needs_quoting = 0;
689 int contains_single_quote = 0;
691 for (p = arg; *p != '\0'; p++)
712 contains_single_quote = 1;
715 if (contains_single_quote || arg[0] == '\0') {
717 for (p = arg; *p != '\0'; p++)
730 else if (needs_quoting) {
739 char **possible_command::get_argv()
745 void synopsis(FILE *stream)
748 "usage: %s [-abceghiklpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
749 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
750 " [-Darg] [-Karg] [-Larg] [-Idir] [files...]\n",
758 "-h\tprint this message\n"
759 "-k\tpreprocess with preconv\n"
760 "-t\tpreprocess with tbl\n"
761 "-p\tpreprocess with pic\n"
762 "-e\tpreprocess with eqn\n"
763 "-g\tpreprocess with grn\n"
764 "-G\tpreprocess with grap\n"
765 "-s\tpreprocess with soelim\n"
766 "-R\tpreprocess with refer\n"
767 "-Tdev\tuse device dev\n"
768 "-X\tuse X11 previewer rather than usual postprocessor\n"
769 "-mname\tread macros tmac.name\n"
770 "-dcs\tdefine a string c as s\n"
771 "-rcn\tdefine a number register c as n\n"
772 "-nnum\tnumber first page n\n"
773 "-olist\toutput only pages in list\n"
774 "-ffam\tuse fam as the default font family\n"
775 "-Fdir\tsearch dir for device directories\n"
776 "-Mdir\tsearch dir for macro files\n"
777 "-v\tprint version number\n"
778 "-z\tsuppress formatted output\n"
779 "-Z\tdon't postprocess\n"
780 "-a\tproduce ASCII description of output\n"
781 "-i\tread standard input after named input files\n"
782 "-wname\tenable warning name\n"
783 "-Wname\tinhibit warning name\n"
784 "-E\tinhibit all errors\n"
785 "-b\tprint backtraces with errors or warnings\n"
786 "-l\tspool the output\n"
787 "-c\tdisable color output\n"
788 "-C\tenable compatibility mode\n"
789 "-V\tprint commands on stdout instead of running them\n"
790 "-Parg\tpass arg to the postprocessor\n"
791 "-Larg\tpass arg to the spooler\n"
792 "-N\tdon't allow newlines within eqn delimiters\n"
793 "-S\tenable safer mode (the default)\n"
794 "-U\tenable unsafe mode\n"
795 "-Idir\tsearch dir for soelim, troff, and grops. Implies -s\n"
796 "-Karg\tuse arg as input encoding. Implies -k\n"
797 "-Darg\tuse arg as default input encoding. Implies -k\n"
803 void usage(FILE *stream)
806 fprintf(stream, "%s -h gives more help\n", program_name);
811 void c_error(const char *format, const char *arg1, const char *arg2,
814 error(format, arg1, arg2, arg3);
817 void c_fatal(const char *format, const char *arg1, const char *arg2,
820 fatal(format, arg1, arg2, arg3);