/*
 * Copyright (c) 1980 The Regents of the University of California.
 * All rights reserved.
 *
 * 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 the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 */
/* Modified for the Linux SCSI tape driver by Kai Makisara.
   Last Modified: Sun May 12 21:44:08 1996 by root@kai.makisara.fi
*/

#ifndef lint
static char rcsid[] = "$Id: /usr2/users/root/mt-st-0.4/mt.c at Sun May 12 21:44:08 1996 by root@kai.makisara.fi$";
char copyright[] =
"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

/*
 * mt --
 *   magnetic tape manipulation program
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <fcntl.h>

#define	equal(s1,s2)	(strcmp(s1, s2) == 0)

static void status();
static int do_dat_compression();

static struct commands {
    char *c_name;
    int c_code;
    int c_count_bits;
    int c_ronly;
} com[] = {
    { "weof",	MTWEOF,	0, 0 },
    { "wset",	MTWSM,	0, 0 },
    { "eof",	MTWEOF,	0, 0 },
    { "fsf",	MTFSF,	0, 1 },
    { "fsfm",	MTFSFM,	0, 1 },
    { "bsf",	MTBSF,	0, 1 },
    { "bsfm",	MTBSFM,	0, 1 },
    { "fsr",	MTFSR,	0, 1 },
    { "bsr",	MTBSR,	0, 1 },
    { "fss",	MTFSS,	0, 1 },
    { "bss",	MTBSS,	0, 1 },
    { "rewind",	MTREW,	0, 1 },
    { "offline",	MTOFFL,	0, 1 },
    { "rewoffl",	MTOFFL,	0, 1 },
    { "retension",	MTRETEN,0, 1 },
    { "eod",	MTEOM,	0, 1 },
    { "seod",	MTEOM,	0, 1 },
    { "seek",	MTSEEK, 0, 1 },
    { "tell",	MTTELL,	0, 1 },
    { "status",	MTNOP,	0, 1 },
    { "erase",	MTERASE, 0, 0},
    { "setblk",	MTSETBLK, 0, 1},
#ifdef MTLOCK
    { "lock",	MTLOCK, 0, 1 },
    { "unlock",     MTUNLOCK, 0, 1 },
#endif
#ifdef MTLOAD
    { "load",	MTLOAD,	0, 1 },
#endif
#ifdef MTCOMPRESSION
    { "compression",MTCOMPRESSION, 0, 1},
#endif
    { "setdensity",	MTSETDENSITY, 0, 1},
    { "drvbuffer",	MTSETDRVBUFFER, 0, 1},
    { "stwrthreshold", MTSETDRVBUFFER, MT_ST_WRITE_THRESHOLD, 1},
    { "stoptions",	1002, 0, 1},
#ifdef MT_ST_SETBOOLEANS
    { "stsetoptions",   1003, 0, 1},
    { "stclearoptions", 1004, 0, 1},
    { "defblksize", MTSETDRVBUFFER, MT_ST_DEF_BLKSIZE, 1},
    { "defdensity", MTSETDRVBUFFER, MT_ST_DEF_DENSITY, 1},
    { "defdrvbuffer", MTSETDRVBUFFER, MT_ST_DEF_DRVBUFFER, 1},
    { "defcompression", MTSETDRVBUFFER, MT_ST_DEF_COMPRESSION, 1},
#endif
    { "densities", 1000, 0, 0},
    { "datcompression", 1001, 0, 0},
#ifdef MTSETPART
    { "setpartition", MTSETPART, 0, 1},
    { "mkpartition", MTMKPART, 0, 1},
    { "partseek", 1006, 0, 0},
#endif
    { NULL }
};

static struct densities {
    int code;
    char *name;
} density_tbl[] = {
    {0x00, "default"},
    {0x01, "NRZI (800 bpi)"},
    {0x02, "PE (1600 bpi)"},
    {0x03, "GCR (6250 bpi)"},
    {0x05, "QIC-45/60 (GCR, 8000 bpi)"},
    {0x06, "PE (3200 bpi)"},
    {0x07, "IMFM (6400 bpi)"},
    {0x08, "GCR (8000 bpi)"},
    {0x09, "GCR /37871 bpi)"},
    {0x0a, "MFM (6667 bpi)"},
    {0x0b, "PE (1600 bpi)"},
    {0x0c, "GCR (12960 bpi)"},
    {0x0d, "GCR (25380 bpi)"},
    {0x0f, "QIC-120 (GCR 10000 bpi)"},
    {0x10, "QIC-150/250 (GCR 10000 bpi)"},
    {0x11, "QIC-320/525 (GCR 16000 bpi)"},
    {0x12, "QIC-1350 (RLL 51667 bpi)"},
    {0x13, "DDS (61000 bpi)"},
    {0x14, "EXB-8200 (RLL 43245 bpi)"},
    {0x15, "EXB-8500 (RLL 45434 bpi)"},
    {0x16, "MFM 10000 bpi"},
    {0x17, "MFM 42500 bpi"},
    {0x24, "DDS-2"},
    {140, "EXB-8505 compressed"},
    {144, "EXB-8205 compressed"},
    {-1, NULL}};


static struct booleans {
    char *name;
    unsigned long bitmask;
    char *expl;
} boolean_tbl[] = {
    {"buffer-writes", MT_ST_BUFFER_WRITES, "buffered writes"},
    {"async-writes",  MT_ST_ASYNC_WRITES,  "asynchronous writes"},
    {"read-ahead",    MT_ST_READ_AHEAD,    "read-ahead for fixed block size"},
    {"debug",         MT_ST_DEBUGGING,     "debugging (if compiled into driver)"},
    {"two-fms",       MT_ST_TWO_FM,        "write two filemarks when file closed"},
    {"fast-eod",      MT_ST_FAST_MTEOM, "space directly to eod (and lose file number)"},
    {"auto-lock",     MT_ST_AUTO_LOCK,     "automatically lock/unlock drive door"},
    {"def-writes",    MT_ST_DEF_WRITES,    "the block size and density are for writes"},
    {"can-bsr",       MT_ST_CAN_BSR,       "drive can space backwards well"},
    {"no-blklimits",  MT_ST_NO_BLKLIMS,    "drive doesn't support read block limits"},
    {"can-partitions",MT_ST_CAN_PARTITIONS,"drive can handle partitioned tapes"},
    {"scsi2logical",  MT_ST_SCSI2LOGICAL,  "logical block addresses used with SCSI-2"},
    {NULL, 0}};


	static void
usage(int explain)
{
    int ind;
    char line[100];

    fprintf(stderr, "usage: mt [-v] [-h] [ -f device ] command [ count ]\n");
    if (explain) {
	for (ind=0; com[ind].c_name != NULL; ) {
	    if (ind == 0)
		strcpy(line, "commands: ");
	    else
		strcpy(line, "          ");
	    for ( ; com[ind].c_name != NULL; ind++) {
		strcat(line, com[ind].c_name);
		if (com[ind+1].c_name != NULL)
		    strcat(line, ", ");
		else
		    strcat(line, ".");
		if (strlen(line) >= 70) {
		    fprintf(stderr, "%s\n", line);
		    ind++;
		    break;
		}
	    }
	}
    }
}



	static void
print_densities()
{
    int i;

    printf("Some SCSI tape density codes:\ncode   explanation\n");
    for (i=0; density_tbl[i].code >= 0; i++)
	printf("0x%02x   %s\n", density_tbl[i].code, density_tbl[i].name);
}


	static void
print_booleans(void)
{
    int i;

    fprintf(stderr, "The implemented property names are:\n");
    for (i=0; boolean_tbl[i].name != NULL; i++)
	fprintf(stderr, "  %9s -> %s\n", boolean_tbl[i].name,
		boolean_tbl[i].expl);
}



int mtfd;
struct mtop mt_com;
struct mtget mt_status;
struct mtpos mt_pos;
char *tape;

int main(int argc, char **argv)
{
    int c_code, c_ronly, i, an, len;
    char *cp;
    struct commands *comp, *comp2;

    if (argc >= 2 && equal(argv[1], "-v")) {
	printf("mt-st v. 0.4\n");
	exit(0);
    }

    if (argc >= 2 && equal(argv[1], "-h")) {
	usage(1);
	exit(0);
    }

    if (argc > 2 && (equal(argv[1], "-t") || equal(argv[1], "-f"))) {
	argc -= 2;
	tape = argv[2];
	argv += 2;
    } else
	if ((tape = getenv("TAPE")) == NULL)
	    tape = DEFTAPE;
    if (argc < 2) {
	usage(0);
	exit(1);
    }
    cp = argv[1];

    len = strlen(cp);
    for (comp = com; comp->c_name != NULL; comp++)
	if (strncmp(cp, comp->c_name, len) == 0)
	    break;
    if (comp->c_name == NULL) {
	fprintf(stderr, "mt: unknown command \"%s\"\n", cp);
	usage(1);
	exit(1);
    }
    if (len != strlen(comp->c_name)) {
	for (comp2 = comp + 1; comp2->c_name != NULL; comp2++)
	    if (strncmp(cp, comp2->c_name, len) == 0)
		break;
	if (comp2->c_name != NULL) {
	    fprintf(stderr, "mt: ambiguous command \"%s\"\n", cp);
	    usage(1);
	    exit(1);
	}
    }
    c_code = comp->c_code;
    c_ronly = comp->c_ronly;

    if (c_code == 1000) {
	print_densities();
	exit(0);
    }
    if ((mtfd = open(tape, c_ronly ? O_RDONLY : O_RDWR)) < 0) {
	perror(tape);
	exit(1);
    }
    if (c_code == 1001) {
	if (!do_dat_compression(mtfd, (argc > 2), argv[2]))
	    exit(2);
    }
    else if (c_code >= 1002 && c_code <= 1004) {
	mt_com.mt_op = MTSETDRVBUFFER;
	if (argc < 3)
	    mt_com.mt_count = 0;
	else if (isdigit(*argv[2]))
	    mt_com.mt_count = (argc > 2 ? strtol(argv[2], NULL, 0) : 1) &
	    ~MT_ST_OPTIONS;
	else
	    for (an = 2, mt_com.mt_count = 0; an < argc; an++) {
		len = strlen(argv[an]);
		for (i=0; boolean_tbl[i].name != NULL; i++)
		    if (!strncmp(boolean_tbl[i].name, argv[an], len)) {
			mt_com.mt_count |= boolean_tbl[i].bitmask;
			break;
		    }
		if (boolean_tbl[i].name == NULL) {
		    fprintf(stderr, "Illegal property name '%s'.\n", argv[an]);
		    print_booleans();
		    exit(1);
		}
		if (len != strlen(boolean_tbl[i].name))
		    for (i++ ; boolean_tbl[i].name != NULL; i++)
			if (!strncmp(boolean_tbl[i].name, argv[an], len)) {
			    fprintf(stderr, "Property name '%s' ambiguous.\n",
				    argv[an]);
			    exit(1);
			}
	    }

	switch (c_code) {
	case 1002:
	    mt_com.mt_count |= MT_ST_BOOLEANS;
	    break;
	case 1003:
	    mt_com.mt_count |= MT_ST_SETBOOLEANS;
	    break;
	case 1004:
	    mt_com.mt_count |= MT_ST_CLEARBOOLEANS;
	    break;
	}
	if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
	    perror(tape);
	    exit(2);
	}
    }
    else if (c_code == 1006) {
	mt_com.mt_op = MTSETPART;
	mt_com.mt_count = (argc > 2 ? strtol(argv[2], NULL, 0) : 0);
	if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
	    perror(tape);
	    exit(2);
	}
	mt_com.mt_op = MTSEEK;
	mt_com.mt_count = (argc > 3 ? strtol(argv[3], NULL, 0) : 0);
	if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
	    perror(tape);
	    exit(2);
	}
    }
    else if (c_code == MTTELL) {
	if (ioctl(mtfd, MTIOCPOS, (char *)&mt_pos) < 0) {
	    perror(tape);
	    exit(2);
	}
	printf("At block %ld.\n", mt_pos.mt_blkno);
    }
    else if (c_code == MTSETDRVBUFFER) {
	mt_com.mt_op = c_code;
	mt_com.mt_count = (argc > 2 ? strtol(argv[2], NULL, 0) : 1);
	if ((comp->c_count_bits & MT_ST_OPTIONS) == MT_ST_DEF_OPTIONS)
	    mt_com.mt_count &= 0xfffff;
	else
	    mt_com.mt_count &= 0xfffffff;
	mt_com.mt_count |= comp->c_count_bits;
	if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
	    perror(tape);
	    exit(2);
	}
    }
    else if (c_code != MTNOP) {
	mt_com.mt_op = comp->c_code;
	mt_com.mt_count = (argc > 2 ? strtol(argv[2], NULL, 0) : 1);
	mt_com.mt_count |= comp->c_count_bits;
	if (mt_com.mt_count < 0) {
	    fprintf(stderr, "mt: negative repeat count\n");
	    exit(1);
	}
	if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
	    perror(tape);
	    exit(2);
	}
    } else {
	if (ioctl(mtfd, MTIOCGET, (char *)&mt_status) < 0) {
	    perror(tape);
	    exit(2);
	}
	status(&mt_status);
    }
    return 0;
}

struct tape_desc {
	short	t_type;		/* type of magtape device */
	char	*t_name;	/* printing name */
} tapes[] = {
	{ MT_ISSCSI1,	"SCSI 1" },
	{ MT_ISSCSI2,   "SCSI 2" },
	{ 0 }
};


