1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <sys/types.h> 31 #include <sys/file.h> 32 #include <sys/fcntl.h> 33 #include <sys/stat.h> 34 #include <sys/mman.h> 35 #include <string.h> 36 #include <errno.h> 37 #include "postreverse.h" 38 39 /* 40 * This version of postreverse should parse any Adobe DSC conforming 41 * PostScript file and most that are not conforming, but minimally have the 42 * page (%%Page:) and trailer (%%Trailer) comments in them at the begining of 43 * the line. 44 * 45 * If a document cannot be parsed (no page and trailer comments), it is passed 46 * through untouched. If you look through the code you will find that it 47 * doesn't ever look for the PostScript magic (%!). This is because it 48 * assumes that PostScript is sent in. If PostScript is in sent in, it will 49 * still attempt to parse it based on DSC page and trailer comments as if it 50 * were postscript. 51 * 52 * flow goes as follows: 53 * 1) get command line options (including parsing a page 54 * list if supplied) 55 * 2) if no filename is supplied in command line, copy 56 * stdin to temp file. 57 * 3) parse the document: 58 * start from begining looking for a DSC page comment 59 * (that is the header) start from the end looking for 60 * a DSC trailer comment (that is the trailer) start from 61 * the header until the trailer looking for DSC page 62 * comments. Each one signifies a new page. 63 * start from the header until the trailer looking for BSD 64 * global comments. Each one violates page independence and 65 * will be stored so it can be printed after the header and 66 * before any pages. 67 * 4) print the document: if there is no header, trailer, or 68 * pages, print it from start to end unaltered if they all 69 * exist, print the header, pages, and trailer the pages 70 * are compared against a page list before being printed, 71 * and are reversed if the reverse flag has been set. 72 * If global definitions were found in the pages of a 73 * document, they are printed after the header and before 74 * the pages. 75 */ 76 77 static void * 78 nmalloc(size_t size) 79 { 80 void *ret = malloc(size); 81 82 if (!ret) { 83 (void) fprintf(stderr, 84 "postreverse : malloc() failed : Out of memory\n"); 85 exit(2); 86 } 87 return (ret); 88 } 89 90 static void * 91 nrealloc(void *ptr, size_t size) 92 { 93 void *ret = realloc(ptr, size); 94 95 if (!ret) { 96 (void) fprintf(stderr, 97 "postreverse : realloc() failed - Out of memory\n"); 98 exit(2); 99 } 100 return (ret); 101 } 102 103 /* 104 * nstrlen() provides the same functionality as strlen() while also checking 105 * that the pointer does not cross the end of file. 106 * 107 * Returns the number of non-NULL bytes in string argument. 108 */ 109 110 static size_t 111 nstrlen(const char *s, char *bptr) 112 { 113 const char *s0 = s; 114 115 while (s < bptr && *s != '\0') 116 s++; 117 return (s - s0); 118 } 119 120 /* 121 * nstrstr() provides the same functionality as strstr() while also checking 122 * that the pointers do not cross the end of the file. 123 * 124 * nstrstr() locates the first occurrence in the string as1 of the sequence of 125 * characters (excluding the terminating null character) in the string as2. 126 * nstrstr() returns a pointer to the located string, or a null pointer if 127 * the string is not found. If as2 is "", the function returns as1. 128 */ 129 130 static char * 131 nstrstr(const char *as1, const char *as2, char *bptr) 132 { 133 const char *s1, *s2; 134 const char *tptr; 135 char c; 136 137 s1 = as1; 138 s2 = as2; 139 140 if (s2 == NULL || *s2 == '\0') 141 return ((char *)s1); 142 c = *s2; 143 144 while (s1 < bptr && *s1) 145 if (*s1++ == c) { 146 tptr = s1; 147 while ((s1 < bptr) && 148 (c = *++s2) == *s1++ && c); 149 if (c == 0) 150 return ((char *)tptr - 1); 151 s1 = tptr; 152 s2 = as2; 153 c = *s2; 154 } 155 return (NULL); 156 } 157 158 159 /* 160 * caddr_t strrstr(caddr_t as1, caddr_t as2 char *bptr1) 161 * return the address of the beginning of the last occruence of as2 162 * in as1 or NULL if not found 163 */ 164 caddr_t 165 strrstr(caddr_t s1, caddr_t s2, char *bptr) 166 { 167 char *t1, *t2; 168 char c; 169 170 171 t1 = s1 + nstrlen(s1, bptr) - 1; 172 t2 = s2 + nstrlen(s2, bptr) - 1; 173 174 if (t2 == NULL || *t2 == '\0') 175 return ((char *)t1); 176 c = *t2; 177 178 while (s1 <= t1) 179 if (*t1-- == c) { 180 while ((c = *--t2) == *t1-- && t2 > s2); 181 if (t2 <= s2) 182 return ((char *)t1 + 1); 183 t2 = s2 + nstrlen(s2, bptr) - 1; 184 c = *t2; 185 } 186 return (NULL); 187 } 188 189 /* 190 * Copy stdin to a temp file and return the name 191 */ 192 char * 193 StdinToFile() 194 { 195 char *fileName = tmpnam(NULL); 196 int fd; 197 int count; 198 char buf[BUFSIZ]; 199 200 if ((fd = open(fileName, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) { 201 fprintf(stderr, "open(%s): %s\n", fileName, 202 strerror(errno)); 203 return (NULL); 204 } 205 while ((count = read(0, buf, sizeof (buf))) > 0) 206 if (write(fd, buf, count) != count) { 207 fprintf(stderr, "write(%d, 0x%x, %d): %s\n", fd, buf, 208 count, strerror(errno)); 209 close(fd); 210 unlink(fileName); 211 return (NULL); 212 } 213 return (fileName); 214 } 215 216 /* 217 * Usage(char *name) - program usage 218 */ 219 void 220 Usage(char *name) 221 { 222 fprintf(stderr, "Usage: %s [ -o list ] [ -r ] [ filename ]\n", name); 223 exit(1); 224 } 225 226 227 /* 228 * int **ParsePageList(char *list) 229 * This will parse as string #,#,#-#,#... into an array of pointers 230 * to integers. This array will contain all numbers in the list including 231 * those int the range #-#. The list returned is NULL terminated. 232 * It uses 2 passes to build the list. pass 1 counts the # of ints and 233 * allocates the space, and pass 2 fills in the list. 234 */ 235 int ** 236 ParsePageList(char *list) 237 { 238 int **pageList = NULL; 239 int pass = 0; 240 241 if (list == NULL) 242 return (NULL); 243 244 while (pass++ < 2) { 245 char *page; 246 char *tmplist; 247 int size = 0; 248 249 tmplist = strdup(list); 250 page = strtok(tmplist, ","); 251 252 do { 253 int start, end; 254 char *s1 = page, *s2; 255 256 if (s2 = strchr(page, '-')) { 257 *s2++ = NULL; 258 start = atoi(s1); 259 end = atoi(s2); 260 if (end < start) { 261 int tmp = end; 262 263 end = start; 264 start = tmp; 265 } 266 } else 267 start = end = atoi(s1); 268 269 while (start <= end) 270 if (pass == 1) 271 /* count the pages for allocation */ 272 size++, start++; 273 else { /* fill in the page list */ 274 int *tmp = (int *)nmalloc(sizeof (int)); 275 *tmp = start++; 276 pageList[size++] = tmp; 277 } 278 } while (page = strtok(NULL, ",")); 279 free(tmplist); 280 if (pass == 1) 281 pageList = (int **)calloc(sizeof (int *), (size + 1)); 282 } 283 return (pageList); 284 } 285 286 287 /* 288 * int PageIsListed(int page, int **pageList) 289 * returns 1 if the pagelist is empty or if the page is in the 290 * NULL terminated pageList. returns 0 if the page is not listed 291 */ 292 int 293 PageIsListed(int page, int **pageList) 294 { 295 int count = 0; 296 297 if (!pageList) 298 return (1); 299 300 for (count = 0; pageList[count] != NULL; count++) 301 if (*pageList[count] == page) 302 return (1); 303 return (0); 304 } 305 306 307 /* 308 * Writes the document Header to the fd 309 */ 310 int 311 WriteDocumentHeader(int fd, DOCUMENT * d) 312 { 313 if (d) { 314 HEADER *h = d->header; 315 316 if (h) 317 return (write(fd, h->start, h->size)); 318 } 319 errno = EINVAL; 320 return (-1); 321 } 322 323 /* 324 * Writes the document global block to the fd 325 */ 326 int 327 WriteGlobal(int fd, GLOBAL * g) 328 { 329 if (g) 330 return (write(fd, g->start, g->size)); 331 errno = EINVAL; 332 return (-1); 333 } 334 335 /* 336 * Writes the document Trailer to the fd 337 */ 338 int 339 WriteDocumentTrailer(int fd, DOCUMENT * d) 340 { 341 if (d) { 342 TRAILER *t = d->trailer; 343 344 if (t) 345 return (write(fd, t->start, t->size)); 346 } 347 errno = EINVAL; 348 return (-1); 349 } 350 351 /* 352 * Writes the document page to the fd 353 */ 354 int 355 WritePage(int fd, PAGE * p, int global, char *bptr) 356 { 357 if (p) { 358 caddr_t ptr1; 359 360 if (((ptr1 = nstrstr(p->start, PS_BEGIN_GLOBAL, bptr)) 361 != NULL) && (ptr1 < p->start + p->size) && 362 (global != 0)) { 363 /* BeginGlobal/EndGlobal in the page... */ 364 write(fd, p->start, ptr1 - p->start); 365 ptr1 = nstrstr(ptr1, PS_END_GLOBAL, bptr); 366 ptr1 += nstrlen(PS_END_GLOBAL, bptr); 367 return (write(fd, ptr1, (p->size - (ptr1 - p->start)))); 368 } else 369 return (write(fd, p->start, p->size)); 370 } 371 errno = EINVAL; 372 return (-1); 373 } 374 375 /* 376 * Writes out the document pages in pageList (or all if NULL) and reverse 377 * the output if reverse == 1 378 */ 379 void 380 WriteDocument(DOCUMENT * document, int reverse, int **pageList) 381 { 382 int count = 0; 383 int prnindex; 384 385 if (document->header && document->trailer && document->page) { 386 WriteDocumentHeader(1, document); 387 388 if (document->global != NULL) { 389 while (document->global[count] != NULL) { 390 GLOBAL *global = document->global[count++]; 391 392 if (global) 393 WriteGlobal(1, global); 394 } 395 } 396 count = reverse ? (document->pages-1) : 0; 397 398 for (prnindex = 0; prnindex < document->pages; prnindex++) { 399 PAGE *page = document->page[count]; 400 401 if (page && PageIsListed(page->number, pageList)) 402 WritePage(1, page, document->global != NULL, 403 document->start + document->size); 404 405 count = reverse ? count - 1 : count + 1; 406 } 407 408 WriteDocumentTrailer(1, document); 409 } else { 410 write(1, document->start, document->size); 411 } 412 } 413 414 /* 415 * get a document header from document and return a pointer to a HEADER 416 * structure. 417 */ 418 HEADER * 419 DocumentHeader(DOCUMENT * document) 420 { 421 HEADER *header; 422 caddr_t start; 423 424 header = (HEADER *) nmalloc(sizeof (*header)); 425 memset(header, 0, sizeof (*header)); 426 if (start = nstrstr(document->start, PS_PAGE, 427 document->start + document->size)) { 428 header->label = "Document Header"; 429 header->start = document->start; 430 header->size = (start - document->start + 1); 431 } else { 432 free(header); 433 header = NULL; 434 } 435 return (header); 436 } 437 438 439 /* 440 * get a document trailer from document and return a pointer to a trailer 441 * structure. 442 */ 443 TRAILER * 444 DocumentTrailer(DOCUMENT * document) 445 { 446 TRAILER *trailer; 447 448 trailer = (TRAILER *) nmalloc(sizeof (*trailer)); 449 memset(trailer, 0, sizeof (trailer)); 450 if (trailer->start = strrstr(document->start, PS_TRAILER, 451 document->start + document->size)) { 452 trailer->label = "Document Trailer"; 453 trailer->start += 1; 454 trailer->size = nstrlen(trailer->start, 455 document->start + document->size); 456 } else { 457 free(trailer); 458 trailer = NULL; 459 } 460 return (trailer); 461 } 462 463 GLOBAL ** 464 DocumentGlobals(DOCUMENT * document) 465 { 466 GLOBAL **globals = NULL, *global; 467 caddr_t start, ptr1; 468 int count = 0; 469 char *bptr = document->start + document->size; 470 long allocated_slots = 0; 471 caddr_t global_end; 472 473 start = nstrstr(document->start, PS_PAGE, bptr); 474 if (start != NULL) { 475 for (ptr1 = nstrstr(start, PS_BEGIN_GLOBAL, bptr); ptr1 != NULL; 476 ptr1 = nstrstr(++ptr1, PS_BEGIN_GLOBAL, bptr)) { 477 count++; 478 479 global = (GLOBAL *) nmalloc(sizeof (GLOBAL)); 480 if ((global_end = nstrstr(++ptr1, PS_END_GLOBAL, bptr)) 481 == NULL) { 482 fprintf(stderr, 483 "DSC violation: %%%%BeginGlobal " 484 "with no %%%%EndGlobal\n"); 485 exit(-1); 486 } 487 memset(global, 0, sizeof (GLOBAL)); 488 global->start = ptr1; 489 global->size = strchr(++global_end, '\n') - ptr1 + 1; 490 491 if (count > allocated_slots) { 492 globals = (GLOBAL **) nrealloc(globals, 493 (allocated_slots + BLOCKSIZE) * 494 sizeof (GLOBAL *)); 495 memset(globals + 496 allocated_slots * sizeof (GLOBAL *), 0, 497 BLOCKSIZE * 498 sizeof (GLOBAL *)); 499 allocated_slots += BLOCKSIZE; 500 } 501 502 globals[count - 1] = global; 503 ptr1 = global->start + global->size; 504 } 505 } 506 return (globals); 507 } 508 509 510 /* 511 * get the pages from a document and return a pointer a list of PAGE 512 * structures. 513 */ 514 PAGE ** 515 DocumentPages(DOCUMENT * document) 516 { 517 PAGE **pages = NULL, *page; 518 caddr_t ptr1, page_end; 519 char *bptr = document->start + document->size; 520 long allocated_slots = 0; 521 long no_pages = 0; 522 long number; 523 char *label, *tmp, *tmp_end; 524 525 for (ptr1 = nstrstr(document->start, PS_PAGE, bptr); ptr1 != NULL; 526 ptr1 = nstrstr(++ptr1, PS_PAGE, bptr)) { 527 no_pages++; 528 529 if (no_pages > allocated_slots) { 530 pages = (PAGE **) nrealloc(pages, 531 (allocated_slots + BLOCKSIZE) * sizeof (PAGE *)); 532 memset(pages + allocated_slots, 0, 533 BLOCKSIZE * sizeof (PAGE *)); 534 allocated_slots += BLOCKSIZE; 535 } 536 page = (PAGE *) nmalloc(sizeof (PAGE)); 537 label = NULL; 538 number = -1; 539 540 /* page start & end */ 541 if ((page_end = nstrstr(++ptr1, PS_PAGE, bptr)) == NULL) 542 if (document->trailer) 543 page_end = document->trailer->start - 1; 544 else 545 page_end = document->start + document->size; 546 547 /* page label & number */ 548 if (tmp = strchr(ptr1, ' ')) { 549 550 if (tmp_end = strchr(++tmp, ' ')) { 551 label = (char *)nmalloc((tmp_end - tmp) + 1); 552 memset(label, 0, (tmp_end - tmp) + 1); 553 strncpy(label, tmp, (tmp_end - tmp)); 554 number = atol(++tmp_end); 555 } 556 } 557 memset(page, 0, sizeof (PAGE)); 558 page->label = label; 559 page->number = number; 560 page->start = ptr1; 561 page->size = page_end - ptr1 + 1; 562 563 pages[document->pages++] = page; 564 } 565 return (pages); 566 } 567 568 /* 569 * parse a document and return a pointer to a DOCUMENT structure 570 */ 571 DOCUMENT * 572 DocumentParse(char *name) 573 { 574 DOCUMENT *document = NULL; 575 int fd; 576 struct stat st; 577 578 if (stat(name, &st) < 0) { 579 fprintf(stderr, "stat(%s): %s\n", name, strerror(errno)); 580 return (NULL); 581 } 582 if (st.st_size == 0) { 583 fprintf(stderr, "%s: empty file\n", name); 584 return (NULL); 585 } 586 if ((fd = open(name, O_RDONLY)) < 0) { 587 fprintf(stderr, "open(%s, O_RDONLY): %s\n", name, 588 strerror(errno)); 589 return (NULL); 590 } 591 document = (DOCUMENT *) nmalloc(sizeof (DOCUMENT)); 592 memset(document, 0, sizeof (DOCUMENT)); 593 if ((document->start = mmap((void *)0, (size_t)st.st_size, PROT_READ, 594 MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { 595 fprintf(stderr, "mmap(0, %ld, PROT_READ," 596 " MAP_SHARED, %d, 0): %s\n", 597 st.st_size, fd, strerror(errno)); 598 free(document); 599 document = NULL; 600 } else { 601 /* order in important */ 602 document->name = strdup(name); 603 document->size = nstrlen(document->start, 604 document->start + st.st_size); 605 document->header = DocumentHeader(document); 606 document->trailer = DocumentTrailer(document); 607 document->page = DocumentPages(document); 608 document->global = DocumentGlobals(document); 609 } 610 close(fd); 611 return (document); 612 } 613 614 615 #if defined(DEBUG) 616 /* 617 * Print out the contents of the document structure 618 */ 619 void 620 PrintDocumentInfo(DOCUMENT * d) 621 { 622 if (d) { 623 printf("Document:\n\tname: %s\n\tstart: 0x%x\n\tsize: %ld\n", 624 d->name, d->start, d->size); 625 if (d->header) { 626 HEADER *h = d->header; 627 628 printf("\tHeader: %s (0x%x, %ld)\n", 629 h->label, h->start, h->size); 630 } 631 if (d->global) { 632 int count = 0; 633 634 while (d->global[count++] != NULL); 635 printf("\tDSC violating BeginGlobals: %d\n", count); 636 } 637 if (d->page) { 638 PAGE *p; 639 int count = 0; 640 641 printf("\tPages: (%d)\n", d->pages); 642 for (p = d->page[0]; p != NULL; p = d->page[++count]) 643 printf("\t\t %4d (%s) - (0x%x, %ld)\n", 644 p->number, 645 (p->label ? p->label : "Page"), 646 p->start, p->size); 647 } 648 if (d->trailer) { 649 TRAILER *t = d->trailer; 650 651 printf("\tTrailer: %s (0x%x, %ld)\n", 652 t->label, t->start, t->size); 653 } 654 } 655 } 656 #endif /* DEBUG */ 657 658 659 int 660 main(int ac, char *av[]) 661 { 662 DOCUMENT *document; 663 char *fileName = NULL; 664 char *programName = NULL; 665 char *unlinkFile = NULL; 666 int reversePages = 1; 667 int **pageList = NULL; 668 int option; 669 670 if (programName = strrchr(av[0], '/')) 671 programName++; 672 else 673 programName = av[0]; 674 675 while ((option = getopt(ac, av, "o:r")) != EOF) 676 switch (option) { 677 case 'o': 678 pageList = ParsePageList(optarg); 679 break; 680 case 'r': 681 reversePages = 0; 682 break; 683 case '?': 684 Usage(programName); 685 break; 686 default: 687 fprintf(stderr, "missing case for option %c\n", option); 688 Usage(programName); 689 break; 690 } 691 692 ac -= optind; 693 av += optind; 694 695 switch (ac) { 696 case 0: 697 unlinkFile = fileName = StdinToFile(); 698 break; 699 case 1: 700 fileName = av[0]; 701 break; 702 default: 703 Usage(programName); 704 } 705 706 if ((document = DocumentParse(fileName)) == NULL) { 707 fprintf(stderr, "Unable to parse document (%s)\n", fileName); 708 exit(0); 709 } 710 #if defined(DEBUG) && defined(NOTDEF) 711 PrintDocumentInfo(document); 712 #endif /* DEBUG */ 713 714 WriteDocument(document, reversePages, pageList); 715 716 if (unlinkFile) 717 unlink(unlinkFile); 718 719 return (0); 720 } 721