1114402Sru// -*- C++ -*- 2114402Sru/* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003 3114402Sru 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#include "pic.h" 23114402Sru 24114402Sruextern int yyparse(); 25114402Sruextern "C" const char *Version_string; 26114402Sru 27114402Sruoutput *out; 28114402Sruchar *graphname; // the picture box name in TeX mode 29114402Sru 30114402Sruint flyback_flag; 31114402Sruint zero_length_line_flag = 0; 32114402Sru// Non-zero means we're using a groff driver. 33114402Sruint driver_extension_flag = 1; 34114402Sruint compatible_flag = 0; 35114402Sruint safer_flag = 1; 36114402Sruint command_char = '.'; // the character that introduces lines 37114402Sru // that should be passed through tranparently 38114402Srustatic int lf_flag = 1; // non-zero if we should attempt to understand 39114402Sru // lines beginning with `.lf' 40114402Sru 41114402Sru// Non-zero means a parse error was encountered. 42114402Srustatic int had_parse_error = 0; 43114402Sru 44114402Sruvoid do_file(const char *filename); 45114402Sru 46114402Sruclass top_input : public input { 47114402Sru FILE *fp; 48114402Sru int bol; 49114402Sru int eof; 50114402Sru int push_back[3]; 51114402Sru int start_lineno; 52114402Srupublic: 53114402Sru top_input(FILE *); 54114402Sru int get(); 55114402Sru int peek(); 56114402Sru int get_location(const char **, int *); 57114402Sru}; 58114402Sru 59114402Srutop_input::top_input(FILE *p) : fp(p), bol(1), eof(0) 60114402Sru{ 61114402Sru push_back[0] = push_back[1] = push_back[2] = EOF; 62114402Sru start_lineno = current_lineno; 63114402Sru} 64114402Sru 65114402Sruint top_input::get() 66114402Sru{ 67114402Sru if (eof) 68114402Sru return EOF; 69114402Sru if (push_back[2] != EOF) { 70114402Sru int c = push_back[2]; 71114402Sru push_back[2] = EOF; 72114402Sru return c; 73114402Sru } 74114402Sru else if (push_back[1] != EOF) { 75114402Sru int c = push_back[1]; 76114402Sru push_back[1] = EOF; 77114402Sru return c; 78114402Sru } 79114402Sru else if (push_back[0] != EOF) { 80114402Sru int c = push_back[0]; 81114402Sru push_back[0] = EOF; 82114402Sru return c; 83114402Sru } 84114402Sru int c = getc(fp); 85114402Sru while (invalid_input_char(c)) { 86114402Sru error("invalid input character code %1", int(c)); 87114402Sru c = getc(fp); 88114402Sru bol = 0; 89114402Sru } 90114402Sru if (bol && c == '.') { 91114402Sru c = getc(fp); 92114402Sru if (c == 'P') { 93114402Sru c = getc(fp); 94114402Sru if (c == 'F' || c == 'E') { 95114402Sru int d = getc(fp); 96114402Sru if (d != EOF) 97114402Sru ungetc(d, fp); 98114402Sru if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { 99114402Sru eof = 1; 100114402Sru flyback_flag = c == 'F'; 101114402Sru return EOF; 102114402Sru } 103114402Sru push_back[0] = c; 104114402Sru push_back[1] = 'P'; 105114402Sru return '.'; 106114402Sru } 107114402Sru if (c == 'S') { 108114402Sru c = getc(fp); 109114402Sru if (c != EOF) 110114402Sru ungetc(c, fp); 111114402Sru if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { 112114402Sru error("nested .PS"); 113114402Sru eof = 1; 114114402Sru return EOF; 115114402Sru } 116114402Sru push_back[0] = 'S'; 117114402Sru push_back[1] = 'P'; 118114402Sru return '.'; 119114402Sru } 120114402Sru if (c != EOF) 121114402Sru ungetc(c, fp); 122114402Sru push_back[0] = 'P'; 123114402Sru return '.'; 124114402Sru } 125114402Sru else { 126114402Sru if (c != EOF) 127114402Sru ungetc(c, fp); 128114402Sru return '.'; 129114402Sru } 130114402Sru } 131114402Sru if (c == '\n') { 132114402Sru bol = 1; 133114402Sru current_lineno++; 134114402Sru return '\n'; 135114402Sru } 136114402Sru bol = 0; 137114402Sru if (c == EOF) { 138114402Sru eof = 1; 139114402Sru error("end of file before .PE or .PF"); 140114402Sru error_with_file_and_line(current_filename, start_lineno - 1, 141114402Sru ".PS was here"); 142114402Sru } 143114402Sru return c; 144114402Sru} 145114402Sru 146114402Sruint top_input::peek() 147114402Sru{ 148114402Sru if (eof) 149114402Sru return EOF; 150114402Sru if (push_back[2] != EOF) 151114402Sru return push_back[2]; 152114402Sru if (push_back[1] != EOF) 153114402Sru return push_back[1]; 154114402Sru if (push_back[0] != EOF) 155114402Sru return push_back[0]; 156114402Sru int c = getc(fp); 157114402Sru while (invalid_input_char(c)) { 158114402Sru error("invalid input character code %1", int(c)); 159114402Sru c = getc(fp); 160114402Sru bol = 0; 161114402Sru } 162114402Sru if (bol && c == '.') { 163114402Sru c = getc(fp); 164114402Sru if (c == 'P') { 165114402Sru c = getc(fp); 166114402Sru if (c == 'F' || c == 'E') { 167114402Sru int d = getc(fp); 168114402Sru if (d != EOF) 169114402Sru ungetc(d, fp); 170114402Sru if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { 171114402Sru eof = 1; 172114402Sru flyback_flag = c == 'F'; 173114402Sru return EOF; 174114402Sru } 175114402Sru push_back[0] = c; 176114402Sru push_back[1] = 'P'; 177114402Sru push_back[2] = '.'; 178114402Sru return '.'; 179114402Sru } 180114402Sru if (c == 'S') { 181114402Sru c = getc(fp); 182114402Sru if (c != EOF) 183114402Sru ungetc(c, fp); 184114402Sru if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { 185114402Sru error("nested .PS"); 186114402Sru eof = 1; 187114402Sru return EOF; 188114402Sru } 189114402Sru push_back[0] = 'S'; 190114402Sru push_back[1] = 'P'; 191114402Sru push_back[2] = '.'; 192114402Sru return '.'; 193114402Sru } 194114402Sru if (c != EOF) 195114402Sru ungetc(c, fp); 196114402Sru push_back[0] = 'P'; 197114402Sru push_back[1] = '.'; 198114402Sru return '.'; 199114402Sru } 200114402Sru else { 201114402Sru if (c != EOF) 202114402Sru ungetc(c, fp); 203114402Sru push_back[0] = '.'; 204114402Sru return '.'; 205114402Sru } 206114402Sru } 207114402Sru if (c != EOF) 208114402Sru ungetc(c, fp); 209114402Sru if (c == '\n') 210114402Sru return '\n'; 211114402Sru return c; 212114402Sru} 213114402Sru 214114402Sruint top_input::get_location(const char **filenamep, int *linenop) 215114402Sru{ 216114402Sru *filenamep = current_filename; 217114402Sru *linenop = current_lineno; 218114402Sru return 1; 219114402Sru} 220114402Sru 221114402Sruvoid do_picture(FILE *fp) 222114402Sru{ 223114402Sru flyback_flag = 0; 224114402Sru int c; 225114402Sru a_delete graphname; 226114402Sru graphname = strsave("graph"); // default picture name in TeX mode 227114402Sru while ((c = getc(fp)) == ' ') 228114402Sru ; 229114402Sru if (c == '<') { 230114402Sru string filename; 231114402Sru while ((c = getc(fp)) == ' ') 232114402Sru ; 233114402Sru while (c != EOF && c != ' ' && c != '\n') { 234114402Sru filename += char(c); 235114402Sru c = getc(fp); 236114402Sru } 237114402Sru if (c == ' ') { 238114402Sru do { 239114402Sru c = getc(fp); 240114402Sru } while (c != EOF && c != '\n'); 241114402Sru } 242114402Sru if (c == '\n') 243114402Sru current_lineno++; 244114402Sru if (filename.length() == 0) 245114402Sru error("missing filename after `<'"); 246114402Sru else { 247114402Sru filename += '\0'; 248114402Sru const char *old_filename = current_filename; 249114402Sru int old_lineno = current_lineno; 250114402Sru // filenames must be permanent 251114402Sru do_file(strsave(filename.contents())); 252114402Sru current_filename = old_filename; 253114402Sru current_lineno = old_lineno; 254114402Sru } 255114402Sru out->set_location(current_filename, current_lineno); 256114402Sru } 257114402Sru else { 258114402Sru out->set_location(current_filename, current_lineno); 259114402Sru string start_line; 260114402Sru while (c != EOF) { 261114402Sru if (c == '\n') { 262114402Sru current_lineno++; 263114402Sru break; 264114402Sru } 265114402Sru start_line += c; 266114402Sru c = getc(fp); 267114402Sru } 268114402Sru if (c == EOF) 269114402Sru return; 270114402Sru start_line += '\0'; 271114402Sru double wid, ht; 272114402Sru switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) { 273114402Sru case 1: 274114402Sru ht = 0.0; 275114402Sru break; 276114402Sru case 2: 277114402Sru break; 278114402Sru default: 279114402Sru ht = wid = 0.0; 280114402Sru break; 281114402Sru } 282114402Sru out->set_desired_width_height(wid, ht); 283114402Sru out->set_args(start_line.contents()); 284114402Sru lex_init(new top_input(fp)); 285114402Sru if (yyparse()) { 286114402Sru had_parse_error = 1; 287114402Sru lex_error("giving up on this picture"); 288114402Sru } 289114402Sru parse_cleanup(); 290114402Sru lex_cleanup(); 291114402Sru 292114402Sru // skip the rest of the .PF/.PE line 293114402Sru while ((c = getc(fp)) != EOF && c != '\n') 294114402Sru ; 295114402Sru if (c == '\n') 296114402Sru current_lineno++; 297114402Sru out->set_location(current_filename, current_lineno); 298114402Sru } 299114402Sru} 300114402Sru 301114402Sruvoid do_file(const char *filename) 302114402Sru{ 303114402Sru FILE *fp; 304114402Sru if (strcmp(filename, "-") == 0) 305114402Sru fp = stdin; 306114402Sru else { 307114402Sru errno = 0; 308114402Sru fp = fopen(filename, "r"); 309114402Sru if (fp == 0) { 310114402Sru delete out; 311114402Sru fatal("can't open `%1': %2", filename, strerror(errno)); 312114402Sru } 313114402Sru } 314114402Sru out->set_location(filename, 1); 315114402Sru current_filename = filename; 316114402Sru current_lineno = 1; 317114402Sru enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START; 318114402Sru for (;;) { 319114402Sru int c = getc(fp); 320114402Sru if (c == EOF) 321114402Sru break; 322114402Sru switch (state) { 323114402Sru case START: 324114402Sru if (c == '.') 325114402Sru state = HAD_DOT; 326114402Sru else { 327114402Sru putchar(c); 328114402Sru if (c == '\n') { 329114402Sru current_lineno++; 330114402Sru state = START; 331114402Sru } 332114402Sru else 333114402Sru state = MIDDLE; 334114402Sru } 335114402Sru break; 336114402Sru case MIDDLE: 337114402Sru putchar(c); 338114402Sru if (c == '\n') { 339114402Sru current_lineno++; 340114402Sru state = START; 341114402Sru } 342114402Sru break; 343114402Sru case HAD_DOT: 344114402Sru if (c == 'P') 345114402Sru state = HAD_P; 346114402Sru else if (lf_flag && c == 'l') 347114402Sru state = HAD_l; 348114402Sru else { 349114402Sru putchar('.'); 350114402Sru putchar(c); 351114402Sru if (c == '\n') { 352114402Sru current_lineno++; 353114402Sru state = START; 354114402Sru } 355114402Sru else 356114402Sru state = MIDDLE; 357114402Sru } 358114402Sru break; 359114402Sru case HAD_P: 360114402Sru if (c == 'S') 361114402Sru state = HAD_PS; 362114402Sru else { 363114402Sru putchar('.'); 364114402Sru putchar('P'); 365114402Sru putchar(c); 366114402Sru if (c == '\n') { 367114402Sru current_lineno++; 368114402Sru state = START; 369114402Sru } 370114402Sru else 371114402Sru state = MIDDLE; 372114402Sru } 373114402Sru break; 374114402Sru case HAD_PS: 375114402Sru if (c == ' ' || c == '\n' || compatible_flag) { 376114402Sru ungetc(c, fp); 377114402Sru do_picture(fp); 378114402Sru state = START; 379114402Sru } 380114402Sru else { 381114402Sru fputs(".PS", stdout); 382114402Sru putchar(c); 383114402Sru state = MIDDLE; 384114402Sru } 385114402Sru break; 386114402Sru case HAD_l: 387114402Sru if (c == 'f') 388114402Sru state = HAD_lf; 389114402Sru else { 390114402Sru putchar('.'); 391114402Sru putchar('l'); 392114402Sru putchar(c); 393114402Sru if (c == '\n') { 394114402Sru current_lineno++; 395114402Sru state = START; 396114402Sru } 397114402Sru else 398114402Sru state = MIDDLE; 399114402Sru } 400114402Sru break; 401114402Sru case HAD_lf: 402114402Sru if (c == ' ' || c == '\n' || compatible_flag) { 403114402Sru string line; 404114402Sru while (c != EOF) { 405114402Sru line += c; 406114402Sru if (c == '\n') { 407114402Sru current_lineno++; 408114402Sru break; 409114402Sru } 410114402Sru c = getc(fp); 411114402Sru } 412114402Sru line += '\0'; 413114402Sru interpret_lf_args(line.contents()); 414114402Sru printf(".lf%s", line.contents()); 415114402Sru state = START; 416114402Sru } 417114402Sru else { 418114402Sru fputs(".lf", stdout); 419114402Sru putchar(c); 420114402Sru state = MIDDLE; 421114402Sru } 422114402Sru break; 423114402Sru default: 424114402Sru assert(0); 425114402Sru } 426114402Sru } 427114402Sru switch (state) { 428114402Sru case START: 429114402Sru break; 430114402Sru case MIDDLE: 431114402Sru putchar('\n'); 432114402Sru break; 433114402Sru case HAD_DOT: 434114402Sru fputs(".\n", stdout); 435114402Sru break; 436114402Sru case HAD_P: 437114402Sru fputs(".P\n", stdout); 438114402Sru break; 439114402Sru case HAD_PS: 440114402Sru fputs(".PS\n", stdout); 441114402Sru break; 442114402Sru case HAD_l: 443114402Sru fputs(".l\n", stdout); 444114402Sru break; 445114402Sru case HAD_lf: 446114402Sru fputs(".lf\n", stdout); 447114402Sru break; 448114402Sru } 449114402Sru if (fp != stdin) 450114402Sru fclose(fp); 451114402Sru} 452114402Sru 453114402Sru#ifdef FIG_SUPPORT 454114402Sruvoid do_whole_file(const char *filename) 455114402Sru{ 456114402Sru // Do not set current_filename. 457114402Sru FILE *fp; 458114402Sru if (strcmp(filename, "-") == 0) 459114402Sru fp = stdin; 460114402Sru else { 461114402Sru errno = 0; 462114402Sru fp = fopen(filename, "r"); 463114402Sru if (fp == 0) 464114402Sru fatal("can't open `%1': %2", filename, strerror(errno)); 465114402Sru } 466114402Sru lex_init(new file_input(fp, filename)); 467114402Sru if (yyparse()) 468114402Sru had_parse_error = 1; 469114402Sru parse_cleanup(); 470114402Sru lex_cleanup(); 471114402Sru} 472114402Sru#endif 473114402Sru 474114402Sruvoid usage(FILE *stream) 475114402Sru{ 476114402Sru fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name); 477114402Sru#ifdef TEX_SUPPORT 478114402Sru fprintf(stream, " %s -t [ -cvzC ] [ filename ... ]\n", program_name); 479114402Sru#endif 480114402Sru#ifdef FIG_SUPPORT 481114402Sru fprintf(stream, " %s -f [ -v ] [ filename ]\n", program_name); 482114402Sru#endif 483114402Sru} 484114402Sru 485114402Sru#if defined(__MSDOS__) || defined(__EMX__) 486114402Srustatic char *fix_program_name(char *arg, char *dflt) 487114402Sru{ 488114402Sru if (!arg) 489114402Sru return dflt; 490114402Sru char *prog = strchr(arg, '\0'); 491114402Sru for (;;) { 492114402Sru if (prog == arg) 493114402Sru break; 494114402Sru --prog; 495114402Sru if (strchr("\\/:", *prog)) { 496114402Sru prog++; 497114402Sru break; 498114402Sru } 499114402Sru } 500114402Sru char *ext = strchr(prog, '.'); 501114402Sru if (ext) 502114402Sru *ext = '\0'; 503114402Sru for (char *p = prog; *p; p++) 504114402Sru if ('A' <= *p && *p <= 'Z') 505114402Sru *p = 'a' + (*p - 'A'); 506114402Sru return prog; 507114402Sru} 508114402Sru#endif /* __MSDOS__ || __EMX__ */ 509114402Sru 510114402Sruint main(int argc, char **argv) 511114402Sru{ 512114402Sru setlocale(LC_NUMERIC, "C"); 513114402Sru#if defined(__MSDOS__) || defined(__EMX__) 514114402Sru argv[0] = fix_program_name(argv[0], "pic"); 515114402Sru#endif /* __MSDOS__ || __EMX__ */ 516114402Sru program_name = argv[0]; 517114402Sru static char stderr_buf[BUFSIZ]; 518114402Sru setbuf(stderr, stderr_buf); 519114402Sru int opt; 520114402Sru#ifdef TEX_SUPPORT 521114402Sru int tex_flag = 0; 522114402Sru int tpic_flag = 0; 523114402Sru#endif 524114402Sru#ifdef FIG_SUPPORT 525114402Sru int whole_file_flag = 0; 526114402Sru int fig_flag = 0; 527114402Sru#endif 528114402Sru static const struct option long_options[] = { 529114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 530114402Sru { "version", no_argument, 0, 'v' }, 531114402Sru { NULL, 0, 0, 0 } 532114402Sru }; 533114402Sru while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL)) 534114402Sru != EOF) 535114402Sru switch (opt) { 536114402Sru case 'C': 537114402Sru compatible_flag = 1; 538114402Sru break; 539114402Sru case 'D': 540114402Sru case 'T': 541114402Sru break; 542114402Sru case 'S': 543114402Sru safer_flag = 1; 544114402Sru break; 545114402Sru case 'U': 546114402Sru safer_flag = 0; 547114402Sru break; 548114402Sru case 'f': 549114402Sru#ifdef FIG_SUPPORT 550114402Sru whole_file_flag++; 551114402Sru fig_flag++; 552114402Sru#else 553114402Sru fatal("fig support not included"); 554114402Sru#endif 555114402Sru break; 556114402Sru case 'n': 557114402Sru driver_extension_flag = 0; 558114402Sru break; 559114402Sru case 'p': 560114402Sru case 'x': 561114402Sru warning("-%1 option is obsolete", char(opt)); 562114402Sru break; 563114402Sru case 't': 564114402Sru#ifdef TEX_SUPPORT 565114402Sru tex_flag++; 566114402Sru#else 567114402Sru fatal("TeX support not included"); 568114402Sru#endif 569114402Sru break; 570114402Sru case 'c': 571114402Sru#ifdef TEX_SUPPORT 572114402Sru tpic_flag++; 573114402Sru#else 574114402Sru fatal("TeX support not included"); 575114402Sru#endif 576114402Sru break; 577114402Sru case 'v': 578114402Sru { 579114402Sru printf("GNU pic (groff) version %s\n", Version_string); 580114402Sru exit(0); 581114402Sru break; 582114402Sru } 583114402Sru case 'z': 584114402Sru // zero length lines will be printed as dots 585114402Sru zero_length_line_flag++; 586114402Sru break; 587114402Sru case CHAR_MAX + 1: // --help 588114402Sru usage(stdout); 589114402Sru exit(0); 590114402Sru break; 591114402Sru case '?': 592114402Sru usage(stderr); 593114402Sru exit(1); 594114402Sru break; 595114402Sru default: 596114402Sru assert(0); 597114402Sru } 598114402Sru parse_init(); 599114402Sru#ifdef TEX_SUPPORT 600114402Sru if (tpic_flag) { 601114402Sru out = make_tpic_output(); 602114402Sru lf_flag = 0; 603114402Sru } 604114402Sru else if (tex_flag) { 605114402Sru out = make_tex_output(); 606114402Sru command_char = '\\'; 607114402Sru lf_flag = 0; 608114402Sru } 609114402Sru else 610114402Sru#endif 611114402Sru#ifdef FIG_SUPPORT 612114402Sru if (fig_flag) 613114402Sru out = make_fig_output(); 614114402Sru else 615114402Sru#endif 616114402Sru out = make_troff_output(); 617114402Sru#ifdef FIG_SUPPORT 618114402Sru if (whole_file_flag) { 619114402Sru if (optind >= argc) 620114402Sru do_whole_file("-"); 621114402Sru else if (argc - optind > 1) { 622114402Sru usage(stderr); 623114402Sru exit(1); 624114402Sru } else 625114402Sru do_whole_file(argv[optind]); 626114402Sru } 627114402Sru else { 628114402Sru#endif 629114402Sru if (optind >= argc) 630114402Sru do_file("-"); 631114402Sru else 632114402Sru for (int i = optind; i < argc; i++) 633114402Sru do_file(argv[i]); 634114402Sru#ifdef FIG_SUPPORT 635114402Sru } 636114402Sru#endif 637114402Sru delete out; 638114402Sru if (ferror(stdout) || fflush(stdout) < 0) 639114402Sru fatal("output error"); 640114402Sru return had_parse_error; 641114402Sru} 642114402Sru 643