1 /*@ A front end for S-roff.
3 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
58 CTA(IDX_NCOMMANDS
<= MAX_COMMANDS
);
60 class possible_command
72 void set_name(const char *);
73 void set_name(const char *, const char *);
74 const char *get_name();
75 void append_arg(const char *, const char * = 0);
76 void insert_arg(const char *);
77 void insert_args(string s
);
80 void print(int is_last
, FILE *fp
);
88 possible_command commands
[IDX_NCOMMANDS
];
90 int run_commands(int no_pipe
);
91 void print_commands(FILE *);
92 void append_arg_to_string(const char *arg
, string
&str
);
93 void handle_unknown_desc_command(const char *command
, const char *arg
,
94 const char *filename
, int lineno
);
95 const char *xbasename(const char *); // FIXME lib.h??
97 void usage(FILE *stream
);
100 int main(int argc
, char **argv
)
102 program_name
= argv
[0];
103 static char stderr_buf
[BUFSIZ
];
104 setbuf(stderr
, stderr_buf
);
105 string Pargs
, Largs
, Fargs
;
116 const char *command_prefix
= getenv(U_ROFF_COMMAND_PREFIX
);
117 const char *encoding
= getenv(U_ROFF_ENCODING
);
119 command_prefix
= PROG_PREFIX
;
120 commands
[IDX_TROFF
].set_name(command_prefix
, "troff");
121 static const struct option long_options
[] = {
122 { "help", no_argument
, 0, 'h' },
123 { "version", no_argument
, 0, 'v' },
126 while ((opt
= getopt_long(
128 "abcCd:D:eEf:F:gGhiI:lkK:L:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
140 commands
[IDX_SOELIM
].set_name(command_prefix
, "soelim");
141 commands
[IDX_SOELIM
].append_arg(buf
, optarg
);
142 // .psbb may need to search for files
143 commands
[IDX_TROFF
].append_arg(buf
, optarg
);
144 // \X'ps:import' may need to search for files
150 commands
[IDX_PRECONV
].set_name(command_prefix
, "preconv");
151 commands
[IDX_PRECONV
].append_arg("-D", optarg
);
154 commands
[IDX_PRECONV
].append_arg("-e", optarg
);
158 commands
[IDX_PRECONV
].set_name(command_prefix
, "preconv");
161 commands
[IDX_TBL
].set_name(command_prefix
, "tbl");
164 commands
[IDX_PIC
].set_name(command_prefix
, "pic");
167 commands
[IDX_GRN
].set_name(command_prefix
, "grn");
170 commands
[IDX_GRAP
].set_name(command_prefix
, "grap");
174 commands
[IDX_EQN
].set_name(command_prefix
, "eqn");
177 commands
[IDX_SOELIM
].set_name(command_prefix
, "soelim");
180 commands
[IDX_REFER
].set_name(command_prefix
, "refer");
184 commands
[IDX_TROFF
].append_arg(buf
);
197 printf(L_ROFF
" v" VERSION
);
198 printf(L_ROFF_COPYRIGHT_PRINTOUT
);
199 printf("\ncalled subprograms:\n\n");
201 commands
[IDX_POST
].append_arg(buf
);
204 commands
[IDX_SOELIM
].append_arg(buf
);
205 commands
[IDX_REFER
].append_arg(buf
);
206 commands
[IDX_PIC
].append_arg(buf
);
207 commands
[IDX_GRAP
].append_arg(buf
);
208 commands
[IDX_TBL
].append_arg(buf
);
209 commands
[IDX_GRN
].append_arg(buf
);
210 commands
[IDX_EQN
].append_arg(buf
);
211 commands
[IDX_TROFF
].append_arg(buf
);
214 commands
[IDX_EQN
].append_arg(buf
);
221 commands
[IDX_TROFF
].append_arg(buf
);
224 commands
[IDX_TROFF
].append_arg(buf
);
233 if (strcmp(optarg
, "xhtml") == 0) {
234 // force soelim to aid the html preprocessor
235 commands
[IDX_SOELIM
].set_name(command_prefix
, "soelim");
244 if (strcmp(optarg
, "html") == 0)
245 // force soelim to aid the html preprocessor
246 commands
[IDX_SOELIM
].set_name(command_prefix
, "soelim");
250 font::command_line_font_dir(optarg
);
251 if (Fargs
.length() > 0) {
252 Fargs
+= PATH_SEP_CHAR
;
267 commands
[IDX_TROFF
].append_arg(buf
, optarg
);
270 commands
[IDX_EQN
].append_arg(buf
, optarg
);
271 commands
[IDX_GRAP
].append_arg(buf
, optarg
);
272 commands
[IDX_GRN
].append_arg(buf
, optarg
);
273 commands
[IDX_TROFF
].append_arg(buf
, optarg
);
280 append_arg_to_string(optarg
, Largs
);
292 commands
[IDX_PRECONV
].set_name(command_prefix
, "preconv");
293 if (!Kflag
&& *encoding
)
294 commands
[IDX_PRECONV
].append_arg("-e", encoding
);
297 commands
[IDX_TROFF
].insert_arg("-U");
298 commands
[IDX_PIC
].append_arg("-U");
300 font::set_unknown_desc_command_handler(handle_unknown_desc_command
);
301 if (!font::load_desc())
302 fatal("invalid device `%1'", device
);
304 fatal("no `postpro' command in DESC file for device `%1'", device
);
305 if (predriver
&& !zflag
) {
306 commands
[IDX_TROFF
].insert_arg(commands
[IDX_TROFF
].get_name());
307 commands
[IDX_TROFF
].set_name(predriver
);
308 // pass the device arguments to the predrivers as well
309 commands
[IDX_TROFF
].insert_args(Pargs
);
310 if (eflag
&& is_xhtml
)
311 commands
[IDX_TROFF
].insert_arg("-e");
313 commands
[IDX_TROFF
].insert_arg("-v");
315 const char *real_driver
= 0;
317 commands
[IDX_POST
].set_name(postdriver
);
318 const char *p
= Pargs
.contents();
319 const char *end
= p
+ Pargs
.length();
321 commands
[IDX_POST
].append_arg(p
);
322 p
= strchr(p
, '\0') + 1;
324 if (lflag
&& !vflag
&& spooler
) {
325 commands
[IDX_SPOOL
].set_name(BSHELL
);
326 commands
[IDX_SPOOL
].append_arg(BSHELL_DASH_C
);
328 Largs
= spooler
+ Largs
;
329 commands
[IDX_SPOOL
].append_arg(Largs
.contents());
332 commands
[IDX_POST
].set_name(0);
333 commands
[IDX_SPOOL
].set_name(0);
335 commands
[IDX_TROFF
].append_arg("-T", device
);
336 if (strcmp(device
, "html") == 0) {
339 fatal("`-o' option is invalid with device `xhtml'");
341 commands
[IDX_EQN
].append_arg("-Tmathml:xhtml");
343 commands
[IDX_EQN
].clear_name();
347 fatal("`-o' option is invalid with device `html'");
348 // html renders equations as images via ps
349 commands
[IDX_EQN
].append_arg("-Tps:html");
353 commands
[IDX_EQN
].append_arg("-T", device
);
355 commands
[IDX_GRN
].append_arg("-T", device
);
358 for (first_index
= 0; first_index
< IDX_TROFF
; ++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
= U_ROFF_FONT_PATH
;
373 char *fontpath
= getenv(U_ROFF_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
= U_ROFF_PATH__
;
391 if (putenv(strsave(e
.contents())))
392 fatal("putenv failed");
393 char *binpath
= getenv(U_ROFF_BIN_PATH
);
396 if (binpath
&& *binpath
)
405 if (putenv(strsave(f
.contents())))
406 fatal("putenv failed");
409 print_commands(Vflag
== 1 ? stdout
: stderr
);
412 return run_commands(vflag
);
415 const char *xbasename(const char *s
) // TODO -> library
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(FILE *fp
)
480 for (last
= IDX_SPOOL
; 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
, fp
);
488 // Run the commands. Return the code with which to exit.
490 int run_commands(int no_pipe
)
492 char **v
[IDX_NCOMMANDS
];
494 for (int i
= 0; i
< IDX_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::clear_name()
525 void possible_command::set_name(const char *s1
, const char *s2
)
528 name
= new char[strlen(s1
) + strlen(s2
) + 1];
533 const char *possible_command::get_name()
538 void possible_command::clear_args()
543 void possible_command::append_arg(const char *s
, const char *t
)
551 void possible_command::insert_arg(const char *s
)
559 void possible_command::insert_args(string s
)
561 const char *p
= s
.contents();
562 const char *end
= p
+ s
.length();
566 // find the total number of arguments in our string
569 p
= strchr(p
, '\0') + 1;
571 // now insert each argument preserving the order
572 for (int i
= l
- 1; i
>= 0; i
--) {
574 for (int j
= 0; j
< i
; j
++)
575 p
= strchr(p
, '\0') + 1;
580 void possible_command::build_argv()
584 // Count the number of arguments.
585 int len
= args
.length();
590 for (int i
= 0; i
< len
; i
++)
594 // Build an argument vector.
595 argv
= new char *[argc
+ 1];
597 for (int i
= 1; i
< argc
; i
++) {
599 p
= strchr(p
, '\0') + 1;
604 void possible_command::print(int is_last
, FILE *fp
)
607 if (IS_BSHELL(argv
[0])
608 && argv
[1] != 0 && strcmp(argv
[1], BSHELL_DASH_C
) == 0
609 && argv
[2] != 0 && argv
[3] == 0)
614 for (int i
= 1; argv
[i
] != 0; i
++) {
616 append_arg_to_string(argv
[i
], str
);
626 void append_arg_to_string(const char *arg
, string
&str
) // TODO quoting -> lib
629 int needs_quoting
= 0;
630 int contains_single_quote
= 0;
632 for (p
= arg
; *p
!= '\0'; p
++)
653 contains_single_quote
= 1;
656 if (contains_single_quote
|| arg
[0] == '\0') {
658 for (p
= arg
; *p
!= '\0'; p
++)
671 else if (needs_quoting
) {
680 char **possible_command::get_argv()
686 void synopsis(FILE *stream
)
689 "Synopsis: %s [-abceghiklpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
690 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
691 " [-Darg] [-Karg] [-Larg] [-Idir] [files...]\n",
699 "-h\tprint this message\n"
700 "-k\tpreprocess with preconv\n"
701 "-t\tpreprocess with tbl\n"
702 "-p\tpreprocess with pic\n"
703 "-e\tpreprocess with eqn\n"
704 "-g\tpreprocess with grn\n"
705 "-G\tpreprocess with grap\n"
706 "-s\tpreprocess with soelim\n"
707 "-R\tpreprocess with refer\n"
708 "-Tdev\tuse device dev\n"
709 "-mname\tread macros tmac.name\n"
710 "-dcs\tdefine a string c as s\n"
711 "-rcn\tdefine a number register c as n\n"
712 "-nnum\tnumber first page n\n"
713 "-olist\toutput only pages in list\n"
714 "-ffam\tuse fam as the default font family\n"
715 "-Fdir\tsearch dir for device directories\n"
716 "-Mdir\tsearch dir for macro files\n"
717 "-v\tprint version number\n"
718 "-z\tsuppress formatted output\n"
719 "-Z\tdon't postprocess\n"
720 "-a\tproduce ASCII description of output\n"
721 "-i\tread standard input after named input files\n"
722 "-wname\tenable warning name\n"
723 "-Wname\tinhibit warning name\n"
724 "-E\tinhibit all errors\n"
725 "-b\tprint backtraces with errors or warnings\n"
726 "-l\tspool the output\n"
727 "-c\tdisable color output\n"
728 "-C\tenable compatibility mode\n"
729 "-V\tprint commands on stdout instead of running them\n"
730 "-Parg\tpass arg to the postprocessor\n"
731 "-Larg\tpass arg to the spooler\n"
732 "-N\tdon't allow newlines within eqn delimiters\n"
733 "-S\tenable safer mode (the default)\n"
734 "-U\tenable unsafe mode\n"
735 "-Idir\tsearch dir for soelim, troff, and grops. Implies -s\n"
736 "-Karg\tuse arg as input encoding. Implies -k\n"
737 "-Darg\tuse arg as default input encoding. Implies -k\n"
743 void usage(FILE *stream
)
746 fprintf(stream
, "%s -h gives more help\n", program_name
);
750 void c_error(const char *format
, const char *arg1
, const char *arg2
,
753 error(format
, arg1
, arg2
, arg3
);
756 void c_fatal(const char *format
, const char *arg1
, const char *arg2
,
759 fatal(format
, arg1
, arg2
, arg3
);