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