/*-
 * Copyright (c) 1996 Arno Augustin
 *
 * Email:  augustin@informatik.uni-erlangen.de
 *
 *	 $Id: pnmscan.c,v 1.2 1996/10/03 15:48:52 arno Exp arno $
 *
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 * 
 * -  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * -  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * */


#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <math.h>

#ifdef VMS
#include <iodef.h>
#include <descrip.h>
#include <starlet.h>
#include <LIB$ROUTINES.H>
#endif

char *rcsid="$Id: pnmscan.c,v 1.2 1996/10/03 15:48:52 arno Exp arno $";

#define PNMSCAN_VERSION "1.3 3/10/96"

#define MODE_MULTIBIT_COLOR	1
#define MODE_MULTIBIT_GRAY	2
#define MODE_LINEART_GRAY	3
#define MODE_HALFTONE_GRAY      4

#define FILTER_GRAY		0
#define FILTER_RED		1
#define FILTER_GREEN		2
#define FILTER_BLUE		3

#define UNIT_INCH		1
#define UNIT_MM			2
#define UNIT_PIX		3


struct scanmode {
    u_short	mode;
    u_short	filter;			/* color filter if GRAY MODE */
    u_short	dpi;			/* scan resolution in dpi */
    u_short	internal_gamma;		/* internal gamma table */
    float	gamma;			/* gamma value */
    u_short     grain;			/* internal halftone pattern 0x00 to 0x0b */
    short	brightness;		/* brightnes -36 to 36 (per cent) */
    short	contrast;		/* contast -84 to 84 (per cent) */
    u_short	velocity;		/* 0 to 4 (0=fast, 4=slow) */
    u_short     backtrack;		/* 0 off, 1 on */
    u_short	units;			/* units (mm, pixel, inches) */
    float	paperlength;		/* in units  */
    float	areas[5][4];	        /* frame area, window areas 0-3 */
    int         nwindows;               /* number of defined windows */
};


static struct scanmode sm = {
    MODE_LINEART_GRAY,		/* mode */
    FILTER_GRAY,		/* filter */
    300,			/* dpi */
    0,				/* use gamma table (0-32) */
    0.0,			/* 0 = use default gamma table */
    6,				/* grain */
    0,				/* brightness */
    0,				/* contrast   */
    4,				/* velocity   */
    0,           		/* backtrack */
    UNIT_MM,			/* units      */
    297.0,			/* paperlength*/
    0.0,0.0,210.0,297.0,	/* A4 area    */
    0.0,0.0,0.0,0.0,		/* window 0   */
    0.0,0.0,0.0,0.0,		/* window 1   */
    0.0,0.0,0.0,0.0,		/* window 2   */
    0.0,0.0,0.0,0.0,		/* window 3   */
    0                           /* no windows */
};

#ifndef VMS 
#define DEFAULTDEVICE "/dev/scanner"
#else
#define DEFAULTDEVICE "MUSCAN"

struct SCSI$DESC {
        unsigned int    SCSI$L_OPCODE;          /* SCSI Operation Code */
        unsigned int    SCSI$L_FLAGS;           /* SCSI Flags Bit Map */
        char *          SCSI$A_CMD_ADDR;        /* ->SCSI command buffer */
        unsigned int    SCSI$L_CMD_LEN;         /* SCSI command length, bytes */
        char *          SCSI$A_DATA_ADDR;       /* ->SCSI data buffer */
        unsigned int    SCSI$L_DATA_LEN;        /* SCSI data length, bytes */
        unsigned int    SCSI$L_PAD_LEN;         /* SCSI pad length, bytes */
        unsigned int    SCSI$L_PH_CH_TMOUT;     /* SCSI phase change timeout */
        unsigned int    SCSI$L_DISCON_TMOUT;    /* SCSI disconnect timeout */
        unsigned int    SCSI$L_RES_1;           /* Reserved */
        unsigned int    SCSI$L_RES_2;           /* Reserved */
        unsigned int    SCSI$L_RES_3;           /* Reserved */
        unsigned int    SCSI$L_RES_4;           /* Reserved */
        unsigned int    SCSI$L_RES_5;           /* Reserved */
        unsigned int    SCSI$L_RES_6;           /* Reserved */
};
#ifdef __ALPHA
#pragma member_alignment save
#pragma nomember_alignment
#endif

struct SCSI$IOSB {
        unsigned short int SCSI$W_VMS_STAT;     /* VMS status code */
        unsigned long int  SCSI$L_IOSB_TFR_CNT; /* Actual #bytes transferred */
        char               SCSI$B_IOSB_FILL_1;
        unsigned char      SCSI$B_IOSB_STS;     /* SCSI device status */
};

#ifdef __ALPHA
#pragma member_alignment restore
#endif
#define SCSI$K_GOOD_STATUS      0
#define SCSI$K_WRITE            0X0     /* direction of transfer=write */
#define SCSI$K_READ             0X1     /* direction of transfer=read */
#define SCSI$K_FL_ENAB_DIS      0X2     /* enable disconnects */
#define SCSI$K_FL_ENAB_SYNC     0X4     /* enable sync */
#define GK_EFN 0                /* Event flag number */

static char gk_device[] = {DEFAULTDEVICE};
static unsigned short int gk_chan,transfer_length;
static int i,status;
static $DESCRIPTOR(gk_device_desc, gk_device);
static struct SCSI$IOSB gk_iosb ;
static struct SCSI$DESC gk_desc;

int fd1;
char *outname = "pnm.dat";
#define nextargs (--argc,*++argv)
#endif

#define STDOUT	1

char *device = DEFAULTDEVICE;
int outfd = 1, scanfd = -1;
int verbose = 0, reset = 0;


#include "proto_neu.h"



static void abortscan(int sig)
{
  sleep(1);
  stopscan(scanfd), exit(0);
}

#if defined(linux)
#include <signal.h>
static void setsignals()
{
  signal(SIGQUIT, abortscan);
  signal(SIGTERM, abortscan);
  signal(SIGINT, abortscan);
  signal(SIGPIPE, abortscan);
  signal(SIGABRT, abortscan);
  signal(SIGHUP, abortscan);
}
#else
static void setsignals(){}
#endif


static void print_settings(m)
    struct scanmode *m;
{
    char string1[256], string2[256], *patternstr, *modestr, *filterstr,
         *unitstr;
    int i;

    switch(m->mode) {
    case MODE_MULTIBIT_COLOR:
        m->filter = FILTER_GRAY;
	modestr = "24-bit RGB (PPM)";
	break;
    case MODE_MULTIBIT_GRAY:  modestr = "8-bit Gray (PGM)"; break;
    case MODE_LINEART_GRAY:   modestr = "1-bit B/W lineart (PBM)"; break;
    case MODE_HALFTONE_GRAY:  modestr = "1-bit B/W dithered (PBM)"; break;
    }

