2 /* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
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 SOELIM_INDEX = 0;
55 const int REFER_INDEX = SOELIM_INDEX + 1;
56 const int GRAP_INDEX = REFER_INDEX + 1;
57 const int PIC_INDEX = GRAP_INDEX + 1;
58 const int TBL_INDEX = PIC_INDEX + 1;
59 const int GRN_INDEX = TBL_INDEX + 1;
60 const int EQN_INDEX = GRN_INDEX + 1;
61 const int TROFF_INDEX = EQN_INDEX + 1;
62 const int POST_INDEX = TROFF_INDEX + 1;
63 const int SPOOL_INDEX = POST_INDEX + 1;
65 const int NCOMMANDS = SPOOL_INDEX + 1;
67 class possible_command {
76 void set_name(const char *);
77 void set_name(const char *, const char *);
78 const char *get_name();
79 void append_arg(const char *, const char * = 0);
80 void insert_arg(const char *);
81 void insert_args(string s);
84 void print(int is_last, FILE *fp);
87 extern "C" const char *Version_string;
94 possible_command commands[NCOMMANDS];
96 int run_commands(int no_pipe);
97 void print_commands(FILE *);
98 void append_arg_to_string(const char *arg, string &str);
99 void handle_unknown_desc_command(const char *command, const char *arg,
100 const char *filename, int lineno);
101 const char *xbasename(const char *);
103 void usage(FILE *stream);
106 int main(int argc, char **argv)
108 program_name = argv[0];
109 static char stderr_buf[BUFSIZ];
110 setbuf(stderr, stderr_buf);
111 assert(NCOMMANDS <= MAX_COMMANDS);
112 string Pargs, Largs, Fargs;
121 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
123 command_prefix = PROG_PREFIX;
124 commands[TROFF_INDEX].set_name(command_prefix, "troff");
125 static const struct option long_options[] = {
126 { "help", no_argument, 0, 'h' },
127 { "version", no_argument, 0, 'v' },
130 while ((opt = getopt_long(argc, argv,
131 "abcCd:eEf:F:gGhiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
143 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
144 commands[SOELIM_INDEX].append_arg(buf, optarg);
145 // .psbb may need to search for files
146 commands[TROFF_INDEX].append_arg(buf, optarg);
147 // \X'ps:import' may need to search for files
153 commands[TBL_INDEX].set_name(command_prefix, "tbl");
156 commands[PIC_INDEX].set_name(command_prefix, "pic");
159 commands[GRN_INDEX].set_name(command_prefix, "grn");
162 commands[GRAP_INDEX].set_name(command_prefix, "grap");
165 commands[EQN_INDEX].set_name(command_prefix, "eqn");
168 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
171 commands[REFER_INDEX].set_name(command_prefix, "refer");
175 commands[TROFF_INDEX].append_arg(buf);
189 printf("GNU groff version %s\n", Version_string);
190 printf("Copyright (C) 2004 Free Software Foundation, Inc.\n"
191 "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
192 "You may redistribute copies of groff and its subprograms\n"
193 "under the terms of the GNU General Public License.\n"
194 "For more information about these matters, see the file named COPYING.\n");
195 printf("\ncalled subprograms:\n\n");
198 commands[POST_INDEX].append_arg(buf);
201 commands[SOELIM_INDEX].append_arg(buf);
202 commands[REFER_INDEX].append_arg(buf);
203 commands[PIC_INDEX].append_arg(buf);
204 commands[GRAP_INDEX].append_arg(buf);
205 commands[TBL_INDEX].append_arg(buf);
206 commands[GRN_INDEX].append_arg(buf);
207 commands[EQN_INDEX].append_arg(buf);
208 commands[TROFF_INDEX].append_arg(buf);
211 commands[EQN_INDEX].append_arg(buf);
218 commands[TROFF_INDEX].append_arg(buf);
221 commands[TROFF_INDEX].append_arg(buf);
230 if (strcmp(optarg, "html") == 0) {
231 // force soelim to aid the html preprocessor
232 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
234 if (strcmp(optarg, "Xps") == 0) {
235 warning("-TXps option is obsolete: use -X -Tps instead");
243 font::command_line_font_dir(optarg);
244 if (Fargs.length() > 0) {
245 Fargs += PATH_SEP_CHAR;
260 commands[TROFF_INDEX].append_arg(buf, optarg);
263 commands[EQN_INDEX].append_arg(buf, optarg);
264 commands[GRAP_INDEX].append_arg(buf, optarg);
265 commands[GRN_INDEX].append_arg(buf, optarg);
266 commands[TROFF_INDEX].append_arg(buf, optarg);
273 append_arg_to_string(optarg, Largs);
288 commands[PIC_INDEX].append_arg("-S");
290 commands[TROFF_INDEX].insert_arg("-U");
291 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
292 if (!font::load_desc())
293 fatal("invalid device `%1'", device);
295 fatal("no `postpro' command in DESC file for device `%1'", device);
296 if (predriver && !zflag) {
297 commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
298 commands[TROFF_INDEX].set_name(predriver);
299 // pass the device arguments to the predrivers as well
300 commands[TROFF_INDEX].insert_args(Pargs);
302 commands[TROFF_INDEX].insert_arg("-v");
304 const char *real_driver = 0;
306 real_driver = postdriver;
307 postdriver = (char *)GXDITVIEW;
308 commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
311 commands[POST_INDEX].set_name(postdriver);
312 int gxditview_flag = postdriver && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
313 if (gxditview_flag && argc - optind == 1) {
314 commands[POST_INDEX].append_arg("-title");
315 commands[POST_INDEX].append_arg(argv[optind]);
316 commands[POST_INDEX].append_arg("-xrm");
317 commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
318 string filename_string("|");
319 append_arg_to_string(argv[0], filename_string);
320 append_arg_to_string("-Z", filename_string);
321 for (int i = 1; i < argc; i++)
322 append_arg_to_string(argv[i], filename_string);
323 filename_string += '\0';
324 commands[POST_INDEX].append_arg("-filename");
325 commands[POST_INDEX].append_arg(filename_string.contents());
327 if (gxditview_flag && Xflag) {
328 string print_string(real_driver);
330 print_string += " | ";
331 print_string += spooler;
332 print_string += Largs;
334 print_string += '\0';
335 commands[POST_INDEX].append_arg("-printCommand");
336 commands[POST_INDEX].append_arg(print_string.contents());
338 const char *p = Pargs.contents();
339 const char *end = p + Pargs.length();
341 commands[POST_INDEX].append_arg(p);
342 p = strchr(p, '\0') + 1;
345 commands[POST_INDEX].append_arg("-");
346 if (lflag && !vflag && !Xflag && spooler) {
347 commands[SPOOL_INDEX].set_name(BSHELL);
348 commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
350 Largs = spooler + Largs;
351 commands[SPOOL_INDEX].append_arg(Largs.contents());
354 commands[POST_INDEX].set_name(0);
355 commands[SPOOL_INDEX].set_name(0);
357 commands[TROFF_INDEX].append_arg("-T", device);
358 // html renders equations as images via ps
359 if (strcmp(device, "html") == 0) {
361 fatal("`-o' option is invalid with device `html'");
362 commands[EQN_INDEX].append_arg("-Tps:html");
365 commands[EQN_INDEX].append_arg("-T", device);
367 commands[GRN_INDEX].append_arg("-T", device);
370 for (first_index = 0; first_index < TROFF_INDEX; first_index++)
371 if (commands[first_index].get_name() != 0)
374 if (argv[optind][0] == '-' && argv[optind][1] != '\0')
375 commands[first_index].append_arg("--");
376 for (int i = optind; i < argc; i++)
377 commands[first_index].append_arg(argv[i]);
379 commands[first_index].append_arg("-");
381 if (Fargs.length() > 0) {
382 string e = "GROFF_FONT_PATH";
385 char *fontpath = getenv("GROFF_FONT_PATH");
386 if (fontpath && *fontpath) {
391 if (putenv(strsave(e.contents())))
392 fatal("putenv failed");
395 // we save the original path in GROFF_PATH__ and put it into the
396 // environment -- troff will pick it up later.
397 char *path = getenv("PATH");
398 string e = "GROFF_PATH__";
403 if (putenv(strsave(e.contents())))
404 fatal("putenv failed");
405 char *binpath = getenv("GROFF_BIN_PATH");
408 if (binpath && *binpath)
417 if (putenv(strsave(f.contents())))
418 fatal("putenv failed");
421 print_commands(Vflag == 1 ? stdout : stderr);
424 return run_commands(vflag);
427 const char *xbasename(const char *s)
431 // DIR_SEPS[] are possible directory separator characters, see nonposix.h
432 // We want the rightmost separator of all possible ones.
433 // Example: d:/foo\\bar.
434 const char *p = strrchr(s, DIR_SEPS[0]), *p1;
435 const char *sep = &DIR_SEPS[1];
439 p1 = strrchr(s, *sep);
440 if (p1 && (!p || p1 > p))
444 return p ? p + 1 : s;
447 void handle_unknown_desc_command(const char *command, const char *arg,
448 const char *filename, int lineno)
450 if (strcmp(command, "print") == 0) {
452 error_with_file_and_line(filename, lineno,
453 "`print' command requires an argument");
455 spooler = strsave(arg);
457 if (strcmp(command, "prepro") == 0) {
459 error_with_file_and_line(filename, lineno,
460 "`prepro' command requires an argument");
462 for (const char *p = arg; *p; p++)
464 error_with_file_and_line(filename, lineno,
465 "invalid `prepro' argument `%1'"
466 ": program name required", arg);
469 predriver = strsave(arg);
472 if (strcmp(command, "postpro") == 0) {
474 error_with_file_and_line(filename, lineno,
475 "`postpro' command requires an argument");
477 for (const char *p = arg; *p; p++)
479 error_with_file_and_line(filename, lineno,
480 "invalid `postpro' argument `%1'"
481 ": program name required", arg);
484 postdriver = strsave(arg);
489 void print_commands(FILE *fp)
492 for (last = SPOOL_INDEX; last >= 0; last--)
493 if (commands[last].get_name() != 0)
495 for (int i = 0; i <= last; i++)
496 if (commands[i].get_name() != 0)
497 commands[i].print(i == last, fp);
500 // Run the commands. Return the code with which to exit.
502 int run_commands(int no_pipe)
506 for (int i = 0; i < NCOMMANDS; i++)
507 if (commands[i].get_name() != 0)
508 v[j++] = commands[i].get_argv();
509 return run_pipeline(j, v, no_pipe);
512 possible_command::possible_command()
517 possible_command::~possible_command()
523 void possible_command::set_name(const char *s)
529 void possible_command::set_name(const char *s1, const char *s2)
532 name = new char[strlen(s1) + strlen(s2) + 1];
537 const char *possible_command::get_name()
542 void possible_command::clear_args()
547 void possible_command::append_arg(const char *s, const char *t)
555 void possible_command::insert_arg(const char *s)
563 void possible_command::insert_args(string s)
565 const char *p = s.contents();
566 const char *end = p + s.length();
570 // find the total number of arguments in our string
573 p = strchr(p, '\0') + 1;
575 // now insert each argument preserving the order
576 for (int i = l - 1; i >= 0; i--) {
578 for (int j = 0; j < i; j++)
579 p = strchr(p, '\0') + 1;
584 void possible_command::build_argv()
588 // Count the number of arguments.
589 int len = args.length();
594 for (int i = 0; i < len; i++)
598 // Build an argument vector.
599 argv = new char *[argc + 1];
601 for (int i = 1; i < argc; i++) {
603 p = strchr(p, '\0') + 1;
608 void possible_command::print(int is_last, FILE *fp)
611 if (IS_BSHELL(argv[0])
612 && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
613 && argv[2] != 0 && argv[3] == 0)
618 for (int i = 1; argv[i] != 0; i++) {
620 append_arg_to_string(argv[i], str);
630 void append_arg_to_string(const char *arg, string &str)
633 int needs_quoting = 0;
634 int contains_single_quote = 0;
636 for (p = arg; *p != '\0'; p++)
657 contains_single_quote = 1;
660 if (contains_single_quote || arg[0] == '\0') {
662 for (p = arg; *p != '\0'; p++)
675 else if (needs_quoting) {
684 char **possible_command::get_argv()
690 void synopsis(FILE *stream)
693 "usage: %s [-abceghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
694 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
695 " [-Larg] [-Idir] [files...]\n",
703 "-h\tprint this message\n"
704 "-t\tpreprocess with tbl\n"
705 "-p\tpreprocess with pic\n"
706 "-e\tpreprocess with eqn\n"
707 "-g\tpreprocess with grn\n"
708 "-G\tpreprocess with grap\n"
709 "-s\tpreprocess with soelim\n"
710 "-R\tpreprocess with refer\n"
711 "-Tdev\tuse device dev\n"
712 "-X\tuse X11 previewer rather than usual postprocessor\n"
713 "-mname\tread macros tmac.name\n"
714 "-dcs\tdefine a string c as s\n"
715 "-rcn\tdefine a number register c as n\n"
716 "-nnum\tnumber first page n\n"
717 "-olist\toutput only pages in list\n"
718 "-ffam\tuse fam as the default font family\n"
719 "-Fdir\tsearch dir for device directories\n"
720 "-Mdir\tsearch dir for macro files\n"
721 "-v\tprint version number\n"
722 "-z\tsuppress formatted output\n"
723 "-Z\tdon't postprocess\n"
724 "-a\tproduce ASCII description of output\n"
725 "-i\tread standard input after named input files\n"
726 "-wname\tenable warning name\n"
727 "-Wname\tinhibit warning name\n"
728 "-E\tinhibit all errors\n"
729 "-b\tprint backtraces with errors or warnings\n"
730 "-l\tspool the output\n"
731 "-c\tdisable color output\n"
732 "-C\tenable compatibility mode\n"
733 "-V\tprint commands on stdout instead of running them\n"
734 "-Parg\tpass arg to the postprocessor\n"
735 "-Larg\tpass arg to the spooler\n"
736 "-N\tdon't allow newlines within eqn delimiters\n"
737 "-S\tenable safer mode (the default)\n"
738 "-U\tenable unsafe mode\n"
739 "-Idir\tsearch dir for soelim, troff, and grops. Implies -s\n"
745 void usage(FILE *stream)
748 fprintf(stream, "%s -h gives more help\n", program_name);
753 void c_error(const char *format, const char *arg1, const char *arg2,
756 error(format, arg1, arg2, arg3);
759 void c_fatal(const char *format, const char *arg1, const char *arg2,
762 fatal(format, arg1, arg2, arg3);