/*
 * DATlib   Copyright (c) 1995-1996 Marcus Meissner &
 *          Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *          All rights reserved.
 *
 *    Copyright (C) 1997 Hewlett-Packard Ltd.
 *    Copyright (C) 1997 Impulsphysik GmbH
 *    Copyright (C) 1997 Arne Ludwig
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Marcus Meissner
 *    at the Friedrich-Alexander Universitaet Erlangen-Nuernberg (FAU), IMMD4
 *
 * 4. The name of the University or the author may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND THE AUTHOR ``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 UNIVERSITY OR THE AUTHOR 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.
 */

/* $Id: dat.c,v 1.10 1997/08/06 14:53:48 msmeissn Exp $
 *
 * $Log: dat.c,v $
 * Revision 1.10  1997/08/06 14:53:48  msmeissn
 * more audiotape handling
 *
 * Revision 1.9  1997/06/03 18:39:59  msmeissn
 * "audio" command
 * some dat sta - audio fixes
 *
 * Revision 1.8  1997/06/02 18:45:36  msmeissn
 * a huge heap of additions by Arne Ludwig
 * - more HP support including reading of eeproms, more logpages etc.
 * - SCO and non BSDish MT ioctl support
 *
 * Revision 1.7  1997/05/05 15:30:49  msmeissn
 * *** empty log message ***
 *
 * Revision 1.6  1997/05/05 08:56:37  msmeissn
 * added drive cleaning
 *
 * Revision 1.5  1997/05/02 18:13:20  msmeissn
 * wech
 *
 * Revision 1.4  1997/04/30 14:11:51  msmeissn
 * fixed partition handling i hope
 *
 * Revision 1.3  1997/04/24 10:55:45  msmeissn
 * *** empty log message ***
 *
 * Revision 1.2  1997/04/08 14:29:35  msmeissn
 * include versioning
 *
 * Revision 1.1  1997/03/16 20:27:25  msmeissn
 * Initial revision
 *
 *
 * dat.c: Like the mt(1) command, but extended for DDS functions
 */

/*
 * TODO:
 * 	errormessages nur bei -d
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/errno.h>
extern int errno;
#include <sys/types.h>
#include <sys/fcntl.h>

#ifdef sun
#define DAT_SUN
#endif

#if defined(sco) || defined(M_UNIX)
#define DAT_SCO
#endif

#ifdef linux
#include <sys/mtio.h>
#define DAT_BSDISH
#define HAS_OS
#endif

#ifdef DAT_SUN
#include <sys/mtio.h>
#define       HAS_OS
#define       DAT_BSDISH
#endif

#ifdef DAT_SCO
#include <sys/tape.h>
#define       HAS_OS

#define       MTFSF           MT_RFM
#define       MTBSF           -1
#define       MTWEOF          -1
#define       MTBSR           -1
#define       MTFSR           -1
#define       MTEOM           MT_EOD
#define       MTREW           MT_REWIND
#define       MTOFFL          -1
#define       MTRETEN         MT_RETEN
#define       MTERASE         MT_ERASE
#endif

#ifdef DAT_BSD
#include <sys/ioctl.h>
#include <sys/mtio.h>
#define       HAS_OS

#define	MTRETEN		MTRETENS
#define	MTEOM		MTEOD
#define	DAT_BSDISH
#endif
 
#ifndef HAS_OS
#error need_operating_system_defined
#endif

#include "datlib.h"
#include "mt_ext.h"

#ifdef sun
int open(char *,int,...);
#endif

char	*tape;
enum	tapesel { TAPE_DEFAULT,TAPE_ENVIRONMENT,TAPE_CMDLINE } tapesel;

#define CMDPROTO int fd,int argc,char **argv
char	**xargv;
int	notape_inserted = 0;

struct log_entry {
	unsigned	long	code;
	char			*name;
} logpage2[]={
	{0x0002,"Total rewrites"},
	{0x0003,"Total (write) errors corrected"},
	{0x0004,"Total frames rewritten after RAW check"},
	{0x0005,"Total groups processed (written)"},
	{0x0006,"Total uncorrectable errors"},
	{0x8007,"Rewrites since last read-type operation"},
	{0x0000,NULL}
},logpage3[]={
	{0x0002,"Total rereads"},
	{0x0003,"Total (read) errors corrected"},
	{0x0004,"Total correctable ECC C3 errors"},
	{0x0005,"Total groups processed (read)"},
	{0x0006,"Total uncorrectable (read) errors"},
	{0x8007,"Rereads since last write-type operation"},
	{0x0000,NULL}
/* altough 30 and 31 are said to be vendor specific, they are the same 
 * for HP/Archive/SONY. Go figure.
 */
},logpage30[]={
	{0x0001,"Current Groups Written"},
	{0x0002,"Current RAW retries (write)"},
	{0x0003,"Current Groups Read"},
	{0x0004,"Current ECC C3 retries (read)"},
	{0x0005,"Previous Groups Written"},
	{0x0006,"Previous RAW retries (write)"},
	{0x0007,"Previous Groups Read"},
	{0x0008,"Previous ECC C3 retries (read)"},
	{0x0009,"Total Groups Written"},
	{0x000A,"Total RAW retries (write)"},
	{0x000B,"Total Groups Read"},
	{0x000C,"Total ECC C3 retries (read)"},
	{0x000D,"Load Count"},
	{0x0000,NULL}
},logpage31[]={
	{0x0001,"Remaining Capacity Partition 0"},
	{0x0002,"Remaining Capacity Partition 1"},
	{0x0003,"Maximum Capacity Partition 0"},
	{0x0004,"Maximum Capacity Partition 1"},
	/* not available the doc says. Hah */
	{0x0000,NULL}
},emptylogpage[]={
	{0x0000,NULL}
/*
 * TapeAlert(TM) Log Page
 */
},logpage_hp_2e[]={
	{0x0000,NULL}
/*
 * Buffer Trace Log
 */
},logpage_hp_32[]={
	{0x0000,NULL}
/*
 * Device Trace Log
 */
},logpage_hp_33[]={
	{0x0000,NULL}

/*
 * Write Frames Error Counters
 */
},logpage_hp_34[]={
      {0x0001,"Frames Read or Written"},
      {0x0002,"Main Data C1 Block Errors: positive tracks"},
      {0x0003,"Main Data C1 Block Errors: negative tracks"},
      {0x0004,"Sub Area 0 C1 Block Errors: positive tracks"},
      {0x0005,"Sub Area 1 C1 Block Errors: positive tracks"},
      {0x0006,"Sub Area 0 C1 Block Errors: negative tracks"},
      {0x0007,"Sub Area 1 C1 Block Errors: negative tracks"},
      {0x0000,NULL}

/*
 * Read Frames Error Counters
 */
},logpage_hp_35[]={
      {0x0001,"Frames Read or Written"},
      {0x0002,"Main Data C1 Block Errors: positive tracks"},
      {0x0003,"Main Data C1 Block Errors: negative tracks"},
      {0x0004,"Sub Area 0 C1 Block Errors: positive tracks"},
      {0x0005,"Sub Area 1 C1 Block Errors: positive tracks"},
      {0x0006,"Sub Area 0 C1 Block Errors: negative tracks"},
      {0x0007,"Sub Area 1 C1 Block Errors: negative tracks"},
      {0x0008,"Total Retry Count"},
      {0x0009,"Read C2 Uncorrectable block"},
      {0x0000,NULL}

/*
 * Bad Group Log
 */
},logpage_hp_36[]={
      {0x0000,NULL}
/*
 * Drive Counters
 */
},logpage_hp_37[]={
	{0x0001,"Loads"},
	{0x0002,"Write Drive Errors"},
	{0x0003,"Read Drive Errors"},
	{0x0000,NULL}
/*
 * Mechanism Counters
 */
},logpage_hp_38[]={
	{0x0001,"FAULTY 12V"},
	{0x0002,"HIGH HUMIDITY"},
	{0x0003,"MODE SENSOR FAULT"},
	{0x0004,"TENSION TOO LOW"},
	{0x0005,"BAD DIAMETER"},
	{0x0006,"CAPSTAN STALLED"},
	{0x0007,"DRUM FG STUCK / Drum PG calib failed"},
	{0x0008,"DRUM STALLED"},
	{0x0009,"DRUM HAS LOST LOCK"},
	{0x000a,"DRUM PG LOST"},
	{0x000b,"TENSION TOO HIGH"},
	{0x000c,"MODE EXPECTED LURKING"},
	{0x000d,"MODE TIMEOUT"},
	{0x000e,"CAPSTAN STOP TIMEOUT"},
	{0x000f,"REELS STOP TIMEOUT"},
	{0x0010,"Supply ReeL STUCK THREADING"},
	{0x0011,"Supply ReeL STUCK CAPSTAN MODE"},
	{0x0012,"CAPSTAN CLEAN SLIP"},
	{0x0013,"Takeup ReeL STUCK CAPSTAN MODE"},
	{0x0014,"RLS STUCK RL MODE"},
	{0x0015,"RLS SPINNING THREADING"},
	{0x0016,"MOTION STOP TIMEOUT"},
	{0x0017,"RLS STUCK THREADING / Zombi error"},
	{0x0018,"Supply ReeL STUCK DURING MOTION"},
	{0x0019,"ROM CHECK FAIL"},
	{0x001a,"Supply ReeL SPIN DURING MOTION"},
	{0x001b,"Takeup ReeL STUCK DURING MOTION"},
	{0x001c,"Takeup ReeL SPIN DURING MOTION"},
	{0x001d,"DOWNLOAD INCOMPATIBLE"},
	{0x001e,"SERVO BUSY"},
	{0x001f,"SERVO HUNG"},
	{0x0000,NULL}
/*
 * Data Compression Log
 */
},logpage_hp_39[]={
#ifdef MARCUS
	{0x0001,"Number of entities written"},
	{0x0002,"Number of entities decompressed"},
	{0x0003,"Number of unsupported entities encountered"},
	{0x0004,"Compression ratio (BCD)"},
#else
	{0x0001,"Number of entities written"},
	{0x0002,"Number of entities read"},
	{0x0003,"Number of records written"},
	{0x0004,"Number of records read"},
	{0x0005,"Kilobytes to data compression"},
	{0x0006,"Kilobytes from data compression"},
	{0x0007,"Kilobytes to tape"},
	{0x0008,"Kilobytes from tape"},
	{0x0009,"Logical size of last entity"},
	{0x000A,"Physical size of last entity"},
	{0x000B,"Uncompressed entities encountered"},
#endif
	{0x0000,NULL}
/*
 * Data Compression Trace Log
 */
},logpage_hp_3a[]={
	{0x0000,NULL}
/*
 * Channel Trace Log
 */
},logpage_hp_3b[]={
      {0x0000,NULL}
/*
 * Read Retry Log Page
 */
},logpage_hp_3e[]={
      {0x0000,NULL}
},logpage_sony_33[]={
	{0x0001,"Drum revolution minute"},
	{0x0002,"Load count"},
	{0x0003,"Thread count"},
	{0x0004,"Mechanism motion count (rotary encoder)"},
	{0x0005,"Cleaning interval (minute)"},
	{0x0006,"EEPROM written count"},
	{0x0007,"MCD serial number"},
	{0x0008,"PCB serial number"},
	{0x0009,"Driver serial number"},
	{0x0000,NULL}
},logpage_sony_39[]={
	{0x0001,"Number of entities written"},
	{0x0002,"Number of entities read"},
	{0x0003,"Number of records written"},
	{0x0004,"Number of records read"},
	{0x0005,"Kilobytes to data compression"},
	{0x0006,"Kilobytes from data compression"},
	{0x0007,"Kilobytes to tape"},
	{0x0008,"Kilobytes from tape"},
	{0x0009,"Logical entity size"},
	{0x000A,"Physical entity size"},
	{0x000B,"Uncompressed entities"},
	{0x0000,NULL}
};

