/* ------------------------------------------------------------------------------------
 *
 *      User-level SCSI Nikon Coolscan driver 
 *	Written by Jochen Karrer, cip307@cip.physik.uni-wuerzburg.de 
 * 	
 *	
 *	Parts stolen from the HP-Scanjet driver hpbscanpbm from
 *	David Etherton, etherton@netcom.com
 *
 *	Thanks to Heiko Eissfeldt (heiko@colossus.escape.de)
 *	who answered my questions about the generic SCSI-device.
 *
 *      Copyright (C) 1995, Jochen Karrer	
 *      Released under the terms of the GPL.
 *      *NO WARRANTY*
 *
 *
 *	THIS IS ALPHA SOFTWARE.  IT WORKS FINE FOR ME.
 *	IT MIGHT NOT WORK FOR YOU.  
 *	
 *	Version 0.1alpha: initial release
 *
 * -----------------------------------------------------------------------------------
 */

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

#include "/usr/src/linux/drivers/scsi/sg.h"
#include "coolscan.h"

int fd;
int quiet = 0;
int debug = 0;

/*
 * ----------------------------------------------------------------------
 *
 *	SCSI Commands:
 *
 * ----------------------------------------------------------------------
 */

/*
 *
 * sg_xfer transfers a command to the generic SCSI-Device 
 * and fetches the result. 
 *  
 */

int sg_xfer(u_char *src,int len,u_char *dest,
                int reply_len)
{
        int count, result;
        static struct sg_header hdr;
        static unsigned char buf[SG_BIG_BUFF];
        hdr.pack_len = sizeof(hdr) + len;
        hdr.reply_len = sizeof(hdr) + reply_len;
        memcpy(buf, &hdr, sizeof(hdr));
        memcpy(buf + sizeof(hdr), src, len);
        if (write(fd, buf, hdr.pack_len) == -1) {
                perror("sg_xfer(write)");
                return -1;
        }
        if ((count = read(fd,buf,hdr.reply_len)) == -1) {
                perror("sg_xfer(read)");
                return -1;
        }
        result = ((struct sg_header*)buf)->result;
        if (result) {
                fprintf(stderr,"sg_xfer: result is %d\n",result);
        }
        if (count > sizeof(struct sg_header)) {
                count -= sizeof(struct sg_header);
                memcpy(dest,buf + sizeof(hdr),count);
        }
        else {
		/* If no datas return header */
                count = 0;
		if(dest) memcpy(dest,buf,sizeof(hdr));
	}
        return count;
}

int inquire_device(char *mfg,char *model)
{
        static unsigned char inquiry[] = { 0x12,0,0,0,0x24,0 };
        unsigned char reply[256];
        if (sg_xfer(inquiry,sizeof(inquiry),reply,sizeof(reply)) < 16)
                return -1;
        memcpy(mfg,reply + 8, 8);
        memcpy(model,reply + 16, 16);
        return reply[0];
}

/*
 *
 *	The coolscan understands only the 10 Byte read and write commands
 *	and discriminates between different types of data, which
 *	are coded into a byte normaly used for the LBA 
 *
 */

