home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / pclcomp.lzh / pclcomp.2 / pclcomp.c
C/C++ Source or Header  |  1991-05-19  |  53KB  |  2,250 lines

  1. /*
  2. **  Pclcomp -- PCL compression filter.
  3. **
  4. **  If you have any problems or errors to report, please send them to me:
  5. **
  6. **  Tony Parkhurst  
  7. ** 
  8. **  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
  9. **
  10. **  Please send a copy of the graphic file that is a problem, and the version
  11. **  of pclcomp you are using.
  12. **
  13. **  All suggestions and requests are welcome.
  14. */
  15.  
  16. /*
  17.  ***************************************************************************
  18.  *
  19.  * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $ 
  20.  * $Date: 91/04/30 09:41:24 $ 
  21.  * $Revision: 1.28 $
  22.  *
  23.  * Description:    Compresses pcl graphics files.
  24.  *
  25.  * Author:       Tony Parkhurst
  26.  * Created:      890427
  27.  * Language:     C
  28.  *
  29.  * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
  30.  *
  31.  ***************************************************************************
  32.  */
  33.  
  34.  
  35. /*
  36.  ***************************************************************************
  37.  *
  38.  * $Log:    pclcomp.c,v $
  39.  * Revision 1.28  91/04/30  09:41:24  09:41:24  tony (Tony Parkhurst)
  40.  * Now puts stdin and stdout in binary mode for MSDOS.
  41.  *     Changes courtesy of Mike Slomin.
  42.  * Changed usage message a bit.
  43.  * 
  44.  * Revision 1.27  91/04/23  15:48:05  15:48:05  tony (Tony Parkhurst)
  45.  * Added handling of plus_sign in value fields.
  46.  * 
  47.  * Revision 1.26  91/04/23  09:47:11  09:47:11  tony (Tony Parkhurst)
  48.  * Pass thru unknown modes.
  49.  * 
  50.  * Revision 1.25  91/04/18  11:09:27  11:09:27  tony (Tony Parkhurst)
  51.  * Added parse for fractions in values (i.e. <esc>(s16.67H)
  52.  * 
  53.  * Revision 1.24  91/04/10  14:16:30  14:16:30  tony (Tony Parkhurst)
  54.  * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option
  55.  * 
  56.  * Revision 1.23  91/04/05  14:53:25  14:53:25  tony (Tony Parkhurst)
  57.  * Added fixed for deskjet
  58.  * Also added a stripping feature.
  59.  * 
  60.  * Revision 1.22  91/04/05  08:48:53  08:48:53  tony (Tony Parkhurst)
  61.  * Added some error checkin on output for MS-DOS users.
  62.  * 
  63.  * Revision 1.21  91/04/04  12:53:32  12:53:32  tony (Tony Parkhurst)
  64.  * Replaced parser.
  65.  *    Now handles combined escape sequences.
  66.  *    Now handles downloads.
  67.  *    Now combines mode changes with data.
  68.  * 
  69.  * Revision 1.20  91/04/04  08:02:12  08:02:12  tony (Tony Parkhurst)
  70.  * Removed some test code.
  71.  * 
  72.  * Revision 1.19  91/03/25  14:38:48  14:38:48  tony (Tony Parkhurst)
  73.  * Changed defaults.
  74.  * 
  75.  * Revision 1.18  91/03/25  14:31:22  14:31:22  tony (Tony Parkhurst)
  76.  * Re-worked memory allocation stuff for funky input files.
  77.  * 
  78.  * Revision 1.17  91/03/25  13:50:19  13:50:19  tony (Tony Parkhurst)
  79.  * Use command line args for file w/o -i or -o.
  80.  * 
  81.  * Revision 1.16  91/03/04  14:23:15  14:23:15  tony (Tony Parkhurst)
  82.  * Fixed to allow ONLY mode 3 if the user really wants it.
  83.  * 
  84.  * Revision 1.15  91/03/04  14:08:23  14:08:23  tony (Tony Parkhurst)
  85.  * Added an exit(0) at the end of main.
  86.  * fixed up some zerostrip stuff.
  87.  * made mode 3 the highest priority mode.
  88.  * 
  89.  * Revision 1.14  91/02/20  13:57:27  13:57:27  tony (Tony Parkhurst)
  90.  * Changed priority a bit.
  91.  * Added some zerostripping for mode 2.
  92.  * 
  93.  * Revision 1.13  91/02/06  15:31:00  15:31:00  tony (Tony Parkhurst)
  94.  * oops.
  95.  * 
  96.  * Revision 1.12  91/02/06  14:41:28  14:41:28  tony (Tony Parkhurst)
  97.  * fixed usage message
  98.  * 
  99.  * Revision 1.11  91/02/06  14:38:10  14:38:10  tony (Tony Parkhurst)
  100.  * Added file input and output for MS-DOS.
  101.  * 
  102.  * Revision 1.10  91/02/05  17:49:23  17:49:23  tony (Tony Parkhurst)
  103.  * Fixed problem with zero stripped input.
  104.  * 
  105.  * Revision 1.9  91/02/05  16:11:39  16:11:39  tony (Tony Parkhurst)
  106.  * Removed delay code and bitfield stuff.
  107.  * 
  108.  * Revision 1.8  91/02/05  11:04:53  11:04:53  tony (Tony Parkhurst)
  109.  * Added io delay stuff for triton.
  110.  * 
  111.  * Revision 1.7  91/02/05  10:28:32  10:28:32  tony (Tony Parkhurst)
  112.  * Fix for someone specifing ONLY mode 3.
  113.  * 
  114.  * Revision 1.6  91/01/29  14:13:09  14:13:09  tony (Tony Parkhurst)
  115.  * Updated some comments.
  116.  * 
  117.  * Revision 1.5  91/01/29  13:26:24  13:26:24  tony (Tony Parkhurst)
  118.  * Cleaned up, revamped a bit.
  119.  * 
  120.  * Revision 1.4  89/11/09  15:59:16  15:59:16  tony (Tony Parkhurst)
  121.  * Fix for esc * r U coming after esc * r A.
  122.  * 
  123.  * Revision 1.3  89/10/24  11:31:12  11:31:12  tony (Tony Parkhurst)
  124.  * Added parsing of <esc>*rC
  125.  * 
  126.  * Revision 1.2  89/10/13  09:56:46  09:56:46  tony (Tony Parkhurst)
  127.  * Completely revamped by Greg G.
  128.  * 
  129.  * Revision 1.1  89/06/15  13:57:46  13:57:46  tony (Tony Parkhurst)
  130.  * Initial revision
  131.  * 
  132.  *
  133.  ***************************************************************************
  134.  */
  135.  
  136. static char *rcs_id="$Header: pclcomp.c,v 1.28 91/04/30 09:41:24 tony Exp $";
  137.  
  138. static char *rev_id="$Revision: 1.28 $";
  139.  
  140. static char *author="Copyright (c) 1991, Tony Parkhurst";
  141.  
  142. /* This program takes a PCL graphics file and will try and
  143.  * optimize the compression.
  144.  */
  145.  
  146. /*
  147.  *   This program was first a filter by Dean to compress pcl graphics.
  148.  *
  149.  *   This program now will do optimal compression using modes 0,1,2 and 3
  150.  *
  151.  *   Also, this program will take compressed input.
  152.  *
  153.  *   Input and output formats are standard pcl.
  154.  *
  155.  *   Imaging files will be compressed too.
  156.  *
  157.  *   pclcomp does not take advantage of Y-Offset for blank areas.  
  158.  *   This is because Y-Offset creates white areas, but we don't do enough
  159.  *   parsing to determine what value "white" has.  An application that
  160.  *   can assume white values could make use of this sequence.
  161.  *
  162.  *   pclcomp does not do any of the block compression modes (4-8).
  163.  *
  164.  *   There are a few obvious inefficiencies that I will fix later.
  165.  *
  166.  *   Speaking of mode 3, there is a possible problem because each of the
  167.  *   output row storage areas are 2* size of what mode 0 would be.  This
  168.  *   is clearly sufficient for modes 1 and 2, but it may not be for mode
  169.  *   3.  But I cannot think of a case in Mode 3 where this would be a problem.
  170.  *
  171.  *   An additional enhancement would be to compare all the planes in a
  172.  *   multi-plane file (color) and if nothing changed, using mode 3, just
  173.  *   output a single <esc>*b0W.
  174.  */
  175.  
  176. /*
  177.  *   Usage:  pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile
  178.  *
  179.  *   Pclcomp will do graphics compression based on compression modes 0, 1, 2
  180.  *   and 3.  (Mode 0 is uncompressed).  Pclcomp will accept all modes, and
  181.  *   will attempt to optimize by selecting the best output mode for each
  182.  *   row (or plane) of data.  By default, pclcomp will use all 4 modes, but
  183.  *   the user may restrict which output modes to use with the -0123 options.
  184.  *   For example, to use pclcomp for output to a PaintJet which only knows
  185.  *   modes 0 and 1 (the XL also understands modes 2 and 3), one would use:
  186.  *
  187.  *      pclcomp -01 < infile > outfile
  188.  *
  189.  *   Note:  Mode 0 should always be allowed.  None of the other modes is
  190.  *   guaranteed to be better than mode 0 in all cases.
  191.  *
  192.  *   The 'v' option tells the number of rows (planes) of data input and output
  193.  *   in the different modes (to stderr).
  194.  *
  195.  *   The 'z' option is useful for PaintJet files using only modes 0 and 1, it
  196.  *   does zero "stripping" as the PaintJet will do zero "filling".
  197.  *   NOTE: 'z' now means do NOT zerostrip.
  198.  *
  199.  *   The 'n' option is to change the default number of pixels in a picture.
  200.  *   The proper way to set the pixel width is with the source raster width
  201.  *   sequence <esc*r#S>, but soo many applications just assume the default,
  202.  *   which is different on different printers, so I am providing this
  203.  *   command line option to set a new default.  One could also change the
  204.  *   DEFAULT constant below (make sure it is a multiple of 8).  Currently
  205.  *   it is set to 8" at 180 dpi (1440), but for 300 dpi, set it to 2400.
  206.  *
  207.  *   default is now 2400 (for 300dpi ala LaserJet).
  208.  */
  209.  
  210. #include <stdio.h>
  211. #include <string.h>
  212.  
  213. #ifdef MSDOS
  214. #include <fcntl.h>
  215. #endif
  216.  
  217.  
  218. /* This flag is for code that uses bitfields for 68000 systems */
  219. #define BITFIELDS 0
  220.  
  221. #define Get_Character() getchar()
  222.  
  223. #define MIN(x,y)    ( ((x) < (y)) ? (x) : (y) )
  224.  
  225. #define TRUE 1
  226. #define FALSE 0
  227.  
  228. #define ESC    27
  229.  
  230. #define DEFAULT 2400        /* default width in pixels (multiple of 8) */
  231.  
  232. #define MAXMODES  4
  233. #define MAXPLANES 8
  234. #define MAXBYTES 60000        /* now mostly meaningless, just a big number */
  235.  
  236. unsigned char    *seed_row[MAXPLANES];
  237. unsigned char    *new_row;
  238. unsigned char    *out_row[MAXMODES];
  239. unsigned int    out_size[MAXMODES];
  240.  
  241. char    memflag = FALSE;    /* set when memory has been allocated */
  242.  
  243.  
  244. char    mode0=FALSE,
  245.     mode1=FALSE,
  246.     mode2=FALSE,
  247.     mode3=FALSE;
  248.  
  249. unsigned char    num_planes=1;
  250. unsigned char    curr_plane=0;
  251.  
  252. char    imaging = FALSE;        /* not imaging, so no lockout */
  253.  
  254. char    verbose = FALSE;
  255.  
  256. unsigned char    inmode = 0;        /* input compression mode */
  257. unsigned char    outmode = 0;        /* output compression mode */
  258.  
  259. unsigned int    rasterwidth=DEFAULT/8;    /* width of picture, in bytes */
  260. unsigned int    rpix = DEFAULT;        /* width of picture, in pixels */
  261.  
  262. unsigned char    invert=FALSE;        /* invert the data (obsolete) */
  263.  
  264. unsigned char    zerostrip= TRUE;    /* strip trailing zeros */
  265.  
  266. unsigned int    inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0};
  267.  
  268. char    widthwarning = FALSE;    /* for trucation warning */
  269. char    firstrow = TRUE;    /* to prevent mode 3 from being first */
  270.  
  271.  
  272. struct {            /* this will hold the data for the  */
  273.      unsigned char model;    /* configuring of image processing   */
  274.      unsigned char pix_mode;
  275.      unsigned char inx_bits;
  276.      unsigned char red;
  277.      unsigned char green;
  278.      unsigned char blue;
  279.      short wr;
  280.      short wg;
  281.      short wb;
  282.      short br;
  283.      short bg;
  284.      short bb; 
  285. } imdata;
  286.  
  287. extern    unsigned char *malloc();
  288.  
  289. char    *filein = NULL, *fileout = NULL;
  290.  
  291. /*
  292. **  These variables are for the new parser.
  293. **  The new parser handles more sequences, and also deals with combined
  294. **  escape sequences better.
  295. */
  296.  
  297. int    parameter;
  298. int    group_char;
  299. int    terminator;
  300. int    old_terminator;
  301. int    value;
  302. int    frac;
  303. int    scanf_count;
  304. char    in_sequence = FALSE;
  305. char    pass_seq;
  306. char    plus_sign;        /* for relative values */
  307.  
  308.  
  309. /* dummy buffer */
  310. char buf[BUFSIZ];
  311.  
  312. /*
  313. **  If the printer is a DeskJet, then we must handle <esc>*rB differently
  314. **  Option '-d' will turn on this mode.
  315. */
  316.  
  317. char    deskjet = FALSE;
  318.  
  319.  
  320. /*
  321. **  Many drivers it seems put <esc>*rB<esc>*rA between each and every row
  322. **  of data.  This defeats compression mode 3 on a DeskJet, and also
  323. **  makes the PaintJet (not XL) quite slow.  This next flag "-s" on the
  324. **  command line, will attempt to do a reasonable job of stripping
  325. **  out the excess commands.
  326. **
  327. **  The in_graphics flag will be used to strip unwanted control chars from
  328. **  the file.
  329. */
  330.  
  331. char    strip_seq = FALSE;
  332. char    in_graphics = FALSE;
  333.  
  334.  
  335. /*
  336. **  Just for certain special cases, it would be nice to append an <esc>E reset
  337. **  to the end of the job.  Specify with "-r".
  338. */
  339.  
  340. char    reset_seq = FALSE;
  341.  
  342.  
  343. char    *progname;        /* to hold the program name for verbose */
  344.  
  345.  
  346.  
  347. /*
  348. **
  349. **        Main program.
  350. **
  351. */
  352.  
  353. main(argc, argv)
  354. int argc;
  355. char *argv[];
  356. {
  357.   int    c,j;
  358.   extern char *optarg;
  359.   extern int   optind;
  360.  
  361.     progname = argv[0];
  362.  
  363. #ifdef MSDOS
  364.     setmode(fileno(stdin), O_BINARY);    /* Place stdin and stdout in */
  365.     setmode(fileno(stdout), O_BINARY);    /* binary mode. (Mike Slomin)*/
  366. #endif
  367.  
  368.     /* parse up the args here */
  369.  
  370.   while ((c = getopt(argc, argv, "0123drsvzn:i:o:s")) != EOF )
  371.     switch(c){
  372.     case '0':
  373.             mode0++;
  374.             break;
  375.     case '1':
  376.             mode1++;
  377.             break;
  378.     case '2':
  379.             mode2++;
  380.             break;
  381.     case '3':
  382.             mode3++;
  383.             break;
  384.     case 'd':
  385.             deskjet++;
  386.             break;
  387.     case 'r':
  388.             reset_seq++;
  389.             break;
  390.     case 's':
  391.             strip_seq++;
  392.             break;
  393.     case 'v':
  394.             verbose++;
  395.             break;
  396.     case 'z':
  397.             zerostrip = FALSE;
  398.             break;
  399.     case 'n':
  400.             rpix = atoi(optarg);    /* new default */
  401.             rasterwidth = (rpix + 7) / 8;    /* round up */
  402.             break;
  403.  
  404.     case 'i':
  405.             filein = optarg;
  406.             break;
  407.     case 'o':
  408.             fileout = optarg;
  409.             break;
  410.  
  411.     case '?':
  412.     default:
  413.             fprintf(stderr, "Usage: %s [-0123drsvz] [-n###] [infile [outfile]]\n",
  414.                 argv[0]);
  415.             exit(-1);
  416.     };
  417.  
  418.     if ( verbose )
  419.     {
  420.         fprintf(stderr, "%s: %s\n", argv[0], rev_id);
  421.     }
  422.  
  423.  
  424.   if ( ! ( mode0 || mode1 || mode2 || mode3) )    /* any modes on? */
  425.     mode0 = mode1 = mode2 = mode3 = TRUE;    /* all  modes by default */
  426.  
  427.     /*
  428.     **  Check to see if any file args were given on the command line.
  429.     **  Ones that were not preceded by a "-i" or "-o".
  430.     */
  431.  
  432.     if ( filein == NULL && optind < argc && argv[optind] != NULL )
  433.         filein = argv[optind++];
  434.  
  435.     if ( fileout == NULL && optind < argc && argv[optind] != NULL )
  436.         fileout = argv[optind++];
  437.  
  438.     /*
  439.     **  Now open files for stdin and stdout if provided by the user.
  440.     */
  441.  
  442.     if ( filein != NULL )        /* new input file */
  443.  
  444.         if ( freopen( filein, "rb", stdin ) == NULL )
  445.         {
  446.             fprintf(stderr,"Unable to open %s for input.\n",filein);
  447.             exit(-42);
  448.         }
  449.  
  450.     if ( fileout != NULL )        /* new output file */
  451.  
  452.         if ( freopen( fileout, "wb", stdout ) == NULL )
  453.         {
  454.             fprintf(stderr, "Unable to open %s for output.\n",
  455.                 fileout);
  456.             exit(-43);
  457.         }
  458.  
  459.  
  460.     /*
  461.     **  This is the pcl input parsing loop.
  462.     */
  463.  
  464.     while( ( c = getchar() ) != EOF )
  465.     {
  466.  
  467.         /*  Ignore all chars until an escape char  */
  468.  
  469.         /*
  470.         **  If we are in graphics, toss it if strip_seq is set.
  471.         */
  472.  
  473.         if ( c != ESC )
  474.         {
  475.             if ( !strip_seq || !in_graphics )
  476.                 putchar(c);    /* pass it thru */
  477.  
  478.             continue;    /* pop to the top of the loop */
  479.         }
  480.  
  481.         /*
  482.         **  Now we have an escape sequence, get the parameter char.
  483.         */
  484.  
  485.         parameter = getchar();
  486.  
  487.         if ( parameter == EOF )        /* oops */
  488.         {
  489.             putchar ( ESC );
  490.             fprintf(stderr, "Warning:  File ended with <esc>.\n");
  491.             break;            /* unexpected end of input */
  492.         }
  493.  
  494.         /*
  495.         **  Now check to see if it is a two character sequence.
  496.         */
  497.  
  498.         if ( parameter >= '0' && parameter <= '~' )
  499.         {
  500.             putchar ( ESC );
  501.             putchar ( parameter );        /* pass it thru */
  502.  
  503.             /*
  504.             **  If the second character is an E, then we
  505.             **  and the printer do a reset.
  506.             */
  507.  
  508.             if ( parameter == 'E' )
  509.             {
  510.                 free_mem();
  511.                 curr_plane = 0;
  512.                 num_planes = 1;
  513.                 imaging = FALSE;
  514.                 inmode = 0;
  515.                 outmode = 0;
  516.                 in_graphics = FALSE;
  517.  
  518.                 /* can't do this if user gave value with -n.
  519.                 rasterwidth = DEFAULT/8;
  520.                 rpix = DEFAULT;
  521.                 */
  522.             }
  523.  
  524.             continue;        /* return to the top */
  525.         }
  526.  
  527.         /*
  528.         **  Now check to make sure that the parameter character is
  529.         **  within range.
  530.         */
  531.  
  532.         if ( parameter < '!' || parameter > '/' )
  533.         {
  534.             putchar ( ESC );
  535.             putchar ( parameter );
  536.  
  537.             fprintf(stderr, "Warning:  Invalid escape sequence.\n");
  538.  
  539.             continue;
  540.         }
  541.  
  542.         /*
  543.         **  We are only interested in certain parameters, so pass
  544.         **  the rest of the sequences.
  545.         */
  546.  
  547.         /*
  548.         **  For the moment, we are only interested in '*' (graphics)
  549.         **  '(' and ')' (downloads).  Although we do not do anything
  550.         **  with downloads, we need to pass the binary data thru
  551.         **  untouched.
  552.         **  Also, '&' is handled too.
  553.         */
  554.  
  555.         if ( parameter != '*' && parameter != '(' 
  556.             && parameter != ')' && parameter != '&' )
  557.         {
  558.             putchar ( ESC );
  559.             putchar ( parameter );
  560.             Flush_To_Term();        /* flush rest of seq. */
  561.             continue;
  562.         }
  563.  
  564.  
  565.         /*
  566.         **  Parameter character is in range, look for a valid group char
  567.         */
  568.  
  569.         group_char = getchar();
  570.  
  571.         if ( group_char == EOF )    /* oops, ran out of input */
  572.         {
  573.             putchar ( ESC );
  574.             putchar ( parameter );
  575.  
  576.             fprintf(stderr, "Warning:  Incomplete escape sequence.\n");
  577.             break;
  578.         }
  579.  
  580.         /*
  581.         **  See if in proper range.  If it isn't, it is not an error
  582.         **  because the group character is optional for some sequences.
  583.         **  For the moment, we are not interested in those sequences,
  584.         **  so pass them thru.
  585.         */
  586.  
  587.         if ( group_char < '`' || group_char > '~' )
  588.         {
  589.             putchar ( ESC );
  590.             putchar ( parameter );
  591.             putchar ( group_char );
  592.             if ( group_char < '@' || group_char > '^' )
  593.                 Flush_To_Term();    /* pass rest of seq. */
  594.             continue;
  595.         }
  596.  
  597.         /*
  598.         **  Now we have a valid group character, decide if we want
  599.         **  to deal with this escape sequence.
  600.         **
  601.         **  Sequences we want do deal with include:
  602.         **
  603.         **    <esc>*r    ** graphics
  604.         **    <esc>*b    ** graphics
  605.         **    <esc>*v    ** graphics
  606.         **
  607.         **  Sequences we must pass thru binary data:
  608.         **
  609.         **    <esc>*c    ** pattern
  610.         **    <esc>*t    ** obsolete
  611.         **    <esc>(f    ** download char set
  612.         **    <esc>(s    ** download char
  613.         **    <esc>)s    ** download font
  614.         **    <esc>&a    ** logical page
  615.         **    <esc>&l    ** obsolete
  616.         **
  617.         */
  618.  
  619.         if (  ( parameter == '*'
  620.             && group_char != 'r' && group_char != 'b' 
  621.             && group_char != 'v' && group_char != 'c' 
  622.             && group_char != 't' )
  623.            || ( parameter == '&'
  624.             && group_char != 'a' && group_char != 'l' )
  625.            || ( parameter == '(' 
  626.             && group_char != 'f' && group_char != 's' )
  627.            || ( parameter == ')' && group_char != 's' ) )
  628.         {
  629.             /*
  630.             **  Definately not interested in the sequence.
  631.             */
  632.  
  633.             putchar ( ESC );
  634.             putchar ( parameter );
  635.             putchar ( group_char );
  636.             Flush_To_Term();
  637.             continue;
  638.         }
  639.  
  640.         /*
  641.         **  Now set up a pass thru flag so we can ignore the entire
  642.         **  sequences of some of these.
  643.         */
  644.  
  645.         if ( parameter != '*' )
  646.             pass_seq = TRUE;
  647.         else if ( group_char == 'c' || group_char == 't' )
  648.             pass_seq = TRUE;
  649.         else
  650.             pass_seq = FALSE;
  651.  
  652.  
  653.         /*
  654.         **  Now we have a sequence that we are definately interested in.
  655.         **
  656.         **  Get the value field and terminator, and loop until final
  657.         **  terminator is found.
  658.         */
  659.  
  660.         do
  661.         {
  662.             /* first see if the value has a plus sign */
  663.  
  664.             scanf_count = scanf(" + %d", &value );
  665.  
  666.             if ( scanf_count == 1 )
  667.  
  668.                 plus_sign = TRUE;
  669.             else
  670.             {
  671.                 plus_sign = FALSE;
  672.  
  673.                 scanf_count = scanf(" %d", &value );
  674.  
  675.                 if ( scanf_count == 0 )
  676.                     value = 0;        /* by default */
  677.             }
  678.  
  679.             terminator = getchar();
  680.  
  681.             /*
  682.             ** check to see if a fractional parameter was passed.
  683.             */
  684.  
  685.             frac = 0;    /* in case no fraction */
  686.  
  687.             if ( terminator == '.' )
  688.             {
  689.                 /*
  690.                 **  Need to get fractional part.
  691.                 **
  692.                 **  This will not work properly if the
  693.                 **  fraction is < .1 (i.e. a leading 0 is
  694.                 **  present).  For example, the value
  695.                 **  14.05 for point size, which would get
  696.                 **  rounded to 14.00 for scalable fonts,
  697.                 **  would get passed thru as 14.5 which is
  698.                 **  rounded to 14.5.  This is unlikely to
  699.                 **  happen as the 14.05 case is rare, but
  700.                 **  it is valid PCL, so I will fix this
  701.                 **  in the future.
  702.                 */
  703.  
  704.                 if ( scanf("%d", &frac) != 1 )
  705.                 {
  706.                     frac = 0;    /* no frac? */
  707.                 }
  708.  
  709.                 /*
  710.                 **  Now get the real terminator.
  711.                 */
  712.  
  713.                 terminator = getchar();
  714.             }
  715.  
  716.  
  717.             if ( terminator == EOF )    /* barf */
  718.             {
  719.                 fprintf(stderr, "Warning:  Incomplete sequence at end of file.\n");
  720.                 break;
  721.             }
  722.  
  723.             /*
  724.             **  If the pass_seq flag is set, then just pass
  725.             **  it thru to stdout until a 'W' is found.
  726.             */
  727.  
  728.             if ( pass_seq )
  729.             {
  730.                 /*
  731.                 **  If not in sequence, then we output esc
  732.                 **  otherwise, output the saved terminator.
  733.                 */
  734.  
  735.                 if ( !in_sequence )
  736.                 {
  737.                     in_sequence = TRUE;
  738.                     putchar ( ESC );
  739.                     putchar ( parameter );
  740.                     putchar ( group_char );
  741.                 } else
  742.                 {
  743.                     putchar ( old_terminator );
  744.                 }
  745.  
  746.                 /* now pass the value */
  747.  
  748.                 if ( plus_sign )
  749.                     putchar('+');
  750.  
  751.                 if ( scanf_count )    /* there was a value */
  752.                     printf("%0d", value);
  753.                 
  754.                 /* need to output the fractional part */
  755.  
  756.                 if ( frac )
  757.                     printf(".%0d", frac);
  758.  
  759.                 /*
  760.                 **  We save the terminator, because we may
  761.                 **  need to change it to upper case.
  762.                 */
  763.  
  764.                 old_terminator = terminator;
  765.  
  766.                 /* if binary data, pass it thru */
  767.  
  768.                 if ( terminator == 'W' )    /* aha */
  769.                 {
  770.                     putchar ( terminator );
  771.                     in_sequence = FALSE;    /* terminates */
  772.                     Flush_Bytes ( value );    /* pass data */
  773.                 }
  774.  
  775.                 continue;
  776.             }
  777.  
  778.             /*
  779.             **  Ok, this is a sequence we want to pay attention to.
  780.             **
  781.             **  Do_Graphics returns TRUE if we need to pass seq.
  782.             **
  783.             **  Note:  Do_Graphics modifies the parser vars such
  784.             **         as in_sequence.  This is because it may
  785.             **         have to output stuff directly.
  786.             */
  787.  
  788.             if ( Do_Graphics ( group_char, value, terminator ) )
  789.             {
  790.                 /*
  791.                 **  If not in sequence, then we output esc
  792.                 **  otherwise, output the saved terminator.
  793.                 */
  794.  
  795.                 if ( !in_sequence )
  796.                 {
  797.                     in_sequence = TRUE;
  798.                     putchar ( ESC );
  799.                     putchar ( parameter );
  800.                     putchar ( group_char );
  801.                 } else
  802.                 {
  803.                     putchar ( old_terminator );
  804.                 }
  805.  
  806.                 /* now pass the value */
  807.  
  808.                 if ( plus_sign )
  809.                     putchar('+');
  810.  
  811.                 if ( scanf_count )    /* there was a value */
  812.                     printf("%0d", value);
  813.  
  814.                 /* need to output the fractional part */
  815.  
  816.                 if ( frac )
  817.                     printf(".%0d", frac);
  818.  
  819.                 /*
  820.                 **  We save the terminator, because we may
  821.                 **  need to change it to upper case.
  822.                 */
  823.  
  824.                 old_terminator = terminator;
  825.             }
  826.  
  827.         } while ( terminator >= '`' && terminator <= '~' );
  828.  
  829.         /*
  830.         ** The oppsite test (above) may be more appropriate.  That is, 
  831.         ** !(terminator >= '@' && terminator <= '^').
  832.         */
  833.         
  834.         /*
  835.         **  If we were in a sequence, then we must terminate it.
  836.         **  If it was lower case, then it must be uppered.
  837.         */
  838.  
  839.         if ( in_sequence )
  840.         {
  841.             putchar ( terminator & 0xdf );        /* a ==> A */
  842.             in_sequence = FALSE;
  843.         }
  844.     }
  845.     
  846.  
  847.     /*
  848.     **  If the user wants a reset, give him one.
  849.     */
  850.  
  851.     if ( reset_seq )
  852.     {
  853.         putchar ( ESC );
  854.         putchar ( 'E' );
  855.     }
  856.  
  857.  
  858.     /*
  859.     **  Finished up, so print stats and close output file.
  860.     */
  861.  
  862.     fclose(stdout);
  863.  
  864.  
  865.     if ( verbose )
  866.     {
  867.         for(j = 0; j < 4; j++)
  868.             fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]);
  869.         for(j = 0; j < 4; j++)
  870.             fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]);
  871.     }
  872.  
  873.     exit(0);
  874. }
  875.  
  876.  
  877. /*
  878. **  Do_Graphics() takes the graphics escape sequence and performs the
  879. **  necessary functions.
  880. **  TRUE is returned if the escape sequence needs to be passed to the output.
  881. */
  882.  
  883. int    Do_Graphics( group, num, terminator )
  884. int    group, num, terminator;
  885. {
  886.  
  887.     /*  first look at vW  */
  888.  
  889.     if ( group == 'v' )
  890.  
  891.         if ( terminator != 'W' )
  892.             
  893.             return ( TRUE );    /* pass it thru */
  894.         else
  895.         {
  896.             if ( !in_sequence )
  897.             {
  898.                 putchar ( ESC );
  899.                 putchar ( parameter );
  900.                 putchar ( group );
  901.             } else
  902.                 putchar ( old_terminator );
  903.  
  904.             in_sequence = FALSE;        /* terminating */
  905.  
  906.             printf("%0d", num);
  907.             putchar ( terminator );
  908.  
  909.             free_mem();    /* reset memory */
  910.  
  911.             imaging++;
  912.  
  913.             fread(&imdata, MIN(num, 18), 1, stdin);
  914.             fwrite(&imdata, MIN(num, 18), 1, stdout);
  915.  
  916.             num -= MIN(num, 18);
  917.  
  918.             /* copy rest of unknown data */
  919.  
  920.             if ( num > 0 )
  921.                 Flush_Bytes(num);
  922.  
  923.  
  924.             switch(imdata.pix_mode){
  925.                 case 0x00:
  926.                     rasterwidth = (rpix + 7)/8;
  927.                     num_planes = imdata.inx_bits;
  928.                     break;
  929.                 case 0x01:
  930.                     rasterwidth = rpix*imdata.inx_bits/8;
  931.                     break;
  932.                 case 0x02:
  933.                     rasterwidth = (rpix + 7)/8;
  934.                     num_planes =imdata.red + imdata.green +
  935.                                 imdata.blue;
  936.                     break;
  937.                 case 0x03:
  938.                     rasterwidth = (imdata.red +
  939.                                    imdata.green +
  940.                                    imdata.blue)*rpix/8;
  941.                     break;
  942.             }
  943.  
  944.             return ( FALSE );
  945.         }
  946.  
  947.     /*
  948.     **  Now deal with <esc>*r stuff
  949.     */
  950.  
  951.     if ( group == 'r' )
  952.     {
  953.         switch ( terminator )
  954.         {
  955.             case 'A':
  956.             case 'a':
  957.  
  958.                 /* in graphics mode, may do stripping */
  959.                 in_graphics = TRUE;
  960.  
  961.                 /* if user wants to strip redundant seq */
  962.                 if ( strip_seq && memflag )
  963.                     return( FALSE );
  964.  
  965.                 curr_plane=0;
  966.                 zero_seeds();    /* may allocate mem */
  967.                 break;
  968.  
  969.             case 'C':
  970.             case 'c':
  971.  
  972.                 /* not in graphics disable code strip */
  973.  
  974.                 in_graphics = FALSE;
  975.  
  976.                 if ( strip_seq )
  977.                     return( FALSE );
  978.  
  979.                 inmode = 0;
  980.                 outmode = 0;
  981.  
  982.                 free_mem();
  983.                 curr_plane=0;
  984.                 break;
  985.  
  986.             case 'B':
  987.             case 'b':
  988.  
  989.                 /* not in graphics disable code strip */
  990.  
  991.                 in_graphics = FALSE;
  992.  
  993.                 if ( strip_seq )
  994.                     return( FALSE );
  995.  
  996.                 if ( deskjet )    /* B resets modes on DJ */
  997.                 {
  998.                     inmode = 0;
  999.                     outmode = 0;
  1000.                 }
  1001.                 free_mem();
  1002.                 curr_plane=0;
  1003.                 break;
  1004.  
  1005.             case 'S':
  1006.             case 's':
  1007.  
  1008.                 /* free mem in case widths changed */
  1009.                 free_mem();
  1010.  
  1011.                 rpix = num;
  1012.  
  1013.                 if (imaging){
  1014.                     switch(imdata.pix_mode)
  1015.                     {
  1016.                         case 0x00:
  1017.                             rasterwidth=(rpix+7)/8;
  1018.                             break;
  1019.                         case 0x01:
  1020.                             rasterwidth = 
  1021.                              rpix*imdata.inx_bits/8;
  1022.                             break;
  1023.                         case 0x02:
  1024.                             rasterwidth=(rpix+7)/8;
  1025.                             break;
  1026.                         case 0x03:
  1027.                             rasterwidth = 
  1028.                               (imdata.red 
  1029.                               + imdata.green
  1030.                               + imdata.blue)*rpix/8;
  1031.                             break;
  1032.                     }
  1033.                 } else
  1034.                     rasterwidth = (num + 7) / 8;
  1035.                 break;
  1036.  
  1037.             case 'T':
  1038.             case 't':
  1039.                 break;
  1040.  
  1041.             case 'U':
  1042.             case 'u':
  1043.                 curr_plane=0;
  1044.                 free_mem();    /* if ESC*rA came first */
  1045.                 num_planes = num;
  1046.                 imaging = FALSE;    /* goes off */
  1047.                 break;
  1048.  
  1049.             default:
  1050.                 break;
  1051.         }
  1052.  
  1053.         return ( TRUE );        /* pass sequence on */
  1054.  
  1055.     }    /* group r */
  1056.  
  1057.     /*
  1058.     **  Last and final group 'b'.  All the graphics data comes thru here.
  1059.     */
  1060.  
  1061.  
  1062.     switch ( terminator )
  1063.     {
  1064.            case 'm':
  1065.            case 'M':
  1066.             inmode = num;
  1067.             return ( FALSE );    /* we do NOT pass this */
  1068.             break;
  1069.  
  1070.            /*
  1071.            **  <esc>*b#X is obsolete, don't bother with it. 
  1072.            **  If I did do something, then I would zero part of the
  1073.            **  seed rows.
  1074.            */
  1075.  
  1076.            case 'x':
  1077.            case 'X':
  1078.             break;
  1079.  
  1080.            case 'y':
  1081.            case 'Y':
  1082.             /* zero only if allocated */
  1083.             if ( memflag )
  1084.                 zero_seeds();
  1085.             break;
  1086.  
  1087.            case 'W':
  1088.             if(!memflag)
  1089.                 zero_seeds();        /* get memory */
  1090.  
  1091.             /* fire up sequence */
  1092.  
  1093.             if ( !in_sequence )
  1094.             {
  1095.                 putchar ( ESC );
  1096.                 putchar ( parameter );
  1097.                 putchar ( group );
  1098.             } else
  1099.                 putchar ( old_terminator );
  1100.  
  1101.             in_sequence = FALSE;        /* terminating */
  1102.  
  1103.             if(curr_plane < num_planes) 
  1104.             {
  1105.  
  1106.                 Process(num, 'W');
  1107.  
  1108.                 if ( curr_plane + 1 < num_planes )
  1109.                 {
  1110.                     /* now we have a problem */
  1111.                     zero_upper(curr_plane + 1);
  1112.                 }
  1113.             } else
  1114.                 Process_extra(num,'W');   
  1115.  
  1116.             curr_plane=0;
  1117.  
  1118.             return ( FALSE );
  1119.  
  1120.             break;
  1121.  
  1122.            case 'V':
  1123.             if(!memflag)
  1124.                 zero_seeds();        /* get memory */
  1125.             
  1126.             /*
  1127.             **  If curr_plane is the last plane, this should
  1128.             **  be a 'W', not a 'V'.  I could change it,
  1129.             **  then I would fix Process_extra() to not output
  1130.             **  anything as the 'W' was already sent.
  1131.             */
  1132.  
  1133.             if( curr_plane < num_planes ) 
  1134.             {
  1135.                 /* fire up sequence */
  1136.  
  1137.                 if ( !in_sequence )
  1138.                 {
  1139.                     putchar ( ESC );
  1140.                     putchar ( parameter );
  1141.                     putchar ( group );
  1142.                 } else
  1143.                     putchar ( old_terminator );
  1144.  
  1145.                 in_sequence = FALSE;    /* terminating */
  1146.             
  1147.  
  1148.                 Process(num, 'V');
  1149.                 curr_plane++;
  1150.             } else
  1151.                 Process_extra(num,'V');
  1152.  
  1153.             return ( FALSE );
  1154.  
  1155.             break;
  1156.  
  1157.         default:
  1158.             break;
  1159.     }
  1160.  
  1161.     return ( TRUE );        /* pass sequence */
  1162. }
  1163.  
  1164.  
  1165.  
  1166. /*
  1167. **  Flush_To_Term() simply passes thru input until a valid terminator
  1168. **  character is found.  This is for unwanted escape sequences.
  1169. */
  1170.  
  1171. Flush_To_Term()
  1172. {
  1173.     int    c;
  1174.  
  1175.     do
  1176.     {
  1177.         c = getchar();
  1178.  
  1179.         if ( c == EOF )            /* this is a problem */
  1180.             return;
  1181.         
  1182.         putchar ( c );
  1183.  
  1184.     } while ( c < '@' || c > '^' );
  1185. }
  1186.  
  1187.  
  1188. /*
  1189. **  Flush_Bytes() simply transfers so many bytes directly from input to output.
  1190. **  This is used to pass thru binary data that we are not interested in so that
  1191. **  it will not confuse the parser.  I.e. downloads.
  1192. */
  1193.  
  1194. Flush_Bytes( num )
  1195. unsigned int    num;
  1196. {
  1197.     int    bnum;
  1198.  
  1199.     while ( num > 0 )
  1200.     {
  1201.         bnum = MIN ( BUFSIZ, num );
  1202.  
  1203.         fread( buf, 1, bnum, stdin );
  1204.  
  1205.         if ( fwrite( buf, 1, bnum, stdout ) < bnum )
  1206.  
  1207.             /* check for error and exit */
  1208.  
  1209.             if ( ferror(stdout) )
  1210.             {
  1211.                 perror("Output error");
  1212.                 exit(-2);
  1213.             }
  1214.  
  1215.         num -= bnum;
  1216.     }
  1217. }
  1218.  
  1219.  
  1220.  
  1221.  
  1222. /*----------------------------------------*/
  1223.  
  1224. /*
  1225. **    Zero_seeds() will allocate and initialize memory.
  1226. **    If memory has already been allocated, then it will just initialize it.
  1227. */
  1228.  
  1229.  
  1230. zero_seeds()
  1231. {
  1232.     int r;
  1233.  
  1234.     /* first allocate and init seed_rows for number of planes. */
  1235.  
  1236.     for ( r = 0; r < num_planes ; r++)
  1237.     {
  1238.         if(!memflag)
  1239.         {
  1240.             seed_row[r] = (unsigned char *) malloc(rasterwidth);
  1241.  
  1242.             if ( seed_row[r] == NULL )
  1243.             {
  1244.                 fprintf(stderr, "Out of memory.\n");
  1245.                 exit(-3);
  1246.             }
  1247.         }
  1248.  
  1249.         /* zero seeds for mode 3 */
  1250.  
  1251.         memset(seed_row[r], 0, rasterwidth);
  1252.     }
  1253.  
  1254.  
  1255.     if(!memflag)
  1256.     {
  1257.         new_row = (unsigned char *) malloc(rasterwidth);
  1258.  
  1259.         if ( new_row == NULL )
  1260.         {
  1261.             fprintf(stderr, "Out of memory.\n");
  1262.             exit(-3);
  1263.         }
  1264.  
  1265.         for(r=0; r<MAXMODES; r++)
  1266.         {
  1267.             /* 2 * width is needed for modes 1, 2 and 3 */
  1268.  
  1269.             out_row[r] = (unsigned char *) malloc(2 * rasterwidth);
  1270.  
  1271.             if ( out_row[r] == NULL )
  1272.             {
  1273.                 fprintf(stderr, "Out of memory.\n");
  1274.                 exit(-3);
  1275.             }
  1276.         }
  1277.  
  1278.     }
  1279.  
  1280.     memset(new_row, 0, rasterwidth);
  1281.  
  1282.     memflag = TRUE;            /* memory is in place */
  1283. }
  1284.  
  1285.  
  1286. /* this routine if for incomplete transfers of data */
  1287.  
  1288. zero_upper(plane)
  1289. int    plane;
  1290. {
  1291.     int i;
  1292.  
  1293.     /* assume memory already present */
  1294.  
  1295.     for ( i = plane; i < num_planes; i++)
  1296.         memset(seed_row[i], 0, rasterwidth);
  1297. }
  1298.  
  1299.  
  1300. Process(inbytes, terminator)
  1301. int inbytes, terminator;
  1302. {
  1303.  
  1304.     int insize;
  1305.     int minmode = 0;
  1306.  
  1307.     inuse[inmode]++;
  1308.  
  1309.     switch ( inmode ) {
  1310.  
  1311.     case 0:
  1312.         if ( !widthwarning && inbytes > rasterwidth )
  1313.         {
  1314.             /* This is likely to result in data truncation. */
  1315.             widthwarning = TRUE;
  1316.             fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n");
  1317.         }
  1318.  
  1319.         insize = Mode_0_Graphics(inbytes,rasterwidth,new_row,invert);
  1320.         break;
  1321.     case 1:
  1322.         insize = Mode_1_Graphics(inbytes,rasterwidth,new_row,invert);
  1323.         break;
  1324.     case 2:
  1325.         insize = Mode_2_Graphics(inbytes,rasterwidth,new_row,invert);
  1326.         break;
  1327.     case 3:
  1328.         memcpy(new_row, seed_row[curr_plane], rasterwidth);
  1329.         insize = Mode_3_Graphics(inbytes,rasterwidth,new_row,invert);
  1330.         break;
  1331.  
  1332.     default:        /* unknown mode? */
  1333.  
  1334.         /*  Don't know what to do about seed rows, pass stuff thru */
  1335.  
  1336.         fprintf(stderr, "%s: Unsupported compression mode %d.\n",
  1337.             progname, inmode );
  1338.  
  1339.         ChangeMode(inmode);    /* go to that mode */
  1340.  
  1341.         /* <esc>*b has already been output */
  1342.  
  1343.         printf("%1d%c", inbytes, terminator);
  1344.  
  1345.         Flush_Bytes( inbytes );
  1346.  
  1347.         firstrow = TRUE;        /* pop it out of mode 3 */
  1348.  
  1349.         /*  Go ahead and clear the seed rows if present  */
  1350.         if ( memflag )
  1351.             zero_seeds();
  1352.  
  1353.         return;
  1354.  
  1355.     }
  1356.  
  1357.  
  1358.     /*
  1359.     **
  1360.     */
  1361.  
  1362.     if ( mode0 )
  1363.         /* actually, this is redundant since new_row is mode 0 */
  1364.         out_size[0] = Output_0( new_row, out_row[0], rasterwidth );
  1365.     else
  1366.         out_size[0] = MAXBYTES+1;
  1367.  
  1368.     if ( mode1 )
  1369.         out_size[1] = Output_1( new_row, out_row[1], rasterwidth );
  1370.     else
  1371.         out_size[1] = MAXBYTES+1;
  1372.  
  1373.     if ( mode2 )
  1374.         out_size[2] = Output_2( new_row, out_row[2], rasterwidth );
  1375.     else
  1376.         out_size[2] = MAXBYTES+1;
  1377.  
  1378.     if ( mode3 )
  1379.         out_size[3] = Output_3( seed_row[curr_plane], new_row, out_row[3], rasterwidth );
  1380.     else
  1381.         out_size[3] = MAXBYTES+1;
  1382.     
  1383.  
  1384.     /*
  1385.     **  Now determine which mode will give the best output.  Note that it
  1386.     **  takes 5 bytes to change modes, so we penalize all modes that are
  1387.     **  not the current output by 5 bytes.  This is to discourage changing
  1388.     **  unless the benifit is worth it.  The exception to this rule is
  1389.     **  mode 3.  We want to encourage going to mode 3 because of the seed
  1390.     **  row behaviour.  That is, if we have a simple picture that does
  1391.     **  not change much, and say each of the sizes for modes 1 and 2 always
  1392.     **  comes out to 4 bytes of data, then if we add 5 to mode 3 each time,
  1393.     **  it would never get selected.  But, we remove the penalty, and if
  1394.     **  mode 3 is selected (0 bytes of data needed for mode 3), then each
  1395.     **  succesive row only needs 0 bytes of data.  For a 300 dpi A size
  1396.     **  picture with 3 data planes, this could be a savings of 37k bytes.
  1397.     */
  1398.  
  1399.     /*
  1400.     **  With the new parser, the output to change modes is now only
  1401.     **  2 bytes, since it gets combined with the *b#W sequence.
  1402.     **  So, I decided to ignore the switching penalty.
  1403.     */
  1404.  
  1405.     /*
  1406.     **  Due to a possible bug in PaintJet XL, don't allow mode 3 to be
  1407.     **  selected for the first row of output.  But do allow it if the
  1408.     **  user has no other mode selected.
  1409.     */
  1410.  
  1411.     if ( firstrow && (mode0 || mode1 || mode2) )
  1412.     {
  1413.         out_size[3] = MAXBYTES+1;    /* disable mode 3 for now */
  1414.  
  1415.         if ( terminator == 'W' )    /* last plane? */
  1416.             firstrow = FALSE;    /* no longer first row */
  1417.     }
  1418.  
  1419.  
  1420.     minmode = 3;
  1421.  
  1422.     if ( out_size[2] < out_size[minmode] )
  1423.         minmode = 2;
  1424.  
  1425.     if ( out_size[1] < out_size[minmode] )
  1426.         minmode = 1;
  1427.  
  1428.     if ( out_size[0] < out_size[minmode] )
  1429.         minmode = 0;
  1430.  
  1431.  
  1432.                     /* I may remove this sometime */
  1433.     if ( minmode != outmode )
  1434.         if ( out_size[minmode] == out_size[outmode] )
  1435.             minmode = outmode;
  1436.  
  1437.  
  1438.     outuse[minmode]++;
  1439.  
  1440.     if ( outmode != minmode )
  1441.         ChangeMode( minmode );
  1442.     
  1443.     /* <esc>*b has already been output */
  1444.  
  1445.     printf("%1d%c", out_size[minmode], terminator);
  1446.  
  1447.     if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) < 
  1448.                             out_size[minmode] )
  1449.  
  1450.         /* check for error and exit */
  1451.  
  1452.         if ( ferror(stdout) )
  1453.         {
  1454.             perror("Output error");
  1455.             exit(-2);
  1456.         }
  1457.  
  1458.  
  1459.     memcpy(seed_row[curr_plane], new_row, rasterwidth);
  1460.  
  1461. }
  1462.  
  1463. Process_extra(bytes, terminator)
  1464. int bytes;
  1465. char terminator;
  1466. {
  1467.     int  i;
  1468.  
  1469.     /* toss any excess data */
  1470.  
  1471.     for(i = 0; i < bytes; i++)
  1472.        getchar();
  1473.  
  1474.     /* last plane? force move down to next row */
  1475.  
  1476.     if(terminator == 'W')
  1477.     {
  1478.         /* <esc>*b has already been output */
  1479.         printf("0W");
  1480.  
  1481.         firstrow = FALSE;        /* not on first row anymore */
  1482.  
  1483.     }
  1484.  
  1485. }
  1486.  
  1487. ChangeMode(newmode)
  1488. int newmode;
  1489. {
  1490.     /*
  1491.     **  <esc>*b have already been output.
  1492.     **  terminator is 'm' instead of 'M' since will be followed by 'W'
  1493.     */
  1494.     printf("%1dm", newmode);
  1495.     outmode = newmode;
  1496. }
  1497.  
  1498.  
  1499. /* these decoders came from graphics.c in the gp parser */
  1500.  
  1501.  
  1502. /* $PAGE$ */
  1503. /*-----------------------------------------------------------------------*\
  1504.  |                                                                       |
  1505.  |  Function Name: Mode_0_Graphics                                       |
  1506.  |                                                                       |
  1507.  |  Description:                                                         |
  1508.  |                                                                       |
  1509.  |  This is the routine that handles a Mode 0 graphics block transfer    |
  1510.  |  to the Formatter Module.                                             |
  1511.  |                                                                       |
  1512. \*-----------------------------------------------------------------------*/
  1513.  
  1514. /* FUNCTION */
  1515.  
  1516. Mode_0_Graphics(input_bytes, output_bytes, address, invert)
  1517.  
  1518. unsigned int
  1519.    input_bytes,                 /* Count of bytes to be read. */
  1520.    output_bytes;                /* Count of bytes to be stored. */
  1521.  
  1522. unsigned char
  1523.    *address;                    /* Pointer to where to store bytes. */
  1524.  
  1525. unsigned char            /* Boolean to request data inversion */
  1526.     invert;
  1527.  
  1528. {
  1529.    /* LOCAL VARIABLES */
  1530.  
  1531.    unsigned char
  1532.       *store_ptr;               /* Pointer to where to store the byte. */
  1533.  
  1534.    unsigned int
  1535.       read_bytes,               /* Local copy of input_bytes. */
  1536.       write_bytes;              /* Local copy of output_bytes. */
  1537.  
  1538.    /* CODE */
  1539.  
  1540.    /* Initialize the local variables. */
  1541.  
  1542.    read_bytes = input_bytes;
  1543.    write_bytes = output_bytes;
  1544.    store_ptr = address;
  1545.    
  1546.  
  1547.    /* transfer the lesser of available bytes or available room */
  1548.  
  1549.    if (invert)
  1550.      Inv_Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
  1551.    else
  1552.      Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
  1553.  
  1554.    /* now zero fill or throw excess data away */
  1555.  
  1556.    if ( read_bytes > write_bytes )
  1557.       Discard_Block(read_bytes - write_bytes);        /* throw excess */
  1558.    else {
  1559.       store_ptr += read_bytes;                /* adjust pointer */
  1560.       write_bytes -= read_bytes;            /* zero fill count */
  1561.  
  1562.       memset(store_ptr, 0, write_bytes);
  1563.    }
  1564.  
  1565.    return ( input_bytes );
  1566. }
  1567.  
  1568. /* $PAGE$ */
  1569. /*-----------------------------------------------------------------------*\
  1570.  |                                                                       |
  1571.  |  Function Name: Mode_1_Graphics                                       |
  1572.  |                                                                       |
  1573.  |  Description:                                                         |
  1574.  |                                                                       |
  1575.  |  This is the routine that handles a Mode 1 graphics block transfer    |
  1576.  |  to the Formatter Module.  Mode 1 graphics is a compacted mode.       |
  1577.  |  The data in Mode 1 is in pairs.  The first byte is a replicate       |
  1578.  |  count and the second byte is the data.  The data byte is stored      |
  1579.  |  then replicated the replicate count.  Therefore a replicate count    |
  1580.  |  of 0 means the data byte is stored once.  The input byte count       |
  1581.  |  must be an even amount for the data to be in byte pairs.             |
  1582.  |                                                                       |
  1583. \*-----------------------------------------------------------------------*/
  1584.  
  1585. /* FUNCTION */
  1586.  
  1587. Mode_1_Graphics(input_bytes, output_bytes, address, invert)
  1588.  
  1589. unsigned int
  1590.    input_bytes,                 /* Count of bytes to be read. */
  1591.    output_bytes;                /* Count of bytes to be stored. */
  1592.  
  1593. unsigned char
  1594.    *address;                    /* Pointer to where to store bytes. */
  1595.  
  1596. unsigned char            /* Boolean to request data inversion */
  1597.     invert;
  1598.  
  1599. {
  1600.    /* LOCAL VARIABLES */
  1601.  
  1602.    unsigned char
  1603.       *store_ptr,               /* Pointer to where to store the byte. */
  1604.       input_char;               /* Byte to be replicated. */
  1605.  
  1606.    unsigned int
  1607.       read_bytes,               /* Local copy of input_bytes. */
  1608.       write_bytes;              /* Local copy of output_bytes. */
  1609.  
  1610.    int
  1611.       replicate_count;          /* Number of times to replicate data. */
  1612.  
  1613.    /* CODE */
  1614.  
  1615.    /* Initialize the local variables. */
  1616.  
  1617.    read_bytes = input_bytes;
  1618.    write_bytes = output_bytes;
  1619.    store_ptr = address;
  1620.    
  1621.    /* Check for an even input count. */
  1622.  
  1623.    if ((read_bytes % 2) == 0)
  1624.    {
  1625.       /* Even so input data is in pairs as required. So store the data. */
  1626.    
  1627.       while ((read_bytes != 0) && (write_bytes != 0))
  1628.       {
  1629.          /* First get the replicate count and the byte to store. */
  1630.  
  1631.          replicate_count = (unsigned char) Get_Character();
  1632.          input_char = (invert ? ~Get_Character() : Get_Character());
  1633.          read_bytes -= 2;
  1634.       
  1635.          /* Since write_bytes was 0 there is room to store the byte. */
  1636.  
  1637.          *store_ptr++ = input_char;
  1638.          write_bytes--;
  1639.          
  1640.          /* Now make sure there is room for the replicated data. */
  1641.  
  1642.          if (replicate_count > write_bytes)
  1643.          {
  1644.             /* Too much so limit to the room available. */
  1645.  
  1646.             replicate_count = write_bytes;
  1647.          }
  1648.             
  1649.          /* Update the amount to be written. */
  1650.  
  1651.          write_bytes -= replicate_count;
  1652.  
  1653.          /* Then replicate it. */
  1654.  
  1655.          while (replicate_count != 0)
  1656.          {
  1657.             /* Store the byte the decrement the count. */
  1658.  
  1659.             *store_ptr++ = input_char;
  1660.          
  1661.             replicate_count--;
  1662.          }
  1663.       }
  1664.  
  1665.    }
  1666.    /* Discard any left over input. */
  1667.     /* OR */
  1668.    /* Discard all of the input data as odd byte count. */
  1669.  
  1670.    Discard_Block(read_bytes);
  1671.  
  1672.    read_bytes = store_ptr - address;  /* how much was done? */
  1673.  
  1674.    /* zero fill if needed */
  1675.    memset(store_ptr, 0, write_bytes);
  1676.          
  1677.    return(read_bytes);
  1678. }
  1679.  
  1680. /* $PAGE$ */
  1681. /*-----------------------------------------------------------------------*\
  1682.  |                                                                       |
  1683.  |  Function Name: Mode_2_Graphics                                       |
  1684.  |                                                                       |
  1685.  |  Description:                                                         |
  1686.  |                                                                       |
  1687.  |  This is the routine that handles a Mode 2 graphics block transfer    |
  1688.  |  to the Formatter Module.  Mode 2 graphics is a compacted mode.       |
  1689.  |  The data in Mode 2 is of one of two types.  The first type is a      |
  1690.  |  class type and the second type is a data type.  The class type is    |
  1691.  |  a single byte which is a combination of replicate count and a sub    |
  1692.  |  mode.  There are two sub modes within mode 2, sub mode 0 and sub     |
  1693.  |  mode 1.  These sub modes are flagged by the MSB of the class type    |
  1694.  |  byte.  If the MSB = 0 then the replicate count is the value of the   |
  1695.  |  class type byte.  In sub mode 0 the replicate count ranges from 1    |
  1696.  |  to 127.  In sub mode 0 the next byte and then the replicate count    |
  1697.  |  of bytes are of the data type and stored.  If the MSB = 1 then the   |
  1698.  |  sub mode is 1 and the replicate count is the negative value of the   |
  1699.  |  class type.  In sub mode 1 the replicate count is stored in 2s       |
  1700.  |  compliment form and ranges from -1 to -127.  In sub mode 1 the       |
  1701.  |  next byte is of the data type and is stored.  That data byte is      |
  1702.  |  then replicated and stored the replicate count.  If the class type   |
  1703.  |  byte is 128 then there is no data type byte.                         |
  1704.  |                                                                       |
  1705. \*-----------------------------------------------------------------------*/
  1706.  
  1707. /* FUNCTION */
  1708.  
  1709. Mode_2_Graphics(input_bytes, output_bytes, address, invert)
  1710.  
  1711. unsigned int
  1712.    input_bytes,                 /* Count of bytes to be read. */
  1713.    output_bytes;                /* Count of bytes to be stored. */
  1714.  
  1715. unsigned char
  1716.    *address;                    /* Pointer to where to store bytes. */
  1717.  
  1718. unsigned char            /* Boolean to request data inversion */
  1719.     invert;
  1720.  
  1721. {
  1722.    /* LOCAL VARIABLES */
  1723.  
  1724.    unsigned char
  1725.       *store_ptr,               /* Pointer to where to store the byte. */
  1726.       input_char,               /* Byte to be replicated. */
  1727.       sub_mode;                 /* Flag if sub mode is 0 or 1. */
  1728.  
  1729.    unsigned int
  1730.       read_bytes,               /* Local copy of input_bytes. */
  1731.       write_bytes;              /* Local copy of output_bytes. */
  1732.  
  1733.    int
  1734.       replicate_count;          /* Number of times to replicate data. */
  1735.  
  1736.    /* CODE */
  1737.  
  1738.    /* Initialize the local variables. */
  1739.  
  1740.    read_bytes = input_bytes;
  1741.    write_bytes = output_bytes;
  1742.    store_ptr = address;
  1743.    
  1744.    while ((read_bytes > 1) && (write_bytes != 0))
  1745.    {
  1746.       /* First get the class type byte and the first data type byte. */
  1747.  
  1748.       replicate_count = Get_Character();
  1749.  
  1750.       /* First check that this not an ignore class type. */
  1751.  
  1752.       if (replicate_count != 128)
  1753.       {
  1754.          /* Not ignore so get the data class byte. */
  1755.  
  1756.          input_char = (invert ? ~Get_Character() : Get_Character());
  1757.          read_bytes -= 2;
  1758.          
  1759.         /* Since write_bytes wasn't 0 there is room to store the byte. */
  1760.  
  1761.          *store_ptr++ = input_char;
  1762.          write_bytes--;
  1763.  
  1764.          /* Determine the sub mode. */
  1765.    
  1766.          if (replicate_count > 128)
  1767.          {
  1768.             /* Sub mode 1. */
  1769.    
  1770.             sub_mode = 1;
  1771.             /* replicate count was unsigned char */
  1772.             replicate_count = 256 - replicate_count;
  1773.          }
  1774.          else
  1775.          {
  1776.             /* Sub mode 0. */
  1777.    
  1778.             sub_mode = 0;
  1779.  
  1780.             /* See if there is enoungh input left for the data byte count. */
  1781.  
  1782.             if (replicate_count > read_bytes)
  1783.             {
  1784.                /* Too many data bytes so limit to the input left. */
  1785.  
  1786.                replicate_count = read_bytes;
  1787.             }
  1788.          }
  1789.             
  1790.          /* Now make sure there is room for the replicated data. */
  1791.    
  1792.          if (replicate_count > write_bytes)
  1793.          {
  1794.             /* Too much so limit to the room available. */
  1795.    
  1796.             replicate_count = write_bytes;
  1797.          }
  1798.                
  1799.          /* Update the amount to be written. */
  1800.    
  1801.          write_bytes -= replicate_count;
  1802.    
  1803.          /* Then replicate it. */
  1804.    
  1805.          if (sub_mode == 0)
  1806.          {
  1807.             /* Sub mode 0 so get the replicate count of data bytes. */
  1808.    
  1809.             if (invert)
  1810.               Inv_Transfer_Block(replicate_count, store_ptr);
  1811.             else
  1812.               Transfer_Block(replicate_count, store_ptr);
  1813.  
  1814.             read_bytes -= replicate_count;
  1815.             
  1816.             /* Find the last byte stored. */
  1817.    
  1818.             store_ptr += replicate_count;
  1819.          }
  1820.          else
  1821.          {
  1822.             /* Sub mode 1 so just duplicate the original byte. */
  1823.    
  1824.             while (replicate_count != 0)
  1825.             {
  1826.                /* Store the byte the decrement the count. */
  1827.    
  1828.                *store_ptr++ = input_char;
  1829.             
  1830.                replicate_count--;
  1831.             }
  1832.          }
  1833.       }
  1834.       else
  1835.       {
  1836.          /* Ignore class so don't get the data class byte. */
  1837.  
  1838.          read_bytes--;
  1839.       }
  1840.    }
  1841.  
  1842.    /* Now discard any left over input. */
  1843.  
  1844.    Discard_Block(read_bytes);
  1845.  
  1846.    read_bytes = store_ptr - address;
  1847.  
  1848.    /* zero fill if needed */
  1849.    memset(store_ptr, 0, write_bytes);
  1850.          
  1851.    
  1852.    return(read_bytes);
  1853. }
  1854.  
  1855. /* $PAGE$ */
  1856. /*-----------------------------------------------------------------------*\
  1857.  |                                                                       |
  1858.  |  Function Name: Mode_3_Graphics                                       |
  1859.  |                                                                       |
  1860.  |  Description:                                                         |
  1861.  |                                                                       |
  1862.  |  This is the routine that handles a Mode 3 graphics block transfer    |
  1863.  |  to the Formatter Module.  Mode 3 graphics is a compacted mode.       |
  1864.  |  Mode 3 data is a difference from one row to the next.  In order to   |
  1865.  |  work, each row must be saved to be a seed for the next.  This        |
  1866.  |  mode is used in conjuction with other compaction modes when the      |
  1867.  |  data remains fairly constant between pairs of rows.                  |
  1868.  |  The data is in the form:                                             |
  1869.  |  <command byte>[<optional offset bytes>]<1 to 8 replacement bytes>    |
  1870.  |  The command byte is in the form:                                     |
  1871.  |    Bits 5-7: Number of bytes to replace (1 - 8)                       |
  1872.  |    Bits 0-4: Relative offset from last byte.                          |
  1873.  |       (If the offset is 31, then add the following bytes for offset   |
  1874.  |       until an offset byte of less then 255 (but inclusive)           |
  1875.  |                                                                       |
  1876. \*-----------------------------------------------------------------------*/
  1877.  
  1878. /* FUNCTION */
  1879.  
  1880. Mode_3_Graphics(input_bytes, output_bytes, address, invert)
  1881.  
  1882. unsigned int
  1883.    input_bytes,                 /* Count of bytes to be read. */
  1884.    output_bytes;                /* Count of bytes to be stored. */
  1885.  
  1886. unsigned char
  1887.    *address;                    /* Pointer to where to store bytes. */
  1888.  
  1889. unsigned char            /* Boolean to request data inversion */
  1890.     invert;
  1891.  
  1892. {
  1893.    /* LOCAL VARIABLES */
  1894.  
  1895.    unsigned char
  1896.       *store_ptr,               /* Pointer to where to store the byte. */
  1897.       input_char;               /* Byte to be changed. */
  1898.  
  1899.    unsigned int
  1900.       read_bytes,               /* Local copy of input_bytes. */
  1901.       write_bytes;              /* Local copy of output_bytes. */
  1902.  
  1903.    unsigned int
  1904.       replace,            /* number of bytes to replace, 1-8 */
  1905.       offset;            /* relative offset */
  1906.  
  1907. #if BITFIELDS
  1908.    union comtype {
  1909.       unsigned char comchar;    /* command byte as char */
  1910.       struct btype {
  1911.      unsigned repcount:3;    /* replace count 1-8 */
  1912.      unsigned roff:5;    /* relative offset 0-30 */
  1913.       } bitf;
  1914.    } command;
  1915. #else
  1916.     unsigned char    command;
  1917. #endif
  1918.  
  1919.    /* CODE */
  1920.  
  1921.    /* Initialize the local variables. */
  1922.  
  1923.    read_bytes = input_bytes;
  1924.    write_bytes = output_bytes;
  1925.    store_ptr = address;
  1926.  
  1927. /* read_bytes has to be at least 2 to be valid */
  1928.  
  1929.    while ( read_bytes > 1 && write_bytes > 0 ){
  1930.  
  1931.       /* start by getting the command byte */
  1932.  
  1933.       read_bytes--;
  1934.  
  1935. #if BITFIELDS
  1936.       command.comchar = Get_Character();
  1937.  
  1938.       replace = command.bitf.repcount + 1;    /* replace count 1-8 */
  1939.  
  1940.       offset = command.bitf.roff;        /* offset 0-30, 31= extend */
  1941. #else
  1942.     command = Get_Character();
  1943.     replace = (command >> 5) + 1;
  1944.     offset = command & 0x1f;
  1945. #endif
  1946.  
  1947.       store_ptr += offset;
  1948.       write_bytes -= offset;
  1949.  
  1950.       if ( offset == 31 )        /* get more offsets */
  1951.      do{
  1952.  
  1953.         offset = Get_Character();
  1954.  
  1955.         read_bytes--;
  1956.             if ( read_bytes == 0 )        /* premature finish? */
  1957.            return;                /* no zero fill wih 3 */
  1958.  
  1959.         store_ptr += offset;
  1960.             write_bytes -= offset;
  1961.  
  1962.      } while (offset == 255);    /* 255 = keep going */
  1963.       
  1964.       /* now do the byte replacement */
  1965.  
  1966.       while ( replace-- && write_bytes > 0 && read_bytes > 0 ){
  1967.      
  1968.      *store_ptr++ = (invert ? ~Get_Character() : Get_Character() );
  1969.  
  1970.      read_bytes--;
  1971.      write_bytes--;
  1972.       }
  1973.    }
  1974.    
  1975.    /* don't do any zero fill with mode 3 */
  1976.  
  1977.    /* discard any leftover input */
  1978.  
  1979.    Discard_Block(read_bytes);
  1980.  
  1981.    return( store_ptr - address );
  1982. }
  1983.  
  1984.  
  1985. Discard_Block(count)
  1986. unsigned int count;
  1987. {
  1988.     while ( count-- )
  1989.         getchar();
  1990. }
  1991.  
  1992. Transfer_Block( count, dest )
  1993. unsigned int count;
  1994. unsigned char *dest;
  1995. {
  1996.     fread(dest, 1, count, stdin);
  1997. }
  1998.  
  1999. /* this doesn't invert at the moment */
  2000.  
  2001. Inv_Transfer_Block( count, dest )
  2002. unsigned int count;
  2003. unsigned char *dest;
  2004. {
  2005.     fread(dest, 1, count, stdin);
  2006. }
  2007.  
  2008.  
  2009. Output_0(src, dest, count)
  2010. unsigned char *src, *dest;
  2011. int count;
  2012. {
  2013.     memcpy(dest, src, count);
  2014.  
  2015.     if ( zerostrip )
  2016.         while ( count && dest[count-1] == 0 )
  2017.             count--;
  2018.  
  2019.     return(count);
  2020.  
  2021. }
  2022.  
  2023. Output_1(src, dest, count)
  2024. unsigned char *src, *dest;
  2025. register int count;
  2026. {
  2027.     unsigned char *optr = dest, *iptr;
  2028.     int k,c;
  2029.  
  2030.     if ( zerostrip )            /* strip zeros */
  2031.     {
  2032.         iptr = src + count - 1;        /* point to end of data */
  2033.  
  2034.         while ( count > 0 && *iptr-- == 0 )    /* hunt thru 0's */
  2035.             count--;
  2036.     }
  2037.  
  2038.     iptr = src;
  2039.  
  2040.     while ( count ){
  2041.         
  2042.         c = *iptr++;        /* get value to work with */
  2043.         count--;
  2044.  
  2045.         k = 0;
  2046.  
  2047.         while ( *iptr == c && k < 255 && count ){
  2048.             k++;
  2049.             iptr++;
  2050.             count--;
  2051.         }
  2052.  
  2053.         *optr++ = k;        /* output repeat count */
  2054.         *optr++ = c;        /* output value */
  2055.     }
  2056.  
  2057.     count = optr - dest;        /* for return value */
  2058.  
  2059.     return ( count );
  2060. }
  2061.  
  2062.  
  2063. Output_2(src, dest, count)
  2064. unsigned char *src, *dest;
  2065. register int count;
  2066. {
  2067.     unsigned char *optr = dest, *iptr;
  2068.     int k,c;
  2069.     unsigned char *tptr, *tptr1, *tptr2;
  2070.     int tk,tc;
  2071.  
  2072.  
  2073.     if ( zerostrip )            /* strip zeros */
  2074.     {
  2075.         iptr = src + count - 1;        /* point to end of data */
  2076.  
  2077.         while ( count > 0 && *iptr-- == 0 )    /* hunt thru 0's */
  2078.             count--;
  2079.     }
  2080.  
  2081.     iptr = src;
  2082.  
  2083.     while ( count ){
  2084.         
  2085.         c = *iptr++;        /* get value to work with */
  2086.         count--;
  2087.  
  2088.         k = 0;
  2089.  
  2090.         while ( *iptr == c && k < 127 && count ){
  2091.             k++;
  2092.             iptr++;
  2093.             count--;
  2094.         }
  2095.  
  2096.         if ( k >= 1 ){
  2097.             *optr++ = 256 - k;    /* output repeat count */
  2098.             *optr++ = c;        /* output value */
  2099.         } else {
  2100.                     /* a two byte replicate run will
  2101.                      * be sent as a repeated byte 
  2102.                      * unless it is preceeded and
  2103.                      * and followed by a literal run,
  2104.                      * in which case it is merged
  2105.                      * into the run.
  2106.                      */
  2107.             tk = 0;
  2108.             tc = c;
  2109.             tptr = iptr;
  2110.             tptr1 = tptr;
  2111.             tptr1++;
  2112.             tptr2 = tptr1;
  2113.             tptr2++;
  2114.  
  2115.             while ( tk < 128 && (count - tk) > 0    && 
  2116.                 ((*tptr != tc) || (*tptr == tc &&
  2117.                                    (count - tk - 1) > 0 &&
  2118.                                    *tptr1 != *tptr &&
  2119.                                    *tptr2 != *tptr1))){
  2120.  
  2121.                 tc = *tptr++;
  2122.                 tk++;
  2123.                 tptr1++;
  2124.                 tptr2++;
  2125.             }
  2126.  
  2127.             if ( count && tk )
  2128.                 tk--;
  2129.  
  2130.             *optr++ = tk;        /* output count */
  2131.             *optr++ = c;        /* output firstvalue */
  2132.  
  2133.             while ( tk-- > 0){
  2134.                 *optr++ = *iptr++;
  2135.                 count--;
  2136.             }
  2137.  
  2138.         }
  2139.     }
  2140.  
  2141.     count = optr - dest;        /* for return value */
  2142.  
  2143.     return ( count );
  2144. }
  2145.  
  2146. Output_3(seed, new, dest, count)
  2147. unsigned char *seed, *new, *dest;
  2148. int count;
  2149. {
  2150.     unsigned char *sptr=seed, *nptr=new, *dptr=dest;
  2151.     int i,j;
  2152.  
  2153.  
  2154. #if BITFIELDS
  2155.    union comtype {
  2156.       unsigned char comchar;    /* command byte as char */
  2157.       struct btype {
  2158.      unsigned repcount:3;    /* replace count 1-8 */
  2159.      unsigned roff:5;    /* relative offset 0-30 */
  2160.       } bitf;
  2161.    } command;
  2162. #else
  2163.     unsigned char    command;
  2164. #endif
  2165.  
  2166.     while ( count > 0 ){
  2167.         i = 0;
  2168.  
  2169.                     /* find first diff */
  2170.         while ( *sptr == *nptr && i < count ){
  2171.             i++;
  2172.             sptr++;
  2173.             nptr++;
  2174.         }
  2175.  
  2176.         if ( i >= count )    /* too far to find diff */
  2177.             return(dptr - dest);    /* bail */
  2178.  
  2179.         count -= i;
  2180.         
  2181.         /* now count how many bytes to change */
  2182.  
  2183.         for ( j = 1; j < 8; j++)    /* j == 0 is already known */
  2184.             if ( j > count || sptr[j] == nptr[j] )
  2185.                 break;
  2186.         
  2187.         j--;    /* adjust */
  2188.  
  2189. #if BITFIELDS
  2190.         command.bitf.repcount = j;    /* 0-7 ==> 1-8 */
  2191.  
  2192.         command.bitf.roff = MIN ( i, 31 );
  2193.  
  2194.         *dptr++ = command.comchar;    /* output command */
  2195. #else
  2196.         command = (j << 5);
  2197.         command += MIN( i, 31 );
  2198.         *dptr++ = command;
  2199. #endif
  2200.  
  2201.         if ( i == 31 )
  2202.             *dptr++ = 0;
  2203.         
  2204.         i -= MIN (i, 31);
  2205.  
  2206.         while( i ){
  2207.             *dptr++ = MIN ( i, 255 );
  2208.  
  2209.             if ( i == 255 )
  2210.                 *dptr++ = 0;
  2211.             
  2212.             i -= MIN ( i, 255 );
  2213.         }
  2214.  
  2215.         while (j-- >= 0){
  2216.             *dptr++ = *nptr++;
  2217.             sptr++;
  2218.             count--;
  2219.         }
  2220.     }
  2221.  
  2222.     return ( dptr - dest );
  2223. }
  2224.  
  2225.  
  2226. /*----------------------------------------------------------------------*\
  2227.  * This is here in case <ESC>*rU is sent after <ESC>*r#A, in which case *
  2228.  * we must deallocate the memory to provide for a different amount of   *
  2229.  * planes when graphics are sent.                                       *
  2230. \*----------------------------------------------------------------------*/
  2231.  
  2232. free_mem()    
  2233. {
  2234.     int r;
  2235.  
  2236.  
  2237.     if ( !memflag )
  2238.         return;        /* no memory to free */
  2239.  
  2240.     free(new_row);
  2241.  
  2242.     for(r = MAXMODES -1; r >= 0; r--)
  2243.         free(out_row[r]);
  2244.  
  2245.     for(r = num_planes - 1; r >= 0; r--)
  2246.         free(seed_row[r]);
  2247.  
  2248.     memflag = FALSE;
  2249. }
  2250.