struct log_sense_table {
	int	nr;
	char	*name;
	struct	log_entry *log_entries;
} ls_table[] = {
	{0x02,"Write Error Page",logpage2},
	{0x03,"Read Error Page",logpage3},
	{0x30,"Tape Log",logpage30},
	{0x31,"Tape Capicity Log",logpage31},
	{0x00,NULL,emptylogpage}
}, ls_hp_table[] = {
	{0x2E,"TapeAlert Log Page",logpage_hp_2e},
	{0x32,"Buffer Trace Log",logpage_hp_32},
	{0x33,"Device Trace Log",logpage_hp_33},
	{0x34,"Write Frames Error Counters",logpage_hp_34},
	{0x35,"Read Frames Error Counters",logpage_hp_35},
	{0x36,"Bad Group Log",logpage_hp_36},
	{0x37,"Drive Counters",logpage_hp_37},
	{0x38,"Mechanism Counters",logpage_hp_38},
	{0x39,"Data Compression Log",logpage_hp_39},
	{0x3a,"Data Compression Trace Log",logpage_hp_3a},
	{0x3b,"Channel Trace Log",logpage_hp_3b},
	{0x3e,"Read Retry Log Page",logpage_hp_3e},
	{0x00,NULL,emptylogpage}
}, ls_archive_table[] = {
	{0x00,NULL,emptylogpage}
}, ls_sony_table[] = {
	{0x33,"Driver Usage Log Page",logpage_sony_33},
	{0x39,"Data Compression Transfer Log",logpage_sony_39},
	{0x00,NULL,emptylogpage}
};

struct log_sense_table_arch {
	char	*product;
	struct	log_sense_table	*lt;
} ls_arch_table[]={
	{"ARCHIVE",ls_archive_table},
	{"HP",ls_hp_table},
	{"SONY",ls_sony_table},
	{NULL,NULL}
};

struct {
	char	*name;
	int	rdonly;
	int	mtop;
	int	argc;
} stdcmdtable[]={
	{"fsf",	1,MTFSF,		1},
	{"bsf",	1,MTBSF,		1},
	{"eof",	0,MTWEOF,		1},
	{"weof",0,MTWEOF,		1},
	{"bsr",	1,MTBSR,		1},
	{"fsr",	1,MTFSR,		1},
#ifdef MTNBSF
	{"nbsf",1,MTNBSF,		1},
#endif
	{"eom",	1,MTEOM,		1},
	{"rew",	1,MTREW,		0},
	{"off",	1,MTOFFL,		0},
	{"ret",	1,MTRETEN,		0},
	{"era",	0,MTERASE,		0},
	{NULL,	0,0,			0}
};

int do_request_sense(CMDPROTO);
int do_status(CMDPROTO);
int do_asf(CMDPROTO);
int do_inquire(CMDPROTO);
int do_read_eeprom(CMDPROTO);
int do_audio(CMDPROTO);
int do_create_partition(CMDPROTO);
int do_delete_partition(CMDPROTO);
int do_scsi_reset(CMDPROTO);
int do_compression(CMDPROTO);
int do_decompression(CMDPROTO);
int do_select_partition(CMDPROTO);
int usage(CMDPROTO);

/*
 * The freebsd user scsi ioctl doesn't work if the device is opened
 * for read only, so we have to permit writing always. :-(
 */
#ifdef __FreeBSD__
#define	RDO	0
#else
#define	RDO	1
#endif

struct {
	char	*name;
	int	(*fun)(CMDPROTO);
	int	rdonly;
} cmdtable[]={
	{"asf",do_asf,RDO,},
	{"st",do_status,RDO,},
	{"req",do_request_sense,RDO,},
	{"inq",do_inquire,RDO,},
	{"eeprom",do_read_eeprom,RDO,},
	{"audio",do_audio,0,},
	{"crt",do_create_partition,0,},
	{"del",do_delete_partition,0,},
	{"swi",do_select_partition,RDO,},
	{"sel",do_select_partition,RDO,},
	{"res",do_scsi_reset,RDO,},
	{"comp",do_compression,0,},
	{"decomp",do_decompression,0,},
	{"help",usage,0},
	{NULL,NULL,0,},
};