/*
 * Interpret the status buffer returned
 */
void status(struct mtget *bp)
{
    struct tape_desc *mt;
    int dens, i;
    char *density;

    for (mt = tapes; mt->t_type; mt++)
	if (mt->t_type == bp->mt_type)
	    break;
    if (mt->t_type == 0) {
	if (bp->mt_type & 0x800000)
	    printf ("qic-117 drive type = 0x%05lx, ", bp->mt_type & 0x1ffff);
	else
	    printf("Unknown tape drive type (type code %ld)\n", bp->mt_type);
	printf("File number=%d, block number=%d.\n",
	       bp->mt_fileno, bp->mt_blkno);
	printf("mt_resid: %ld, mt_erreg: 0x%lx\n",
	       bp->mt_resid, bp->mt_erreg);
	printf("mt_dsreg: 0x%lx, mt_gstat: 0x%lx\n",
	       bp->mt_dsreg, bp->mt_gstat);
    }
    else {
	printf("%s tape drive:\n", mt->t_name);
	if (mt->t_type == MT_ISSCSI2)
	    printf("File number=%d, block number=%d, partition=%ld.\n",
		   bp->mt_fileno, bp->mt_blkno, (bp->mt_resid & 0xff));
	else
	    printf("File number=%d, block number=%d.\n",
		   bp->mt_fileno, bp->mt_blkno);
	if (bp->mt_type == MT_ISSCSI1 ||
	    bp->mt_type == MT_ISSCSI2) {
	    dens = (bp->mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT;
	    density = "unknown";
	    for (i=0; density_tbl[i].code >= 0; i++)
		if (density_tbl[i].code == dens) {
		    density = density_tbl[i].name;
		    break;
		}
	    printf("Tape block size %ld bytes. Density code 0x%x (%s).\n",
		   ((bp->mt_dsreg & MT_ST_BLKSIZE_MASK) >> MT_ST_BLKSIZE_SHIFT),
		   dens, density);

	    printf("Soft error count since last status=%ld\n",
		   (bp->mt_erreg & MT_ST_SOFTERR_MASK) >> MT_ST_SOFTERR_SHIFT);
	}
    }

    printf("General status bits on (%lx):\n", bp->mt_gstat);
    if (GMT_EOF(bp->mt_gstat))
	printf(" EOF");
    if (GMT_BOT(bp->mt_gstat))
	printf(" BOT");
    if (GMT_EOT(bp->mt_gstat))
	printf(" EOT");
    if (GMT_SM(bp->mt_gstat))
	printf(" SM");
    if (GMT_EOD(bp->mt_gstat))
	printf(" EOD");
    if (GMT_WR_PROT(bp->mt_gstat))
	printf(" WR_PROT");
    if (GMT_ONLINE(bp->mt_gstat))
	printf(" ONLINE");
    if (GMT_D_6250(bp->mt_gstat))
	printf(" D_6250");
    if (GMT_D_1600(bp->mt_gstat))
	printf(" D_1600");
    if (GMT_D_800(bp->mt_gstat))
	printf(" D_800");
    if (GMT_DR_OPEN(bp->mt_gstat))
	printf(" DR_OPEN");	  
    if (GMT_IM_REP_EN(bp->mt_gstat))
	printf(" IM_REP_EN");
    printf("\n");
}


/*** Get and set the DAT compression (Mode Page 15) ***/

#if 0
#include <linux/scsi.h>
#else
/* The file scsi.h is not visible to the users after 1.3.98. The necessary
   SCSI constants are defined here (not so bad because these are constants
   defined in the SCSI standard. */
#define MODE_SELECT           0x15
#define MODE_SENSE            0x1a
#endif

#ifndef SCSI_IOCTL_SEND_COMMAND
/* This has not been defined in a public include file */
#define SCSI_IOCTL_SEND_COMMAND 1
#endif

#define COMPRESSION_PAGE        0x0f
#define COMPRESSION_PAGE_LENGTH 16

#define IOCTL_HEADER_LENGTH 8
#define CMD_6_LENGTH        6
#define MODE_HEADER_OFFSET  (IOCTL_HEADER_LENGTH + CMD_6_LENGTH)
#define MODE_HEADER_LENGTH  4

#define DCE_MASK  0x80
#define DCC_MASK  0x40
#define RED_MASK  0x60


static int read_mode_page(int fn, int page, int length, unsigned char *buffer,
			  int do_mask)
{
    int result, *ip;
    unsigned char tmpbuffer[30], *cmd;

    memset(tmpbuffer, 0, IOCTL_HEADER_LENGTH + CMD_6_LENGTH);
    ip = (int *)&(tmpbuffer[0]);
    *ip = 0;
    *(ip+1) = length + MODE_HEADER_LENGTH;

    cmd = &(tmpbuffer[8]);
    cmd[0] = MODE_SENSE;
    cmd[1] = 8;
    cmd[2] = page;
    if (do_mask)
	cmd[2] |= 0x40;  /* Get changeable parameter mask */
    cmd[4] = length + MODE_HEADER_LENGTH;

    result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, tmpbuffer);
    if (result) {
	if (getuid() != 0)
	    fprintf(stderr, "%s: This operation is only allowed for root.\n",
		    tape);
	else
	    fprintf(stderr, "%s: Can't read SCSI mode page.\n", tape);
	return 0;
    }
    memcpy(buffer, tmpbuffer + IOCTL_HEADER_LENGTH, length + MODE_HEADER_LENGTH);
    return 1;
}


