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