[comp.text.tex] SUMMARY: 2

chou@oahu.cs.ucla.edu (Ching-Tsun Chou) (11/28/90)

I asked some time ago about how to print 2 pages on 1 page of physical paper.
Here are the replies that I got.  I've edited them a bit to save space 
and post only those that contain actual codes.

My sincere thanks to all who responded to my query.

By the way, many people mentioned Rockiki's dvidvi.
Does anyone know where to get it?
Please send your reply to:

	<chou@cs.ucla.edu>

- Ching Tsun


From: mernst@theory.lcs.mit.edu (Michael Ernst)

You can use Rockiki's dvidvi to rearrange .dvi files.

You can also use the following hack, which loses some resolution since it
uses fonts at a smaller-than-usual size instead of substituting for other
fonts; but the result is readable nonetheless.  I got this shell script
from Michelangelo Grigni; I don't know whether he's the original author.

					-Michael Ernst
					 mernst@theory.lcs.mit.edu 

===========================================================================
: # multi.sh
# print files with several pages per sheet. Defaults:
nup=-2
dividers=true
printer=-P${PRINTER-ps}
dviheaders=/a/aviary/psfig/lib/tex.ps # /a/aviary/psfig/lib/mac.MDII.pro

case "$1" in
""|-h*) exec cat << END_USAGE
usage:	multi [-1|-2|-4|-8] [-d] [-Pprinter] [-Jjobname] file ...

Print files (postscript, plain text, or dvi) with N virtual pages per real
sheet of paper, where N=1, 2, 4, or 8 (default -N is "$nup").
DVI files are piped through dvi2ps, text files are piped through enscript.
The -d option turns off black dividing lines between the virtual pages.
The default printer job name is "multi:" plus the first file name, this
can be changed with the -J option.  The default printer is "$printer".
END_USAGE
	;;
esac

# parse the arguments
files=
for arg
do
	case "$arg" in
	-[1248]) nup=$arg ;;
	-d*)	dividers=false ;;
	-P*)	printer="$arg" ;;
	-J*)	job="$arg" ;;
	*)	: ${job="-Jmulti:$arg"}
		if test ! -f "$arg"
		then
			echo "$0: cannot find file $arg" 1>&2
			exit 1
		fi
		files="$files $arg" ;;
	esac
done

case "$nup" in
-1)	landscape=false; rows=1; cols=1; dividers=false;;
-2)	landscape=true; rows=1; cols=2;;
-4)	landscape=false; rows=2; cols=2;;
-8)	landscape=true; rows=2; cols=4;;
*)	echo "$0: internal error" 1>&2; exit 2;;
esac

case "$files" in
"")	echo "$0: no files specified!" 1>&2; exit 1;;
esac

echo "Sending output via 'lpr $printer $job'" 1>&2
(
echo "First sending multi.ps preamble" 1>&2
cat << 'END_MULTI'
%!PS-Adobe-1.0
%%Creator: Ross Cartlidge <rossc@extro.ucc.su.oz>
%%Title: Multiple pages on one page
%%CreationDate: Tuesday July 25 18:00:00 1989
%%Pages: 0
%%DocumentFonts:
%%BoundingBox: 0 0 0 0
%%EndComments
%
% Uncomment the next line if you wish to load multi into the "exitserver"
% state of the PostScript device
% serverdict begin 0 exitserver
%
%
%	make each operator to overlay a procedure so a bind in 
%	a prolog will not stop the overlaying by "multi"
%

[
	/gsave
	/grestore
	/grestoreall
	/initgraphics
	/initmatrix
	/currentmatrix
	/setmatrix
	% Path construction operators
	/initclip
	% Virtual memory operators
	/save
	% ones which needed special overloading
	/showpage
	/erasepage
	/copypage
	/restore
	% ignore these
	/letter
	/legal
	/a4
	/b5
	/lettersmall
	/note
]
{
%	if exists check if operator else define {}
	dup where
	{
		pop
% 		If operator then make into procedure
		dup load type /operatortype eq
		{
			1 array cvx dup
			0
			3 index cvx		% /n -> n
			put			% {}[0] -> n
			bind
			def
		}
		{
			pop
		}
		ifelse
	}
	{
		{} def
	}
	ifelse
}
forall

%
%	Initialise endmulti to execute an error
%
/endmulti
{
	count array astore /ostack exch def
	250 array execstack /estack exch def
	20 array dictstack /dstack exch def
	$error /newerror true put
	$error /errorname (No matching multi) cvn put
	$error /command (endmulti) put
	$error /ostack ostack put
	$error /estack estack put
	$error /dstack dstack put
	stop
}
bind
def

%
%	Put multiple logical pages on one physical page
%	until "endmulti" called
%	
%	landscape nrows ncols dividers multi -
%
%	landscape	boolean, if true divide page in landscape orientation
%	nrows		integer, number of logical pages down physical page
%	ncols		integer, number of logical pages across physical page
%	dividers	boolean, if true divide logical pages by lines
%
/multi
{
	currentdict
	64 dict begin
	/initdict exch def	% store initial dict for backward reference
	/dividers exch def
	/cols exch def
	/rows exch def

%
%	get size of current page
%
	initgraphics clippath pathbbox
	/Y exch def	% Max Y
	/X exch def	% Max X
	/y exch def	% Min Y
	/x exch def	% Min X
	/W X x add def	% Width of Page
	/H Y y add def	% Height of page

%	if landscape
	{
%
%		Note: x and y are reversed
%
		/w Y y sub def	% Width of imageable region
		/h X x sub def	% Height of imageable region
		/L		% Map to landscape
			-90 matrix rotate
			0 H matrix translate
			matrix concatmatrix
		def
		/O y x matrix translate def	% Move to origin
	}
	{
		/w X x sub def
		/h Y y sub def
		/L matrix def
		/O x y matrix translate def
	}
	ifelse

%
%	CTM (multi) = C x T x M x L x I
%	CTM (normal) = C x I
%	CTM (normal) = CTM (multi) x (T x M x L x I)-1 x I
%	M = (Scale rows/cols) x (Scale logical to physical) x
%		(Translate to physical clip origin
%	T = (Convert logical page to spot and physical)
%	L = (Convert to landscape)
%	I = Initial Physical CTM
%	C = Random transform on logical page
	/I
		matrix currentmatrix
	def
	/I_inv
		I matrix invertmatrix
	def

	/M
			w W div cols div
			h H div rows div
		matrix scale			%TMP
		O
		matrix concatmatrix
	def

%	matrix T <current T>
	/T
	{
		page# cols mod W mul
		rows page# cols idiv sub 1 sub H mul
		3 -1 roll translate
	}
	def

%
%	Utility functions
%	NB: *_t1 are temporary variables
%

%	matrix fromcanon <I-1 x T x M x L x I>
	/From_t1 matrix def
	/From_t2 matrix def
	/From_t3 matrix def
	/From_t4 matrix def
	/fromcanon
	{
		I_inv
		From_t1 T
		M
		L
		I
		From_t2 concatmatrix	
		From_t3 concatmatrix
		From_t4 concatmatrix
		3 -1 roll concatmatrix
	}
	def

%	/n {} mkmulti -
%	makes a new function called "n" in previous dict with:-
%		{}[0] = /n
%		{}[1] = currentdict
%		currentdict.n = prevdict.n
%
	/mkmulti
	{
		1 index dup load def	%define old val in current dict
		5 array cvx
		dup 3 4 -1 roll put	% A[3] = {}
		dup 0 3 index put	% A[0] = /n
		dup 1 currentdict put	% A[1] = currentdict
		dup 2 /begin cvx put	% A[2] = begin
		dup 4 /exec cvx put	% A[4] = exec
		initdict 3 1 roll
		put			% define initdict.n to multi function
	}
	def

%
%	path_to_proc {}
%		make proc represenation of current path
%
	/path_to_proc
	{
		{
			[
				/newpath cvx
				{ /moveto cvx}
				{ /lineto cvx}
				{ /curveto  cvx}
				{ /closepath cvx }
				pathforall
			]
			cvx
			exch pop
		}
		stopped
		{
			$error /errorname get /invalidaccess eq
			{
				cleartomark
				$error /newerror false put
				(%%Warning%% charpath in path - path nulled) =
				cvx exec
			}
			{
				stop
			}
			ifelse
		}
		if
	}
	def
	/path_def
	{
		{ currentpoint } stopped
		{
			$error /newerror false put
			{ newpath }
		}
		{
			/newpath cvx 3 1 roll /moveto cvx 4 array astore cvx
		}
		ifelse
	}
	cvlit def

%
%	Draw lines round logical pages
%
	/draw_dividers
	{
		initgraphics
		L concat
		M concat
		1 1 cols 1 sub
		{
			W mul
			dup
			0 moveto
			rows H mul lineto
		}
		for
		1 1 rows 1 sub
		{
			H mul
			dup
			0 exch moveto
			cols W mul exch lineto
		}
		for
		stroke
	}
	def

%
%	for each graphics operator which affects absolute state
%
	/M1 matrix def
	/M3 matrix def
	/M2 matrix def
	[
		/gsave
		/grestore
		/grestoreall
		/initgraphics
		/initmatrix
		/currentmatrix
		/setmatrix
		% Path construction operators
		/initclip
		% Virtual memory operators
		/save
	]
	{
		{
%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Tocano (canon mode)
%
			M1 currentmatrix
			Tocanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec

			load exec

%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Fromcanon (Non canon mode)
%
			M1 currentmatrix
			Fromcanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec
			end
		}
		mkmulti
	}
	forall

%
%	Define the operators which can't use the standard template
%
	/showpage
	{
		/page# page# 1 add def

%		Update the transform matrices
		page# npages eq
		{
			dividers
			{
				draw_dividers
			}
			if
			load exec	% the previous showpage
			/page# 0 def
		}
		{
			pop
		}
		ifelse
		/Fromcanon Fromcanon fromcanon def
		/Tocanon Fromcanon Tocanon invertmatrix def
		end
		initgraphics	% the new initgraphics
	}
	mkmulti

	/copypage
	{
		pop
		end
		gsave
		showpage
		grestore
	}
	mkmulti

	/erasepage
	{
		pop
		end
		gsave
		initclip
		clippath
		1 setgray fill
		grestore
	}
	mkmulti
	[
		/letter
		/legal
		/a4
		/b5
		/lettersmall
		/note
	]
	{
		{
			pop end
			(%%Warning%% Device change ignored) =
		}
		mkmulti
	}
	forall

%
%	Define restore separately as it affects the value of page#, etc
%
	/restore
	{
		pop
%		Push the values to restore after restore
		mark exch 	% put mark under -save-
		page#
		Fromcanon aload pop
		Tocanon aload pop

		counttomark -1 roll	% get -save- to the top
		restore

%		Restore popped values
		Tocanon astore pop
		Fromcanon astore pop
		/page# exch def
		pop	% mark

%		Save paths
		path_def path_to_proc
		clippath  { { } } path_to_proc

%
%		CTM <- CTM x Fromcanon (Non canon mode)
%
		M1 currentmatrix
		Fromcanon
		M2
		concatmatrix
		setmatrix

%		Restore paths
		initclip exec clip
		exec
		end
	}
	mkmulti
%
%	procedure to undo the effect of multi
%
	/endmulti
	{
		pop	% don't need /endmulti
		[
			/gsave
			/grestore
			/grestoreall
			/initgraphics
			/initmatrix
			/currentmatrix
			/setmatrix
			% Path construction operators
			/initclip
			% Virtual memory operators
			/save
			% ones which needed special overloading
			/showpage
			/erasepage
			/copypage
			/restore
			% ignore these
			/letter
			/legal
			/a4
			/b5
			/lettersmall
			/note
			%
			/endmulti
		]
		{
			initdict exch
			dup load 		% get old value
			put			% restore old value
		}
		forall
		page# 0 ne	% if not at new page show uncomplete page
		{
			dividers
			{
				draw_dividers
			}
			if
			showpage
		}
		if
		end
	}
	mkmulti

%
%	Set up in multi(non canon) mode
%
	/page# 0 def
	/npages rows cols mul def
	/Fromcanon matrix fromcanon def
	/Tocanon Fromcanon matrix invertmatrix def
	end
	initgraphics
}
bind
def
END_MULTI
echo "Using landscape=$landscape rows=$rows cols=$cols dividers=$dividers" 1>&2
echo "$landscape $rows $cols $dividers multi"
# Now send all the files, special handling for dvi and text files:
dvisent=no
echo "Printing files:" 1>&2
for file in $files
do
	case "$file" in
	*.ps)	echo "$file: assumed PostScript" 1>&2
		cat $file ;;
	*.dvi)	case $dvisent in
		no)	echo "Sending dvi2ps header files" 1>&2
			cat $dviheaders
			dvisent=yes ;;
		esac
		echo "$file: piping via dvi2ps" 1>&2
		dvi2ps -r -h $file ;; # 2> /dev/null
	*)	case "`file -L $file`" in
		*"PostScript document")
			echo "$file: assumed PostScript" 1>&2
			cat $file ;;
		*)	echo "$file: assumed ascii, piping via enscript" 1>&2
			enscript -1RB -p - $file ;; # 2> /dev/null
		esac ;;
	esac
	echo ''
done
echo "endmulti"
echo "Done." 1>&2
) | lpr $printer $job


From: dwilkins@MATHS.TCD.IE

Here are my own macros for doing this, by inserting the
relevant command (e.g. \twopageafour) at or near the head of the TeX
document (before any text or other material to be printed).

Unfortunately for you, it is set up for A4 paper (297mm * 210mm).
You would have to hack the \fullhsize, \voffset and \hoffset
to get it to work with US letter or legal paper.  Also the
macros use the landscape \special command for the dvitops
dvi -> postscript translator.  If you use printer driver
(e.g. dvips, dvi2ps, dvi3ps), you would have to use whatever
landscape mode \special command is available

---------------------Cut-------------------------------------

% \twopageoutput is a macro to output on A4 paper, two
% pages at a time.  The user specifies the \vsize and \hsize
% parameters for the individual pages, together with the
% width of the outside margins.  The macro \twopageafour
% calls \twopageoutput with these values set to
% 160 true mm, 125 true mm and 10 true mm respectively

\newdimen\fullhsize
\newbox\leftpage
\def\twopageoutput#1#2#3{\vsize=#1\hsize=#2\relax
   \fullhsize=#3\multiply\fullhsize by -2
   \advance \fullhsize by 297 true mm
   \voffset=79.6  true mm \advance\voffset by -0.5\vsize
   \hoffset=#3\advance \hoffset by -25.4 true mm
   \let\lr=L
   \tenpointstyle
   \output={\if L\lr
         \global\setbox\leftpage=\line{\vbox{\makeheadline
         \pagebody\makefootline}\hss}%
         \advancepageno\global\let\lr=R
      \else
         \shipout\vbox{\hbox to \fullhsize{\box\leftpage\hfil
         \line{\vbox{\makeheadline\pagebody\makefootline}\hss}}%
         \special{dvitops: landscape}}%
         \advancepageno\global\let\lr=L
      \fi
   \ifnum\outputpenalty>-20000 \else \dosupereject \fi}}
\def\twopageafour{\twopageoutput{160 true mm}{125 true mm}{10 true mm}}

---------------------Cut-------------------------------------

I don't know whether this is of any use to you.  You will probably
get something more suitable from somebody else.

David Wilkins (dwilkins@maths.tcd.ie)
School of Mathematics, Trinity College, Dublin 2, Ireland