int read_data_10(unsigned char type,unsigned char *dest,int len)
{
        u_char read_10[10] = { 0x28,0,type,0,0,0,0,len >> 8,len,0 };
        if ((len=sg_xfer(read_10,10,dest,len)) < 0)
                return -1;
	return len;
}
int write_data_10(char *data,int len)
{	
	static u_char write_10[256] = { 0x2a,0,0,0,0,0,0,0,0,0}; 
	write_10[7] = len >> 8; write_10[8] = len;
        memcpy(write_10 + 10, data, len); 
        if (sg_xfer(write_10,10 + len,0,0) < 0)
                return -1;
        return 0;
}
int test_unit_ready( ){
	static u_char test_unit_ready[6] = { 0,0,0,0,0,0 };
	struct sg_header dest;
	int i,count;
	if((count = sg_xfer(test_unit_ready,6,(char *)&dest,0 ))<0)
		return -1;
	printf("ADD_SENSECODE 0x%x\n",dest.sense_buffer[ADD_SENSECODE] );
	printf("ADD_SC_QUALIFIER 0x%x\n",dest.sense_buffer[ADD_SC_QUALIFIER] );
	for(i=0;i<15;i++)printf("0x%x ",dest.sense_buffer[i]);
	printf("\ncount %d\n",count);
	printf("reply_len %d,result %d\n",dest.reply_len,dest.result);
	return (int) dest.sense_buffer[0];
}
void write_string(unsigned char *format,...)
{
        va_list args;
        unsigned char buf[256];
        va_start(args, format);
        vsprintf(buf,format,args);
        write_data_10(buf,strlen(buf));
}
int is_coolscan(char *devname){
	
	fd = open(devname, O_RDWR);

        if (fd >= 0 ) {	
		int type;
                char mfr[9], model[17];

		memset(mfr,0,9); memset(model,0,17);
                type = inquire_device((char *)mfr,model);
		if(!quiet){
                	fprintf(stderr,"Model:%s\nVendor:%s\n",model,mfr);
		}
                if (type == 6 && !strncmp(mfr,"Nikon",5))
                        return 1;
                close(fd);
        }
        return -1;
	
}

int find_coolscan(void)
{
        unsigned char buf[16];
        int i;
        for (i='a'; i <= 'z'; i++) {
                sprintf(buf,"/dev/sg%c",i);
                if ( is_coolscan(buf) != -1) {
                        if (!quiet)
                                fprintf(stderr,
                                        "Coolscan found on '%s'\n",buf);
                        return 1;
                }
        }
        return -1;
}

/*
 *
 *	COOLSCAN commands :
 *
 */

void wait_for_ack(int timeout){

	unsigned char dest[256];
	dest[0]=0;
	while( (dest[0]!=6) && (timeout-- !=0))
		read_data_10(1,dest,10);
	if(timeout == 0)fprintf(stderr,"timeout waiting for acknowledge\n");  
	
}

/*
 *
 *	Get data block a block of data by first reading the header
 *  	and then the datas. 
 *
 */

int get_data_block(int type,unsigned char *buf,int buflen){
        int len,i;

        /* read headerdata */
        if ((i = read_data_10(type,buf,4)) != 4) {
                fprintf(stderr, "Got %d instead of 4 bytes\n", i);
                return -1;
        }
        len = (buf[1]<<8) + buf[2];

        /* check header data */
        if(buf[0]!=2){
                fprintf(stderr, "wrong start code 0x%x in data block, len%d\n",buf[0],len);
                for(i=0;i<20;i++) fprintf(stderr, " %x",buf[i]);
                fprintf(stderr, "\n");
                return -1;
        }

        if(len > buflen) {
                fprintf(stderr, "buffer too small. should be %d bytes big\n",len);
                return -1;
        }


        /* read line data */
        if (len != (i = read_data_10(type,buf,len))) {
                fprintf(stderr, "read %d instead of %d\n",i,len);
                return -1;
        }

        /* check line data */
        if ( buf[len-1] == 0x03) {
                /* final message detected */
                return len-1;
        }

        /* check, if there is an end-of-line-mark */
        if (buf[len-1] == 0x17 ) {
                /* complete line detected */
                return len - 1;  /* return # bytes without end-of-line-mark */
        } else {
                fprintf(stderr, "wrong end code %x in data block with len %d \n",buf[len-1],len);
                for(i=0;i<20;i++)fprintf(stderr, " %x",buf[i]);
                fprintf(stderr, "\n");
        }
        return -1;
}

/*
 *	Sets the film type, and the output data format. The manual
 *	doesn't say that I have to wait for acknowledge, but if I
 *	do not, the following command sometimes will not be executed.
 *
 */

void set_film_type(int type){
	switch(type){
		case COLOR_POSITIVE:
			write_string("CP\015\012");
			break;

		case COLOR_NEGATIVE:
			write_string("CN\015\012");
			break;

		case MONO_POSITIVE:
			write_string("MP\015\012");
			break;

		case MONO_NEGATIVE:
			write_string("MN\015\012");
			break;

		default:
			fprintf(stderr,"illegal film type %d\n",type);

	}
	wait_for_ack(10);  
}