static int write_mode_page(int fn, int page, int length, unsigned char *buffer)
{
    int result, *ip;
    unsigned char tmpbuffer[40], *cmd;

    memset(tmpbuffer, 0, IOCTL_HEADER_LENGTH + CMD_6_LENGTH);
    ip = (int *)&(tmpbuffer[0]);
    *ip = length + MODE_HEADER_LENGTH;
    *(ip+1) = 0;

    cmd = &(tmpbuffer[IOCTL_HEADER_LENGTH]);
    cmd[0] = MODE_SELECT;
    cmd[1] = 0x10;
    cmd[4] = length + MODE_HEADER_LENGTH;

    memcpy(tmpbuffer + MODE_HEADER_OFFSET, buffer, length + MODE_HEADER_LENGTH);
    tmpbuffer[MODE_HEADER_OFFSET] = 0;      /* reserved data length */
    tmpbuffer[MODE_HEADER_OFFSET + 1] = 0;  /* reserved meadia type */
    tmpbuffer[MODE_HEADER_OFFSET + MODE_HEADER_LENGTH] &= 0x3f; 
                                 /* reserved bits in page code byte */

    result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, tmpbuffer);
    if (result) {
	fprintf(stderr, "%s: Can't write mode page.\n", tape);
	return 0;
    }
    return 1;
}