int
do_audio(CMDPROTO) {
	int	dens;

	if (argc<2) {
		dens=datlib_get_density(fd);
		if (dens==-1) {
			fprintf(stderr,"Couldn't detect current density(%s).\n",strerror(errno));
			return 1;
		}
		printf("Drive is %sin audio mode.\n",(dens==0x80)?"":"not ");
	} else {
		if (!strcmp(argv[1],"on")) {
			dens=0x80;
		} else {
			if (!strcmp(argv[1],"off")) {
				dens=0x13;
			} else {
				fprintf(stderr,"Usage: %s audio [on|off]\n",xargv[0]);
				return 1;
			}
		}
		if (-1==datlib_set_density(fd,dens)) {
			fprintf(stderr,"Failed to set new density 0x%02x(%s)\n",
				dens,strerror(errno)
			);
			return 1;
		}
		dens=datlib_get_density(fd);
		printf("Audio %sabled\n",(dens==0x80)?"en":"dis");
	}
	return 0;
}

int
do_decompression(CMDPROTO) {
	int	dodecomp;

	if (argc<2) {
		dodecomp=datlib_query_decompression(fd);
		if (dodecomp==-1) {
			fprintf(stderr,"Couldn't detect current decompression parameters(%s).\n",strerror(errno));
			return 1;
		}
		printf("decompression %sabled (method %d)\n",dodecomp?"en":"dis",dodecomp);
	} else {
		if (!strcmp(argv[1],"on")) {
			dodecomp=1;
		} else {
			if (!strcmp(argv[1],"off")) {
				dodecomp=0;
			} else if (sscanf(argv[1],"%d",&dodecomp)) {
				/* empty */
			} else {
				fprintf(stderr,"Usage: %s decomp [on|off|<mode>]\n",xargv[0]);
				return 1;
			}
		}
		if (-1==datlib_set_decompression(fd,dodecomp)) {
			fprintf(stderr,"Failed to set new decompression.(%s)\n",
				strerror(errno)
			);
			return 1;
		}
		if (dodecomp)
			printf("decompression enabled (method %d)\n",dodecomp);
		else
			printf("decompression disabled.\n");
	}
	return 0;
}

int
do_compression(CMDPROTO) {
	int	docomp;
	char	buf[256],vendor[10];

	if (argc<2) {
		docomp=datlib_query_compression(fd);
		if (docomp==-1) {
			fprintf(stderr,"Couldn't detect current compression parameters(%s).\n",strerror(errno));
			return 1;
		}
		printf("compression %sabled (method %d).\n",docomp?"en":"dis",docomp);
	} else {
		if (!strcmp(argv[1],"on")) {
			docomp=1;
		} else {
			if (!strcmp(argv[1],"off")) {
				docomp=0;
			} else if (sscanf(argv[1],"%d",&docomp)) {
				/* empty */
			} else {
				fprintf(stderr,"Usage: %s comp [on|off|<mode>]\n",xargv[0]);
				return 1;
			}
		}
		if (-1==datlib_get_inquiry(fd,buf,255)) {
			perror("datlib_get_inquiry");
			return -1;
		}
		memcpy(vendor,buf+8,8);vendor[8]='\0';
		
		/* failed visibly? or failed silently? */
		if (	(-1==datlib_set_compression(fd,docomp)) ||
			((!!datlib_query_compression(fd))!=(!!docomp))
		) {
			if (!strncmp("HP ",vendor,3)) {
				if (	(datlib_get_density(fd)==0x00) ||
					(datlib_get_density(fd)==0x03)
				) {
					fprintf(stderr,
"This HP-DAT has been configured to allow for a change of\n"
"DCLZ-compression only through the selection of the appropriate\n"
"density. This command cannot determine which minor-device node has\n"
"been configured with or without compression, but it is recommended,\n"
"that \"/dev/rmt/<X>l\" disables compression and all other device nodes\n"
"enable compression.\n"
					);
					return 1;
				}
			}
			fprintf(stderr,"Failed to set new compression.(%s)\n",
				strerror(errno)
			);
			return 1;
		}
		if (docomp)
			printf("compression enabled (method %d)\n",docomp);
		else
			printf("compression disabled\n");
	}
	return 0;
}

int
do_scsi_reset(CMDPROTO) {
#ifdef MTIORESET
	if (-1==ioctl(fd,MTIORESET,NULL)) {
		fprintf(stderr,"%s:ioctl MTIORESET failed(%s)",xargv[0],strerror(errno));
		return -1;
	}
	return 0;
#else
	errno = EINVAL;
	return -1;
#endif
}

struct productinf {
	char	*vendor;
	char	*product;
	char	*info;
} products[] = {
	{"SONY","SDT-5000","(DDS2, 400 KByte/sec)"},
	{"SONY","SDT-7000","(DDS2, 778 KByte/sec)"},
	{"SONY","SDT-9000","(DDS3, 1150 KByte/sec)"},
	{"HP","C1533","(DDS2, 510 KByte/sec)"},
	{"HP","C1533A","(DDS2, 510 KByte/sec)"},
	{"HP","35450","(DDS1, 180 KByte/sec)"},
	{"HP","35470","(DDS1, 180 KByte/sec)"},
	{"HP","35480","(DDS1, 180 KByte/sec)"},
	{NULL,NULL,NULL}
};

int
do_request_sense(CMDPROTO) {
	char	rqbuf[256];
	int	res,i;

	res=datlib_request_sense(fd,rqbuf,255);
	if (res==-1) {
		perror("datlib_request_sense");
		return 1;
	}
	fprintf(stderr,"Sense Information:\n");
	for (i=0;i<res;i++) {
		fprintf(stderr,"%02x ",rqbuf[i]);
	}
	fprintf(stderr,"\n");

        fprintf(stderr,"Request Sense reports:\n");
        if ((rqbuf[0]&0x7f)!=0x70) {
                fprintf(stderr,"\tInvalid header.\n");
                return -1;
        }
        fprintf(stderr,"\tCurrent command read filemark: %s\n",(rqbuf[2]&0x80)?"yes":"no");
        fprintf(stderr,"\tEarly warning passed: %s\n",(rqbuf[2]&0x40)?"yes":"no");
        fprintf(stderr,"\tIncorrect blocklengt: %s\n",(rqbuf[2]&0x20)?"yes":"no");
        fprintf(stderr,"\tSense Key: %d\n",rqbuf[2]&0xf);
        if (rqbuf[0]&0x80)
                fprintf(stderr,"\tResidual Length: %d\n",rqbuf[3]*0x1000000+rqbuf[4]*0x10000+rqbuf[5]*0x100+rqbuf[6]);
        fprintf(stderr,"\tAdditional Sense Length: %d\n",rqbuf[7]);
        fprintf(stderr,"\tAdditional Sense Code: %d\n",rqbuf[12]);
        fprintf(stderr,"\tAdditional Sense Code Qualifier: %d\n",rqbuf[13]);
        if (rqbuf[15]&0x80) {
                fprintf(stderr,"\tIllegal Param is in %s\n",(rqbuf[15]&0x40)?"the CDB":"the Data Out Phase");
                if (rqbuf[15]&0x8) {
                        fprintf(stderr,"Pointer at %d, bit %d\n",rqbuf[16]*256+rqbuf[17],rqbuf[15]&0x7);
                }
        }
	return 0;
}

