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