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