int
do_status(CMDPROTO) {
#ifdef MTIOCGET
	struct	mtget	xmtget;
#endif
	unsigned char	buf[65535+100];
	unsigned char	vendor[10],product[10],xbuf[40];
	int	i,ret,hassunfirmware=0,compenabled=0,hascomp=0,hasaudio=0;
	int	dens,tapeisaudio=0,tapeisarchive=0;
	int	partitions,partsizetype,partsize;
	unsigned char	*f;
	struct	mt_exver	mtexver;

	datlib_get_version(fd,buf,65535);
	printf("Version: %s\n",buf);
	printf("Driver:\n");
	printf("\t$TAPE: %s",tape);
	switch (tapesel) {
	case TAPE_CMDLINE: printf(" (commandline)\n");break;
	case TAPE_ENVIRONMENT: printf(" (environment $TAPE)\n");break;
	case TAPE_DEFAULT: printf(" (default)\n");break;
	default:break;
	}
#ifdef MTIOCGET
	if (-1==ioctl(fd,MTIOCGET,&xmtget)) {
		fprintf(stderr,"%s status failed(%s)\n",xargv[0],strerror(errno));
	} else {
		printf("\tType : 0x%02x ",xmtget.mt_type);
		switch (xmtget.mt_type) {
		case 0x2c:
#if defined(MT_ISPYTHON) && (MT_ISPYTHON!=0x2c)
		case MT_ISPYTHON:
#endif
			printf("(Archive Python)\n");
			break;
#ifdef MT_ISHPDAT
		case MT_ISHPDAT:
			printf("(HP 354x0 DAT)\n");
			break;
#endif
		case 0x34:
			printf("(Generic DAT)\n");
			break;
		default:printf("\n");
			break;
		}
	}
#endif
#ifdef MTIOCGETDRIVETYPE
	{
		struct mtdrivetype_request mtdtr;
		struct mtdrivetype mtdtp;
		mtdtr.size = sizeof(mtdtp);
		mtdtr.mtdtp = &mtdtp;
		if (-1!=ioctl(fd,MTIOCGETDRIVETYPE,&mtdtr)) {
			printf("\tName : \"%s\"\n",mtdtp.name);
/*			printf("\tVendorID     : \"%s\"\n",mtdtp.vid);*/
		}
	}
#endif
#ifdef MTIOEXVER
	if (-1!=ioctl(fd,MTIOEXVER,&mtexver)) {
		if (mtexver.mt_exver_uscsicmdrestricted)
			printf("\tUSCSICMD: restricted for superuser\n");
		else
			printf("\tUSCSICMD: available for all users\n");
		if (!mtexver.mt_exver_longerase)
			printf("\tErase: long\n");
		else
			printf("\tErase: short\n");
	}
#endif

	printf("\nDrive:\n");

	/* TAPEDRIVER Part */
	if (-1==datlib_get_inquiry(fd,buf,255)) {
		perror("datlib_get_inquiry");
		return -1;
	}
	memcpy(vendor,buf+8,8);vendor[8]='\0';
	printf("\tVendor       : \"%s\"\n",vendor);
	while (strlen(vendor) && vendor[strlen(vendor)-1]==' ')
		vendor[strlen(vendor)-1]='\0';
	f=buf+16; while (*f!=' ') f++;
	*f='\0';
	strcpy(product,buf+16);
	printf("\tProduct      : \"%s\" ",product);
	while (strlen(product) && product[strlen(product)-1]==' ')
		product[strlen(product)-1]='\0';
	for (i=0;products[i].vendor;i++)
		if (	!strcmp(products[i].vendor,vendor) &&
			!strcmp(products[i].product,product)
		)
			break;
	if (products[i].info)
		printf("%s",products[i].info);
	printf("\n");
	tapeisaudio = 0;
	if (	!strncmp(vendor,"ARCHIVE",strlen("ARCHIVE")) &&
		!strncmp(product,"Python",strlen("Python"))
	) {
		tapeisarchive = 1;
		dens=datlib_get_density(fd);
		switch (dens) {
		case 0x80:
			tapeisaudio = 1;
			hasaudio = 1;
			break;
		default:
		case 0x13:
			if (-1==datlib_set_density(fd,0x80))
				break;
			hasaudio=1;
			datlib_set_density(fd,dens);
			break;
		}
		if (!hasaudio) {
			if (-1!=datlib_set_density(fd,0x8c)) {
				hassunfirmware=1;
				datlib_set_density(fd,dens);
			}
		}
	}

	buf[36]='\0';
	strcpy(xbuf,f+1);
	printf("\tFirmware     : \"%s\"",xbuf);
	if (hassunfirmware) printf(" (Sun Firmware)");
	printf("\n");

	if (-1!=datlib_get_mode_page(fd,0xf,buf,255)) {
		i=4+buf[3];
		hascomp		= 1;
		compenabled	= !!(buf[i+2]&0x80);
	} else
		perror("datlib_get_mode_page 0xf");

	printf("\tCapabilities : ");
	if (hasaudio) {
		printf("Audio");
		if (tapeisaudio)
			printf("(on) ");
		else
			printf("(off) ");
	}
	if (hascomp) printf("Compression ");else printf("No-Compression ");
	printf("\n");
	if (hascomp) printf("\tCompression  : %sabled\n",(compenabled?"en":"dis"));
	if (!strcmp(vendor,"HP")) {
		char	rqbuf[256];

		if (-1==datlib_request_sense(fd,rqbuf,255))
			perror("datlib_request_sense");
		if (rqbuf[21]&0x08)
			printf("\tDRIVE NEEDS CLEANING\n");
	}
	printf("\n");
	if (notape_inserted) {
		printf("Tape: No tape inserted.\n");
	} else {
		int	ret,xx,nroflogpages;
		char	overview[300];
		printf("Tape: \n\t");
		if (-1!=(ret=datlib_get_mode_page(fd,0x11,buf,255))) {
			printf("%swrite-protected, ",(buf[2]&0x80)?"":"not ");
			i=4+buf[3];
			partitions = buf[i+3];
			partsizetype = (buf[i+4]>>3)&3;
			switch (buf[i+1]) {
			case 10: /* SONY */
				if (partitions)
					partsize = buf[i+10]*256+buf[i+11];
				else
					partsize = buf[i+8]*256+buf[i+9];
				break;
			default: /* oops */
				fprintf(stderr,"do_status: modepage 0x11:bad size %d\n:",buf[i+1]);
			case 8: /* HP, Archive */
				partsize = buf[i+8]*256+buf[i+9];
				break;
			case 6: /* no additional partition */
				partsize = 0;
				break;
			}
		}
		/* the overview page contains all other pages */
		xx=datlib_get_log_page(fd,0x00,overview,300);
		if (xx!=-1) {
			nroflogpages=overview[3]+overview[2]*256;
			for (i=0;i<nroflogpages;i++) {
				if (overview[i+4]==0) {
					/* already got that page */
					continue;
				}
				if (overview[i+4]==0x31)
					break;
			}
			if (/*!tapeisaudio*/1 && (overview[i+4]==0x31)) {
				int	maxpart0=0,maxpart1=0;
#ifdef M_UNIX
				memset (buf, '\0', sizeof (buf));
				xx=datlib_get_log_page(fd,0x31,buf,32766);
#else
				xx=datlib_get_log_page(fd,0x31,buf,65535);
#endif
				if (xx!=-1) {
					int	l,k,x;

					l=4;k=buf[3]+4;
					while (l<k) {
						switch (buf[l+3]) {
						case 0:	x=0;break;
						case 1: x=buf[l+4];
							break;
						case 2: x=buf[l+4]*256+buf[l+5];
							break;
						case 3: x=buf[l+4]*65536+buf[l+5]*256+buf[l+6];
							break;
						case 4: x=buf[l+4]*16777216+buf[l+5]*65536+buf[l+6]*256+buf[l+7];
							break;
						default:
							x=0;break;
						}
						switch (buf[l]*256+buf[l+1]) {
						case 0x0003: /* max cap part 0*/
							maxpart0=x;
							break;
						case 0x0004:
							maxpart1=x;
							break;
						}
						l=l+4+buf[l+3];
					}
					maxpart0+=maxpart1;
					if (!maxpart0) {
						if (tapeisaudio++)
							printf("Audio tape");
						else
							printf("tape of unknown length (probably audio tape)");
					} else if (maxpart0 < 1400000) {
						printf("60m tape (DDS1 - 1.2 GByte raw capacity) ");
					} else if (maxpart0 <2000000) {
						printf("90m tape (DDS1 - 2 GByte raw capacity) ");
					} else if (maxpart0 <4000000) {
						printf("120m tape (DDS2 - 4 GByte raw capacity) ");
					} else if (maxpart0 <12000000) {
						printf("125m tape (DDS3 - 12 GByte raw capacity) ");
					}
				}
			}
		}
		printf("\n");
		if (!tapeisaudio) {
			if (!partitions)
				printf("\tsingle-partition tape\n");
			else {
				printf("\tdual-partition tape, extra partition size ");
				printf("%d ",partsize);
				switch (partsizetype) {
				case 0:printf("bytes");break;
				case 1:printf("kilobytes");break;
				case 2:printf("megabytes");break;
				default:printf("unknown type");break;
				}
				printf("\n\tselected partition: ");
				switch (datlib_query_active_partition(fd)){
				case 0:printf("main(0)");break;
				case 1:printf("aux(1)");break;
				default:printf("unknown(%d)",datlib_query_active_partition(fd));break;
				}
				printf("\n");
			}
#ifdef DAT_BSDISH
			printf("\tfilenumber %d, blocknumber %d\n",xmtget.mt_fileno,xmtget.mt_blkno);
#endif
		}

	}
	return 0;
}

