1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004
3151497Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru// A front end for groff.
23114402Sru
24114402Sru#include "lib.h"
25114402Sru
26114402Sru#include <stdlib.h>
27114402Sru#include <signal.h>
28114402Sru#include <errno.h>
29114402Sru
30114402Sru#include "assert.h"
31114402Sru#include "errarg.h"
32114402Sru#include "error.h"
33114402Sru#include "stringclass.h"
34114402Sru#include "cset.h"
35114402Sru#include "font.h"
36114402Sru#include "device.h"
37114402Sru#include "pipeline.h"
38114402Sru#include "nonposix.h"
39114402Sru#include "defs.h"
40114402Sru
41114402Sru#define GXDITVIEW "gxditview"
42114402Sru
43114402Sru// troff will be passed an argument of -rXREG=1 if the -X option is
44114402Sru// specified
45114402Sru#define XREG ".X"
46114402Sru
47114402Sru#ifdef NEED_DECLARATION_PUTENV
48114402Sruextern "C" {
49114402Sru  int putenv(const char *);
50114402Sru}
51114402Sru#endif /* NEED_DECLARATION_PUTENV */
52114402Sru
53114402Sru// The number of commands must be in sync with MAX_COMMANDS in pipeline.h
54114402Sruconst int SOELIM_INDEX = 0;
55114402Sruconst int REFER_INDEX = SOELIM_INDEX + 1;
56114402Sruconst int GRAP_INDEX = REFER_INDEX + 1;
57114402Sruconst int PIC_INDEX = GRAP_INDEX + 1;
58114402Sruconst int TBL_INDEX = PIC_INDEX + 1;
59114402Sruconst int GRN_INDEX = TBL_INDEX + 1;
60114402Sruconst int EQN_INDEX = GRN_INDEX + 1;
61114402Sruconst int TROFF_INDEX = EQN_INDEX + 1;
62114402Sruconst int POST_INDEX = TROFF_INDEX + 1;
63114402Sruconst int SPOOL_INDEX = POST_INDEX + 1;
64114402Sru
65114402Sruconst int NCOMMANDS = SPOOL_INDEX + 1;
66114402Sru
67114402Sruclass possible_command {
68114402Sru  char *name;
69114402Sru  string args;
70114402Sru  char **argv;
71114402Sru
72114402Sru  void build_argv();
73114402Srupublic:
74114402Sru  possible_command();
75114402Sru  ~possible_command();
76114402Sru  void set_name(const char *);
77114402Sru  void set_name(const char *, const char *);
78114402Sru  const char *get_name();
79114402Sru  void append_arg(const char *, const char * = 0);
80114402Sru  void insert_arg(const char *);
81114402Sru  void insert_args(string s);
82114402Sru  void clear_args();
83114402Sru  char **get_argv();
84114402Sru  void print(int is_last, FILE *fp);
85114402Sru};
86114402Sru
87114402Sruextern "C" const char *Version_string;
88114402Sru
89114402Sruint lflag = 0;
90114402Sruchar *spooler = 0;
91114402Sruchar *postdriver = 0;
92114402Sruchar *predriver = 0;
93114402Sru
94114402Srupossible_command commands[NCOMMANDS];
95114402Sru
96114402Sruint run_commands(int no_pipe);
97151497Sruvoid print_commands(FILE *);
98114402Sruvoid append_arg_to_string(const char *arg, string &str);
99114402Sruvoid handle_unknown_desc_command(const char *command, const char *arg,
100114402Sru				 const char *filename, int lineno);
101114402Sruconst char *xbasename(const char *);
102114402Sru
103114402Sruvoid usage(FILE *stream);
104114402Sruvoid help();
105114402Sru
106114402Sruint main(int argc, char **argv)
107114402Sru{
108114402Sru  program_name = argv[0];
109114402Sru  static char stderr_buf[BUFSIZ];
110114402Sru  setbuf(stderr, stderr_buf);
111114402Sru  assert(NCOMMANDS <= MAX_COMMANDS);
112114402Sru  string Pargs, Largs, Fargs;
113114402Sru  int vflag = 0;
114114402Sru  int Vflag = 0;
115114402Sru  int zflag = 0;
116114402Sru  int iflag = 0;
117114402Sru  int Xflag = 0;
118151497Sru  int oflag = 0;
119114402Sru  int safer_flag = 1;
120114402Sru  int opt;
121114402Sru  const char *command_prefix = getenv("GROFF_COMMAND_PREFIX");
122114402Sru  if (!command_prefix)
123114402Sru    command_prefix = PROG_PREFIX;
124114402Sru  commands[TROFF_INDEX].set_name(command_prefix, "troff");
125114402Sru  static const struct option long_options[] = {
126114402Sru    { "help", no_argument, 0, 'h' },
127114402Sru    { "version", no_argument, 0, 'v' },
128114402Sru    { NULL, 0, 0, 0 }
129114402Sru  };
130114402Sru  while ((opt = getopt_long(argc, argv,
131114402Sru			    "abcCd:eEf:F:gGhiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ",
132114402Sru			    long_options, NULL))
133114402Sru	 != EOF) {
134114402Sru    char buf[3];
135114402Sru    buf[0] = '-';
136114402Sru    buf[1] = opt;
137114402Sru    buf[2] = '\0';
138114402Sru    switch (opt) {
139114402Sru    case 'i':
140114402Sru      iflag = 1;
141114402Sru      break;
142114402Sru    case 'I':
143114402Sru      commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
144114402Sru      commands[SOELIM_INDEX].append_arg(buf, optarg);
145151497Sru      // .psbb may need to search for files
146151497Sru      commands[TROFF_INDEX].append_arg(buf, optarg);
147151497Sru      // \X'ps:import' may need to search for files
148151497Sru      Pargs += buf;
149151497Sru      Pargs += optarg;
150151497Sru      Pargs += '\0';
151114402Sru      break;
152114402Sru    case 't':
153114402Sru      commands[TBL_INDEX].set_name(command_prefix, "tbl");
154114402Sru      break;
155114402Sru    case 'p':
156114402Sru      commands[PIC_INDEX].set_name(command_prefix, "pic");
157114402Sru      break;
158114402Sru    case 'g':
159114402Sru      commands[GRN_INDEX].set_name(command_prefix, "grn");
160114402Sru      break;
161114402Sru    case 'G':
162114402Sru      commands[GRAP_INDEX].set_name(command_prefix, "grap");
163114402Sru      break;
164114402Sru    case 'e':
165114402Sru      commands[EQN_INDEX].set_name(command_prefix, "eqn");
166114402Sru      break;
167114402Sru    case 's':
168114402Sru      commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
169114402Sru      break;
170114402Sru    case 'R':
171114402Sru      commands[REFER_INDEX].set_name(command_prefix, "refer");
172114402Sru      break;
173114402Sru    case 'z':
174114402Sru    case 'a':
175114402Sru      commands[TROFF_INDEX].append_arg(buf);
176114402Sru      // fall through
177114402Sru    case 'Z':
178114402Sru      zflag++;
179114402Sru      break;
180114402Sru    case 'l':
181114402Sru      lflag++;
182114402Sru      break;
183114402Sru    case 'V':
184114402Sru      Vflag++;
185114402Sru      break;
186114402Sru    case 'v':
187114402Sru      vflag = 1;
188114402Sru      {
189114402Sru	printf("GNU groff version %s\n", Version_string);
190151497Sru	printf("Copyright (C) 2004 Free Software Foundation, Inc.\n"
191114402Sru	       "GNU groff comes with ABSOLUTELY NO WARRANTY.\n"
192114402Sru	       "You may redistribute copies of groff and its subprograms\n"
193114402Sru	       "under the terms of the GNU General Public License.\n"
194114402Sru	       "For more information about these matters, see the file named COPYING.\n");
195114402Sru	printf("\ncalled subprograms:\n\n");
196114402Sru        fflush(stdout);
197114402Sru      }
198114402Sru      commands[POST_INDEX].append_arg(buf);
199114402Sru      // fall through
200114402Sru    case 'C':
201114402Sru      commands[SOELIM_INDEX].append_arg(buf);
202114402Sru      commands[REFER_INDEX].append_arg(buf);
203114402Sru      commands[PIC_INDEX].append_arg(buf);
204114402Sru      commands[GRAP_INDEX].append_arg(buf);
205114402Sru      commands[TBL_INDEX].append_arg(buf);
206114402Sru      commands[GRN_INDEX].append_arg(buf);
207114402Sru      commands[EQN_INDEX].append_arg(buf);
208114402Sru      commands[TROFF_INDEX].append_arg(buf);
209114402Sru      break;
210114402Sru    case 'N':
211114402Sru      commands[EQN_INDEX].append_arg(buf);
212114402Sru      break;
213114402Sru    case 'h':
214114402Sru      help();
215114402Sru      break;
216114402Sru    case 'E':
217114402Sru    case 'b':
218114402Sru      commands[TROFF_INDEX].append_arg(buf);
219114402Sru      break;
220114402Sru    case 'c':
221114402Sru      commands[TROFF_INDEX].append_arg(buf);
222114402Sru      break;
223114402Sru    case 'S':
224114402Sru      safer_flag = 1;
225114402Sru      break;
226114402Sru    case 'U':
227114402Sru      safer_flag = 0;
228114402Sru      break;
229114402Sru    case 'T':
230114402Sru      if (strcmp(optarg, "html") == 0) {
231114402Sru	// force soelim to aid the html preprocessor
232114402Sru	commands[SOELIM_INDEX].set_name(command_prefix, "soelim");
233114402Sru      }
234114402Sru      if (strcmp(optarg, "Xps") == 0) {
235114402Sru	warning("-TXps option is obsolete: use -X -Tps instead");
236114402Sru	device = "ps";
237114402Sru	Xflag++;
238114402Sru      }
239114402Sru      else
240114402Sru	device = optarg;
241114402Sru      break;
242114402Sru    case 'F':
243114402Sru      font::command_line_font_dir(optarg);
244114402Sru      if (Fargs.length() > 0) {
245151497Sru	Fargs += PATH_SEP_CHAR;
246114402Sru	Fargs += optarg;
247114402Sru      }
248114402Sru      else
249114402Sru	Fargs = optarg;
250114402Sru      break;
251151497Sru    case 'o':
252151497Sru      oflag = 1;
253114402Sru    case 'f':
254114402Sru    case 'm':
255114402Sru    case 'r':
256114402Sru    case 'd':
257114402Sru    case 'n':
258114402Sru    case 'w':
259114402Sru    case 'W':
260114402Sru      commands[TROFF_INDEX].append_arg(buf, optarg);
261114402Sru      break;
262114402Sru    case 'M':
263114402Sru      commands[EQN_INDEX].append_arg(buf, optarg);
264114402Sru      commands[GRAP_INDEX].append_arg(buf, optarg);
265114402Sru      commands[GRN_INDEX].append_arg(buf, optarg);
266114402Sru      commands[TROFF_INDEX].append_arg(buf, optarg);
267114402Sru      break;
268114402Sru    case 'P':
269114402Sru      Pargs += optarg;
270114402Sru      Pargs += '\0';
271114402Sru      break;
272114402Sru    case 'L':
273114402Sru      append_arg_to_string(optarg, Largs);
274114402Sru      break;
275114402Sru    case 'X':
276114402Sru      Xflag++;
277114402Sru      break;
278114402Sru    case '?':
279114402Sru      usage(stderr);
280114402Sru      exit(1);
281114402Sru      break;
282114402Sru    default:
283114402Sru      assert(0);
284114402Sru      break;
285114402Sru    }
286114402Sru  }
287114402Sru  if (safer_flag)
288114402Sru    commands[PIC_INDEX].append_arg("-S");
289114402Sru  else
290114402Sru    commands[TROFF_INDEX].insert_arg("-U");
291114402Sru  font::set_unknown_desc_command_handler(handle_unknown_desc_command);
292114402Sru  if (!font::load_desc())
293114402Sru    fatal("invalid device `%1'", device);
294114402Sru  if (!postdriver)
295114402Sru    fatal("no `postpro' command in DESC file for device `%1'", device);
296114402Sru  if (predriver && !zflag) {
297114402Sru    commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name());
298114402Sru    commands[TROFF_INDEX].set_name(predriver);
299114402Sru    // pass the device arguments to the predrivers as well
300114402Sru    commands[TROFF_INDEX].insert_args(Pargs);
301114402Sru    if (vflag)
302114402Sru      commands[TROFF_INDEX].insert_arg("-v");
303114402Sru  }
304114402Sru  const char *real_driver = 0;
305114402Sru  if (Xflag) {
306114402Sru    real_driver = postdriver;
307151497Sru    postdriver = (char *)GXDITVIEW;
308114402Sru    commands[TROFF_INDEX].append_arg("-r" XREG "=", "1");
309114402Sru  }
310114402Sru  if (postdriver)
311114402Sru    commands[POST_INDEX].set_name(postdriver);
312114402Sru  int gxditview_flag = postdriver && strcmp(xbasename(postdriver), GXDITVIEW) == 0;
313114402Sru  if (gxditview_flag && argc - optind == 1) {
314114402Sru    commands[POST_INDEX].append_arg("-title");
315114402Sru    commands[POST_INDEX].append_arg(argv[optind]);
316114402Sru    commands[POST_INDEX].append_arg("-xrm");
317114402Sru    commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
318114402Sru    string filename_string("|");
319114402Sru    append_arg_to_string(argv[0], filename_string);
320114402Sru    append_arg_to_string("-Z", filename_string);
321114402Sru    for (int i = 1; i < argc; i++)
322114402Sru      append_arg_to_string(argv[i], filename_string);
323114402Sru    filename_string += '\0';
324114402Sru    commands[POST_INDEX].append_arg("-filename");
325114402Sru    commands[POST_INDEX].append_arg(filename_string.contents());
326114402Sru  }
327114402Sru  if (gxditview_flag && Xflag) {
328114402Sru    string print_string(real_driver);
329114402Sru    if (spooler) {
330114402Sru      print_string += " | ";
331114402Sru      print_string += spooler;
332114402Sru      print_string += Largs;
333114402Sru    }
334114402Sru    print_string += '\0';
335114402Sru    commands[POST_INDEX].append_arg("-printCommand");
336114402Sru    commands[POST_INDEX].append_arg(print_string.contents());
337114402Sru  }
338114402Sru  const char *p = Pargs.contents();
339114402Sru  const char *end = p + Pargs.length();
340114402Sru  while (p < end) {
341114402Sru    commands[POST_INDEX].append_arg(p);
342114402Sru    p = strchr(p, '\0') + 1;
343114402Sru  }
344114402Sru  if (gxditview_flag)
345114402Sru    commands[POST_INDEX].append_arg("-");
346151497Sru  if (lflag && !vflag && !Xflag && spooler) {
347114402Sru    commands[SPOOL_INDEX].set_name(BSHELL);
348114402Sru    commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C);
349114402Sru    Largs += '\0';
350114402Sru    Largs = spooler + Largs;
351114402Sru    commands[SPOOL_INDEX].append_arg(Largs.contents());
352114402Sru  }
353114402Sru  if (zflag) {
354114402Sru    commands[POST_INDEX].set_name(0);
355114402Sru    commands[SPOOL_INDEX].set_name(0);
356114402Sru  }
357114402Sru  commands[TROFF_INDEX].append_arg("-T", device);
358114402Sru  // html renders equations as images via ps
359151497Sru  if (strcmp(device, "html") == 0) {
360151497Sru    if (oflag)
361151497Sru      fatal("`-o' option is invalid with device `html'");
362114402Sru    commands[EQN_INDEX].append_arg("-Tps:html");
363151497Sru  }
364114402Sru  else
365114402Sru    commands[EQN_INDEX].append_arg("-T", device);
366114402Sru
367114402Sru  commands[GRN_INDEX].append_arg("-T", device);
368114402Sru
369114402Sru  int first_index;
370114402Sru  for (first_index = 0; first_index < TROFF_INDEX; first_index++)
371114402Sru    if (commands[first_index].get_name() != 0)
372114402Sru      break;
373114402Sru  if (optind < argc) {
374114402Sru    if (argv[optind][0] == '-' && argv[optind][1] != '\0')
375114402Sru      commands[first_index].append_arg("--");
376114402Sru    for (int i = optind; i < argc; i++)
377114402Sru      commands[first_index].append_arg(argv[i]);
378114402Sru    if (iflag)
379114402Sru      commands[first_index].append_arg("-");
380114402Sru  }
381114402Sru  if (Fargs.length() > 0) {
382114402Sru    string e = "GROFF_FONT_PATH";
383114402Sru    e += '=';
384114402Sru    e += Fargs;
385114402Sru    char *fontpath = getenv("GROFF_FONT_PATH");
386114402Sru    if (fontpath && *fontpath) {
387151497Sru      e += PATH_SEP_CHAR;
388114402Sru      e += fontpath;
389114402Sru    }
390114402Sru    e += '\0';
391114402Sru    if (putenv(strsave(e.contents())))
392114402Sru      fatal("putenv failed");
393114402Sru  }
394114402Sru  {
395114402Sru    // we save the original path in GROFF_PATH__ and put it into the
396114402Sru    // environment -- troff will pick it up later.
397114402Sru    char *path = getenv("PATH");
398114402Sru    string e = "GROFF_PATH__";
399114402Sru    e += '=';
400114402Sru    if (path && *path)
401114402Sru      e += path;
402114402Sru    e += '\0';
403114402Sru    if (putenv(strsave(e.contents())))
404114402Sru      fatal("putenv failed");
405114402Sru    char *binpath = getenv("GROFF_BIN_PATH");
406114402Sru    string f = "PATH";
407114402Sru    f += '=';
408114402Sru    if (binpath && *binpath)
409114402Sru      f += binpath;
410114402Sru    else
411114402Sru      f += BINPATH;
412114402Sru    if (path && *path) {
413151497Sru      f += PATH_SEP_CHAR;
414114402Sru      f += path;
415114402Sru    }
416114402Sru    f += '\0';
417114402Sru    if (putenv(strsave(f.contents())))
418114402Sru      fatal("putenv failed");
419114402Sru  }
420151497Sru  if (Vflag)
421151497Sru    print_commands(Vflag == 1 ? stdout : stderr);
422151497Sru  if (Vflag == 1)
423114402Sru    exit(0);
424114402Sru  return run_commands(vflag);
425114402Sru}
426114402Sru
427114402Sruconst char *xbasename(const char *s)
428114402Sru{
429114402Sru  if (!s)
430114402Sru    return 0;
431114402Sru  // DIR_SEPS[] are possible directory separator characters, see nonposix.h
432114402Sru  // We want the rightmost separator of all possible ones.
433114402Sru  // Example: d:/foo\\bar.
434114402Sru  const char *p = strrchr(s, DIR_SEPS[0]), *p1;
435114402Sru  const char *sep = &DIR_SEPS[1];
436114402Sru
437114402Sru  while (*sep)
438114402Sru    {
439114402Sru      p1 = strrchr(s, *sep);
440114402Sru      if (p1 && (!p || p1 > p))
441114402Sru	p = p1;
442114402Sru      sep++;
443114402Sru    }
444114402Sru  return p ? p + 1 : s;
445114402Sru}
446114402Sru
447114402Sruvoid handle_unknown_desc_command(const char *command, const char *arg,
448114402Sru				 const char *filename, int lineno)
449114402Sru{
450114402Sru  if (strcmp(command, "print") == 0) {
451114402Sru    if (arg == 0)
452114402Sru      error_with_file_and_line(filename, lineno,
453114402Sru			       "`print' command requires an argument");
454114402Sru    else
455114402Sru      spooler = strsave(arg);
456114402Sru  }
457114402Sru  if (strcmp(command, "prepro") == 0) {
458114402Sru    if (arg == 0)
459114402Sru      error_with_file_and_line(filename, lineno,
460114402Sru			       "`prepro' command requires an argument");
461114402Sru    else {
462114402Sru      for (const char *p = arg; *p; p++)
463114402Sru	if (csspace(*p)) {
464114402Sru	  error_with_file_and_line(filename, lineno,
465114402Sru				   "invalid `prepro' argument `%1'"
466114402Sru				   ": program name required", arg);
467114402Sru	  return;
468114402Sru	}
469114402Sru      predriver = strsave(arg);
470114402Sru    }
471114402Sru  }
472114402Sru  if (strcmp(command, "postpro") == 0) {
473114402Sru    if (arg == 0)
474114402Sru      error_with_file_and_line(filename, lineno,
475114402Sru			       "`postpro' command requires an argument");
476114402Sru    else {
477114402Sru      for (const char *p = arg; *p; p++)
478114402Sru	if (csspace(*p)) {
479114402Sru	  error_with_file_and_line(filename, lineno,
480114402Sru				   "invalid `postpro' argument `%1'"
481114402Sru				   ": program name required", arg);
482114402Sru	  return;
483114402Sru	}
484114402Sru      postdriver = strsave(arg);
485114402Sru    }
486114402Sru  }
487114402Sru}
488114402Sru
489151497Sruvoid print_commands(FILE *fp)
490114402Sru{
491114402Sru  int last;
492114402Sru  for (last = SPOOL_INDEX; last >= 0; last--)
493114402Sru    if (commands[last].get_name() != 0)
494114402Sru      break;
495114402Sru  for (int i = 0; i <= last; i++)
496114402Sru    if (commands[i].get_name() != 0)
497151497Sru      commands[i].print(i == last, fp);
498114402Sru}
499114402Sru
500114402Sru// Run the commands. Return the code with which to exit.
501114402Sru
502114402Sruint run_commands(int no_pipe)
503114402Sru{
504114402Sru  char **v[NCOMMANDS];
505114402Sru  int j = 0;
506114402Sru  for (int i = 0; i < NCOMMANDS; i++)
507114402Sru    if (commands[i].get_name() != 0)
508114402Sru      v[j++] = commands[i].get_argv();
509114402Sru  return run_pipeline(j, v, no_pipe);
510114402Sru}
511114402Sru
512114402Srupossible_command::possible_command()
513114402Sru: name(0), argv(0)
514114402Sru{
515114402Sru}
516114402Sru
517114402Srupossible_command::~possible_command()
518114402Sru{
519114402Sru  a_delete name;
520114402Sru  a_delete argv;
521114402Sru}
522114402Sru
523114402Sruvoid possible_command::set_name(const char *s)
524114402Sru{
525114402Sru  a_delete name;
526114402Sru  name = strsave(s);
527114402Sru}
528114402Sru
529114402Sruvoid possible_command::set_name(const char *s1, const char *s2)
530114402Sru{
531114402Sru  a_delete name;
532114402Sru  name = new char[strlen(s1) + strlen(s2) + 1];
533114402Sru  strcpy(name, s1);
534114402Sru  strcat(name, s2);
535114402Sru}
536114402Sru
537114402Sruconst char *possible_command::get_name()
538114402Sru{
539114402Sru  return name;
540114402Sru}
541114402Sru
542114402Sruvoid possible_command::clear_args()
543114402Sru{
544114402Sru  args.clear();
545114402Sru}
546114402Sru
547114402Sruvoid possible_command::append_arg(const char *s, const char *t)
548114402Sru{
549114402Sru  args += s;
550114402Sru  if (t)
551114402Sru    args += t;
552114402Sru  args += '\0';
553114402Sru}
554114402Sru
555114402Sruvoid possible_command::insert_arg(const char *s)
556114402Sru{
557114402Sru  string str(s);
558114402Sru  str += '\0';
559114402Sru  str += args;
560114402Sru  args = str;
561114402Sru}
562114402Sru
563114402Sruvoid possible_command::insert_args(string s)
564114402Sru{
565114402Sru  const char *p = s.contents();
566114402Sru  const char *end = p + s.length();
567114402Sru  int l = 0;
568114402Sru  if (p >= end)
569114402Sru    return;
570114402Sru  // find the total number of arguments in our string
571114402Sru  do {
572114402Sru    l++;
573114402Sru    p = strchr(p, '\0') + 1;
574114402Sru  } while (p < end);
575114402Sru  // now insert each argument preserving the order
576114402Sru  for (int i = l - 1; i >= 0; i--) {
577114402Sru    p = s.contents();
578114402Sru    for (int j = 0; j < i; j++)
579114402Sru      p = strchr(p, '\0') + 1;
580114402Sru    insert_arg(p);
581114402Sru  }
582114402Sru}
583114402Sru
584114402Sruvoid possible_command::build_argv()
585114402Sru{
586114402Sru  if (argv)
587114402Sru    return;
588114402Sru  // Count the number of arguments.
589114402Sru  int len = args.length();
590114402Sru  int argc = 1;
591114402Sru  char *p = 0;
592114402Sru  if (len > 0) {
593114402Sru    p = &args[0];
594114402Sru    for (int i = 0; i < len; i++)
595114402Sru      if (p[i] == '\0')
596114402Sru	argc++;
597114402Sru  }
598114402Sru  // Build an argument vector.
599114402Sru  argv = new char *[argc + 1];
600114402Sru  argv[0] = name;
601114402Sru  for (int i = 1; i < argc; i++) {
602114402Sru    argv[i] = p;
603114402Sru    p = strchr(p, '\0') + 1;
604114402Sru  }
605114402Sru  argv[argc] = 0;
606114402Sru}
607114402Sru
608114402Sruvoid possible_command::print(int is_last, FILE *fp)
609114402Sru{
610114402Sru  build_argv();
611114402Sru  if (IS_BSHELL(argv[0])
612114402Sru      && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0
613114402Sru      && argv[2] != 0 && argv[3] == 0)
614114402Sru    fputs(argv[2], fp);
615114402Sru  else {
616114402Sru    fputs(argv[0], fp);
617114402Sru    string str;
618114402Sru    for (int i = 1; argv[i] != 0; i++) {
619114402Sru      str.clear();
620114402Sru      append_arg_to_string(argv[i], str);
621114402Sru      put_string(str, fp);
622114402Sru    }
623114402Sru  }
624114402Sru  if (is_last)
625114402Sru    putc('\n', fp);
626114402Sru  else
627114402Sru    fputs(" | ", fp);
628114402Sru}
629114402Sru
630114402Sruvoid append_arg_to_string(const char *arg, string &str)
631114402Sru{
632114402Sru  str += ' ';
633114402Sru  int needs_quoting = 0;
634114402Sru  int contains_single_quote = 0;
635114402Sru  const char*p;
636114402Sru  for (p = arg; *p != '\0'; p++)
637114402Sru    switch (*p) {
638114402Sru    case ';':
639114402Sru    case '&':
640114402Sru    case '(':
641114402Sru    case ')':
642114402Sru    case '|':
643114402Sru    case '^':
644114402Sru    case '<':
645114402Sru    case '>':
646114402Sru    case '\n':
647114402Sru    case ' ':
648114402Sru    case '\t':
649114402Sru    case '\\':
650114402Sru    case '"':
651114402Sru    case '$':
652114402Sru    case '?':
653114402Sru    case '*':
654114402Sru      needs_quoting = 1;
655114402Sru      break;
656114402Sru    case '\'':
657114402Sru      contains_single_quote = 1;
658114402Sru      break;
659114402Sru    }
660114402Sru  if (contains_single_quote || arg[0] == '\0') {
661114402Sru    str += '"';
662114402Sru    for (p = arg; *p != '\0'; p++)
663114402Sru      switch (*p) {
664114402Sru      case '"':
665114402Sru      case '\\':
666114402Sru      case '$':
667114402Sru	str += '\\';
668114402Sru	// fall through
669114402Sru      default:
670114402Sru	str += *p;
671114402Sru	break;
672114402Sru      }
673114402Sru    str += '"';
674114402Sru  }
675114402Sru  else if (needs_quoting) {
676114402Sru    str += '\'';
677114402Sru    str += arg;
678114402Sru    str += '\'';
679114402Sru  }
680114402Sru  else
681114402Sru    str += arg;
682114402Sru}
683114402Sru
684114402Sruchar **possible_command::get_argv()
685114402Sru{
686114402Sru  build_argv();
687114402Sru  return argv;
688114402Sru}
689114402Sru
690114402Sruvoid synopsis(FILE *stream)
691114402Sru{
692114402Sru  fprintf(stream,
693114402Sru"usage: %s [-abceghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
694114402Sru"       [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
695114402Sru"       [-Larg] [-Idir] [files...]\n",
696114402Sru	  program_name);
697114402Sru}
698114402Sru
699114402Sruvoid help()
700114402Sru{
701114402Sru  synopsis(stdout);
702114402Sru  fputs("\n"
703114402Sru"-h\tprint this message\n"
704114402Sru"-t\tpreprocess with tbl\n"
705114402Sru"-p\tpreprocess with pic\n"
706114402Sru"-e\tpreprocess with eqn\n"
707114402Sru"-g\tpreprocess with grn\n"
708114402Sru"-G\tpreprocess with grap\n"
709114402Sru"-s\tpreprocess with soelim\n"
710114402Sru"-R\tpreprocess with refer\n"
711114402Sru"-Tdev\tuse device dev\n"
712114402Sru"-X\tuse X11 previewer rather than usual postprocessor\n"
713114402Sru"-mname\tread macros tmac.name\n"
714114402Sru"-dcs\tdefine a string c as s\n"
715114402Sru"-rcn\tdefine a number register c as n\n"
716114402Sru"-nnum\tnumber first page n\n"
717114402Sru"-olist\toutput only pages in list\n"
718114402Sru"-ffam\tuse fam as the default font family\n"
719114402Sru"-Fdir\tsearch dir for device directories\n"
720114402Sru"-Mdir\tsearch dir for macro files\n"
721114402Sru"-v\tprint version number\n"
722114402Sru"-z\tsuppress formatted output\n"
723114402Sru"-Z\tdon't postprocess\n"
724114402Sru"-a\tproduce ASCII description of output\n"
725114402Sru"-i\tread standard input after named input files\n"
726114402Sru"-wname\tenable warning name\n"
727114402Sru"-Wname\tinhibit warning name\n"
728114402Sru"-E\tinhibit all errors\n"
729114402Sru"-b\tprint backtraces with errors or warnings\n"
730114402Sru"-l\tspool the output\n"
731114402Sru"-c\tdisable color output\n"
732114402Sru"-C\tenable compatibility mode\n"
733114402Sru"-V\tprint commands on stdout instead of running them\n"
734114402Sru"-Parg\tpass arg to the postprocessor\n"
735114402Sru"-Larg\tpass arg to the spooler\n"
736114402Sru"-N\tdon't allow newlines within eqn delimiters\n"
737114402Sru"-S\tenable safer mode (the default)\n"
738114402Sru"-U\tenable unsafe mode\n"
739151497Sru"-Idir\tsearch dir for soelim, troff, and grops.  Implies -s\n"
740114402Sru"\n",
741114402Sru	stdout);
742114402Sru  exit(0);
743114402Sru}
744114402Sru
745114402Sruvoid usage(FILE *stream)
746114402Sru{
747114402Sru  synopsis(stream);
748114402Sru  fprintf(stream, "%s -h gives more help\n", program_name);
749114402Sru}
750114402Sru
751114402Sruextern "C" {
752114402Sru
753114402Sruvoid c_error(const char *format, const char *arg1, const char *arg2,
754114402Sru	     const char *arg3)
755114402Sru{
756114402Sru  error(format, arg1, arg2, arg3);
757114402Sru}
758114402Sru
759114402Sruvoid c_fatal(const char *format, const char *arg1, const char *arg2,
760114402Sru	     const char *arg3)
761114402Sru{
762114402Sru  fatal(format, arg1, arg2, arg3);
763114402Sru}
764114402Sru
765114402Sru}
766