Home | History | Annotate | Download | only in postreverse
      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