int
do_asf(CMDPROTO) {
#ifdef DAT_BSDISH
	struct	mtget	xmtget;
	struct	mtop	xmtop;
	struct	mtget mt_status;
#endif
	int	mt_fileno;

	if (argc<2) {
		mt_fileno = 1;
	} else {
		if (!sscanf(argv[1],"%d",&(mt_fileno)))
			return -1;
	}

	if (mt_fileno<0) {
		fprintf(stderr, "%s: negative file number\n",xargv[0]);
		return -1;
	}
#ifdef DAT_BSDISH
	(void) ioctl(fd, MTIOCGET, (char *)&mt_status);
	if (ioctl(fd, MTIOCGET, (char *)&mt_status) < 0) {
		perror("dat");
		return -1;
	}
	/*
	 * Check if device supports reporting current file
	 * tape file position.  If not, rewind the tape, and
	 * space forward.
	 */
	if (
#if defined(MTF_ASF) && defined(MTNBSF)
		!(mt_status.mt_flags & MTF_ASF)
#else
		1
#endif
	) 
	{
		mt_status.mt_fileno = 0;
		xmtop.mt_count = 1;
		xmtop.mt_op = MTREW;
		if (ioctl(fd, MTIOCTOP, &xmtop) < 0) {
			perror("dat MTIOCTOP");
			exit(2);
		}
	}
#endif /* DAT_BSDISH */
#ifdef DAT_SCO
	if (ioctl(fd, MT_REWIND, (void *)0) < 0) {
		perror("dat MT_REWIND");
		exit(2);
	}
#endif
#ifdef DAT_BSDISH
#ifdef MTNBSF
	/* this case won't trigger ... see above #if */
	if (mt_fileno < mt_status.mt_fileno) {
		xmtop.mt_op = MTNBSF;
		xmtop.mt_count =  mt_status.mt_fileno - mt_fileno;
	} else
#endif
	{
		xmtop.mt_op = MTFSF;
		xmtop.mt_count =  mt_fileno - mt_status.mt_fileno;
	}
	if (ioctl(fd, MTIOCTOP, &xmtop) < 0) {
		perror("dat MTIOCTOP");
		return -1;
	}
#endif
#ifdef DAT_SCO
	if (ioctl(fd, MT_RFM, (void *)mt_fileno) < 0) {
		perror("dat MT_RFM");
		return -1;
	}
#endif
	return 0;
}

/*
 * ALLCAPS messages are not documented for read buffer, but for mechanism
 * log (s.a.) and are also found in DATAUDIT.EXE
 */
char *eeprom_errstrings[256] = {
	/*00*/        "Unknown device error 00",
	"12V supply fault. The voltage is too low.",
	"Unknown device error 02",
	"Mode sensor fault. Unexpected mechanism mode.",
	"TENSION TOO LOW",
	"Bad diameter calculation",
	"The capstan motor has stalled",
	"Servo calibration failure or servo comms problem (C15x7)",
	"The drum is stationary when it should be rotating",
	"Drum phase lock lost during write or drum speed problem (C15x7)",
	"Drum phase generator problem, drum is rotating but DPG inactive",
	"TENSION TOO HIGH",
	"Eject-to-unthread abort because lurk switch deactived",
	"Mode movement timeout",
	"CAPSTAN STOP TIMEOUT",
	"Servo stop reels timeout",

	/*10*/        "The supply reel has stuck during tape threading",
	"The supply reel has stuck (CAPSTAN MODE)",
	"There is excessive movement during a capstan clean",
	"The takeup reel has stuck (CAPSTAN MODE)",
	"The supply or takeup reel is stuck during fast-search/rewind",
	"The servo did not see the 1st cmd or reels spinning while threading",
	"The drum failed to stop rotating",
	"The reels stuck whilst the drive was threading",
	"The supply reel stuck during a capstan or reel driven tape motion",
	"Servo ROM checksum failure",
	"Supply reel spin. Tape may have snapped.",
	"The takeup reel stuck during a capstan or reel driven tape motion",
	"Takeup reel spin. Tape may have snapped.",
	"Download incompatible",
	"Servo busy or servo timeout",
	"Servo hung",

	/*20*/        "Filemark encountered during read, write, space",
	"Setmark encountered during read, write, space",
	"A record of unexpected length was read",
	"Format error in group just read",
	"Buffer firmware lost tape position",
	"Parity error on reading data from buffer",
	"Mismatch in bytes transferred",
	"An illegal machine state has been detected",
	"An illegal buffer command has been received",
	"Illegal parameters with valid buffer command",
	"SPACE or LOCATE failed because position is in compressed entity",
	"WRITE SETMARKS failed because number out of range",
	"Unknown device error 2C",
	"Unknown device error 2D",
	"Unknown device error 2E",
	"Unknown device error 2F",

	/*30*/        "Unknown device error 30",
	"Unknown device error 31",
	"Unknown device error 32",
	"Unknown device error 33",
	"Unknown device error 34",
	"Unknown device error 35",
	"Unknown device error 36",
	"Unknown device error 37",
	"Unknown device error 38",
	"Unknown device error 39",
	"Unknown device error 3A",
	"Unknown device error 3B",
	"Unknown device error 3C",
	"Unknown device error 3D",
	"Unknown device error 3E",
	"Unknown device error 3F",

	/* compression errors not typed in */

	/*40*/        "Unknown device error 40",
	"Unknown device error 41",
	"Unknown device error 42",
	"Unknown device error 43",
	"Unknown device error 44",
	"Unknown device error 45",
	"Unknown device error 46",
	"Unknown device error 47",
	"Unknown device error 48",
	"Unknown device error 49",
	"Unknown device error 4A",
	"Unknown device error 4B",
	"Unknown device error 4C",
	"Unknown device error 4D",
	"Unknown device error 4E",
	"Unknown device error 4F",

	/* more compression errors */

	/*50*/        "Unknown device error 50",
	"Unknown device error 51",
	"Unknown device error 52",
	"Unknown device error 53",
	"Unknown device error 54",
	"Unknown device error 55",
	"Unknown device error 56",
	"Unknown device error 57",
	"Unknown device error 58",
	"Unknown device error 59",
	"Unknown device error 5A",
	"Unknown device error 5B",
	"Unknown device error 5C",
	"Unknown device error 5D",
	"Unknown device error 5E",
	"Unknown device error 5F",

	/* autoloader errors */

	/*60*/        "Unknown device error 60",
	"Unknown device error 61",
	"Unknown device error 62",
	"Unknown device error 63",
	"Unknown device error 64",
	"Unknown device error 65",
	"Unknown device error 66",
	"Unknown device error 67",
	"Unknown device error 68",
	"Unknown device error 69",
	"Unknown device error 6A",
	"Unknown device error 6B",
	"Unknown device error 6C",
	"Unknown device error 6D",
	"Unknown device error 6E",
	"Unknown device error 6F",

	/* more autoloader errors */

	/*70*/        "Unknown device error 70",
	"Unknown device error 71",
	"Unknown device error 72",
	"Unknown device error 73",
	"Unknown device error 74",
	"Unknown device error 75",
	"Unknown device error 76",
	"Unknown device error 77",
	"Unknown device error 78",
	"Unknown device error 79",
	"Unknown device error 7A",
	"Unknown device error 7B",
	"Unknown device error 7C",
	"Unknown device error 7D",
	"Unknown device error 7E",
	"Unknown device error 7F",

	/*80*/        "Unexpected command received during a write",
	"Failure to get a write command",
	"The write command queue is in a mess",
	"Report problems during a write",
	"The RAW retry limit has been exceeded for a group",
	"The RAW retry limit has been exceeded writing a frame",
	"No communications message sent by servo processor within period",
	"Bad read command received",
	"Failure to get read command",
	"The read command queue is in a mess",
	"Report problems during a read",
	"Hardware parity error or frame sequence discontinuity (C15x7)",
	"A C3 row calculation failed",
	"Streamfail occured during error mapping or C3 correction failed (C15x7)
	",
	"Too many frames with bad sub-data. Position may be lost.",
	"Too many guessed logical frame IDs. Position may be lost.",

	/*90*/        "A group being read was found to be incomplete",
	"A bad positive track reading 22-frame group",
	"A bad negative track reading 22-frame group",
	"Conflict between tracks reading 22-frame group",
	"Timeout during hardware CS ECC syndrome check (read)",
	"Checksum mismatch in positive track during C3 correction (read)",
	"Checksum mismatch in negative track during C3 correction (read)",
	"Unknown device error 97",
	"Unknown device error 98",
	"Unknown device error 99",
	"Unknown device error 9A",
	"Unknown device error 9B",
	"Unknown device error 9C",
	"Unknown device error 9D",
	"Unknown device error 9E",
	"Unknown device error 9F",

	/*A0*/        "Unknown device error A0",
	"Unknown device error A1",
	"Unknown device error A2",
	"Unknown device error A3",
	"Unknown device error A4",
	"Unknown device error A5",
	"Unknown device error A6",
	"Unknown device error A7",
	"Unknown device error A8",
	"Unknown device error A9",
	"Unknown device error AA",
	"Unknown device error AB",
	"Unknown device error AC",
	"Supply-reel motor took too long to reach speed (AD)",
	"Unknown device error AE",
	"Unknown device error AF",

	/*B0*/        "Unknown device error B0",
	"Unknown device error B1",
	"Unknown device error B2",
	"Unknown device error B3",
	"Unknown device error B4",
	"Unknown device error B5",
	"Unknown device error B6",
	"Unknown device error B7",
	"Unknown device error B8",
	"Unknown device error B9",
	"Unknown device error BA",
	"Unknown device error BB",
	"Unknown device error BC",
	"Unknown device error BD",
	"Unknown device error BE",
	"Unknown device error BF",

	/*C0*/        "Unknown device error C0",
	"Unknown device error C1",
	"Unknown device error C2",
	"Unknown device error C3",
	"Unknown device error C4",
	"Unreadable sub-data encountered when track-following (C5)",
	"Unknown device error C6",
	"Unknown device error C7",
	"Unknown device error C8",
	"Unknown device error C9",
	"Unknown device error CA",
	"Unknown device error CB",
	"Unknown device error CC",
	"The target position has been missed (CD)",
	"Unknown device error CE",
	"Failure to reach the target point on tape (CF)",

	/*D0*/        "Unknown device error D0",
	"Failure to position",
	"Unknown device error D2",
	"Unknown device error D3",
	"Unknown device error D4",
	"Unknown device error D5",
	"Unknown device error D6",
	"Unknown device error D7",
	"Unknown device error D8",
	"Unknown device error D9",
	"Unknown device error DA",
	"Unknown device error DB",
	"Unknown device error DC",
	"Unknown device error DD",
	"Unknown device error DE",
	"Unknown device error DF",

	/*E0*/        "Unknown device error E0",
	"Unknown device error E1",
	"A system log on the tape is unreadable (E2)",
	"Unknown device error E3",
	"Unknown device error E4",
	"Unknown device error E5",
	"Unknown device error E6",
	"Unable to find the target position on tape (E7)",
	"Unknown device error E8",
	"Unknown device error E9",
	"Unknown device error EA",
	"Unknown device error EB",
	"Unknown device error EC",
	"Unknown device error ED",
	"Unknown device error EE",
	"Unknown device error EF",

	/*F0*/        "Command rejected because previous failed (F0)",
	"Unknown device error F1",
	"Unknown device error F2",
	"An unsupported tape has been inserted",
	"A cleaning sequence has occured (F4)",
	"Unknown device error F5",
	"Unknown device error F6",
	"Unknown device error F7",
	"Unknown device error F8",
	"Unknown device error F9",
	"Unknown device error FA",
	"Unknown device error FB",
	"Unknown device error FC",
	"Unknown device error FD",
	"Unknown device error FE",
	"Unknown device error FF",
};

