2 /* Copyright (C) 1989-2000, 2001, 2002 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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
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
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. */
21 // A front end for groff.
32 #include "stringclass.h"
40 #define GXDITVIEW "gxditview"
42 // troff will be passed an argument of -rXREG=1 if the -X option is
46 #ifdef NEED_DECLARATION_PUTENV
48 int putenv(const char *);
50 #endif /* NEED_DECLARATION_PUTENV */
52 // The number of commands must be in sync with MAX_COMMANDS in pipeline.h
53 const int SOELIM_INDEX = 0;
54 const int REFER_INDEX = SOELIM_INDEX + 1;
55 const int GRAP_INDEX = REFER_INDEX + 1;
56 const int PIC_INDEX = GRAP_INDEX + 1;
57 const int TBL_INDEX = PIC_INDEX + 1;
58 const int GRN_INDEX = TBL_INDEX + 1;
59 const int EQN_INDEX = GRN_INDEX + 1;
60 const int TROFF_INDEX = EQN_INDEX + 1;
61 const int POST_INDEX = TROFF_INDEX + 1;
62 const int SPOOL_INDEX = POST_INDEX + 1;
64 const int NCOMMANDS = SPOOL_INDEX + 1;
66 class possible_command {
75 void set_name(const char *);
76 void set_name(const char *, const char *);
77 const char *get_name();
78 void append_arg(const char *, const char * = 0);
79 void insert_arg(const char *);
80 void insert_args(string s);
83 void print(int is_last, FILE *fp);
86 extern "C" const char *Version_string;
93 possible_command commands[NCOMMANDS];
95 int run_commands(int no_pipe);
96 void print_commands();
97 void append_arg_to_string(const char *arg, string &str);
98 void handle_unknown_desc_command(const char *command, const char *arg,
99 const char *filename, int lineno);
100 const char *xbasename(const char *);
102 void usage(FILE *stream);
105 int main(int argc, char **argv)
107 program_name = argv[0];
108 static char stderr_buf[BUFSIZ];
109 setbuf(stderr, stderr_buf);
110 assert(NCOMMANDS <= MAX_COMMANDS);
111 string Pargs, Largs, Fargs;
119 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
121 command_prefix = PROG_PREFIX;
122 commands[TROFF_INDEX].set_name(command_prefix, "troff");
123 static const struct option long_options[] = {
124 { "help", no_argument, 0, 'h' },
125 { "version", no_argument, 0, 'v' },
128 while ((opt = getopt_long(argc, argv,
129 "abcCd:eEf:F:gGhiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
141 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
142 commands[SOELIM_INDEX].append_arg(buf, optarg);
145 commands[TBL_INDEX].set_name(command_prefix, "tbl");
148 commands[PIC_INDEX].set_name(command_prefix, "pic");
151 commands[GRN_INDEX].set_name(command_prefix, "grn");
154 commands[GRAP_INDEX].set_name(command_prefix, "grap");
157 commands[EQN_INDEX].set_name(command_prefix, "eqn");
160 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
163 commands[REFER_INDEX].set_name(command_prefix, "refer");
167 commands[TROFF_INDEX].append_arg(buf);
181 printf("GNU groff version %s\n", Version_string);
182 printf("Copyright (C) 2002 Free Software Foundation, Inc.\n"
183 "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
184 "You may redistribute copies of groff and its subprograms\n"
185 "under the terms of the GNU General Public License.\n"
186 "For more information about these matters, see the file named COPYING.\n");
187 printf("\ncalled subprograms:\n\n");
190 commands[POST_INDEX].append_arg(buf);
193 commands[SOELIM_INDEX].append_arg(buf);
194 commands[REFER_INDEX].append_arg(buf);
195 commands[PIC_INDEX].append_arg(buf);
196 commands[GRAP_INDEX].append_arg(buf);
197 commands[TBL_INDEX].append_arg(buf);
198 commands[GRN_INDEX].append_arg(buf);
199 commands[EQN_INDEX].append_arg(buf);
200 commands[TROFF_INDEX].append_arg(buf);
203 commands[EQN_INDEX].append_arg(buf);
210 commands[TROFF_INDEX].append_arg(buf);
213 commands[TROFF_INDEX].append_arg(buf);
222 if (strcmp(optarg, "html") == 0) {
223 // force soelim to aid the html preprocessor
224 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
226 if (strcmp(optarg, "Xps") == 0) {
227 warning("-TXps option is obsolete: use -X -Tps instead");
235 font::command_line_font_dir(optarg);
236 if (Fargs.length() > 0) {
237 Fargs += PATH_SEP[0];
251 commands[TROFF_INDEX].append_arg(buf, optarg);
254 commands[EQN_INDEX].append_arg(buf, optarg);
255 commands[GRAP_INDEX].append_arg(buf, optarg);
256 commands[GRN_INDEX].append_arg(buf, optarg);
257 commands[TROFF_INDEX].append_arg(buf, optarg);
264 append_arg_to_string(optarg, Largs);
279 commands[PIC_INDEX].append_arg("-S");
281 commands[TROFF_INDEX].insert_arg("-U");
282 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
283 if (!font::load_desc())
284 fatal("invalid device `%1'", device);
286 fatal("no `postpro' command in DESC file for device `%1'", device);
287 if (predriver && !zflag) {
288 commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
289 commands[TROFF_INDEX].set_name(predriver);
290 // pass the device arguments to the predrivers as well
291 commands[TROFF_INDEX].insert_args(Pargs);
293 commands[TROFF_INDEX].insert_arg("-v");
295 const char *real_driver = 0;
297 real_driver = postdriver;
298 postdriver = GXDITVIEW;
299 commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
302 commands[POST_INDEX].set_name(postdriver);
303 int gxditview_flag = postdriver && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
304 if (gxditview_flag && argc - optind == 1) {
305 commands[POST_INDEX].append_arg("-title");
306 commands[POST_INDEX].append_arg(argv[optind]);
307 commands[POST_INDEX].append_arg("-xrm");
308 commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
309 string filename_string("|");
310 append_arg_to_string(argv[0], filename_string);
311 append_arg_to_string("-Z", filename_string);
312 for (int i = 1; i < argc; i++)
313 append_arg_to_string(argv[i], filename_string);
314 filename_string += '\0';
315 commands[POST_INDEX].append_arg("-filename");
316 commands[POST_INDEX].append_arg(filename_string.contents());
318 if (gxditview_flag && Xflag) {
319 string print_string(real_driver);
321 print_string += " | ";
322 print_string += spooler;
323 print_string += Largs;
325 print_string += '\0';
326 commands[POST_INDEX].append_arg("-printCommand");
327 commands[POST_INDEX].append_arg(print_string.contents());
329 const char *p = Pargs.contents();
330 const char *end = p + Pargs.length();
332 commands[POST_INDEX].append_arg(p);
333 p = strchr(p, '\0') + 1;
336 commands[POST_INDEX].append_arg("-");
337 if (lflag && !Xflag && spooler) {
338 commands[SPOOL_INDEX].set_name(BSHELL);
339 commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
341 Largs = spooler + Largs;
342 commands[SPOOL_INDEX].append_arg(Largs.contents());
345 commands[POST_INDEX].set_name(0);
346 commands[SPOOL_INDEX].set_name(0);
348 commands[TROFF_INDEX].append_arg("-T", device);
349 // html renders equations as images via ps
350 if (strcmp(device, "html") == 0)
351 commands[EQN_INDEX].append_arg("-Tps:html");
353 commands[EQN_INDEX].append_arg("-T", device);
355 commands[GRN_INDEX].append_arg("-T", device);
358 for (first_index = 0; first_index < TROFF_INDEX; first_index++)
359 if (commands[first_index].get_name() != 0)
362 if (argv[optind][0] == '-' && argv[optind][1] != '\0')
363 commands[first_index].append_arg("--");
364 for (int i = optind; i < argc; i++)
365 commands[first_index].append_arg(argv[i]);
367 commands[first_index].append_arg("-");
369 if (Fargs.length() > 0) {
370 string e = "GROFF_FONT_PATH";
373 char *fontpath = getenv("GROFF_FONT_PATH");
374 if (fontpath && *fontpath) {
379 if (putenv(strsave(e.contents())))
380 fatal("putenv failed");
383 // we save the original path in GROFF_PATH__ and put it into the
384 // environment -- troff will pick it up later.
385 char *path = getenv("PATH");
386 string e = "GROFF_PATH__";
391 if (putenv(strsave(e.contents())))
392 fatal("putenv failed");
393 char *binpath = getenv("GROFF_BIN_PATH");
396 if (binpath && *binpath)
405 if (putenv(strsave(f.contents())))
406 fatal("putenv failed");
412 return run_commands(vflag);
415 const char *xbasename(const char *s)
419 // DIR_SEPS[] are possible directory separator characters, see nonposix.h
420 // We want the rightmost separator of all possible ones.
421 // Example: d:/foo\\bar.
422 const char *p = strrchr(s, DIR_SEPS[0]), *p1;
423 const char *sep = &DIR_SEPS[1];
427 p1 = strrchr(s, *sep);
428 if (p1 && (!p || p1 > p))
432 return p ? p + 1 : s;
435 void handle_unknown_desc_command(const char *command, const char *arg,
436 const char *filename, int lineno)
438 if (strcmp(command, "print") == 0) {
440 error_with_file_and_line(filename, lineno,
441 "`print' command requires an argument");
443 spooler = strsave(arg);
445 if (strcmp(command, "prepro") == 0) {
447 error_with_file_and_line(filename, lineno,
448 "`prepro' command requires an argument");
450 for (const char *p = arg; *p; p++)
452 error_with_file_and_line(filename, lineno,
453 "invalid `prepro' argument `%1'"
454 ": program name required", arg);
457 predriver = strsave(arg);
460 if (strcmp(command, "postpro") == 0) {
462 error_with_file_and_line(filename, lineno,
463 "`postpro' command requires an argument");
465 for (const char *p = arg; *p; p++)
467 error_with_file_and_line(filename, lineno,
468 "invalid `postpro' argument `%1'"
469 ": program name required", arg);
472 postdriver = strsave(arg);
477 void print_commands()
480 for (last = SPOOL_INDEX; last >= 0; last--)
481 if (commands[last].get_name() != 0)
483 for (int i = 0; i <= last; i++)
484 if (commands[i].get_name() != 0)
485 commands[i].print(i == last, stdout);
488 // Run the commands. Return the code with which to exit.
490 int run_commands(int no_pipe)
494 for (int i = 0; i < NCOMMANDS; i++)
495 if (commands[i].get_name() != 0)
496 v[j++] = commands[i].get_argv();
497 return run_pipeline(j, v, no_pipe);
500 possible_command::possible_command()
505 possible_command::~possible_command()
511 void possible_command::set_name(const char *s)
517 void possible_command::set_name(const char *s1, const char *s2)
520 name = new char[strlen(s1) + strlen(s2) + 1];
525 const char *possible_command::get_name()
530 void possible_command::clear_args()
535 void possible_command::append_arg(const char *s, const char *t)
543 void possible_command::insert_arg(const char *s)
551 void possible_command::insert_args(string s)
553 const char *p = s.contents();
554 const char *end = p + s.length();
558 // find the total number of arguments in our string
561 p = strchr(p, '\0') + 1;
563 // now insert each argument preserving the order
564 for (int i = l - 1; i >= 0; i--) {
566 for (int j = 0; j < i; j++)
567 p = strchr(p, '\0') + 1;
572 void possible_command::build_argv()
576 // Count the number of arguments.
577 int len = args.length();
582 for (int i = 0; i < len; i++)
586 // Build an argument vector.
587 argv = new char *[argc + 1];
589 for (int i = 1; i < argc; i++) {
591 p = strchr(p, '\0') + 1;
596 void possible_command::print(int is_last, FILE *fp)
599 if (IS_BSHELL(argv[0])
600 && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
601 && argv[2] != 0 && argv[3] == 0)
606 for (int i = 1; argv[i] != 0; i++) {
608 append_arg_to_string(argv[i], str);
618 void append_arg_to_string(const char *arg, string &str)
621 int needs_quoting = 0;
622 int contains_single_quote = 0;
624 for (p = arg; *p != '\0'; p++)
645 contains_single_quote = 1;
648 if (contains_single_quote || arg[0] == '\0') {
650 for (p = arg; *p != '\0'; p++)
663 else if (needs_quoting) {
672 char **possible_command::get_argv()
678 void synopsis(FILE *stream)
681 "usage: %s [-abceghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
682 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
683 " [-Larg] [-Idir] [files...]\n",
691 "-h\tprint this message\n"
692 "-t\tpreprocess with tbl\n"
693 "-p\tpreprocess with pic\n"
694 "-e\tpreprocess with eqn\n"
695 "-g\tpreprocess with grn\n"
696 "-G\tpreprocess with grap\n"
697 "-s\tpreprocess with soelim\n"
698 "-R\tpreprocess with refer\n"
699 "-Tdev\tuse device dev\n"
700 "-X\tuse X11 previewer rather than usual postprocessor\n"
701 "-mname\tread macros tmac.name\n"
702 "-dcs\tdefine a string c as s\n"
703 "-rcn\tdefine a number register c as n\n"
704 "-nnum\tnumber first page n\n"
705 "-olist\toutput only pages in list\n"
706 "-ffam\tuse fam as the default font family\n"
707 "-Fdir\tsearch dir for device directories\n"
708 "-Mdir\tsearch dir for macro files\n"
709 "-v\tprint version number\n"
710 "-z\tsuppress formatted output\n"
711 "-Z\tdon't postprocess\n"
712 "-a\tproduce ASCII description of output\n"
713 "-i\tread standard input after named input files\n"
714 "-wname\tenable warning name\n"
715 "-Wname\tinhibit warning name\n"
716 "-E\tinhibit all errors\n"
717 "-b\tprint backtraces with errors or warnings\n"
718 "-l\tspool the output\n"
719 "-c\tdisable color output\n"
720 "-C\tenable compatibility mode\n"
721 "-V\tprint commands on stdout instead of running them\n"
722 "-Parg\tpass arg to the postprocessor\n"
723 "-Larg\tpass arg to the spooler\n"
724 "-N\tdon't allow newlines within eqn delimiters\n"
725 "-S\tenable safer mode (the default)\n"
726 "-U\tenable unsafe mode\n"
727 "-Idir\tsearch dir for soelim. Implies -s\n"
733 void usage(FILE *stream)
736 fprintf(stream, "%s -h gives more help\n", program_name);
741 void c_error(const char *format, const char *arg1, const char *arg2,
744 error(format, arg1, arg2, arg3);
747 void c_fatal(const char *format, const char *arg1, const char *arg2,
750 fatal(format, arg1, arg2, arg3);