main.cpp revision 256281
1186690Sobrien// -*- C++ -*- 2186690Sobrien/* Copyright (C) 1989-1992, 2000, 2001, 2002, 2003 3186690Sobrien Free Software Foundation, Inc. 4186690Sobrien Written by James Clark (jjc@jclark.com) 5186690Sobrien 6186690SobrienThis file is part of groff. 7186690Sobrien 8186690Sobriengroff is free software; you can redistribute it and/or modify it under 9186690Sobrienthe terms of the GNU General Public License as published by the Free 10186690SobrienSoftware Foundation; either version 2, or (at your option) any later 11186690Sobrienversion. 12186690Sobrien 13186690Sobriengroff is distributed in the hope that it will be useful, but WITHOUT ANY 14186690SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or 15186690SobrienFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16186690Sobrienfor more details. 17186690Sobrien 18186690SobrienYou should have received a copy of the GNU General Public License along 19186690Sobrienwith groff; see the file COPYING. If not, write to the Free Software 20186690SobrienFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21186690Sobrien 22186690Sobrien#include "pic.h" 23186690Sobrien 24186690Sobrienextern int yyparse(); 25186690Sobrienextern "C" const char *Version_string; 26186690Sobrien 27186690Sobrienoutput *out; 28186690Sobrienchar *graphname; // the picture box name in TeX mode 29186690Sobrien 30186690Sobrienint flyback_flag; 31186690Sobrienint zero_length_line_flag = 0; 32186690Sobrien// Non-zero means we're using a groff driver. 33186690Sobrienint driver_extension_flag = 1; 34186690Sobrienint compatible_flag = 0; 35186690Sobrienint safer_flag = 1; 36186690Sobrienint command_char = '.'; // the character that introduces lines 37186690Sobrien // that should be passed through tranparently 38186690Sobrienstatic int lf_flag = 1; // non-zero if we should attempt to understand 39186690Sobrien // lines beginning with `.lf' 40186690Sobrien 41186690Sobrien// Non-zero means a parse error was encountered. 42186690Sobrienstatic int had_parse_error = 0; 43186690Sobrien 44186690Sobrienvoid do_file(const char *filename); 45186690Sobrien 46186690Sobrienclass top_input : public input { 47186690Sobrien FILE *fp; 48186690Sobrien int bol; 49186690Sobrien int eof; 50186690Sobrien int push_back[3]; 51186690Sobrien int start_lineno; 52186690Sobrienpublic: 53186690Sobrien top_input(FILE *); 54186690Sobrien int get(); 55186690Sobrien int peek(); 56186690Sobrien int get_location(const char **, int *); 57186690Sobrien}; 58186690Sobrien 59186690Sobrientop_input::top_input(FILE *p) : fp(p), bol(1), eof(0) 60186690Sobrien{ 61186690Sobrien push_back[0] = push_back[1] = push_back[2] = EOF; 62186690Sobrien start_lineno = current_lineno; 63186690Sobrien} 64186690Sobrien 65186690Sobrienint top_input::get() 66186690Sobrien{ 67186690Sobrien if (eof) 68186690Sobrien return EOF; 69186690Sobrien if (push_back[2] != EOF) { 70186690Sobrien int c = push_back[2]; 71186690Sobrien push_back[2] = EOF; 72186690Sobrien return c; 73186690Sobrien } 74186690Sobrien else if (push_back[1] != EOF) { 75186690Sobrien int c = push_back[1]; 76186690Sobrien push_back[1] = EOF; 77186690Sobrien return c; 78186690Sobrien } 79186690Sobrien else if (push_back[0] != EOF) { 80186690Sobrien int c = push_back[0]; 81186690Sobrien push_back[0] = EOF; 82186690Sobrien return c; 83328875Seadler } 84328875Seadler int c = getc(fp); 85186690Sobrien while (invalid_input_char(c)) { 86186690Sobrien error("invalid input character code %1", int(c)); 87186690Sobrien c = getc(fp); 88186690Sobrien bol = 0; 89186690Sobrien } 90186690Sobrien if (bol && c == '.') { 91186690Sobrien c = getc(fp); 92186690Sobrien if (c == 'P') { 93186690Sobrien c = getc(fp); 94186690Sobrien if (c == 'F' || c == 'E') { 95186690Sobrien int d = getc(fp); 96186690Sobrien if (d != EOF) 97186690Sobrien ungetc(d, fp); 98186690Sobrien if (d == EOF || d == ' ' || d == '\n' || compatible_flag) { 99186690Sobrien eof = 1; 100186690Sobrien flyback_flag = c == 'F'; 101186690Sobrien return EOF; 102186690Sobrien } 103186690Sobrien push_back[0] = c; 104186690Sobrien push_back[1] = 'P'; 105186690Sobrien return '.'; 106186690Sobrien } 107186690Sobrien if (c == 'S') { 108186690Sobrien c = getc(fp); 109186690Sobrien if (c != EOF) 110186690Sobrien ungetc(c, fp); 111186690Sobrien if (c == EOF || c == ' ' || c == '\n' || compatible_flag) { 112186690Sobrien error("nested .PS"); 113186690Sobrien eof = 1; 114186690Sobrien return EOF; 115186690Sobrien } 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