    switch(m->filter) {
    case FILTER_GRAY:  filterstr = "no filter"; break;
    case FILTER_RED:   filterstr = "red"; break;
    case FILTER_GREEN: filterstr = "green"; break;
    case FILTER_BLUE:  filterstr = "blue"; break;
    }
    
    switch(m->units) {
    case UNIT_MM:   unitstr = "mm" ; break;
    case UNIT_PIX:  unitstr = "pixel" ; break;
    case UNIT_INCH: unitstr = "inch" ; break; 
    }


    fprintf(stderr, "settings:\n");
    fprintf(stderr, "\tdevice        = %s\n", device);
    fprintf(stderr, "\tmode          = %s\n", modestr);
    fprintf(stderr, "\tfilter        = %s\n", filterstr);
    fprintf(stderr, "\tscanarea      = (%3.1f, %3.1f, %3.1f, %3.1f) %s\n",
	    m->areas[0][0], m->areas[0][1], m->areas[0][2], m->areas[0][3],
	    unitstr);

    if(m->nwindows == 0) fprintf(stderr, "\t(no windows)\n");
    for(i=0; i<m->nwindows; i++) {
        fprintf(stderr, "\twindow %d      = (%3.1f, %3.1f, %3.1f, %3.1f) %s\n",
		i, m->areas[i+1][0], m->areas[i+1][1], m->areas[i+1][2], m->areas[i+1][3],
		unitstr);
    }
    fprintf(stderr, "\tdpi           = %d\n", m->dpi);
    fprintf(stderr, "\tbrightness    = %d%%\n", m->brightness);
    fprintf(stderr, "\tcontrast      = %d%%\n", m->contrast);
    fprintf(stderr, "\tgrain         = %d\n", m->grain);
    fprintf(stderr, "\tpaper length  = %3.1f\n", m->paperlength);
    fprintf(stderr, "\tvelocity      = %d\n", m->velocity);
    sprintf(string1,"%3.1f (exp. ramp)", m->gamma);
    sprintf(string2,"scanner internal table #%d", m->internal_gamma);
    fprintf(stderr, "\tgamma         = %s\n",
	    m->gamma != 0.0 ? string1 : string2);
    fprintf(stderr, "\tbacktracking  = %s\n", m->backtrack ? "on" : "off");
    fprintf(stderr, "\n");
}


int checkrange(m)
    struct scanmode *m;
{
    float coor[5][4], multiplier;
    int i, n;
    
    if(m->dpi <3 || m->dpi >600 ||
       m->brightness >36 || m->brightness < -36 ||
       m->contrast >84 || m->contrast < -84 ||
       m->internal_gamma >32 || m->grain >0x0b) {
	fprintf(stderr, "pnmscan: range check failed - invalid scan parameters"); 
	return 0;
    }

    /* coordinates to inch */
    switch(m->units) {
    case UNIT_MM:    multiplier = 1/25.4;      break;
    case UNIT_INCH:  multiplier = 1;	       break;
    case UNIT_PIX:   multiplier = 2.54/m->dpi; break;
    }
    for(n=0; n<m->nwindows+1; n++) {
        for(i=0; i<4; i++)  coor[n][i] = m->areas[n][i]*multiplier;
    }
    
    /* range check frame */
    if(coor[0][0] > coor[0][2] || coor[0][1] > coor[0][3] ||
       coor[0][0] < 0.0 || coor[0][2] > 8.5 ||
       coor[0][1] < 0.0 || coor[0][3] > 14.0) {

      fprintf(stderr, "pnmscan: invalid scan area\n"); 
      return 0;
    }

    /* range check windows */
    for(n=1; n<m->nwindows+1; n++) {
        if(coor[n][0] > coor[n][2] || coor[n][1] > coor[n][3] ||
	   coor[n][0] < coor[0][0] || coor[n][1] < coor[0][1] ||
	   coor[n][2] > coor[0][2] || coor[n][3] > coor[0][3]){
	    fprintf(stderr, "pnmscan: window %d out of range\n", n-1); 
	    return 0;
	}
    }
    return 1;
}


static parse_options(argc, argv)
    int argc;
    char **argv;
{
    int i, n, flag;
    
    for(i=1; i<argc && argv[i][0]=='-'; i++) {

        switch( argv[i][1] ) {

	case 'c':		/* color mode */
	    sm.mode = MODE_MULTIBIT_COLOR;
	    break;
	case 'g':		/* gray mode */
	    sm.mode = MODE_MULTIBIT_GRAY;
	    break;
	case 'l':		/* lineart mode */
	    sm.mode = MODE_LINEART_GRAY;
	    break;
	case 'h':		/* halftone mode */
	    if(argv[i][2] == 'e') usage(argv[0]);
	    sm.mode = MODE_HALFTONE_GRAY;
	    break;
	case 't':		/* backtracking on */
	    sm.backtrack = 1;
	    break;
	case 'd':		/* scanner device */
	    if(++i >= argc)  usage(argv[0]);
	    if(getuid() != 0) {
	      fprintf(stderr,"Must be root to set device to %s\n", argv[i]);
	      exit(10);
	    } else {
	      device = argv[i];
	    }
	    break;
	case 'r':		/* resolution */
	    if(++i >= argc)  usage(argv[0]);
	    sm.dpi = atoi(argv[i]);
	    break;
	case 'B':		/* brightness */
	    if(++i >= argc)  usage(argv[0]);
	    sm.brightness = atoi(argv[i]);
	    break;
	case 'C':		/* contrast */
	    if(++i >= argc)  usage(argv[0]);
	    sm.contrast = atoi(argv[i]);
	    break;
	case 'H':		/* grain */
	    if(++i >= argc)  usage(argv[0]);
	    sm.grain = atoi(argv[i]);
	    break;
	case 'v':		/* velocity */
	    if(++i >= argc)  usage(argv[0]);
	    sm.velocity = atoi(argv[i]);
	    break;
	case 'f':		/* filter */
	    switch(argv[i][2]) {
	    case 'r':    sm.filter = FILTER_RED; break;
	    case 'g':    sm.filter = FILTER_GREEN; break;
	    case 'b':    sm.filter = FILTER_BLUE; break;
	    } break;
	case 'a': 		/* scan area */
	    for(n=0; n<4; n++) {
		if(++i >= argc) usage(argv[0]);
		sm.areas[0][n] = atof(argv[i]);
	    }
	    break;
	case 'w': 		/* scan window */
	    if(sm.nwindows >= 4) {
	      fprintf(stderr, "only 4 windows supported!\n");
	      usage(argv[0]);
	    }
	    for(flag=0, n=0; n<4; n++){
		if(++i >= argc) usage(argv[0]);
		flag |= ((sm.areas[sm.nwindows+1][n]= atof(argv[i])) != 0.0);
	    }
	    if(flag) sm.nwindows++;
	    break;
	case 'i':		/* inches */
	    sm.units = UNIT_INCH;
	    break;
	case 'p':		/* pixel */
	    sm.units = UNIT_PIX;
	    break;
	case 'm':		/* mm */
	    sm.units = UNIT_MM;
	    break;
	case 'T':		/* gammatab */
	    if(++i >= argc)  usage(argv[0]);
	    sm.internal_gamma = atoi(argv[i]);
	    break;
	case 'G':		/* gammaval */
	    if(++i >= argc)  usage(argv[0]);
	    sm.gamma = atof(argv[i]);
	    break;
	case 'V':
	    verbose = 1;
	    break;
	case 'R':
	    reset = 1;
	    break;
#ifdef VMS
        case 'o':
	    if(++i >= argc)  usage(argv[0]);
	    outname = argv[i];    
	    break;
#endif
	default:
	    usage(argv[0]);
	    break;
	}
    }

    if(i<argc) 	usage(argv[0]);
}


