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