/*
 *	Does a prescan. Some pixels from the image are read for determining
 * 	optimal settings. 
 *      The manual says, this cancels the commands
 *	BRT (brightness) CNT (contrast) and SFT (Shift of the lockup table.)
 * 	BRT means setting the value to zero.
 *	The exposure time correction will be set to 50.
 * 	The manual says prescan changes the exposure Volume. When read
 *	the internal datas of the scanner with the rcnd command
 *	after prescanning none of the values seem to depend
 *	from the dia I inserted. So may be it is not possible
 *	to get the result of the operation.
 *	prescan results are canceled by changing the film_type
 * 	between negative and positive. and bei the APIN, INM and INT
 *	command. 
 *
 */

void prescan(){
	write_string("PR,R\015\012"); 
	wait_for_ack(10);
}

/*
 *	The normal setting for the exposure time is 50. The manual
 *	says that the exposure time will be proportional to this
 *	factor.
 *
 */

int exp_time(int volume){
	if((volume>100)||(volume<25))return 0;
	write_string("AP%d\015\012",volume);
	return 1;
}

/*
 *
 *	The Coolscan has 4 color-balance register for every color.
 *	I don't know for what I should need four.
 *      The normal contents of the color balance registers is 50:50:50	
 * 	The manual says that the exposure time of the ccd chip will
 *	be multiplied with this factors.
 *	If you register a negative color balance if the scanner
 *	is in positive state, the scanner will switch to negative.
 *
 */

void register_color_balance_negative(int k,int red,int green,int blue ){
	if( (red < 25)  || (green < 25)  || (blue < 25) ) return;
	if( (red > 100) || (green > 100) || (blue > 100) ) return;
	if( (k<1) || (k>4) ) return;
	write_string("CB0,%d,%d,%d,US%d\015\12",red,green,blue,k);
}
void register_color_balance_positive(int k,int red,int green,int blue){
        if( (red < 25)  || (green < 25)  || (blue < 25) ) return;
        if( (red > 100) || (green > 100) || (blue > 100) ) return;
        if( (k<1) || (k>4) ) return;
        write_string("CB1,%d,%d,%d,US%d\015\12",red,green,blue,k);
}

void register_color_balance(int k, int red, int green, int blue,int film_type ){
	if((film_type == COLOR_POSITIVE)||(film_type == MONO_POSITIVE))
		register_color_balance_positive(k,red,green,blue );
	else	register_color_balance_negative(k,red,green,blue );
}

/*
 *	Specifys which of the four user colorbalance registers will be used.
 *	Because I need only one colorbalance at the same time, I use
 *	only the first. Be careful. The  colorbalance is also valid
 *	in monochrome mode ( By default green ), but if you select
 *	a color balance with specify_color_balance, the scanner will
 *	switch to color-mode.
 */

void specify_color_balance(int which){
	write_string("US%d\015\013",which);
}
void read_color_balance(int which){ char data[20];
	int i;
	write_string("RCB%d\015\012",which);
	get_data_block(0,data,5);
	for(i=1;i<4;i++){
		printf("colorb %d\n",data[i]);
	}
	
}

/*
 *	A data block received with get_data_block will contain
 *	first the red line, then the green line, and then the 
 *	blue line, if line_sequential_output is used.
 *
 *
 */

void line_sequential_output(){
	write_string("S_RGBL\015\012");
}

/*
 *
 *	A data block received with get_data_block will contain
 *	one byte red, one byte green and one byte blue for every pixel.
 *	this is ideal for ppm or pgm.
 *
 */

void pixel_interleave_output(){
	write_string("S_RGBD\015\012");
}

void mono_output_color(char c){
	if(c == 'r' || c == 'b' || c == 'g'){
		write_string("F%c\015\012",c);
	}
}
/* 
 *	Scans an area starting at x1,y1 
 *	with a pitch ot px,py
 *	nx*ny pixels will be scanned.
 *	The output format is ppm or pgm.
 *	the manual says that there will be a block of the len 1 and ETX (end text
 *	= 0x03 ) after the last block, but there is none.
 *	I also get one line fewer than expected, so I call this with 
 *	1 line more than required. The manual doesn't say that I have
 *	to wait for acknowledge after the BX command.
 */

