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 *
nmalloc(size_t size)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 *
nrealloc(void * ptr,size_t size)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
nstrlen(const char * s,char * bptr)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 *
nstrstr(const char * as1,const char * as2,char * bptr)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
strrstr(caddr_t s1,caddr_t s2,char * bptr)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 *
StdinToFile()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
Usage(char * name)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 **
ParsePageList(char * list)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
PageIsListed(int page,int ** pageList)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
WriteDocumentHeader(int fd,DOCUMENT * d)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
WriteGlobal(int fd,GLOBAL * g)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
WriteDocumentTrailer(int fd,DOCUMENT * d)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
WritePage(int fd,PAGE * p,int global,char * bptr)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
WriteDocument(DOCUMENT * document,int reverse,int ** pageList)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 *
DocumentHeader(DOCUMENT * document)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 *
DocumentTrailer(DOCUMENT * document)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 **
DocumentGlobals(DOCUMENT * document)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 **
DocumentPages(DOCUMENT * document)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 *
DocumentParse(char * name)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
PrintDocumentInfo(DOCUMENT * d)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
main(int ac,char * av[])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