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