void scan( char *filename,int format,int x1,int y1,int px,int py,int nx,int ny){
	static u_char data[8000];
	int lines=nx,len;
	FILE *file;
	data[2]=0;
	file=fopen(filename,"w");
	if(format == FORMAT_PPM){
		fprintf(file,"P6\n# Coolscan/Linux\n%d %d\n255\n",ny,nx);
	}
	if(format == FORMAT_PGM){
		fprintf(file,"P5\n# Coolscan/Linux\n%d %d\n255\n",ny,nx);
	}
	write_string("BX %d,%d,%d,%d,%d,%d\015\012",x1,y1,px,py,nx+1,ny);
	wait_for_ack(50); 
	while( (lines-- > 0) && (( len = get_data_block(0,data,8000))>0)  ){
		if(debug)
			fprintf(stderr,"length %d, endcode 0x%x \n",len,data[len]);
		fwrite(data,1,len,file);	

		/* May be this helps speeding up the
		   bus because there is no disconnect 
		   possible on my machine, because of an 
		   very old SCSI I Harddisk. 
		*/
		 usleep(50000);
	}
	fclose(file);
	
}
void read_line(int line,char *buf){
	int len;
	stage_move_to(line);
        write_string("LN %d,%d,%d,%d\015\012",line,0,1,MAX_Y);
        len = get_data_block(0,buf,9000);

        if(debug) fprintf(stderr,"The length %d, the endcode 0x%x \n",len,buf[len]);


}
void print_line(int line){
	static u_char buf[8000];
	int i;
	read_line( line,buf);
	for(i=0;i<MAX_Y;i+=10){
                printf("%d ",buf[i]);
        }
}
void tv_gamma(){
	write_string("GMA8\015\012");
}
void linear_gamma(){
	write_string("GML8\015\012");
}

/*
 *
 *	I don't know what "brightness" does exactly mean.    
 *	the technical manual doesn't say, but I think its something
 *	like analog gain. If somebody knows it please let me know.
 *
 */

void brightness(int red,int green,int blue) {
	write_string("BRT%+d,%+d,%+d\015\012",red,green,blue);
}

/*
 *
 *      I don't know what "contrast" does exactly mean.
 *      the technical manual doesn't say, but I think its something
 *      like analog offset. If somebody knows it please let me know.
 *
 */


void contrast(int red,int green, int blue){
	write_string("CNT%+d,%+d,%+d\015\012",red,green,blue);
}

/*	Analog Gamma Correction		*/

void agon(){
	write_string("AGON\015\012");
}
void agof(){
	write_string("AGOF\015\012");
}

/* 	Averaging between neighbour pixels	*/

void avon(){
	write_string("AVON\015\012");
}
void avof(){
	write_string("AVOF\015\012");
}

void read_rom_ver(){
	unsigned char buf[100];
	int i;
	write_string("RVER\015\012");
	/*read_data_10(0,buf,80);*/
	get_data_block(0,buf,80);
	for(i=0;i<40;i++)printf(" %x",buf[i]);
}
void test_interface(){
	static unsigned char buf[130];
	int i;
	write_string("TS1\015\012");
	printf("test interface \n");
	get_data_block(0,buf,129);
	printf("\n\ntest:");
	for(i=0;i<140;i++)printf(" %x",buf[i]);
	printf("\n");
}
int read_error(){
	unsigned char buf[255];
	int i,len;
	for(i=0;i<100;i++)buf[i]=0;
	write_string("RER\015\012");
	len = get_data_block(0,buf,10);
	if(len < 1 ) {
		fprintf(stderr,"can't read error code\n");
		return -1;
	}
	if(debug) fprintf(stderr,"\nerrorcode 0x%x\n",buf[0]);
	switch(buf[0]){
		case 0x0:
			if(debug)
				fprintf(stderr,"no error\n");
			break;
		case 0x1:
			fprintf(stderr,"command format error\n");
			break;
		case 0x2:
			fprintf(stderr,"Inadequate command parameter\n");
			break;
		case 0x3:
			fprintf(stderr,"Occurence of interface time out\n"); 
			break;
		case 0x7:
		case 0x8:
			fprintf(stderr,"Stage-related error\n");
			break;
		case 0xe:
			fprintf(stderr,"Other command than abort while scanning\n");
			break;
		case 0xf:
			fprintf(stderr,"Command from another Nikon scanner\n");
			break;
		case 0x21:
			fprintf(stderr,"Error during prescanning ( maybe the film is to dark )\n");
			break;
		case 0x24:
			fprintf(stderr,"CCD overflow\n");
			break;
		case 0x28:
			fprintf(stderr,"Internal memory error\n");
			break;
		case 0x51:
  		case 0x52:
		case 0x53:
			fprintf(stderr,"Light Source illumination error\n");
			break;
		case 0x54:
		case 0x55:
		case 0x56:
			fprintf(stderr,"Error in light source illumination\n");
			break;
		case 0x66:
			fprintf(stderr,"Time out during execution of BX command\n");	
			break;
		default:   
			fprintf(stderr,"unknown error code 0x%x\n",buf[0]);
	
	}
	return buf[0];
}