static void usage(name)
    char *name;
{
    fprintf(stderr, "\nusage: %s [options]\n\n", name);
    fprintf(stderr, "valid options are:\n");
    fprintf(stderr,
	    "\t-d <device>       set scanner device\n");
    fprintf(stderr,
	    "\t-c                color mode, output is PPM\n");
    fprintf(stderr,
	    "\t-g                grayscale mode, output is PGM\n");
    fprintf(stderr,
	    "\t-h                halftone mode, output is PBM\n");
    fprintf(stderr,
	    "\t-l                lineart mode, output is PBM\n");
    fprintf(stderr,
	    "\t-fN               set filter (N=r,b,g)\n");
    fprintf(stderr,    
	    "\t-r <dpi>          set resolution\n");
    fprintf(stderr,
	    "\t-B <brightness>   set brightness (-36 to 36, default 0)\n");
    fprintf(stderr,
	    "\t-C <contrast>     set contrast (-84 to 84, default 0)\n");
    fprintf(stderr,
	    "\t-H <grain>        set halftone grain (0 to 11)\n");
    fprintf(stderr,
	    "\t-v <velocity>     set speed (0=fast, 4=slow, default 4)\n");
    fprintf(stderr,
	    "\t-i                units are inches\n");
    fprintf(stderr,
	    "\t-m                units are mm\n");
    fprintf(stderr,
	    "\t-p                units are pixel \n");
    fprintf(stderr,
	    "\t-a <x1 y1 x2 y2>  set scan area (in units)\n");
    fprintf(stderr,
	    "\t-w <x1 y1 x2 y2>  set a scan window (max. 4 windows)\n");
    fprintf(stderr,
	    "\t-T <gammatab>     use internal gamma table (0 to 32)\n");
    fprintf(stderr,
	    "\t-G <gammaval>     download computed gamma ramp\n");
    fprintf(stderr,
	    "\t-t                select backtracking\n");
    fprintf(stderr,
	    "\t-V                Verbose output\n");
    fprintf(stderr,
	    "\t-R                Reset Scanner\n");
#ifdef VMS
    fprintf(stderr,
            "\t-o                filename (an VMS-specific extention)\n\t                  Default-name pnm.dat\n");
    print_settings(&sm);
#endif    
    fprintf(stderr, "\npnmscan %s - © 1996 Arno Augustin \n",
	    PNMSCAN_VERSION);

    exit(1);
}


static void errexit(char *format, ...)
{
    va_list ap;

    va_start(ap, format);

    fprintf(stderr, "pnmscan -- ");
    vfprintf(stderr, format, ap);
    perror(" ");

    va_end(ap);
    exit(10);
}


static int gettmpfd()
{
    char *tmpname, tmpfilename[256];
    static int nr;
    int fd;
#ifndef VMS    
    if (tmpname = getenv("TMP")); else tmpname = "/tmp";
    sprintf(tmpfilename,"%s/scan_%d_%d", tmpname, getpid(),nr++);

    if(-1 == (fd =open(tmpfilename, O_RDWR | O_CREAT, 0600))) return -1;
    unlink(tmpfilename);
#else
    if (tmpname = getenv("TMP")); else tmpname = "pnm";
    sprintf(tmpfilename,"%scan_%d_%d", tmpname, getpid(),nr++);
    if(-1 == (fd =open(tmpfilename, O_RDWR | O_CREAT, 0600,"fop=tmd"))) return -1;
#endif
    return fd;
}


static void
buildgamma( table, gamma )
    unsigned char table[];
    double gamma;
{
    register int i, v;
    int maxval = 255;
    double ind, one_over_gamma = 1.0/gamma, q = (double) maxval;

    for ( i=0 ; i <=  maxval; i++ ) {
	ind = ( (double) i ) / q;

	v = ( q * pow( ind, one_over_gamma ) ) + 0.5;
	if ( v > (int) maxval )  v = maxval;
	table[i] = v;
    }
}

    
			 
/****************************** Scanner SCSI ********************************/

#define NOBIT 0x00
#define BIT0  0x01
#define BIT1  0x02
#define BIT2  0x04
#define BIT3  0x08
#define BIT4  0x10
#define BIT5  0x20
#define BIT6  0x40
#define BIT7  0x80


#define CMD_TEST_UNIT_READY	0x00
#define CMD_INQUIRY		0x12
#define CMD_SET_AREAS		0x04
#define CMD_MODE_SELECT		0x15
#define CMD_LOAD_LUT		0x55
#define CMD_START_STOP		0x1b
#define CMD_STATUS		0x0f
#define CMD_READ		0x08
#define CMD_BACKTRACK		0x10


/* filter */
#define GRAY		0
#define RED		1
#define GREEN		2
#define BLUE		3

#define MULTIBIT	1
#define SINGLEBIT	0

/* user defined halftone pattern size */
#define HTPATTERN_8     0x88	/* 8x8 */
#define HTPATTERN_6	0x66	/* 6x6 */
#define HTPATTERN_5	0x55    /* 5x5 */
#define HTPATTERN_4	0x44    /* 4x4 */
#define HTPATTERN_3	0x33    /* 3x3 */
#define HTPATTERN_2	0x22    /* 2x2 */


