Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / groff / src / roff / groff / groff.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
3                  2008, 2009
4    Free Software Foundation, Inc.
5      Written by James Clark (jjc@jclark.com)
6
7 This file is part of groff.
8
9 groff 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 3 of the License, or
12 (at your option) any later version.
13
14 groff 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
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 // A front end for groff.
23
24 #include "lib.h"
25
26 #include <stdlib.h>
27 #include <signal.h>
28 #include <errno.h>
29
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 "nonposix.h"
39 #include "defs.h"
40
41 #define GXDITVIEW "gxditview"
42
43 // troff will be passed an argument of -rXREG=1 if the -X option is
44 // specified
45 #define XREG ".X"
46
47 #ifdef NEED_DECLARATION_PUTENV
48 extern "C" {
49   int putenv(const char *);
50 }
51 #endif /* NEED_DECLARATION_PUTENV */
52
53 // The number of commands must be in sync with MAX_COMMANDS in pipeline.h
54 const int PRECONV_INDEX = 0;
55 const int SOELIM_INDEX = PRECONV_INDEX + 1;
56 const int REFER_INDEX = SOELIM_INDEX + 1;
57 const int GRAP_INDEX = REFER_INDEX + 1;
58 const int PIC_INDEX = GRAP_INDEX + 1;
59 const int TBL_INDEX = PIC_INDEX + 1;
60 const int GRN_INDEX = TBL_INDEX + 1;
61 const int EQN_INDEX = GRN_INDEX + 1;
62 const int TROFF_INDEX = EQN_INDEX + 1;
63 const int POST_INDEX = TROFF_INDEX + 1;
64 const int SPOOL_INDEX = POST_INDEX + 1;
65
66 const int NCOMMANDS = SPOOL_INDEX + 1;
67
68 class possible_command {
69   char *name;
70   string args;
71   char **argv;
72
73   void build_argv();
74 public:
75   possible_command();
76   ~possible_command();
77   void clear_name();
78   void set_name(const char *);
79   void set_name(const char *, const char *);
80   const char *get_name();
81   void append_arg(const char *, const char * = 0);
82   void insert_arg(const char *);
83   void insert_args(string s);
84   void clear_args();
85   char **get_argv();
86   void print(int is_last, FILE *fp);
87 };
88
89 extern "C" const char *Version_string;
90
91 int lflag = 0;
92 char *spooler = 0;
93 char *postdriver = 0;
94 char *predriver = 0;
95
96 possible_command commands[NCOMMANDS];
97
98 int run_commands(int no_pipe);
99 void print_commands(FILE *);
100 void append_arg_to_string(const char *arg, string &str);
101 void handle_unknown_desc_command(const char *command, const char *arg,
102                                  const char *filename, int lineno);
103 const char *xbasename(const char *);
104
105 void usage(FILE *stream);
106 void help();
107
108 int main(int argc, char **argv)
109 {
110   program_name = argv[0];
111   static char stderr_buf[BUFSIZ];
112   setbuf(stderr, stderr_buf);
113   assert(NCOMMANDS <= MAX_COMMANDS);
114   string Pargs, Largs, Fargs;
115   int Kflag = 0;
116   int vflag = 0;
117   int Vflag = 0;
118   int zflag = 0;
119   int iflag = 0;
120   int Xflag = 0;
121   int oflag = 0;
122   int safer_flag = 1;
123   int is_xhtml = 0;
124   int eflag = 0;
125   int opt;
126   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
127   const char *encoding = getenv("GROFF_ENCODING");
128   if (!command_prefix)
129     command_prefix = PROG_PREFIX;
130   commands[TROFF_INDEX].set_name(command_prefix, "troff");
131   static const struct option long_options[] = {
132     { "help", no_argument, 0, 'h' },
133     { "version", no_argument, 0, 'v' },
134     { NULL, 0, 0, 0 }
135   };
136   while ((opt = getopt_long(
137                   argc, argv,
138                   "abcCd:D:eEf:F:gGhiI:lkK:L:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
139                   long_options, NULL))
140          != EOF) {
141     char buf[3];
142     buf[0] = '-';
143     buf[1] = opt;
144     buf[2] = '\0';
145     switch (opt) {
146     case 'i':
147       iflag = 1;
148       break;
149     case 'I':
150       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
151       commands[SOELIM_INDEX].append_arg(buf, optarg);
152       // .psbb may need to search for files
153       commands[TROFF_INDEX].append_arg(buf, optarg);
154       // \X'ps:import' may need to search for files
155       Pargs += buf;
156       Pargs += optarg;
157       Pargs += '\0';
158       break;
159     case 'D':
160       commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
161       commands[PRECONV_INDEX].append_arg("-D", optarg);
162       break;
163     case 'K':
164       commands[PRECONV_INDEX].append_arg("-e", optarg);
165       Kflag = 1;
166       // fall through
167     case 'k':
168       commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
169       break;
170     case 't':
171       commands[TBL_INDEX].set_name(command_prefix, "tbl");
172       break;
173     case 'p':
174       commands[PIC_INDEX].set_name(command_prefix, "pic");
175       break;
176     case 'g':
177       commands[GRN_INDEX].set_name(command_prefix, "grn");
178       break;
179     case 'G':
180       commands[GRAP_INDEX].set_name(command_prefix, "grap");
181       break;
182     case 'e':
183       eflag = 1;
184       commands[EQN_INDEX].set_name(command_prefix, "eqn");
185       break;
186     case 's':
187       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
188       break;
189     case 'R':
190       commands[REFER_INDEX].set_name(command_prefix, "refer");
191       break;
192     case 'z':
193     case 'a':
194       commands[TROFF_INDEX].append_arg(buf);
195       // fall through
196     case 'Z':
197       zflag++;
198       break;
199     case 'l':
200       lflag++;
201       break;
202     case 'V':
203       Vflag++;
204       break;
205     case 'v':
206       vflag = 1;
207       printf("GNU groff version %s\n", Version_string);
208       printf(
209         "Copyright (C) 2009 Free Software Foundation, Inc.\n"
210         "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
211         "You may redistribute copies of groff and its subprograms\n"
212         "under the terms of the GNU General Public License.\n"
213         "For more information about these matters, see the file named COPYING.\n");
214       printf("\ncalled subprograms:\n\n");
215       fflush(stdout);
216       commands[POST_INDEX].append_arg(buf);
217       // fall through
218     case 'C':
219       commands[SOELIM_INDEX].append_arg(buf);
220       commands[REFER_INDEX].append_arg(buf);
221       commands[PIC_INDEX].append_arg(buf);
222       commands[GRAP_INDEX].append_arg(buf);
223       commands[TBL_INDEX].append_arg(buf);
224       commands[GRN_INDEX].append_arg(buf);
225       commands[EQN_INDEX].append_arg(buf);
226       commands[TROFF_INDEX].append_arg(buf);
227       break;
228     case 'N':
229       commands[EQN_INDEX].append_arg(buf);
230       break;
231     case 'h':
232       help();
233       break;
234     case 'E':
235     case 'b':
236       commands[TROFF_INDEX].append_arg(buf);
237       break;
238     case 'c':
239       commands[TROFF_INDEX].append_arg(buf);
240       break;
241     case 'S':
242       safer_flag = 1;
243       break;
244     case 'U':
245       safer_flag = 0;
246       break;
247     case 'T':
248       if (strcmp(optarg, "xhtml") == 0) {
249         // force soelim to aid the html preprocessor
250         commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
251         Pargs += "-x";
252         Pargs += '\0';
253         Pargs += 'x';
254         Pargs += '\0';
255         is_xhtml = 1;
256         device = "html";
257         break;
258       }
259       if (strcmp(optarg, "html") == 0)
260         // force soelim to aid the html preprocessor
261         commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
262       
263       if (strcmp(optarg, "Xps") == 0) {
264         warning("-TXps option is obsolete: use -X -Tps instead");
265         device = "ps";
266         Xflag++;
267       }
268       else
269         device = optarg;
270       break;
271     case 'F':
272       font::command_line_font_dir(optarg);
273       if (Fargs.length() > 0) {
274         Fargs += PATH_SEP_CHAR;
275         Fargs += optarg;
276       }
277       else
278         Fargs = optarg;
279       break;
280     case 'o':
281       oflag = 1;
282     case 'f':
283     case 'm':
284     case 'r':
285     case 'd':
286     case 'n':
287     case 'w':
288     case 'W':
289       commands[TROFF_INDEX].append_arg(buf, optarg);
290       break;
291     case 'M':
292       commands[EQN_INDEX].append_arg(buf, optarg);
293       commands[GRAP_INDEX].append_arg(buf, optarg);
294       commands[GRN_INDEX].append_arg(buf, optarg);
295       commands[TROFF_INDEX].append_arg(buf, optarg);
296       break;
297     case 'P':
298       Pargs += optarg;
299       Pargs += '\0';
300       break;
301     case 'L':
302       append_arg_to_string(optarg, Largs);
303       break;
304     case 'X':
305       Xflag++;
306       break;
307     case '?':
308       usage(stderr);
309       exit(1);
310       break;
311     default:
312       assert(0);
313       break;
314     }
315   }
316   if (encoding) {
317     commands[PRECONV_INDEX].set_name(command_prefix, "preconv");
318     if (!Kflag && *encoding)
319       commands[PRECONV_INDEX].append_arg("-e", encoding);
320   }
321   if (!safer_flag) {
322     commands[TROFF_INDEX].insert_arg("-U");
323     commands[PIC_INDEX].append_arg("-U");
324   }
325   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
326   if (!font::load_desc())
327     fatal("invalid device `%1'", device);
328   if (!postdriver)
329     fatal("no `postpro' command in DESC file for device `%1'", device);
330   if (predriver && !zflag) {
331     commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
332     commands[TROFF_INDEX].set_name(predriver);
333     // pass the device arguments to the predrivers as well
334     commands[TROFF_INDEX].insert_args(Pargs);
335     if (eflag && is_xhtml)
336       commands[TROFF_INDEX].insert_arg("-e");
337     if (vflag)
338       commands[TROFF_INDEX].insert_arg("-v");
339   }
340   const char *real_driver = 0;
341   if (Xflag) {
342     real_driver = postdriver;
343     postdriver = (char *)GXDITVIEW;
344     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
345   }
346   if (postdriver)
347     commands[POST_INDEX].set_name(postdriver);
348   int gxditview_flag = postdriver
349                        && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
350   if (gxditview_flag && argc - optind == 1) {
351     commands[POST_INDEX].append_arg("-title");
352     commands[POST_INDEX].append_arg(argv[optind]);
353     commands[POST_INDEX].append_arg("-xrm");
354     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
355     string filename_string("|");
356     append_arg_to_string(argv[0], filename_string);
357     append_arg_to_string("-Z", filename_string);
358     for (int i = 1; i < argc; i++)
359       append_arg_to_string(argv[i], filename_string);
360     filename_string += '\0';
361     commands[POST_INDEX].append_arg("-filename");
362     commands[POST_INDEX].append_arg(filename_string.contents());
363   }
364   if (gxditview_flag && Xflag) {
365     string print_string(real_driver);
366     if (spooler) {
367       print_string += " | ";
368       print_string += spooler;
369       print_string += Largs;
370     }
371     print_string += '\0';
372     commands[POST_INDEX].append_arg("-printCommand");
373     commands[POST_INDEX].append_arg(print_string.contents());
374   }
375   const char *p = Pargs.contents();
376   const char *end = p + Pargs.length();
377   while (p < end) {
378     commands[POST_INDEX].append_arg(p);
379     p = strchr(p, '\0') + 1;
380   }
381   if (gxditview_flag)
382     commands[POST_INDEX].append_arg("-");
383   if (lflag && !vflag && !Xflag && spooler) {
384     commands[SPOOL_INDEX].set_name(BSHELL);
385     commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
386     Largs += '\0';
387     Largs = spooler + Largs;
388     commands[SPOOL_INDEX].append_arg(Largs.contents());
389   }
390   if (zflag) {
391     commands[POST_INDEX].set_name(0);
392     commands[SPOOL_INDEX].set_name(0);
393   }
394   commands[TROFF_INDEX].append_arg("-T", device);
395   if (strcmp(device, "html") == 0) {
396     if (is_xhtml) {
397       if (oflag)
398         fatal("`-o' option is invalid with device `xhtml'");
399       if (zflag)
400         commands[EQN_INDEX].append_arg("-Tmathml:xhtml");
401       else if (eflag)
402         commands[EQN_INDEX].clear_name();
403     }
404     else {
405       if (oflag)
406         fatal("`-o' option is invalid with device `html'");
407       // html renders equations as images via ps
408       commands[EQN_INDEX].append_arg("-Tps:html");
409     }
410   }
411   else
412     commands[EQN_INDEX].append_arg("-T", device);
413
414   commands[GRN_INDEX].append_arg("-T", device);
415
416   int first_index;
417   for (first_index = 0; first_index < TROFF_INDEX; first_index++)
418     if (commands[first_index].get_name() != 0)
419       break;
420   if (optind < argc) {
421     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
422       commands[first_index].append_arg("--");
423     for (int i = optind; i < argc; i++)
424       commands[first_index].append_arg(argv[i]);
425     if (iflag)
426       commands[first_index].append_arg("-");
427   }
428   if (Fargs.length() > 0) {
429     string e = "GROFF_FONT_PATH";
430     e += '=';
431     e += Fargs;
432     char *fontpath = getenv("GROFF_FONT_PATH");
433     if (fontpath && *fontpath) {
434       e += PATH_SEP_CHAR;
435       e += fontpath;
436     }
437     e += '\0';
438     if (putenv(strsave(e.contents())))
439       fatal("putenv failed");
440   }
441   {
442     // we save the original path in GROFF_PATH__ and put it into the
443     // environment -- troff will pick it up later.
444     char *path = getenv("PATH");
445     string e = "GROFF_PATH__";
446     e += '=';
447     if (path && *path)
448       e += path;
449     e += '\0';
450     if (putenv(strsave(e.contents())))
451       fatal("putenv failed");
452     char *binpath = getenv("GROFF_BIN_PATH");
453     string f = "PATH";
454     f += '=';
455     if (binpath && *binpath)
456       f += binpath;
457     else
458       f += BINPATH;
459     if (path && *path) {
460       f += PATH_SEP_CHAR;
461       f += path;
462     }
463     f += '\0';
464     if (putenv(strsave(f.contents())))
465       fatal("putenv failed");
466   }
467   if (Vflag)
468     print_commands(Vflag == 1 ? stdout : stderr);
469   if (Vflag == 1)
470     exit(0);
471   return run_commands(vflag);
472 }
473
474 const char *xbasename(const char *s)
475 {
476   if (!s)
477     return 0;
478   // DIR_SEPS[] are possible directory separator characters, see nonposix.h
479   // We want the rightmost separator of all possible ones.
480   // Example: d:/foo\\bar.
481   const char *p = strrchr(s, DIR_SEPS[0]), *p1;
482   const char *sep = &DIR_SEPS[1];
483
484   while (*sep)
485     {
486       p1 = strrchr(s, *sep);
487       if (p1 && (!p || p1 > p))
488         p = p1;
489       sep++;
490     }
491   return p ? p + 1 : s;
492 }
493
494 void handle_unknown_desc_command(const char *command, const char *arg,
495                                  const char *filename, int lineno)
496 {
497   if (strcmp(command, "print") == 0) {
498     if (arg == 0)
499       error_with_file_and_line(filename, lineno,
500                                "`print' command requires an argument");
501     else
502       spooler = strsave(arg);
503   }
504   if (strcmp(command, "prepro") == 0) {
505     if (arg == 0)
506       error_with_file_and_line(filename, lineno,
507                                "`prepro' command requires an argument");
508     else {
509       for (const char *p = arg; *p; p++)
510         if (csspace(*p)) {
511           error_with_file_and_line(filename, lineno,
512                                    "invalid `prepro' argument `%1'"
513                                    ": program name required", arg);
514           return;
515         }
516       predriver = strsave(arg);
517     }
518   }
519   if (strcmp(command, "postpro") == 0) {
520     if (arg == 0)
521       error_with_file_and_line(filename, lineno,
522                                "`postpro' command requires an argument");
523     else {
524       for (const char *p = arg; *p; p++)
525         if (csspace(*p)) {
526           error_with_file_and_line(filename, lineno,
527                                    "invalid `postpro' argument `%1'"
528                                    ": program name required", arg);
529           return;
530         }
531       postdriver = strsave(arg);
532     }
533   }
534 }
535
536 void print_commands(FILE *fp)
537 {
538   int last;
539   for (last = SPOOL_INDEX; last >= 0; last--)
540     if (commands[last].get_name() != 0)
541       break;
542   for (int i = 0; i <= last; i++)
543     if (commands[i].get_name() != 0)
544       commands[i].print(i == last, fp);
545 }
546
547 // Run the commands. Return the code with which to exit.
548
549 int run_commands(int no_pipe)
550 {
551   char **v[NCOMMANDS];
552   int j = 0;
553   for (int i = 0; i < NCOMMANDS; i++)
554     if (commands[i].get_name() != 0)
555       v[j++] = commands[i].get_argv();
556   return run_pipeline(j, v, no_pipe);
557 }
558
559 possible_command::possible_command()
560 : name(0), argv(0)
561 {
562 }
563
564 possible_command::~possible_command()
565 {
566   a_delete name;
567   a_delete argv;
568 }
569
570 void possible_command::set_name(const char *s)
571 {
572   a_delete name;
573   name = strsave(s);
574 }
575
576 void possible_command::clear_name()
577 {
578   a_delete name;
579   a_delete argv;
580   name = NULL;
581   argv = NULL;
582 }
583
584 void possible_command::set_name(const char *s1, const char *s2)
585 {
586   a_delete name;
587   name = new char[strlen(s1) + strlen(s2) + 1];
588   strcpy(name, s1);
589   strcat(name, s2);
590 }
591
592 const char *possible_command::get_name()
593 {
594   return name;
595 }
596
597 void possible_command::clear_args()
598 {
599   args.clear();
600 }
601
602 void possible_command::append_arg(const char *s, const char *t)
603 {
604   args += s;
605   if (t)
606     args += t;
607   args += '\0';
608 }
609
610 void possible_command::insert_arg(const char *s)
611 {
612   string str(s);
613   str += '\0';
614   str += args;
615   args = str;
616 }
617
618 void possible_command::insert_args(string s)
619 {
620   const char *p = s.contents();
621   const char *end = p + s.length();
622   int l = 0;
623   if (p >= end)
624     return;
625   // find the total number of arguments in our string
626   do {
627     l++;
628     p = strchr(p, '\0') + 1;
629   } while (p < end);
630   // now insert each argument preserving the order
631   for (int i = l - 1; i >= 0; i--) {
632     p = s.contents();
633     for (int j = 0; j < i; j++)
634       p = strchr(p, '\0') + 1;
635     insert_arg(p);
636   }
637 }
638
639 void possible_command::build_argv()
640 {
641   if (argv)
642     return;
643   // Count the number of arguments.
644   int len = args.length();
645   int argc = 1;
646   char *p = 0;
647   if (len > 0) {
648     p = &args[0];
649     for (int i = 0; i < len; i++)
650       if (p[i] == '\0')
651         argc++;
652   }
653   // Build an argument vector.
654   argv = new char *[argc + 1];
655   argv[0] = name;
656   for (int i = 1; i < argc; i++) {
657     argv[i] = p;
658     p = strchr(p, '\0') + 1;
659   }
660   argv[argc] = 0;
661 }
662
663 void possible_command::print(int is_last, FILE *fp)
664 {
665   build_argv();
666   if (IS_BSHELL(argv[0])
667       && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
668       && argv[2] != 0 && argv[3] == 0)
669     fputs(argv[2], fp);
670   else {
671     fputs(argv[0], fp);
672     string str;
673     for (int i = 1; argv[i] != 0; i++) {
674       str.clear();
675       append_arg_to_string(argv[i], str);
676       put_string(str, fp);
677     }
678   }
679   if (is_last)
680     putc('\n', fp);
681   else
682     fputs(" | ", fp);
683 }
684
685 void append_arg_to_string(const char *arg, string &str)
686 {
687   str += ' ';
688   int needs_quoting = 0;
689   int contains_single_quote = 0;
690   const char*p;
691   for (p = arg; *p != '\0'; p++)
692     switch (*p) {
693     case ';':
694     case '&':
695     case '(':
696     case ')':
697     case '|':
698     case '^':
699     case '<':
700     case '>':
701     case '\n':
702     case ' ':
703     case '\t':
704     case '\\':
705     case '"':
706     case '$':
707     case '?':
708     case '*':
709       needs_quoting = 1;
710       break;
711     case '\'':
712       contains_single_quote = 1;
713       break;
714     }
715   if (contains_single_quote || arg[0] == '\0') {
716     str += '"';
717     for (p = arg; *p != '\0'; p++)
718       switch (*p) {
719       case '"':
720       case '\\':
721       case '$':
722         str += '\\';
723         // fall through
724       default:
725         str += *p;
726         break;
727       }
728     str += '"';
729   }
730   else if (needs_quoting) {
731     str += '\'';
732     str += arg;
733     str += '\'';
734   }
735   else
736     str += arg;
737 }
738
739 char **possible_command::get_argv()
740 {
741   build_argv();
742   return argv;
743 }
744
745 void synopsis(FILE *stream)
746 {
747   fprintf(stream,
748 "usage: %s [-abceghiklpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
749 "       [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
750 "       [-Darg] [-Karg] [-Larg] [-Idir] [files...]\n",
751           program_name);
752 }
753
754 void help()
755 {
756   synopsis(stdout);
757   fputs("\n"
758 "-h\tprint this message\n"
759 "-k\tpreprocess with preconv\n"
760 "-t\tpreprocess with tbl\n"
761 "-p\tpreprocess with pic\n"
762 "-e\tpreprocess with eqn\n"
763 "-g\tpreprocess with grn\n"
764 "-G\tpreprocess with grap\n"
765 "-s\tpreprocess with soelim\n"
766 "-R\tpreprocess with refer\n"
767 "-Tdev\tuse device dev\n"
768 "-X\tuse X11 previewer rather than usual postprocessor\n"
769 "-mname\tread macros tmac.name\n"
770 "-dcs\tdefine a string c as s\n"
771 "-rcn\tdefine a number register c as n\n"
772 "-nnum\tnumber first page n\n"
773 "-olist\toutput only pages in list\n"
774 "-ffam\tuse fam as the default font family\n"
775 "-Fdir\tsearch dir for device directories\n"
776 "-Mdir\tsearch dir for macro files\n"
777 "-v\tprint version number\n"
778 "-z\tsuppress formatted output\n"
779 "-Z\tdon't postprocess\n"
780 "-a\tproduce ASCII description of output\n"
781 "-i\tread standard input after named input files\n"
782 "-wname\tenable warning name\n"
783 "-Wname\tinhibit warning name\n"
784 "-E\tinhibit all errors\n"
785 "-b\tprint backtraces with errors or warnings\n"
786 "-l\tspool the output\n"
787 "-c\tdisable color output\n"
788 "-C\tenable compatibility mode\n"
789 "-V\tprint commands on stdout instead of running them\n"
790 "-Parg\tpass arg to the postprocessor\n"
791 "-Larg\tpass arg to the spooler\n"
792 "-N\tdon't allow newlines within eqn delimiters\n"
793 "-S\tenable safer mode (the default)\n"
794 "-U\tenable unsafe mode\n"
795 "-Idir\tsearch dir for soelim, troff, and grops.  Implies -s\n"
796 "-Karg\tuse arg as input encoding.  Implies -k\n"
797 "-Darg\tuse arg as default input encoding.  Implies -k\n"
798 "\n",
799         stdout);
800   exit(0);
801 }
802
803 void usage(FILE *stream)
804 {
805   synopsis(stream);
806   fprintf(stream, "%s -h gives more help\n", program_name);
807 }
808
809 extern "C" {
810
811 void c_error(const char *format, const char *arg1, const char *arg2,
812              const char *arg3)
813 {
814   error(format, arg1, arg2, arg3);
815 }
816
817 void c_fatal(const char *format, const char *arg1, const char *arg2,
818              const char *arg3)
819 {
820   fatal(format, arg1, arg2, arg3);
821 }
822
823 }