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