#if defined(linux)
#	define READBUF 32*1024
#else
#	define READBUF 64*1024
#endif

#define MAXXSIZE READBUF/3


struct image_status {
   u_int	busy,		/* 0= scanner ready, 1= singlebit, X= error */
   		rowbytes,	/* number of bytes per scan line */
                scanlines;      /* number of lines remaining */
};


static u_char linbuf[READBUF];


void scan(fd, m)
    int fd; struct scanmode *m;
{
    u_int i;
    struct image_status *istat;
    char pbmhead[100];
    
    waitready(fd, 30);
    /* stopscan(scanfd); */
    
    /* start scan */
    switch(m->mode) {
    case MODE_MULTIBIT_GRAY:
	initscan(fd, m);
	startscan(scanfd, m->filter, MULTIBIT, m->dpi);
	istat = getistatus(scanfd);
	if(verbose) fprintf(stderr, "writing %dx%d PGM image\n",
			    istat->rowbytes, istat->scanlines);
	sprintf(pbmhead, "P5\n%d %d\n255\n",istat->rowbytes,istat->scanlines);
#ifndef VMS
	write(STDOUT, pbmhead, strlen(pbmhead));
#else
	fd1 = creat(outname,0,"rfm=stmlf", "mrs=32767");
        if (fd1 == -1)
        {
        perror("Output file creation failed\n");
        exit(2);
        }

	write(fd1, pbmhead, strlen(pbmhead));
#endif
	waitready(fd, 2);
#ifndef VMS
	readpage(fd, STDOUT, istat->rowbytes, istat->scanlines, 0);
#else
        readpage(fd,fd1, istat->rowbytes, istat->scanlines, 0);
#endif
	break;

    case MODE_LINEART_GRAY:
    case MODE_HALFTONE_GRAY:
	initscan(fd, m);
	startscan(scanfd, m->filter, SINGLEBIT, m->dpi);
	waitready(fd, 2);
	istat = getistatus(scanfd);
	if(verbose) fprintf(stderr, "writing %dx%d PBM image\n",
			    istat->rowbytes * 8, istat->scanlines);
	sprintf(pbmhead, "P4\n%d %d\n",
		istat->rowbytes * 8, istat->scanlines);
#ifndef VMS
	write(STDOUT, pbmhead, strlen(pbmhead));
	readpage(fd, STDOUT, istat->rowbytes, istat->scanlines, 1);
#else
        fd1 = creat(outname,0,"rfm=stmlf", "mrs=32767");
        if (fd1 == -1)
        {
        perror("Output file creation failed\n");
        exit(2);
        }
        write(fd1, pbmhead, strlen(pbmhead));
        readpage(fd, fd1, istat->rowbytes, istat->scanlines, 1);
#endif
	break;
    case MODE_MULTIBIT_COLOR:
    {
	int rfd, gfd, bfd, byte;
	u_char rbuf[MAXXSIZE], gbuf[MAXXSIZE], bbuf[MAXXSIZE], *out;
	
	if(-1 == (rfd = gettmpfd()) ||
	   -1 == (gfd = gettmpfd()) ||
	   -1 == (bfd = gettmpfd())) {
	    errexit("Error creating tmpfile");
	}
	initscan(fd, m);
	startscan(scanfd, FILTER_RED, MULTIBIT, m->dpi);
	waitready(fd, 2);
	istat = getistatus(scanfd);
	readpage(fd, rfd, istat->rowbytes, istat->scanlines, 0);
	waitready(fd, 25);
	initscan(fd, m);
	startscan(scanfd, FILTER_GREEN, MULTIBIT, m->dpi);
	waitready(fd, 2);
	istat = getistatus(scanfd);
	readpage(fd, gfd, istat->rowbytes, istat->scanlines, 0);
	waitready(fd, 25);
	initscan(fd, m);
	startscan(scanfd, FILTER_BLUE, MULTIBIT, m->dpi);
	waitready(fd, 2);
	istat = getistatus(scanfd);
	readpage(fd, bfd, istat->rowbytes, istat->scanlines, 0);

	/* write ppm picture */
	if(verbose) fprintf(stderr, "writing %dx%d PPM image\n",
			    istat->rowbytes, istat->scanlines);
	sprintf(pbmhead, "P6\n%d %d\n255\n", istat->rowbytes, istat->scanlines);
#ifndef VMS
	write(STDOUT, pbmhead, strlen(pbmhead));
#else
        fd1 = creat(outname,0,"rfm=stmlf", "mrs=32767");
        if (fd1 == -1)
        {
        perror("Output file creation failed\n");
        exit(2);
        }
	write(fd1, pbmhead, strlen(pbmhead));
#endif
	lseek(rfd, 0, SEEK_SET);
	lseek(gfd, 0, SEEK_SET);
	lseek(bfd, 0, SEEK_SET);
	errno = 0;
	for(i=0; i<istat->scanlines; i++){
	    int rb = istat->rowbytes;
	    if(read(rfd, rbuf, rb) < rb || read(gfd, gbuf, rb) < rb||
	       read(bfd, bbuf, rb) < rb  ) {
		stopscan(fd);
		errexit("read tmp file failed");
	    }
	    for(byte = 0, out = linbuf; byte < rb; byte++){
		*out++ = rbuf[byte];
		*out++ = gbuf[byte];
		*out++ = bbuf[byte];
	    }
#ifndef VMS
	    write(STDOUT, linbuf, istat->rowbytes * 3);
#else
            write(fd1, linbuf, istat->rowbytes * 3);
#endif
	}
#ifdef VMS
	close(rfd);
        close(gfd);
        close(bfd);
#endif
	break;
    }
    }

}


initscan(fd, m)
    int fd; struct scanmode *m;
{
    u_short w[20], paperlen;
    float multiplier;
    u_int i, n, j;
   
    waitready(fd, 3);

    /* coordinates to 1/8 inch */
    switch(m->units) {
    case UNIT_MM:    multiplier = 8.0/25.4;        break;
    case UNIT_INCH:  multiplier = 8.0;	           break;
    case UNIT_PIX:   multiplier = 8.0*2.54/m->dpi; break;
    }
    for(n=0, j=0; n<5; n++) {
        for(i=0; i<4; i++)
	    w[j++] = m->areas[n][i]*multiplier;
    }
    paperlen = m->paperlength*multiplier;

    /* set scan windows */
    setareas(scanfd,
	     (m->mode == MODE_HALFTONE_GRAY) ? 1 : 0, w, m->nwindows);

    /* set mode */
    setvalues(scanfd, paperlen, m->dpi, m->brightness, m->contrast,
	      m->grain, m->velocity);

    /* set look up table */
    if(m->gamma ==  0.0 || m->gamma == 1.0) uselut(scanfd, m->internal_gamma);
    else {
	u_char r[256], g[256], b[256];

	buildgamma(r, (double) m->gamma );
	buildgamma(g, (double) m->gamma );
	buildgamma(b, (double) m->gamma );
	loadlut(fd, r, b, g);
    }

    /* set backtracking */
    setbacktrack(fd, m->backtrack);
}


