postreverse.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include "postreverse.h"
/*
* This version of postreverse should parse any Adobe DSC conforming
* PostScript file and most that are not conforming, but minimally have the
* page (%%Page:) and trailer (%%Trailer) comments in them at the begining of
* the line.
*
* If a document cannot be parsed (no page and trailer comments), it is passed
* through untouched. If you look through the code you will find that it
* doesn't ever look for the PostScript magic (%!). This is because it
* assumes that PostScript is sent in. If PostScript is in sent in, it will
* still attempt to parse it based on DSC page and trailer comments as if it
* were postscript.
*
* flow goes as follows:
* 1) get command line options (including parsing a page
* list if supplied)
* 2) if no filename is supplied in command line, copy
* stdin to temp file.
* 3) parse the document:
* start from begining looking for a DSC page comment
* (that is the header) start from the end looking for
* a DSC trailer comment (that is the trailer) start from
* the header until the trailer looking for DSC page
* comments. Each one signifies a new page.
* start from the header until the trailer looking for BSD
* global comments. Each one violates page independence and
* will be stored so it can be printed after the header and
* before any pages.
* 4) print the document: if there is no header, trailer, or
* pages, print it from start to end unaltered if they all
* exist, print the header, pages, and trailer the pages
* are compared against a page list before being printed,
* and are reversed if the reverse flag has been set.
* If global definitions were found in the pages of a
* document, they are printed after the header and before
* the pages.
*/
static void *
nmalloc(size_t size)
{
void *ret = malloc(size);
if (!ret) {
(void) fprintf(stderr,
"postreverse : malloc() failed : Out of memory\n");
exit(2);
}
return (ret);
}
static void *
nrealloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (!ret) {
(void) fprintf(stderr,
"postreverse : realloc() failed - Out of memory\n");
exit(2);
}
return (ret);
}
/*
* nstrlen() provides the same functionality as strlen() while also checking
* that the pointer does not cross the end of file.
*
* Returns the number of non-NULL bytes in string argument.
*/
static size_t
nstrlen(const char *s, char *bptr)
{
const char *s0 = s;
while (s < bptr && *s != '\0')
s++;
return (s - s0);
}
/*
* nstrstr() provides the same functionality as strstr() while also checking
* that the pointers do not cross the end of the file.
*
* nstrstr() locates the first occurrence in the string as1 of the sequence of
* characters (excluding the terminating null character) in the string as2.
* nstrstr() returns a pointer to the located string, or a null pointer if
* the string is not found. If as2 is "", the function returns as1.
*/
static char *
nstrstr(const char *as1, const char *as2, char *bptr)
{
const char *s1, *s2;
const char *tptr;
char c;
s1 = as1;
s2 = as2;
if (s2 == NULL || *s2 == '\0')
return ((char *)s1);
c = *s2;
while (s1 < bptr && *s1)
if (*s1++ == c) {
tptr = s1;
while ((s1 < bptr) &&
(c = *++s2) == *s1++ && c);
if (c == 0)
return ((char *)tptr - 1);
s1 = tptr;
s2 = as2;
c = *s2;
}
return (NULL);
}
/*
* caddr_t strrstr(caddr_t as1, caddr_t as2 char *bptr1)
* return the address of the beginning of the last occruence of as2
* in as1 or NULL if not found
*/
caddr_t
strrstr(caddr_t s1, caddr_t s2, char *bptr)
{
char *t1, *t2;
char c;
t1 = s1 + nstrlen(s1, bptr) - 1;
t2 = s2 + nstrlen(s2, bptr) - 1;
if (t2 == NULL || *t2 == '\0')
return ((char *)t1);
c = *t2;
while (s1 <= t1)
if (*t1-- == c) {
while ((c = *--t2) == *t1-- && t2 > s2);
if (t2 <= s2)
return ((char *)t1 + 1);
t2 = s2 + nstrlen(s2, bptr) - 1;
c = *t2;
}
return (NULL);
}
/*
* Copy stdin to a temp file and return the name
*/
char *
StdinToFile()
{
char *fileName = tmpnam(NULL);
int fd;
int count;
char buf[BUFSIZ];
if ((fd = open(fileName, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) {
fprintf(stderr, "open(%s): %s\n", fileName,
strerror(errno));
return (NULL);
}
while ((count = read(0, buf, sizeof (buf))) > 0)
if (write(fd, buf, count) != count) {
fprintf(stderr, "write(%d, 0x%x, %d): %s\n", fd, buf,
count, strerror(errno));
close(fd);
unlink(fileName);
return (NULL);
}
return (fileName);
}
/*
* Usage(char *name) - program usage
*/
void
Usage(char *name)
{
fprintf(stderr, "Usage: %s [ -o list ] [ -r ] [ filename ]\n", name);
exit(1);
}
/*
* int **ParsePageList(char *list)
* This will parse as string #,#,#-#,#... into an array of pointers
* to integers. This array will contain all numbers in the list including
* those int the range #-#. The list returned is NULL terminated.
* It uses 2 passes to build the list. pass 1 counts the # of ints and
* allocates the space, and pass 2 fills in the list.
*/
int **
ParsePageList(char *list)
{
int **pageList = NULL;
int pass = 0;
if (list == NULL)
return (NULL);
while (pass++ < 2) {
char *page;
char *tmplist;
int size = 0;
tmplist = strdup(list);
page = strtok(tmplist, ",");
do {
int start, end;
char *s1 = page, *s2;
if (s2 = strchr(page, '-')) {
*s2++ = NULL;
start = atoi(s1);
end = atoi(s2);
if (end < start) {
int tmp = end;
end = start;
start = tmp;
}
} else
start = end = atoi(s1);
while (start <= end)
if (pass == 1)
/* count the pages for allocation */
size++, start++;
else { /* fill in the page list */
int *tmp = (int *)nmalloc(sizeof (int));
*tmp = start++;
pageList[size++] = tmp;
}
} while (page = strtok(NULL, ","));
free(tmplist);
if (pass == 1)
pageList = (int **)calloc(sizeof (int *), (size + 1));
}
return (pageList);
}
/*
* int PageIsListed(int page, int **pageList)
* returns 1 if the pagelist is empty or if the page is in the
* NULL terminated pageList. returns 0 if the page is not listed
*/
int
PageIsListed(int page, int **pageList)
{
int count = 0;
if (!pageList)
return (1);
for (count = 0; pageList[count] != NULL; count++)
if (*pageList[count] == page)
return (1);
return (0);
}
/*
* Writes the document Header to the fd
*/
int
WriteDocumentHeader(int fd, DOCUMENT * d)
{
if (d) {
HEADER *h = d->header;
if (h)
return (write(fd, h->start, h->size));
}
errno = EINVAL;
return (-1);
}
/*
* Writes the document global block to the fd
*/
int
WriteGlobal(int fd, GLOBAL * g)
{
if (g)
return (write(fd, g->start, g->size));
errno = EINVAL;
return (-1);
}
/*
* Writes the document Trailer to the fd
*/
int
WriteDocumentTrailer(int fd, DOCUMENT * d)
{
if (d) {
TRAILER *t = d->trailer;
if (t)
return (write(fd, t->start, t->size));
}
errno = EINVAL;
return (-1);
}
/*
* Writes the document page to the fd
*/
int
WritePage(int fd, PAGE * p, int global, char *bptr)
{
if (p) {
caddr_t ptr1;
if (((ptr1 = nstrstr(p->start, PS_BEGIN_GLOBAL, bptr))
!= NULL) && (ptr1 < p->start + p->size) &&
(global != 0)) {
/* BeginGlobal/EndGlobal in the page... */
write(fd, p->start, ptr1 - p->start);
ptr1 = nstrstr(ptr1, PS_END_GLOBAL, bptr);
ptr1 += nstrlen(PS_END_GLOBAL, bptr);
return (write(fd, ptr1, (p->size - (ptr1 - p->start))));
} else
return (write(fd, p->start, p->size));
}
errno = EINVAL;
return (-1);
}
/*
* Writes out the document pages in pageList (or all if NULL) and reverse
* the output if reverse == 1
*/
void
WriteDocument(DOCUMENT * document, int reverse, int **pageList)
{
int count = 0;
int prnindex;
if (document->header && document->trailer && document->page) {
WriteDocumentHeader(1, document);
if (document->global != NULL) {
while (document->global[count] != NULL) {
GLOBAL *global = document->global[count++];
if (global)
WriteGlobal(1, global);
}
}
count = reverse ? (document->pages-1) : 0;
for (prnindex = 0; prnindex < document->pages; prnindex++) {
PAGE *page = document->page[count];
if (page && PageIsListed(page->number, pageList))
WritePage(1, page, document->global != NULL,
document->start + document->size);
count = reverse ? count - 1 : count + 1;
}
WriteDocumentTrailer(1, document);
} else {
write(1, document->start, document->size);
}
}
/*
* get a document header from document and return a pointer to a HEADER
* structure.
*/
HEADER *
DocumentHeader(DOCUMENT * document)
{
HEADER *header;
caddr_t start;
header = (HEADER *) nmalloc(sizeof (*header));
memset(header, 0, sizeof (*header));
if (start = nstrstr(document->start, PS_PAGE,
document->start + document->size)) {
header->label = "Document Header";
header->start = document->start;
header->size = (start - document->start + 1);
} else {
free(header);
header = NULL;
}
return (header);
}
/*
* get a document trailer from document and return a pointer to a trailer
* structure.
*/
TRAILER *
DocumentTrailer(DOCUMENT * document)
{
TRAILER *trailer;
trailer = (TRAILER *) nmalloc(sizeof (*trailer));
memset(trailer, 0, sizeof (trailer));
if (trailer->start = strrstr(document->start, PS_TRAILER,
document->start + document->size)) {
trailer->label = "Document Trailer";
trailer->start += 1;
trailer->size = nstrlen(trailer->start,
document->start + document->size);
} else {
free(trailer);
trailer = NULL;
}
return (trailer);
}
GLOBAL **
DocumentGlobals(DOCUMENT * document)
{
GLOBAL **globals = NULL, *global;
caddr_t start, ptr1;
int count = 0;
char *bptr = document->start + document->size;
long allocated_slots = 0;
caddr_t global_end;
start = nstrstr(document->start, PS_PAGE, bptr);
if (start != NULL) {
for (ptr1 = nstrstr(start, PS_BEGIN_GLOBAL, bptr); ptr1 != NULL;
ptr1 = nstrstr(++ptr1, PS_BEGIN_GLOBAL, bptr)) {
count++;
global = (GLOBAL *) nmalloc(sizeof (GLOBAL));
if ((global_end = nstrstr(++ptr1, PS_END_GLOBAL, bptr))
== NULL) {
fprintf(stderr,
"DSC violation: %%%%BeginGlobal "
"with no %%%%EndGlobal\n");
exit(-1);
}
memset(global, 0, sizeof (GLOBAL));
global->start = ptr1;
global->size = strchr(++global_end, '\n') - ptr1 + 1;
if (count > allocated_slots) {
globals = (GLOBAL **) nrealloc(globals,
(allocated_slots + BLOCKSIZE) *
sizeof (GLOBAL *));
memset(globals +
allocated_slots * sizeof (GLOBAL *), 0,
BLOCKSIZE *
sizeof (GLOBAL *));
allocated_slots += BLOCKSIZE;
}
globals[count - 1] = global;
ptr1 = global->start + global->size;
}
}
return (globals);
}
/*
* get the pages from a document and return a pointer a list of PAGE
* structures.
*/
PAGE **
DocumentPages(DOCUMENT * document)
{
PAGE **pages = NULL, *page;
caddr_t ptr1, page_end;
char *bptr = document->start + document->size;
long allocated_slots = 0;
long no_pages = 0;
long number;
char *label, *tmp, *tmp_end;
for (ptr1 = nstrstr(document->start, PS_PAGE, bptr); ptr1 != NULL;
ptr1 = nstrstr(++ptr1, PS_PAGE, bptr)) {
no_pages++;
if (no_pages > allocated_slots) {
pages = (PAGE **) nrealloc(pages,
(allocated_slots + BLOCKSIZE) * sizeof (PAGE *));
memset(pages + allocated_slots * sizeof (PAGE *), 0,
BLOCKSIZE * sizeof (PAGE *));
allocated_slots += BLOCKSIZE;
}
page = (PAGE *) nmalloc(sizeof (PAGE));
label = NULL;
number = -1;
/* page start & end */
if ((page_end = nstrstr(++ptr1, PS_PAGE, bptr)) == NULL)
if (document->trailer)
page_end = document->trailer->start - 1;
else
page_end = document->start + document->size;
/* page label & number */
if (tmp = strchr(ptr1, ' ')) {
if (tmp_end = strchr(++tmp, ' ')) {
label = (char *)nmalloc((tmp_end - tmp) + 1);
memset(label, 0, (tmp_end - tmp) + 1);
strncpy(label, tmp, (tmp_end - tmp));
number = atol(++tmp_end);
}
}
memset(page, 0, sizeof (PAGE));
page->label = label;
page->number = number;
page->start = ptr1;
page->size = page_end - ptr1 + 1;
pages[document->pages++] = page;
}
return (pages);
}
/*
* parse a document and return a pointer to a DOCUMENT structure
*/
DOCUMENT *
DocumentParse(char *name)
{
DOCUMENT *document = NULL;
int fd;
struct stat st;
if (stat(name, &st) < 0) {
fprintf(stderr, "stat(%s): %s\n", name, strerror(errno));
return (NULL);
}
if (st.st_size == 0) {
fprintf(stderr, "%s: empty file\n", name);
return (NULL);
}
if ((fd = open(name, O_RDONLY)) < 0) {
fprintf(stderr, "open(%s, O_RDONLY): %s\n", name,
strerror(errno));
return (NULL);
}
document = (DOCUMENT *) nmalloc(sizeof (DOCUMENT));
memset(document, 0, sizeof (DOCUMENT));
if ((document->start = mmap((void *)0, (size_t)st.st_size, PROT_READ,
MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
fprintf(stderr, "mmap(0, %ld, PROT_READ,"
" MAP_SHARED, %d, 0): %s\n",
st.st_size, fd, strerror(errno));
free(document);
document = NULL;
} else {
/* order in important */
document->name = strdup(name);
document->size = nstrlen(document->start,
document->start + st.st_size);
document->header = DocumentHeader(document);
document->trailer = DocumentTrailer(document);
document->page = DocumentPages(document);
document->global = DocumentGlobals(document);
}
close(fd);
return (document);
}
#if defined(DEBUG)
/*
* Print out the contents of the document structure
*/
void
PrintDocumentInfo(DOCUMENT * d)
{
if (d) {
printf("Document:\n\tname: %s\n\tstart: 0x%x\n\tsize: %ld\n",
d->name, d->start, d->size);
if (d->header) {
HEADER *h = d->header;
printf("\tHeader: %s (0x%x, %ld)\n",
h->label, h->start, h->size);
}
if (d->global) {
int count = 0;
while (d->global[count++] != NULL);
printf("\tDSC violating BeginGlobals: %d\n", count);
}
if (d->page) {
PAGE *p;
int count = 0;
printf("\tPages: (%d)\n", d->pages);
for (p = d->page[0]; p != NULL; p = d->page[++count])
printf("\t\t %4d (%s) - (0x%x, %ld)\n",
p->number,
(p->label ? p->label : "Page"),
p->start, p->size);
}
if (d->trailer) {
TRAILER *t = d->trailer;
printf("\tTrailer: %s (0x%x, %ld)\n",
t->label, t->start, t->size);
}
}
}
#endif /* DEBUG */
main(int ac, char *av[])
{
DOCUMENT *document;
char *fileName = NULL;
char *programName = NULL;
char *unlinkFile = NULL;
int reversePages = 1;
int **pageList = NULL;
int option;
if (programName = strrchr(av[0], '/'))
programName++;
else
programName = av[0];
while ((option = getopt(ac, av, "o:r")) != EOF)
switch (option) {
case 'o':
pageList = ParsePageList(optarg);
break;
case 'r':
reversePages = 0;
break;
case '?':
Usage(programName);
break;
default:
fprintf(stderr, "missing case for option %c\n", option);
Usage(programName);
break;
}
ac -= optind;
av += optind;
switch (ac) {
case 0:
unlinkFile = fileName = StdinToFile();
break;
case 1:
fileName = av[0];
break;
default:
Usage(programName);
}
if ((document = DocumentParse(fileName)) == NULL) {
fprintf(stderr, "Unable to parse document (%s)\n", fileName);
exit(0);
}
#if defined(DEBUG) && defined(NOTDEF)
PrintDocumentInfo(document);
#endif /* DEBUG */
WriteDocument(document, reversePages, pageList);
if (unlinkFile)
unlink(unlinkFile);
exit(0);
}