int
do_read_eeprom(CMDPROTO) {
	unsigned char   buf[0x70+20];
	int     slot, soff, i;
#if 1
	unsigned char   extinq[256];
	int vendorpages[] = {0x1, 0x2, 0x3, 0xc0, 0xc1, /*0xc2*/};

	memset (extinq, '*', sizeof(extinq));
	printf ("HP Extended Inquiry Pages:\n");

	for (slot=0; slot < sizeof(vendorpages)/sizeof(vendorpages[0]);slot++) {

		if (-1==datlib_get_inquiry_extended(fd, vendorpages[slot], extinq,255)) {
			perror("datlib_get_inquiry_extended");
		} else {
			printf ("Page 0x%02x: \"%s\"\n", vendorpages[slot], &extinq[5]);
		}
	}
#endif

	memset (buf, 0xfe, sizeof (buf));

	/*
	 * Now really read the EEPROM
	 */
	if (-1==datlib_read_buffer(fd,0x01,0x290,buf,0x70)) {
		perror("datlib_read_buffer");
		return -1;
	}

	printf ("Failure Log from HP Drive EEPROM:\n\n");

	for (slot=0; slot < 30; slot++) {
		soff = (slot%10)*10 + (slot/10)*3; 
		if (buf[soff] != 0 && buf[soff] != 0xff) {
			printf ("SCSI(%02x/%02x) (%s,%s,%s) %s\n",
				buf[soff+1],
				buf[soff+2]&15,
				buf[soff+2]&16?"prev":"curr",
				buf[soff+2]&64?"hang":"norm",
				buf[soff+2]&128?"MCAU":"----",
				eeprom_errstrings[buf[soff]]
			);
		}
	}
} /* do_read_eeprom */