void readpage(fd, outfd, rowbytes, scanlines, invert)
    int fd, outfd;
    u_short rowbytes, scanlines, invert;
{
    
    u_int i, j, rdlines, rdlen, rdblocks, sl=0;

    rdlines = READBUF/rowbytes;
    rdlen = rowbytes*rdlines;
    rdblocks = scanlines/rdlines;

    if(verbose)
        fprintf(stderr,"scanning: %d lines, %d bytes per line, %d lines per block\n",
		scanlines, rowbytes, rdlines);

    for(i=0; i<rdblocks; i++) {
	readlines(fd, linbuf, rdlen, rdlines);
	sl += rdlines;
	if(verbose) fprintf(stderr, "%4d lines scanned (%3d%%)\r",
			    sl, sl*100/scanlines);
	if(invert) {
	    for(j=0; j<rdlen; j++) linbuf[j] ^= 0xff;
	}
	if( -1 == write(outfd, linbuf, rdlen)) goto error;
    }

    rdlines = scanlines - (rdlines*rdblocks);
    rdlen = rowbytes*rdlines;
    readlines(scanfd, linbuf, rdlen, rdlines);
    sl += rdlines;
    if(verbose) fprintf(stderr, "%4d lines scanned (%3d%%)\r",
			sl, sl*100/scanlines);
    if(invert) {
	for(j=0; j<rdlen; j++) linbuf[j] ^= 0xff;
    }
    if( -1 == write(outfd, linbuf, rdlen)) goto error;

    if(verbose) fprintf(stderr, "\n");
    return;

  error:
    waitready(fd, 25);
    stopscan(fd);
    errexit("write() failed");
}


#define FRAMEHEADER     0x00
#define WINDOWHEADER	BIT7
#define USERHTHEADER    BIT6


#define SETCDB(cdb, cmd, len)	(cdb)[0]=cmd;\
				(cdb)[1]=0;\
				(cdb)[2]=((len)>>16) & 0xff;\
				(cdb)[3]=((len)>>8) & 0xff;\
				(cdb)[4]=(len) & 0xff;\
				(cdb)[5]=0;

#define SETCDB10(cdb, cmd, len)         (cdb)[0]=cmd;\
					(cdb)[1]=0;\
					(cdb)[2]=0;\
					(cdb)[3]=0;\
					(cdb)[4]=0;\
					(cdb)[5]=0;\
					(cdb)[6]=((len) >> 16) & 0xff;\
					(cdb)[7]=((len) >>  8) & 0xff;\
					(cdb)[8]=(len) & 0xff;\
					(cdb)[9]=0
int checkscanner(fd)
    int fd;
{
    u_char cdb[6], data[96];
    int i;
    
    waitready(fd, 30);
    SETCDB(cdb, CMD_INQUIRY, sizeof(data));
    getdata(fd, data, sizeof(data), cdb);

    /* check SCSI device type (scanner is 0x06) */
    if( (data[0] & 0x1f) != 0x06 ) return 0;

    if(verbose) {
        for(i=0; i<36; i++)  if(!isprint(data[i])) data[i]=0;
	
	fprintf(stderr,"\tscanner: %.8s %.16s rev %.4s, %d pass\n\n",
		data+8, data+16, data+32, data[57]&BIT6 ? 1 : 3);
    }

    return 1;
}


/* not used */
void test_unit_ready(fd)
    int fd;
{
    u_char cdb[6], buffer[10];

    SETCDB(cdb,CMD_TEST_UNIT_READY,0);
    getdata(fd, buffer, 10, cdb);
}


setareas(fd, halftone, coor, nwin)
    int fd, halftone, nwin;
    u_short *coor;
{
    u_char data[45], cdb[6];
    int i, j, k, n;
    
    /* coor[0] to coor[3] is scan area, coor[4] to coor[20] are windows */
    /* force 1/8" units! using pixel units causes the scanner to step in 1/3"! */

    for(n=0, j=0, k=0; n<nwin+1; n++) {
        /* set header */
        if(n==0) data[j++] = FRAMEHEADER | (halftone ? BIT0 : NOBIT);
	else     data[j++] = WINDOWHEADER;

	/* set coordinates */
        for(i=0; i<4; i++, k++) {
	    data[j++] = coor[k] & 0xff;		/* LSB */
	    data[j++] = (coor[k] >> 8 ) & 0xff;	/* MSB */
	}
    }

    SETCDB(cdb, CMD_SET_AREAS, j);
    if(-1 == putdata(fd, data, j, cdb)){
	fprintf(stderr,"setareas: unable to send data!\n");
	return 0;
    }
    return 1;
}


setvalues(fd, paperlen, dpi, brightness, contrast, grain, speed)
    int fd;
    short brightness, contrast;
    u_short paperlen, grain, speed;
    
{
    u_char data[10], cdb[6];
    static u_char ss_res[] = {
	10, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
	0x1e, 0x3c, 0x5a, 0x14, 0x32, 0x50, 0x0a, 0x28, 0x46, 0x64};

#define SETRES_6000CX(dpi)     (  (dpi) ==   0  ? ss_res[600/30]\
			        : (dpi) <    3  ? 3/3\
				: (dpi) <= 300  ? (dpi)/3\
			        : (dpi) <  330  ? 300/3\
			        : (dpi) <= 600  ? ss_res[(dpi)/30]\
			        : ss_res[600/30])

#define SETBR(br)   ((br)/3+12) /* set brightnes */
#define SETCONT(br) ((br)/7+12) /* set contrast  */

    if(paperlen > 112) {
	fprintf(stderr,"pnmscan: paperlen out of range (0 to 14in)\n");
    }

    data[0] = BIT7|BIT1|BIT0;	/* always 1/8 inch, no user defined halftone pattern */
    data[1] = SETRES_6000CX(dpi);
    data[2] = SETBR(brightness);
    data[3] = SETCONT(contrast);
    data[4] = grain;
    data[5] = speed;
    data[6] = 0;
    data[7] = 0;
    data[8] = paperlen & 0xff;
    data[9] = (paperlen >> 8) & 0xff;

    SETCDB(cdb, CMD_MODE_SELECT, sizeof(data));
    putdata(fd, data, sizeof(data), cdb);

    return 1;
}