/*
 * --------------------------------------------------------------------------
 *	rcnd reads the internal settings in the scanner, like exposure time
 *	brightness and contrast ...
 * --------------------------------------------------------------------------
 */

void  rcnd(struct cs_idata_s *data){
	write_string("RCND\015\012");
	get_data_block(0,(char *) data,257);
}
void print_idata(struct cs_idata_s *d){
	fprintf(stderr,"AP-Value (exposure time) %d\n",d->apval);
	fprintf(stderr,"colorbalance r,g,b: %d,%d,%d\n",d->ucb_r,d->ucb_g,d->ucb_b);
	fprintf(stderr,"brightness r,g,d: %d,%d,%d\n",d->brt_r,d->brt_g,d->brt_b);
	fprintf(stderr,"contrast: r,g,b: %d,%d,%d\n",d->cnt_r,d->cnt_g,d->cnt_b);
	fprintf(stderr,"lut shift: r,g,b: %d,%d,%d\n",d->sft_r,d->sft_g,d->sft_b);
	fprintf(stderr,"stage pos: %d\n",d->x_address);
	fprintf(stderr,"film_type %d\n",d->film_type); 
	fprintf(stderr,"undoc80 ( light source calibration ) %d,%d,%d\n",d->undoc80[0],d->undoc80[1],d->undoc80[2]);
	fprintf(stderr,"undoc88 ( prescan callibration )     %d,%d,%d\n",d->undoc88[0],d->undoc88[1],d->undoc88[2]);
	fprintf(stderr,"undoc90 ( total exposure time )      %d,%d,%d\n",d->undoc90[0],d->undoc90[1],d->undoc90[2]);

}
void dump_idata(struct cs_idata_s *d){
	int i,j;
	for(i=0;i<16;i++){	
		for(j=0;j<16;j++){
			fprintf(stderr," %02x",((u_char *)d)[j+i*16]);
		}
		fprintf(stderr,"\n");
	}
}
/*
 *	reset actually crashes the coolcan, and I have to use the power
 *	switch. Don't ask me why 
 *
 */

void reset(){
	
	write_string("INT\015\012");
	sleep(40);
	/* wait_for_ack(10); */
}
void init_mem(){
	write_string("INM\015\012");
}

/*
 *	APIN initializes exposure time and gamma.
 * 	If I do a wait for acknowledge after the APIN  command
 *      the Scanner will crash if apin is executed the first time
 *	after power on . If I wait for acknowledge after later executions
 *	of apin this is no problem. If I don't wait at all
 *      the next command (for expample prescan) will not be executed
 */ 
	
