[mod.sources] v06i042: Page reverser for ditroff

sources-request@mirror.UUCP (07/09/86)

Submitted by: cca!caip!uw-beaver!uw-wally!schwartz (Michael F. Schwartz)
Mod.sources: Volume 6, Issue 42
Archive-name: ditrev

Documents generated by ditroff(1) come out with page 1 on the bottom and the
last page on top when you print them.  I got tired of having to manually
reverse the pages, so I hacked up a filter that reverses the pages for you.
It's simple to use, and runs pretty fast:

	ditrev [file]

outputs to stdout an image like the input, except all the pages are reversed.
So, to use this, use something like the following command:

       troff inputfile -t | ditrev | lpr -n

(where the /usr/local/ditroff is before the /bin/troff version in your search
path).

Note: There is another program called psrev that reverses PostScript file pages
(my filter reverses ditroff pages; ditroff output is translated to PostScript
before being printed on laserwriters).  More about the differences between
these programs is described in the man page.

 - Mike Schwartz
   University of Washington Computer Science Department
   ihnp4!uw-beaver!schwartz  (USENET)
   schwartz@wally.arpa  (ARPANET)
   schwartz%wally.arpa@csnet-relay.arpa  (CSNET)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	ditrev.1
#	ditrev.c
#	makefile
# This archive created: Fri Jun 27 15:47:59 1986
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'ditrev.1'" '(2762 characters)'
if test -f 'ditrev.1'
then
	echo shar: "will not over-write existing file 'ditrev.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'ditrev.1'
	X.TH DITREV 1 local
	X.SH NAME
	Xditrev \- reverse ditroff(1) pages
	X.SH SYNOPSIS
	X.B ditrev
	X[file]
	X.SH DESCRIPTION
	X.I Ditrev
	Xreverses the pages in a ditroff
	Xfile (or stdin, if no file is
	Xspecified) and outputs to stdout. This is useful when sending typeset
	Xditroff output to a laser printer:
	Xditroff outputs the pages in an order that, without this filter, forces
	Xyou to manually reverse them all.
	X.PP
	XTo use this filter, you would use something like the following command:
	X.RS
	Xtroff file -t -Pps | ditrev | lpr -n -Pps
	X.RE
	X(where /usr/local/ditroff is ahead of /usr/bin in your search path).
	X.SH COMPARISON WITH PSREV
	XThere is another program called psrev
	Xthat reverses the pages of PostScript
	Xfiles.  Since many laser printers (e.g., the MacIntosh LaserWriter) use
	XPostScript, one could do
	X.RS
	Xtroff file -t -Pps | dps | psrev | lpr -Pps
	X.RE
	X(dps translates between ditroff format and PostScript).  Although I didn't know
	Xof the existence of psrev when I wrote
	X.I ditrev,
	Xit turns out that there are several advantages of
	X.I ditrev
	Xover psrev:
	X.IP 1.
	X.I Ditrev
	Xis more general - it works no matter what image format the printer accepts.
	XThus,
	X.I ditrev
	Xcan be used for any printer, and the user doesn't have to know what format
	Xthe printer expects.  
	X.IP
	XOn the other hand, psrev works no matter what
	Xtext formatter generated the file, as long as the printer accepts
	XPostScript.  But, since most text processors (Scribe and tex(1L) in
	Xparticular) output the pages in the correct order, this probably
	Xisn't too useful.
	X.IP 2.
	X.I Ditrev
	Xruns faster than psrev.
	X.IP 3.
	XNeither dps nor psrev seem to have man pages.
	X.IP 4.
	XDps is often only available on the machine
	Xwhich drives the printer, so you would actually have to do
	X.RS
	X.RS
	Xtroff file -t -Pps | rsh 'dps | psrev | lpr -Pps'
	X.RE
	XThis can become more complicated and installation dependent
	Xif these other filters aren't
	Xin your search path on the remote machine.
	X.RE
	X.SH AUTHOR
	XMike Schwartz, University of Washington Computer Science Department.
	X.ce 3
	Xihnp4!uw-beaver!schwartz  (USENET)
	Xschwartz@wally.arpa  (ARPANET)
	Xschwartz%wally.arpa@csnet-relay.arpa  (CSNET)
	X.SH BUGS
	X.IP 1.
	XThe transformation performed is very simple; I discovered it by looking at the
	Xoutput of ditroff and the source of dsun(1).  Thus, although this filter
	Xseems to work, I may have made some oversimplifications.
	X.IP 2.
	XInput is read into dynamically allocated buffers, and hence, for a large input
	Xfile, alot of memory is used (but no more than necessary, if compiled with
	X-DREALLOC).  This could have been avoided by disallowing input from stdin and
	Xusing fseek(3) to move through the file. I didn't do this because I wanted to
	Xallow input to come from stdin.
	X.SH SEE ALSO
	Xtroff(1), ditroff(1), dsun(1), lpr(1), tex(1L), printcap(5).
