1210284Sjmallett// -*- C++ -*- 2232812Sjmallett/* Copyright (C) 1989-2000, 2001, 2002, 2003, 2004 3215990Sjmallett Free Software Foundation, Inc. 4210284Sjmallett Written by James Clark (jjc@jclark.com) 5210284Sjmallett 6215990SjmallettThis file is part of groff. 7215990Sjmallett 8215990Sjmallettgroff is free software; you can redistribute it and/or modify it under 9210284Sjmallettthe terms of the GNU General Public License as published by the Free 10215990SjmallettSoftware Foundation; either version 2, or (at your option) any later 11215990Sjmallettversion. 12210284Sjmallett 13215990Sjmallettgroff is distributed in the hope that it will be useful, but WITHOUT ANY 14215990SjmallettWARRANTY; without even the implied warranty of MERCHANTABILITY or 15215990SjmallettFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16215990Sjmallettfor more details. 17215990Sjmallett 18232812SjmallettYou should have received a copy of the GNU General Public License along 19215990Sjmallettwith groff; see the file COPYING. If not, write to the Free Software 20215990SjmallettFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21215990Sjmallett 22215990Sjmallett// A front end for groff. 23215990Sjmallett 24215990Sjmallett#include "lib.h" 25215990Sjmallett 26215990Sjmallett#include <stdlib.h> 27215990Sjmallett#include <signal.h> 28215990Sjmallett#include <errno.h> 29232812Sjmallett 30215990Sjmallett#include "assert.h" 31215990Sjmallett#include "errarg.h" 32215990Sjmallett#include "error.h" 33215990Sjmallett#include "stringclass.h" 34215990Sjmallett#include "cset.h" 35215990Sjmallett#include "font.h" 36215990Sjmallett#include "device.h" 37215990Sjmallett#include "pipeline.h" 38210284Sjmallett#include "nonposix.h" 39210284Sjmallett#include "defs.h" 40210284Sjmallett 41210284Sjmallett#define GXDITVIEW "gxditview" 42210284Sjmallett 43210284Sjmallett// troff will be passed an argument of -rXREG=1 if the -X option is 44210284Sjmallett// specified 45215990Sjmallett#define XREG ".X" 46210284Sjmallett 47210284Sjmallett#ifdef NEED_DECLARATION_PUTENV 48210284Sjmallettextern "C" { 49210284Sjmallett int putenv(const char *); 50210284Sjmallett} 51232812Sjmallett#endif /* NEED_DECLARATION_PUTENV */ 52210284Sjmallett 53210284Sjmallett// The number of commands must be in sync with MAX_COMMANDS in pipeline.h 54210284Sjmallettconst int SOELIM_INDEX = 0; 55210284Sjmallettconst int REFER_INDEX = SOELIM_INDEX + 1; 56210284Sjmallettconst int GRAP_INDEX = REFER_INDEX + 1; 57210284Sjmallettconst int PIC_INDEX = GRAP_INDEX + 1; 58210284Sjmallettconst int TBL_INDEX = PIC_INDEX + 1; 59210284Sjmallettconst int GRN_INDEX = TBL_INDEX + 1; 60215990Sjmallettconst int EQN_INDEX = GRN_INDEX + 1; 61210284Sjmallettconst int TROFF_INDEX = EQN_INDEX + 1; 62210284Sjmallettconst int POST_INDEX = TROFF_INDEX + 1; 63210284Sjmallettconst int SPOOL_INDEX = POST_INDEX + 1; 64210284Sjmallett 65210284Sjmallettconst int NCOMMANDS = SPOOL_INDEX + 1; 66210284Sjmallett 67210284Sjmallettclass possible_command { 68210284Sjmallett char *name; 69210284Sjmallett string args; 70210284Sjmallett char **argv; 71210284Sjmallett 72210284Sjmallett void build_argv(); 73210284Sjmallettpublic: 74210284Sjmallett possible_command(); 75210284Sjmallett ~possible_command(); 76210284Sjmallett void set_name(const char *); 77210284Sjmallett void set_name(const char *, const char *); 78210284Sjmallett const char *get_name(); 79210284Sjmallett void append_arg(const char *, const char * = 0); 80210284Sjmallett void insert_arg(const char *); 81210284Sjmallett void insert_args(string s); 82210284Sjmallett void clear_args(); 83210284Sjmallett char **get_argv(); 84210284Sjmallett void print(int is_last, FILE *fp); 85210284Sjmallett}; 86210284Sjmallett 87215990Sjmallettextern "C" const char *Version_string; 88210284Sjmallett 89210284Sjmallettint lflag = 0; 90210284Sjmallettchar *spooler = 0; 91210284Sjmallettchar *postdriver = 0; 92210284Sjmallettchar *predriver = 0; 93232812Sjmallett 94210284Sjmallettpossible_command commands[NCOMMANDS]; 95210284Sjmallett 96215990Sjmallettint run_commands(int no_pipe); 97232812Sjmallettvoid print_commands(FILE *); 98210284Sjmallettvoid append_arg_to_string(const char *arg, string &str); 99232812Sjmallettvoid handle_unknown_desc_command(const char *command, const char *arg, 100232812Sjmallett const char *filename, int lineno); 101232812Sjmallettconst char *xbasename(const char *); 102232812Sjmallett 103232812Sjmallettvoid usage(FILE *stream); 104232812Sjmallettvoid help(); 105232812Sjmallett 106232812Sjmallettint main(int argc, char **argv) 107215990Sjmallett{ 108215990Sjmallett program_name = argv[0]; 109215990Sjmallett static char stderr_buf[BUFSIZ]; 110210284Sjmallett setbuf(stderr, stderr_buf); 111215990Sjmallett assert(NCOMMANDS <= MAX_COMMANDS); 112215990Sjmallett string Pargs, Largs, Fargs; 113210284Sjmallett int vflag = 0; 114215990Sjmallett int Vflag = 0; 115210284Sjmallett int zflag = 0; 116210284Sjmallett int iflag = 0; 117210284Sjmallett int Xflag = 0; 118210284Sjmallett int oflag = 0; 119210284Sjmallett int safer_flag = 1; 120210284Sjmallett int opt; 121210284Sjmallett const char *command_prefix = getenv("GROFF_COMMAND_PREFIX"); 122232812Sjmallett if (!command_prefix) 123232812Sjmallett command_prefix = PROG_PREFIX; 124232812Sjmallett commands[TROFF_INDEX].set_name(command_prefix, "troff"); 125232812Sjmallett static const struct option long_options[] = { 126232812Sjmallett { "help", no_argument, 0, 'h' }, 127232812Sjmallett { "version", no_argument, 0, 'v' }, 128232812Sjmallett { NULL, 0, 0, 0 } 129232812Sjmallett }; 130232812Sjmallett while ((opt = getopt_long(argc, argv, 131232812Sjmallett "abcCd:eEf:F:gGhiI:lL:m:M:n:No:pP:r:RsStT:UvVw:W:XzZ", 132232812Sjmallett long_options, NULL)) 133232812Sjmallett != EOF) { 134232812Sjmallett char buf[3]; 135215990Sjmallett buf[0] = '-'; 136210284Sjmallett buf[1] = opt; 137210284Sjmallett buf[2] = '\0'; 138232812Sjmallett switch (opt) { 139232812Sjmallett case 'i': 140210284Sjmallett iflag = 1; 141210284Sjmallett break; 142210284Sjmallett case 'I': 143210284Sjmallett commands[SOELIM_INDEX].set_name(command_prefix, "soelim"); 144215990Sjmallett commands[SOELIM_INDEX].append_arg(buf, optarg); 145210284Sjmallett // .psbb may need to search for files 146210284Sjmallett commands[TROFF_INDEX].append_arg(buf, optarg); 147210284Sjmallett // \X'ps:import' may need to search for files 148210284Sjmallett Pargs += buf; 149210284Sjmallett Pargs += optarg; 150210284Sjmallett Pargs += '\0'; 151210284Sjmallett break; 152210284Sjmallett case 't': 153210284Sjmallett commands[TBL_INDEX].set_name(command_prefix, "tbl"); 154210284Sjmallett break; 155210284Sjmallett case 'p': 156210284Sjmallett commands[PIC_INDEX].set_name(command_prefix, "pic"); 157210284Sjmallett break; 158210284Sjmallett case 'g': 159210284Sjmallett commands[GRN_INDEX].set_name(command_prefix, "grn"); 160215990Sjmallett break; 161210284Sjmallett case 'G': 162210284Sjmallett commands[GRAP_INDEX].set_name(command_prefix, "grap"); 163210284Sjmallett break; 164210284Sjmallett case 'e': 165210284Sjmallett commands[EQN_INDEX].set_name(command_prefix, "eqn"); 166210284Sjmallett break; 167210284Sjmallett case 's': 168210284Sjmallett commands[SOELIM_INDEX].set_name(command_prefix, "soelim"); 169215990Sjmallett break; 170210284Sjmallett case 'R': 171210284Sjmallett commands[REFER_INDEX].set_name(command_prefix, "refer"); 172210284Sjmallett break; 173210284Sjmallett case 'z': 174210284Sjmallett case 'a': 175210284Sjmallett commands[TROFF_INDEX].append_arg(buf); 176210284Sjmallett // fall through 177210284Sjmallett case 'Z': 178210284Sjmallett zflag++; 179232812Sjmallett break; 180210284Sjmallett case 'l': 181232812Sjmallett lflag++; 182232812Sjmallett break; 183232812Sjmallett case 'V': 184232812Sjmallett Vflag++; 185232812Sjmallett break; 186232812Sjmallett case 'v': 187210284Sjmallett vflag = 1; 188232812Sjmallett { 189232812Sjmallett printf("GNU groff version %s\n", Version_string); 190232812Sjmallett printf("Copyright (C) 2004 Free Software Foundation, Inc.\n" 191232812Sjmallett "GNU groff comes with ABSOLUTELY NO WARRANTY.\n" 192232812Sjmallett "You may redistribute copies of groff and its subprograms\n" 193232812Sjmallett "under the terms of the GNU General Public License.\n" 194232812Sjmallett "For more information about these matters, see the file named COPYING.\n"); 195232812Sjmallett printf("\ncalled subprograms:\n\n"); 196232812Sjmallett fflush(stdout); 197232812Sjmallett } 198232812Sjmallett commands[POST_INDEX].append_arg(buf); 199232812Sjmallett // fall through 200232812Sjmallett case 'C': 201232812Sjmallett commands[SOELIM_INDEX].append_arg(buf); 202232812Sjmallett commands[REFER_INDEX].append_arg(buf); 203232812Sjmallett commands[PIC_INDEX].append_arg(buf); 204232812Sjmallett commands[GRAP_INDEX].append_arg(buf); 205232812Sjmallett commands[TBL_INDEX].append_arg(buf); 206232812Sjmallett commands[GRN_INDEX].append_arg(buf); 207232812Sjmallett commands[EQN_INDEX].append_arg(buf); 208232812Sjmallett commands[TROFF_INDEX].append_arg(buf); 209232812Sjmallett break; 210232812Sjmallett case 'N': 211232812Sjmallett commands[EQN_INDEX].append_arg(buf); 212232812Sjmallett break; 213232812Sjmallett case 'h': 214232812Sjmallett help(); 215232812Sjmallett break; 216232812Sjmallett case 'E': 217232812Sjmallett case 'b': 218232812Sjmallett commands[TROFF_INDEX].append_arg(buf); 219232812Sjmallett break; 220232812Sjmallett case 'c': 221232812Sjmallett commands[TROFF_INDEX].append_arg(buf); 222232812Sjmallett break; 223232812Sjmallett case 'S': 224232812Sjmallett safer_flag = 1; 225232812Sjmallett break; 226232812Sjmallett case 'U': 227232812Sjmallett safer_flag = 0; 228232812Sjmallett break; 229232812Sjmallett case 'T': 230210284Sjmallett if (strcmp(optarg, "html") == 0) { 231210284Sjmallett // force soelim to aid the html preprocessor 232210284Sjmallett commands[SOELIM_INDEX].set_name(command_prefix, "soelim"); 233210284Sjmallett } 234210284Sjmallett if (strcmp(optarg, "Xps") == 0) { 235210284Sjmallett warning("-TXps option is obsolete: use -X -Tps instead"); 236210284Sjmallett device = "ps"; 237210284Sjmallett Xflag++; 238210284Sjmallett } 239210284Sjmallett else 240210284Sjmallett device = optarg; 241210284Sjmallett break; 242210284Sjmallett case 'F': 243232812Sjmallett font::command_line_font_dir(optarg); 244210284Sjmallett if (Fargs.length() > 0) { 245210284Sjmallett Fargs += PATH_SEP_CHAR; 246210284Sjmallett Fargs += optarg; 247210284Sjmallett } 248215990Sjmallett else 249210284Sjmallett Fargs = optarg; 250210284Sjmallett break; 251210284Sjmallett case 'o': 252210284Sjmallett oflag = 1; 253210284Sjmallett case 'f': 254210284Sjmallett case 'm': 255210284Sjmallett case 'r': 256210284Sjmallett case 'd': 257210284Sjmallett case 'n': 258210284Sjmallett case 'w': 259232812Sjmallett case 'W': 260210284Sjmallett commands[TROFF_INDEX].append_arg(buf, optarg); 261210284Sjmallett break; 262210284Sjmallett case 'M': 263210284Sjmallett commands[EQN_INDEX].append_arg(buf, optarg); 264210284Sjmallett commands[GRAP_INDEX].append_arg(buf, optarg); 265210284Sjmallett commands[GRN_INDEX].append_arg(buf, optarg); 266210284Sjmallett commands[TROFF_INDEX].append_arg(buf, optarg); 267210284Sjmallett break; 268210284Sjmallett case 'P': 269210284Sjmallett Pargs += optarg; 270210284Sjmallett Pargs += '\0'; 271210284Sjmallett break; 272210284Sjmallett case 'L': 273210284Sjmallett append_arg_to_string(optarg, Largs); 274210284Sjmallett break; 275210284Sjmallett case 'X': 276210284Sjmallett Xflag++; 277210284Sjmallett break; 278210284Sjmallett case '?': 279210284Sjmallett usage(stderr); 280210284Sjmallett exit(1); 281210284Sjmallett break; 282210284Sjmallett default: 283210284Sjmallett assert(0); 284210284Sjmallett break; 285210284Sjmallett } 286210284Sjmallett } 287210284Sjmallett if (safer_flag) 288210284Sjmallett commands[PIC_INDEX].append_arg("-S"); 289210284Sjmallett else 290210284Sjmallett commands[TROFF_INDEX].insert_arg("-U"); 291210284Sjmallett font::set_unknown_desc_command_handler(handle_unknown_desc_command); 292210284Sjmallett if (!font::load_desc()) 293210284Sjmallett fatal("invalid device `%1'", device); 294210284Sjmallett if (!postdriver) 295210284Sjmallett fatal("no `postpro' command in DESC file for device `%1'", device); 296210284Sjmallett if (predriver && !zflag) { 297210284Sjmallett commands[TROFF_INDEX].insert_arg(commands[TROFF_INDEX].get_name()); 298210284Sjmallett commands[TROFF_INDEX].set_name(predriver); 299210284Sjmallett // pass the device arguments to the predrivers as well 300210284Sjmallett commands[TROFF_INDEX].insert_args(Pargs); 301210284Sjmallett if (vflag) 302210284Sjmallett commands[TROFF_INDEX].insert_arg("-v"); 303210284Sjmallett } 304210284Sjmallett const char *real_driver = 0; 305210284Sjmallett if (Xflag) { 306210284Sjmallett real_driver = postdriver; 307210284Sjmallett postdriver = (char *)GXDITVIEW; 308210284Sjmallett commands[TROFF_INDEX].append_arg("-r" XREG "=", "1"); 309210284Sjmallett } 310210284Sjmallett if (postdriver) 311210284Sjmallett commands[POST_INDEX].set_name(postdriver); 312210284Sjmallett int gxditview_flag = postdriver && strcmp(xbasename(postdriver), GXDITVIEW) == 0; 313210284Sjmallett if (gxditview_flag && argc - optind == 1) { 314 commands[POST_INDEX].append_arg("-title"); 315 commands[POST_INDEX].append_arg(argv[optind]); 316 commands[POST_INDEX].append_arg("-xrm"); 317 commands[POST_INDEX].append_arg("*iconName:", argv[optind]); 318 string filename_string("|"); 319 append_arg_to_string(argv[0], filename_string); 320 append_arg_to_string("-Z", filename_string); 321 for (int i = 1; i < argc; i++) 322 append_arg_to_string(argv[i], filename_string); 323 filename_string += '\0'; 324 commands[POST_INDEX].append_arg("-filename"); 325 commands[POST_INDEX].append_arg(filename_string.contents()); 326 } 327 if (gxditview_flag && Xflag) { 328 string print_string(real_driver); 329 if (spooler) { 330 print_string += " | "; 331 print_string += spooler; 332 print_string += Largs; 333 } 334 print_string += '\0'; 335 commands[POST_INDEX].append_arg("-printCommand"); 336 commands[POST_INDEX].append_arg(print_string.contents()); 337 } 338 const char *p = Pargs.contents(); 339 const char *end = p + Pargs.length(); 340 while (p < end) { 341 commands[POST_INDEX].append_arg(p); 342 p = strchr(p, '\0') + 1; 343 } 344 if (gxditview_flag) 345 commands[POST_INDEX].append_arg("-"); 346 if (lflag && !vflag && !Xflag && spooler) { 347 commands[SPOOL_INDEX].set_name(BSHELL); 348 commands[SPOOL_INDEX].append_arg(BSHELL_DASH_C); 349 Largs += '\0'; 350 Largs = spooler + Largs; 351 commands[SPOOL_INDEX].append_arg(Largs.contents()); 352 } 353 if (zflag) { 354 commands[POST_INDEX].set_name(0); 355 commands[SPOOL_INDEX].set_name(0); 356 } 357 commands[TROFF_INDEX].append_arg("-T", device); 358 // html renders equations as images via ps 359 if (strcmp(device, "html") == 0) { 360 if (oflag) 361 fatal("`-o' option is invalid with device `html'"); 362 commands[EQN_INDEX].append_arg("-Tps:html"); 363 } 364 else 365 commands[EQN_INDEX].append_arg("-T", device); 366 367 commands[GRN_INDEX].append_arg("-T", device); 368 369 int first_index; 370 for (first_index = 0; first_index < TROFF_INDEX; first_index++) 371 if (commands[first_index].get_name() != 0) 372 break; 373 if (optind < argc) { 374 if (argv[optind][0] == '-' && argv[optind][1] != '\0') 375 commands[first_index].append_arg("--"); 376 for (int i = optind; i < argc; i++) 377 commands[first_index].append_arg(argv[i]); 378 if (iflag) 379 commands[first_index].append_arg("-"); 380 } 381 if (Fargs.length() > 0) { 382 string e = "GROFF_FONT_PATH"; 383 e += '='; 384 e += Fargs; 385 char *fontpath = getenv("GROFF_FONT_PATH"); 386 if (fontpath && *fontpath) { 387 e += PATH_SEP_CHAR; 388 e += fontpath; 389 } 390 e += '\0'; 391 if (putenv(strsave(e.contents()))) 392 fatal("putenv failed"); 393 } 394 { 395 // we save the original path in GROFF_PATH__ and put it into the 396 // environment -- troff will pick it up later. 397 char *path = getenv("PATH"); 398 string e = "GROFF_PATH__"; 399 e += '='; 400 if (path && *path) 401 e += path; 402 e += '\0'; 403 if (putenv(strsave(e.contents()))) 404 fatal("putenv failed"); 405 char *binpath = getenv("GROFF_BIN_PATH"); 406 string f = "PATH"; 407 f += '='; 408 if (binpath && *binpath) 409 f += binpath; 410 else 411 f += BINPATH; 412 if (path && *path) { 413 f += PATH_SEP_CHAR; 414 f += path; 415 } 416 f += '\0'; 417 if (putenv(strsave(f.contents()))) 418 fatal("putenv failed"); 419 } 420 if (Vflag) 421 print_commands(Vflag == 1 ? stdout : stderr); 422 if (Vflag == 1) 423 exit(0); 424 return run_commands(vflag); 425} 426 427const char *xbasename(const char *s) 428{ 429 if (!s) 430 return 0; 431 // DIR_SEPS[] are possible directory separator characters, see nonposix.h 432 // We want the rightmost separator of all possible ones. 433 // Example: d:/foo\\bar. 434 const char *p = strrchr(s, DIR_SEPS[0]), *p1; 435 const char *sep = &DIR_SEPS[1]; 436 437 while (*sep) 438 { 439 p1 = strrchr(s, *sep); 440 if (p1 && (!p || p1 > p)) 441 p = p1; 442 sep++; 443 } 444 return p ? p + 1 : s; 445} 446 447void handle_unknown_desc_command(const char *command, const char *arg, 448 const char *filename, int lineno) 449{ 450 if (strcmp(command, "print") == 0) { 451 if (arg == 0) 452 error_with_file_and_line(filename, lineno, 453 "`print' command requires an argument"); 454 else 455 spooler = strsave(arg); 456 } 457 if (strcmp(command, "prepro") == 0) { 458 if (arg == 0) 459 error_with_file_and_line(filename, lineno, 460 "`prepro' command requires an argument"); 461 else { 462 for (const char *p = arg; *p; p++) 463 if (csspace(*p)) { 464 error_with_file_and_line(filename, lineno, 465 "invalid `prepro' argument `%1'" 466 ": program name required", arg); 467 return; 468 } 469 predriver = strsave(arg); 470 } 471 } 472 if (strcmp(command, "postpro") == 0) { 473 if (arg == 0) 474 error_with_file_and_line(filename, lineno, 475 "`postpro' command requires an argument"); 476 else { 477 for (const char *p = arg; *p; p++) 478 if (csspace(*p)) { 479 error_with_file_and_line(filename, lineno, 480 "invalid `postpro' argument `%1'" 481 ": program name required", arg); 482 return; 483 } 484 postdriver = strsave(arg); 485 } 486 } 487} 488 489void print_commands(FILE *fp) 490{ 491 int last; 492 for (last = SPOOL_INDEX; last >= 0; last--) 493 if (commands[last].get_name() != 0) 494 break; 495 for (int i = 0; i <= last; i++) 496 if (commands[i].get_name() != 0) 497 commands[i].print(i == last, fp); 498} 499 500// Run the commands. Return the code with which to exit. 501 502int run_commands(int no_pipe) 503{ 504 char **v[NCOMMANDS]; 505 int j = 0; 506 for (int i = 0; i < NCOMMANDS; i++) 507 if (commands[i].get_name() != 0) 508 v[j++] = commands[i].get_argv(); 509 return run_pipeline(j, v, no_pipe); 510} 511 512possible_command::possible_command() 513: name(0), argv(0) 514{ 515} 516 517possible_command::~possible_command() 518{ 519 a_delete name; 520 a_delete argv; 521} 522 523void possible_command::set_name(const char *s) 524{ 525 a_delete name; 526 name = strsave(s); 527} 528 529void possible_command::set_name(const char *s1, const char *s2) 530{ 531 a_delete name; 532 name = new char[strlen(s1) + strlen(s2) + 1]; 533 strcpy(name, s1); 534 strcat(name, s2); 535} 536 537const char *possible_command::get_name() 538{ 539 return name; 540} 541 542void possible_command::clear_args() 543{ 544 args.clear(); 545} 546 547void possible_command::append_arg(const char *s, const char *t) 548{ 549 args += s; 550 if (t) 551 args += t; 552 args += '\0'; 553} 554 555void possible_command::insert_arg(const char *s) 556{ 557 string str(s); 558 str += '\0'; 559 str += args; 560 args = str; 561} 562 563void possible_command::insert_args(string s) 564{ 565 const char *p = s.contents(); 566 const char *end = p + s.length(); 567 int l = 0; 568 if (p >= end) 569 return; 570 // find the total number of arguments in our string 571 do { 572 l++; 573 p = strchr(p, '\0') + 1; 574 } while (p < end); 575 // now insert each argument preserving the order 576 for (int i = l - 1; i >= 0; i--) { 577 p = s.contents(); 578 for (int j = 0; j < i; j++) 579 p = strchr(p, '\0') + 1; 580 insert_arg(p); 581 } 582} 583 584void possible_command::build_argv() 585{ 586 if (argv) 587 return; 588 // Count the number of arguments. 589 int len = args.length(); 590 int argc = 1; 591 char *p = 0; 592 if (len > 0) { 593 p = &args[0]; 594 for (int i = 0; i < len; i++) 595 if (p[i] == '\0') 596 argc++; 597 } 598 // Build an argument vector. 599 argv = new char *[argc + 1]; 600 argv[0] = name; 601 for (int i = 1; i < argc; i++) { 602 argv[i] = p; 603 p = strchr(p, '\0') + 1; 604 } 605 argv[argc] = 0; 606} 607 608void possible_command::print(int is_last, FILE *fp) 609{ 610 build_argv(); 611 if (IS_BSHELL(argv[0]) 612 && argv[1] != 0 && strcmp(argv[1], BSHELL_DASH_C) == 0 613 && argv[2] != 0 && argv[3] == 0) 614 fputs(argv[2], fp); 615 else { 616 fputs(argv[0], fp); 617 string str; 618 for (int i = 1; argv[i] != 0; i++) { 619 str.clear(); 620 append_arg_to_string(argv[i], str); 621 put_string(str, fp); 622 } 623 } 624 if (is_last) 625 putc('\n', fp); 626 else 627 fputs(" | ", fp); 628} 629 630void append_arg_to_string(const char *arg, string &str) 631{ 632 str += ' '; 633 int needs_quoting = 0; 634 int contains_single_quote = 0; 635 const char*p; 636 for (p = arg; *p != '\0'; p++) 637 switch (*p) { 638 case ';': 639 case '&': 640 case '(': 641 case ')': 642 case '|': 643 case '^': 644 case '<': 645 case '>': 646 case '\n': 647 case ' ': 648 case '\t': 649 case '\\': 650 case '"': 651 case '$': 652 case '?': 653 case '*': 654 needs_quoting = 1; 655 break; 656 case '\'': 657 contains_single_quote = 1; 658 break; 659 } 660 if (contains_single_quote || arg[0] == '\0') { 661 str += '"'; 662 for (p = arg; *p != '\0'; p++) 663 switch (*p) { 664 case '"': 665 case '\\': 666 case '$': 667 str += '\\'; 668 // fall through 669 default: 670 str += *p; 671 break; 672 } 673 str += '"'; 674 } 675 else if (needs_quoting) { 676 str += '\''; 677 str += arg; 678 str += '\''; 679 } 680 else 681 str += arg; 682} 683 684char **possible_command::get_argv() 685{ 686 build_argv(); 687 return argv; 688} 689 690void synopsis(FILE *stream) 691{ 692 fprintf(stream, 693"usage: %s [-abceghilpstvzCENRSUVXZ] [-Fdir] [-mname] [-Tdev] [-ffam]\n" 694" [-wname] [-Wname] [-Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n" 695" [-Larg] [-Idir] [files...]\n", 696 program_name); 697} 698 699void help() 700{ 701 synopsis(stdout); 702 fputs("\n" 703"-h\tprint this message\n" 704"-t\tpreprocess with tbl\n" 705"-p\tpreprocess with pic\n" 706"-e\tpreprocess with eqn\n" 707"-g\tpreprocess with grn\n" 708"-G\tpreprocess with grap\n" 709"-s\tpreprocess with soelim\n" 710"-R\tpreprocess with refer\n" 711"-Tdev\tuse device dev\n" 712"-X\tuse X11 previewer rather than usual postprocessor\n" 713"-mname\tread macros tmac.name\n" 714"-dcs\tdefine a string c as s\n" 715"-rcn\tdefine a number register c as n\n" 716"-nnum\tnumber first page n\n" 717"-olist\toutput only pages in list\n" 718"-ffam\tuse fam as the default font family\n" 719"-Fdir\tsearch dir for device directories\n" 720"-Mdir\tsearch dir for macro files\n" 721"-v\tprint version number\n" 722"-z\tsuppress formatted output\n" 723"-Z\tdon't postprocess\n" 724"-a\tproduce ASCII description of output\n" 725"-i\tread standard input after named input files\n" 726"-wname\tenable warning name\n" 727"-Wname\tinhibit warning name\n" 728"-E\tinhibit all errors\n" 729"-b\tprint backtraces with errors or warnings\n" 730"-l\tspool the output\n" 731"-c\tdisable color output\n" 732"-C\tenable compatibility mode\n" 733"-V\tprint commands on stdout instead of running them\n" 734"-Parg\tpass arg to the postprocessor\n" 735"-Larg\tpass arg to the spooler\n" 736"-N\tdon't allow newlines within eqn delimiters\n" 737"-S\tenable safer mode (the default)\n" 738"-U\tenable unsafe mode\n" 739"-Idir\tsearch dir for soelim, troff, and grops. Implies -s\n" 740"\n", 741 stdout); 742 exit(0); 743} 744 745void usage(FILE *stream) 746{ 747 synopsis(stream); 748 fprintf(stream, "%s -h gives more help\n", program_name); 749} 750 751extern "C" { 752 753void c_error(const char *format, const char *arg1, const char *arg2, 754 const char *arg3) 755{ 756 error(format, arg1, arg2, arg3); 757} 758 759void c_fatal(const char *format, const char *arg1, const char *arg2, 760 const char *arg3) 761{ 762 fatal(format, arg1, arg2, arg3); 763} 764 765} 766