1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004 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 "driver.h" 23#include "stringclass.h" 24#include "cset.h" 25 26#include "ps.h" 27 28#ifdef NEED_DECLARATION_PUTENV 29extern "C" { 30 int putenv(const char *); 31} 32#endif /* NEED_DECLARATION_PUTENV */ 33 34#define GROPS_PROLOGUE "prologue" 35 36static void print_ps_string(const string &s, FILE *outfp); 37 38cset white_space("\n\r \t\f"); 39string an_empty_string; 40 41char valid_input_table[256]= { 42#ifndef IS_EBCDIC_HOST 43 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 45 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 48 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 49 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 51 52 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 53 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 54 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 55 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 56 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 57 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 58 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 59 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 60#else 61 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 62 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 66 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 67 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 68 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 69 70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 71 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 72 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 73 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 76 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 78#endif 79}; 80 81const char *extension_table[] = { 82 "DPS", 83 "CMYK", 84 "Composite", 85 "FileSystem", 86}; 87 88const int NEXTENSIONS = sizeof(extension_table)/sizeof(extension_table[0]); 89 90const char *resource_table[] = { 91 "font", 92 "procset", 93 "file", 94 "encoding", 95 "form", 96 "pattern", 97}; 98 99const int NRESOURCES = sizeof(resource_table)/sizeof(resource_table[0]); 100 101static int read_uint_arg(const char **pp, unsigned *res) 102{ 103 while (white_space(**pp)) 104 *pp += 1; 105 if (**pp == '\0') { 106 error("missing argument"); 107 return 0; 108 } 109 const char *start = *pp; 110 // XXX use strtoul 111 long n = strtol(start, (char **)pp, 10); 112 if (n == 0 && *pp == start) { 113 error("not an integer"); 114 return 0; 115 } 116 if (n < 0) { 117 error("argument must not be negative"); 118 return 0; 119 } 120 *res = unsigned(n); 121 return 1; 122} 123 124struct resource { 125 resource *next; 126 resource_type type; 127 string name; 128 enum { NEEDED = 01, SUPPLIED = 02, FONT_NEEDED = 04, BUSY = 010 }; 129 unsigned flags; 130 string version; 131 unsigned revision; 132 char *filename; 133 int rank; 134 resource(resource_type, string &, string & = an_empty_string, unsigned = 0); 135 ~resource(); 136 void print_type_and_name(FILE *outfp); 137}; 138 139resource::resource(resource_type t, string &n, string &v, unsigned r) 140: next(0), type(t), flags(0), revision(r), filename(0), rank(-1) 141{ 142 name.move(n); 143 version.move(v); 144 if (type == RESOURCE_FILE) { 145 if (name.search('\0') >= 0) 146 error("filename contains a character with code 0"); 147 filename = name.extract(); 148 } 149} 150 151resource::~resource() 152{ 153 a_delete filename; 154} 155 156void resource::print_type_and_name(FILE *outfp) 157{ 158 fputs(resource_table[type], outfp); 159 putc(' ', outfp); 160 print_ps_string(name, outfp); 161 if (type == RESOURCE_PROCSET) { 162 putc(' ', outfp); 163 print_ps_string(version, outfp); 164 fprintf(outfp, " %u", revision); 165 } 166} 167 168resource_manager::resource_manager() 169: extensions(0), language_level(0), resource_list(0) 170{ 171 read_download_file(); 172 string procset_name("grops"); 173 extern const char *version_string; 174 extern const char *revision_string; 175 unsigned revision_uint; 176 if (!read_uint_arg(&revision_string, &revision_uint)) 177 revision_uint = 0; 178 string procset_version(version_string); 179 procset_resource = lookup_resource(RESOURCE_PROCSET, procset_name, 180 procset_version, revision_uint); 181 procset_resource->flags |= resource::SUPPLIED; 182} 183 184resource_manager::~resource_manager() 185{ 186 while (resource_list) { 187 resource *tem = resource_list; 188 resource_list = resource_list->next; 189 delete tem; 190 } 191} 192 193resource *resource_manager::lookup_resource(resource_type type, 194 string &name, 195 string &version, 196 unsigned revision) 197{ 198 resource *r; 199 for (r = resource_list; r; r = r->next) 200 if (r->type == type 201 && r->name == name 202 && r->version == version 203 && r->revision == revision) 204 return r; 205 r = new resource(type, name, version, revision); 206 r->next = resource_list; 207 resource_list = r; 208 return r; 209} 210 211// Just a specialized version of lookup_resource(). 212 213resource *resource_manager::lookup_font(const char *name) 214{ 215 resource *r; 216 for (r = resource_list; r; r = r->next) 217 if (r->type == RESOURCE_FONT 218 && strlen(name) == (size_t)r->name.length() 219 && memcmp(name, r->name.contents(), r->name.length()) == 0) 220 return r; 221 string s(name); 222 r = new resource(RESOURCE_FONT, s); 223 r->next = resource_list; 224 resource_list = r; 225 return r; 226} 227 228void resource_manager::need_font(const char *name) 229{ 230 lookup_font(name)->flags |= resource::FONT_NEEDED; 231} 232 233typedef resource *Presource; // Work around g++ bug. 234 235void resource_manager::document_setup(ps_output &out) 236{ 237 int nranks = 0; 238 resource *r; 239 for (r = resource_list; r; r = r->next) 240 if (r->rank >= nranks) 241 nranks = r->rank + 1; 242 if (nranks > 0) { 243 // Sort resource_list in reverse order of rank. 244 Presource *head = new Presource[nranks + 1]; 245 Presource **tail = new Presource *[nranks + 1]; 246 int i; 247 for (i = 0; i < nranks + 1; i++) { 248 head[i] = 0; 249 tail[i] = &head[i]; 250 } 251 for (r = resource_list; r; r = r->next) { 252 i = r->rank < 0 ? 0 : r->rank + 1; 253 *tail[i] = r; 254 tail[i] = &(*tail[i])->next; 255 } 256 resource_list = 0; 257 for (i = 0; i < nranks + 1; i++) 258 if (head[i]) { 259 *tail[i] = resource_list; 260 resource_list = head[i]; 261 } 262 a_delete head; 263 a_delete tail; 264 // check it 265 for (r = resource_list; r; r = r->next) 266 if (r->next) 267 assert(r->rank >= r->next->rank); 268 for (r = resource_list; r; r = r->next) 269 if (r->type == RESOURCE_FONT && r->rank >= 0) 270 supply_resource(r, -1, out.get_file()); 271 } 272} 273 274void resource_manager::print_resources_comment(unsigned flag, FILE *outfp) 275{ 276 int continued = 0; 277 for (resource *r = resource_list; r; r = r->next) 278 if (r->flags & flag) { 279 if (continued) 280 fputs("%%+ ", outfp); 281 else { 282 fputs(flag == resource::NEEDED 283 ? "%%DocumentNeededResources: " 284 : "%%DocumentSuppliedResources: ", 285 outfp); 286 continued = 1; 287 } 288 r->print_type_and_name(outfp); 289 putc('\n', outfp); 290 } 291} 292 293void resource_manager::print_header_comments(ps_output &out) 294{ 295 for (resource *r = resource_list; r; r = r->next) 296 if (r->type == RESOURCE_FONT && (r->flags & resource::FONT_NEEDED)) 297 supply_resource(r, 0, 0); 298 print_resources_comment(resource::NEEDED, out.get_file()); 299 print_resources_comment(resource::SUPPLIED, out.get_file()); 300 print_language_level_comment(out.get_file()); 301 print_extensions_comment(out.get_file()); 302} 303 304void resource_manager::output_prolog(ps_output &out) 305{ 306 FILE *outfp = out.get_file(); 307 out.end_line(); 308 char *path; 309 if (!getenv("GROPS_PROLOGUE")) { 310 string e = "GROPS_PROLOGUE"; 311 e += '='; 312 e += GROPS_PROLOGUE; 313 e += '\0'; 314 if (putenv(strsave(e.contents()))) 315 fatal("putenv failed"); 316 } 317 char *prologue = getenv("GROPS_PROLOGUE"); 318 FILE *fp = font::open_file(prologue, &path); 319 if (!fp) 320 fatal("can't find `%1'", prologue); 321 fputs("%%BeginResource: ", outfp); 322 procset_resource->print_type_and_name(outfp); 323 putc('\n', outfp); 324 process_file(-1, fp, path, outfp); 325 fclose(fp); 326 a_delete path; 327 fputs("%%EndResource\n", outfp); 328} 329 330void resource_manager::import_file(const char *filename, ps_output &out) 331{ 332 out.end_line(); 333 string name(filename); 334 resource *r = lookup_resource(RESOURCE_FILE, name); 335 supply_resource(r, -1, out.get_file(), 1); 336} 337 338void resource_manager::supply_resource(resource *r, int rank, FILE *outfp, 339 int is_document) 340{ 341 if (r->flags & resource::BUSY) { 342 r->name += '\0'; 343 fatal("loop detected in dependency graph for %1 `%2'", 344 resource_table[r->type], 345 r->name.contents()); 346 } 347 r->flags |= resource::BUSY; 348 if (rank > r->rank) 349 r->rank = rank; 350 char *path = 0; // pacify compiler 351 FILE *fp = 0; 352 if (r->filename != 0) { 353 if (r->type == RESOURCE_FONT) { 354 fp = font::open_file(r->filename, &path); 355 if (!fp) { 356 error("can't find `%1'", r->filename); 357 a_delete r->filename; 358 r->filename = 0; 359 } 360 } 361 else { 362 errno = 0; 363 fp = include_search_path.open_file_cautious(r->filename); 364 if (!fp) { 365 error("can't open `%1': %2", r->filename, strerror(errno)); 366 a_delete r->filename; 367 r->filename = 0; 368 } 369 else 370 path = r->filename; 371 } 372 } 373 if (fp) { 374 if (outfp) { 375 if (r->type == RESOURCE_FILE && is_document) { 376 fputs("%%BeginDocument: ", outfp); 377 print_ps_string(r->name, outfp); 378 putc('\n', outfp); 379 } 380 else { 381 fputs("%%BeginResource: ", outfp); 382 r->print_type_and_name(outfp); 383 putc('\n', outfp); 384 } 385 } 386 process_file(rank, fp, path, outfp); 387 fclose(fp); 388 if (r->type == RESOURCE_FONT) 389 a_delete path; 390 if (outfp) { 391 if (r->type == RESOURCE_FILE && is_document) 392 fputs("%%EndDocument\n", outfp); 393 else 394 fputs("%%EndResource\n", outfp); 395 } 396 r->flags |= resource::SUPPLIED; 397 } 398 else { 399 if (outfp) { 400 if (r->type == RESOURCE_FILE && is_document) { 401 fputs("%%IncludeDocument: ", outfp); 402 print_ps_string(r->name, outfp); 403 putc('\n', outfp); 404 } 405 else { 406 fputs("%%IncludeResource: ", outfp); 407 r->print_type_and_name(outfp); 408 putc('\n', outfp); 409 } 410 } 411 r->flags |= resource::NEEDED; 412 } 413 r->flags &= ~resource::BUSY; 414} 415 416#define PS_MAGIC "%!PS-Adobe-" 417 418static int ps_get_line(string &buf, FILE *fp) 419{ 420 buf.clear(); 421 int c = getc(fp); 422 if (c == EOF) 423 return 0; 424 current_lineno++; 425 while (c != '\r' && c != '\n' && c != EOF) { 426 if (!valid_input_table[c]) 427 error("invalid input character code %1", int(c)); 428 buf += c; 429 c = getc(fp); 430 } 431 buf += '\n'; 432 buf += '\0'; 433 if (c == '\r') { 434 c = getc(fp); 435 if (c != EOF && c != '\n') 436 ungetc(c, fp); 437 } 438 return 1; 439} 440 441static int read_text_arg(const char **pp, string &res) 442{ 443 res.clear(); 444 while (white_space(**pp)) 445 *pp += 1; 446 if (**pp == '\0') { 447 error("missing argument"); 448 return 0; 449 } 450 if (**pp != '(') { 451 for (; **pp != '\0' && !white_space(**pp); *pp += 1) 452 res += **pp; 453 return 1; 454 } 455 *pp += 1; 456 res.clear(); 457 int level = 0; 458 for (;;) { 459 if (**pp == '\0' || **pp == '\r' || **pp == '\n') { 460 error("missing ')'"); 461 return 0; 462 } 463 if (**pp == ')') { 464 if (level == 0) { 465 *pp += 1; 466 break; 467 } 468 res += **pp; 469 level--; 470 } 471 else if (**pp == '(') { 472 level++; 473 res += **pp; 474 } 475 else if (**pp == '\\') { 476 *pp += 1; 477 switch (**pp) { 478 case 'n': 479 res += '\n'; 480 break; 481 case 'r': 482 res += '\n'; 483 break; 484 case 't': 485 res += '\t'; 486 break; 487 case 'b': 488 res += '\b'; 489 break; 490 case 'f': 491 res += '\f'; 492 break; 493 case '0': 494 case '1': 495 case '2': 496 case '3': 497 case '4': 498 case '5': 499 case '6': 500 case '7': 501 { 502 int val = **pp - '0'; 503 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { 504 *pp += 1; 505 val = val*8 + (**pp - '0'); 506 if ((*pp)[1] >= '0' && (*pp)[1] <= '7') { 507 *pp += 1; 508 val = val*8 + (**pp - '0'); 509 } 510 } 511 } 512 break; 513 default: 514 res += **pp; 515 break; 516 } 517 } 518 else 519 res += **pp; 520 *pp += 1; 521 } 522 return 1; 523} 524 525resource *resource_manager::read_file_arg(const char **ptr) 526{ 527 string arg; 528 if (!read_text_arg(ptr, arg)) 529 return 0; 530 return lookup_resource(RESOURCE_FILE, arg); 531} 532 533resource *resource_manager::read_font_arg(const char **ptr) 534{ 535 string arg; 536 if (!read_text_arg(ptr, arg)) 537 return 0; 538 return lookup_resource(RESOURCE_FONT, arg); 539} 540 541resource *resource_manager::read_procset_arg(const char **ptr) 542{ 543 string arg; 544 if (!read_text_arg(ptr, arg)) 545 return 0; 546 string version; 547 if (!read_text_arg(ptr, version)) 548 return 0; 549 unsigned revision; 550 if (!read_uint_arg(ptr, &revision)) 551 return 0; 552 return lookup_resource(RESOURCE_PROCSET, arg, version, revision); 553} 554 555resource *resource_manager::read_resource_arg(const char **ptr) 556{ 557 while (white_space(**ptr)) 558 *ptr += 1; 559 const char *name = *ptr; 560 while (**ptr != '\0' && !white_space(**ptr)) 561 *ptr += 1; 562 if (name == *ptr) { 563 error("missing resource type"); 564 return 0; 565 } 566 int ri; 567 for (ri = 0; ri < NRESOURCES; ri++) 568 if (strlen(resource_table[ri]) == size_t(*ptr - name) 569 && memcmp(resource_table[ri], name, *ptr - name) == 0) 570 break; 571 if (ri >= NRESOURCES) { 572 error("unknown resource type"); 573 return 0; 574 } 575 if (ri == RESOURCE_PROCSET) 576 return read_procset_arg(ptr); 577 string arg; 578 if (!read_text_arg(ptr, arg)) 579 return 0; 580 return lookup_resource(resource_type(ri), arg); 581} 582 583static const char *matches_comment(string &buf, const char *comment) 584{ 585 if ((size_t)buf.length() < strlen(comment) + 3) 586 return 0; 587 if (buf[0] != '%' || buf[1] != '%') 588 return 0; 589 const char *bufp = buf.contents() + 2; 590 for (; *comment; comment++, bufp++) 591 if (*bufp != *comment) 592 return 0; 593 if (comment[-1] == ':') 594 return bufp; 595 if (*bufp == '\0' || white_space(*bufp)) 596 return bufp; 597 return 0; 598} 599 600// Return 1 if the line should be copied out. 601 602int resource_manager::do_begin_resource(const char *ptr, int, FILE *, 603 FILE *) 604{ 605 resource *r = read_resource_arg(&ptr); 606 if (r) 607 r->flags |= resource::SUPPLIED; 608 return 1; 609} 610 611int resource_manager::do_include_resource(const char *ptr, int rank, FILE *, 612 FILE *outfp) 613{ 614 resource *r = read_resource_arg(&ptr); 615 if (r) { 616 if (r->type == RESOURCE_FONT) { 617 if (rank >= 0) 618 supply_resource(r, rank + 1, outfp); 619 else 620 r->flags |= resource::FONT_NEEDED; 621 } 622 else 623 supply_resource(r, rank, outfp); 624 } 625 return 0; 626} 627 628int resource_manager::do_begin_document(const char *ptr, int, FILE *, 629 FILE *) 630{ 631 resource *r = read_file_arg(&ptr); 632 if (r) 633 r->flags |= resource::SUPPLIED; 634 return 1; 635} 636 637int resource_manager::do_include_document(const char *ptr, int rank, FILE *, 638 FILE *outfp) 639{ 640 resource *r = read_file_arg(&ptr); 641 if (r) 642 supply_resource(r, rank, outfp, 1); 643 return 0; 644} 645 646int resource_manager::do_begin_procset(const char *ptr, int, FILE *, 647 FILE *outfp) 648{ 649 resource *r = read_procset_arg(&ptr); 650 if (r) { 651 r->flags |= resource::SUPPLIED; 652 if (outfp) { 653 fputs("%%BeginResource: ", outfp); 654 r->print_type_and_name(outfp); 655 putc('\n', outfp); 656 } 657 } 658 return 0; 659} 660 661int resource_manager::do_include_procset(const char *ptr, int rank, FILE *, 662 FILE *outfp) 663{ 664 resource *r = read_procset_arg(&ptr); 665 if (r) 666 supply_resource(r, rank, outfp); 667 return 0; 668} 669 670int resource_manager::do_begin_file(const char *ptr, int, FILE *, 671 FILE *outfp) 672{ 673 resource *r = read_file_arg(&ptr); 674 if (r) { 675 r->flags |= resource::SUPPLIED; 676 if (outfp) { 677 fputs("%%BeginResource: ", outfp); 678 r->print_type_and_name(outfp); 679 putc('\n', outfp); 680 } 681 } 682 return 0; 683} 684 685int resource_manager::do_include_file(const char *ptr, int rank, FILE *, 686 FILE *outfp) 687{ 688 resource *r = read_file_arg(&ptr); 689 if (r) 690 supply_resource(r, rank, outfp); 691 return 0; 692} 693 694int resource_manager::do_begin_font(const char *ptr, int, FILE *, 695 FILE *outfp) 696{ 697 resource *r = read_font_arg(&ptr); 698 if (r) { 699 r->flags |= resource::SUPPLIED; 700 if (outfp) { 701 fputs("%%BeginResource: ", outfp); 702 r->print_type_and_name(outfp); 703 putc('\n', outfp); 704 } 705 } 706 return 0; 707} 708 709int resource_manager::do_include_font(const char *ptr, int rank, FILE *, 710 FILE *outfp) 711{ 712 resource *r = read_font_arg(&ptr); 713 if (r) { 714 if (rank >= 0) 715 supply_resource(r, rank + 1, outfp); 716 else 717 r->flags |= resource::FONT_NEEDED; 718 } 719 return 0; 720} 721 722int resource_manager::change_to_end_resource(const char *, int, FILE *, 723 FILE *outfp) 724{ 725 if (outfp) 726 fputs("%%EndResource\n", outfp); 727 return 0; 728} 729 730int resource_manager::do_begin_preview(const char *, int, FILE *fp, FILE *) 731{ 732 string buf; 733 do { 734 if (!ps_get_line(buf, fp)) { 735 error("end of file in preview section"); 736 break; 737 } 738 } while (!matches_comment(buf, "EndPreview")); 739 return 0; 740} 741 742int read_one_of(const char **ptr, const char **s, int n) 743{ 744 while (white_space(**ptr)) 745 *ptr += 1; 746 if (**ptr == '\0') 747 return -1; 748 const char *start = *ptr; 749 do { 750 ++(*ptr); 751 } while (**ptr != '\0' && !white_space(**ptr)); 752 for (int i = 0; i < n; i++) 753 if (strlen(s[i]) == size_t(*ptr - start) 754 && memcmp(s[i], start, *ptr - start) == 0) 755 return i; 756 return -1; 757} 758 759void skip_possible_newline(FILE *fp, FILE *outfp) 760{ 761 int c = getc(fp); 762 if (c == '\r') { 763 current_lineno++; 764 if (outfp) 765 putc(c, outfp); 766 int cc = getc(fp); 767 if (cc != '\n') { 768 if (cc != EOF) 769 ungetc(cc, fp); 770 } 771 else { 772 if (outfp) 773 putc(cc, outfp); 774 } 775 } 776 else if (c == '\n') { 777 current_lineno++; 778 if (outfp) 779 putc(c, outfp); 780 } 781 else if (c != EOF) 782 ungetc(c, fp); 783} 784 785int resource_manager::do_begin_data(const char *ptr, int, FILE *fp, 786 FILE *outfp) 787{ 788 while (white_space(*ptr)) 789 ptr++; 790 const char *start = ptr; 791 unsigned numberof; 792 if (!read_uint_arg(&ptr, &numberof)) 793 return 0; 794 static const char *types[] = { "Binary", "Hex", "ASCII" }; 795 const int Binary = 0; 796 int type = 0; 797 static const char *units[] = { "Bytes", "Lines" }; 798 const int Bytes = 0; 799 int unit = Bytes; 800 while (white_space(*ptr)) 801 ptr++; 802 if (*ptr != '\0') { 803 type = read_one_of(&ptr, types, 3); 804 if (type < 0) { 805 error("bad data type"); 806 return 0; 807 } 808 while (white_space(*ptr)) 809 ptr++; 810 if (*ptr != '\0') { 811 unit = read_one_of(&ptr, units, 2); 812 if (unit < 0) { 813 error("expected `Bytes' or `Lines'"); 814 return 0; 815 } 816 } 817 } 818 if (type != Binary) 819 return 1; 820 if (outfp) { 821 fputs("%%BeginData: ", outfp); 822 fputs(start, outfp); 823 } 824 if (numberof > 0) { 825 unsigned bytecount = 0; 826 unsigned linecount = 0; 827 do { 828 int c = getc(fp); 829 if (c == EOF) { 830 error("end of file within data section"); 831 return 0; 832 } 833 if (outfp) 834 putc(c, outfp); 835 bytecount++; 836 if (c == '\r') { 837 int cc = getc(fp); 838 if (cc != '\n') { 839 linecount++; 840 current_lineno++; 841 } 842 if (cc != EOF) 843 ungetc(c, fp); 844 } 845 else if (c == '\n') { 846 linecount++; 847 current_lineno++; 848 } 849 } while ((unit == Bytes ? bytecount : linecount) < numberof); 850 } 851 skip_possible_newline(fp, outfp); 852 string buf; 853 if (!ps_get_line(buf, fp)) { 854 error("missing %%%%EndData line"); 855 return 0; 856 } 857 if (!matches_comment(buf, "EndData")) 858 error("bad %%%%EndData line"); 859 if (outfp) 860 fputs(buf.contents(), outfp); 861 return 0; 862} 863 864int resource_manager::do_begin_binary(const char *ptr, int, FILE *fp, 865 FILE *outfp) 866{ 867 if (!outfp) 868 return 0; 869 unsigned count; 870 if (!read_uint_arg(&ptr, &count)) 871 return 0; 872 if (outfp) 873 fprintf(outfp, "%%%%BeginData: %u Binary Bytes\n", count); 874 while (count != 0) { 875 int c = getc(fp); 876 if (c == EOF) { 877 error("end of file within binary section"); 878 return 0; 879 } 880 if (outfp) 881 putc(c, outfp); 882 --count; 883 if (c == '\r') { 884 int cc = getc(fp); 885 if (cc != '\n') 886 current_lineno++; 887 if (cc != EOF) 888 ungetc(cc, fp); 889 } 890 else if (c == '\n') 891 current_lineno++; 892 } 893 skip_possible_newline(fp, outfp); 894 string buf; 895 if (!ps_get_line(buf, fp)) { 896 error("missing %%%%EndBinary line"); 897 return 0; 898 } 899 if (!matches_comment(buf, "EndBinary")) { 900 error("bad %%%%EndBinary line"); 901 if (outfp) 902 fputs(buf.contents(), outfp); 903 } 904 else if (outfp) 905 fputs("%%EndData\n", outfp); 906 return 0; 907} 908 909static unsigned parse_extensions(const char *ptr) 910{ 911 unsigned flags = 0; 912 for (;;) { 913 while (white_space(*ptr)) 914 ptr++; 915 if (*ptr == '\0') 916 break; 917 const char *name = ptr; 918 do { 919 ++ptr; 920 } while (*ptr != '\0' && !white_space(*ptr)); 921 int i; 922 for (i = 0; i < NEXTENSIONS; i++) 923 if (strlen(extension_table[i]) == size_t(ptr - name) 924 && memcmp(extension_table[i], name, ptr - name) == 0) { 925 flags |= (1 << i); 926 break; 927 } 928 if (i >= NEXTENSIONS) { 929 string s(name, ptr - name); 930 s += '\0'; 931 error("unknown extension `%1'", s.contents()); 932 } 933 } 934 return flags; 935} 936 937// XXX if it has not been surrounded with {Begin,End}Document need to strip 938// out Page: Trailer {Begin,End}Prolog {Begin,End}Setup sections. 939 940// XXX Perhaps the decision whether to use BeginDocument or 941// BeginResource: file should be postponed till we have seen 942// the first line of the file. 943 944void resource_manager::process_file(int rank, FILE *fp, const char *filename, 945 FILE *outfp) 946{ 947 // If none of these comments appear in the header section, and we are 948 // just analyzing the file (ie outfp is 0), then we can return immediately. 949 static const char *header_comment_table[] = { 950 "DocumentNeededResources:", 951 "DocumentSuppliedResources:", 952 "DocumentNeededFonts:", 953 "DocumentSuppliedFonts:", 954 "DocumentNeededProcSets:", 955 "DocumentSuppliedProcSets:", 956 "DocumentNeededFiles:", 957 "DocumentSuppliedFiles:", 958 }; 959 960 const int NHEADER_COMMENTS = sizeof(header_comment_table) 961 / sizeof(header_comment_table[0]); 962 struct comment_info { 963 const char *name; 964 int (resource_manager::*proc)(const char *, int, FILE *, FILE *); 965 }; 966 967 static comment_info comment_table[] = { 968 { "BeginResource:", &resource_manager::do_begin_resource }, 969 { "IncludeResource:", &resource_manager::do_include_resource }, 970 { "BeginDocument:", &resource_manager::do_begin_document }, 971 { "IncludeDocument:", &resource_manager::do_include_document }, 972 { "BeginProcSet:", &resource_manager::do_begin_procset }, 973 { "IncludeProcSet:", &resource_manager::do_include_procset }, 974 { "BeginFont:", &resource_manager::do_begin_font }, 975 { "IncludeFont:", &resource_manager::do_include_font }, 976 { "BeginFile:", &resource_manager::do_begin_file }, 977 { "IncludeFile:", &resource_manager::do_include_file }, 978 { "EndProcSet", &resource_manager::change_to_end_resource }, 979 { "EndFont", &resource_manager::change_to_end_resource }, 980 { "EndFile", &resource_manager::change_to_end_resource }, 981 { "BeginPreview:", &resource_manager::do_begin_preview }, 982 { "BeginData:", &resource_manager::do_begin_data }, 983 { "BeginBinary:", &resource_manager::do_begin_binary }, 984 }; 985 986 const int NCOMMENTS = sizeof(comment_table)/sizeof(comment_table[0]); 987 string buf; 988 int saved_lineno = current_lineno; 989 const char *saved_filename = current_filename; 990 current_filename = filename; 991 current_lineno = 0; 992 if (!ps_get_line(buf, fp)) { 993 current_filename = saved_filename; 994 current_lineno = saved_lineno; 995 return; 996 } 997 if ((size_t)buf.length() < sizeof(PS_MAGIC) 998 || memcmp(buf.contents(), PS_MAGIC, sizeof(PS_MAGIC) - 1) != 0) { 999 if (outfp) { 1000 do { 1001 if (!(broken_flags & STRIP_PERCENT_BANG) 1002 || buf[0] != '%' || buf[1] != '!') 1003 fputs(buf.contents(), outfp); 1004 } while (ps_get_line(buf, fp)); 1005 } 1006 } 1007 else { 1008 if (!(broken_flags & STRIP_PERCENT_BANG) && outfp) 1009 fputs(buf.contents(), outfp); 1010 int in_header = 1; 1011 int interesting = 0; 1012 int had_extensions_comment = 0; 1013 int had_language_level_comment = 0; 1014 for (;;) { 1015 if (!ps_get_line(buf, fp)) 1016 break; 1017 int copy_this_line = 1; 1018 if (buf[0] == '%') { 1019 if (buf[1] == '%') { 1020 const char *ptr; 1021 int i; 1022 for (i = 0; i < NCOMMENTS; i++) 1023 if ((ptr = matches_comment(buf, comment_table[i].name))) { 1024 copy_this_line 1025 = (this->*(comment_table[i].proc))(ptr, rank, fp, outfp); 1026 break; 1027 } 1028 if (i >= NCOMMENTS && in_header) { 1029 if ((ptr = matches_comment(buf, "EndComments"))) 1030 in_header = 0; 1031 else if (!had_extensions_comment 1032 && (ptr = matches_comment(buf, "Extensions:"))) { 1033 extensions |= parse_extensions(ptr); 1034 // XXX handle possibility that next line is %%+ 1035 had_extensions_comment = 1; 1036 } 1037 else if (!had_language_level_comment 1038 && (ptr = matches_comment(buf, "LanguageLevel:"))) { 1039 unsigned ll; 1040 if (read_uint_arg(&ptr, &ll) && ll > language_level) 1041 language_level = ll; 1042 had_language_level_comment = 1; 1043 } 1044 else { 1045 for (i = 0; i < NHEADER_COMMENTS; i++) 1046 if (matches_comment(buf, header_comment_table[i])) { 1047 interesting = 1; 1048 break; 1049 } 1050 } 1051 } 1052 if ((broken_flags & STRIP_STRUCTURE_COMMENTS) 1053 && (matches_comment(buf, "EndProlog") 1054 || matches_comment(buf, "Page:") 1055 || matches_comment(buf, "Trailer"))) 1056 copy_this_line = 0; 1057 } 1058 else if (buf[1] == '!') { 1059 if (broken_flags & STRIP_PERCENT_BANG) 1060 copy_this_line = 0; 1061 } 1062 } 1063 else 1064 in_header = 0; 1065 if (!outfp && !in_header && !interesting) 1066 break; 1067 if (copy_this_line && outfp) 1068 fputs(buf.contents(), outfp); 1069 } 1070 } 1071 current_filename = saved_filename; 1072 current_lineno = saved_lineno; 1073} 1074 1075void resource_manager::read_download_file() 1076{ 1077 char *path = 0; 1078 FILE *fp = font::open_file("download", &path); 1079 if (!fp) 1080 fatal("can't find `download'"); 1081 char buf[512]; 1082 int lineno = 0; 1083 while (fgets(buf, sizeof(buf), fp)) { 1084 lineno++; 1085 char *p = strtok(buf, " \t\r\n"); 1086 if (p == 0 || *p == '#') 1087 continue; 1088 char *q = strtok(0, " \t\r\n"); 1089 if (!q) 1090 fatal_with_file_and_line(path, lineno, "missing filename"); 1091 lookup_font(p)->filename = strsave(q); 1092 } 1093 a_delete path; 1094 fclose(fp); 1095} 1096 1097// XXX Can we share some code with ps_output::put_string()? 1098 1099static void print_ps_string(const string &s, FILE *outfp) 1100{ 1101 int len = s.length(); 1102 const char *str = s.contents(); 1103 int funny = 0; 1104 if (str[0] == '(') 1105 funny = 1; 1106 else { 1107 for (int i = 0; i < len; i++) 1108 if (str[i] <= 040 || str[i] > 0176) { 1109 funny = 1; 1110 break; 1111 } 1112 } 1113 if (!funny) { 1114 put_string(s, outfp); 1115 return; 1116 } 1117 int level = 0; 1118 int i; 1119 for (i = 0; i < len; i++) 1120 if (str[i] == '(') 1121 level++; 1122 else if (str[i] == ')' && --level < 0) 1123 break; 1124 putc('(', outfp); 1125 for (i = 0; i < len; i++) 1126 switch (str[i]) { 1127 case '(': 1128 case ')': 1129 if (level != 0) 1130 putc('\\', outfp); 1131 putc(str[i], outfp); 1132 break; 1133 case '\\': 1134 fputs("\\\\", outfp); 1135 break; 1136 case '\n': 1137 fputs("\\n", outfp); 1138 break; 1139 case '\r': 1140 fputs("\\r", outfp); 1141 break; 1142 case '\t': 1143 fputs("\\t", outfp); 1144 break; 1145 case '\b': 1146 fputs("\\b", outfp); 1147 break; 1148 case '\f': 1149 fputs("\\f", outfp); 1150 break; 1151 default: 1152 if (str[i] < 040 || str[i] > 0176) 1153 fprintf(outfp, "\\%03o", str[i] & 0377); 1154 else 1155 putc(str[i], outfp); 1156 break; 1157 } 1158 putc(')', outfp); 1159} 1160 1161void resource_manager::print_extensions_comment(FILE *outfp) 1162{ 1163 if (extensions) { 1164 fputs("%%Extensions:", outfp); 1165 for (int i = 0; i < NEXTENSIONS; i++) 1166 if (extensions & (1 << i)) { 1167 putc(' ', outfp); 1168 fputs(extension_table[i], outfp); 1169 } 1170 putc('\n', outfp); 1171 } 1172} 1173 1174void resource_manager::print_language_level_comment(FILE *outfp) 1175{ 1176 if (language_level) 1177 fprintf(outfp, "%%%%LanguageLevel: %u\n", language_level); 1178} 1179