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