uselut(fd, nr)
    int fd; u_int nr;
{
    u_char cdb[10];

    if(nr > 32) {
  	fprintf(stderr,"pnmscan: loadlut out of range (0 to 32)\n");
	return 0;
    }
    SETCDB10(cdb, CMD_LOAD_LUT, 0);
    cdb[2] = nr;
    getdata(fd, NULL, 0, cdb);

    return 1;
}


loadlut(fd, red, green, blue)
    int fd; u_char *red, *green, *blue;
{
    u_char cdb[10], data[3*256];
    int i;

    for(i=0; i<256; i++){
	data[i] =	*red++;
	data[i+256] =	*green++;
	data[i+512] =	*blue++;
    }
    SETCDB10(cdb, CMD_LOAD_LUT, sizeof(data));
    cdb[2] = 0x27;
    putdata(fd, data, sizeof(data), cdb);
    return 1;
}


setbacktrack(fd, on)
    int fd, on;
{
    u_char cdb[6];
    if(on) {
	SETCDB(cdb, CMD_BACKTRACK, 0x82);
    } else {
	SETCDB(cdb, CMD_BACKTRACK, 0x80);
    }
    getdata(fd, NULL, 0, cdb);
    return 1;
}


stopscan(fd)
    int fd;
{
    u_char cdb[6];
    SETCDB(cdb, CMD_START_STOP, 0);
    getdata(fd, NULL, 0, cdb);
    return 1;
}


startscan(fd, filter, multibit, dpi)
    int fd; u_int filter, multibit, dpi;
{
    u_char cdb[6], flags;
    if(filter > 3) {
	fprintf(stderr,"pnmscan: unknown filter\n");
	return 0;
    }
    
    flags = (filter << 3) | BIT0 |
	(dpi >= 330 ? BIT7 : NOBIT) | (multibit ? BIT6 : NOBIT);
    SETCDB(cdb, CMD_START_STOP, flags);
    getdata(fd, NULL, 0, cdb);
    return 1;
}


struct image_status *getistatus(fd)
    int fd; 
{
    static struct image_status istat;
    u_char cdb[6], data[6];

    SETCDB(cdb, CMD_STATUS, sizeof(data));
    getdata(fd, data, sizeof(data), cdb);

    istat.busy =	data[0];
    istat.rowbytes =	((u_int) data[2] << 8 ) | (u_int) data[1];
    istat.scanlines =	((u_int) data[4] << 8 ) | (u_int) data[3];

    return &istat;
}


int readlines(fd, buffer, len, lines)
    int fd, lines, len;
    u_char *buffer;
{
    u_char cdb[6];

    SETCDB(cdb, CMD_READ, lines);
    getdata(fd, buffer, len, cdb);
    return 1;
}


/************************* System dependent functions *********************
 *
 *  int getdata(fd, buffer, len, cdb)
 *  int putdata(fd, buffer, len, cdb)
 *  int int waitready(fd, maxsec)
 */

/****************************** BSD/OS ***********************************/

#if defined(__bsdi__)
#include "/sys/dev/scsi/scsi_ioctl.h"
#include "/sys/dev/scsi/scsi.h"
#include "/sys/dev/scsi/us.h"
#include <sys/ioctl.h>
static int pid;


int getdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    struct scsi_cdb *scdb = (struct scsi_cdb *) cdb;
    int retlen;
    u_char c[1];
    
    if(!len || !buffer) {
	len =1;
	buffer = c;
    }

    if(!pid){
	pid = getpid();
	if(-1 ==  ioctl(fd, USIOCSFORMAT, &pid) ){
	   perror("scsi format pid failed:");
	   return -1;
       }
    }
    if(-1 ==  ioctl(fd, USIOCSCSICOMMAND, scdb) ||
       -1 == (retlen = read(fd, buffer,len))) {
	perror("getdata failed:");
	return -1;
    }
    return retlen;

}


int putdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    struct scsi_cdb *scdb = (struct scsi_cdb *) cdb;
    int retlen;
    u_char c[1];
    
    if(!len || !buffer) {
	len =1;
	buffer = c;
    }

    if(!pid){
	pid = getpid();
	if(-1 == ioctl(fd, USIOCSFORMAT, &pid)){
	   perror("scsi format pid failed:");
	   return -1;
       }
    }
    if(-1 ==  ioctl(fd, USIOCSCSICOMMAND, scdb) ||
       -1 == (retlen = write(fd, buffer, len))) {
	perror("setdata failed:");
	return -1;
    }
    return retlen;
}


int waitready(fd, maxsec)
    int fd, maxsec;
{
    int ready,i;

    for(i=0; i< maxsec; i++){
	if(-1 ==  ioctl(fd, USIOCTESTREADY, &ready)) {
	    perror("test unit ready failed");
	    return 0;
	}
	if(ready) return 1;
	sleep(1);
    }
    return 0;
}
#endif

/******************************* linux ************************************/

/*  u_char cdb[6];
    readlines(fd, linbuf, rdlen, rdlines);

    SETCDB(cdb, CMD_READ, lines);
    getdata(fd, buffer, len, cdb);
*/
#ifdef linux

#include </usr/src/linux/include/scsi/sg.h>

/* define this: */
#define SCSI_IOCTL_TEST_UNIT_READY 2

/* or include this:
   #define __KERNEL__ 1
   #include </usr/src/linux/drivers/block/blk.h>
   #include </usr/src/linux/drivers/scsi/scsi.h>
   #include </usr/src/linux/drivers/scsi/scsi_ioctl.h>

   and compile with -I /usr/src/linux/include
*/


int getdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    int intb[(sizeof(struct sg_header) +READBUF + sizeof(int) -1)
	     / sizeof(int)];
    u_char *scsibuf = (u_char *) intb;
    struct sg_header *sghp = (struct sg_header *) intb;
    int retlen;
    int cdblen = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);

    sghp->pack_len  = cdblen + sizeof(*sghp);
    sghp->reply_len = len + sizeof(*sghp);
    
    memcpy(scsibuf+sizeof(*sghp), cdb, cdblen);
    
    if (write(fd, scsibuf, sghp->pack_len) != sghp->pack_len) {
	fprintf(stderr, "getdata - write failed: %s\n", strerror (errno));
	return -1;
    }
    
    if ((retlen = read (fd, scsibuf,  sghp->reply_len)) == -1) {
	fprintf(stderr, "getdata - read failed: %s\n", strerror (errno));
	return -1;
    }

    if(sghp->result != 0) {
	fprintf(stderr," SCSI cmd 0x%x filed -- error 0x%x\n", cdb[0],
		sghp->result);
	return -1;
    }
    retlen -= sizeof(*sghp);
    memcpy(buffer, scsibuf+sizeof(*sghp), retlen);

    return retlen;
}


int putdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    int intb[(sizeof(struct sg_header) +READBUF + sizeof(int) -1)
	     / sizeof(int)];
    u_char *scsibuf = (u_char *) intb;
    struct sg_header *sghp = (struct sg_header *) intb;
    int retlen;
    int cdblen = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);

    sghp->pack_len  = cdblen + sizeof(*sghp) + len;
    sghp->reply_len = sizeof(*sghp);
    
    memcpy(scsibuf+sizeof(*sghp), cdb, cdblen);
    memcpy(scsibuf+sizeof(*sghp) + cdblen, buffer, len);
    
    if ((retlen = write(fd, scsibuf, sghp->pack_len)) != sghp->pack_len) {
	fprintf(stderr, "putdata - write failed: %s\n", strerror (errno));
	return -1;
    }
    
    if (read (fd, scsibuf,  sghp->reply_len) == -1) {
	fprintf(stderr, "putdata - read failed: %s\n", strerror (errno));
	return -1;
    }

    if(sghp->result != 0) {
	fprintf(stderr," SCSI cmd 0x%x filed -- error 0x%x\n", cdb[0],
		sghp->result);
	return -1;
    }
    retlen -= sizeof(*sghp);

    return retlen;
}


/* is there no other way to test if unit is ready....? */
int waitready(fd, maxsec)
    int fd, maxsec;
{
    int busy = 0,i, dummy;
    for(i=0; i< maxsec; i++){
	/* liunx special?!? --- if not ready ioctl returns != 0 */
	busy = ioctl(fd, SCSI_IOCTL_TEST_UNIT_READY, &dummy);
	if(!busy) return 1;
	sleep(1);
    }

    return 0;
}

#endif
/******************************* SunOS ************************************/

#ifdef sun
#if     defined(svr4) || defined(__svr4__)
#define SVR4    1
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <malloc.h>
#include <errno.h>
#include <math.h>
#if     SVR4
#  include <sys/scsi/impl/uscsi.h>
#  include <sys/scsi/targets/sddef.h>
#  include <sys/cdio.h>
#  include <sys/scsi/generic/commands.h>
#else
#  include <sys/buf.h>
#  include <sun/dkio.h>
#  include <scsi/impl/uscsi.h>
#  include <scsi/targets/srdef.h>
#  include <scsi/generic/commands.h>
#endif  /* ! SVR4 */
#if     SVR4
#define SET_USCSI_TIMEOUT(sc, to)       ((sc)->uscsi_timeout = (to))
#else
#define SET_USCSI_TIMEOUT(sc, to)       (to)
#endif


int getdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    struct uscsi_cmd sc;
    int retlen;

    memset((void*)(&sc), 0, sizeof(sc));
    sc.uscsi_cdb = (caddr_t)cdb;
    sc.uscsi_cdblen = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);
    sc.uscsi_bufaddr = (char *)buffer;
    sc.uscsi_buflen = len;
    sc.uscsi_flags = (len ? USCSI_READ : 0);
    if (ioctl(fd, USCSICMD, &sc) == -1) {
	perror("USCSICMD get data failed");
                return -1;
    }
    if (sc.uscsi_status) {
	fprintf(stderr, "bad status: USCSICMD get data\n");
	return -1;
    }
    return len;

}


int putdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
    struct uscsi_cmd sc;
    int retlen;

    memset((void*)(&sc), 0, sizeof(sc));
    sc.uscsi_cdb = (caddr_t)cdb;
    sc.uscsi_cdblen = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);
    sc.uscsi_bufaddr = (char *)buffer;
    sc.uscsi_buflen = len;
    sc.uscsi_flags = (len ? USCSI_WRITE : 0);
    if (ioctl(fd, USCSICMD, &sc) == -1) {
	perror("USCSICMD send data failed");
                return -1;
    }
    if (sc.uscsi_status) {
	fprintf(stderr, "bad status: USCSICMD send data\n");
	return -1;
    }
    return len;
}


int waitready(fd, maxsec)
    int fd, maxsec;
{
    int ready,i;
    static u_char cdb[6];
    struct uscsi_cmd sc;
    int retlen;

    for(i=0; i< maxsec; i++){
	memset((void*)(&sc), 0, sizeof(sc));
	sc.uscsi_cdb = (caddr_t)cdb;
	sc.uscsi_cdblen = 6;
	sc.uscsi_flags = USCSI_SILENT;
	if (ioctl(fd, USCSICMD, &sc) == -1) {
	    perror("test unit ready failed");
	    return -1;
	}
	if (!sc.uscsi_status) return 1;
	sleep(1);
    }
    return 0;
}