void
_dump_modepage(unsigned char *buf) {
	switch(buf[0]) {
	case 0x01:
		printf("Read/Write Error Recovery Page:\n");
		printf("\tRead Retry Count: %d\n",buf[3]);
		printf("\tWrite Retry Count: %d\n",buf[8]);
		break;
	case 0x02:
		printf("Connect/Disconnect Page:\n");
		printf("\tBus Inactivity Limit: %g ms\n",(buf[4]*256+buf[5])/10.0);
		printf("\tMaximum Burst Size: %d b\n",buf[10]*256+buf[11]);
		break;
	case 0x0a:
		printf("Control Mode Page:\n");
		printf("\tReport Log Exception Condition: %s\n",buf[2]?"ON":"OFF");
		break;
	case 0x0f:
		printf("Compression Control Page:\n");
		printf("\tData Compression: %sabled.\n",
			(buf[2]&0x80)?"en":"dis"
		);
		printf("\tData Decompression: %sabled.\n",
			(buf[3]&0x80)?"en":"dis"
		);
		printf("\tReport Error on Decompression: %sabled.\n",
			(buf[3]&0x20)?"en":"dis"
		);
		printf("\tCompression Algorithm:");
		switch(buf[7]) {
		case 0x00:printf("uncompressed");break;
		case 0x20:printf("DCLZ compressed");break;
		default:printf("compressed with an unknown method: 0x%02x",buf[7]);break;
		}
		printf("\n");
		printf("\tDecompression Algorithm:");
		switch(buf[11]) {
		case 0x00:printf("uncompressed");break;
		case 0x20:printf("DCLZ compressed");break;
		default:printf("compressed with an unknown method: 0x%02x",buf[7]);break;
		}
		printf("\n");
		break;
	case 0x10:
		printf("Device configuration page:\n");
		/* meaning of raw is reversed... */
		printf("\tRead-after-Write %sabled.\n",
			(buf[2]&0x10)?"dis":"en"
		);
		printf("\tECC C3 error checking %sabled.\n",
			(buf[2]&0x08)?"en":"dis"
		);
		printf("\tN-Groups: %d\n",buf[2]&0x7);
		printf("\tActive Partition: %d\n",buf[3]);
		printf("\tWrite Delay Time:%d x100ms\n",
			buf[6]*256+buf[7]
		);
		printf("\tRecord Setmarks (RSMK): %s\n",
			(buf[8]&0x20)?"on":"off"
		);
		printf("\tReport Early Warnings: %s\n",
			(buf[8]&1)?"on":"off"
		);
		printf("\tSynchronize at Early-Warning: %s\n",
			(buf[10]&0x08)?"on":"off"
		);
		printf("\tData Buffer Recovery: %s\n",
			(buf[8]&0x80)?"on":"off"
		);
		printf("\tBlock Identifiers: %s\n",
			(buf[8]&0x40)?"on":"off"
		);
		break;
	case 0x11:
		printf("Medium Partition Page:\n");
		printf("\tMaximum additional partitions:%d\n",
			buf[2]
		);
		printf("\tAdditional Partitions defined:%d\n",
			buf[3]
		);
		printf("\tPartition Size Unit:");
		switch ((buf[4]>>3)&3) {
		case 0:printf("bytes");break;
		case 1:printf("kilobytes");break;
		case 2:printf("megabytes");break;
		default:printf("err, value of %d in PSUM field?",(buf[4]>>3)&3);break;
		}
		printf("\n");
		printf("\tMedium Format recognition: %d\n",
			buf[5]
		);
		switch (buf[1]) {
		default:
		case 6:
			break;
		case 8:
			printf("\tAdditional Partition Size:%d\n",buf[8]*256+buf[9]);
			break;
		case 10:
			printf("\tPartition 0 Size:%d\n",buf[8]*256+buf[9]);
			if (buf[3])
				printf("\tPartition 1 Size:%d\n",buf[10]*256+buf[11]);
			break;
		}
		break;
		/* MODE SELECT ONLY ?????? */
	case 0x1c:
		printf("HP TapeAlert(TM) Mode Page:\n");
		printf ("\tInformation reported by: %s\n",buf[2]&0x08 ? "Check Condition" : "Application must poll");
		break; 
	case 0x1d:
		/* for autoloaders */
		printf("Element Address Assigment Page:\n");
		printf("\tNumber of Storage Elements:%d\n",buf[9]);
		break;
	case 0x1f:
		printf("Capabilities Page:\n");
		printf("\tIntentionally left blank.\n");
		break;
	default: {
		int	j;
		printf("Unknown Page 0x%02x:\n\t",buf[0]);
		for (j=2;j<2+buf[1];j++) 
			printf("%02x ",buf[j]);
		printf("\n");
		break;
	}
	}
}

int
do_inquire(CMDPROTO) {
	unsigned char	buf[65535+100];
	unsigned char	*f,vendor[8],product[30],xbuf[8];
	int	i;

	if (-1==datlib_get_inquiry(fd,buf,255)) {
		perror("datlib_get_inquiry");
		return -1;
	}
	printf("Type: %s\n",buf[1]&64?"Autoloader":"Single Tape Drive");
	memcpy(vendor,buf+8,8);vendor[8]='\0';
	printf("Vendor: \"%s\"\n",vendor);
	f=buf+16; while (*f!=' ') f++;
	*f='\0';
	strcpy(product,buf+16);
	printf("Product:  \"%s\"\n",product);
	while (strlen(product) && product[strlen(product)-1]==' ')
		product[strlen(product)-1]='\0';

	buf[255]='\0';
	printf("Firmware: %s\n",f+1);
	{
		int	len,i,j,k;

		if (-1==datlib_get_mode_page(fd,0x0,buf,255)) {
			perror("datlib_get_mode_page 0x0");
			exit(1);
		} else
			perror("datlib_get_mode_page 0xf");
		len=buf[0];
		printf("Tape is %swrite protected.\n",(buf[2]&0x80)?"":"NOT ");
		i=4;
		if (buf[3]) {
			printf("Blockdescriptor found:\n");
			printf("\tDensity: %s (%02x)\n",
				buf[i] == 0x13 ? "DDS-1" :
				buf[i] == 0x24 ? "DDS-2" :
				buf[i] == 0x25 ? "DDS-3" :
				"unknown",
				buf[i]
			);
			printf("\t#blocks: %d\n",buf[i+1]*65536+buf[i+2]*256+buf[i+3]);
			printf("\tBlocksize: %d",buf[i+5]*65536+buf[i+6]*256+buf[i+7]);
			if (!(buf[i+5]*65536+buf[i+6]*256+buf[i+7]))
				printf(" (variable)");
			printf("\n");
			i+=buf[3];
		} else {
			printf("No Blockdescriptor found.\n");
		}
		if (-1==datlib_get_mode_page(fd,0x3f,buf,255)) {
			fprintf(stderr,"datlib_get_mode_page 0x3F FAILED, trying to retrieve single pages....\n");
			for (k=0x1;k<0x3f;k++) {
				if (-1==datlib_get_mode_page(fd,k,buf,255)) {
					fprintf(stderr,"failed to get modepage %d.\n",k);
					continue;
				}
				_dump_modepage(buf+4+buf[3]);
			}
		} else {
			len=buf[0];
			i=4+buf[3];
			while (i<len) {
				_dump_modepage(buf+i);
				i+=buf[i+1]+2;
			}
		}
	}
	printf("\n");
	{
		int	newline,i,j,k,l,m,nroflogpages,xx;
		unsigned	char	overview[1000];
		struct	log_sense_table *lt;

		/* the overview page contains all other pages */
		xx=datlib_get_log_page(fd,0x00,overview,300);
		if (xx==-1) {
			perror("datlib_get_log_page(0x00)");
			return -1;
		}
		nroflogpages=overview[3]+overview[2]*256;
		for (i=0;i<nroflogpages;i++) {
			if (overview[i+4]==0) {
				/* already got that page */
				continue;
			}
#ifdef M_UNIX
			memset (buf, '\0', sizeof (buf));
			xx=datlib_get_log_page(fd,overview[i+4],buf,32766);
#else

			xx=datlib_get_log_page(fd,overview[i+4],buf,65535);
#endif
			if (xx==-1) {
				fprintf(stderr, "datlib_get_log_page(%x): %s\n",
					overview[i+4], strerror(errno)
				);
				/* continue; */
			}
			j=0;
			while (	(ls_table[j].name!=NULL) && 
				(overview[i+4]!=ls_table[j].nr)
			)	j++;
			if (ls_table[j].name==NULL) {
				m=j;j=0;
				while (	(ls_arch_table[j].product!=NULL) &&
					strncasecmp(ls_arch_table[j].product,(char *) vendor,strlen(ls_arch_table[j].product))
				) j++;
				if (ls_arch_table[j].product!=NULL) {
					k=0;
					while (	(ls_arch_table[j].lt[k].name!=NULL) &&
						(overview[i+4]!=ls_arch_table[j].lt[k].nr)
					)	k++;
					lt=&(ls_arch_table[j].lt[k]);
				} else {
					lt=&(ls_table[m]);
				}
			} else {
				lt=&(ls_table[j]);
			}
			printf("Logpage %s (%2x):\n",lt->name?lt->name:"unknown",overview[i+4]);
			l=4;k=256*buf[2]+buf[3]+4;
			while (l<k) {
				m=0;
				while (	(lt->log_entries[m].name!=NULL) &&
					(lt->log_entries[m].code!=(buf[l]*256+buf[l+1]))
				)
					m++;
 
				/* special HP trace pages don't need this */
				if (	lt->log_entries != logpage_hp_32 &&
					lt->log_entries != logpage_hp_33 &&
					lt->log_entries != logpage_hp_36
				) {
					if (lt->log_entries[m].name==NULL)
						printf("%-45s (%04x):","Unknown Parameter",buf[l]*256+buf[l+1]);
					else
						printf("%-45s (%04lx):",lt->log_entries[m].name,lt->log_entries[m].code);
				}
				newline = 1;
				switch (buf[l+3]) {
				case 0:	break;
				case 1: printf("%d",buf[l+4]);
					break;
				case 2: printf("%d",buf[l+4]*256+buf[l+5]);
					break;
				case 3:
					if (lt->log_entries == logpage_hp_36) {
						if (buf[l+4]) {
							printf(" error %02x:  %5d",
							buf[l+4],
							buf[l+5]*256+buf[l+6]
							);
						} else
							newline = 0;
					} else
						printf("%d",buf[l+4]*65536+buf[l+5]*256+buf[l+6]);
					break;
				case 4: printf("%d",buf[l+4]*16777216+buf[l+5]*65536+buf[l+6]*256+buf[l+7]);
					break;
				case 8: /* is it funny format of trace log? */
					if (	lt->log_entries==logpage_hp_32||
						lt->log_entries==logpage_hp_33
					) {
						printf ("TPID:%04x time:%9.3f args %04x %04x",
							buf[l+4]*256+buf[l+5],
							(double) (buf[l+6]*256+buf[l+7])*512.0/1000000.0,
							buf[l+8]*256+buf[l+9],
							buf[l+10]*256+buf[l+11]
						);
						break;
					}
					/* FALLTHROUGH */
				default:
					for (j=l+4;j<l+4+buf[l+3];j++)
						printf("%02x ",buf[j]);
				}
				l=l+4+buf[l+3];
				if (newline) printf("\n");
			}
			printf("\n");
		}
	}
	return 0;
}