void apin() {
	write_string("APIN\015\012");
	usleep(200000);
}
void eject(){
	write_string("EJ,R\015\012");
	wait_for_ack(10);
}
void stage_move_to(int n){
	if( (n>3887) || (n<-1) ) return ;
	write_string("MV%d,R\015\012",n);
	wait_for_ack(50);
}
void abort_scan(){
	write_string("ABT\015\012");
}
void read_empty(){
	int i;
	char data[2000];
	for(i=0;i<20;i++){
		read_data_10(0,data,2000); 
	}
}
void  init(struct cs_state_struct *s){
	strcpy(s->outfile , "picture");
	s->outputformat = FORMAT_PPM;
	s->film_type = COLOR_POSITIVE;
	s->red = 50;
	s->green = 50;
	s->blue = 50;
	s->exp_time = 50;	
	s->bright_red = s->bright_green =  s->bright_blue = 0;
	s->contrast_red = s->contrast_green = s->contrast_blue = 0;
	/*cs_state.output_color = green; */
	s->x1 = 0;
	s->x2 = MAX_X;
	s->y1 = 0;
	s->y2 = MAX_Y;
	s->px = 10;
	s->py = 10;	
	s->nx = (s->x2 - s->x1)/s->px;
	s->ny = (s->y2 - s->y1)/s->py;	
	s->gamma = LINEAR_GAMMA;
	s->analog_gamma = 1;
	s->averaging = 0;
	s->prescan_valid=0;
	s->do_prescan = 0;

}
void set_state( struct cs_state_struct *s){
	read_error();
	set_film_type(s->film_type);
	read_error();
	if( (s->film_type == COLOR_POSITIVE) || (s->film_type == MONO_POSITIVE) )
		register_color_balance_positive(1,s->red,
					  s->green,
					  s->blue );
	else	register_color_balance_negative(1,s->red,
                                          s->green,
                                          s->blue ); 
	exp_time(s->exp_time);
	if( (s->film_type == COLOR_POSITIVE) || (s->film_type == COLOR_NEGATIVE ) ){
		specify_color_balance(1); 
	}
	brightness(s->bright_red,s->bright_green,s->bright_blue);
	contrast(s->contrast_red,s->contrast_green,s->contrast_blue);



	if(s->gamma == LINEAR_GAMMA) linear_gamma();
	if(s->gamma == TV_GAMMA) tv_gamma();
	if(s->analog_gamma)agon();
	else agof();
	if(s->averaging)avon();
	else avof(); 

}
#ifndef COOLSCAN_LIB

