[comp.sources.misc] v19i014: pclcomp - HP-PCL graphics compression filter for printers., Part02/02

tony@sdd.hp.com (Tony Parkhurst) (05/03/91)

Submitted-by: Tony Parkhurst <tony@sdd.hp.com>
Posting-number: Volume 19, Issue 14
Archive-name: pclcomp/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  pclcomp.c
# Wrapped by kent@sparky on Thu May  2 12:17:56 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 2 (of 2)."'
if test -f 'pclcomp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pclcomp.c'\"
else
  echo shar: Extracting \"'pclcomp.c'\" \(52617 characters\)
  sed "s/^X//" >'pclcomp.c' <<'END_OF_FILE'
X/*
X**  Pclcomp -- PCL compression filter.
X**
X**  If you have any problems or errors to report, please send them to me:
X**
X**  Tony Parkhurst  
X** 
X**  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
X**
X**  Please send a copy of the graphic file that is a problem, and the version
X**  of pclcomp you are using.
X**
X**  All suggestions and requests are welcome.
X*/
X
X/*
X ***************************************************************************
X *
X * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $ 
X * $Date: 91/04/30 09:41:24 $ 
X * $Revision: 1.28 $
X *
X * Description:	Compresses pcl graphics files.
X *
X * Author:       Tony Parkhurst
X * Created:      890427
X * Language:     C
X *
X * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
X *
X ***************************************************************************
X */
X
X
X/*
X ***************************************************************************
X *
X * $Log:	pclcomp.c,v $
X * Revision 1.28  91/04/30  09:41:24  09:41:24  tony (Tony Parkhurst)
X * Now puts stdin and stdout in binary mode for MSDOS.
X * 	Changes courtesy of Mike Slomin.
X * Changed usage message a bit.
X * 
X * Revision 1.27  91/04/23  15:48:05  15:48:05  tony (Tony Parkhurst)
X * Added handling of plus_sign in value fields.
X * 
X * Revision 1.26  91/04/23  09:47:11  09:47:11  tony (Tony Parkhurst)
X * Pass thru unknown modes.
X * 
X * Revision 1.25  91/04/18  11:09:27  11:09:27  tony (Tony Parkhurst)
X * Added parse for fractions in values (i.e. <esc>(s16.67H)
X * 
X * Revision 1.24  91/04/10  14:16:30  14:16:30  tony (Tony Parkhurst)
X * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option
X * 
X * Revision 1.23  91/04/05  14:53:25  14:53:25  tony (Tony Parkhurst)
X * Added fixed for deskjet
X * Also added a stripping feature.
X * 
X * Revision 1.22  91/04/05  08:48:53  08:48:53  tony (Tony Parkhurst)
X * Added some error checkin on output for MS-DOS users.
X * 
X * Revision 1.21  91/04/04  12:53:32  12:53:32  tony (Tony Parkhurst)
X * Replaced parser.
X *    Now handles combined escape sequences.
X *    Now handles downloads.
X *    Now combines mode changes with data.
X * 
X * Revision 1.20  91/04/04  08:02:12  08:02:12  tony (Tony Parkhurst)
X * Removed some test code.
X * 
X * Revision 1.19  91/03/25  14:38:48  14:38:48  tony (Tony Parkhurst)
X * Changed defaults.
X * 
X * Revision 1.18  91/03/25  14:31:22  14:31:22  tony (Tony Parkhurst)
X * Re-worked memory allocation stuff for funky input files.
X * 
X * Revision 1.17  91/03/25  13:50:19  13:50:19  tony (Tony Parkhurst)
X * Use command line args for file w/o -i or -o.
X * 
X * Revision 1.16  91/03/04  14:23:15  14:23:15  tony (Tony Parkhurst)
X * Fixed to allow ONLY mode 3 if the user really wants it.
X * 
X * Revision 1.15  91/03/04  14:08:23  14:08:23  tony (Tony Parkhurst)
X * Added an exit(0) at the end of main.
X * fixed up some zerostrip stuff.
X * made mode 3 the highest priority mode.
X * 
X * Revision 1.14  91/02/20  13:57:27  13:57:27  tony (Tony Parkhurst)
X * Changed priority a bit.
X * Added some zerostripping for mode 2.
X * 
X * Revision 1.13  91/02/06  15:31:00  15:31:00  tony (Tony Parkhurst)
X * oops.
X * 
X * Revision 1.12  91/02/06  14:41:28  14:41:28  tony (Tony Parkhurst)
X * fixed usage message
X * 
X * Revision 1.11  91/02/06  14:38:10  14:38:10  tony (Tony Parkhurst)
X * Added file input and output for MS-DOS.
X * 
X * Revision 1.10  91/02/05  17:49:23  17:49:23  tony (Tony Parkhurst)
X * Fixed problem with zero stripped input.
X * 
X * Revision 1.9  91/02/05  16:11:39  16:11:39  tony (Tony Parkhurst)
X * Removed delay code and bitfield stuff.
X * 
X * Revision 1.8  91/02/05  11:04:53  11:04:53  tony (Tony Parkhurst)
X * Added io delay stuff for triton.
X * 
X * Revision 1.7  91/02/05  10:28:32  10:28:32  tony (Tony Parkhurst)
X * Fix for someone specifing ONLY mode 3.
X * 
X * Revision 1.6  91/01/29  14:13:09  14:13:09  tony (Tony Parkhurst)
X * Updated some comments.
X * 
X * Revision 1.5  91/01/29  13:26:24  13:26:24  tony (Tony Parkhurst)
X * Cleaned up, revamped a bit.
X * 
X * Revision 1.4  89/11/09  15:59:16  15:59:16  tony (Tony Parkhurst)
X * Fix for esc * r U coming after esc * r A.
X * 
X * Revision 1.3  89/10/24  11:31:12  11:31:12  tony (Tony Parkhurst)
X * Added parsing of <esc>*rC
X * 
X * Revision 1.2  89/10/13  09:56:46  09:56:46  tony (Tony Parkhurst)
X * Completely revamped by Greg G.
X * 
X * Revision 1.1  89/06/15  13:57:46  13:57:46  tony (Tony Parkhurst)
X * Initial revision
X * 
X *
X ***************************************************************************
X */
X 
Xstatic char *rcs_id="$Header: pclcomp.c,v 1.28 91/04/30 09:41:24 tony Exp $";
X
Xstatic char *rev_id="$Revision: 1.28 $";
X 
Xstatic char *author="Copyright (c) 1991, Tony Parkhurst";
X
X/* This program takes a PCL graphics file and will try and
X * optimize the compression.
X */
X
X/*
X *   This program was first a filter by Dean to compress pcl graphics.
X *
X *   This program now will do optimal compression using modes 0,1,2 and 3
X *
X *   Also, this program will take compressed input.
X *
X *   Input and output formats are standard pcl.
X *
X *   Imaging files will be compressed too.
X *
X *   pclcomp does not take advantage of Y-Offset for blank areas.  
X *   This is because Y-Offset creates white areas, but we don't do enough
X *   parsing to determine what value "white" has.  An application that
X *   can assume white values could make use of this sequence.
X *
X *   pclcomp does not do any of the block compression modes (4-8).
X *
X *   There are a few obvious inefficiencies that I will fix later.
X *
X *   Speaking of mode 3, there is a possible problem because each of the
X *   output row storage areas are 2* size of what mode 0 would be.  This
X *   is clearly sufficient for modes 1 and 2, but it may not be for mode
X *   3.  But I cannot think of a case in Mode 3 where this would be a problem.
X *
X *   An additional enhancement would be to compare all the planes in a
X *   multi-plane file (color) and if nothing changed, using mode 3, just
X *   output a single <esc>*b0W.
X */
X
X/*
X *   Usage:  pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile
X *
X *   Pclcomp will do graphics compression based on compression modes 0, 1, 2
X *   and 3.  (Mode 0 is uncompressed).  Pclcomp will accept all modes, and
X *   will attempt to optimize by selecting the best output mode for each
X *   row (or plane) of data.  By default, pclcomp will use all 4 modes, but
X *   the user may restrict which output modes to use with the -0123 options.
X *   For example, to use pclcomp for output to a PaintJet which only knows
X *   modes 0 and 1 (the XL also understands modes 2 and 3), one would use:
X *
X *      pclcomp -01 < infile > outfile
X *
X *   Note:  Mode 0 should always be allowed.  None of the other modes is
X *   guaranteed to be better than mode 0 in all cases.
X *
X *   The 'v' option tells the number of rows (planes) of data input and output
X *   in the different modes (to stderr).
X *
X *   The 'z' option is useful for PaintJet files using only modes 0 and 1, it
X *   does zero "stripping" as the PaintJet will do zero "filling".
X *   NOTE: 'z' now means do NOT zerostrip.
X *
X *   The 'n' option is to change the default number of pixels in a picture.
X *   The proper way to set the pixel width is with the source raster width
X *   sequence <esc*r#S>, but soo many applications just assume the default,
X *   which is different on different printers, so I am providing this
X *   command line option to set a new default.  One could also change the
X *   DEFAULT constant below (make sure it is a multiple of 8).  Currently
X *   it is set to 8" at 180 dpi (1440), but for 300 dpi, set it to 2400.
X *
X *   default is now 2400 (for 300dpi ala LaserJet).
X */
X
X#include <stdio.h>
X#include <string.h>
X
X#ifdef MSDOS
X#include <fcntl.h>
X#endif
X
X
X/* This flag is for code that uses bitfields for 68000 systems */
X#define BITFIELDS 0
X
X#define Get_Character() getchar()
X
X#define MIN(x,y)	( ((x) < (y)) ? (x) : (y) )
X
X#define TRUE 1
X#define FALSE 0
X
X#define ESC	27
X
X#define DEFAULT 2400		/* default width in pixels (multiple of 8) */
X
X#define MAXMODES  4
X#define MAXPLANES 8
X#define MAXBYTES 60000		/* now mostly meaningless, just a big number */
X
Xunsigned char	*seed_row[MAXPLANES];
Xunsigned char	*new_row;
Xunsigned char	*out_row[MAXMODES];
Xunsigned int	out_size[MAXMODES];
X
Xchar	memflag = FALSE;	/* set when memory has been allocated */
X
X
Xchar	mode0=FALSE,
X	mode1=FALSE,
X	mode2=FALSE,
X	mode3=FALSE;
X
Xunsigned char	num_planes=1;
Xunsigned char	curr_plane=0;
X
Xchar	imaging = FALSE;		/* not imaging, so no lockout */
X
Xchar	verbose = FALSE;
X
Xunsigned char	inmode = 0;		/* input compression mode */
Xunsigned char	outmode = 0;		/* output compression mode */
X
Xunsigned int	rasterwidth=DEFAULT/8;	/* width of picture, in bytes */
Xunsigned int	rpix = DEFAULT;		/* width of picture, in pixels */
X
Xunsigned char	invert=FALSE;		/* invert the data (obsolete) */
X
Xunsigned char	zerostrip= TRUE;	/* strip trailing zeros */
X
Xunsigned int	inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0};
X
Xchar	widthwarning = FALSE;	/* for trucation warning */
Xchar	firstrow = TRUE;	/* to prevent mode 3 from being first */
X
X
Xstruct {			/* this will hold the data for the  */
X     unsigned char model;	/* configuring of image processing   */
X     unsigned char pix_mode;
X     unsigned char inx_bits;
X     unsigned char red;
X     unsigned char green;
X     unsigned char blue;
X     short wr;
X     short wg;
X     short wb;
X     short br;
X     short bg;
X     short bb; 
X} imdata;
X
Xextern	unsigned char *malloc();
X
Xchar	*filein = NULL, *fileout = NULL;
X
X/*
X**  These variables are for the new parser.
X**  The new parser handles more sequences, and also deals with combined
X**  escape sequences better.
X*/
X
Xint	parameter;
Xint	group_char;
Xint	terminator;
Xint	old_terminator;
Xint	value;
Xint	frac;
Xint	scanf_count;
Xchar	in_sequence = FALSE;
Xchar	pass_seq;
Xchar	plus_sign;		/* for relative values */
X
X
X/* dummy buffer */
Xchar buf[BUFSIZ];
X
X/*
X**  If the printer is a DeskJet, then we must handle <esc>*rB differently
X**  Option '-d' will turn on this mode.
X*/
X
Xchar	deskjet = FALSE;
X
X
X/*
X**  Many drivers it seems put <esc>*rB<esc>*rA between each and every row
X**  of data.  This defeats compression mode 3 on a DeskJet, and also
X**  makes the PaintJet (not XL) quite slow.  This next flag "-s" on the
X**  command line, will attempt to do a reasonable job of stripping
X**  out the excess commands.
X**
X**  The in_graphics flag will be used to strip unwanted control chars from
X**  the file.
X*/
X
Xchar	strip_seq = FALSE;
Xchar	in_graphics = FALSE;
X
X
X/*
X**  Just for certain special cases, it would be nice to append an <esc>E reset
X**  to the end of the job.  Specify with "-r".
X*/
X
Xchar	reset_seq = FALSE;
X
X
Xchar	*progname;		/* to hold the program name for verbose */
X
X
X
X/*
X**
X**		Main program.
X**
X*/
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  int	c,j;
X  extern char *optarg;
X  extern int   optind;
X
X	progname = argv[0];
X
X#ifdef MSDOS
X	setmode(fileno(stdin), O_BINARY);	/* Place stdin and stdout in */
X	setmode(fileno(stdout), O_BINARY);	/* binary mode. (Mike Slomin)*/
X#endif
X
X	/* parse up the args here */
X
X  while ((c = getopt(argc, argv, "0123drsvzn:i:o:s")) != EOF )
X	switch(c){
X	case '0':
X			mode0++;
X			break;
X	case '1':
X			mode1++;
X			break;
X	case '2':
X			mode2++;
X			break;
X	case '3':
X			mode3++;
X			break;
X	case 'd':
X			deskjet++;
X			break;
X	case 'r':
X			reset_seq++;
X			break;
X	case 's':
X			strip_seq++;
X			break;
X	case 'v':
X			verbose++;
X			break;
X	case 'z':
X			zerostrip = FALSE;
X			break;
X	case 'n':
X			rpix = atoi(optarg);	/* new default */
X			rasterwidth = (rpix + 7) / 8;	/* round up */
X			break;
X
X	case 'i':
X			filein = optarg;
X			break;
X	case 'o':
X			fileout = optarg;
X			break;
X
X	case '?':
X	default:
X			fprintf(stderr, "Usage: %s [-0123drsvz] [-n###] [infile [outfile]]\n",
X				argv[0]);
X			exit(-1);
X	};
X
X	if ( verbose )
X	{
X		fprintf(stderr, "%s: %s\n", argv[0], rev_id);
X	}
X
X
X  if ( ! ( mode0 || mode1 || mode2 || mode3) )	/* any modes on? */
X	mode0 = mode1 = mode2 = mode3 = TRUE;	/* all  modes by default */
X
X	/*
X	**  Check to see if any file args were given on the command line.
X	**  Ones that were not preceded by a "-i" or "-o".
X	*/
X
X	if ( filein == NULL && optind < argc && argv[optind] != NULL )
X		filein = argv[optind++];
X
X	if ( fileout == NULL && optind < argc && argv[optind] != NULL )
X		fileout = argv[optind++];
X
X	/*
X	**  Now open files for stdin and stdout if provided by the user.
X	*/
X
X	if ( filein != NULL )		/* new input file */
X
X		if ( freopen( filein, "rb", stdin ) == NULL )
X		{
X			fprintf(stderr,"Unable to open %s for input.\n",filein);
X			exit(-42);
X		}
X
X	if ( fileout != NULL )		/* new output file */
X
X		if ( freopen( fileout, "wb", stdout ) == NULL )
X		{
X			fprintf(stderr, "Unable to open %s for output.\n",
X				fileout);
X			exit(-43);
X		}
X
X
X	/*
X	**  This is the pcl input parsing loop.
X	*/
X
X	while( ( c = getchar() ) != EOF )
X	{
X
X		/*  Ignore all chars until an escape char  */
X
X		/*
X		**  If we are in graphics, toss it if strip_seq is set.
X		*/
X
X		if ( c != ESC )
X		{
X			if ( !strip_seq || !in_graphics )
X				putchar(c);	/* pass it thru */
X
X			continue;	/* pop to the top of the loop */
X		}
X
X		/*
X		**  Now we have an escape sequence, get the parameter char.
X		*/
X
X		parameter = getchar();
X
X		if ( parameter == EOF )		/* oops */
X		{
X			putchar ( ESC );
X			fprintf(stderr, "Warning:  File ended with <esc>.\n");
X			break;			/* unexpected end of input */
X		}
X
X		/*
X		**  Now check to see if it is a two character sequence.
X		*/
X
X		if ( parameter >= '0' && parameter <= '~' )
X		{
X			putchar ( ESC );
X			putchar ( parameter );		/* pass it thru */
X
X			/*
X			**  If the second character is an E, then we
X			**  and the printer do a reset.
X			*/
X
X			if ( parameter == 'E' )
X			{
X				free_mem();
X				curr_plane = 0;
X				num_planes = 1;
X				imaging = FALSE;
X				inmode = 0;
X				outmode = 0;
X				in_graphics = FALSE;
X
X				/* can't do this if user gave value with -n.
X				rasterwidth = DEFAULT/8;
X				rpix = DEFAULT;
X				*/
X			}
X
X			continue;		/* return to the top */
X		}
X
X		/*
X		**  Now check to make sure that the parameter character is
X		**  within range.
X		*/
X
X		if ( parameter < '!' || parameter > '/' )
X		{
X			putchar ( ESC );
X			putchar ( parameter );
X
X			fprintf(stderr, "Warning:  Invalid escape sequence.\n");
X
X			continue;
X		}
X
X		/*
X		**  We are only interested in certain parameters, so pass
X		**  the rest of the sequences.
X		*/
X
X		/*
X		**  For the moment, we are only interested in '*' (graphics)
X		**  '(' and ')' (downloads).  Although we do not do anything
X		**  with downloads, we need to pass the binary data thru
X		**  untouched.
X		**  Also, '&' is handled too.
X		*/
X
X		if ( parameter != '*' && parameter != '(' 
X			&& parameter != ')' && parameter != '&' )
X		{
X			putchar ( ESC );
X			putchar ( parameter );
X			Flush_To_Term();		/* flush rest of seq. */
X			continue;
X		}
X
X
X		/*
X		**  Parameter character is in range, look for a valid group char
X		*/
X
X		group_char = getchar();
X
X		if ( group_char == EOF )	/* oops, ran out of input */
X		{
X			putchar ( ESC );
X			putchar ( parameter );
X
X			fprintf(stderr, "Warning:  Incomplete escape sequence.\n");
X			break;
X		}
X
X		/*
X		**  See if in proper range.  If it isn't, it is not an error
X		**  because the group character is optional for some sequences.
X		**  For the moment, we are not interested in those sequences,
X		**  so pass them thru.
X		*/
X
X		if ( group_char < '`' || group_char > '~' )
X		{
X			putchar ( ESC );
X			putchar ( parameter );
X			putchar ( group_char );
X			if ( group_char < '@' || group_char > '^' )
X				Flush_To_Term();	/* pass rest of seq. */
X			continue;
X		}
X
X		/*
X		**  Now we have a valid group character, decide if we want
X		**  to deal with this escape sequence.
X		**
X		**  Sequences we want do deal with include:
X		**
X		**    <esc>*r	** graphics
X		**    <esc>*b	** graphics
X		**    <esc>*v	** graphics
X		**
X		**  Sequences we must pass thru binary data:
X		**
X		**    <esc>*c	** pattern
X		**    <esc>*t	** obsolete
X		**    <esc>(f	** download char set
X		**    <esc>(s	** download char
X		**    <esc>)s	** download font
X		**    <esc>&a	** logical page
X		**    <esc>&l	** obsolete
X		**
X		*/
X
X		if (  ( parameter == '*'
X			&& group_char != 'r' && group_char != 'b' 
X			&& group_char != 'v' && group_char != 'c' 
X			&& group_char != 't' )
X		   || ( parameter == '&'
X			&& group_char != 'a' && group_char != 'l' )
X		   || ( parameter == '(' 
X			&& group_char != 'f' && group_char != 's' )
X		   || ( parameter == ')' && group_char != 's' ) )
X		{
X			/*
X			**  Definately not interested in the sequence.
X			*/
X
X			putchar ( ESC );
X			putchar ( parameter );
X			putchar ( group_char );
X			Flush_To_Term();
X			continue;
X		}
X
X		/*
X		**  Now set up a pass thru flag so we can ignore the entire
X		**  sequences of some of these.
X		*/
X
X		if ( parameter != '*' )
X			pass_seq = TRUE;
X		else if ( group_char == 'c' || group_char == 't' )
X			pass_seq = TRUE;
X		else
X			pass_seq = FALSE;
X
X
X		/*
X		**  Now we have a sequence that we are definately interested in.
X		**
X		**  Get the value field and terminator, and loop until final
X		**  terminator is found.
X		*/
X
X		do
X		{
X			/* first see if the value has a plus sign */
X
X			scanf_count = scanf(" + %d", &value );
X
X			if ( scanf_count == 1 )
X
X				plus_sign = TRUE;
X			else
X			{
X				plus_sign = FALSE;
X
X				scanf_count = scanf(" %d", &value );
X
X				if ( scanf_count == 0 )
X					value = 0;		/* by default */
X			}
X
X			terminator = getchar();
X
X			/*
X			** check to see if a fractional parameter was passed.
X			*/
X
X			frac = 0;	/* in case no fraction */
X
X			if ( terminator == '.' )
X			{
X				/*
X				**  Need to get fractional part.
X				**
X				**  This will not work properly if the
X				**  fraction is < .1 (i.e. a leading 0 is
X				**  present).  For example, the value
X				**  14.05 for point size, which would get
X				**  rounded to 14.00 for scalable fonts,
X				**  would get passed thru as 14.5 which is
X				**  rounded to 14.5.  This is unlikely to
X				**  happen as the 14.05 case is rare, but
X				**  it is valid PCL, so I will fix this
X				**  in the future.
X				*/
X
X				if ( scanf("%d", &frac) != 1 )
X				{
X					frac = 0;	/* no frac? */
X				}
X
X				/*
X				**  Now get the real terminator.
X				*/
X
X				terminator = getchar();
X			}
X
X
X			if ( terminator == EOF )	/* barf */
X			{
X				fprintf(stderr, "Warning:  Incomplete sequence at end of file.\n");
X				break;
X			}
X
X			/*
X			**  If the pass_seq flag is set, then just pass
X			**  it thru to stdout until a 'W' is found.
X			*/
X
X			if ( pass_seq )
X			{
X				/*
X				**  If not in sequence, then we output esc
X				**  otherwise, output the saved terminator.
X				*/
X
X				if ( !in_sequence )
X				{
X					in_sequence = TRUE;
X					putchar ( ESC );
X					putchar ( parameter );
X					putchar ( group_char );
X				} else
X				{
X					putchar ( old_terminator );
X				}
X
X				/* now pass the value */
X
X				if ( plus_sign )
X					putchar('+');
X
X				if ( scanf_count )	/* there was a value */
X					printf("%0d", value);
X				
X				/* need to output the fractional part */
X
X				if ( frac )
X					printf(".%0d", frac);
X
X				/*
X				**  We save the terminator, because we may
X				**  need to change it to upper case.
X				*/
X
X				old_terminator = terminator;
X
X				/* if binary data, pass it thru */
X
X				if ( terminator == 'W' )	/* aha */
X				{
X					putchar ( terminator );
X					in_sequence = FALSE;	/* terminates */
X					Flush_Bytes ( value );	/* pass data */
X				}
X
X				continue;
X			}
X
X			/*
X			**  Ok, this is a sequence we want to pay attention to.
X			**
X			**  Do_Graphics returns TRUE if we need to pass seq.
X			**
X			**  Note:  Do_Graphics modifies the parser vars such
X			**         as in_sequence.  This is because it may
X			**         have to output stuff directly.
X			*/
X
X			if ( Do_Graphics ( group_char, value, terminator ) )
X			{
X				/*
X				**  If not in sequence, then we output esc
X				**  otherwise, output the saved terminator.
X				*/
X
X				if ( !in_sequence )
X				{
X					in_sequence = TRUE;
X					putchar ( ESC );
X					putchar ( parameter );
X					putchar ( group_char );
X				} else
X				{
X					putchar ( old_terminator );
X				}
X
X				/* now pass the value */
X
X				if ( plus_sign )
X					putchar('+');
X
X				if ( scanf_count )	/* there was a value */
X					printf("%0d", value);
X
X				/* need to output the fractional part */
X
X				if ( frac )
X					printf(".%0d", frac);
X
X				/*
X				**  We save the terminator, because we may
X				**  need to change it to upper case.
X				*/
X
X				old_terminator = terminator;
X			}
X
X		} while ( terminator >= '`' && terminator <= '~' );
X
X		/*
X		** The oppsite test (above) may be more appropriate.  That is, 
X		** !(terminator >= '@' && terminator <= '^').
X		*/
X		
X		/*
X		**  If we were in a sequence, then we must terminate it.
X		**  If it was lower case, then it must be uppered.
X		*/
X
X		if ( in_sequence )
X		{
X			putchar ( terminator & 0xdf );		/* a ==> A */
X			in_sequence = FALSE;
X		}
X	}
X	
X
X	/*
X	**  If the user wants a reset, give him one.
X	*/
X
X	if ( reset_seq )
X	{
X		putchar ( ESC );
X		putchar ( 'E' );
X	}
X
X
X	/*
X	**  Finished up, so print stats and close output file.
X	*/
X
X	fclose(stdout);
X
X
X	if ( verbose )
X	{
X		for(j = 0; j < 4; j++)
X			fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]);
X		for(j = 0; j < 4; j++)
X			fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]);
X	}
X
X	exit(0);
X}
X
X
X/*
X**  Do_Graphics() takes the graphics escape sequence and performs the
X**  necessary functions.
X**  TRUE is returned if the escape sequence needs to be passed to the output.
X*/
X
Xint	Do_Graphics( group, num, terminator )
Xint	group, num, terminator;
X{
X
X	/*  first look at vW  */
X
X	if ( group == 'v' )
X
X		if ( terminator != 'W' )
X			
X			return ( TRUE );	/* pass it thru */
X		else
X		{
X			if ( !in_sequence )
X			{
X				putchar ( ESC );
X				putchar ( parameter );
X				putchar ( group );
X			} else
X				putchar ( old_terminator );
X
X			in_sequence = FALSE;		/* terminating */
X
X			printf("%0d", num);
X			putchar ( terminator );
X
X			free_mem();	/* reset memory */
X
X			imaging++;
X
X			fread(&imdata, MIN(num, 18), 1, stdin);
X			fwrite(&imdata, MIN(num, 18), 1, stdout);
X
X			num -= MIN(num, 18);
X
X			/* copy rest of unknown data */
X
X			if ( num > 0 )
X				Flush_Bytes(num);
X
X
X			switch(imdata.pix_mode){
X				case 0x00:
X					rasterwidth = (rpix + 7)/8;
X					num_planes = imdata.inx_bits;
X					break;
X				case 0x01:
X					rasterwidth = rpix*imdata.inx_bits/8;
X					break;
X				case 0x02:
X					rasterwidth = (rpix + 7)/8;
X					num_planes =imdata.red + imdata.green +
X					            imdata.blue;
X					break;
X				case 0x03:
X					rasterwidth = (imdata.red +
X					               imdata.green +
X					               imdata.blue)*rpix/8;
X					break;
X			}
X
X			return ( FALSE );
X		}
X
X	/*
X	**  Now deal with <esc>*r stuff
X	*/
X
X	if ( group == 'r' )
X	{
X		switch ( terminator )
X		{
X			case 'A':
X			case 'a':
X
X				/* in graphics mode, may do stripping */
X				in_graphics = TRUE;
X
X				/* if user wants to strip redundant seq */
X				if ( strip_seq && memflag )
X					return( FALSE );
X
X				curr_plane=0;
X				zero_seeds();	/* may allocate mem */
X				break;
X
X			case 'C':
X			case 'c':
X
X				/* not in graphics disable code strip */
X
X				in_graphics = FALSE;
X
X				if ( strip_seq )
X					return( FALSE );
X
X				inmode = 0;
X				outmode = 0;
X
X				free_mem();
X				curr_plane=0;
X				break;
X
X			case 'B':
X			case 'b':
X
X				/* not in graphics disable code strip */
X
X				in_graphics = FALSE;
X
X				if ( strip_seq )
X					return( FALSE );
X
X				if ( deskjet )	/* B resets modes on DJ */
X				{
X					inmode = 0;
X					outmode = 0;
X				}
X				free_mem();
X				curr_plane=0;
X				break;
X
X			case 'S':
X			case 's':
X
X				/* free mem in case widths changed */
X				free_mem();
X
X				rpix = num;
X
X				if (imaging){
X					switch(imdata.pix_mode)
X					{
X						case 0x00:
X							rasterwidth=(rpix+7)/8;
X							break;
X						case 0x01:
X							rasterwidth = 
X							 rpix*imdata.inx_bits/8;
X							break;
X						case 0x02:
X							rasterwidth=(rpix+7)/8;
X							break;
X						case 0x03:
X							rasterwidth = 
X							  (imdata.red 
X							  + imdata.green
X							  + imdata.blue)*rpix/8;
X							break;
X					}
X				} else
X					rasterwidth = (num + 7) / 8;
X				break;
X
X			case 'T':
X			case 't':
X				break;
X
X			case 'U':
X			case 'u':
X				curr_plane=0;
X				free_mem();	/* if ESC*rA came first */
X				num_planes = num;
X				imaging = FALSE;	/* goes off */
X				break;
X
X			default:
X				break;
X		}
X
X		return ( TRUE );		/* pass sequence on */
X
X	}	/* group r */
X
X	/*
X	**  Last and final group 'b'.  All the graphics data comes thru here.
X	*/
X
X
X	switch ( terminator )
X	{
X	       case 'm':
X	       case 'M':
X			inmode = num;
X			return ( FALSE );	/* we do NOT pass this */
X			break;
X
X	       /*
X	       **  <esc>*b#X is obsolete, don't bother with it. 
X	       **  If I did do something, then I would zero part of the
X	       **  seed rows.
X	       */
X
X	       case 'x':
X	       case 'X':
X			break;
X
X	       case 'y':
X	       case 'Y':
X			/* zero only if allocated */
X			if ( memflag )
X				zero_seeds();
X			break;
X
X	       case 'W':
X			if(!memflag)
X				zero_seeds();		/* get memory */
X
X			/* fire up sequence */
X
X			if ( !in_sequence )
X			{
X				putchar ( ESC );
X				putchar ( parameter );
X				putchar ( group );
X			} else
X				putchar ( old_terminator );
X
X			in_sequence = FALSE;		/* terminating */
X
X			if(curr_plane < num_planes) 
X			{
X
X				Process(num, 'W');
X
X				if ( curr_plane + 1 < num_planes )
X				{
X					/* now we have a problem */
X					zero_upper(curr_plane + 1);
X				}
X			} else
X				Process_extra(num,'W');   
X
X			curr_plane=0;
X
X			return ( FALSE );
X
X			break;
X
X	       case 'V':
X			if(!memflag)
X				zero_seeds();		/* get memory */
X			
X			/*
X			**  If curr_plane is the last plane, this should
X			**  be a 'W', not a 'V'.  I could change it,
X			**  then I would fix Process_extra() to not output
X			**  anything as the 'W' was already sent.
X			*/
X
X			if( curr_plane < num_planes ) 
X			{
X				/* fire up sequence */
X
X				if ( !in_sequence )
X				{
X					putchar ( ESC );
X					putchar ( parameter );
X					putchar ( group );
X				} else
X					putchar ( old_terminator );
X
X				in_sequence = FALSE;	/* terminating */
X			
X
X				Process(num, 'V');
X				curr_plane++;
X			} else
X				Process_extra(num,'V');
X
X			return ( FALSE );
X
X			break;
X
X		default:
X			break;
X	}
X
X	return ( TRUE );		/* pass sequence */
X}
X
X
X
X/*
X**  Flush_To_Term() simply passes thru input until a valid terminator
X**  character is found.  This is for unwanted escape sequences.
X*/
X
XFlush_To_Term()
X{
X	int	c;
X
X	do
X	{
X		c = getchar();
X
X		if ( c == EOF )			/* this is a problem */
X			return;
X		
X		putchar ( c );
X
X	} while ( c < '@' || c > '^' );
X}
X
X
X/*
X**  Flush_Bytes() simply transfers so many bytes directly from input to output.
X**  This is used to pass thru binary data that we are not interested in so that
X**  it will not confuse the parser.  I.e. downloads.
X*/
X
XFlush_Bytes( num )
Xunsigned int	num;
X{
X	int	bnum;
X
X	while ( num > 0 )
X	{
X		bnum = MIN ( BUFSIZ, num );
X
X		fread( buf, 1, bnum, stdin );
X
X		if ( fwrite( buf, 1, bnum, stdout ) < bnum )
X
X			/* check for error and exit */
X
X			if ( ferror(stdout) )
X			{
X				perror("Output error");
X				exit(-2);
X			}
X
X		num -= bnum;
X	}
X}
X
X
X
X
X/*----------------------------------------*/
X
X/*
X**	Zero_seeds() will allocate and initialize memory.
X**	If memory has already been allocated, then it will just initialize it.
X*/
X
X
Xzero_seeds()
X{
X	int r;
X
X	/* first allocate and init seed_rows for number of planes. */
X
X	for ( r = 0; r < num_planes ; r++)
X	{
X		if(!memflag)
X		{
X			seed_row[r] = (unsigned char *) malloc(rasterwidth);
X
X			if ( seed_row[r] == NULL )
X			{
X				fprintf(stderr, "Out of memory.\n");
X				exit(-3);
X			}
X		}
X
X		/* zero seeds for mode 3 */
X
X		memset(seed_row[r], 0, rasterwidth);
X	}
X
X
X	if(!memflag)
X	{
X		new_row = (unsigned char *) malloc(rasterwidth);
X
X		if ( new_row == NULL )
X		{
X			fprintf(stderr, "Out of memory.\n");
X			exit(-3);
X		}
X
X		for(r=0; r<MAXMODES; r++)
X		{
X			/* 2 * width is needed for modes 1, 2 and 3 */
X
X			out_row[r] = (unsigned char *) malloc(2 * rasterwidth);
X
X			if ( out_row[r] == NULL )
X			{
X				fprintf(stderr, "Out of memory.\n");
X				exit(-3);
X			}
X		}
X
X	}
X
X	memset(new_row, 0, rasterwidth);
X
X	memflag = TRUE;			/* memory is in place */
X}
X
X
X/* this routine if for incomplete transfers of data */
X
Xzero_upper(plane)
Xint	plane;
X{
X	int i;
X
X	/* assume memory already present */
X
X	for ( i = plane; i < num_planes; i++)
X		memset(seed_row[i], 0, rasterwidth);
X}
X
X
XProcess(inbytes, terminator)
Xint inbytes, terminator;
X{
X
X	int insize;
X	int minmode = 0;
X
X	inuse[inmode]++;
X
X	switch ( inmode ) {
X
X	case 0:
X		if ( !widthwarning && inbytes > rasterwidth )
X		{
X			/* This is likely to result in data truncation. */
X			widthwarning = TRUE;
X			fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n");
X		}
X
X		insize = Mode_0_Graphics(inbytes,rasterwidth,new_row,invert);
X		break;
X	case 1:
X		insize = Mode_1_Graphics(inbytes,rasterwidth,new_row,invert);
X		break;
X	case 2:
X		insize = Mode_2_Graphics(inbytes,rasterwidth,new_row,invert);
X		break;
X	case 3:
X		memcpy(new_row, seed_row[curr_plane], rasterwidth);
X		insize = Mode_3_Graphics(inbytes,rasterwidth,new_row,invert);
X		break;
X
X	default:		/* unknown mode? */
X
X		/*  Don't know what to do about seed rows, pass stuff thru */
X
X		fprintf(stderr, "%s: Unsupported compression mode %d.\n",
X			progname, inmode );
X
X		ChangeMode(inmode);	/* go to that mode */
X
X		/* <esc>*b has already been output */
X
X		printf("%1d%c", inbytes, terminator);
X
X		Flush_Bytes( inbytes );
X
X		firstrow = TRUE;		/* pop it out of mode 3 */
X
X		/*  Go ahead and clear the seed rows if present  */
X		if ( memflag )
X			zero_seeds();
X
X		return;
X
X	}
X
X
X	/*
X	**
X	*/
X
X	if ( mode0 )
X		/* actually, this is redundant since new_row is mode 0 */
X		out_size[0] = Output_0( new_row, out_row[0], rasterwidth );
X	else
X		out_size[0] = MAXBYTES+1;
X
X	if ( mode1 )
X		out_size[1] = Output_1( new_row, out_row[1], rasterwidth );
X	else
X		out_size[1] = MAXBYTES+1;
X
X	if ( mode2 )
X		out_size[2] = Output_2( new_row, out_row[2], rasterwidth );
X	else
X		out_size[2] = MAXBYTES+1;
X
X	if ( mode3 )
X		out_size[3] = Output_3( seed_row[curr_plane], new_row, out_row[3], rasterwidth );
X	else
X		out_size[3] = MAXBYTES+1;
X	
X
X	/*
X	**  Now determine which mode will give the best output.  Note that it
X	**  takes 5 bytes to change modes, so we penalize all modes that are
X	**  not the current output by 5 bytes.  This is to discourage changing
X	**  unless the benifit is worth it.  The exception to this rule is
X	**  mode 3.  We want to encourage going to mode 3 because of the seed
X	**  row behaviour.  That is, if we have a simple picture that does
X	**  not change much, and say each of the sizes for modes 1 and 2 always
X	**  comes out to 4 bytes of data, then if we add 5 to mode 3 each time,
X	**  it would never get selected.  But, we remove the penalty, and if
X	**  mode 3 is selected (0 bytes of data needed for mode 3), then each
X	**  succesive row only needs 0 bytes of data.  For a 300 dpi A size
X	**  picture with 3 data planes, this could be a savings of 37k bytes.
X	*/
X
X	/*
X	**  With the new parser, the output to change modes is now only
X	**  2 bytes, since it gets combined with the *b#W sequence.
X	**  So, I decided to ignore the switching penalty.
X	*/
X
X	/*
X	**  Due to a possible bug in PaintJet XL, don't allow mode 3 to be
X	**  selected for the first row of output.  But do allow it if the
X	**  user has no other mode selected.
X	*/
X
X	if ( firstrow && (mode0 || mode1 || mode2) )
X	{
X		out_size[3] = MAXBYTES+1;	/* disable mode 3 for now */
X
X		if ( terminator == 'W' )	/* last plane? */
X			firstrow = FALSE;	/* no longer first row */
X	}
X
X
X	minmode = 3;
X
X	if ( out_size[2] < out_size[minmode] )
X		minmode = 2;
X
X	if ( out_size[1] < out_size[minmode] )
X		minmode = 1;
X
X	if ( out_size[0] < out_size[minmode] )
X		minmode = 0;
X
X
X					/* I may remove this sometime */
X	if ( minmode != outmode )
X		if ( out_size[minmode] == out_size[outmode] )
X			minmode = outmode;
X
X
X	outuse[minmode]++;
X
X	if ( outmode != minmode )
X		ChangeMode( minmode );
X	
X	/* <esc>*b has already been output */
X
X	printf("%1d%c", out_size[minmode], terminator);
X
X	if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) < 
X							out_size[minmode] )
X
X		/* check for error and exit */
X
X		if ( ferror(stdout) )
X		{
X			perror("Output error");
X			exit(-2);
X		}
X
X
X	memcpy(seed_row[curr_plane], new_row, rasterwidth);
X
X}
X
XProcess_extra(bytes, terminator)
Xint bytes;
Xchar terminator;
X{
X	int  i;
X
X	/* toss any excess data */
X
X	for(i = 0; i < bytes; i++)
X	   getchar();
X
X	/* last plane? force move down to next row */
X
X	if(terminator == 'W')
X	{
X		/* <esc>*b has already been output */
X		printf("0W");
X
X		firstrow = FALSE;		/* not on first row anymore */
X
X	}
X
X}
X
XChangeMode(newmode)
Xint newmode;
X{
X	/*
X	**  <esc>*b have already been output.
X	**  terminator is 'm' instead of 'M' since will be followed by 'W'
X	*/
X	printf("%1dm", newmode);
X	outmode = newmode;
X}
X
X
X/* these decoders came from graphics.c in the gp parser */
X
X
X/* $PAGE$ */
X/*-----------------------------------------------------------------------*\
X |                                                                       |
X |  Function Name: Mode_0_Graphics                                       |
X |                                                                       |
X |  Description:                                                         |
X |                                                                       |
X |  This is the routine that handles a Mode 0 graphics block transfer    |
X |  to the Formatter Module.                                             |
X |                                                                       |
X\*-----------------------------------------------------------------------*/
X
X/* FUNCTION */
X
XMode_0_Graphics(input_bytes, output_bytes, address, invert)
X
Xunsigned int
X   input_bytes,                 /* Count of bytes to be read. */
X   output_bytes;                /* Count of bytes to be stored. */
X
Xunsigned char
X   *address;                    /* Pointer to where to store bytes. */
X
Xunsigned char			/* Boolean to request data inversion */
X	invert;
X
X{
X   /* LOCAL VARIABLES */
X
X   unsigned char
X      *store_ptr;               /* Pointer to where to store the byte. */
X
X   unsigned int
X      read_bytes,               /* Local copy of input_bytes. */
X      write_bytes;              /* Local copy of output_bytes. */
X
X   /* CODE */
X
X   /* Initialize the local variables. */
X
X   read_bytes = input_bytes;
X   write_bytes = output_bytes;
X   store_ptr = address;
X   
X
X   /* transfer the lesser of available bytes or available room */
X
X   if (invert)
X     Inv_Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
X   else
X     Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
X
X   /* now zero fill or throw excess data away */
X
X   if ( read_bytes > write_bytes )
X      Discard_Block(read_bytes - write_bytes);		/* throw excess */
X   else {
X      store_ptr += read_bytes;				/* adjust pointer */
X      write_bytes -= read_bytes;			/* zero fill count */
X
X      memset(store_ptr, 0, write_bytes);
X   }
X
X   return ( input_bytes );
X}
X
X/* $PAGE$ */
X/*-----------------------------------------------------------------------*\
X |                                                                       |
X |  Function Name: Mode_1_Graphics                                       |
X |                                                                       |
X |  Description:                                                         |
X |                                                                       |
X |  This is the routine that handles a Mode 1 graphics block transfer    |
X |  to the Formatter Module.  Mode 1 graphics is a compacted mode.       |
X |  The data in Mode 1 is in pairs.  The first byte is a replicate       |
X |  count and the second byte is the data.  The data byte is stored      |
X |  then replicated the replicate count.  Therefore a replicate count    |
X |  of 0 means the data byte is stored once.  The input byte count       |
X |  must be an even amount for the data to be in byte pairs.             |
X |                                                                       |
X\*-----------------------------------------------------------------------*/
X
X/* FUNCTION */
X
XMode_1_Graphics(input_bytes, output_bytes, address, invert)
X
Xunsigned int
X   input_bytes,                 /* Count of bytes to be read. */
X   output_bytes;                /* Count of bytes to be stored. */
X
Xunsigned char
X   *address;                    /* Pointer to where to store bytes. */
X
Xunsigned char			/* Boolean to request data inversion */
X	invert;
X
X{
X   /* LOCAL VARIABLES */
X
X   unsigned char
X      *store_ptr,               /* Pointer to where to store the byte. */
X      input_char;               /* Byte to be replicated. */
X
X   unsigned int
X      read_bytes,               /* Local copy of input_bytes. */
X      write_bytes;              /* Local copy of output_bytes. */
X
X   int
X      replicate_count;          /* Number of times to replicate data. */
X
X   /* CODE */
X
X   /* Initialize the local variables. */
X
X   read_bytes = input_bytes;
X   write_bytes = output_bytes;
X   store_ptr = address;
X   
X   /* Check for an even input count. */
X
X   if ((read_bytes % 2) == 0)
X   {
X      /* Even so input data is in pairs as required. So store the data. */
X   
X      while ((read_bytes != 0) && (write_bytes != 0))
X      {
X         /* First get the replicate count and the byte to store. */
X
X         replicate_count = (unsigned char) Get_Character();
X         input_char = (invert ? ~Get_Character() : Get_Character());
X         read_bytes -= 2;
X      
X         /* Since write_bytes was 0 there is room to store the byte. */
X
X         *store_ptr++ = input_char;
X         write_bytes--;
X         
X         /* Now make sure there is room for the replicated data. */
X
X         if (replicate_count > write_bytes)
X         {
X            /* Too much so limit to the room available. */
X
X            replicate_count = write_bytes;
X         }
X            
X         /* Update the amount to be written. */
X
X         write_bytes -= replicate_count;
X
X         /* Then replicate it. */
X
X         while (replicate_count != 0)
X         {
X            /* Store the byte the decrement the count. */
X
X            *store_ptr++ = input_char;
X         
X            replicate_count--;
X         }
X      }
X
X   }
X   /* Discard any left over input. */
X	/* OR */
X   /* Discard all of the input data as odd byte count. */
X
X   Discard_Block(read_bytes);
X
X   read_bytes = store_ptr - address;  /* how much was done? */
X
X   /* zero fill if needed */
X   memset(store_ptr, 0, write_bytes);
X         
X   return(read_bytes);
X}
X
X/* $PAGE$ */
X/*-----------------------------------------------------------------------*\
X |                                                                       |
X |  Function Name: Mode_2_Graphics                                       |
X |                                                                       |
X |  Description:                                                         |
X |                                                                       |
X |  This is the routine that handles a Mode 2 graphics block transfer    |
X |  to the Formatter Module.  Mode 2 graphics is a compacted mode.       |
X |  The data in Mode 2 is of one of two types.  The first type is a      |
X |  class type and the second type is a data type.  The class type is    |
X |  a single byte which is a combination of replicate count and a sub    |
X |  mode.  There are two sub modes within mode 2, sub mode 0 and sub     |
X |  mode 1.  These sub modes are flagged by the MSB of the class type    |
X |  byte.  If the MSB = 0 then the replicate count is the value of the   |
X |  class type byte.  In sub mode 0 the replicate count ranges from 1    |
X |  to 127.  In sub mode 0 the next byte and then the replicate count    |
X |  of bytes are of the data type and stored.  If the MSB = 1 then the   |
X |  sub mode is 1 and the replicate count is the negative value of the   |
X |  class type.  In sub mode 1 the replicate count is stored in 2s       |
X |  compliment form and ranges from -1 to -127.  In sub mode 1 the       |
X |  next byte is of the data type and is stored.  That data byte is      |
X |  then replicated and stored the replicate count.  If the class type   |
X |  byte is 128 then there is no data type byte.                         |
X |                                                                       |
X\*-----------------------------------------------------------------------*/
X
X/* FUNCTION */
X
XMode_2_Graphics(input_bytes, output_bytes, address, invert)
X
Xunsigned int
X   input_bytes,                 /* Count of bytes to be read. */
X   output_bytes;                /* Count of bytes to be stored. */
X
Xunsigned char
X   *address;                    /* Pointer to where to store bytes. */
X
Xunsigned char			/* Boolean to request data inversion */
X	invert;
X
X{
X   /* LOCAL VARIABLES */
X
X   unsigned char
X      *store_ptr,               /* Pointer to where to store the byte. */
X      input_char,               /* Byte to be replicated. */
X      sub_mode;                 /* Flag if sub mode is 0 or 1. */
X
X   unsigned int
X      read_bytes,               /* Local copy of input_bytes. */
X      write_bytes;              /* Local copy of output_bytes. */
X
X   int
X      replicate_count;          /* Number of times to replicate data. */
X
X   /* CODE */
X
X   /* Initialize the local variables. */
X
X   read_bytes = input_bytes;
X   write_bytes = output_bytes;
X   store_ptr = address;
X   
X   while ((read_bytes > 1) && (write_bytes != 0))
X   {
X      /* First get the class type byte and the first data type byte. */
X
X      replicate_count = Get_Character();
X
X      /* First check that this not an ignore class type. */
X
X      if (replicate_count != 128)
X      {
X         /* Not ignore so get the data class byte. */
X
X         input_char = (invert ? ~Get_Character() : Get_Character());
X         read_bytes -= 2;
X         
X        /* Since write_bytes wasn't 0 there is room to store the byte. */
X
X         *store_ptr++ = input_char;
X         write_bytes--;
X
X         /* Determine the sub mode. */
X   
X         if (replicate_count > 128)
X         {
X            /* Sub mode 1. */
X   
X            sub_mode = 1;
X            /* replicate count was unsigned char */
X            replicate_count = 256 - replicate_count;
X         }
X         else
X         {
X            /* Sub mode 0. */
X   
X            sub_mode = 0;
X
X            /* See if there is enoungh input left for the data byte count. */
X
X            if (replicate_count > read_bytes)
X            {
X               /* Too many data bytes so limit to the input left. */
X
X               replicate_count = read_bytes;
X            }
X         }
X            
X         /* Now make sure there is room for the replicated data. */
X   
X         if (replicate_count > write_bytes)
X         {
X            /* Too much so limit to the room available. */
X   
X            replicate_count = write_bytes;
X         }
X               
X         /* Update the amount to be written. */
X   
X         write_bytes -= replicate_count;
X   
X         /* Then replicate it. */
X   
X         if (sub_mode == 0)
X         {
X            /* Sub mode 0 so get the replicate count of data bytes. */
X   
X            if (invert)
X              Inv_Transfer_Block(replicate_count, store_ptr);
X            else
X              Transfer_Block(replicate_count, store_ptr);
X
X            read_bytes -= replicate_count;
X            
X            /* Find the last byte stored. */
X   
X            store_ptr += replicate_count;
X         }
X         else
X         {
X            /* Sub mode 1 so just duplicate the original byte. */
X   
X            while (replicate_count != 0)
X            {
X               /* Store the byte the decrement the count. */
X   
X               *store_ptr++ = input_char;
X            
X               replicate_count--;
X            }
X         }
X      }
X      else
X      {
X         /* Ignore class so don't get the data class byte. */
X
X         read_bytes--;
X      }
X   }
X
X   /* Now discard any left over input. */
X
X   Discard_Block(read_bytes);
X
X   read_bytes = store_ptr - address;
X
X   /* zero fill if needed */
X   memset(store_ptr, 0, write_bytes);
X         
X   
X   return(read_bytes);
X}
X
X/* $PAGE$ */
X/*-----------------------------------------------------------------------*\
X |                                                                       |
X |  Function Name: Mode_3_Graphics                                       |
X |                                                                       |
X |  Description:                                                         |
X |                                                                       |
X |  This is the routine that handles a Mode 3 graphics block transfer    |
X |  to the Formatter Module.  Mode 3 graphics is a compacted mode.       |
X |  Mode 3 data is a difference from one row to the next.  In order to   |
X |  work, each row must be saved to be a seed for the next.  This        |
X |  mode is used in conjuction with other compaction modes when the      |
X |  data remains fairly constant between pairs of rows.                  |
X |  The data is in the form:                                             |
X |  <command byte>[<optional offset bytes>]<1 to 8 replacement bytes>    |
X |  The command byte is in the form:                                     |
X |    Bits 5-7: Number of bytes to replace (1 - 8)                       |
X |    Bits 0-4: Relative offset from last byte.                          |
X |       (If the offset is 31, then add the following bytes for offset   |
X |       until an offset byte of less then 255 (but inclusive)           |
X |                                                                       |
X\*-----------------------------------------------------------------------*/
X
X/* FUNCTION */
X
XMode_3_Graphics(input_bytes, output_bytes, address, invert)
X
Xunsigned int
X   input_bytes,                 /* Count of bytes to be read. */
X   output_bytes;                /* Count of bytes to be stored. */
X
Xunsigned char
X   *address;                    /* Pointer to where to store bytes. */
X
Xunsigned char			/* Boolean to request data inversion */
X	invert;
X
X{
X   /* LOCAL VARIABLES */
X
X   unsigned char
X      *store_ptr,               /* Pointer to where to store the byte. */
X      input_char;               /* Byte to be changed. */
X
X   unsigned int
X      read_bytes,               /* Local copy of input_bytes. */
X      write_bytes;              /* Local copy of output_bytes. */
X
X   unsigned int
X      replace,			/* number of bytes to replace, 1-8 */
X      offset;			/* relative offset */
X
X#if BITFIELDS
X   union comtype {
X      unsigned char comchar;	/* command byte as char */
X      struct btype {
X	 unsigned repcount:3;	/* replace count 1-8 */
X	 unsigned roff:5;	/* relative offset 0-30 */
X      } bitf;
X   } command;
X#else
X	unsigned char	command;
X#endif
X
X   /* CODE */
X
X   /* Initialize the local variables. */
X
X   read_bytes = input_bytes;
X   write_bytes = output_bytes;
X   store_ptr = address;
X
X/* read_bytes has to be at least 2 to be valid */
X
X   while ( read_bytes > 1 && write_bytes > 0 ){
X
X      /* start by getting the command byte */
X
X      read_bytes--;
X
X#if BITFIELDS
X      command.comchar = Get_Character();
X
X      replace = command.bitf.repcount + 1;	/* replace count 1-8 */
X
X      offset = command.bitf.roff;		/* offset 0-30, 31= extend */
X#else
X	command = Get_Character();
X	replace = (command >> 5) + 1;
X	offset = command & 0x1f;
X#endif
X
X      store_ptr += offset;
X      write_bytes -= offset;
X
X      if ( offset == 31 )		/* get more offsets */
X	 do{
X
X	    offset = Get_Character();
X
X	    read_bytes--;
X            if ( read_bytes == 0 )		/* premature finish? */
X	       return;				/* no zero fill wih 3 */
X
X	    store_ptr += offset;
X            write_bytes -= offset;
X
X	 } while (offset == 255);	/* 255 = keep going */
X      
X      /* now do the byte replacement */
X
X      while ( replace-- && write_bytes > 0 && read_bytes > 0 ){
X	 
X	 *store_ptr++ = (invert ? ~Get_Character() : Get_Character() );
X
X	 read_bytes--;
X	 write_bytes--;
X      }
X   }
X   
X   /* don't do any zero fill with mode 3 */
X
X   /* discard any leftover input */
X
X   Discard_Block(read_bytes);
X
X   return( store_ptr - address );
X}
X
X
XDiscard_Block(count)
Xunsigned int count;
X{
X	while ( count-- )
X		getchar();
X}
X
XTransfer_Block( count, dest )
Xunsigned int count;
Xunsigned char *dest;
X{
X	fread(dest, 1, count, stdin);
X}
X
X/* this doesn't invert at the moment */
X
XInv_Transfer_Block( count, dest )
Xunsigned int count;
Xunsigned char *dest;
X{
X	fread(dest, 1, count, stdin);
X}
X
X
XOutput_0(src, dest, count)
Xunsigned char *src, *dest;
Xint count;
X{
X	memcpy(dest, src, count);
X
X	if ( zerostrip )
X		while ( count && dest[count-1] == 0 )
X			count--;
X
X	return(count);
X
X}
X
XOutput_1(src, dest, count)
Xunsigned char *src, *dest;
Xregister int count;
X{
X	unsigned char *optr = dest, *iptr;
X	int k,c;
X
X	if ( zerostrip )			/* strip zeros */
X	{
X		iptr = src + count - 1;		/* point to end of data */
X
X		while ( count > 0 && *iptr-- == 0 )	/* hunt thru 0's */
X			count--;
X	}
X
X	iptr = src;
X
X	while ( count ){
X		
X		c = *iptr++;		/* get value to work with */
X		count--;
X
X		k = 0;
X
X		while ( *iptr == c && k < 255 && count ){
X			k++;
X			iptr++;
X			count--;
X		}
X
X		*optr++ = k;		/* output repeat count */
X		*optr++ = c;		/* output value */
X	}
X
X	count = optr - dest;		/* for return value */
X
X	return ( count );
X}
X
X
XOutput_2(src, dest, count)
Xunsigned char *src, *dest;
Xregister int count;
X{
X	unsigned char *optr = dest, *iptr;
X	int k,c;
X	unsigned char *tptr, *tptr1, *tptr2;
X	int tk,tc;
X
X
X	if ( zerostrip )			/* strip zeros */
X	{
X		iptr = src + count - 1;		/* point to end of data */
X
X		while ( count > 0 && *iptr-- == 0 )	/* hunt thru 0's */
X			count--;
X	}
X
X	iptr = src;
X
X	while ( count ){
X		
X		c = *iptr++;		/* get value to work with */
X		count--;
X
X		k = 0;
X
X		while ( *iptr == c && k < 127 && count ){
X			k++;
X			iptr++;
X			count--;
X		}
X
X		if ( k >= 1 ){
X			*optr++ = 256 - k;	/* output repeat count */
X			*optr++ = c;		/* output value */
X		} else {
X					/* a two byte replicate run will
X					 * be sent as a repeated byte 
X					 * unless it is preceeded and
X					 * and followed by a literal run,
X					 * in which case it is merged
X					 * into the run.
X					 */
X			tk = 0;
X			tc = c;
X			tptr = iptr;
X			tptr1 = tptr;
X			tptr1++;
X			tptr2 = tptr1;
X			tptr2++;
X
X			while ( tk < 128 && (count - tk) > 0    && 
X				((*tptr != tc) || (*tptr == tc &&
X				                   (count - tk - 1) > 0 &&
X				                   *tptr1 != *tptr &&
X				                   *tptr2 != *tptr1))){
X
X				tc = *tptr++;
X				tk++;
X				tptr1++;
X				tptr2++;
X			}
X
X			if ( count && tk )
X				tk--;
X
X			*optr++ = tk;		/* output count */
X			*optr++ = c;		/* output firstvalue */
X
X			while ( tk-- > 0){
X				*optr++ = *iptr++;
X				count--;
X			}
X
X		}
X	}
X
X	count = optr - dest;		/* for return value */
X
X	return ( count );
X}
X
XOutput_3(seed, new, dest, count)
Xunsigned char *seed, *new, *dest;
Xint count;
X{
X	unsigned char *sptr=seed, *nptr=new, *dptr=dest;
X	int i,j;
X
X
X#if BITFIELDS
X   union comtype {
X      unsigned char comchar;	/* command byte as char */
X      struct btype {
X	 unsigned repcount:3;	/* replace count 1-8 */
X	 unsigned roff:5;	/* relative offset 0-30 */
X      } bitf;
X   } command;
X#else
X	unsigned char	command;
X#endif
X
X	while ( count > 0 ){
X		i = 0;
X
X					/* find first diff */
X		while ( *sptr == *nptr && i < count ){
X			i++;
X			sptr++;
X			nptr++;
X		}
X
X		if ( i >= count )	/* too far to find diff */
X			return(dptr - dest);	/* bail */
X
X		count -= i;
X		
X		/* now count how many bytes to change */
X
X		for ( j = 1; j < 8; j++)	/* j == 0 is already known */
X			if ( j > count || sptr[j] == nptr[j] )
X				break;
X		
X		j--;	/* adjust */
X
X#if BITFIELDS
X		command.bitf.repcount = j;	/* 0-7 ==> 1-8 */
X
X		command.bitf.roff = MIN ( i, 31 );
X
X		*dptr++ = command.comchar;	/* output command */
X#else
X		command = (j << 5);
X		command += MIN( i, 31 );
X		*dptr++ = command;
X#endif
X
X		if ( i == 31 )
X			*dptr++ = 0;
X		
X		i -= MIN (i, 31);
X
X		while( i ){
X			*dptr++ = MIN ( i, 255 );
X
X			if ( i == 255 )
X				*dptr++ = 0;
X			
X			i -= MIN ( i, 255 );
X		}
X
X		while (j-- >= 0){
X			*dptr++ = *nptr++;
X			sptr++;
X			count--;
X		}
X	}
X
X	return ( dptr - dest );
X}
X
X
X/*----------------------------------------------------------------------*\
X * This is here in case <ESC>*rU is sent after <ESC>*r#A, in which case *
X * we must deallocate the memory to provide for a different amount of   *
X * planes when graphics are sent.                                       *
X\*----------------------------------------------------------------------*/
X
Xfree_mem()	
X{
X	int r;
X
X
X	if ( !memflag )
X		return;		/* no memory to free */
X
X	free(new_row);
X
X	for(r = MAXMODES -1; r >= 0; r--)
X		free(out_row[r]);
X
X	for(r = num_planes - 1; r >= 0; r--)
X		free(seed_row[r]);
X
X	memflag = FALSE;
X}
END_OF_FILE
  if test 52617 -ne `wc -c <'pclcomp.c'`; then
    echo shar: \"'pclcomp.c'\" unpacked with wrong size!
  fi
  # end of 'pclcomp.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.