* src/preproc/grn/main.cc: Slight formatting.
[s-roff.git] / src / roff / groff / groff.cc
blob04ae853b591a01d9cc64033e1c60dc4e4c5b27a8
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2000 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
10 version.
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
15 for more details.
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.
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <errno.h>
29 #include "lib.h"
30 #include "assert.h"
31 #include "errarg.h"
32 #include "error.h"
33 #include "stringclass.h"
34 #include "cset.h"
35 #include "font.h"
36 #include "device.h"
37 #include "pipeline.h"
38 #include "defs.h"
40 #define BSHELL "/bin/sh"
41 #define GXDITVIEW "gxditview"
43 // troff will be passed an argument of -rXREG=1 if the -X option is
44 // specified
45 #define XREG ".X"
47 #ifndef STDLIB_H_DECLARES_PUTENV
48 extern "C" {
49 int putenv(const char *);
51 #endif /* not STDLIB_H_DECLARES_PUTENV */
53 const int SOELIM_INDEX = 0;
54 const int REFER_INDEX = SOELIM_INDEX + 1;
55 const int PIC_INDEX = REFER_INDEX + 1;
56 const int TBL_INDEX = PIC_INDEX + 1;
57 const int GRN_INDEX = TBL_INDEX + 1;
58 const int EQN_INDEX = GRN_INDEX + 1;
59 const int TROFF_INDEX = EQN_INDEX + 1;
60 const int POST_INDEX = TROFF_INDEX + 1;
61 const int SPOOL_INDEX = POST_INDEX + 1;
63 const int NCOMMANDS = SPOOL_INDEX + 1;
65 class possible_command {
66 char *name;
67 string args;
68 char **argv;
70 void build_argv();
71 public:
72 possible_command();
73 ~possible_command();
74 void set_name(const char *);
75 void set_name(const char *, const char *);
76 const char *get_name();
77 void append_arg(const char *, const char * = 0);
78 void insert_arg(const char *);
79 void clear_args();
80 char **get_argv();
81 void print(int is_last, FILE *fp);
84 int lflag = 0;
85 char *spooler = 0;
86 char *driver = 0;
88 possible_command commands[NCOMMANDS];
90 int run_commands();
91 void print_commands();
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 *);
97 void usage();
98 void help();
100 int main(int argc, char **argv)
102 program_name = argv[0];
103 static char stderr_buf[BUFSIZ];
104 setbuf(stderr, stderr_buf);
105 assert(NCOMMANDS <= MAX_COMMANDS);
106 string Pargs, Largs, Fargs;
107 int Vflag = 0;
108 int zflag = 0;
109 int iflag = 0;
110 int Xflag = 0;
111 int safer_flag = 1;
112 int opt;
113 const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
114 if (!command_prefix)
115 command_prefix = PROG_PREFIX;
116 commands[TROFF_INDEX].set_name(command_prefix, "troff");
117 while ((opt = getopt(argc, argv,
118 "abCd:eEf:F:ghiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ"))
119 != EOF) {
120 char buf[3];
121 buf[0] = '-';
122 buf[1] = opt;
123 buf[2] = '\0';
124 switch (opt) {
125 case 'i':
126 iflag = 1;
127 break;
128 case 'I':
129 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
130 commands[SOELIM_INDEX].append_arg(buf, optarg);
131 break;
132 case 't':
133 commands[TBL_INDEX].set_name(command_prefix, "tbl");
134 break;
135 case 'p':
136 commands[PIC_INDEX].set_name(command_prefix, "pic");
137 break;
138 case 'g':
139 commands[GRN_INDEX].set_name(command_prefix, "grn");
140 break;
141 case 'e':
142 commands[EQN_INDEX].set_name(command_prefix, "eqn");
143 break;
144 case 's':
145 commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
146 break;
147 case 'R':
148 commands[REFER_INDEX].set_name(command_prefix, "refer");
149 break;
150 case 'z':
151 case 'a':
152 commands[TROFF_INDEX].append_arg(buf);
153 // fall through
154 case 'Z':
155 zflag++;
156 break;
157 case 'l':
158 lflag++;
159 break;
160 case 'V':
161 Vflag++;
162 break;
163 case 'v':
164 case 'C':
165 commands[SOELIM_INDEX].append_arg(buf);
166 commands[PIC_INDEX].append_arg(buf);
167 commands[TBL_INDEX].append_arg(buf);
168 commands[GRN_INDEX].append_arg(buf);
169 commands[EQN_INDEX].append_arg(buf);
170 commands[TROFF_INDEX].append_arg(buf);
171 break;
172 case 'N':
173 commands[EQN_INDEX].append_arg(buf);
174 break;
175 case 'h':
176 help();
177 break;
178 case 'E':
179 case 'b':
180 commands[TROFF_INDEX].append_arg(buf);
181 break;
182 case 'S':
183 safer_flag = 1;
184 break;
185 case 'U':
186 safer_flag = 0;
187 break;
188 case 'T':
189 if (strcmp(optarg, "Xps") == 0) {
190 warning("-TXps option is obsolete: use -X -Tps instead");
191 device = "ps";
192 Xflag++;
194 else
195 device = optarg;
196 break;
197 case 'F':
198 font::command_line_font_dir(optarg);
199 if (Fargs.length() > 0) {
200 Fargs += ':';
201 Fargs += optarg;
203 else
204 Fargs = optarg;
205 break;
206 case 'f':
207 case 'o':
208 case 'm':
209 case 'r':
210 case 'd':
211 case 'n':
212 case 'w':
213 case 'W':
214 commands[TROFF_INDEX].append_arg(buf, optarg);
215 break;
216 case 'M':
217 commands[EQN_INDEX].append_arg(buf, optarg);
218 commands[GRN_INDEX].append_arg(buf, optarg);
219 commands[TROFF_INDEX].append_arg(buf, optarg);
220 break;
221 case 'P':
222 Pargs += optarg;
223 Pargs += '\0';
224 break;
225 case 'L':
226 append_arg_to_string(optarg, Largs);
227 break;
228 case 'X':
229 Xflag++;
230 break;
231 case '?':
232 usage();
233 break;
234 default:
235 assert(0);
236 break;
239 if (safer_flag) {
240 commands[PIC_INDEX].append_arg("-S");
241 commands[TROFF_INDEX].insert_arg("-msafer");
242 } else {
243 commands[TROFF_INDEX].insert_arg("-U");
245 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
246 if (!font::load_desc())
247 fatal("invalid device `%1'", device);
248 if (!driver)
249 fatal("no `postpro' command in DESC file for device `%1'", device);
250 const char *real_driver = 0;
251 if (Xflag) {
252 real_driver = driver;
253 driver = GXDITVIEW;
254 commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
256 if (driver)
257 commands[POST_INDEX].set_name(driver);
258 int gxditview_flag = driver && strcmp(xbasename(driver), GXDITVIEW) == 0;
259 if (gxditview_flag && argc - optind == 1) {
260 commands[POST_INDEX].append_arg("-title");
261 commands[POST_INDEX].append_arg(argv[optind]);
262 commands[POST_INDEX].append_arg("-xrm");
263 commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
264 string filename_string("|");
265 append_arg_to_string(argv[0], filename_string);
266 append_arg_to_string("-Z", filename_string);
267 for (int i = 1; i < argc; i++)
268 append_arg_to_string(argv[i], filename_string);
269 filename_string += '\0';
270 commands[POST_INDEX].append_arg("-filename");
271 commands[POST_INDEX].append_arg(filename_string.contents());
273 if (gxditview_flag && Xflag) {
274 string print_string(real_driver);
275 if (spooler) {
276 print_string += " | ";
277 print_string += spooler;
278 print_string += Largs;
280 print_string += '\0';
281 commands[POST_INDEX].append_arg("-printCommand");
282 commands[POST_INDEX].append_arg(print_string.contents());
284 const char *p = Pargs.contents();
285 const char *end = p + Pargs.length();
286 while (p < end) {
287 commands[POST_INDEX].append_arg(p);
288 p = strchr(p, '\0') + 1;
290 if (gxditview_flag)
291 commands[POST_INDEX].append_arg("-");
292 if (lflag && !Xflag && spooler) {
293 commands[SPOOL_INDEX].set_name(BSHELL);
294 commands[SPOOL_INDEX].append_arg("-c");
295 Largs += '\0';
296 Largs = spooler + Largs;
297 commands[SPOOL_INDEX].append_arg(Largs.contents());
299 if (zflag) {
300 commands[POST_INDEX].set_name(0);
301 commands[SPOOL_INDEX].set_name(0);
303 commands[TROFF_INDEX].append_arg("-T", device);
304 commands[EQN_INDEX].append_arg("-T", device);
305 commands[GRN_INDEX].append_arg("-T", device);
307 int first_index;
308 for (first_index = 0; first_index < TROFF_INDEX; first_index++)
309 if (commands[first_index].get_name() != 0)
310 break;
311 if (optind < argc) {
312 if (argv[optind][0] == '-' && argv[optind][1] != '\0')
313 commands[first_index].append_arg("--");
314 for (int i = optind; i < argc; i++)
315 commands[first_index].append_arg(argv[i]);
316 if (iflag)
317 commands[first_index].append_arg("-");
319 if (Fargs.length() > 0) {
320 string e = "GROFF_FONT_PATH";
321 e += '=';
322 e += Fargs;
323 char *fontpath = getenv("GROFF_FONT_PATH");
324 if (fontpath && *fontpath) {
325 e += ':';
326 e += fontpath;
328 e += '\0';
329 if (putenv(strsave(e.contents())))
330 fatal("putenv failed");
332 if (Vflag) {
333 print_commands();
334 exit(0);
336 return run_commands();
339 const char *xbasename(const char *s)
341 if (!s)
342 return 0;
343 const char *p = strrchr(s, '/');
344 return p ? p + 1 : s;
347 void handle_unknown_desc_command(const char *command, const char *arg,
348 const char *filename, int lineno)
350 if (strcmp(command, "print") == 0) {
351 if (arg == 0)
352 error_with_file_and_line(filename, lineno,
353 "`print' command requires an argument");
354 else
355 spooler = strsave(arg);
357 if (strcmp(command, "postpro") == 0) {
358 if (arg == 0)
359 error_with_file_and_line(filename, lineno,
360 "`postpro' command requires an argument");
361 else {
362 for (const char *p = arg; *p; p++)
363 if (csspace(*p)) {
364 error_with_file_and_line(filename, lineno,
365 "invalid `postpro' argument `%1'"
366 ": program name required", arg);
367 return;
369 driver = strsave(arg);
374 void print_commands()
376 int last;
377 for (last = SPOOL_INDEX; last >= 0; last--)
378 if (commands[last].get_name() != 0)
379 break;
380 for (int i = 0; i <= last; i++)
381 if (commands[i].get_name() != 0)
382 commands[i].print(i == last, stdout);
385 // Run the commands. Return the code with which to exit.
387 int run_commands()
389 char **v[NCOMMANDS];
390 int j = 0;
391 for (int i = 0; i < NCOMMANDS; i++)
392 if (commands[i].get_name() != 0)
393 v[j++] = commands[i].get_argv();
394 return run_pipeline(j, v);
397 possible_command::possible_command()
398 : name(0), argv(0)
402 possible_command::~possible_command()
404 a_delete name;
405 a_delete argv;
408 void possible_command::set_name(const char *s)
410 a_delete name;
411 name = strsave(s);
414 void possible_command::set_name(const char *s1, const char *s2)
416 a_delete name;
417 name = new char[strlen(s1) + strlen(s2) + 1];
418 strcpy(name, s1);
419 strcat(name, s2);
422 const char *possible_command::get_name()
424 return name;
427 void possible_command::clear_args()
429 args.clear();
432 void possible_command::append_arg(const char *s, const char *t)
434 args += s;
435 if (t)
436 args += t;
437 args += '\0';
440 void possible_command::insert_arg(const char *s)
442 string str(s);
443 str += '\0';
444 str += args;
445 args = str;
448 void possible_command::build_argv()
450 if (argv)
451 return;
452 // Count the number of arguments.
453 int len = args.length();
454 int argc = 1;
455 char *p = 0;
456 if (len > 0) {
457 p = &args[0];
458 for (int i = 0; i < len; i++)
459 if (p[i] == '\0')
460 argc++;
462 // Build an argument vector.
463 argv = new char *[argc + 1];
464 argv[0] = name;
465 for (int i = 1; i < argc; i++) {
466 argv[i] = p;
467 p = strchr(p, '\0') + 1;
469 argv[argc] = 0;
472 void possible_command::print(int is_last, FILE *fp)
474 build_argv();
475 if (argv[0] != 0 && strcmp(argv[0], BSHELL) == 0
476 && argv[1] != 0 && strcmp(argv[1], "-c") == 0
477 && argv[2] != 0 && argv[3] == 0)
478 fputs(argv[2], fp);
479 else {
480 fputs(argv[0], fp);
481 string str;
482 for (int i = 1; argv[i] != 0; i++) {
483 str.clear();
484 append_arg_to_string(argv[i], str);
485 put_string(str, fp);
488 if (is_last)
489 putc('\n', fp);
490 else
491 fputs(" | ", fp);
494 void append_arg_to_string(const char *arg, string &str)
496 str += ' ';
497 int needs_quoting = 0;
498 int contains_single_quote = 0;
499 const char*p;
500 for (p = arg; *p != '\0'; p++)
501 switch (*p) {
502 case ';':
503 case '&':
504 case '(':
505 case ')':
506 case '|':
507 case '^':
508 case '<':
509 case '>':
510 case '\n':
511 case ' ':
512 case '\t':
513 case '\\':
514 case '"':
515 case '$':
516 case '?':
517 case '*':
518 needs_quoting = 1;
519 break;
520 case '\'':
521 contains_single_quote = 1;
522 break;
524 if (contains_single_quote || arg[0] == '\0') {
525 str += '"';
526 for (p = arg; *p != '\0'; p++)
527 switch (*p) {
528 case '"':
529 case '\\':
530 case '$':
531 str += '\\';
532 // fall through
533 default:
534 str += *p;
535 break;
537 str += '"';
539 else if (needs_quoting) {
540 str += '\'';
541 str += arg;
542 str += '\'';
544 else
545 str += arg;
548 char **possible_command::get_argv()
550 build_argv();
551 return argv;
554 void synopsis()
556 fprintf(stderr,
557 "usage: %s [-abeghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
558 " [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
559 " [-Larg] [-Idir] [files...]\n",
560 program_name);
563 void help()
565 synopsis();
566 fputs("\n"
567 "-h\tprint this message\n"
568 "-t\tpreprocess with tbl\n"
569 "-p\tpreprocess with pic\n"
570 "-e\tpreprocess with eqn\n"
571 "-g\tpreprocess with grn\n"
572 "-s\tpreprocess with soelim\n"
573 "-R\tpreprocess with refer\n"
574 "-Tdev\tuse device dev\n"
575 "-X\tuse X11 previewer rather than usual postprocessor\n"
576 "-mname\tread macros tmac.name\n"
577 "-dcs\tdefine a string c as s\n"
578 "-rcn\tdefine a number register c as n\n"
579 "-nnum\tnumber first page n\n"
580 "-olist\toutput only pages in list\n"
581 "-ffam\tuse fam as the default font family\n"
582 "-Fdir\tsearch dir for device directories\n"
583 "-Mdir\tsearch dir for macro files\n"
584 "-v\tprint version number\n"
585 "-z\tsuppress formatted output\n"
586 "-Z\tdon't postprocess\n"
587 "-a\tproduce ASCII description of output\n"
588 "-i\tread standard input after named input files\n"
589 "-wname\tenable warning name\n"
590 "-Wname\tinhibit warning name\n"
591 "-E\tinhibit all errors\n"
592 "-b\tprint backtraces with errors or warnings\n"
593 "-l\tspool the output\n"
594 "-C\tenable compatibility mode\n"
595 "-V\tprint commands on stdout instead of running them\n"
596 "-Parg\tpass arg to the postprocessor\n"
597 "-Larg\tpass arg to the spooler\n"
598 "-N\tdon't allow newlines within eqn delimiters\n"
599 "-S\tenable safer mode (the default)\n"
600 "-U\tenable unsafe mode\n"
601 "-Idir\tsearch dir for soelim. Implies -s\n"
602 "\n",
603 stderr);
604 exit(0);
607 void usage()
609 synopsis();
610 fprintf(stderr, "%s -h gives more help\n", program_name);
611 exit(1);
614 extern "C" {
616 void c_error(const char *format, const char *arg1, const char *arg2,
617 const char *arg3)
619 error(format, arg1, arg2, arg3);
622 void c_fatal(const char *format, const char *arg1, const char *arg2,
623 const char *arg3)
625 fatal(format, arg1, arg2, arg3);