void print_help(){

	fprintf(stderr,"valid options are:\n"
	"-cp                    color positive\n" 
	"-cn                    color negative\n"
	"-mp                    monochrom positive\n"
	"-mn                    monochrom negative\n"
	"-pr			do prescan before scan\n"
	"-time                  CCD-exposure time ( 25 - 100 )\n"
	"-redtime               CCD-exposure time correction for ren   (25 - 100)\n"
	"-greentime             CCD-exposure time correction for green (25 - 100)\n"
	"-bluetime              CCD-exposure time correction for blue  (25 - 100)\n"
	"-tv                    digital gamma correction for TV	\n"
	"-linear                no digital gamma correction\n"
	"-brt                   brightness\n"
	"-cnt                   contrast\n"
	"-agon                  analog gamma on\n" 
	"-agof                  analog gamma off\n"
	"-avon                  averaging between neigboured pixels on\n"
	"-avof                  averaging between neigboured pixels of\n"
	"-ej                    eject slide\n"
	"-x1                    start coordinate for scan (0 - 3887) \n"
	"-y1                    start coordinate for scan (0 - 2591) \n"
	"-x2                    end coordinate for scan (x1+2*px - 3888) \n"
	"-y2                    end coordinate for scan (y1+2*py - 2592) \n"
	"-px                    pitch in x-direction ( 1 - 25 )\n"
	"-py                    pitch in y-direction ( 1 - 25 )\n"
	"-o                     outfile	\n"
	"-i                     interactive mode\n"
	"-help                  help\n"
	);

}
int main(int argc,char * argv[]){
	struct cs_state_struct settings;
	init(&settings);
	if(find_coolscan()<0){
                fprintf(stderr,"no Coolscan found\n");
                exit(1);
        }
	while (--argc && **++argv=='-') {
                if (!strcmp(argv[0],"-cp") ){
                        settings.film_type = COLOR_POSITIVE;
                        if(settings.outputformat == FORMAT_PGM){
                                settings.outputformat = FORMAT_PPM;
                        }
                }

		else if (!strcmp(argv[0],"-cn") ){
			settings.film_type = COLOR_NEGATIVE;
                        if(settings.outputformat == FORMAT_PGM){
                                settings.outputformat = FORMAT_PPM;
                        }
                }
		else if (!strcmp(argv[0],"-mp") ){
                        settings.film_type = MONO_POSITIVE;
			if(settings.outputformat == FORMAT_PPM){
			 	settings.outputformat = FORMAT_PGM;
			}
		}
		else if (!strcmp(argv[0],"-mn") ){
                        settings.film_type = MONO_NEGATIVE;
                        if(settings.outputformat == FORMAT_PPM){
                                settings.outputformat = FORMAT_PGM;
                        }
                }

		else if (!strcmp(argv[0],"-pr") )
			settings.do_prescan = 1;
		else if (!strcmp(argv[0],"-time") ){
			int i = nextargi;
			if( i>=25 && i <= 100) settings.exp_time = i; 
			else {
				print_help();
				exit(1);
			}
		}
		else if (!strcmp(argv[0],"-redtime") ){
			int i = nextargi;
                        if( i>=25 && i <= 100) settings.red = i; 
                        else {
                                print_help();
                                exit(1);
                        }
		}

		else if (!strcmp(argv[0],"-greentime") ){
			int i = nextargi;
			if( i>=25 && i <= 100) settings.green = i;
                        else {
                                print_help();
                                exit(1);
                        }
		}

		else if (!strcmp(argv[0],"-bluetime") ){
			int i = nextargi;
			if( i>=25 && i <= 100) settings.blue = i;
                        else {
                                print_help();
                                exit(1);
                        }
		}
		else if (!strcmp(argv[0],"-tv") )
			settings.gamma = TV_GAMMA;
		else if (!strcmp(argv[0],"-linear") )
			settings.gamma = LINEAR_GAMMA;
		else if (!strcmp(argv[0],"-brt") ){
			int i = nextargi;
			if(i>=-5 && i <= 5 ){
				settings.bright_red = settings.bright_green =
				settings.bright_blue = i;
			}
			else  {
                                print_help();
                                exit(1);
                        }
		}
		else if (!strcmp(argv[0],"-cnt") ){
			int i = nextargi;
			if( i>= -5 && i <=5 ) {
				settings.contrast_red = settings.contrast_green =
				settings.contrast_blue = i;
			}

		}	
		else if (!strcmp(argv[0],"-o") )
                        strcpy(settings.outfile , nextargs);
		else if (!strcmp(argv[0],"-agon") )
			settings.analog_gamma = 1;
		else if (!strcmp(argv[0],"-agof") )
			settings.analog_gamma = 0;
		else if (!strcmp(argv[0],"-avon") )
			settings.averaging = 1;
		else if (!strcmp(argv[0],"-avof") )
			settings.averaging = 1;
		else if (!strcmp(argv[0],"-ej") ){
			eject(); 
			exit(0);
		}
		else if (!strcmp(argv[0],"-x1") )
			settings.x1 = nextargi;
		else if (!strcmp(argv[0],"-x2") )
			settings.x2 = nextargi;
		else if (!strcmp(argv[0],"-y1") )
			settings.y1 = nextargi;
		else if (!strcmp(argv[0],"-y2") )
			settings.y2 = nextargi;
		else if (!strcmp(argv[0],"-px") )
			settings.px = nextargi;
		else if (!strcmp(argv[0],"-py") )
			settings.py = nextargi;
		else if (!strcmp(argv[0],"-i") ){
			interactive_main(argc,argv);
			exit(0);
		}
		else if (!strcmp(argv[0],"-help") ){
		 	print_help();
			exit(0);
		}
		else {
			print_help();
			exit(0);
		}
		
	}
	settings.nx = (settings.x2 - settings.x1 ) /settings.px;
	settings.ny = (settings.y2 - settings.y1 ) /settings.py;

	read_error();
        abort_scan();
	read_empty();
        pixel_interleave_output(); 
	apin();

	/* first do prescan then set state, because prescan sets  
	 * some values to default.
         */
	if(settings.do_prescan) prescan();
	set_film_type(settings.film_type);
	set_state(&settings);
	{
		struct cs_state_struct *s;
        	s = &settings;
		scan(s->outfile,s->outputformat,s->x1,s->y1,s->px,s->py,s->nx,s->ny );
		read_empty();
	}
	exit(0);
}
#endif