From: kai@nada.kth.se (Kai-Mikael J{{-Aro)
Subject: This is multi

Article 73 of comp.text.tex:
Path: draken!kth.se!sunic!uupsi!rpi!zaphod.mps.ohio-state.edu!uakari!ames!apple!uokmax!munnari.oz.au!cluster!metro!news
From: rossc@extro.ucc.su.oz.au (Ross Cartlidge)
Newsgroups: comp.text.tex
Subject: Multi.ps - 2 up printing (and more)
Message-ID: <1990Feb24.005958.29506@metro.ucc.su.oz.au>
Date: 24 Feb 90 00:59:58 GMT
Reply-To: rossc@extro.ucc.su.oz.au (Ross Cartlidge)
Organization: University Computing Service, Uni. of Sydney, Australia.
Lines: 719

To everyone who asked for this:-

#! /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:
#	multi.ps
#	eg.sh
#	README.multi
# This archive created: Wed Feb 21 20:47:57 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'multi.ps'
then
	echo shar: "will not over-write existing file 'multi.ps'"
else
cat << \SHAR_EOF > 'multi.ps'
%!PS-Adobe-1.0
%%Creator: Ross Cartlidge <rossc@extro.ucc.su.oz>
%%Title: Multiple pages on one page
%%CreationDate: Tuesday July 25 18:00:00 1989
%%Pages: 0
%%DocumentFonts:
%%BoundingBox: 0 0 0 0
%%EndComments
%
% Uncomment the next line if you wish to load multi into the "exitserver"
% state of the PostScript device
% serverdict begin 0 exitserver
%
%
%	make each operator to overlay a procedure so a bind in 
%	a prolog will not stop the overlaying by "multi"
%

[
	/gsave
	/grestore
	/grestoreall
	/initgraphics
	/initmatrix
	/currentmatrix
	/setmatrix
	% Path construction operators
	/initclip
	% Virtual memory operators
	/save
	% ones which needed special overloading
	/showpage
	/erasepage
	/copypage
	/restore
	% ignore these
	/letter
	/legal
	/a4
	/b5
	/lettersmall
	/note
]
{
%	if exists check if operator else define {}
	dup where
	{
		pop
% 		If operator then make into procedure
		dup load type /operatortype eq
		{
			1 array cvx dup
			0
			3 index cvx		% /n -> n
			put			% {}[0] -> n
			bind
			def
		}
		{
			pop
		}
		ifelse
	}
	{
		{} def
	}
	ifelse
}
forall

%
%	Initialise endmulti to execute an error
%
/endmulti
{
	count array astore /ostack exch def
	250 array execstack /estack exch def
	20 array dictstack /dstack exch def
	$error /newerror true put
	$error /errorname (No matching multi) cvn put
	$error /command (endmulti) put
	$error /ostack ostack put
	$error /estack estack put
	$error /dstack dstack put
	stop
}
bind
def

%
%	Put multiple logical pages on one physical page
%	until "endmulti" called
%	
%	landscape nrows ncols dividers multi -
%
%	landscape	boolean, if true divide page in landscape orientation
%	nrows		integer, number of logical pages down physical page
%	ncols		integer, number of logical pages across physical page
%	dividers	boolean, if true divide logical pages by lines
%
/multi
{
	currentdict
	64 dict begin
	/initdict exch def	% store initial dict for backward reference
	/dividers exch def
	/cols exch def
	/rows exch def

%
%	get size of current page
%
	initgraphics clippath pathbbox
	/Y exch def	% Max Y
	/X exch def	% Max X
	/y exch def	% Min Y
	/x exch def	% Min X
	/W X x add def	% Width of Page
	/H Y y add def	% Height of page

%	if landscape
	{
%
%		Note: x and y are reversed
%
		/w Y y sub def	% Width of imageable region
		/h X x sub def	% Height of imageable region
		/L		% Map to landscape
			-90 matrix rotate
			0 H matrix translate
			matrix concatmatrix
		def
		/O y x matrix translate def	% Move to origin
	}
	{
		/w X x sub def
		/h Y y sub def
		/L matrix def
		/O x y matrix translate def
	}
	ifelse

%
%	CTM (multi) = C x T x M x L x I
%	CTM (normal) = C x I
%	CTM (normal) = CTM (multi) x (T x M x L x I)-1 x I
%	M = (Scale rows/cols) x (Scale logical to physical) x
%		(Translate to physical clip origin
%	T = (Convert logical page to spot and physical)
%	L = (Convert to landscape)
%	I = Initial Physical CTM
%	C = Random transform on logical page
	/I
		matrix currentmatrix
	def
	/I_inv
		I matrix invertmatrix
	def

	/M
			w W div cols div
			h H div rows div
		matrix scale			%TMP
		O
		matrix concatmatrix
	def

%	matrix T <current T>
	/T
	{
		page# cols mod W mul
		rows page# cols idiv sub 1 sub H mul
		3 -1 roll translate
	}
	def

%
%	Utility functions
%	NB: *_t1 are temporary variables
%

%	matrix fromcanon <I-1 x T x M x L x I>
	/From_t1 matrix def
	/From_t2 matrix def
	/From_t3 matrix def
	/From_t4 matrix def
	/fromcanon
	{
		I_inv
		From_t1 T
		M
		L
		I
		From_t2 concatmatrix	
		From_t3 concatmatrix
		From_t4 concatmatrix
		3 -1 roll concatmatrix
	}
	def

%	/n {} mkmulti -
%	makes a new function called "n" in previous dict with:-
%		{}[0] = /n
%		{}[1] = currentdict
%		currentdict.n = prevdict.n
%
	/mkmulti
	{
		1 index dup load def	%define old val in current dict
		5 array cvx
		dup 3 4 -1 roll put	% A[3] = {}
		dup 0 3 index put	% A[0] = /n
		dup 1 currentdict put	% A[1] = currentdict
		dup 2 /begin cvx put	% A[2] = begin
		dup 4 /exec cvx put	% A[4] = exec
		initdict 3 1 roll
		put			% define initdict.n to multi function
	}
	def

%
%	path_to_proc {}
%		make proc represenation of current path
%
	/path_to_proc
	{
		{
			[
				/newpath cvx
				{ /moveto cvx}
				{ /lineto cvx}
				{ /curveto  cvx}
				{ /closepath cvx }
				pathforall
			]
			cvx
			exch pop
		}
		stopped
		{
			$error /errorname get /invalidaccess eq
			{
				cleartomark
				$error /newerror false put
				(%%Warning%% charpath in path - path nulled) =
				cvx exec
			}
			{
				stop
			}
			ifelse
		}
		if
	}
	def
	/path_def
	{
		{ currentpoint } stopped
		{
			$error /newerror false put
			{ newpath }
		}
		{
			/newpath cvx 3 1 roll /moveto cvx 4 array astore cvx
		}
		ifelse
	}
	cvlit def

%
%	Draw lines round logical pages
%
	/draw_dividers
	{
		initgraphics
		L concat
		M concat
		1 1 cols 1 sub
		{
			W mul
			dup
			0 moveto
			rows H mul lineto
		}
		for
		1 1 rows 1 sub
		{
			H mul
			dup
			0 exch moveto
			cols W mul exch lineto
		}
		for
		stroke
	}
	def

%
%	for each graphics operator which affects absolute state
%
	/M1 matrix def
	/M3 matrix def
	/M2 matrix def
	[
		/gsave
		/grestore
		/grestoreall
		/initgraphics
		/initmatrix
		/currentmatrix
		/setmatrix
		% Path construction operators
		/initclip
		% Virtual memory operators
		/save
	]
	{
		{
%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Tocano (canon mode)
%
			M1 currentmatrix
			Tocanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec

			load exec

%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Fromcanon (Non canon mode)
%
			M1 currentmatrix
			Fromcanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec
			end
		}
		mkmulti
	}
	forall

%
%	Define the operators which can't use the standard template
%
	/showpage
	{
		/page# page# 1 add def

%		Update the transform matrices
		page# npages eq
		{
			dividers
			{
				draw_dividers
			}
			if
			load exec	% the previous showpage
			/page# 0 def
		}
		{
			pop
		}
		ifelse
		/Fromcanon Fromcanon fromcanon def
		/Tocanon Fromcanon Tocanon invertmatrix def
		end
		initgraphics	% the new initgraphics
	}
	mkmulti

	/copypage
	{
		pop
		end
		gsave
		showpage
		grestore
	}
	mkmulti

	/erasepage
	{
		pop
		end
		gsave
		initclip
		clippath
		1 setgray fill
		grestore
	}
	mkmulti
	[
		/letter
		/legal
		/a4
		/b5
		/lettersmall
		/note
	]
	{
		{
			pop end
			(%%Warning%% Device change ignored) =
		}
		mkmulti
	}
	forall

%
%	Define restore separately as it affects the value of page#, etc
%
	/restore
	{
		pop
%		Push the values to restore after restore
		mark exch 	% put mark under -save-
		page#
		Fromcanon aload pop
		Tocanon aload pop

		counttomark -1 roll	% get -save- to the top
		restore

%		Restore popped values
		Tocanon astore pop
		Fromcanon astore pop
		/page# exch def
		pop	% mark

%		Save paths
		path_def path_to_proc
		clippath  { { } } path_to_proc

%
%		CTM <- CTM x Fromcanon (Non canon mode)
%
		M1 currentmatrix
		Fromcanon
		M2
		concatmatrix
		setmatrix

%		Restore paths
		initclip exec clip
		exec
		end
	}
	mkmulti
%
%	procedure to undo the effect of multi
%
	/endmulti
	{
		pop	% don't need /endmulti
		[
			/gsave
			/grestore
			/grestoreall
			/initgraphics
			/initmatrix
			/currentmatrix
			/setmatrix
			% Path construction operators
			/initclip
			% Virtual memory operators
			/save
			% ones which needed special overloading
			/showpage
			/erasepage
			/copypage
			/restore
			% ignore these
			/letter
			/legal
			/a4
			/b5
			/lettersmall
			/note
			%
			/endmulti
		]
		{
			initdict exch
			dup load 		% get old value
			put			% restore old value
		}
		forall
		page# 0 ne	% if not at new page show uncomplete page
		{
			dividers
			{
				draw_dividers
			}
			if
			showpage
		}
		if
		end
	}
	mkmulti

%
%	Set up in multi(non canon) mode
%
	/page# 0 def
	/npages rows cols mul def
	/Fromcanon matrix fromcanon def
	/Tocanon Fromcanon matrix invertmatrix def
	end
	initgraphics
}
bind
def
SHAR_EOF
fi
if test -f 'eg.sh'
then
	echo shar: "will not over-write existing file 'eg.sh'"
else
cat << \SHAR_EOF > 'eg.sh'
#!/bin/sh
cat multi.ps - <<-'!'
	% landscape mode, 1 row, 2 cols, no dividers
	true 1 2 true multi
	%
	/Helvetica findfont 12 scalefont setfont
	100 100 moveto
	save
	(This is Page One) show
	showpage
	restore
	save
	(This is Page Two) show
	showpage
	restore
	%
	endmulti
!
SHAR_EOF
chmod +x 'eg.sh'
fi
if test -f 'README.multi'
then
	echo shar: "will not over-write existing file 'README.multi'"
else
cat << \SHAR_EOF > 'README.multi'
This package is a PostScript prolog which when used with almost
any valid PostScript will output it with multiple logical pages
on each physical page. Each logical page will be identical (but smaller)
to the original result of each showpage.

Written By:

	Ross Cartlidge
	University Computing Service
	Building H08, Sydney University
	NSW, 2006, Australia

	rossc@extro.ucc.su.oz		Email
	+61 2 692 3495			Phone
	+61 2 660 6557			Fax

It works by overlaying each PostScript Operator which is affected by
or affects the absolute graphics state with a procedure which first goes back
to normal full page graphics state, does the operator, and
then restores the multiple page graphics state. Thus when "initgraphics", eg,
is executed its transformation of CTM, path and clippath is mapped
onto the multiple page representation. Also when a "save" is done the
full page version is saved, thus when "restore" is done - possibly many
logical pages into the future - the graphics state saved is transformed
into the new multi page.

All its local variables are stored in private (unnamed) directories
and the only "pollution" of the current directory is with the entry
and exit points "multi" and "endmulti".

It is written to be able to be included in the "exitserver" state
of the the server, thus it can be loaded before any other permanent
prologs which may use absolute graphics commands (like "showpage").

It works recursively so a "multi" can be called inside another "multi"
to any level (VM permitting).

It tries to use as little VM as possible but some is consumed in
restoring paths between multi and single page graphics states
so large documents may cause VMerror earlier than in normal representation
unless "save-restore" is used appropriately. 

USAGE:
	"multi.ps" defines 2 procedures:-
	
    (i)
	landscape nrows ncols dividers multi -

	Go into multi page representation

	landscape	boolean, if true divide page in landscape orientation
	nrows		integer, number of logical pages down physical page
	ncols		integer, number of logical pages across physical page
	dividers	boolean, if true divide logical pages by lines

   (ii)
	endmulti -

	End multi page representation


EXAMPLES:
	Display pages in "side by side" form

	% landscape mode, 1 row, 2 cols, dividers
	true 1 2 true multi
	%
	/Helvetica findfont 12 scalefont setfont
	100 100 moveto
	save
	(This is Page One) show
	showpage
	restore
	save
	(This is Page Two) show
	showpage
	restore
	%
	endmulti

To run this example type:-
	sh eg.sh | <your PostScript print command>

It's not a bad idea to load multi.ps in to the
"exitserver" state of your LaserWriter as then
it will work with any prologs you may be using.
Make sure you load it first! To load it into
the "exitserver" state see the comments at the start of multi.ps.
You then only need the :-
	true 1 2 false multi
line in code sent to the LaserWriter.


CAVEATS:
	If you have a proprietary font in you path
	or your clippath then the path will be lost
	when one of the overlayed operators is used.
	If you need the font in the path then either
	stop the overloading of that operator and
	assume its effect won't be used across logical
	page boundaries or change the postscript you are "multi-ing"
	to refer to the systemdict version of the operators
	where the effect is not used across page boundaries.
	
	EG: CHANGE
		(FRED true charpath gsave fill grestore stroke
	TO
		(FRED) true charpath systemdict begin gsave end
		fill systemdict begin grestore end stroke

	OR
		remove the overloading of gsave/grestore from multi.ps

	More memory is consumed than normal output

	Direct references to operators through "systemdict" will
	circumvent overlays

	Programs which rely on the overlayed named being operators
	and  not procedures will fail. For example:-
		/showpage { showpage } bind def
	will create an infinite loop when showpage is called.
	Also procedures directly on the stack do not execute
	unlike operators so the program:-
		/s [ /showpage load ] cvx def s
	will not work ( an "exec" is required after the "load")

	Device changing operators such as "letter", etc
	are mapped to null procedures
SHAR_EOF
fi
exit 0
#	End of shell archive
--
________________________________________________________________________
Ross Rodney Cartlidge			    |   rossc@extro.ucc.su.oz.au
University Computing Service, H08	    |   Phone:     +61 2 6923497
University of Sydney, NSW 2006, Australia   |   FAX:       +61 2 6606557


From: kai@nada.kth.se (Kai-Mikael J{{-Aro)
Subject: This is mpage

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
echo 'Extracting CHANGES'
sed 's/^X//' > CHANGES << '+ END-OF-FILE CHANGES'
XMany changes have been made between Version 1 and Version 2:
X
X- *BIGGIE* Printing of backspaces, overwrites, formfeeds and other
Xtext is greatly improved. I changed the whole way text was processed
Xand the code went from 3 unreadable pages to one very readable page.
XIt is amazing what the right approch can do.
X
X- *ANOTHER BIGGIE* PostScript documents can now be printed.  Most
Xconforming will be properly printed, multple pages per sheet.  There
Xis still clean up to do here, but it seems to work.  I think that when
XI hit upon the right paradigm for this, it will clean up just like the
Xtext processing code did.  (XRef: TODO)
X
X- Outlines are now optional. If you like to see them (as I do) put the
X"-o" option in your MPAGE environment variable.
X
X- Added recognition of the PRINTER and MPAGE environment variables.
X
X- Added the beginnings of support the European A4 paper.  (The method
Xshould be extended to legal sized paper.)  Credit goes to Michael
XFingerhut, System Manager, IRCAM Paris (France), (...!uunet!ircam!mf)
Xfor the inspiration for the proper way to do this.
X
X- MPage now prints a message stating how many pages (sheets) were
Xcreated and to which printer it was spooled.
X
X- Stuffed the sources under RCS to keep track of changes.
X
+ END-OF-FILE CHANGES
chmod 'u=rw,g=rw,o=r' 'CHANGES'
echo '	-rw-rw-r--  1 kai          1249 Jan  1  1990 CHANGES        (as sent)'
echo -n '	'
/bin/ls -l CHANGES
echo 'Extracting MANIFEST'
sed 's/^X//' > MANIFEST << '+ END-OF-FILE MANIFEST'
X   File Name		Archive #	Description
X----------------------------------------------------------
X README                     1	Notes and descriptions
X MANIFEST                   1	This shipping list
X CHANGES                    1	Changes since Version 1
X Makefile.mpage             2	The Makefile
X TODO                       2	Wish List for changes
X mp_args.c                  2	Command line and options processing
X mp_file.c                  1	Generic file handeling
X mp_glob.c                  1	Global variable setup
X mp_head.h                  1	Definitions
X mp_main.c                  2	Main Control
X mp_page.c                  2	Page layout routines
X mp_post.c                  1	PostScript file processing
X mp_sample.c                2	Prints sample page layout pages 
X mp_text.c                  1	Text file processing
X mpage.1                    2	Manual page
X
+ END-OF-FILE MANIFEST
chmod 'u=rw,g=rw,o=r' 'MANIFEST'
echo '	-rw-rw-r--  1 kai           867 Jan  1  1990 MANIFEST        (as sent)'
echo -n '	'
/bin/ls -l MANIFEST
echo 'Extracting Makefile.mpage'
sed 's/^X//' > Makefile.mpage << '+ END-OF-FILE Makefile.mpage'
X#
X# $Header: Makefile,v 1.3 89/05/25 09:06:55 mark Exp $
X#
XHEAD =  mp_head.h
XSRCS =  mp_main.c mp_glob.c mp_text.c mp_post.c mp_file.c mp_page.c mp_args.c
XMOBJ =  mp_main.o mp_glob.o mp_text.o mp_post.o mp_file.o mp_page.o mp_args.o
XSMPL =  mp_sample.c mp_page.c mp_glob.c mp_args.c
XSOBJ =  mp_sample.o mp_page.o mp_glob.o mp_args.o
X
X
X#CFLAGS = -gx -DDEBUG
XCFLAGS = -gx
X
X.SUFFIXES:	.c,v
X
Xdefault:	mpage msample Makefile.mpage
X	@echo Done!
X
Xmpage:	$(MOBJ)
X	$(CC) $(CFLAGS) -o mpage $(MOBJ)
X
Xmsample:	$(SOBJ)
X	$(CC) $(CFLAGS) -o msample $(SOBJ)
X
XMakefile.mpage:	Makefile
X
Xclean:
X	rm -rf $(MOBJ) mpage mpage.ps
X
Xmp_main.o:	$(HEAD) mp_main.c
X	$(CC) -c $(CFLAGS) mp_main.c
X
Xmp_glob.o:	$(HEAD) mp_glob.c
X	$(CC) -c $(CFLAGS) mp_glob.c
X
Xmp_text.o:	$(HEAD) mp_text.c
X	$(CC) -c $(CFLAGS) mp_text.c
X
Xmp_post.o:	$(HEAD) mp_post.c
X	$(CC) -c $(CFLAGS) mp_post.c
X
Xmp_file.o:	$(HEAD) mp_file.c
X	$(CC) -c $(CFLAGS) mp_file.c
X
Xmp_page.o:	$(HEAD) mp_page.c
X	$(CC) -c $(CFLAGS) mp_page.c
X
Xmp_args.o:	$(HEAD) mp_args.c
X	$(CC) -c $(CFLAGS) mp_args.c
X
Xmp_sample.o:	$(HEAD)
X	$(CC) -c $(CFLAGS) $*.c
X
Xmpage.ps:	mpage.1
X	psroff -t -man mpage.1 > mpage.ps
X
X
X# add your proper install stuff
Xinstall:
X
+ END-OF-FILE Makefile.mpage
chmod 'u=rw,g=rw,o=r' 'Makefile.mpage'
echo '	-rw-rw-r--  1 kai          1173 Jan  1  1990 Makefile.mpage        (as sent)'
echo -n '	'
/bin/ls -l Makefile.mpage
echo 'Extracting README'
sed 's/^X//' > README << '+ END-OF-FILE README'
X
XCOPYRIGHT:
X
XMpage and all the files distributed with mpage are covered by copyright:
X
X Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X  
X     Permission is granted to anyone to make or distribute verbatim
X     copies of this document as received, in any medium, provided
X     that this copyright notice notice is preserved, and that the
X     distributor grants the recipient permission for further
X     redistribution as permitted by this notice.
X
XMpage was written by:
X   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X              >pyrdc!mark            Pyramid Technology Corporation
X ...!pyramid!/                       Vienna, Va    (703)848-2050
X
X
X
X******************** Important Notice ****************************
X
XI am moving to Seoul, Korea in about 2 days.  As such any claims
Xthat I might have a couple minutes to spare to work on improvments
Xor repairs should be reduced accordingly.  While there are still
Xa number of changes I would like to make, I just don't have the
Xtime.  They will have to wait.  However, I still solicit your input
Xso that if I have time, I will do what I can.  I don't know my
Xnew net address or postal address, but mail sent to the above will
Xbe forwarded on an irregular schedule.
X
X**************** End Of Important Notice *************************
X
XDESCRIPTION:
X
XMpage is a program to reduce and print multiple pages of text per
Xsheet on a PostScript compatible printer.
X
XThe following are the files you should have for mpage.
X
X     MANIFEST               The shipping list
X     README                 Notes and descriptions
X     CHANGES                Changes since Version 1
X     Makefile.mpage         The Makefile
X     TODO                   Wish List for changes
X     mp_args.c              Command line and options processing
X     mp_file.c              Generic file handeling
X     mp_glob.c              Global variable setup
X     mp_head.h              Definitions
X     mp_main.c              Main Control
X     mp_page.c              Page layout routines
X     mp_post.c              PostScript file processing
X     mp_sample.c            Prints sample page layout pages 
X     mp_text.c              Text file processing
X     mpage.1                Manual page
X
X
X
XINSTRUCTIONS:
X
XAll you should need to do is copy Makefile.mpage to Makefile and run
Xmake.  This will create mpage and a program called msample.  Mpage is
Xthe program to print n-up pages.  Msample prints a sample outline.  I
Xused it for debugging the placement of the layout.  It is also handy
Xfor other layout purposes.  It accepts all the arguments that mpage
Xdoes, but does not print files.
X
XAs a quick sample try:
X
X	"mpage -p mp_args.c"
Xor
X	"mpage -p8 mp_post.c"
Xor
X	"psroff -t -man mpage.1 | mpage -2"
X
XThe manual page, mpage.1, formats with UCB or ATT manual macros.
X
XThe spooler interface in mpage only submits to a UCB style spooler.  It
Xshouldn't take too much work to convert to submission to a ATT style
Xspooler.
X
X
X
XUSING MPAGE:
X
XYou will find that it takes a while on an Apple LW or Apple LW+ to
Xprint 4 pages per sheet, It takes "a good long time" for 8 pages.
XReduced PostScript documents take a while too.  (A rule of thumb might
Xbe 4 times as long for 4-pages-per-sheet reduction :-) On a QMS 8ppm
Xprinter 4 pages per sheet is about the same speed and other forms of
X"1-up" output.  mpage prints some timing information back from the
Xprinter so you can measure the time required for full jobs.
X
X
X
XADMINSITRATORS:
X
XAs a printer administrator you should see the wish list for caveats
Xand information about future versions.  I still have work to do to
Xmake mpage more "administrator friendly", but figured that I'd better
Xget what's been done out the door.  The alternative is to constanly
Xmake changes and never release anything.
X
X
X
XBUG REPORTS:
X
XMpage is not a supported program.  I do not have the time or resources
Xto maintain mpage.  I am interested in receiving reports of problems
Xso that which I get spare time I can try to fix them.  If you have a
Xproblem please send me a complete, succinct description of the
Xsymptoms.  Please include the brand name of your printer, a copy of
Xthe input file (especially if it is with PostScript input), and a
Xdescription of what to look for on the output.  If your printer has an
Xerror log, please include as much of it as is revalant.  In general,
Xthe more clearly organized information I have, the easier it will be
Xfor me.
X
X
X
XMAKING CHANGES:
X
XPlease, if you make any improvements, send them back to me so that I
Xcan include them in future versions.  Also I would solicit comments as
Xto the usefulness, and effectiveness of mpage.  Let me know what you
Xlike or dislike and feel free to ask any questions you have concerning
Xthis mpage distribution.  Thank you, Mark Hahn.
X
+ END-OF-FILE README
chmod 'u=rw,g=rw,o=r' 'README'
echo '	-rw-rw-r--  1 kai          4768 Jan  1  1990 README        (as sent)'
echo -n '	'
/bin/ls -l README
echo 'Extracting TODO'
sed 's/^X//' > TODO << '+ END-OF-FILE TODO'
XCurrent Wish List:
X
X
X- Should print a help string when invoked as "mpage".  However, like
Xcat(1), I chose to default to processing standard input when no files
Xare specified.  I will change this so that the file "-" refers to
Xstandard input and hence you will have to type something like:
X	ls -Rl | mpage -8 -
XMore thought is required here.  (you comments solicited)
X
X
X- Add support for handeling ATT style spoolers.  Or any type spooler.
XShouldn't be that difficult.
X
X 
X- Support for reducing mpage output.  I.E. if I do:
X		mpage -8P file.txt | mpage -8
XI should get 64-up output.  Problems are that the PostScript code
Xproduced is not "recursive".  The end-of-page macro in particular
Xsuffers under re-definition, you get blank pages.  Another problems is
Xresolution of the printer.  See the next Wish item.
X
X 
X- Better handeling of PostScript input.  The current stuff works, but
Xit is not general enough.  I am currently learning more here, thanks
Xto the "Green Book" and will work on a cleaner parser for Version 3.
X(Oh-my-goodness, a pre-announcement ;-)
X
XPerhaps I should convert the whole PostScript processing routing to be
Xa "Finite Automata".  Each line will represent a "token" parsed by its
Xcomment, output will be based upon transitions from state to state.
XThe automata should allow states and transitions to be added dynamicly
Xso that we can adapt to differening structure conventions, and
Xdiffering PostScript code generators (psdit, dvi2ps, etc ...)
X
X
X- Fix A4 paper handeling.
X
X
X- Add better timing information, possible adding command line
Xarguments to control the amount of timing information added to the
Xfile.  (Eyuk, more arguments:-(
X
X
X- Lots more, I'm sure.
X
+ END-OF-FILE TODO
chmod 'u=rw,g=rw,o=r' 'TODO'
echo '	-rw-rw-r--  1 kai          1685 Jan  1  1990 TODO        (as sent)'
echo -n '	'
/bin/ls -l TODO
echo 'Extracting mp_args.c'
sed 's/^X//' > mp_args.c << '+ END-OF-FILE mp_args.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X	"@(#) $Header: mp_args.c,v 2.6 89/05/25 15:43:07 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_args.c,v $
X * Revision 2.6  89/05/25  15:43:07  mark
X * added the -b options as a converse to the -o option.
X * 
X * Revision 2.5  89/05/25  10:42:39  mark
X * changes to print a page count on stderr after the print job is queued.
X * 
X * Revision 2.4  89/05/25  08:57:47  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.3  89/05/22  14:41:03  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:38:06  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:29  mark
X * New Major Revision
X * 
X * Revision 1.1  89/05/22  14:25:29  mark
X * Initial revision
X *  */
X
Xdo_args(argc, argv, envflag)
X int argc;
X char **argv;
X int envflag;
X{
X	char *optstr;
X	int consumed;
X	int currarg;
X	int opterrors;
X
X	opterrors = 0;
X	for (currarg = 1; currarg < argc; currarg++) {
X		if (*argv[currarg] != '-') {
X			if (envflag) {
X				opterrors++;
X			}
X			break;
X		}
X		optstr = argv[currarg];
X		consumed = 0;
X		while (*++optstr && ! consumed) {
X			switch (*optstr) {
X			default:
X				fprintf(stderr,"%s: unknown option -%c\n",
X				       MPAGE, *optstr);
X				opterrors++;
X				break;
X			case '1':
X				sheetindex = 0;
X				break;
X			case '2':
X				sheetindex = 1;
X				break;
X			case '4':
X				sheetindex = 2;
X				break;
X			case '8':
X				sheetindex = 3;
X				break;
X			case 'A':	/* A4 sized, european paper */
X				opt_a4 = 1;
X				set_page();
X				break;
X			case 'a':	/* across */
X				sheetorder = LEFTRIGHT;
X				break;
X			case 'h':
X				++optstr;
X				opt_doheader = 1;
X				(void)strcpy(opt_header, optstr);
X				consumed = 1;
X				break;
X			case 'L':
X				++optstr;
X				opt_lines = atoi(optstr);
X				consumed = 1;
X				break;
X			case 'l':	/* landscape */
X				sheetaspect = LANDSCAPE;
X				break;
X			case 'n':	/* normal */
X				sheetaspect = NORMAL;
X				break;
X			case 'o':	/* print outlines */
X				opt_outline = 1;
X				break;
X			case 'b':	/* don't print outlines */
X				opt_outline = 0;
X				break;
X			case 'P':	/* Printer */
X				++optstr;
X				(void) strcpy(printer, optstr);
X				consumed = 1;
X				break;
X			case 'p':	/* pr */
X				opt_pr++;
X				break;
X			case 's':	/* silent (don't print page count) */
X				opt_verbose = 0;
X				break;
X			case 'u':	/* updown */
X				sheetorder = UPDOWN;
X				break;
X			case 'v':	/* verbose (print page count) */
X				opt_verbose = 1;
X				break;
X			case 'W':
X				++optstr;
X				opt_width = atoi(optstr);
X				consumed = 1;
X				break;
X			}
X		}
X	}
X	if (opterrors) {
X		return -1;
X	}
X	return currarg;
X}
X
Xdo_env()
X{
X	int argc;
X	char **argv, **slice();
X	char copy[LINESIZE];
X	char *env, *getenv();
X
X	env = getenv("PRINTER");
X	if (env) {
X		strcpy(printer, env);
X	}
X
X	env = getenv("MPAGE");
X	if (env) {
X		strcpy(copy, env);
X		argv = slice(copy, &argc);
X		if (do_args(argc, argv, 1) < 0) {
X			fprintf(stderr, "%s: error in envronment \"%s\"\n",
X				MPAGE, env);
X			return 0;
X		}
X	}
X	return 1;
X}
X
Xchar *slc_argv[20];
X
Xchar **slice(string, cntp)
X char *string;
X int *cntp;
X{
X	int count;
X
X	/*
X	 * mimic the shell for conformity
X	 */
X	slc_argv[0] = MPAGE;
X	count = 1;
X	/*
X	 * while there are still characters to be processed
X	 */
X	while (*string) {
X		/*
X		 * skip any leading or leftover white space
X		 */
X		while (*string == ' ') {
X			string++;
X		}
X		/*
X		 * make sure we had more than just white space before
X		 * we believe we actually have an argument
X		 */
X		if (*string) {
X			/*
X			 * point the next slot in argv to this string
X			 */
X			slc_argv[count] = string;
X			count += 1;
X			/*
X			 * and go looking for the end of this string
X			 * which is delienated by a space or NULL
X			 */
X			while (*string && *string != ' ') {
X				string++;
X			}
X			/*
X			 * if this not the end of the string, then convert
X			 * the space into a NULL and move forward one byte.
X			 * if this is the end of the string, we already have
X			 * a suitable NULL byte for the string and it also
X			 * drops us out of all the loops
X			 */
X			if (*string) {
X				*string = 0;
X				string++;
X			}
X		}
X	}
X	/*
X	 * return the count via the integer pointer we were given
X	 * and put a null pointer into the argv array for conformity
X	 */
X	slc_argv[count] = 0;
X	*cntp = count;
X	return slc_argv;
X}
X
+ END-OF-FILE mp_args.c
chmod 'u=rw,g=rw,o=r' 'mp_args.c'
echo '	-rw-rw-r--  1 kai          5081 Jan  1  1990 mp_args.c        (as sent)'
echo -n '	'
/bin/ls -l mp_args.c
echo 'Extracting mp_file.c'
sed 's/^X//' > mp_file.c << '+ END-OF-FILE mp_file.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X	"@(#) $Header: mp_file.c,v 2.4 89/05/25 08:58:18 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_file.c,v $
X * Revision 2.4  89/05/25  08:58:18  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.3  89/05/22  14:40:59  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:38:02  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:24  mark
X * New Major Revision
X * 
X * Revision 1.1  89/05/22  14:23:34  mark
X * Initial revision
X *  */
X
X/*
X * do_file converts one file into postscript for output.  The file type is
X * determined then the proper conversion routine is selected.
X */
Xdo_file(fname, asheet, outfd)
X char *fname;
X struct sheet *asheet;
X FILE *outfd;
X{
X	FILE *fd;
X	int firstchr;
X
X	/*
X	 * if we have the pr option, then we have to assume it's a text file
X	 */
X	if (opt_pr) {
X		do_pr_file(fname, asheet, outfd);
X		return;
X	}
X	/*
X	 * if not using pr(1), open fname and try to figure out what type of
X	 * file it is
X	 */
X	if ((fd = fopen(fname, "r")) == NULL) {
X		fprintf(stderr, "%s: cannot open %s\n",
X			MPAGE, fname);
X		perror(MPAGE);
X		return;
X	}
X	/*
X	 * check for the cutomary characters that flag a postscript file
X	 */
X	if (ps_check(fd)) {
X		/*
X		 * found the flag signaling PS input
X		 */
X		do_ps_doc(fd, asheet, outfd);
X	} else {
X		/*
X		 * no postscript flag, print the ascii text
X		 */
X		do_text_doc(fd, asheet, outfd);
X	}
X	(void)fclose(fd);
X}
X
X/*
X * do_file processes one text file into postscript, but first runs the file
X * through pr(1).
X */
Xdo_pr_file(fname, asheet, outfd)
X char *fname;
X struct sheet *asheet;
X FILE *outfd;
X{
X	FILE *fd;
X	char command[LINESIZE];
X
X	/*
X	 * build the proper command based upon a specified
X	 * header or not
X	 */
X	if (opt_doheader) {
X		(void)sprintf(command, "pr -l%d -w%d -h \"%s\" %s",
X			      asheet->sh_plength, asheet->sh_cwidth,
X			      opt_header, fname);
X	} else {
X		(void)sprintf(command, "pr -l%d -w%d %s",
X			      asheet->sh_plength, asheet->sh_cwidth,
X			      fname);
X	}
X	/*
X	 * open a pipe to the proper pr(1) command, and pr provides
X	 * us with the input
X	 */
X	if ((fd = popen(command, "r")) == NULL) {
X		fprintf(stderr, "%s: cannot create pipe for '%s'\n",
X			command);
X		perror(MPAGE);
X	} else {
X		do_text_doc(fd, asheet, outfd);
X		(void)pclose(fd);
X	}
X}
X
X/*
X * do_stdin uses do_????_doc to process the standard input
X */
Xdo_stdin(asheet, outfd)
X struct sheet *asheet;
X FILE *outfd;
X{
X	FILE *fd;
X	char command[LINESIZE];
X	char tmpfile[LINESIZE];
X	char buffer[LINESIZE];
X	int incnt, outcnt;
X
X	if (opt_pr) {
X		Debug(DB_STDIN, "%%do_stdin: pr option selects text\n", 0);
X		/*
X		 * if pr(1) is to be used we need to read the input
X		 * and pass it to a pr(1) command which will write
X		 * a temporary file; this temporary file will then
X		 * be used as input to the do_doc routine
X		 */
X		(void)strcpy(tmpfile, "/usr/tmp/mpageXXXXXX");
X		(void)mktemp(tmpfile);
X		if (opt_doheader) {
X			(void)sprintf(command, "pr -l%d -w%d -h \"%s\"> %s",
X				      asheet->sh_plength, asheet->sh_cwidth,
X				      opt_header, tmpfile);
X		} else {
X			(void)sprintf(command, "pr -l%d -w%d > %s",
X				      asheet->sh_plength, asheet->sh_cwidth,
X				      tmpfile);
X		}
X		/*
X		 * open a pipe to the pr(1) command which will create a
X		 * temporary file for convertin into PS
X		 */
X		if ((fd = popen(command, "w")) == NULL) {
X			fprintf(stderr, "%s: cannot create pipe for '%s'\n",
X				command);
X			perror(MPAGE);
X			return;
X		}
X#ifdef DEBUG
X		errno = 0;
X		Debug(DB_STDIN, "%% sizeof buffer == %d\n", sizeof buffer);
X#endif
X		/*
X		 * read input to mpage and pass it onto the pr(1) command
X		 */
X		do {
X			incnt = fread(buffer, 1, sizeof buffer, stdin);
X			outcnt = fwrite(buffer, 1, incnt, fd);
X			Debug(DB_STDIN, "%% incnt == %d,", incnt);
X			Debug(DB_STDIN, " outcnt == %d,", outcnt);
X			Debug(DB_STDIN, " errno == %d\n", errno);
X		} while (incnt && outcnt);
X		Debug(DB_STDIN, "%% Done with while\n", 0);
X		(void)pclose(fd);
X		Debug(DB_STDIN, "%% closed pipe, looking for tmpfile\n", 0);
X		/*
X		 * now open the temporary file and use do_doc to
X		 * convert it to PS
X		 */
X		if ((fd = fopen(tmpfile, "r")) == NULL) {
X			fprintf(stderr, "%s: cannot open %s\n",
X				MPAGE, tmpfile);
X			perror(MPAGE);
X		} else {
X			Debug(DB_STDIN, "%% got tmpfile, now do_doc\n", 0);
X			do_text_doc(fd, asheet, outfd);
X			(void)fclose(fd);
X		}
X		/*
X		 * tidy up by removing our temp file
X		 */
X		Debug(DB_STDIN, "%% now remove '%s'\n", tmpfile);
X		(void)unlink(tmpfile);
X	} else {
X		/*
X		 * check for the cutomary flag at the start of postscript files
X		 */
X		if (ps_check(stdin)) {
X			/*
X			 * found the flag signaling PS input
X			 */
X			Debug(DB_STDIN, "%%do_stdin: is postscript\n", 0);
X			do_ps_doc(stdin, asheet, outfd);
X		} else {
X			/*
X			 * no postscript flag, print the ascii text
X			 */
X			Debug(DB_STDIN, "%%do_stdin: not postscript\n", 0);
X			do_text_doc(stdin, asheet, outfd);
X		}
X	}
X}
X
X
X
+ END-OF-FILE mp_file.c
chmod 'u=rw,g=rw,o=r' 'mp_file.c'
echo '	-rw-rw-r--  1 kai          5779 Jan  1  1990 mp_file.c        (as sent)'
echo -n '	'
/bin/ls -l mp_file.c
echo 'Extracting mp_glob.c'
sed 's/^X//' > mp_glob.c << '+ END-OF-FILE mp_glob.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X	"@(#) $Header: mp_glob.c,v 2.7 89/08/09 11:44:01 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_glob.c,v $
X * Revision 2.7  89/08/09  11:44:01  mark
X * change the defaults so that outlines are normally printed
X * 
X * Revision 2.6  89/05/25  10:42:37  mark
X * changes to print a page count on stderr after the print job is queued.
X * 
X * Revision 2.5  89/05/25  09:50:29  mark
X * turned off all debugging.
X * 
X * Revision 2.4  89/05/25  08:58:21  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.3  89/05/22  14:40:49  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:37:51  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:18  mark
X * New Major Revision
X * 
X * Revision 1.1  89/05/22  14:21:16  mark
X * Initial revision
X *  */
X
X/*
X * to turn on debugging, define the preprocessor macro DEBUG and set
X * the variable Debug_flag to the sum of the sections to debug.
X */
X# ifdef DEBUG
Xint Debug_flag = DB_PSMPAGE;
X# endif DEBUG
X
X/*
X * some basic PS parameters
X */
Xint ps_width = 612;	/* number of points in the X direction (8.5 inches) */
Xint ps_height = 792;	/* number of points in the Y direction (11 inches) */
X
X/*
X * outlines for various page orientations and number of output pages to
X * put on printed pages
X */
Xint outline_1();
Xint outline_2();
Xint outline_4();
Xint outline_8();
X
X/*
X * the structures describe where to put the reduced pages of output on the
X * printed page.
X */
X/* base point for one page, normal aspect */
Xstruct pagepoints one_normal[] = {
X	{ xbase1, ybase1 },
X	{  0,  0 }
X};
X/* base points for two pages, normal aspect */
Xstruct pagepoints two_normal[] = {
X	{ xbase1, ytop4 },	{ xbase1 , ytop2 },
X	{  0,  0 }
X};
X/* base points for four pages, normal aspect, running reduced pages
X * read from left to right */
Xstruct pagepoints lr_four_normal[] = {
X  	{ xbase1, ybase3 },	{ xbase2, ybase3 },
X	{ xbase1, ybase1 },	{ xbase2, ybase1 },
X	{  0,  0 }
X};
X/* base points for four pages, normal aspect, running reduced pages
X * read from top to bottom (up/down) */
Xstruct pagepoints ud_four_normal[] = {
X  	{ xbase1, ybase3 },	{ xbase1, ybase1 },
X	{ xbase2, ybase3 },	{ xbase2, ybase1 },
X	{  0,  0 }
X};
X/* base points for eight pages, normal aspect, running reduced pages
X * read from left to right */
Xstruct pagepoints lr_eight_normal[] = {
X	{ xbase2, ytop4 },	{ xbase2, ytop3 },
X	{ xbase2, ytop2 },	{ xbase2, ytop1 },
X	{ xbase1, ytop4 },	{ xbase1, ytop3 },
X	{ xbase1, ytop2 },	{ xbase1, ytop1 },
X	{  0,  0 }
X};
X/* base points for eight pages, normal aspect, running reduced pages
X * read from top to bottom (up/down) */
Xstruct pagepoints ud_eight_normal[] = {
X	{ xbase2, ytop4 },	{ xbase1, ytop4 },
X	{ xbase2, ytop3 },	{ xbase1, ytop3 },
X	{ xbase2, ytop2 },	{ xbase1, ytop2 },
X	{ xbase2, ytop1 },	{ xbase1, ytop1 },
X	{  0,  0 }
X};
X/* base point for one page, in landscape */
Xstruct pagepoints one_landscape[] = {
X  	{ xbase1, ytop4 },
X	{  0,  0 }
X};
X/* base points for two pages, in landscape */
Xstruct pagepoints two_landscape[] = {
X  	{ xbase1, ybase3 },	{ xbase1, ybase1 },
X	{  0,  0 }
X};
X/* base points for four pages, in landscape, running reduced pages
X * read from left to right */
Xstruct pagepoints lr_four_landscape[] = {
X  	{ xbase2, ytop4 },	{ xbase2, ytop2 },
X	{ xbase1, ytop4 },	{ xbase1, ytop2 },
X	{  0,  0 }
X};
X/* base points for four pages, in landscape, running reduced pages
X * read from top to bottom (up/down) */
Xstruct pagepoints ud_four_landscape[] = {
X  	{ xbase2, ytop4 },	{ xbase1, ytop4 },
X  	{ xbase2, ytop2 },	{ xbase1, ytop2 },
X	{  0,  0 }
X};
X/* base points for eight pages, in landscape, running reduced pages
X * read from left to right */
Xstruct pagepoints lr_eight_landscape[] = {
X	{ xbase1, ybase4 },	{ xbase2, ybase4 },
X	{ xbase1, ybase3 },	{ xbase2, ybase3 },
X	{ xbase1, ybase2 },	{ xbase2, ybase2 },
X	{ xbase1, ybase1 },	{ xbase2, ybase1 },
X	{  0,  0 }
X};
X/* base points for eight pages, in landscape, running reduced pages
X * read from top to bottom (up/down) */
Xstruct pagepoints ud_eight_landscape[] = {
X	{ xbase1, ybase4 },	{ xbase1, ybase3 },
X	{ xbase1, ybase2 },	{ xbase1, ybase1 },
X	{ xbase2, ybase4 },	{ xbase2, ybase3 },
X	{ xbase2, ybase2 },	{ xbase2, ybase1 },
X	{  0,  0 }
X};
X
X/* list of sheets (printed page formats) for
X * left to right reading, in normal aspect */
Xstruct sheet lr_normal[] = {
X/* 0 */	80, 66, xwid1, yht1,    0, outline_1, one_normal,
X/* 1 */	80, 66, yht2,  xwid1, -90, outline_2, two_normal,
X/* 2 */	80, 66, xwid2, yht2,    0, outline_4, lr_four_normal,
X/* 3 */	80, 66, yht4,  xwid2, -90, outline_8, lr_eight_normal,
X};
X
X/* list of sheets (printed page formats) for
X * top to bottom reading, in normal aspect */
Xstruct sheet ud_normal[] = {
X/* 0 */	80, 66, xwid1, yht1,    0, outline_1, one_normal,
X/* 1 */	80, 66, yht2,  xwid1, -90, outline_2, two_normal,
X/* 2 */	80, 66, xwid2, yht2,    0, outline_4, ud_four_normal,
X/* 3 */	80, 66, yht4,  xwid2, -90, outline_8, ud_eight_normal,
X};
X
X/* list of sheets (printed page formats) for
X * left to right reading, in landscape */
Xstruct sheet lr_landscape[] = {
X/* 0 */	132, 52, yht1,  xwid1, -90, outline_1, one_landscape,
X/* 1 */	132, 52, xwid1, yht2,    0, outline_2, two_landscape,
X/* 2 */	132, 52, yht2,  xwid2, -90, outline_4, lr_four_landscape,
X/* 3 */	132, 52, xwid2, yht4,    0, outline_8, lr_eight_landscape,
X};
X
X/* list of sheets (printed page formats) for
X * top to bottom reading, in landscape */
Xstruct sheet ud_landscape[] = {
X/* 0 */	132, 52, yht1,  xwid1, -90, outline_1, one_landscape,
X/* 1 */	132, 52, xwid1, yht2,    0, outline_2, two_landscape,
X/* 2 */	132, 52, yht2,  xwid2, -90, outline_4, ud_four_landscape,
X/* 3 */	132, 52, xwid2, yht4,    0, outline_8, ud_eight_landscape,
X};
X
X/* array of sheet lists for left to right reading printed pages */
Xstruct sheet *left_right[] = {
X	lr_normal,
X	lr_landscape
X  };
X
X/* arrays for top to bottom reading printed pages */
Xstruct sheet *up_down[] = {
X	ud_normal,
X	ud_landscape
X  };
X
X/*
X * Variables for holding the chosen options,  The defaults are set here.
X * the sheetlist pointer is set to point to the array for either up/down
X * or left/right reading.  This array is index by sheetorder, and then
X * sheetindex.  sheetindex encodes the number of reduced pages per printed
X * page and indexes into the sheet list (0 = 1 page, 1 = two pages, 2 =
X * four pages, 3 = eight pages).
X */
Xstruct sheet **sheetlist;/* array of sheet lists (up/down or left/right) */
Xint sheetaspect = NORMAL;/* either normal or landscape */
Xint sheetorder = UPDOWN;/* up/down or left/right flag */
Xint sheetindex = 2;	/* index to number of pages of output per printed */
Xint pg_sheetmargin = 20;/* non-printable border on sheet */
Xint pg_pagemargin = 4;  /* border for pages */
Xint fsize = 12;		/* font scale size */
Xint opt_pr = 0;		/* boolean, if true use pr(1) to format output */
Xint opt_lines = 0;	/* number of lines to fit on an reduced page */
Xint opt_width = 0;	/* number of columns to fit on an reduced page */
Xint opt_doheader = 0;	/* have a head for pr's -h option */
Xint opt_a4 = 0;		/* default to US paper */
Xint opt_outline = 1;	/* don't normally outline the pages */
Xint opt_verbose = 1;	/* by default, print a count of pages produced */
Xchar opt_header[LINESIZE]; /* the header for pr's -h option */
Xchar outcommand[LINESIZE]; /* the command which is the output filter */
X# ifndef DEBUG
Xchar printer[LINESIZE] = "PostScript";	/* the printer(argument to lpr -P??) */
X# else
Xchar printer[LINESIZE] = "";	/* the printer (argument to lpr -P??) */
X# endif DEBUG
X/*
X * various global information
X */
Xchar MPAGE[] = "mpage";	/* program name */
Xint ps_pagenum = 0;	/* current page count */
Xchar usage[] =		/* usage string */
X"mpage [-aulnp1248] [-Llines] [-Wwidth] [-hheader] [-Pprinter] [files...]\n";
X
+ END-OF-FILE mp_glob.c
chmod 'u=rw,g=rw,o=r' 'mp_glob.c'
echo '	-rw-rw-r--  1 kai          8638 Jan  1  1990 mp_glob.c        (as sent)'
echo -n '	'
/bin/ls -l mp_glob.c
echo 'Extracting mp_head.h'
sed 's/^X//' > mp_head.h << '+ END-OF-FILE mp_head.h'
X/* $Header: mp_head.h,v 2.8 89/05/25 10:42:07 mark Exp $ */
X
X/*
X * mpage:	A program to reduce pages of print so that several pages
X * 	  	of output appear on one sheet of paper.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_head.h,v $
X * Revision 2.8  89/05/25  10:42:07  mark
X * changes to print a page count on stderr after the print job is queued.
X * 
X * Revision 2.7  89/05/25  10:25:13  mark
X * add new debugging keyword for tracking the processing of postscript
X * documents.
X * 
X * Revision 2.6  89/05/25  10:23:17  mark
X * fixed typo in rcs keyword
X *  */
X
X/*
X * Through-out the program comments I have tried to refer to pages a the
X * logical printed page of text that gets reduced.  Sheets refer to physical
X * pieces of paper.  Hence, mulitple pages appear on a sheet.  "page" is a
X * logical or virtual entity, and "sheet" is physical entity.
X */
X
Xchar *ctime();
XFILE *popen();
Xchar *strcpy();
Xchar *mktemp();
Xtime_t time();
X
X# define	TRUE		1
X# define	FALSE		0
X
X# define	LINESIZE	1024
X
X# define	FILE_EOF	1
X# define	FILE_MORE	2
X
X# define	LINE_MORE	5
X# define	LINE_EOF_NOW	4
X# define	LINE_EOF	3
X# define	LINE_OVERPRINT	2
X# define	LINE_BLANK	1
X
X
X/*
X * to turn on debugging, define the preprocessor macro DEBUG and set
X * the variable Debug_flag to the sum of the sections to debug.
X */
X# ifdef DEBUG
X# define Debug(f,s,a)	if (Debug_flag & f) printf(s,a)
X# define DB_GETLINE	0x0000001
X# define DB_ONEPAGE	0x0000002
X# define DB_STDIN	0x0000004
X# define DB_PSDOC	0x0000008
X# define DB_PSPAGE	0x0000010
X# define DB_PSCHECK	0x0000020
X# define DB_PSROFF	0x0000040
X# define DB_PSMPAGE	0x0000080
X# define DB_POINTS	0x0000100
X# define DB_POST	0x0000200
X# define DB_UNUSED	0x0000400
Xextern int Debug_flag;
Xextern int errno;
X# else DEBUG
X# define Debug(f,s,a)
X# endif DEBUG
X
X/*
X * defintions for sorting out types of postscript input
X */
X# define	PS_NONE		0
X# define	PS_PSROFF	1
X# define	PS_MPAGE	2
X# define	PS_CONFORM	3
X# define	PS_OTHER	4
X
X/*
X * "Conforming" postscript flag string  (remember ps_check strips
X * the "%!" flag from PS files
X */
X# define	PS_FLAG		"PS-Adobe-"
X
X/*
X * some basic PS parameters
X */
Xextern int ps_width;	/* number of points in the X direction (8.5 inches) */
Xextern int ps_height;	/* number of points in the Y direction (11 inches) */
X
X/*
X * a sheet describes the measurements and orientatation of a page for
X * use in constructing a sheet preabmles.
X */
Xstruct sheet {
X	int sh_cwidth;		/* number of characters across a page */
X	int sh_plength;		/* number of lines down a page */
X	int (*sh_width)();	/* postscript width across a printed page */
X	int (*sh_height)();	/* postscript height of a printed page */
X	int sh_rotate;		/* angle to rotate the page */
X	int (*sh_outline)();	/* text to print as outline for */
X				/*    the printed sheet*/
X	struct	pagepoints *sh_pagepoints; /* where to put pages on */
X					     /*    the printed sheet */
X};
X
X/*
X * simple x and y coordinates for putting pages of output on printed sheet
X */
Xstruct pagepoints {
X	int (*pp_origin_x)();
X	int (*pp_origin_y)();
X};
X
X/* array of sheets where pages are ordered for left to right reading */
Xextern struct sheet *left_right[];
X
X/* arrays for sheets where pages are ordered for top to bottom reading  */
Xextern struct sheet *up_down[];
X
X/* definitions for aspect and reading directions */
X# define NORMAL		0
X# define LANDSCAPE	1
X# define UPDOWN		0
X# define LEFTRIGHT	1
X
X/*
X * Variables for holding the chosen options,  The defaults are set here.
X * the sheetlist pointer is set to point to the array for either up/down
X * or left/right reading.  This array is index by sheetorder, and then
X * sheetindex.  sheetindex encodes the number of reduced pages per printed
X * sheet and indexes into the sheet list (0 = 1 page, 1 = two pages, 2 =
X * four pages, 3 = eight pages).
X */
Xextern struct sheet **sheetlist;/* array of sheet lists (up/down or left/right) */
Xextern int sheetaspect;		/* either normal or landscape */
Xextern int sheetorder;		/* up/down or left/right flag */
Xextern int sheetindex;		/* index to number of pages of sheet */
Xextern int pg_sheetmargin;	/* non-printable border on sheet */
Xextern int pg_pagemargin;	/* border for pages */
Xextern int fsize;		/* font scale size */
Xextern int opt_pr;		/* boolean, if true use pr to format output */
Xextern int opt_lines;		/* number of lines to fit on an reduced page */
Xextern int opt_width;		/* number of columns to fit on reduced page */
Xextern int opt_doheader;	/* have a head for pr's -h option */
Xextern int opt_a4;		/* sheets are A4 sized, european paper */
Xextern int opt_outline;		/* print a nice outline around pages */
Xextern int opt_verbose;		/* print a count of pages sent to printer */
Xextern char opt_header[LINESIZE]; /* the header for pr's -h option */
Xextern char outcommand[LINESIZE]; /* the command which is the output filter */
Xextern char printer[LINESIZE];	/* the printer (argument to lpr -P??) */
X
X/*
X * various global information
X */
Xextern char MPAGE[];		/* program name */
Xextern int ps_pagenum;		/* current page count */
Xextern char usage[];		/* usage string */
X
X/*
X * function declarations for the page point computation functions
X */
Xint xbase1(), xbase2();
Xint ybase1(), ybase2(), ybase3(), ybase4();
Xint ytop1(), ytop2(), ytop3(), ytop4();
Xint xwid1(), xwid2(), xwid4(), xwid8();
Xint yht1(), yht2(), yht4(), yht8();
X
Xstatic char *rcs_header_id =
X	"@(#)	$Header: mp_head.h,v 2.8 89/05/25 10:42:07 mark Exp $";
X
+ END-OF-FILE mp_head.h
chmod 'u=rw,g=rw,o=r' 'mp_head.h'
echo '	-rw-rw-r--  1 kai          5988 Jan  1  1990 mp_head.h        (as sent)'
echo -n '	'
/bin/ls -l mp_head.h
echo 'Extracting mp_main.c'
sed 's/^X//' > mp_main.c << '+ END-OF-FILE mp_main.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X	"@(#) $Header: mp_main.c,v 2.5 89/05/25 10:42:35 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_main.c,v $
X * Revision 2.5  89/05/25  10:42:35  mark
X * changes to print a page count on stderr after the print job is queued.
X * 
X * Revision 2.4  89/05/25  08:58:25  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.3  89/05/22  14:40:28  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:37:30  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:15  mark
X * New Major Revision
X * 
X * Revision 1.1  89/05/22  14:19:56  mark
X * Initial revision
X *  */
X
Xmain(argc,argv)
X int argc;
X char **argv;
X{
X	FILE *outfd;
X	int currarg;
X	struct sheet *thelist;
X	struct sheet *thesheet;
X
X	/*
X	 * examine the environment for PRINTER and MPAGE environment variables
X	 */
X	if (do_env() == 0) {
X		fprintf(stderr, usage, MPAGE);
X		exit(1);
X	}
X		
X	currarg = do_args(argc, argv, 0);
X	if (currarg < 0) {
X		fprintf(stderr, usage, MPAGE);
X		exit(1);
X	}
X
X	/*
X	 * if a printer was specified then create a lpr command using
X	 * the specified printer, else output goes to standard out
X	 */
X	if (*printer) {
X		(void)sprintf(outcommand, "ucb /usr/ucb/lpr -P%s", printer);
X		if ((outfd = popen(outcommand, "w")) == NULL) {
X			fprintf(stderr, "%s: cannot create pipe for '%s'\n",
X				outcommand);
X			perror(MPAGE);
X		}
X	} else {
X		outfd = stdout;
X	}
X
X	/*
X	 * pick the array of sheet lists based upon the specified option
X	 */
X	if (sheetorder == UPDOWN) {
X		sheetlist = up_down;
X	} else {
X		sheetlist = left_right;
X	}
X
X	/*
X	 * from the array of sheet lists pick the proper sheet list for
X	 * the given sheetaspect, then select the proper sheet format
X	 * for the given number of redueced pages per output page
X	 */
X	thelist = sheetlist[sheetaspect];
X	thesheet = &(thelist[sheetindex]);
X
X	/*
X	 * if either lines or columns were specified as options, over
X	 * the default sheets idea of the number of line or cloumns
X	 * per reduced page
X	 */
X	if (opt_lines > 0) {
X		thesheet->sh_plength = opt_lines;
X	}
X	if (opt_width > 0) {
X		thesheet->sh_cwidth = opt_width;
X	}
X
X	/*
X	 * if there are arguments left over after processing options, then
X	 * these are names of files to process, else process the standard
X	 * input
X	 */
X	if (currarg < argc) {
X		ps_title(argv[currarg], outfd);
X		for ( ;currarg < argc; currarg++) {
X			do_file(argv[currarg], thesheet, outfd);
X		}
X	} else {
X		ps_title("<stdin>", outfd);
X		do_stdin(thesheet, outfd);
X	}
X	/*
X	 * having processed all input, print out a PS trailer
X	 * (timeing stuff stolen from old adobe troff stuff)
X	 */
X	fprintf(outfd, "%%%%Trailer\n");
X	fprintf(outfd, "statusdict begin jobname print flush");
X	fprintf(outfd, " (: Job finished:\\n) print\n");
X	fprintf(outfd, "(\\tmpage time (s) = ) print flush usertime ");
X	fprintf(outfd, "mp_stm sub 1000 div ==\n(\\tmpage pages = ) print ");
X	fprintf(outfd, "flush pagecount mp_pgc sub ==\nend flush\n");
X	fprintf(outfd, "%%%%Pages: %d\n", ps_pagenum);
X	if (opt_verbose) {
X		fprintf(stderr, "[%s: %d pages, ", MPAGE, ps_pagenum);
X		if (*printer == 0) {
X			fprintf(stderr, "on <stdout>]\n");
X		} else {
X			fprintf(stderr, "printer %s]\n", printer);
X		}
X	}
X	/*
X	 * proper clean up to make sure the pipe is flushed
X	 */
X	if (*printer) {
X		(void)pclose(outfd);
X	}
X	return 0;
X}
X
X/*
X * ps_title prints a post script header suitable for PS processors
X */
Xps_title(name, outfd)
X char *name;
X FILE *outfd;
X{
X	time_t curr_time;
X	char *time_str;
X
X	fprintf(outfd, "%%!PS-Adobe-1.0\n");
X	fprintf(outfd, "%%%%DocumentFonts: Courier\n");
X	fprintf(outfd, "%%%%Title: %s (mpage)\n", name, MPAGE);
X	fprintf(outfd, "%%%%Creator: %s\n", MPAGE);
X	(void)time(&curr_time);
X	time_str = ctime(&curr_time);
X	fprintf(outfd, "%%%%CreationDate: %s",time_str);
X	fprintf(outfd, "%%%%Pages: (atend)\n");
X	fprintf(outfd, "%%%%BoundingBox: 20 20 596 776\n");
X	fprintf(outfd, "%%%%EndComments\n\n");
X	fprintf(outfd, "/mp_stm usertime def\n");
X	fprintf(outfd, "/mp_pgc statusdict begin pagecount end def\n");
X	fprintf(outfd, "statusdict begin /jobname (%s) def end\n", name);
X}
X
+ END-OF-FILE mp_main.c
chmod 'u=rw,g=rw,o=r' 'mp_main.c'
echo '	-rw-rw-r--  1 kai          4944 Jan  1  1990 mp_main.c        (as sent)'
echo -n '	'
/bin/ls -l mp_main.c
echo 'Extracting mp_page.c'
sed 's/^X//' > mp_page.c << '+ END-OF-FILE mp_page.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X	"@(#) $Header: mp_page.c,v 2.4 89/05/25 08:58:28 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_page.c,v $
X * Revision 2.4  89/05/25  08:58:28  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.3  89/05/22  14:41:01  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:38:04  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:27  mark
X * New Major Revision
X * 
X * Revision 1.1  89/05/22  14:25:52  mark
X * Initial revision
X *  */
X
Xset_page()
X{
X	if (opt_a4) {
X		ps_width = 596;
X		ps_height = 842;
X	} else {
X		ps_width = 612;
X		ps_height = 792;
X	}
X}
X
Xxbase1()
X{
X	Debug(DB_POINTS, "%%xbase1: %d\n", pg_sheetmargin + pg_pagemargin);
X	return pg_sheetmargin + pg_pagemargin;
X}
X
Xxbase2()
X{
X	Debug(DB_POINTS, "%%xbase2: %d\n",  (ps_width / 2) + pg_pagemargin);
X	return (ps_width / 2) + pg_pagemargin;
X}
X
Xybase1()
X{
X	Debug(DB_POINTS, "%%ybase1: %d\n", pg_sheetmargin + pg_pagemargin);
X	return pg_sheetmargin + pg_pagemargin;
X}
X
Xybase2()
X{
X	Debug(DB_POINTS, "%%ybase2: %d\n",
X	      (((ps_height / 2) - pg_sheetmargin) / 2)
X	      + pg_sheetmargin + pg_pagemargin);
X	return (((ps_height / 2) - pg_sheetmargin) / 2)
X	  + pg_sheetmargin + pg_pagemargin;
X}
X
Xybase3()
X{
X	Debug(DB_POINTS, "%%ybase3: %d\n", (ps_height / 2) + pg_pagemargin);
X	return (ps_height / 2) + pg_pagemargin;
X}
X
Xybase4()
X{
X	Debug(DB_POINTS, "%%ybase4: %d\n",
X	      (((ps_height / 2) - pg_sheetmargin) / 2)
X	      + (ps_height / 2) + pg_pagemargin);
X	return (((ps_height / 2) - pg_sheetmargin) / 2)
X	  + (ps_height / 2) + pg_pagemargin;
X}
X
Xytop1()
X{
X	Debug(DB_POINTS, "%%ytop1: %d\n", ybase1() + yht4());
X	return ybase1() + yht4();
X}
X
Xytop2()
X{
X	Debug(DB_POINTS, "%%ytop2: %d\n", ybase2() + yht4());
X	return ybase2() + yht4();
X}
X
Xytop3()
X{
X	Debug(DB_POINTS, "%%ytop3: %d\n", ybase3() + yht4());
X	return ybase3() + yht4();
X}
X
Xytop4()
X{
X	Debug(DB_POINTS, "%%ytop4: %d\n", ybase4() + yht4());
X	return ybase4() + yht4();
X}
X
Xxwid1()
X{
X	Debug(DB_POINTS, "%%xwid1: %d\n",
X	      ps_width - ((2 * pg_sheetmargin) + (2 * pg_pagemargin)));
X	return ps_width - ((2 * pg_sheetmargin) + (2 * pg_pagemargin));
X}
X
Xxwid2()
X{
X	Debug(DB_POINTS, "%%xwid2: %d\n",
X	      (ps_width / 2) - (pg_sheetmargin + (2 * pg_pagemargin)));
X	return (ps_width / 2) - (pg_sheetmargin + (2 * pg_pagemargin));
X}
X
Xyht1()
X{
X	Debug(DB_POINTS, "%%yht1: %d\n",
X	      ps_height - ((2 * pg_sheetmargin) + (2 * pg_pagemargin)));
X	return ps_height - ((2 * pg_sheetmargin) + (2 * pg_pagemargin));
X}
X
Xyht2()
X{
X	Debug(DB_POINTS, "%%yht2: %d\n",
X	      (ps_height / 2) - (pg_sheetmargin + (2 * pg_pagemargin)));
X	return (ps_height / 2) - (pg_sheetmargin + (2 * pg_pagemargin));
X}
X
Xyht4()
X{
X	Debug(DB_POINTS, "%%yht4: %d\n",
X	      (((ps_height / 2) - pg_sheetmargin) / 2) - (2 * pg_pagemargin));
X	return (((ps_height / 2) - pg_sheetmargin) / 2) - (2 * pg_pagemargin);
X}
X
Xoutline_1(outfd)
X FILE *outfd;
X{
X	/* one page outline */
X	fprintf(outfd, "0 setlinewidth\n");
X        fprintf(outfd, "%3d %3d moveto\n", pg_sheetmargin, pg_sheetmargin);
X	fprintf(outfd, "%3d %3d lineto\n", pg_sheetmargin,
X		ps_height - pg_sheetmargin);
X	fprintf(outfd, "%3d %3d lineto\n", ps_width - pg_sheetmargin,
X		ps_height - pg_sheetmargin);
X	fprintf(outfd, "%3d %3d lineto\n", ps_width - pg_sheetmargin,
X		pg_sheetmargin);
X	fprintf(outfd, "closepath stroke\n");
X}
X
Xoutline_2(outfd)
X FILE *outfd;
X{
X	/* two page outline */
X	outline_1(outfd);
X	fprintf(outfd, "%3d %3d moveto\n", pg_sheetmargin, ps_height / 2);
X	fprintf(outfd, "%3d %3d lineto\n",  ps_width - pg_sheetmargin,
X		ps_height / 2);
X	fprintf(outfd, "stroke\n");
X}
X
Xoutline_4(outfd)
X FILE *outfd;
X{
X	/* four page outline */
X	outline_2(outfd);
X	fprintf(outfd, "%3d %3d moveto\n", ps_width / 2 , pg_sheetmargin);
X	fprintf(outfd, "%3d %3d lineto\n", ps_width / 2 ,
X		ps_height - pg_sheetmargin);
X	fprintf(outfd, "stroke\n");
X}
X
Xoutline_8(outfd)
X FILE *outfd;
X{
X	/* eight page outline */
X	outline_4(outfd);
X	fprintf(outfd, "%3d %3d moveto\n", pg_sheetmargin,
X		((ps_height / 2) - pg_sheetmargin) / 2 + pg_sheetmargin);
X	fprintf(outfd, "%3d %3d lineto\n", ps_width - pg_sheetmargin,
X		((ps_height / 2) - pg_sheetmargin) / 2 + pg_sheetmargin);
X	fprintf(outfd, "stroke\n");
X	fprintf(outfd, "%3d %3d moveto\n", pg_sheetmargin,
X		((ps_height / 2) - pg_sheetmargin) / 2 + (ps_height / 2));
X	fprintf(outfd, "%3d %3d lineto\n", ps_width - pg_sheetmargin,
X		((ps_height / 2) - pg_sheetmargin) / 2 + (ps_height / 2));
X	fprintf(outfd, "stroke\n");
X}
X
Xmp_outline(outfd, asheet)
X FILE *outfd;
X struct sheet *asheet;
X{
X	if (opt_outline) {
X		(*asheet->sh_outline)(outfd);
X	}
X}
X
+ END-OF-FILE mp_page.c
chmod 'u=rw,g=rw,o=r' 'mp_page.c'
echo '	-rw-rw-r--  1 kai          5453 Jan  1  1990 mp_page.c        (as sent)'
echo -n '	'
/bin/ls -l mp_page.c
echo 'Extracting mp_post.c'
sed 's/^X//' > mp_post.c << '+ END-OF-FILE mp_post.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X  "@(#) $Header: mp_post.c,v 2.9 89/08/09 11:30:39 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed page.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_post.c,v $
X * Revision 2.9  89/08/09  11:30:39  mark
X * fixed bug in get_psstr.  it did not return a value if it failed
X * to find a string in parparentheses.  the code "lucked-out" on risc
X * machines but failed on machines that use different locations for
X * the function arguments and return values, (680x0s and VAXen).
X * (First time I've seen poor coding work on a RISC but be-fuddle a CISC.
X * 
X * Revision 2.8  89/05/25  10:25:34  mark
X * add new debugging keyword for tracking the processing of postscript
X * documents.
X * 
X * Revision 2.7  89/05/25  10:20:38  mark
X * changed the format of debugging prints in the PS code.
X * 
X * Revision 2.6  89/05/25  08:58:30  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.5  89/05/25  08:49:49  mark
X * added code to disable showpage durring the printing of postscript
X * documents.  we define our own routine to do the showpage.
X * 
X * Revision 2.4  89/05/23  09:39:36  mark
X * Changes to deal with %%BeginSetup sections.  We now scan all the way
X * to the first %%Page comment assuming this will cover everything.
X * Actually we should do something more intelligent, saving the %%BeginSetup
X * section and putting after our page reduction but before the page is
X * printed.
X * 
X * Revision 2.3  89/05/22  14:40:56  mark
X * Fixed the type-o in the rcs identification string
X * 
X * Revision 2.2  89/05/22  14:37:56  mark
X * Added rcs identification usable with the "what" program
X * 
X * Revision 2.1  89/05/22  14:31:22  mark
X * New Major Revision
X * 
X * Revision 1.2  89/05/22  13:49:58  mark
X * placed the log keywork after the initial notice
X * 
X * Revision 1.1  89/05/22  13:48:43  mark
X * Initial revision
X *  */
X
X/*
X * character spaces used throughout for holding the current line from
X * the input file
X */
Xstatic char currline[LINESIZE];
X/*
X * for ps documents, used to remember if we have come across the
X * tailer section.  reset at the beginning of processing for each file
X */
Xstatic int ps_at_trailer;
X
X/*
X * this is the type of postscript document we are processing
X */
Xstatic int ps_posttype;
X
X/*
X * peek at the first two chacters on the open file and check for the
X * two character postscript flag "%!".  If the file is not postscript
X * then the characters are pushed back into the input stream (hopefully).
X */
Xps_check(infd)
X FILE *infd;
X{
X	int firstchar;
X	int secondchar;
X	
X	Debug(DB_PSCHECK, "%%ps_check: in ps_check\n", 0);
X	/*
X	 * eliminate blank files
X	 */
X	if ((firstchar = fgetc(infd)) == EOF) {
X		Debug(DB_PSCHECK, "%%ps_check: file is blank\n", 0);
X		return 0;
X	}
X	/*
X	 * eliminate non-postscript files
X	 */
X	if (firstchar != '%') {
X		Debug(DB_PSCHECK, "%ps_check: 1st char is '%c' not '%'\n",
X		      firstchar);
X		if (ungetc(firstchar, infd) == EOF) {
X			fprintf(stderr, "%s: Lost first character of file ",
X				MPAGE);
X			fprintf(stderr, "while checking for postscript.");
X		}
X		return 0;
X	}
X	Debug(DB_PSCHECK, "%%ps_check: 1st char is '%c'\n", firstchar);
X	/*
X	 * eliminate one character files (containing only a %)
X	 */
X	if ((secondchar = fgetc(infd)) == EOF) {
X		Debug(DB_PSCHECK, "%%ps_check: no second char\n", 0);
X		if (ungetc(firstchar, infd) == EOF) {
X			fprintf(stderr, "%s: Lost first character of file ",
X				MPAGE);
X			fprintf(stderr, "while checking for postscript.");
X		}
X		return 0;
X	}
X	/*
X	 * eliminate files that don't have the full two character
X	 * sequence of "%!".
X	 */
X	if (secondchar != '!') {
X		Debug(DB_PSCHECK, "%%ps_check: 2nd char is '%c' not '!'\n",
X		      secondchar);
X		if (ungetc(secondchar, infd) == EOF) {
X			fprintf(stderr, "%s: Lost first two characters of ",
X				MPAGE);
X			fprintf(stderr, "file while checking for postscript.");
X			return 0;
X		}
X		if (ungetc(firstchar, infd) == EOF) {
X			fprintf(stderr, "%s: Lost first character of file ",
X				MPAGE);
X			fprintf(stderr, "while checking for postscript.");
X		}
X		return 0;
X	}
X	/*
X	 * for post script files the first two characters (the "%!") are
X	 * digested by this routine.  It's just easier than dealing
X	 * with the problems encounted if the characters can't be ungetc'ed.
X	 */
X	Debug(DB_PSCHECK, "%%ps_check: 2nd char is '%c'\n", secondchar);
X	Debug(DB_PSCHECK, "%%ps_check: input is postscript\n", 0);
X	return 1;
X}
X
Xps_gettype(fd, outfd)
X FILE *fd;
X FILE *outfd;
X{
X	int type_known, ps_type, end_comments;
X	char *get_psstr();
X
X	Debug(DB_PSDOC, "%%ps_gettype: in ps_gettype\n", 0);
X	/*
X	 * error check for truncated files
X	 */
X	if (fgets(currline, LINESIZE-1, fd) == NULL) {
X		Debug(DB_PSDOC, "%%ps_gettype: got eof on first line\n", 0);
X		return PS_NONE;
X	}
X	/*
X	 * check for non-conforming postscript
X	 */
X	if (strncmp(currline, PS_FLAG, strlen(PS_FLAG)) != 0) {
X		Debug(DB_PSDOC, "%%ps_gettype: no match PS_FLAG \"%s\"\n",
X		      currline);
X		return PS_OTHER;
X	}
X	/*
X	 * we have some form of conforming postscript, try to identify the
X	 * type
X	 */
X	Debug(DB_PSDOC, "%%ps_gettype: conforming postscript\n", 0);
X	end_comments = 0;
X	type_known = 0;
X	while (!end_comments) {
X		/*
X		 * if we get end of file then we assume non-conforming PS
X		 */
X		if (fgets(currline, LINESIZE-1, fd) == NULL) {
X			Debug(DB_PSDOC, "%%ps_gettype: eof in comments\n", 0);
X			return PS_OTHER;
X		}
X		/*
X		 * if we have run out of leading comments then assume 
X		 * conforming PS (because we had a valid "%!" line)
X		 */
X		if (currline[0] != '%') {
X			Debug(DB_PSDOC, "%%ps_gettype: eof in comments\n", 0);
X			fprintf(outfd, "%s", currline);
X			return PS_CONFORM;
X		}
X		/*
X		 * print out the comment line with an extra % to disguise
X		 * the comment
X		 */
X		fprintf(outfd, "%%%s", currline);
X		/*
X		 * check for the end of the leading comments section
X		 */
X		if (strncmp(currline, "%%EndComments", 13) == 0) {
X			end_comments = 1;
X		}
X		/*
X		 * once we know the type of PS, we no longer need to keep
X		 * checking.
X		 */
X		if (type_known) {
X			continue;
X		}
X		/*
X		 * check for mpage output
X		 */
X		if (strncmp(currline, "%%Creator: mpage", 16) == 0) {
X			Debug(DB_PSDOC, "%%ps_gettype: mpage document\n", 0);
X			type_known = 1;
X			ps_type = PS_MPAGE;
X		}
X		/*
X		 * check for psroff output
X		 */
X		if (strncmp(currline, "%%Title: ", 9) == 0) {
X			if (strcmp(get_psstr(currline), "ditroff") == 0) {
X				Debug(DB_PSDOC, "%%ps_gettype: psroff\n", 0);
X				type_known = 1;
X				ps_type = PS_PSROFF;
X			}
X		}
X	}
X	if (type_known) {
X		return ps_type;
X	}
X	Debug(DB_PSDOC, "%%ps_gettype: unknow type conforming PS\n", 0);
X	return PS_CONFORM;
X}
X
X/*
X * get_psstr will extract (and return a pointer to) a string in parentheses.
X * it is used for checking for psroff (ditroff) postscript.
X */
Xchar *get_psstr(line)
X char *line;
X{
X	char space[LINESIZE];
X	char *p1, *p2;
X
X	p1 = line;
X	while (*p1) {
X		if (*p1 == '(') {
X			p1++;
X			p2 = space;
X			while (*p1 && (*p1 != ')')) {
X				*p2++ = *p1++;
X			}
X			*p2 = 0;
X			return space;
X		}
X		p1++;
X	}
X	return line;
X}
X	
Xdo_ps_doc(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	Debug(DB_PSDOC, "%%do_ps_doc: postscript document\n", 0);
X	ps_posttype = ps_gettype(fd,outfd);
X	Debug(DB_PSDOC, "%%do_ps_doc: document type is %d\n", ps_posttype);
X	switch(ps_posttype) {
X	case PS_NONE:
X		/*
X		 * why bother?
X		 */
X		break;
X	default:
X		do_post_doc(fd, asheet, outfd);
X		break;
X	}
X}
X
Xdo_post_doc(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	ps_at_trailer = FALSE;
X	Debug(DB_POST, "%%do_post_doc: prolog\n", 0);
X	ps_copyprolog(fd, outfd);
X	Debug(DB_POST, "%%do_post_doc: pages\n", 0);
X	/*
X	 * while there is still input, print pages
X	 */
X	while (do_post_sheet(fd, asheet, outfd) != FILE_EOF)
X	  ;
X	Debug(DB_POST, "%%do_post_doc: trailer\n", 0);
X	do_roff_tailer(fd, outfd);
X}
X
Xdo_other_doc(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	ps_at_trailer = FALSE;
X	ps_copyprolog(fd, outfd);
X}
X
Xps_copyprolog(fd, outfd)
X FILE *fd;
X FILE *outfd;
X{
X	char *rtn;
X
X	Debug(DB_PSDOC, "%%ps_copyprolog: adding mpage prolog\n", 0);
X	fprintf(outfd, "/showsheet { showpage } bind def\n");
X	fprintf(outfd, "/showpage { } def\n");
X	Debug(DB_PSDOC, "%%ps_copyprolog: copying prolog\n", 0);
X	if (ps_posttype == PS_PSROFF) {
X		Debug(DB_PSDOC, "%%ps_copyprolog: calling ps_roff_prolog\n",0);
X		ps_roff_copyprolog(fd, outfd);
X		return;
X	}
X	rtn = fgets(currline, LINESIZE-1, fd);
X	while (rtn) {
X		if (strncmp(currline, "%%Page:", 7) == 0) {
X			fprintf(outfd, "%% %s", currline);
X			return;
X		}
X		fprintf(outfd, "%s", currline);
X		rtn = fgets(currline, LINESIZE-1, fd);
X	}
X	Debug(DB_PSDOC, "%%ps_copyprolog: eof before %%%%EndProlog\n", 0);
X	fprintf(outfd, "%%%%EndProlog\n");
X}
X
Xps_roff_copyprolog(fd, outfd)
X FILE *fd;
X FILE *outfd;
X{
X
X	Debug(DB_PSDOC, "%%ps_roff_copyprolog: copying psroff prolog\n", 0);
X	while(fgets(currline, LINESIZE-1, fd) != NULL) {
X		if (strcmp(currline, "xi\n") == 0) {
X			fprintf(outfd, "%%%s", currline); 
X		} else if (strncmp(currline, "%%Page:", 7) == 0) {
X			fprintf(outfd, "/p { } def\n");
X			fprintf(outfd, "/xt { } def\n");
X			fprintf(outfd, "/xs { } def\n");
X			fprintf(outfd, "%% %s", currline);
X			Debug(DB_PSDOC, "%%ps_copyprolog: Done \n", 0);
X			return;
X		} else {
X			fprintf(outfd, "%s", currline);
X		}
X
X	}
X	Debug(DB_PSDOC, "%%ps_copyprolog: eof before %%%%EndProlog\n", 0);
X	fprintf(outfd, "/p { } def\n");
X	fprintf(outfd, "/xt { } def\n");
X	fprintf(outfd, "/xs { } def\n");
X	fprintf(outfd, "%%%%EndProlog\n");
X}
X
Xps_skip_to_page(fd)
X FILE *fd;
X{
X	Debug(DB_PSDOC, "%%ps_skip to page: copying psroff prolog\n", 0);
X	while(fgets(currline, LINESIZE-1, fd) != NULL) {
X		Debug(DB_PSDOC, "%% %s", currline);
X		if (strncmp(currline, "%%Page:", 7) == 0) {
X			return;
X		}
X	}
X	Debug(DB_PSDOC, "%%ps_skip_to_page: eof before %%%%Page:\n", 0);
X}
X
Xdo_post_sheet(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	char **outline;
X	struct pagepoints *points;
X	int rtn_val;
X
X	/*
X	 * keep track of the pages printed
X	 */
X	ps_pagenum += 1;
X	fprintf(outfd, "%%%%Page: %d %d\n", ps_pagenum, ps_pagenum);
X# ifdef DEBUG
X	if (Debug_flag & DB_PSMPAGE) {
X		fprintf(outfd, "(Page: %d\\n) print flush\n", ps_pagenum);
X	}
X# endif DEBUG
X	/*
X	 * print the page outline
X	 */
X	mp_outline(outfd, asheet);
X	/*
X	 * run through the list of base points for putting reduced pages
X	 * on the printed page
X	 */
X	points = asheet->sh_pagepoints;
X	while (points->pp_origin_x != 0) {
X		/*
X		 * print one reduced page by moveing to the proper point,
X		 * turning to the proper aspect, scaling to the proper
X		 * size, and setting up a clip path to prevent overwritting;
X		 * the print a reduced page of output
X		 */
X# ifdef DEBUG
X		if (Debug_flag & DB_PSMPAGE) {
X			fprintf(outfd, "%%%% %%%%ReducedPageStartsHere\n");
X		}
X# endif DEBUG
X		fprintf(outfd, "/sheetsave save def\n");
X		fprintf(outfd, "gsave\n");
X# ifdef DEBUG
X		if (Debug_flag & DB_PSMPAGE) {
X			fprintf(outfd, "(    %d %d translate %d rotate\\n)",
X				points->pp_origin_x(), points->pp_origin_y(),
X				asheet->sh_rotate);
X			fprintf(outfd, " print flush\n");
X		}
X# endif DEBUG
X		fprintf(outfd, "%d %d translate %d rotate\n",
X		       points->pp_origin_x(), points->pp_origin_y(),
X		       asheet->sh_rotate);
X		fprintf(outfd, "%d %d div %d %d div scale\n",
X		       (*asheet->sh_width)(), ps_width, 
X		       (*asheet->sh_height)(), ps_height);
X		/* output the clip path */
X		fprintf(outfd, "0 0 moveto 0 %d lineto %d %d lineto ",
X			ps_height, ps_width, ps_height);
X		fprintf(outfd, "%d 0 lineto\n", ps_width);
X		fprintf(outfd, "closepath clip newpath\n");
X		/*
X		 * do the individual sheet setup
X		 */
X		ps_sheetsetup(outfd);
X		/*
X		 * place one reduce page on the printed page
X		 */
X		rtn_val = post_onepage(fd, asheet, outfd);
X		/*
X		 * clean up after mpage as drawn its page
X		 */
X		fprintf(outfd, "grestore sheetsave restore\n");
X		points++;
X	}
X	/*
X	 * print the sheet
X	 */
X	fprintf(outfd, "showsheet\n");
X	/*
X	 * let the upper level know about the status of possible EOF
X	 */
X	return rtn_val;
X}
X
Xps_sheetsetup(outfd)
X FILE *outfd;
X{
X	switch (ps_posttype) {
X	case PS_PSROFF:
X		fprintf(outfd, "xi\n");
X		fprintf(outfd, "/p {} def\n");
X		break;
X	case PS_MPAGE:
X		fprintf(outfd, "/showpage {} def\n");
X		break;
X	}
X}
X
Xpost_onepage(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	int len;
X	char *test;
X
X	Debug(DB_PSROFF, "%%post_onepage: Begin page\n", 0);
X	if (ps_at_trailer) {
X		Debug(DB_PSROFF, "%%post_onepage: still at trailer\n", 0);
X		return FILE_EOF;
X	}
X	while(fgets(currline, LINESIZE-1, fd) != NULL) {
X		if (strncmp(currline, "%%Page:",7) == 0) {
X			fprintf(outfd, "%% %s", currline);
X			Debug(DB_PSROFF, "%%post_onepage: next page\n", 0);
X			return FILE_MORE;
X		}
X
X		if (strncmp(currline, "%%Trailer",8) == 0) {
X			fprintf(outfd, "%% %s", currline);
X			Debug(DB_PSROFF, "%%post_onepage: found trailer\n", 0);
X			ps_at_trailer = TRUE;
X			return FILE_EOF;
X		}
X		fprintf(outfd, "%s", currline);
X	}
X	Debug(DB_PSROFF, "%%post_onepage: eof\n", 0);
X	return FILE_EOF;
X}
X
Xdo_roff_tailer(fd, outfd)
X FILE *fd, *outfd;
X{
X	int i;
X
X	Debug(DB_PSDOC, "%%do_roff_trailer: looking for eof\n", 0);
X	i = 0;
X	while(fgets(currline, LINESIZE-1, fd) != NULL) {
X		i++;
X		Debug(DB_PSDOC, "%%%s", currline);
X	}
X	Debug(DB_PSDOC, "%%do_roff_trailer: tailer of %d lines\n", i);
X}
X
+ END-OF-FILE mp_post.c
chmod 'u=rw,g=rw,o=r' 'mp_post.c'
echo '	-rw-rw-r--  1 kai         14039 Jan  1  1990 mp_post.c        (as sent)'
echo -n '	'
/bin/ls -l mp_post.c
echo 'Extracting mp_sample.c'
sed 's/^X//' > mp_sample.c << '+ END-OF-FILE mp_sample.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X  "@(#) $Header: mp_sample.c,v 1.1 89/05/25 08:58:34 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/* $Log:	mp_sample.c,v $
X * Revision 1.1  89/05/25  08:58:34  mark
X * Initial revision
X *  */
X
Xmain( argc, argv)
X int argc;
X char **argv;
X{
X	int currarg;
X
X	/*
X	 * examine the environment for PRINTER and MPAGE environment variables
X	 */
X	if (do_env() == 0) {
X		fprintf(stderr, usage, MPAGE);
X		exit(1);
X	}
X		
X	currarg = do_args(argc, argv, 0);
X	if (currarg < 0) {
X		fprintf(stderr, usage, MPAGE);
X		exit(1);
X	}
X
X	do_sample();
X}
X
Xdo_sample()
X{
X	printf("%%!PS-mpage-layout\n");
X	printf("/Courier findfont 8 scalefont setfont\n");
X	printf("0 setlinewidth\n");
X
X	outline_8(stdout);
X
X	urshow(xbase1(), ybase1());
X	urshow(xbase1(), ybase2());
X	urshow(xbase1(), ybase3());
X	urshow(xbase1(), ybase4());
X	lrshow(xbase1(), ytop1());
X	lrshow(xbase1(), ytop2());
X	lrshow(xbase1(), ytop3());
X	lrshow(xbase1(), ytop4());
X	ulshow(xbase1()+xwid2(), ybase1());
X	ulshow(xbase1()+xwid2(), ybase2());
X	ulshow(xbase1()+xwid2(), ybase3());
X	ulshow(xbase1()+xwid2(), ybase4());
X	llshow(xbase1()+xwid2(), ytop1());
X	llshow(xbase1()+xwid2(), ytop2());
X	llshow(xbase1()+xwid2(), ytop3());
X	llshow(xbase1()+xwid2(), ytop4());
X
X	urshow(xbase2(), ybase1());
X	urshow(xbase2(), ybase2());
X	urshow(xbase2(), ybase3());
X	urshow(xbase2(), ybase4());
X	lrshow(xbase2(), ytop1());
X	lrshow(xbase2(), ytop2());
X	lrshow(xbase2(), ytop3());
X	lrshow(xbase2(), ytop4());
X	ulshow(xbase2()+xwid2(), ybase1());
X	ulshow(xbase2()+xwid2(), ybase2());
X	ulshow(xbase2()+xwid2(), ybase3());
X	ulshow(xbase2()+xwid2(), ybase4());
X	llshow(xbase2()+xwid2(), ytop1());
X	llshow(xbase2()+xwid2(), ytop2());
X	llshow(xbase2()+xwid2(), ytop3());
X	llshow(xbase2()+xwid2(), ytop4());
X	printf("showpage\n");
X}
X
Xurshow(x, y)
X{
X	printf("%%- point %d,%d\n", x, y);
X	box(x, y);
X	printf("\t%d %d moveto (%d,%d) show\n", x+2, y+2, x, y);
X}
X
Xlrshow(x, y)
X{
X	printf("%%- point %d,%d\n", x, y);
X	box(x, y);
X	printf("\t%d %d moveto (%d,%d) show\n", x+2, y-6, x, y);
X}
X
Xulshow(x, y)
X{
X	printf("%%- point %d,%d\n", x, y);
X	box(x, y);
X	printf("\t%d %d moveto\n", x-2, y+2);
X	printf("\t(%d,%d) dup stringwidth pop -1 mul 0 rmoveto show\n", x, y);
X}
X
Xllshow(x, y)
X{
X	printf("%%- point %d,%d\n", x, y);
X	box(x, y);
X	printf("\t%d %d moveto\n", x-2, y-6);
X	printf("\t(%d,%d) dup stringwidth pop -1 mul 0 rmoveto show\n", x, y);
X}
X
Xbox(x, y)
X{
X	printf("\t%d %d moveto %d %d lineto\n", x-1, y, x+1, y);
X	printf("\t%d %d moveto %d %d lineto\n", x, y-1, x, y+1);
X	printf("\tstroke\n");
X}
X
+ END-OF-FILE mp_sample.c
chmod 'u=rw,g=rw,o=r' 'mp_sample.c'
echo '	-rw-rw-r--  1 kai          2580 Jan  1  1990 mp_sample.c        (as sent)'
echo -n '	'
/bin/ls -l mp_sample.c
echo 'Extracting mp_text.c'
sed 's/^X//' > mp_text.c << '+ END-OF-FILE mp_text.c'
X# include <stdio.h>
X# include <sys/types.h>
X
X# ifndef lint
Xstatic char *rcs_id =
X  "@(#) $Header: mp_text.c,v 2.8 89/06/23 13:14:00 mark Exp $";
X# endif
X
X# include "mp_head.h"
X
X/*
X * mpage:	a program to reduce pages of print so that several pages
X * 	  	of output appear on one printed sheet.
X *
X * Written by:
X *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X *              >pyrdc!mark            Pyramid Technology Corporation
X * ...!pyramid!/                       Vienna, Va    (703)848-2050
X *
X *
X * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
X *  
X *     Permission is granted to anyone to make or distribute verbatim
X *     copies of this document as received, in any medium, provided
X *     that this copyright notice notice is preserved, and that the
X *     distributor grants the recipient permission for further
X *     redistribution as permitted by this notice.
X *
X */
X
X/* $Log:	mp_text.c,v $
X * Revision 2.8  89/06/23  13:14:00  mark
X * Fixed range checking for control characters so that tilde was
X * included as a printable character.
X * 
X * Revision 2.7  89/06/21  08:58:56  mark
X * moved the bottom of the clipping region for text below zero so that
X * decenders on the last line can be seen.  Currently the font, font size
X * and this extra space are hard coded.  Something more reasonable should
X * be done.  Probably involves more command line options.  (Sorry, "Something
X * more reasonable" should be "something more flexible")
X * 
X * Revision 2.6  89/05/25  10:20:11  mark
X * changed the format of debugging prints in the PS code.
X * 
X * Revision 2.5  89/05/25  09:01:51  mark
X * rearranged the rcs header keywords for better readability.
X * 
X * Revision 2.4  89/05/24  17:36:56  mark
X * fixed the $Log:	mp_text.c,v $
X * Revision 2.8  89/06/23  13:14:00  mark
X * Fixed range checking for control characters so that tilde was
X * included as a printable character.
X * 
X * Revision 2.7  89/06/21  08:58:56  mark
X * moved the bottom of the clipping region for text below zero so that
X * decenders on the last line can be seen.  Currently the font, font size
X * and this extra space are hard coded.  Something more reasonable should
X * be done.  Probably involves more command line options.  (Sorry, "Something
X * more reasonable" should be "something more flexible")
X * 
X * Revision 2.6  89/05/25  10:20:11  mark
X * changed the format of debugging prints in the PS code.
X * 
X * Revision 2.5  89/05/25  09:01:51  mark
X * rearranged the rcs header keywords for better readability.
X *  rcs keyword.
X *  */
X
X/*
X * keeps track of the current location on the sheet.  it is kept global
X * to while file printing process because of form feeds in particular.
X * form feeds change the vertical page location (line number) but not
X * the horizontal location (character column)
X */
Xstruct pageloc {
X	int pl_line;
X	int pl_col;
X	int pl_new_line;
X	int pl_new_col;
X};
Xstatic struct pageloc loc;
X
X/*
X * do_text_doc processes an input stream fd, reducing output to fit on
X * a printed page as decribed by asheet, and prints this on outfd.
X */
Xdo_text_doc(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	do_text_prolog(outfd);
X
X	/*
X	 * initalize the postion on the first printed page
X	 */
X	loc.pl_line = 1;
X	loc.pl_col = 0;
X	/*
X	 * while we have input, print a page
X	 */
X	while (do_text_sheet(fd, asheet, outfd) != FILE_EOF)
X	  ;
X}
X
X/*
X * ps_text_prolog prints mpage startup information for plain text documents
X */
Xdo_text_prolog(outfd)
X FILE *outfd;
X{
X	time_t curr_time;
X	char *time_str;
X
X	fprintf(outfd, "/Courier findfont %d scalefont setfont\n", fsize -1);
X	fprintf(outfd, "(a) stringwidth pop /mp_a_x exch def\n");
X	fprintf(outfd, "(\\t'a' length ) print mp_a_x ==  ");
X	fprintf(outfd, "flush\n");
X	fprintf(outfd, "%%%%EndProlog\n");
X}
X
X/*
X * do_text_sheet creates one printed sheet consisting of several reduced pages
X */
Xdo_text_sheet(fd, asheet, outfd)
X FILE *fd;
X struct sheet *asheet;
X FILE *outfd;
X{
X	char **outline;
X	struct pagepoints *points;
X	int rtn_val;
X
X	/*
X	 * keep track of the pages printed
X	 */
X	ps_pagenum += 1;
X	fprintf(outfd, "%%%%Page: %d %d\n", ps_pagenum, ps_pagenum);
X# ifdef DEBUG
X	if (Debug_flag & DB_PSMPAGE) {
X		fprintf(outfd, "(Page: %d\\n) print flush\n", ps_pagenum);
X	}
X# endif DEBUG
X	fprintf(outfd, "save\n"); /* for better memory usage */
X	/*
X	 * print the page outline, which draws lines and such
X	 */
X	mp_outline(outfd, asheet);
X	/*
X	 * run through the list of base points for putting reduced pages
X	 * on the printed page
X	 */
X	points = asheet->sh_pagepoints;
X	while (points->pp_origin_x != 0) {
X		/*
X		 * print one reduced page by moveing to the proper point,
X		 * turning to the proper aspect, scaling to the proper
X		 * size, and setting up a clip path to prevent overwritting;
X		 * the print a reduced page of output
X		 */
X		fprintf(outfd, "gsave\n");
X# ifdef DEBUG
X		if (Debug_flag & DB_PSMPAGE) {
X			fprintf(outfd, "(    %d %d translate %d rotate\\n)",
X				points->pp_origin_x(), points->pp_origin_y(),
X				asheet->sh_rotate);
X			fprintf(outfd, " print flush\n");
X		}
X# endif DEBUG
X		fprintf(outfd, "%d %d translate %d rotate\n",
X		       points->pp_origin_x(), points->pp_origin_y(),
X		       asheet->sh_rotate);
X		fprintf(outfd, "%d %d mp_a_x mul div %d %d div scale\n",
X		       (*asheet->sh_width)(), asheet->sh_cwidth, 
X		       (*asheet->sh_height)(), asheet->sh_plength * fsize);
X		/*
X		 * output the clip path (the bottom of the cliping region
X		 * is 1/2 the font size below "0" so that you can see decenders
X		 * on the last line of each page
X		 */
X		fprintf(outfd, "0 -4 moveto 0 %d lineto %d mp_a_x mul ",
X			asheet->sh_plength * fsize, asheet->sh_cwidth);
X		fprintf(outfd, "%d lineto %d mp_a_x mul -4 lineto\n",
X			asheet->sh_plength * fsize, asheet->sh_cwidth);
X		fprintf(outfd, "closepath clip\n");
X		/*
X		 * place one reduced page on the printed page
X		 */
X		rtn_val = text_onepage(fd, asheet, outfd);
X		/*
X		 * clean up this page and move to the next
X		 */
X		fprintf(outfd, "grestore\n");
X		points++;
X	}
X	/*
X	 * release PS vm used, and eject the sheet
X	 */
X	fprintf(outfd, "restore\n");
X	fprintf(outfd, "showpage\n");
X	/*
X	 * let the upper level know about the status of possible EOF
X	 */
X	return rtn_val;
X}
X
X/*
X * onepage places on page of reduced output on the printed page
X * all scaling, translation, and rotation has already been done before
X */
X
Xtext_onepage(file, asheet, outfd)
X FILE *file;
X struct sheet *asheet;
X FILE *outfd;
X{
X	char *text;
X	char linenum;
X	int pos;
X	char *mp_get_text();
X
X	/*
X	 * as we move from one page to the next, restart printing text at
X	 * the head of the page. horziontal location is not reset because
X	 * form feeds leave the column the same from page to page.
X	 */
X	Debug(DB_ONEPAGE, "%% reseting line to top of page\n", 0);
X	loc.pl_line = 1;
X	/*
X	 * keep getting lines of input, until we have filled a page
X	 */
X	while (loc.pl_line <= asheet->sh_plength) {
X		text = mp_get_text(file, &loc);
X		Debug(DB_ONEPAGE, "%% text = %d\n", text);
X		if (text == 0) {
X			return FILE_EOF;
X		}
X		Debug(DB_ONEPAGE, "%% text = (%s)\n", text);
X		Debug(DB_ONEPAGE, "%% loc.pl_line = %d\n", loc.pl_line);
X		Debug(DB_ONEPAGE, "%% loc.pl_col = %d\n", loc.pl_col);
X		Debug(DB_ONEPAGE, "%% loc.pl_new_line = %d\n",loc.pl_new_line);
X		Debug(DB_ONEPAGE, "%% loc.pl_new_col = %d\n", loc.pl_new_col);
X		if (text[0] != 0) {
X			/* fprintf(outfd, "(%s\\n) print flush\n", text); */
X			fprintf(outfd, "%d mp_a_x mul %d moveto (%s) show\n",
X				loc.pl_col,
X				(asheet->sh_plength - loc.pl_line) * fsize,
X				text);
X		}
X		if (loc.pl_new_line == -1) {
X			loc.pl_col = loc.pl_new_col;
X			return FILE_MORE;
X		}
X		loc.pl_line = loc.pl_new_line;
X		loc.pl_col = loc.pl_new_col;
X	}
X	return FILE_MORE;
X}
X
X
Xstatic char text[LINESIZE];
X
Xchar *mp_get_text(infile, locp)
X FILE *infile;
X struct pageloc *locp;
X{
X	int gathering;
X	int tabcnt;
X	int ichr;
X	char *textp;
X
X	textp = text;
X	locp->pl_new_line = locp->pl_line;
X	locp->pl_new_col = locp->pl_col;
X
X	gathering = 1;
X	while (gathering) {
X		ichr = fgetc(infile);
X		Debug(DB_GETLINE, "%%called fgetc ichr = %d", ichr);
X		Debug(DB_GETLINE, "(%d)\n", EOF);
X		/*
X		 * this prevents nulls in the input from confusing the
X		 * program logic with truncated strings
X		 */
X		if (ichr == 0) {
X			ichr = 1;
X		}
X		switch (ichr) {
X		case EOF:
X			gathering = 0;
X			return 0;
X			break;
X		case '\n':
X			locp->pl_new_line += 1;
X			locp->pl_new_col = 0;
X			gathering = 0;
X			break;
X		case '\r':
X			locp->pl_col = 0;
X			gathering = 0;
X			break;
X		case '\b':
X			locp->pl_new_col -= 1;
X			if (locp->pl_new_col < 0) {
X				locp->pl_new_col = 0;
X			}
X			gathering = 0;
X			break;
X		case '\f':
X			locp->pl_new_line = -1;
X			gathering = 0;
X			break;
X		case '\t':
X			tabcnt = 8 - (locp->pl_new_col % 8);
X			locp->pl_new_col += tabcnt;
X			gathering = 0;
X			break;
X/**/
X/*		case ' ':
X/*			locp->pl_new_col += 1;
X/*			gathering = 0;
X/*			break;
X/**/
X		case '(':
X		case ')':
X		case '\\':
X			*textp++ = '\\';
X			*textp++ = ichr;
X			locp->pl_new_col += 1;
X			break;
X		default:
X			if (ichr >= ' ' && ichr <= '~') {
X				*textp++ = ichr;
X				locp->pl_new_col += 1;
X			} else {
X				*textp++ = '\\';
X				*textp++ = '2';
X				*textp++ = '7';
X				*textp++ = '7';
X				locp->pl_new_col += 1;
X			}
X			break;
X		}
X	}
X	*textp = 0;
X	/*
X	 * remove any spaces at the front of the text string by
X	 * "converting" it to a position change
X	 */
X	textp = text;
X	while (*textp && *textp == ' ') {
X		/*
X		 * this affects the starting position of this text string
X		 * (not the next)
X		 */
X		locp->pl_col += 1;
X		textp++;
X	}
X	return textp;
X}
X
+ END-OF-FILE mp_text.c
chmod 'u=rw,g=rw,o=r' 'mp_text.c'
echo '	-rw-rw-r--  1 kai          9543 Jan  1  1990 mp_text.c        (as sent)'
echo -n '	'
/bin/ls -l mp_text.c
echo 'Extracting mpage'
sed 's/^X//' > mpage << '+ END-OF-FILE mpage'
XArticle 1255 of comp.sources.misc:
XPath: draken!sunic!mcsun!uunet!allbery
XFrom: mark@pyrdc.UUCP (Mark Hahn)
XNewsgroups: comp.sources.misc
XSubject: v09i088: Mpage (1 of 2): Reduce and Print Text or Postscript
XKeywords: postscript, reduce, n-up, 4-up, mpage
XMessage-ID: <74697@uunet.UU.NET>
XDate: 27 Dec 89 00:19:16 GMT
XSender: allbery@uunet.UU.NET
XOrganization: Pyramid Technology, Government Systems
XLines: 1893
XApproved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
X
XPosting-number: Volume 9, Issue 88
XSubmitted-by: mark@pyrdc.UUCP (Mark Hahn)
XArchive-name: mpage/part01
X
XMpage will print text or Postscript input in n-up format.  I.e. 4
Xpages of normal text or postscript is reduced to fit on one sheet of
Xpaper (4-up).  Choices are 1, 2, 4, 8, and 16 pages per sheet of paper
Xwith 2 and 4 being most useful and readable.  It accepts Postscript as
Xinput, or at least it works on most of the stuff I get from the Adobe
XFile Server.
X
X. . . . . . . . . . . . The Obligatory Dotted Line . . . . . . . . . . . .
X
X-- 
X  ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X             >pyrdc!mark            Pyramid Technology Corporation
X...!pyramid!/                       Vienna, Va    (703)848-2050
X
X
XArticle 1256 of comp.sources.misc:
XPath: draken!sunic!mcsun!uunet!allbery
XFrom: mark@pyrdc.UUCP (Mark Hahn)
XNewsgroups: comp.sources.misc
XSubject: v09i089: Mpage (2 of 2): Reduce and Print Text or Postscript
XKeywords: postscript, mpage, reduce
XMessage-ID: <74698@uunet.UU.NET>
XDate: 27 Dec 89 00:19:48 GMT
XSender: allbery@uunet.UU.NET
XOrganization: Pyramid Technology, Government Systems
XLines: 1156
XApproved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
X
XPosting-number: Volume 9, Issue 89
XSubmitted-by: mark@pyrdc.UUCP (Mark Hahn)
XArchive-name: mpage/part02
X
X. . . . . . . . . . . The Obligitory Dotted Line . . . . . . . . . . . .
X
X-- 
X  ...!uunet!\                       Mark Hahn, Sr Systems Engineer
X             >pyrdc!mark            Pyramid Technology Corporation
X...!pyramid!/                       Vienna, Va    (703)848-2050
X
X
+ END-OF-FILE mpage
chmod 'u=rw,g=rw,o=r' 'mpage'
echo '	-rw-rw-r--  1 kai          2087 Jan  1  1990 mpage        (as sent)'
echo -n '	'
/bin/ls -l mpage
echo 'Extracting mpage.1'
sed 's/^X//' > mpage.1 << '+ END-OF-FILE mpage.1'
X.TH MPAGE Local 1/13/87
X.SH NAME
Xmpage \- print muliple pages per sheet on POSTSCRIPT printer
X.SH SYNOPSIS
X\fBmpage [-1248povsnluaA] [-Llines] [-Wwidth] [-hheader] [-Pprinter]
X[files...]\fR
X.SH DESCRIPTION
X.I Mpage
Xreads plain text files or POSTSCRIPT documents and prints them on a
XPOSTSCRIPT printer with the text reduced in size so that several
Xpages apperar on one sheet of paper.  This is useful for
Xviewing large printouts on a small amount of paper.
X.PP
XThe following options are recognized:
X.TP
X.B -8
XPrint 8 normal pages per sheet.
X.TP
X.B -4
XPrint 4 normal pages per sheet
X.I [default].
X.TP
X.B -2
XPrint 2 normal pages per sheet.
X.TP
X.B -1
XPrint 1 normal page per sheet (included for symmetry).
X.TP
X.B -p
XPipe input through
X.BR pr ( 1 )
Xbefore printing (assumes the input is a text file).
X.TP
X.B -o
XPrint an outline around each reduced page.
X.I [default].
X.TP
X.B -b
XDon't print an outline, so the reduced pages appear on a blank
Xbackground.
X.TP
X.B -v
XPrint to the standard error output a count of the number of sheets
Xproduced for printing (verbose mode)
X.I [default].
X.TP
X.B -s
XDon't print the count of sheets (silent mode).
X.TP
X.B -n
XPrint in normal (portrait) mode.  The page is 66 lines long by 80
Xcharacters wide by default
X.I [default].
X.TP
X.B -l
XPrint in landscape mode.  The page is 55 lines long by 132
Xcharacters wide by default.
X.TP
X.B -u
XLayout the pages on the sheet so that sucessively numbered pages run
Xdown the sheet (as opposed to left to right)
X.I [default].
X.TP
X.B -a
XLayout the pages on the sheet so that sucessively numbered pages run
Xaccross the page from left to right.
X.TP
X.B -A
XPrepare output for European A4 sized paper.
X.TP
X.BI -P printer
XSpecify the printer to which the POSTSCRIPT output will be sent.
XUsing
X.B -P
Xwith no printer specified will send the POSTSCRIPT to the standard
Xoutput.
X.I [default:
X.BI PostScript\c
X.I ].
X.TP
X.BI -L lines
XAdjust the page reduction parameters so that
X.I lines
Xlines will fit in the space of one page.
XThis overides the default values normally supplied.  (See the
X.B -l
Xand
X.B -n
Xoptions.)
XIf used in conjunction with the
X.B -p
Xoption then this value is passed to pr as well.
X.TP
X.BI -W width
XAdjust the page reduction parameters so that a line
X.I width
Xcharacters long will fit in the space of one page.
XThis overides the default values normally supplied.  (See the
X.B -l
Xand
X.B -n
Xoptions.)
XIf used in conjunction with the
X.B -p
Xoption then this value is passed to pr as well.
X.TP
X.BI -h header
XThis is used only when the 
X.B -p
Xswitch is used and is passed as the "\fB-h\fI\ header\fR" option to
X.BR pr ( 1 ).
X.PP
XThe defaults are: mpage -4onuPPostScript.
X.SH ENVIRONMENT
X.I Mpage
Xexamines the
X.B PRINTER
Xenvironment variable to override its default printer.
X.PP
X.ne 10
X.I Mpage
Xalso examine the
X.B MPAGE
Xenvironment variable for default option settings.  Any option or
Xcombination of options can be specified in the
X.B MPAGE
Xenvironment variable.  For example, if MPAGE is set to the string:
X.in +1i
X.B -2oPqms -L60
X.br
X.in -1i
Xit would (in the absence of other command line arguments) print 2
Xpages per sheet, 60 lines per page, with outlines, on the printer
Xnamed
X.B "qms"
X(overriding the
X.B PRINTER
Xenvironment variable, if it exists.)  In the environment variable,
Xwhite space is used as an option delimeter, and no quoting is
Xrecognized.
X.PP
XAny command line options will override both the
X.B PRINTER
Xand 
X.B MPAGE
Xenvironment variables.
X.SH FILES
X/bin/pr	/bin/fold
X.br
X/usr/tmp/mpageXXXXXX
X.SH BUGS
X.PP
XSuffers under the burden of far too many switches.  (But
X.B I
Xwanted the choices!) 
X.PP
XMany others, I'm sure.
X.SH VERSION
XVersion 2, Released June 1989.
X.SH AUTHORS
XMark P. Hahn (uunet!pyrdc!mark), Pyramid Technology Corportation
X
+ END-OF-FILE mpage.1
chmod 'u=rw,g=rw,o=r' 'mpage.1'
echo '	-rw-rw-r--  1 kai          3718 Jan  1  1990 mpage.1        (as sent)'
echo -n '	'
/bin/ls -l mpage.1
echo 'Extracting mpage.arc'
sed 's/^X//' > mpage.arc << '+ END-OF-FILE mpage.arc'
XCHANGESh!UtaM[,EbgEIqDNbEq$M2#O1#$2ra$yC1d@|9$
X'!BH#GQ
4ne=a
XD["e@pjrEG[Dv$qofGiia$ AF)1an`URQGiI8AEx'1RC&>-I>mZOpdiUI$-*\|wPa Q&aVEBA~uEADfr-BN7g
X*)E',Z #bgjbzXv1H.O
X[n\o($alM$aPe !
XP!a$PCi)a,~mAO$gjSI[5nYG+g
yI#ub/OiaP`UDaee
[4#$p$D|OENi `>%	Dh,nU+k: SofY? LAGft%As *CdQ+XO!xe{HJeB
XeMANIFEST&!Uc  
!O$HA
X18yC'#0nE
XW4 p$[I9 " Ixd0rIIq1& ACP'iScV7kxa$"p{ y;&-\Op d5EUuI#epsl u`UenAp}7bCdu>0l@OA&REDC;2aE,A<>&F n$OoUNvx^% OU Aa[+A]`k!hAFy Qg[}xi C]!(O1Ou\A
XAA\k@p!GF]#`dQ"!r|dTUO*VA2&)q8"!1ebnO
!$ma|As4
X|H2$cI _\|qabP!gO Soe:edDe
XI9c-BH-P8aIqu}}9U4y"D	S^RC nu<APA
=iO,ZAk'IEu6id^vn IU
X?Pc	UVme1ts5]WIY SouoeU4eEe-CA +}\^7C0b&A4oUa-)"$EA+wU.|T1b$	"St$"jn{B3ae !$>"e  aPlo0$d|E$@@1ned]
X{o[
X!]rSpOe MX!*DF~IBlbea~"+XF0$oC+5eeEB
XOa.a'O^1U^	dHcVexdFe&Uctuj`|	DA	gzj(b y#a }$$P

F\\$zo&IxCUC*-u.u\"A:!-
X-Rp$B<I  e}iA0ar a-DKnd	Z oai\[*0U@BoO!pOB LVl ZuU<
XREADMEw
X!U4 
X<EJ#H"eP  	0gE
X@aU|{4kAqFEe e&IZ	
eY#n
X7A0C
X8D
X@B
XPB3YFCa 8YAECILOLE%CNbSnFl eeo\b-z#1dX !Ou
X<w["}Yz;MO p
XYti  E1,4IA.zEE^![aE -Y2}!
XD 8!	F Xo
X U fAA%gPOoxbi]*C}Ii+2YFxlZEa Rf#aeJus  A-oU
p'p`ia$A1@Q@iO-Nk|mlK9$C-iAmZGAEu{qk"xO#egF|w%" ,$8A.T 1L"0A8\aT2BoagAn ALOIi$knxDe  l72}uiDek1 o3ie5Eel	dP+`q1\<
LNUaAm?}nc`U+o,Ae90H{D*LA GD
X@A$daUU-G{",AD2PyU"/'  Aa L,Bx{udk%y&RAPoC$VLBa^j2&F}:A{5 +
6"#22=5*@
X!U
XpNE-5>u '*u"C JO  <i  <@- EA(4a
Xk>2i|+**EUo[!Z< UCoI wWe7ofP
XIl?=2H>A!)DCl9l-/aP?OOqi$4ER -$]1n^n|A19	AXmA	#`Ao-^)f$|ah`0uiIoe  2oK-fuI}e$ao:"r?a"y
IV[C6tu?<	&ZN /}A cb 2oh{udDkIFpO!G	!
X1| pM
Xnxc?R;M|5Ai$"-|C~PC]o!u+YDO,t DA#xa_EAb-.ab}Yc}dAEU}R_oXr`}K.c,M
Xiii 2ly	bA+(  uOgCCV1FyvovogZC	X2 +d_6*!ac@Uac<]]1AgOA}Ml)PrTUGMR=Ncaso^UV #]E A&4WY 1eUn <yE&Cq~BNCeC~#Ra[R=IsqT, A64b-2.Escdvr _qz UnPmv57SgaI4*o0
XDU'QwD@KcK!CN@C~g~I&Q|8FRAR.E+zV} qEb!o}mROcX {M3en$!7rDzA+
X 1S i!qg eHr^A@AY K$X*7-=U4w"AQi!d ZG^+4g+#^1e1?TODOx!aK-CeESA
Xo&I4q+oa6\NO
X -4yC sA3h~&&N>awRoU,7,sI"NS
XQBdp4GpAAPAf7U]&9T$DG"1|A^M|"1GXE-a r, AEm{A		hUYac$}$ (PN:([WCc-i gNUdI,PQQF\O% gPAaaoNCAU}U
Xl Pu
X[!	.F$3Yaxd$O)A!C$+ w*AN	aC^*OE^a`VDyhP)R\	D1lxX^A6Uy`e[S8 ++kEO&Ak-|[pl`+O;O7,<&L&!^+O.$d-'Gg|H q95Ge'`x`?  BA;'@ P0$;1	F=eARG|uF"!L$4e[q9x1MRecb< u_Ua
XBR2D0XEIZtOH"u<yA5GbxiS}**Syu5f6><$>]r!eANf's]&aUo)+H|*JCHo".ROH}/` wooC

X,&cJUE3Itp
XiI)I\0M$"$AAh <AH3"O\oA%eYe-O$DHaS9n)DMz~| AEauOUydc `AhIHH6EcX<Y'Up8R'(a4o)
X
X
:-A);+O^D"F' H Dp2'Omp_args.cp
X!{ eU#@$q3M2e@dCL7.DoPp`A	aoN8eaDHQ 7I
X	@ ->8IA&l7x]EiCz8fA
X7A0C
X8D
X)Cn$x 'WAn8C4
X[ha,q^P|IcFt&O(k$o"1'I%obIOc C+1&[+nAu'aoaAZkiuF9E$-]J goOj~eSih ZCvu%(EO oX3R&EenIag|tleU$*n	YAC3v(<O	QdEagje +  U+au&3IV;SaeCOG iOUYm xRoAj!^\XeZl1\sC)PS
X[i&O|"tP%T*} eET9aAy9AUUvo<nFIIAQG7&POAVY4(uE\:OpC[4 xCa$YuISl=aqCdAAoNbOC7j!F,yuU8E.I 9E[ED5
XOL3Ui@g,<OA+?2\g1o3`E9$|y+a3u
XV 7& *AAC,Pun]Ay Dq`"CF-	PR"9J: &1
XC
pYUn\
X]}7]iU{1iI;,|p0qb,YI'OW-+caB%dm(Dn	xu{,=nJ	20]u)1 CG6oOPxRz(Aj!U
XIg>| W
XI$y[NelkU'U}
XR8RIChuLRXI&[Pp-"P)
XA'sO<+~A
X}$aA]0ZGdcsB|E?&

JyyagJiQ|ZE1b~SxA -UAI\N(<L6,WMs$( w8XW[+l;xziAagp*oe {7~ O,LNx|I"OI%X$,[sAi*aUAL-6u&Jc$	8KI%
X$ha,^P&McFt&Ohk$#1AI%bDOc 25*[2nAu'nWoaE|kiGF9U$-]ZagoOOj~iih ZCz	&)E{ oX3R&E(oIag|tluU$**Un	YAC3z(<O	QdEa&je +  U+Gcy*3IV;VY dBdEADh)vUV[u OxRuAj!`	5VYg$ [nA$\Eu"ax a4UPAZO 0|Tp TC0!?9	qz9}LFb`GaUF2Euf\EeZ2E \4e@o}Ps]a=3fUGe'
X$7-ZdDFA0(=OCp[EHrA
XOl:!
XL`%	3OFQf@C "o`R1 ECoUno&@!G(BvR&a 
X
D"4|D$*Y)I0fva I`u $QapPE.3 3OnIAy%2+O 5	O{Dk,a8APE"+:DIi\
XSBAH,y 0FC%d$%1eW

]-A4|d&V/u/2^etAod$	XeP`7%f(ELE O -r~CIH,x*$Uu+"E p|y<CACD+7r"K2Ani_$$-Yx1LEAYeX{]Cpd&9b'EPDM!7OO	])VQI%1eY/
((}V:-0!	N([ "t
XAiORA *Ai EX(R|B
X(;2&'Nx71FfoO fOoT[-53A  u)E6IPp $YiQOFQOFJiQrJi'J#)DPBPGAN WUtKxtOuScUi eD4gs9QNeGcG		uPs\#dAL'eJwGp 1) AK|G
XEO:I[:( `&A1yC9AD)$ 	?XvE7b)n+2n}AN|h(%AeafX +ok0K"o|e fxJR"I>#y OKo)]z&+B0
X!
X4:Ia|lHEO 	a AaBA#	"g}1o'6E~lkKOt n8!wPS vI  a!b0DoQpA\AE$aO2ZoOg'?C@c
XIx:9d_+<Ai X DEgB2k
XC
XOBP!pceF|N
XRD&.-B_AT<,&b[UhP!
 Aq2"$$&ZSG !dL @
X 
XC
~BwXg	EcCzvgs}*i[{~W\e'y 'vigxC+e 7 g|cxfACfEcpIwvZ7
Xorwcoq'K(mXS"rVhw( v_{&_,A@5q2Joe=A=R4JA=a&ybAGEZ]cYf(! qyzovd'[o8
XAoR"I$$U8x -rUa[So2|EqqZx |~

Xq*o}3}S$ Ctvd`T]nH%&tO !V,9( [e45U sN`g{o3!$|>eA~D	}|*  ;O\J
Xe b:e:8guPtv EoNdRDAA 
o$o)n1YIJweTP@OpNp-`|rf-+a|huI!PB
X:&eDQdt^$'"?UOU5TO3o:Ngd 3zLV$R)a-p76 a:X% 1*	[Su+eD; u+0ETP
XY$	1 UQA8D&I+ktUUBuL0aN0f*ar|!`	aBU1RJn(n4I!XU79eR2N=3pU"+!-O/44XpH 33aN }Nd o$&bm S$$BH)bC)+ t
X AlAeAA$L TAA8iA ,[]L
XT`!0gEeH9oIE	O7 {!SgL$@EInAQ9iUD1*4aAI)c'a6XIa)0	 r-C_aaA1(<AWc@,3ViIAr\D$a
XX:tEGeH\
X8D
Xt1kC1$o 2IP2CdRAia$y dar+I8+aM9iOh U- nDI [mLMCVI
XR !	L(ME37 
XID[K"0*<`?P'!@-!$X/4a	Rx!"EN^ak]2OpEib
X	p"
	ABeC/<A
XE$PD*$`{$d? &A	KA"ho&$NAV	Bb63UnHuJoD'2A9L
X
 *aN\YEOzr*;,{Ebx3?}Al
 N53	ULQpm@Lj8LZUr[f1I
X M 
XDCNpfBo[
@?u0
XS [ RaA
O! !CE$MZ
X"%@I zJ>A$aMYu(O
X`c^_]0ogip
XC,O*@A$Wp|w@# 0	G@#eod,&0eNAC-UmANc| qCTCa <R$zdf/x, JA<>mdVb$KrbCzyGzeD</  ?GcFtdx|pW<|#&"obatE2h'i)V;:xW]3hGhtCoa*C$k2&<>RnxA":Y't stK"Zo	NH!0=@uR`t
XUzj$|t o/V,a(aaaS	24q,d
Xa-'$(o[F	 `	1O_^O	Jx"wUo3|" &u[C8A	Jgd/-[p|e-
X$"J% Wydz) $^c$<E%2u$9E%6ZBei;
Xxai> $V$ ei8p$c$VJM$P
t2$%$>acp8ha?qAd_Pt"(0eDk >-+-a..e*Ae31"tmp_main.c-!{paP#@$q3M2e@dCL7.DoPp`A	aoN8eaDHQ 7I
X	@ ->8IA&l7x]EiCz8fA
X7A0C
X8D
XBuucao O}ErcuVZEA
XOL3 i@gtME^ic.
X4|189AVRhLA6)_jr"@,{Eb$0!:ysI@ $A62hodDN+7sX}t"/0$	ezam$EE+
$_$P|xQ}Ecd}A
XE$O(a@lcPGPO'|A a`A0 @- DF($=2A$A?$P
Xc
XL`JYcI2J'iQ$=7\<&utM 2Co\pAQ	$$ ^atHo" 2 AeuaP&$	xjP2	'	(a:;3 <
hPe#i<
XO($$)iM! aU
X> }!sDv iro*UAM [-`-$'Eanx uo>xne-E{cR+ |csi/->Y"XHCa$6&aK$Oa]Lu{D<&<)sd9(}AV-tO PC uhD 0OAq]|O@+a3$f`^iY/oE
XO(o"kefpaEE"U[-J$[N?' nOdC|dm
X:pI!caIaoC	UU+
X	{+UN}4Qww7zW|}Ea3	Es
Xhh$1%[AO)sz",%Dk$[EC'fPjFr'+C"{RL E:^:*zE.+iLZYC[Ge|k[u xRd  `	5VYg$ [nA$\Eix&x"aU {ZO 0|o HC-!` 9	A_y9OLFb$AGPU2E|Z\EeZ2E
X
XiAT`  0*_AR|K@aaO!u=A
XA`\( a{a	ed"<x 1_yB2de(  '$A	T
Xy	 3s#*CyO7t2uO9wJK 	c<ILNaoe(EaDin2xT9E0X& '{-
RoR4|)E6]sb1ydhPq*QzV'J1%VyJ>Zno"QeSf*4i4<P/)O^r-9I"k BRU- <*
X9I<=$
Xa2|UIt}o/,I?[YI-tc;aOw 3: *aiU[@A&*h|w!q='&
X~a}us* yip$ieCvvd'6oco7{aw"|EG|$W{{G~eg~ ~&~|ow<|yChO{{]~|4z_Wz wz,Gwqyy&$8
XJZ4DhO/h	("dDUPaL	[
X#fXjFr'+&{.ON:^9
&yE2jijY]COIFavVZq}	AIA[!~VQdb V[oID2kgFx,\CUgWjieaPPO&?`Ga<R Fl<Oh$YOs ;I R\"N2&6GHD*[O	`DN[E	@iA\61oU|U_uP Il|i[CUpeoo8F"1 i?[> /A?W'-2 |-Ot0i4Ou 
X6a0p A`NW
XKt24"	%qBo>$$:$ w Hx IACce' /dd
XSxBL>,6`!Y\H?$E5AIo7a' QvUAx$* AHIY
{
XEEE1`
X'DLaP$ $ 6q^ 
uE?Uo<dY3-A
XAnP,kC0BEU'o1u[%, (@]cV-VAfa 
X_I$g 93=7AY2 )]$O9NA0	 e,IT{urBNgNeEi[g|c>e4jPI&Ary&@DKJo)> !N$onO8A- 4 7o,ATtComf:ge)$A%A2/yawaVAKM7AdHQ&A'c&A^| 
XaOs?c{ >z Ro"Yd	P j0$*r u|1a^Oo%eiDcM
Xrp'j
X$G
X&o)&u95O+"$&, $_A$Nac"[C=Si1|YD-e".CPA6 otZ*AHOwUs9uog7n4,.12GE []+eyu"<0* &-=P^i-]o(a$WY	$e-C` FdRDI tI
XiNX-WeD3M
X eUnOyoSVzj.
XCE!|8_DR1[A 60tu^ u$   Z$A 1N*<$+
X;;";  O8"1V;7 	Y+*6{a7a*N1En1( ZK \KT@5Y3 E$i; )U'Ub E ) qa.+a/)hE  [u4USNbOk
XKAkKAp vVt
DRptFr"~e ,kE{<sHvAe
X <DAj+<+<	uh ) i.'<Pe 2Exw&cUo qWIS6h(jA6AA5p7c ,*AoUwo;AKN$on2U B#(
X BAo oPRI
FJz[t]	>u3AIo#,$cI>I c*I*%@ryIN"7e(aIi{9Z "e R
)"?rdO+_'uaDQtaD	aI$AERaEIaI[A yME	IOaOoNIAi3iIyoF[IID-OO|I*pI Yogfk59QtFL0Id	uO0T@4
I-\iP ag`- gDeiA;
XAU$ <nYeI\szQe&
/-
Xc{a	9e< [>i4x}!}\.iNi
XaeilRa+UBiNAdi}o U"dxdi>@ouI
y EU(>$ 91? a*E3$UOhz/LAIIuS[tAY>PIo1?AuE1?A-!VA"[2_
Xo
X#@$q3M2e@dCL7.DoPp`A	aoN8eaDHQ 7I
X?PAE3:,A <W $RRAN9Y[O,Z l 
X*	3g >sAN
X$-&Gy[ofN* Uu>GAiceEy^uEau('eAtIwcZI7mFf R$aEHnJ 4EG+_yeA
X1,i^a%`^zeA'*~Y(@'xA[ }	  
X"AA++,1QA@}&*<i%p*Nnu  aC7oUb1l/G>i x|1e ER>eA o! Lai u0?3\Rc o y0/?48c0?
X[LCpo{&Cep "<p
X'w,nEc12
X-v&1 e
X iC,.D! aP`b !M
X3<Rip
Xmp_text.cP!YcQG%#@$q3M2e@dCL7.DoPp`A	aoN8eaDHQ 7I
X?PATU8aIN 8rP|
XUODdoUIvFN
X7EB i33$[edEop1@#9-3A7JOEH\c$EAD'~FQHeW]+N-l{a?"VI~dk xAY
XCiC3!iC)`2E!Taa.3a]q>{h$Va*}+_x R\*'AR*6 Qw`\OoUEi'drAz
srXE||C-e>.}UNCEuK\*LA |3O7Ob/Ak|oB`
XHnaA/o/*.?@*6Ax]-RI\u E8CeOq
$o/*$IePpc}!U4	oHpIi$:CCxSLc]QR5cu #Fy2	4Ee_>y>s^cDTAVD<}+
X[ L	i@aIgJ,&_A2b i++U[wE\ A	[paz_
X/oA O6
XU oD5pefAXIA oOw:OEP$1oCh\Id(u3}|^nMO\y7QP1*o6(b^9,oafg+W@ -u|CUIIAy$3 it(
X oI-uSBa"CyR-u])Nyy$IFyx.>aya_`ykBf ]I3?]No_3ECon)|py} +Pm1 xL'{CW{A1[&ud
XrDA'"i3;9}ol
X &oA&46V:i
XDjoWtI#te3$68
X5{>cf!N83cguo!B<&ehrya|9AA&|3A`  \OB9aG^ap  H_|eyx8P5+u;$aD={ \UGEPoNPThT%A a19A|x Zv$m-Fm\b$O
X
XaG6O9,IIp*SZP]e#[>2e<(aq1[ap0~C| <6<d$f&|og0|[!4*b0	$23rD2D&9A\	 IU|9xa*p@
Xy ],uckAOE'U2X^R e&<<NFO/
XPUGY$<p CUa\\pa=aDaaOOaMPeuYwyOiYUB_EtxL-UiI aOfBLOM- 
i
-IJ6fP_M#h*i>DeC%IbOUh
[8CU"Rm%pbIC)&EyLEOU{mpage>!O%'A{DI3M1dO"a
X|O$|I:(iAE(A	sa 

X' QdA.u-&$ ZoRoueGA&gt lC1Ny IF<EVmp$sl$d%C nAx2uPAEDUp8Cg1hi e+/ C='X r(<I E Yg DPreuIQu 12g]AFboo$1F2Q k,AuU3A;jfPRKnMQIe+;de nOQ%Bc$T[oeT-M1Or, ex Pz 7\?s|K}o z@[E a~Gn*-C
Xd 9i%|AF/~i,{FAYFe p3$
X
X$r7C-lw|OYD$'}-geO& e/~CA
VD| ETA\yOghEOa
iCuMphc e%a$N$c-a3CIu23i|'<8Oy '| '>oCI}Ayoc?B I,h3
X%C%
jO";IYR TEy  2R-e*|D2,{ 	9ADpl >TA)I3
X+[iA"A,N@d
.fYrK2ha
XOF
X
X,
X
XQ<6	EO(1C]]
X K! aVY fEio|
Xr>+WI&PEUkc }ye1*CmQe!l$z+^
!Cac82>"i2D|Uz| 	O-% +F3a+A[\+UAsU!R
aAGrd8^[Ea^.A($EB!,-]9  , obij
O
b aBKo(A-yp"$aaU['  1d3o hN('W6jI2n"Ko	[rI:)`-XG*6O@"K)aCy
XOkbEE`
Qxei36A- !7Q!1GCy^aAZEmN5Od,^9I>$Oo"arp OcDue"#zwu(ADy< noAoOwn iaCF}D*a4AjE!2AYA |Ve$VcsMf,oJA"eH& 8	-:xE  ] uc{n	_OAxo!^h;aYra  l[ \|E$!aIpNd|OI)a*Ho0i7 (B 1$!Oiy '$|PHNT|d1$Ac vRuU!BoJUu
XvFoyofE+jE"~*zAR<Na
X 	,0AI
X[DYg;B) yLj ~IU'	 p{3PkOO l@"a@
+ END-OF-FILE mpage.arc
chmod 'u=rw,g=rw,o=r' 'mpage.arc'
echo '	-rw-rw-r--  1 kai         41555 Jan  1  1990 mpage.arc        (as sent)'
echo -n '	'
/bin/ls -l mpage.arc
exit 0


From: "Markus Bolz" <paulchen@cs.uni-sb.de>

In fact there are. I don't know any programs that take a *.tex file
and do some magic on that file so you get twosided output but some
DVI's are able to do so. 
If you use dvi2ps ( we've got version 3.1 ) you may specify a 
-t option that will allow positioning of your document on sheets with
DIN A4, letter, legal etc size AND twoup for printing two sides on
one sheet!

  dvi2ps -o twoup mydoc | lpr

will rotate the document by 90 degrees and put two sides on one page.
If you use Rockiki's DVIPS (that's my favorite dvi ) you'll have to
do some changes to the PostScript-Prologue. The file is called 
    tex.pro
and add the following:

/@twoup{[0 1 -1 0 0 0] concat 5.5 8.5 div dup scale Resolution dup dopage 
/isoddpage true def /eop{isoddpage not { clear showpage grestore} if 
/isoddpage isoddpage not def  } def /bop{isoddpage not {8.5 Inch 0 translate}
{ gsave 0 0 translate } ifelse } def /@end{isoddpage not {showpage} if
(%% dvips4.2 - VM used:)print @VMused @pri (. Unused:)print vmstatus @VMused
sub @pri pop pop(. Time:)print usertime btime sub 0.001 mul @pri( seconds\n)
print flush end}def} bdf /twoup{@twoup} bdf

somewhere. Now you can run your dvi 

dvips -t twoup mydocument | lpr

and you'll get what you wanted. There exist other dvi's in our universe,
but these two are the ones I use and they work the way I want them to 
work, so why...

OK. If you don't have one of these dvi's there is another way to get
two sides onto one page. It's a PostScript-File you have to send to
your printer first. This program is called Multi and as far as I know
available via anonymous ftp from labrea.stanford.edu. 
Ahh, wait. There is another program, dvidvi from Tomas Rockiki, that
does the things you want, but it's a little cryptic to use. 

Think that's all. Ehh, don't expect the output you get to be very readable!
The font LaTeX uses is a Bitmapfont and will loose some information 
on scaling operations. The only solution on this problem is to use
PostScript. Some people say they look nicer than CMF, but I don't agree.

Wuff, hope that helps.

     	--W. Scotty Paulchen--

rsingh@elaine18.stanford.edu (Rajesh Kumar Singh) (12/16/90)

Mr Ching Tsun write...............
-----------------------------------------

 I asked some time ago about how to print 2 pages on 1 page of physical paper.
Here are the replies that I got.  I've edited them a bit to save space 
and post only those that contain actual codes.

My sincere thanks to all who responded to my query.

By the way, many people mentioned Rockiki's dvidvi.
Does anyone know where to get it?
Please send your reply to:

	<chou@cs.ucla.edu>

- Ching Tsun

--------------[LONG articles wish  source codes follows]
Hello,

When I compiled mpage,  I did not  receive any eror messages.  However
when I try
to run mpage, it always gives me an error message "sh: ucb: not found"

Did anyone else try to use mpage  and got similar error? If so, what are
your coments.

Thanks. 
--raj 

Rajesh Kumar Singh               E-mail: rsingh@portia.stanford.edu
Dept of Civil Engineering            or  rsingh@cive.stanford.edu 
Blume Earthquake Center, Stanford University, Stanford, CA 94305