SHAR_EOF
if test 2762 -ne "`wc -c < 'ditrev.1'`"
then
	echo shar: "error transmitting 'ditrev.1'" '(should have been 2762 characters)'
fi
fi
echo shar: "extracting 'ditrev.c'" '(5198 characters)'
if test -f 'ditrev.c'
then
	echo shar: "will not over-write existing file 'ditrev.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'ditrev.c'
	X/* Program to reverse pages from input and print on stdout, for ditroff(1)
	X * input.  This is useful when sending typeset ditroff output to a laser
	X * printer:  ditroff outputs the pages in an order that, without this filter,
	X * forces you to manually reverse them all.
	X *
	X * The transformation is simple: Below is some sample input. The X's below
	X * represent any character other than "p"; "p" delimits pages, and "x trailer"
	X * delimits the end of the last page.  Also, all lines start in column 1.
	X *	Xstartingstuff
	X *	p1
	X *	Xpage1stuff
	X *	p2
	X *	Xpage2stuff
	X *	x trailer
	X *	Xfinalstuff
	X *
	X * This input, when run through this filter, will produce the following output:
	X *	Xstartingstuff
	X *	p2
	X *	Xpage2stuff
	X *	p1
	X *	Xpage1stuff
	X *	x trailer
	X *	Xfinalstuff
	X *
	X * By Mike Schwartz, 6-24-86.
	X *	ihnp4!uw-beaver!schwartz  (USENET)
	X *	schwartz@wally.arpa  (ARPANET)
	X *	schwartz%wally.arpa@csnet-relay.arpa  (CSNET)
	X */
	X
	X
	X#include <stdio.h>
	X
	X#define PAGEBUFSIZE 30000    /* Number of bytes initially allocated for each
	X				postscript page.  I empirically determined that
	X				this is plenty more than ever seems to be
	X				needed (max was about 18k).  If compiled with
	X				-DREALLOC, there is no waste involved, since
	X				the buffers are realloc'd after their size is
	X				determined.  There seems to be similar time and
	X				CPU usage characteristics with or without
	X				-DREALLOC, but the space used is considerably
	X				less with -DREALLOC: a 320k input file caused
	X				the dynamic size to grow to about 500k with
	X				-DREALLOC, and to about 1200k without it.  */
	X
	X#define MAXPAGES 500         /* Max number of pages in the document */
	X
	X#define PageDelim(Char) (Char == 'p')
	X#define EndOfAllPagesDelimString "x trailer"
	X#define EndOfAllPages(Line) (strncmp(Line, EndOfAllPagesDelimString, 9) == 0)
	X
	Xint PageIndex = 0;
	X
	Xmain(argc, argv)
	Xint argc;
	Xchar **argv;
	X{
	X	FILE *InputFile = stdin;
	X	char *InputBufPtr;
	X	int CharIndex = 0;
	X	char *PageBuf[MAXPAGES];
	X	char *fgets(), *malloc(), *realloc();
	X
	X	if (argc > 2) {
	X		fputs("Usage: ditrev [file]\n", stderr);
	X		exit(1);
	X	}
	X
	X	if (argc == 2) {
	X		InputFile = fopen(argv[1], "r");
	X		if (InputFile == NULL) {
	X			perror("ditrev: fopen");
	X			exit(1);
	X		}
	X	}
	X
	X	PageBuf[PageIndex] = malloc(PAGEBUFSIZE);
	X	if (PageBuf[PageIndex] == NULL)
	X		Abort("malloc returned no storage");
	X
	X	/* Copy preliminary stuff (before first page) from input to output */
	X	do {
	X		InputBufPtr = fgets(&PageBuf[PageIndex][CharIndex],
	X				    PAGEBUFSIZE, InputFile);
	X		if (InputBufPtr == NULL)  /* EOF */
	X			Abort("Invalid input format");
	X		if (PageDelim(*InputBufPtr))
	X			break;
	X		fputs(InputBufPtr, stdout);
	X	} while(1);
	X
	X
	X	/* Now read and store away all pages until hit EndOfAllPages */
	X	do {
	X		InputBufPtr = fgets(&PageBuf[PageIndex][CharIndex],
	X				    PAGEBUFSIZE, InputFile);
	X		if (InputBufPtr == NULL)  /* EOF */
	X			Abort("Invalid input format");
	X		if (EndOfAllPages(InputBufPtr)) {
	X			/* Put a page delimeter into the end of this page so
	X			   we can find the end when we are printing everything
	X			   out */
	X			*InputBufPtr = 'p';
	X			break;
	X		}
	X		CharIndex += strlen(InputBufPtr) + 1;
	X		/* +1 so we don't write over the null terminator */
	X		if (CharIndex >= PAGEBUFSIZE) {
	X			Abort("Page size being malloc'd is too small (recompile with larger PAGEBUFSIZE)");
	X		}
	X		if (PageDelim(*InputBufPtr)) {
	X#ifdef REALLOC
	X			/* Now that we know how big this page is, realloc the
	X			   old page buffer so we don't waste unused space */
	X			PageBuf[PageIndex] = realloc(PageBuf[PageIndex],
	X						     CharIndex);
	X			if (PageBuf[PageIndex] == NULL)
	X				Abort("realloc returned no storage");
	X#endif REALLOC
	X			/* (Leave the page delimeter in place so we can find
	X			   the end of the old page when we are printing
	X			   everything out).  Advance PageIndex, reset
	X			   CharIndex, and malloc more storage, so we can start
	X			   reading in the next page */
	X			PageIndex++;
	X			PageBuf[PageIndex] = malloc(PAGEBUFSIZE);
	X			if (PageBuf[PageIndex] == NULL)
	X				Abort("malloc returned no storage");
	X			CharIndex = 0;
	X		}
	X	} while(1);
	X
	X
	X	/* Now print out the buffered up pages in reverse order */
	X
	X	for (PageIndex = PageIndex; PageIndex >= 0; PageIndex--) {
	X		CharIndex = 0;
	X		/* Output the page delimeter record expected by the drivers */
	X		printf("p%d\n", PageIndex + 1);
	X		do {
	X			if (PageDelim(PageBuf[PageIndex][CharIndex]))
	X				break;
	X			fputs(&PageBuf[PageIndex][CharIndex], stdout);
	X			CharIndex += strlen(&PageBuf[PageIndex][CharIndex]) + 1;
	X			/* +1 to skip the null terminator */
	X		} while(1);
	X	}
	X
	X	/* Now print out the final lines, starting with the end-pages
	X	   delimeter line */
	X	puts(EndOfAllPagesDelimString);
	X	PageIndex = 0;
	X	CharIndex = 0;
	X#ifdef REALLOC
	X	/* Current PageBuf may have been chopped off too short for final
	X	   stuff */
	X	PageBuf[PageIndex] = realloc(PageBuf[PageIndex], PAGEBUFSIZE);
	X	if (PageBuf[PageIndex] == NULL)
	X		Abort("realloc returned no storage");
	X#endif REALLOC
	X	do {
	X		InputBufPtr = fgets(&PageBuf[PageIndex][CharIndex],
	X				    PAGEBUFSIZE, InputFile);
	X		if (InputBufPtr == NULL)   /* EOF */
	X			exit(0);
	X		fputs(InputBufPtr, stdout);
	X	} while(1);
	X}
	X
	XAbort(Message)
	Xchar *Message;
	X{
	X		fprintf(stderr, "ditrev: %s at page %d\n", Message, PageIndex);
	X		exit(1);
	X}
SHAR_EOF
if test 5198 -ne "`wc -c < 'ditrev.c'`"
then
	echo shar: "error transmitting 'ditrev.c'" '(should have been 5198 characters)'
fi
fi
echo shar: "extracting 'makefile'" '(146 characters)'
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'makefile'
	X# use -DREALLOC if you want this program to use realloc(1)
	XCFLAGS = -O -DREALLOC
	X
	Xditrev:	ditrev.c
	X	cc ${CFLAGS} -o ditrev ditrev.c
	X	strip ditrev
SHAR_EOF
if test 146 -ne "`wc -c < 'makefile'`"
then
	echo shar: "error transmitting 'makefile'" '(should have been 146 characters)'
fi
fi
exit 0
#	End of shell archive