1 /*@ A front end for S-roff.
3 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
5 * Copyright (C) 1989 - 2008
6 * Free Software Foundation, Inc.
7 * Written by James Clark (jjc@jclark.com)
9 * This 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 2, or (at your option) any later
14 * This 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 along
20 * with groff; see the file COPYING. If not, write to the Free Software
21 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "roff-config.h"
41 #include "stringclass.h"
43 // The number of commands must be in sync with MAX_COMMANDS in pipeline.h
44 const int PRECONV_INDEX
= 0;
45 const int SOELIM_INDEX
= PRECONV_INDEX
+ 1;
46 const int REFER_INDEX
= SOELIM_INDEX
+ 1;
47 const int GRAP_INDEX
= REFER_INDEX
+ 1;
48 const int PIC_INDEX
= GRAP_INDEX
+ 1;
49 const int TBL_INDEX
= PIC_INDEX
+ 1;
50 const int GRN_INDEX
= TBL_INDEX
+ 1;
51 const int EQN_INDEX
= GRN_INDEX
+ 1;
52 const int TROFF_INDEX
= EQN_INDEX
+ 1;
53 const int POST_INDEX
= TROFF_INDEX
+ 1;
54 const int SPOOL_INDEX
= POST_INDEX
+ 1;
56 const int NCOMMANDS
= SPOOL_INDEX
+ 1;
57 CTA(NCOMMANDS
<= MAX_COMMANDS
);
59 class possible_command
71 void set_name(const char *);
72 void set_name(const char *, const char *);
73 const char *get_name();
74 void append_arg(const char *, const char * = 0);
75 void insert_arg(const char *);
76 void insert_args(string s
);
79 void print(int is_last
, FILE *fp
);
87 possible_command commands
[NCOMMANDS
];
89 int run_commands(int no_pipe
);
90 void print_commands(FILE *);
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 *); // FIXME lib.h??
96 void usage(FILE *stream
);
99 int main(int argc
, char **argv
)
101 program_name
= argv
[0];
102 static char stderr_buf
[BUFSIZ
];
103 setbuf(stderr
, stderr_buf
);
104 string Pargs
, Largs
, Fargs
;
115 const char *command_prefix
= getenv(U_ROFF_COMMAND_PREFIX
);
116 const char *encoding
= getenv(U_ROFF_ENCODING
);
118 command_prefix
= PROG_PREFIX
;
119 commands
[TROFF_INDEX
].set_name(command_prefix
, "troff");
120 static const struct option long_options
[] = {
121 { "help", no_argument
, 0, 'h' },
122 { "version", no_argument
, 0, 'v' },
125 while ((opt
= getopt_long(
127 "abcCd:D:eEf:F:gGhiI:lkK:L:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
139 commands
[SOELIM_INDEX
].set_name(command_prefix
, "soelim");
140 commands
[SOELIM_INDEX
].append_arg(buf
, optarg
);
141 // .psbb may need to search for files
142 commands
[TROFF_INDEX
].append_arg(buf
, optarg
);
143 // \X'ps:import' may need to search for files
149 commands
[PRECONV_INDEX
].set_name(command_prefix
, "preconv");
150 commands
[PRECONV_INDEX
].append_arg("-D", optarg
);
153 commands
[PRECONV_INDEX
].append_arg("-e", optarg
);
157 commands
[PRECONV_INDEX
].set_name(command_prefix
, "preconv");
160 commands
[TBL_INDEX
].set_name(command_prefix
, "tbl");
163 commands
[PIC_INDEX
].set_name(command_prefix
, "pic");
166 commands
[GRN_INDEX
].set_name(command_prefix
, "grn");
169 commands
[GRAP_INDEX
].set_name(command_prefix
, "grap");
173 commands
[EQN_INDEX
].set_name(command_prefix
, "eqn");
176 commands
[SOELIM_INDEX
].set_name(command_prefix
, "soelim");
179 commands
[REFER_INDEX
].set_name(command_prefix
, "refer");
183 commands
[TROFF_INDEX
].append_arg(buf
);
196 printf(L_ROFF
" v" VERSION
);
197 printf(L_ROFF_COPYRIGHT_PRINTOUT
);
198 printf("\ncalled subprograms:\n\n");
200 commands
[POST_INDEX
].append_arg(buf
);
203 commands
[SOELIM_INDEX
].append_arg(buf
);
204 commands
[REFER_INDEX
].append_arg(buf
);
205 commands
[PIC_INDEX
].append_arg(buf
);
206 commands
[GRAP_INDEX
].append_arg(buf
);
207 commands
[TBL_INDEX
].append_arg(buf
);
208 commands
[GRN_INDEX
].append_arg(buf
);
209 commands
[EQN_INDEX
].append_arg(buf
);
210 commands
[TROFF_INDEX
].append_arg(buf
);
213 commands
[EQN_INDEX
].append_arg(buf
);
220 commands
[TROFF_INDEX
].append_arg(buf
);
223 commands
[TROFF_INDEX
].append_arg(buf
);
232 if (strcmp(optarg
, "xhtml") == 0) {
233 // force soelim to aid the html preprocessor
234 commands
[SOELIM_INDEX
].set_name(command_prefix
, "soelim");
243 if (strcmp(optarg
, "html") == 0)
244 // force soelim to aid the html preprocessor
245 commands
[SOELIM_INDEX
].set_name(command_prefix
, "soelim");
249 font::command_line_font_dir(optarg
);
250 if (Fargs
.length() > 0) {
251 Fargs
+= PATH_SEP_CHAR
;
266 commands
[TROFF_INDEX
].append_arg(buf
, optarg
);
269 commands
[EQN_INDEX
].append_arg(buf
, optarg
);
270 commands
[GRAP_INDEX
].append_arg(buf
, optarg
);
271 commands
[GRN_INDEX
].append_arg(buf
, optarg
);
272 commands
[TROFF_INDEX
].append_arg(buf
, optarg
);
279 append_arg_to_string(optarg
, Largs
);
291 commands
[PRECONV_INDEX
].set_name(command_prefix
, "preconv");
292 if (!Kflag
&& *encoding
)
293 commands
[PRECONV_INDEX
].append_arg("-e", encoding
);
296 commands
[TROFF_INDEX
].insert_arg("-U");
297 commands
[PIC_INDEX
].append_arg("-U");
299 font::set_unknown_desc_command_handler(handle_unknown_desc_command
);
300 if (!font::load_desc())
301 fatal("invalid device `%1'", device
);
303 fatal("no `postpro' command in DESC file for device `%1'", device
);
304 if (predriver
&& !zflag
) {
305 commands
[TROFF_INDEX
].insert_arg(commands
[TROFF_INDEX
].get_name());
306 commands
[TROFF_INDEX
].set_name(predriver
);
307 // pass the device arguments to the predrivers as well
308 commands
[TROFF_INDEX
].insert_args(Pargs
);
309 if (eflag
&& is_xhtml
)
310 commands
[TROFF_INDEX
].insert_arg("-e");
312 commands
[TROFF_INDEX
].insert_arg("-v");
314 const char *real_driver
= 0;
316 commands
[POST_INDEX
].set_name(postdriver
);
317 const char *p
= Pargs
.contents();
318 const char *end
= p
+ Pargs
.length();
320 commands
[POST_INDEX
].append_arg(p
);
321 p
= strchr(p
, '\0') + 1;
323 if (lflag
&& !vflag
&& spooler
) {
324 commands
[SPOOL_INDEX
].set_name(BSHELL
);
325 commands
[SPOOL_INDEX
].append_arg(BSHELL_DASH_C
);
327 Largs
= spooler
+ Largs
;
328 commands
[SPOOL_INDEX
].append_arg(Largs
.contents());
331 commands
[POST_INDEX
].set_name(0);
332 commands
[SPOOL_INDEX
].set_name(0);
334 commands
[TROFF_INDEX
].append_arg("-T", device
);
335 if (strcmp(device
, "html") == 0) {
338 fatal("`-o' option is invalid with device `xhtml'");
340 commands
[EQN_INDEX
].append_arg("-Tmathml:xhtml");
342 commands
[EQN_INDEX
].clear_name();
346 fatal("`-o' option is invalid with device `html'");
347 // html renders equations as images via ps
348 commands
[EQN_INDEX
].append_arg("-Tps:html");
352 commands
[EQN_INDEX
].append_arg("-T", device
);
354 commands
[GRN_INDEX
].append_arg("-T", device
);
357 for (first_index
= 0; first_index
< TROFF_INDEX
; first_index
++)
358 if (commands
[first_index
].get_name() != 0)
361 if (argv
[optind
][0] == '-' && argv
[optind
][1] != '\0')
362 commands
[first_index
].append_arg("--");
363 for (int i
= optind
; i
< argc
; i
++)
364 commands
[first_index
].append_arg(argv
[i
]);
366 commands
[first_index
].append_arg("-");
368 if (Fargs
.length() > 0) {
369 string e
= U_ROFF_FONT_PATH
;
372 char *fontpath
= getenv(U_ROFF_FONT_PATH
);
373 if (fontpath
&& *fontpath
) {
378 if (putenv(strsave(e
.contents())))
379 fatal("putenv failed");
382 // we save the original path in GROFF_PATH__ and put it into the
383 // environment -- troff will pick it up later.
384 char *path
= getenv("PATH");
385 string e
= U_ROFF_PATH__
;
390 if (putenv(strsave(e
.contents())))
391 fatal("putenv failed");
392 char *binpath
= getenv(U_ROFF_BIN_PATH
);
395 if (binpath
&& *binpath
)
404 if (putenv(strsave(f
.contents())))
405 fatal("putenv failed");
408 print_commands(Vflag
== 1 ? stdout
: stderr
);
411 return run_commands(vflag
);
414 const char *xbasename(const char *s
) // TODO -> library
418 // DIR_SEPS[] are possible directory separator characters, see nonposix.h
419 // We want the rightmost separator of all possible ones.
420 // Example: d:/foo\\bar.
421 const char *p
= strrchr(s
, DIR_SEPS
[0]), *p1
;
422 const char *sep
= &DIR_SEPS
[1];
426 p1
= strrchr(s
, *sep
);
427 if (p1
&& (!p
|| p1
> p
))
431 return p
? p
+ 1 : s
;
434 void handle_unknown_desc_command(const char *command
, const char *arg
,
435 const char *filename
, int lineno
)
437 if (strcmp(command
, "print") == 0) {
439 error_with_file_and_line(filename
, lineno
,
440 "`print' command requires an argument");
442 spooler
= strsave(arg
);
444 if (strcmp(command
, "prepro") == 0) {
446 error_with_file_and_line(filename
, lineno
,
447 "`prepro' command requires an argument");
449 for (const char *p
= arg
; *p
; p
++)
451 error_with_file_and_line(filename
, lineno
,
452 "invalid `prepro' argument `%1'"
453 ": program name required", arg
);
456 predriver
= strsave(arg
);
459 if (strcmp(command
, "postpro") == 0) {
461 error_with_file_and_line(filename
, lineno
,
462 "`postpro' command requires an argument");
464 for (const char *p
= arg
; *p
; p
++)
466 error_with_file_and_line(filename
, lineno
,
467 "invalid `postpro' argument `%1'"
468 ": program name required", arg
);
471 postdriver
= strsave(arg
);
476 void print_commands(FILE *fp
)
479 for (last
= SPOOL_INDEX
; last
>= 0; last
--)
480 if (commands
[last
].get_name() != 0)
482 for (int i
= 0; i
<= last
; i
++)
483 if (commands
[i
].get_name() != 0)
484 commands
[i
].print(i
== last
, fp
);
487 // Run the commands. Return the code with which to exit.
489 int run_commands(int no_pipe
)
493 for (int i
= 0; i
< NCOMMANDS
; i
++)
494 if (commands
[i
].get_name() != 0)
495 v
[j
++] = commands
[i
].get_argv();
496 return run_pipeline(j
, v
, no_pipe
);
499 possible_command::possible_command()
504 possible_command::~possible_command()
510 void possible_command::set_name(const char *s
)
516 void possible_command::clear_name()
524 void possible_command::set_name(const char *s1
, const char *s2
)
527 name
= new char[strlen(s1
) + strlen(s2
) + 1];
532 const char *possible_command::get_name()
537 void possible_command::clear_args()
542 void possible_command::append_arg(const char *s
, const char *t
)
550 void possible_command::insert_arg(const char *s
)
558 void possible_command::insert_args(string s
)
560 const char *p
= s
.contents();
561 const char *end
= p
+ s
.length();
565 // find the total number of arguments in our string
568 p
= strchr(p
, '\0') + 1;
570 // now insert each argument preserving the order
571 for (int i
= l
- 1; i
>= 0; i
--) {
573 for (int j
= 0; j
< i
; j
++)
574 p
= strchr(p
, '\0') + 1;
579 void possible_command::build_argv()
583 // Count the number of arguments.
584 int len
= args
.length();
589 for (int i
= 0; i
< len
; i
++)
593 // Build an argument vector.
594 argv
= new char *[argc
+ 1];
596 for (int i
= 1; i
< argc
; i
++) {
598 p
= strchr(p
, '\0') + 1;
603 void possible_command::print(int is_last
, FILE *fp
)
606 if (IS_BSHELL(argv
[0])
607 && argv
[1] != 0 && strcmp(argv
[1], BSHELL_DASH_C
) == 0
608 && argv
[2] != 0 && argv
[3] == 0)
613 for (int i
= 1; argv
[i
] != 0; i
++) {
615 append_arg_to_string(argv
[i
], str
);
625 void append_arg_to_string(const char *arg
, string
&str
) // TODO quoting -> lib
628 int needs_quoting
= 0;
629 int contains_single_quote
= 0;
631 for (p
= arg
; *p
!= '\0'; p
++)
652 contains_single_quote
= 1;
655 if (contains_single_quote
|| arg
[0] == '\0') {
657 for (p
= arg
; *p
!= '\0'; p
++)
670 else if (needs_quoting
) {
679 char **possible_command::get_argv()
685 void synopsis(FILE *stream
)
688 "Synopsis: %s [-abceghiklpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
689 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
690 " [-Darg] [-Karg] [-Larg] [-Idir] [files...]\n",
698 "-h\tprint this message\n"
699 "-k\tpreprocess with preconv\n"
700 "-t\tpreprocess with tbl\n"
701 "-p\tpreprocess with pic\n"
702 "-e\tpreprocess with eqn\n"
703 "-g\tpreprocess with grn\n"
704 "-G\tpreprocess with grap\n"
705 "-s\tpreprocess with soelim\n"
706 "-R\tpreprocess with refer\n"
707 "-Tdev\tuse device dev\n"
708 "-mname\tread macros tmac.name\n"
709 "-dcs\tdefine a string c as s\n"
710 "-rcn\tdefine a number register c as n\n"
711 "-nnum\tnumber first page n\n"
712 "-olist\toutput only pages in list\n"
713 "-ffam\tuse fam as the default font family\n"
714 "-Fdir\tsearch dir for device directories\n"
715 "-Mdir\tsearch dir for macro files\n"
716 "-v\tprint version number\n"
717 "-z\tsuppress formatted output\n"
718 "-Z\tdon't postprocess\n"
719 "-a\tproduce ASCII description of output\n"
720 "-i\tread standard input after named input files\n"
721 "-wname\tenable warning name\n"
722 "-Wname\tinhibit warning name\n"
723 "-E\tinhibit all errors\n"
724 "-b\tprint backtraces with errors or warnings\n"
725 "-l\tspool the output\n"
726 "-c\tdisable color output\n"
727 "-C\tenable compatibility mode\n"
728 "-V\tprint commands on stdout instead of running them\n"
729 "-Parg\tpass arg to the postprocessor\n"
730 "-Larg\tpass arg to the spooler\n"
731 "-N\tdon't allow newlines within eqn delimiters\n"
732 "-S\tenable safer mode (the default)\n"
733 "-U\tenable unsafe mode\n"
734 "-Idir\tsearch dir for soelim, troff, and grops. Implies -s\n"
735 "-Karg\tuse arg as input encoding. Implies -k\n"
736 "-Darg\tuse arg as default input encoding. Implies -k\n"
742 void usage(FILE *stream
)
745 fprintf(stream
, "%s -h gives more help\n", program_name
);
749 void c_error(const char *format
, const char *arg1
, const char *arg2
,
752 error(format
, arg1
, arg2
, arg3
);
755 void c_fatal(const char *format
, const char *arg1
, const char *arg2
,
758 fatal(format
, arg1
, arg2
, arg3
);