/contrib/groff/src/roff/groff/groff.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 765 lines · 679 code · 45 blank · 41 comment · 127 complexity · 8b5ac4897ea36e19f576210a21846f26 MD5 · raw file

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