1// -*- C++ -*- 2/* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003 3 Free Software Foundation, Inc. 4 Written by James Clark (jjc@jclark.com) 5 6This file is part of groff. 7 8groff is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 2, or (at your option) any later 11version. 12 13groff is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18You should have received a copy of the GNU General Public License along 19with groff; see the file COPYING. If not, write to the Free Software 20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21 22#include "pic.h" 23 24extern int yyparse(); 25extern "C" const char *Version_string; 26 27output *out; 28char *graphname; // the picture box name in TeX mode 29 30int flyback_flag; 31int zero_length_line_flag = 0; 32// Non-zero means we're using a groff driver. 33int driver_extension_flag = 1; 34int compatible_flag = 0; 35int safer_flag = 1; 36int command_char = '.'; // the character that introduces lines 37 // that should be passed through tranparently 38static int lf_flag = 1; // non-zero if we should attempt to understand 39 // lines beginning with `.lf' 40 41// Non-zero means a parse error was encountered. 42static int had_parse_error = 0; 43 44void do_file(const char *filename); 45 46class top_input : public input { 47 FILE *fp; 48 int bol; 49 int eof; 50 int push_back[3]; 51 int start_lineno; 52public: 53 top_input(FILE *); 54 int get(); 55 int peek(); 56 int get_location(const char **, int *); 57}; 58 59top_input::top_input(FILE *p) : fp(p), bol(1), eof(0) 60{ 61 push_back[0] = push_back[1] = push_back[2] = EOF; 62 start_lineno = current_lineno; 63} 64 65int top_input::get() 66{ 67 if (eof) 68 return EOF; 69 if (push_back[2] != EOF) { 70 int c = push_back[2]; 71 push_back[2] = EOF; 72 return c; 73 } 74 else if (push_back[1] != EOF) { 75 int c = push_back[1]; 76 push_back[1] = EOF; 77 return c; 78 } 79 else if (push_back[0] != EOF) { 80 int c = push_back[0]; 81 push_back[0] = EOF; 82 return c; 83 } 84 int c = getc(fp); 85 while (invalid_input_char(c)) { 86 error("invalid input character code %1", int(c)); 87 c = getc(fp); 88 bol = 0; 89 } 90 if (bol && c == '.') { 91 c = getc(fp); 92 if (c == 'P') { 93 c = getc(fp); 94 if (c == 'F' || c == 'E') { 95 int d = getc(fp); 96 if (d != EOF) 97 ungetc(d, fp); 98 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { 99 eof = 1; 100 flyback_flag = c == 'F'; 101 return EOF; 102 } 103 push_back[0] = c; 104 push_back[1] = 'P'; 105 return '.'; 106 } 107 if (c == 'S') { 108 c = getc(fp); 109 if (c != EOF) 110 ungetc(c, fp); 111 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { 112 error("nested .PS"); 113 eof = 1; 114 return EOF; 115 } 116 push_back[0] = 'S'; 117 push_back[1] = 'P'; 118 return '.'; 119 } 120 if (c != EOF) 121 ungetc(c, fp); 122 push_back[0] = 'P'; 123 return '.'; 124 } 125 else { 126 if (c != EOF) 127 ungetc(c, fp); 128 return '.'; 129 } 130 } 131 if (c == '\n') { 132 bol = 1; 133 current_lineno++; 134 return '\n'; 135 } 136 bol = 0; 137 if (c == EOF) { 138 eof = 1; 139 error("end of file before .PE or .PF"); 140 error_with_file_and_line(current_filename, start_lineno - 1, 141 ".PS was here"); 142 } 143 return c; 144} 145 146int top_input::peek() 147{ 148 if (eof) 149 return EOF; 150 if (push_back[2] != EOF) 151 return push_back[2]; 152 if (push_back[1] != EOF) 153 return push_back[1]; 154 if (push_back[0] != EOF) 155 return push_back[0]; 156 int c = getc(fp); 157 while (invalid_input_char(c)) { 158 error("invalid input character code %1", int(c)); 159 c = getc(fp); 160 bol = 0; 161 } 162 if (bol && c == '.') { 163 c = getc(fp); 164 if (c == 'P') { 165 c = getc(fp); 166 if (c == 'F' || c == 'E') { 167 int d = getc(fp); 168 if (d != EOF) 169 ungetc(d, fp); 170 if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { 171 eof = 1; 172 flyback_flag = c == 'F'; 173 return EOF; 174 } 175 push_back[0] = c; 176 push_back[1] = 'P'; 177 push_back[2] = '.'; 178 return '.'; 179 } 180 if (c == 'S') { 181 c = getc(fp); 182 if (c != EOF) 183 ungetc(c, fp); 184 if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { 185 error("nested .PS"); 186 eof = 1; 187 return EOF; 188 } 189 push_back[0] = 'S'; 190 push_back[1] = 'P'; 191 push_back[2] = '.'; 192 return '.'; 193 } 194 if (c != EOF) 195 ungetc(c, fp); 196 push_back[0] = 'P'; 197 push_back[1] = '.'; 198 return '.'; 199 } 200 else { 201 if (c != EOF) 202 ungetc(c, fp); 203 push_back[0] = '.'; 204 return '.'; 205 } 206 } 207 if (c != EOF) 208 ungetc(c, fp); 209 if (c == '\n') 210 return '\n'; 211 return c; 212} 213 214int top_input::get_location(const char **filenamep, int *linenop) 215{ 216 *filenamep = current_filename; 217 *linenop = current_lineno; 218 return 1; 219} 220 221void do_picture(FILE *fp) 222{ 223 flyback_flag = 0; 224 int c; 225 a_delete graphname; 226 graphname = strsave("graph"); // default picture name in TeX mode 227 while ((c = getc(fp)) == ' ') 228 ; 229 if (c == '<') { 230 string filename; 231 while ((c = getc(fp)) == ' ') 232 ; 233 while (c != EOF && c != ' ' && c != '\n') { 234 filename += char(c); 235 c = getc(fp); 236 } 237 if (c == ' ') { 238 do { 239 c = getc(fp); 240 } while (c != EOF && c != '\n'); 241 } 242 if (c == '\n') 243 current_lineno++; 244 if (filename.length() == 0) 245 error("missing filename after `<'"); 246 else { 247 filename += '\0'; 248 const char *old_filename = current_filename; 249 int old_lineno = current_lineno; 250 // filenames must be permanent 251 do_file(strsave(filename.contents())); 252 current_filename = old_filename; 253 current_lineno = old_lineno; 254 } 255 out->set_location(current_filename, current_lineno); 256 } 257 else { 258 out->set_location(current_filename, current_lineno); 259 string start_line; 260 while (c != EOF) { 261 if (c == '\n') { 262 current_lineno++; 263 break; 264 } 265 start_line += c; 266 c = getc(fp); 267 } 268 if (c == EOF) 269 return; 270 start_line += '\0'; 271 double wid, ht; 272 switch (sscanf(&start_line[0], "%lf %lf", &wid, &ht)) { 273 case 1: 274 ht = 0.0; 275 break; 276 case 2: 277 break; 278 default: 279 ht = wid = 0.0; 280 break; 281 } 282 out->set_desired_width_height(wid, ht); 283 out->set_args(start_line.contents()); 284 lex_init(new top_input(fp)); 285 if (yyparse()) { 286 had_parse_error = 1; 287 lex_error("giving up on this picture"); 288 } 289 parse_cleanup(); 290 lex_cleanup(); 291 292 // skip the rest of the .PF/.PE line 293 while ((c = getc(fp)) != EOF && c != '\n') 294 ; 295 if (c == '\n') 296 current_lineno++; 297 out->set_location(current_filename, current_lineno); 298 } 299} 300 301void do_file(const char *filename) 302{ 303 FILE *fp; 304 if (strcmp(filename, "-") == 0) 305 fp = stdin; 306 else { 307 errno = 0; 308 fp = fopen(filename, "r"); 309 if (fp == 0) { 310 delete out; 311 fatal("can't open `%1': %2", filename, strerror(errno)); 312 } 313 } 314 out->set_location(filename, 1); 315 current_filename = filename; 316 current_lineno = 1; 317 enum { START, MIDDLE, HAD_DOT, HAD_P, HAD_PS, HAD_l, HAD_lf } state = START; 318 for (;;) { 319 int c = getc(fp); 320 if (c == EOF) 321 break; 322 switch (state) { 323 case START: 324 if (c == '.') 325 state = HAD_DOT; 326 else { 327 putchar(c); 328 if (c == '\n') { 329 current_lineno++; 330 state = START; 331 } 332 else 333 state = MIDDLE; 334 } 335 break; 336 case MIDDLE: 337 putchar(c); 338 if (c == '\n') { 339 current_lineno++; 340 state = START; 341 } 342 break; 343 case HAD_DOT: 344 if (c == 'P') 345 state = HAD_P; 346 else if (lf_flag && c == 'l') 347 state = HAD_l; 348 else { 349 putchar('.'); 350 putchar(c); 351 if (c == '\n') { 352 current_lineno++; 353 state = START; 354 } 355 else 356 state = MIDDLE; 357 } 358 break; 359 case HAD_P: 360 if (c == 'S') 361 state = HAD_PS; 362 else { 363 putchar('.'); 364 putchar('P'); 365 putchar(c); 366 if (c == '\n') { 367 current_lineno++; 368 state = START; 369 } 370 else 371 state = MIDDLE; 372 } 373 break; 374 case HAD_PS: 375 if (c == ' ' || c == '\n' || compatible_flag) { 376 ungetc(c, fp); 377 do_picture(fp); 378 state = START; 379 } 380 else { 381 fputs(".PS", stdout); 382 putchar(c); 383 state = MIDDLE; 384 } 385 break; 386 case HAD_l: 387 if (c == 'f') 388 state = HAD_lf; 389 else { 390 putchar('.'); 391 putchar('l'); 392 putchar(c); 393 if (c == '\n') { 394 current_lineno++; 395 state = START; 396 } 397 else 398 state = MIDDLE; 399 } 400 break; 401 case HAD_lf: 402 if (c == ' ' || c == '\n' || compatible_flag) { 403 string line; 404 while (c != EOF) { 405 line += c; 406 if (c == '\n') { 407 current_lineno++; 408 break; 409 } 410 c = getc(fp); 411 } 412 line += '\0'; 413 interpret_lf_args(line.contents()); 414 printf(".lf%s", line.contents()); 415 state = START; 416 } 417 else { 418 fputs(".lf", stdout); 419 putchar(c); 420 state = MIDDLE; 421 } 422 break; 423 default: 424 assert(0); 425 } 426 } 427 switch (state) { 428 case START: 429 break; 430 case MIDDLE: 431 putchar('\n'); 432 break; 433 case HAD_DOT: 434 fputs(".\n", stdout); 435 break; 436 case HAD_P: 437 fputs(".P\n", stdout); 438 break; 439 case HAD_PS: 440 fputs(".PS\n", stdout); 441 break; 442 case HAD_l: 443 fputs(".l\n", stdout); 444 break; 445 case HAD_lf: 446 fputs(".lf\n", stdout); 447 break; 448 } 449 if (fp != stdin) 450 fclose(fp); 451} 452 453#ifdef FIG_SUPPORT 454void do_whole_file(const char *filename) 455{ 456 // Do not set current_filename. 457 FILE *fp; 458 if (strcmp(filename, "-") == 0) 459 fp = stdin; 460 else { 461 errno = 0; 462 fp = fopen(filename, "r"); 463 if (fp == 0) 464 fatal("can't open `%1': %2", filename, strerror(errno)); 465 } 466 lex_init(new file_input(fp, filename)); 467 if (yyparse()) 468 had_parse_error = 1; 469 parse_cleanup(); 470 lex_cleanup(); 471} 472#endif 473 474void usage(FILE *stream) 475{ 476 fprintf(stream, "usage: %s [ -nvC ] [ filename ... ]\n", program_name); 477#ifdef TEX_SUPPORT 478 fprintf(stream, " %s -t [ -cvzC ] [ filename ... ]\n", program_name); 479#endif 480#ifdef FIG_SUPPORT 481 fprintf(stream, " %s -f [ -v ] [ filename ]\n", program_name); 482#endif 483} 484 485#if defined(__MSDOS__) || defined(__EMX__) 486static char *fix_program_name(char *arg, char *dflt) 487{ 488 if (!arg) 489 return dflt; 490 char *prog = strchr(arg, '\0'); 491 for (;;) { 492 if (prog == arg) 493 break; 494 --prog; 495 if (strchr("\\/:", *prog)) { 496 prog++; 497 break; 498 } 499 } 500 char *ext = strchr(prog, '.'); 501 if (ext) 502 *ext = '\0'; 503 for (char *p = prog; *p; p++) 504 if ('A' <= *p && *p <= 'Z') 505 *p = 'a' + (*p - 'A'); 506 return prog; 507} 508#endif /* __MSDOS__ || __EMX__ */ 509 510int main(int argc, char **argv) 511{ 512 setlocale(LC_NUMERIC, "C"); 513#if defined(__MSDOS__) || defined(__EMX__) 514 argv[0] = fix_program_name(argv[0], "pic"); 515#endif /* __MSDOS__ || __EMX__ */ 516 program_name = argv[0]; 517 static char stderr_buf[BUFSIZ]; 518 setbuf(stderr, stderr_buf); 519 int opt; 520#ifdef TEX_SUPPORT 521 int tex_flag = 0; 522 int tpic_flag = 0; 523#endif 524#ifdef FIG_SUPPORT 525 int whole_file_flag = 0; 526 int fig_flag = 0; 527#endif 528 static const struct option long_options[] = { 529 { "help", no_argument, 0, CHAR_MAX + 1 }, 530 { "version", no_argument, 0, 'v' }, 531 { NULL, 0, 0, 0 } 532 }; 533 while ((opt = getopt_long(argc, argv, "T:CDSUtcvnxzpf", long_options, NULL)) 534 != EOF) 535 switch (opt) { 536 case 'C': 537 compatible_flag = 1; 538 break; 539 case 'D': 540 case 'T': 541 break; 542 case 'S': 543 safer_flag = 1; 544 break; 545 case 'U': 546 safer_flag = 0; 547 break; 548 case 'f': 549#ifdef FIG_SUPPORT 550 whole_file_flag++; 551 fig_flag++; 552#else 553 fatal("fig support not included"); 554#endif 555 break; 556 case 'n': 557 driver_extension_flag = 0; 558 break; 559 case 'p': 560 case 'x': 561 warning("-%1 option is obsolete", char(opt)); 562 break; 563 case 't': 564#ifdef TEX_SUPPORT 565 tex_flag++; 566#else 567 fatal("TeX support not included"); 568#endif 569 break; 570 case 'c': 571#ifdef TEX_SUPPORT 572 tpic_flag++; 573#else 574 fatal("TeX support not included"); 575#endif 576 break; 577 case 'v': 578 { 579 printf("GNU pic (groff) version %s\n", Version_string); 580 exit(0); 581 break; 582 } 583 case 'z': 584 // zero length lines will be printed as dots 585 zero_length_line_flag++; 586 break; 587 case CHAR_MAX + 1: // --help 588 usage(stdout); 589 exit(0); 590 break; 591 case '?': 592 usage(stderr); 593 exit(1); 594 break; 595 default: 596 assert(0); 597 } 598 parse_init(); 599#ifdef TEX_SUPPORT 600 if (tpic_flag) { 601 out = make_tpic_output(); 602 lf_flag = 0; 603 } 604 else if (tex_flag) { 605 out = make_tex_output(); 606 command_char = '\\'; 607 lf_flag = 0; 608 } 609 else 610#endif 611#ifdef FIG_SUPPORT 612 if (fig_flag) 613 out = make_fig_output(); 614 else 615#endif 616 out = make_troff_output(); 617#ifdef FIG_SUPPORT 618 if (whole_file_flag) { 619 if (optind >= argc) 620 do_whole_file("-"); 621 else if (argc - optind > 1) { 622 usage(stderr); 623 exit(1); 624 } else 625 do_whole_file(argv[optind]); 626 } 627 else { 628#endif 629 if (optind >= argc) 630 do_file("-"); 631 else 632 for (int i = optind; i < argc; i++) 633 do_file(argv[i]); 634#ifdef FIG_SUPPORT 635 } 636#endif 637 delete out; 638 if (ferror(stdout) || fflush(stdout) < 0) 639 fatal("output error"); 640 return had_parse_error; 641} 642 643