int
do_delete_partition(CMDPROTO) {
	if (-1==datlib_delete_partition(fd)) {
		perror("datlib_delete_partition");
		return -1;
	}
	return 0;
}
	
int
do_create_partition(CMDPROTO) {
	int	sizetype,size;

	if (argc<2) return -1;
	switch (argv[1][strlen(argv[1])-1]) {
	case 'm':case 'M':
		sizetype=2;
		break;
	case 'k':case 'K':
		sizetype=1;
		break;
	default:
		sizetype=0;
		break;
	}
	if (!sscanf(argv[1],"%d",&size))
		return -1;
	while ((size>65535) && (sizetype<2)) {
		size=size/1024;
		sizetype++;
	}

	if (-1==datlib_create_partition(fd,size,sizetype)) {
		perror("datlib_create_partition");
		return -1;
	}
	return 0;
}

int
do_select_partition(CMDPROTO) {
	int	nr;

	if (argc<2) return -1;
	if (!sscanf(argv[1],"%d",&nr))
		return -1;
	if (nr<0 || nr>1) {
		fprintf(stderr,"DDS tapes may only have two partitions, 0 and 1.\n");
		return -1;
	}

	if (-1==datlib_switch_active_partition(fd,nr)) {
		perror("datlib_switch_active_partition");
		return -1;
	}
	return 0;
}

int
usage(CMDPROTO) {
	char	**methods;
	fprintf(stderr,"Usage:\n"
"%s	inq(uiry) - inquire about tape status\n"
"	sta(tus)  - just as mt status \n"
"	comp on | off | <modenr>   - enable/disable compression\n"
"	decomp on | off | <modenr> - enable/disable decompression\n"
"	fsf [<count>]                - forward <count> (default 1) filemarks\n"
"	bsf [<count>]                - backward <count> (default 1) filemarks\n"
"	fsr [<count>]                - forward <count> (default 1) records\n"
"	bsr [<count>]                - backward <count> (default 1) records\n"
"	eom                          - forward to end of medium\n"
"	rew(ind)                   - rewind tape\n"
"	off(line)                  - eject tape\n"
"	era(se)                    - erase tape\n"
"	(w)eof [<count>]           - write <count> (default 1) EOF marks\n"
"	res(et)                    - reset scsi tape target\n"
"	ret(en)                    - retension tape\n"
"	crt <size>                 - create partition, you may append 'm' 'k'\n"
"                                    to size\n"
"	dele                       - delete second partition\n"
"	sel [<nr>]                 - select partition <nr>\n"
"	audio [on|off]             - report or set audio modus\n"
"	help                       - this helppage\n"
"%s uses "DEFAULTTAPE" or $TAPE or the device specified with the option\n"
"\"-f <device>\".\n"
"Use \"-g <method>\" to specify one of following methods to access the drive:\n"
,
	argv[0],argv[0]
	);
	methods=datlib_get_methods();
	while (*methods) {
		fprintf(stderr,"%s ",*methods);
		methods++;
	}
	fprintf(stderr,"\n");
}

int
main(int argc,char **argv) {
	int	ret,fd,i,ent;
	char	*method;

	xargv=argv;
	tape=NULL;
	method=NULL;
	for (i=1;i<argc;i++) {
		if (!strcmp(argv[i],"-v")) {
			fprintf(stderr,"%s: version %s\n",argv[0],DATLIBVERSION);
			return 0;
		}
		if (!strcmp(argv[i],"-f")) {
			if (i==argc-1) 
				continue;
			if (tape!=NULL) free(tape);
			tape=strdup(argv[i+1]);
			tapesel = TAPE_CMDLINE;
			memcpy(argv+i,argv+i+2,(argc-i-1)*sizeof(char**));
			argc-=2;
			i--;
		}
		if (!strcmp(argv[i],"-g")) {
			if (i==argc-1)
				continue;
			method=argv[i+1];
			memcpy(argv+i,argv+i+2,(argc-i-1)*sizeof(char**));
			argc-=2;
			i--;
		}
	}
	if (tape==NULL) {
		tape=getenv("TAPE");
		tapesel = TAPE_ENVIRONMENT;
	}
	if (tape!=NULL) {
		tape=strdup(tape);
	} else {
		tape=strdup(DEFAULTTAPE);
		tapesel = TAPE_DEFAULT;
	}
	if (argc<2) {
		usage(-1,argc,argv);
		return 0;
	}
	ent=-1;

/* Handlers using the kernel MTIOCTOP interface */
	for (i=0;stdcmdtable[i].name!=NULL;i++) {
		/* check for prefix */
		if (argv[1]==strstr(argv[1],stdcmdtable[i].name)) {
#ifdef DAT_BSDISH
			struct	mtop	xmtop;
#endif
#ifdef DAT_SCO
			struct { int mt_op, mt_count; } xmtop;
#endif

			xmtop.mt_op=stdcmdtable[i].mtop;
			if (argc<stdcmdtable[i].argc+2)
				xmtop.mt_count=1;
			else {
				if (stdcmdtable[i].argc) {
					if (!sscanf(argv[2],"%d",(int*)(&(xmtop.mt_count)))) {
						fprintf(stderr,"Argument is not a number.\n");
						exit(1);
					}
				}
			}
			fd=open(tape,(stdcmdtable[i].rdonly?O_RDONLY:O_WRONLY));
			if (fd==-1) {
				/* most likely tape is not inserted */
				notape_inserted = 1;
				fd=open(tape,O_NDELAY|(stdcmdtable[i].rdonly?O_RDONLY:O_WRONLY));
			}
			if (fd==-1) {
				fprintf(stderr,"%s:open:could not open ",argv[0]);
				perror(tape);
				exit(1);
			}
#ifdef DAT_BSDISH
			if (-1==ioctl(fd,MTIOCTOP,&xmtop)) {
				char	buf[200];
				sprintf(buf,"ioctl MTIOCTOP %s failed",stdcmdtable[i].name);
				perror(buf);
			}
#endif
#ifdef DAT_SCO
			if (-1==ioctl(fd, xmtop.mt_op, (void *)xmtop.mt_count)){
				char    buf[200];
				sprintf(buf,"ioctl MTIOCTOP %s failed",stdcmdtable[i].name);
				perror(buf);
			}
#endif
			close(fd);
			return 0;
		}
	}
	/* special handlers like STATUS etc. */
	for (i=0;cmdtable[i].name!=NULL;i++) {
		/* check for prefix */
		if (argv[1]==strstr(argv[1],cmdtable[i].name)) {
			ent=i;
			break;
		}
	}
	if (ent==-1){
		fprintf(stderr,"no such command: %s\n",argv[1]);
		exit(1);
	}
	fd=open(tape,cmdtable[ent].rdonly?O_RDONLY:O_WRONLY);
	if (fd==-1) {
		/* most likely tape is not inserted */
		notape_inserted = 1;
		fd=open(tape,O_NDELAY|(cmdtable[ent].rdonly?O_RDONLY:O_WRONLY));
	}
	if (fd==-1) {
		perror("open");
		exit(1);
	}
	datlib_init(fd,method);
	ret=cmdtable[ent].fun(fd,argc-1,argv+1);
	close(fd);
	return ret;
}