#endif
/***************************************************************************/
#ifdef VMS
static int waitready(fd, maxsec)
    int fd, maxsec;
{
        static char cdb[6],buffer[10];
        cdb[0] = 0x00;
        cdb[1] = 0;
        cdb[2] = 0;
        cdb[3] = 0;
        cdb[4] = 0;
        cdb[5] = 0;

        gk_desc.SCSI$L_FLAGS = SCSI$K_READ;
        gk_desc.SCSI$A_CMD_ADDR = &cdb[0];   /* ->SCSI command buffer */
        gk_desc.SCSI$L_CMD_LEN = 6;             /* SCSI command length, bytes */
        gk_desc.SCSI$A_DATA_ADDR = (char *)buffer;       /* ->SCSI data buffer */
        gk_desc.SCSI$L_DATA_LEN = 0;            /* SCSI data length, bytes */
        gk_desc.SCSI$L_PH_CH_TMOUT = 0xffff;      /* SCSI phase change timeout, sec */
        gk_desc.SCSI$L_DISCON_TMOUT = 0xffff;     /* SCSI disconnect timeout, sec */

	if (verbose) printf ("Wait until drive becomes ready ... \n");
        do {
                status = sys$qiow ( GK_EFN, gk_chan, IO$_DIAGNOSE,
                                &gk_iosb, 0, 0,
                                &gk_desc, sizeof(gk_desc), 0, 0, 0, 0);

/* Check the various returned status values */

/*
                if (!(status & 1)) lib$stop (status);
                if (!(gk_iosb.SCSI$W_VMS_STAT & 1))
                        lib$stop (gk_iosb.SCSI$W_VMS_STAT);
*/

        } while (gk_iosb.SCSI$B_IOSB_STS != 0);

        if (verbose) printf ("Drive is ready now!\n");
    return 0;
}
int getdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
        gk_desc.SCSI$L_FLAGS = SCSI$K_READ;
        gk_desc.SCSI$A_DATA_ADDR = (char *)buffer;
        gk_desc.SCSI$L_DATA_LEN = len;
        gk_desc.SCSI$A_CMD_ADDR = (caddr_t)cdb;
        gk_desc.SCSI$L_CMD_LEN = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);
        gk_desc.SCSI$L_PH_CH_TMOUT = 0xffff;
        gk_desc.SCSI$L_DISCON_TMOUT = 0xffff;
        if (verbose)
        {
        printf("***********************************************************\n");
        printf("SCSI getdata parameters\n");
        printf("OPCODE: %d",gk_desc.SCSI$L_OPCODE);
        printf(" FLAGS: %d\n",gk_desc.SCSI$L_FLAGS);
        printf("CMD:");
        for (i = 0; i < gk_desc.SCSI$L_CMD_LEN; i++)
        {
        printf("%d ",cdb[i]);
        }
        printf("\n");
        printf("DATA_LEN: %d\n",gk_desc.SCSI$L_DATA_LEN);
        printf("PH_CH_TMOUT: %d",gk_desc.SCSI$L_PH_CH_TMOUT);
        printf(" DISCON_TMOUT: %d\n",gk_desc.SCSI$L_DISCON_TMOUT);
        }
        status = sys$qiow ( GK_EFN, gk_chan, IO$_DIAGNOSE, &gk_iosb, 0, 0,
                        &gk_desc, sizeof(gk_desc), 0, 0, 0, 0);

        if (!(status & 1)) lib$stop(status);

        if (!(gk_iosb.SCSI$W_VMS_STAT & 1))
                lib$stop(gk_iosb.SCSI$W_VMS_STAT);

/* Yes, was SCSI Status OK from QIO? */

        if (gk_iosb.SCSI$B_IOSB_STS != SCSI$K_GOOD_STATUS) {
                printf ("Bad SCSI status returned: %02.2x\n",
                        gk_iosb.SCSI$B_IOSB_STS);
                abort();
        }
/* The command succeeded. Display the SCSI data returned from the target */

        len = gk_iosb.SCSI$L_IOSB_TFR_CNT;
    return len;

}

int putdata(fd, buffer, len, cdb)
    int fd; u_char *buffer; int len; u_char *cdb;
{
        gk_desc.SCSI$L_FLAGS = SCSI$K_WRITE;
        gk_desc.SCSI$A_DATA_ADDR = (char *)buffer;
        gk_desc.SCSI$L_DATA_LEN = len;
        gk_desc.SCSI$A_CMD_ADDR = (caddr_t)cdb;
        gk_desc.SCSI$L_CMD_LEN = (cdb[0] == CMD_LOAD_LUT ? 10 : 6);
        gk_desc.SCSI$L_PH_CH_TMOUT = 0xffff;
        gk_desc.SCSI$L_DISCON_TMOUT = 0xffff;
        if (verbose)
        {
        printf("***********************************************************\n");
        printf("SCSI putdata parameters\n");
        printf("OPCODE: %d",gk_desc.SCSI$L_OPCODE);
        printf(" FLAGS: %d\n",gk_desc.SCSI$L_FLAGS);
        printf("CMD:");
        for (i = 0; i < gk_desc.SCSI$L_CMD_LEN; i++)
        {
        printf("%d ",cdb[i]);
        }
        printf("\n");
        printf("DATA_LEN: %d",gk_desc.SCSI$L_DATA_LEN);
        printf(" DATA:");
        for (i = 0; i < gk_desc.SCSI$L_DATA_LEN; i++)
        {
        printf("%d ",buffer[i]);
        }
        printf("\n");
        printf("PH_CH_TMOUT: %d",gk_desc.SCSI$L_PH_CH_TMOUT);
        printf(" DISCON_TMOUT: %d\n",gk_desc.SCSI$L_DISCON_TMOUT);
        }
        status = sys$qiow ( GK_EFN, gk_chan, IO$_DIAGNOSE, &gk_iosb, 0, 0,
                        &gk_desc, sizeof(gk_desc), 0, 0, 0, 0);

        if (!(status & 1)) lib$stop(status);

        if (!(gk_iosb.SCSI$W_VMS_STAT & 1))
                lib$stop(gk_iosb.SCSI$W_VMS_STAT);

/* Yes, was SCSI Status OK from QIO? */

        if (gk_iosb.SCSI$B_IOSB_STS != SCSI$K_GOOD_STATUS) {
                printf ("Bad SCSI status returned: %02.2x\n",
                        gk_iosb.SCSI$B_IOSB_STS);
                abort();
        }
/* The command succeeded. Display the SCSI data returned from the target */

        len = gk_iosb.SCSI$L_IOSB_TFR_CNT;
    return len;
}
#endif


/* ---- Emacs commands ----
Local Variables:
compile-command: "cc pnmscan.c -o pnmscan -lm"
End:
*/

   
main(argc, argv)
    int argc;
    char **argv;
{
    parse_options(argc,argv);

    if( !checkrange(&sm) ) {
	print_settings(&sm);
	usage(argv[0]);
	exit(1);
    }

    if(verbose) print_settings(&sm);

#ifndef VMS
    if(-1 == (scanfd = open(device, O_RDWR)))
	errexit("unable to open scanner device '%s'", device);
#else
        status = sys$assign ( &gk_device_desc, &gk_chan, 0, 0);
        if (!(status & 1)) {
                printf ("Unable to assign channel to %s", &gk_device[0]);
                sys$exit (status);
        }

        gk_desc.SCSI$L_OPCODE = 1;      /* SCSI Operation Code */
        gk_desc.SCSI$L_PAD_LEN = 0;     /* SCSI pad length, bytes */
        gk_desc.SCSI$L_RES_1 = 0;       /* Reserved */
        gk_desc.SCSI$L_RES_2 = 0;       /* Reserved */
        gk_desc.SCSI$L_RES_3 = 0;       /* Reserved */
        gk_desc.SCSI$L_RES_4 = 0;       /* Reserved */
        gk_desc.SCSI$L_RES_5 = 0;       /* Reserved */
        gk_desc.SCSI$L_RES_6 = 0;       /* Reserved */
#endif
  
    if(reset) stopscan(scanfd), exit(0);
    if( !checkscanner(scanfd) ) {
	fprintf(stderr, "device '%s' is not of type scanner\n", device);
	exit(1);
    }
    setsignals();
    scan(scanfd, &sm);

    exit(0);
}