static int do_dat_compression(int fn, int alter, char *new)
{
    int i;
    unsigned char buffer[30], mask[30];

    if (!read_mode_page(fn, COMPRESSION_PAGE, COMPRESSION_PAGE_LENGTH,
			buffer, 0)) {
	fprintf(stderr, "%s: Can't read compression mode page.\n", tape);
	return 0;
    }

    if (alter) {
	if (!read_mode_page(fn, COMPRESSION_PAGE, COMPRESSION_PAGE_LENGTH,
			    mask, 1)) {
	    fprintf(stderr, "%s: Can't read changeable parameter page.\n", tape);
	    return 0;
	}

	if ((buffer[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
	    fprintf(stderr, "%s: Drive does not support compression.\n", tape);
	    return 0;
	}
	if ((mask[MODE_HEADER_LENGTH + 2] & DCE_MASK) == 0) {
	    fprintf(stderr, "%s: Compression not controllable with software.\n", tape);
	    return 0;
	}

	if (((buffer[MODE_HEADER_LENGTH + 3] & RED_MASK) >> 5) == 2) {
	    if (ioctl(mtfd, MTIOCPOS, (char *)&mt_pos) >= 0
		&& mt_pos.mt_blkno != 0) {
		fprintf(stderr, "%s: Can't change compression here. Please rewind.\n",
			tape);
		return 0;
	    }
	}

	if (*new == '0')
	    buffer[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK;
	else
	    buffer[MODE_HEADER_LENGTH + 2] |= DCE_MASK;

	if (!write_mode_page(fn, COMPRESSION_PAGE, COMPRESSION_PAGE_LENGTH,
			     buffer)) {
	    for (i=2; i < 16; i++)
		buffer[MODE_HEADER_LENGTH + i] &= mask[MODE_HEADER_LENGTH + i];
	    if (!write_mode_page(fn, COMPRESSION_PAGE, COMPRESSION_PAGE_LENGTH,
				 buffer)) {
		fprintf(stderr, "%s: Writing mode SCSI mode page failed.\n", tape);
		fprintf(stderr, "%s: Compression mode not changed.\n", tape);
		return 0;
	    }
	}
	if (!read_mode_page(fn, COMPRESSION_PAGE, COMPRESSION_PAGE_LENGTH,
			    buffer, 0)) {  /* Re-read to check */
	    fprintf(stderr, "%s: Re-read of the compression page failed.\n", tape);
	    return 0;
	}
    }

    if (buffer[4+2] & 0x80)
	printf("Compression on.\n");
    else
	printf("Compression off.\n");

    close(fn);
    return 1;
}
