]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/groff/groff/groff.cc
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / contrib / groff / groff / groff.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
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.
11
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.
16
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. */
20
21 // A front end for groff.
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <errno.h>
28
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"
39
40 #define BSHELL "/bin/sh"
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 #ifndef STDLIB_H_DECLARES_PUTENV
48 extern "C" {
49   int putenv(const char *);
50 }
51 #endif /* not STDLIB_H_DECLARES_PUTENV */
52
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 EQN_INDEX = TBL_INDEX + 1;
58 const int TROFF_INDEX = EQN_INDEX + 1;
59 const int POST_INDEX = TROFF_INDEX + 1;
60 const int SPOOL_INDEX = POST_INDEX + 1;
61
62 const int NCOMMANDS = SPOOL_INDEX + 1;
63
64 class possible_command {
65   char *name;
66   string args;
67   char **argv;
68
69   void build_argv();
70 public:
71   possible_command();
72   ~possible_command();
73   void set_name(const char *);
74   void set_name(const char *, const char *);
75   const char *get_name();
76   void append_arg(const char *, const char * = 0);
77   void insert_arg(const char *);
78   void clear_args();
79   char **get_argv();
80   void print(int is_last, FILE *fp);
81 };
82
83 int lflag = 0;
84 char *spooler = 0;
85 char *driver = 0;
86
87 possible_command commands[NCOMMANDS];
88
89 int run_commands();
90 void print_commands();
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 *);
95
96 void usage();
97 void help();
98
99 int main(int argc, char **argv)
100 {
101   program_name = argv[0];
102   static char stderr_buf[BUFSIZ];
103   setbuf(stderr, stderr_buf);
104   assert(NCOMMANDS <= MAX_COMMANDS);
105   string Pargs, Largs, Fargs;
106   int Vflag = 0;
107   int zflag = 0;
108   int iflag = 0;
109   int Xflag = 0;
110   int safer_flag = 1;
111   int opt;
112   const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
113   if (!command_prefix)
114     command_prefix = PROG_PREFIX;
115   commands[TROFF_INDEX].set_name(command_prefix, "troff");
116   while ((opt = getopt(argc, argv,
117                        "abCd:eEf:F:hiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ"))
118          != EOF) {
119     char buf[3];
120     buf[0] = '-';
121     buf[1] = opt;
122     buf[2] = '\0';
123     switch (opt) {
124     case 'i':
125       iflag = 1;
126       break;
127     case 'I':
128       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
129       commands[SOELIM_INDEX].append_arg(buf, optarg);
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 'e':
138       commands[EQN_INDEX].set_name(command_prefix, "eqn");
139       break;
140     case 's':
141       commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
142       break;
143     case 'R':
144       commands[REFER_INDEX].set_name(command_prefix, "refer");
145       break;
146     case 'z':
147     case 'a':
148       commands[TROFF_INDEX].append_arg(buf);
149       // fall through
150     case 'Z':
151       zflag++;
152       break;
153     case 'l':
154       lflag++;
155       break;
156     case 'V':
157       Vflag++;
158       break;
159     case 'v':
160     case 'C':
161       commands[SOELIM_INDEX].append_arg(buf);
162       commands[PIC_INDEX].append_arg(buf);
163       commands[TBL_INDEX].append_arg(buf);
164       commands[EQN_INDEX].append_arg(buf);
165       commands[TROFF_INDEX].append_arg(buf);
166       break;
167     case 'N':
168       commands[EQN_INDEX].append_arg(buf);
169       break;
170     case 'h':
171       help();
172       break;
173     case 'E':
174     case 'b':
175       commands[TROFF_INDEX].append_arg(buf);
176       break;
177     case 'S':
178       safer_flag = 1;
179       break;
180     case 'U':
181       safer_flag = 0;
182       break;
183     case 'T':
184       if (strcmp(optarg, "Xps") == 0) {
185         warning("-TXps option is obsolete: use -X -Tps instead");
186         device = "ps";
187         Xflag++;
188       }
189       else
190         device = optarg;
191       break;
192     case 'F':
193       font::command_line_font_dir(optarg);
194       if (Fargs.length() > 0) {
195         Fargs += ':';
196         Fargs += optarg;
197       }
198       else
199         Fargs = optarg;
200       break;
201     case 'f':
202     case 'o':
203     case 'm':
204     case 'r':
205     case 'd':
206     case 'n':
207     case 'w':
208     case 'W':
209       commands[TROFF_INDEX].append_arg(buf, optarg);
210       break;
211     case 'M':
212       commands[EQN_INDEX].append_arg(buf, optarg);
213       commands[TROFF_INDEX].append_arg(buf, optarg);
214       break;
215     case 'P':
216       Pargs += optarg;
217       Pargs += '\0';
218       break;
219     case 'L':
220       append_arg_to_string(optarg, Largs);
221       break;
222     case 'X':
223       Xflag++;
224       break;
225     case '?':
226       usage();
227       break;
228     default:
229       assert(0);
230       break;
231     }
232   }
233   if (safer_flag) {
234     commands[PIC_INDEX].append_arg("-S");
235     commands[TROFF_INDEX].insert_arg("-msafer");
236   } else {
237     commands[TROFF_INDEX].insert_arg("-U");
238   }
239   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
240   if (!font::load_desc())
241     fatal("invalid device `%1'", device);
242   if (!driver)
243     fatal("no `postpro' command in DESC file for device `%1'", device);
244   const char *real_driver = 0;
245   if (Xflag) {
246     real_driver = driver;
247     driver = GXDITVIEW;
248     commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
249   }
250   if (driver)
251     commands[POST_INDEX].set_name(driver);
252   int gxditview_flag = driver && strcmp(xbasename(driver), GXDITVIEW) == 0;
253   if (gxditview_flag && argc - optind == 1) {
254     commands[POST_INDEX].append_arg("-title");
255     commands[POST_INDEX].append_arg(argv[optind]);
256     commands[POST_INDEX].append_arg("-xrm");
257     commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
258     string filename_string("|");
259     append_arg_to_string(argv[0], filename_string);
260     append_arg_to_string("-Z", filename_string);
261     for (int i = 1; i < argc; i++)
262       append_arg_to_string(argv[i], filename_string);
263     filename_string += '\0';
264     commands[POST_INDEX].append_arg("-filename");
265     commands[POST_INDEX].append_arg(filename_string.contents());
266   }
267   if (gxditview_flag && Xflag) {
268     string print_string(real_driver);
269     if (spooler) {
270       print_string += " | ";
271       print_string += spooler;
272       print_string += Largs;
273     }
274     print_string += '\0';
275     commands[POST_INDEX].append_arg("-printCommand");
276     commands[POST_INDEX].append_arg(print_string.contents());
277   }
278   const char *p = Pargs.contents();
279   const char *end = p + Pargs.length();
280   while (p < end) {
281     commands[POST_INDEX].append_arg(p);
282     p = strchr(p, '\0') + 1;
283   }
284   if (gxditview_flag)
285     commands[POST_INDEX].append_arg("-");
286   if (lflag && !Xflag && spooler) {
287     commands[SPOOL_INDEX].set_name(BSHELL);
288     commands[SPOOL_INDEX].append_arg("-c");
289     Largs += '\0';
290     Largs = spooler + Largs;
291     commands[SPOOL_INDEX].append_arg(Largs.contents());
292   }
293   if (zflag) {
294     commands[POST_INDEX].set_name(0);
295     commands[SPOOL_INDEX].set_name(0);
296   }
297   commands[TROFF_INDEX].append_arg("-T", device);
298   commands[EQN_INDEX].append_arg("-T", device);
299
300   int first_index;
301   for (first_index = 0; first_index < TROFF_INDEX; first_index++)
302     if (commands[first_index].get_name() != 0)
303       break;
304   if (optind < argc) {
305     if (argv[optind][0] == '-' && argv[optind][1] != '\0')
306       commands[first_index].append_arg("--");
307     for (int i = optind; i < argc; i++)
308       commands[first_index].append_arg(argv[i]);
309     if (iflag)
310       commands[first_index].append_arg("-");
311   }
312   if (Fargs.length() > 0) {
313     string e = "GROFF_FONT_PATH";
314     e += '=';
315     e += Fargs;
316     char *fontpath = getenv("GROFF_FONT_PATH");
317     if (fontpath && *fontpath) {
318       e += ':';
319       e += fontpath;
320     }
321     e += '\0';
322     if (putenv(strsave(e.contents())))
323       fatal("putenv failed");
324   }
325   if (Vflag) {
326     print_commands();
327     exit(0);
328   }
329   return run_commands();
330 }
331
332 const char *xbasename(const char *s)
333 {
334   if (!s)
335     return 0;
336   const char *p = strrchr(s, '/');
337   return p ? p + 1 : s;
338 }
339
340 void handle_unknown_desc_command(const char *command, const char *arg,
341                                  const char *filename, int lineno)
342 {
343   if (strcmp(command, "print") == 0) {
344     if (arg == 0)
345       error_with_file_and_line(filename, lineno,
346                                "`print' command requires an argument");
347     else
348       spooler = strsave(arg);
349   }
350   if (strcmp(command, "postpro") == 0) {
351     if (arg == 0)
352       error_with_file_and_line(filename, lineno,
353                                "`postpro' command requires an argument");
354     else {
355       for (const char *p = arg; *p; p++)
356         if (csspace(*p)) {
357           error_with_file_and_line(filename, lineno,
358                                    "invalid `postpro' argument `%1'"
359                                    ": program name required", arg);
360           return;
361         }
362       driver = strsave(arg);
363     }
364   }
365 }
366
367 void print_commands()
368 {
369   int last;
370   for (last = SPOOL_INDEX; last >= 0; last--)
371     if (commands[last].get_name() != 0)
372       break;
373   for (int i = 0; i <= last; i++)
374     if (commands[i].get_name() != 0)
375       commands[i].print(i == last, stdout);
376 }
377
378 // Run the commands. Return the code with which to exit.
379
380 int run_commands()
381 {
382   char **v[NCOMMANDS];
383   int j = 0;
384   for (int i = 0; i < NCOMMANDS; i++)
385     if (commands[i].get_name() != 0)
386       v[j++] = commands[i].get_argv();
387   return run_pipeline(j, v);
388 }
389
390 possible_command::possible_command()
391 : name(0), argv(0)
392 {
393 }
394
395 possible_command::~possible_command()
396 {
397   a_delete name;
398   a_delete argv;
399 }
400
401 void possible_command::set_name(const char *s)
402 {
403   a_delete name;
404   name = strsave(s);
405 }
406
407 void possible_command::set_name(const char *s1, const char *s2)
408 {
409   a_delete name;
410   name = new char[strlen(s1) + strlen(s2) + 1];
411   strcpy(name, s1);
412   strcat(name, s2);
413 }
414
415 const char *possible_command::get_name()
416 {
417   return name;
418 }
419
420 void possible_command::clear_args()
421 {
422   args.clear();
423 }
424
425 void possible_command::append_arg(const char *s, const char *t)
426 {
427   args += s;
428   if (t)
429     args += t;
430   args += '\0';
431 }
432
433 void possible_command::insert_arg(const char *s)
434 {
435   string str(s);
436   str += '\0';
437   str += args;
438   args = str;
439 }
440
441 void possible_command::build_argv()
442 {
443   if (argv)
444     return;
445   // Count the number of arguments.
446   int len = args.length();
447   int argc = 1;
448   char *p = 0;
449   if (len > 0) {
450     p = &args[0];
451     for (int i = 0; i < len; i++)
452       if (p[i] == '\0')
453         argc++;
454   }
455   // Build an argument vector.
456   argv = new char *[argc + 1];
457   argv[0] = name;
458   for (int i = 1; i < argc; i++) {
459     argv[i] = p;
460     p = strchr(p, '\0') + 1;
461   }
462   argv[argc] = 0;
463 }
464
465 void possible_command::print(int is_last, FILE *fp)
466 {
467   build_argv();
468   if (argv[0] != 0 && strcmp(argv[0], BSHELL) == 0
469       && argv[1] != 0 && strcmp(argv[1], "-c") == 0
470       && argv[2] != 0 && argv[3] == 0)
471     fputs(argv[2], fp);
472   else {
473     fputs(argv[0], fp);
474     string str;
475     for (int i = 1; argv[i] != 0; i++) {
476       str.clear();
477       append_arg_to_string(argv[i], str);
478       put_string(str, fp);
479     }
480   }
481   if (is_last)
482     putc('\n', fp);
483   else
484     fputs(" | ", fp);
485 }
486
487 void append_arg_to_string(const char *arg, string &str)
488 {
489   str += ' ';
490   int needs_quoting = 0;
491   int contains_single_quote = 0;
492   const char*p;
493   for (p = arg; *p != '\0'; p++)
494     switch (*p) {
495     case ';':
496     case '&':
497     case '(':
498     case ')':
499     case '|':
500     case '^':
501     case '<':
502     case '>':
503     case '\n':
504     case ' ':
505     case '\t':
506     case '\\':
507     case '"':
508     case '$':
509     case '?':
510     case '*':
511       needs_quoting = 1;
512       break;
513     case '\'':
514       contains_single_quote = 1;
515       break;
516     }
517   if (contains_single_quote || arg[0] == '\0') {
518     str += '"';
519     for (p = arg; *p != '\0'; p++)
520       switch (*p) {
521       case '"':
522       case '\\':
523       case '$':
524         str += '\\';
525         // fall through
526       default:
527         str += *p;
528         break;
529       }
530     str += '"';
531   }
532   else if (needs_quoting) {
533     str += '\'';
534     str += arg;
535     str += '\'';
536   }
537   else
538     str += arg;
539 }
540
541 char **possible_command::get_argv()
542 {
543   build_argv();
544   return argv;
545 }
546
547 void synopsis()
548 {
549   fprintf(stderr,
550 "usage: %s [-abehilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam] [-wname]\n"
551 "       [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg] [-Larg]\n"
552 "       [files...]\n",
553           program_name);
554 }
555
556 void help()
557 {
558   synopsis();
559   fputs("\n"
560 "-h\tprint this message\n"
561 "-t\tpreprocess with tbl\n"
562 "-p\tpreprocess with pic\n"
563 "-e\tpreprocess with eqn\n"
564 "-s\tpreprocess with soelim\n"
565 "-R\tpreprocess with refer\n"
566 "-Tdev\tuse device dev\n"
567 "-X\tuse X11 previewer rather than usual postprocessor\n"
568 "-mname\tread macros tmac.name\n"
569 "-dcs\tdefine a string c as s\n"
570 "-rcn\tdefine a number register c as n\n"
571 "-nnum\tnumber first page n\n"
572 "-olist\toutput only pages in list\n"
573 "-ffam\tuse fam as the default font family\n"
574 "-Fdir\tsearch directory dir for device directories\n"
575 "-Mdir\tsearch dir for macro files\n"
576 "-v\tprint version number\n"
577 "-z\tsuppress formatted output\n"
578 "-Z\tdon't postprocess\n"
579 "-a\tproduce ASCII description of output\n"
580 "-i\tread standard input after named input files\n"
581 "-wname\tenable warning name\n"
582 "-Wname\tinhibit warning name\n"
583 "-E\tinhibit all errors\n"
584 "-b\tprint backtraces with errors or warnings\n"
585 "-l\tspool the output\n"
586 "-C\tenable compatibility mode\n"
587 "-V\tprint commands on stdout instead of running them\n"
588 "-Parg\tpass arg to the postprocessor\n"
589 "-Larg\tpass arg to the spooler\n"
590 "-N\tdon't allow newlines within eqn delimiters\n"
591 "-S\tenable safer mode (the default)\n"
592 "-U\tenable unsafe mode\n"
593 "\n",
594         stderr);
595   exit(0);
596 }
597
598 void usage()
599 {
600   synopsis();
601   fprintf(stderr, "%s -h gives more help\n", program_name);
602   exit(1);
603 }
604
605 extern "C" {
606
607 void c_error(const char *format, const char *arg1, const char *arg2,
608              const char *arg3)
609 {
610   error(format, arg1, arg2, arg3);
611 }
612
613 void c_fatal(const char *format, const char *arg1, const char *arg2,
614              const char *arg3)
615 {
616   fatal(format, arg1, arg2, arg3);
617 }
618
619 }