/* This program initializes Linux SCSI tape drives using the
   inquiry data from the devices and a text database.

   Copyright 1996-2001 by Kai Mäkisara (email Kai.Makisara@metla.fi)
   Distribution of this program is allowed according to the
   GNU Public Licence.

   Last modified: Thu Nov  8 21:13:48 2001 by makisara@kai.makisara.local
*/

#ifndef lint
static char rcsid[] = "$Id: /usr2/users/makisara/src/sys/mt-st-0.7/stinit.c at Thu Nov  8 21:13:48 2001 by makisara@kai.makisara.local$";
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/sysmacros.h>
#include <linux/major.h>

#include "mtio.h"

#ifndef FALSE
#define TRUE 1
#define FALSE 0
#endif
#define SKIP_WHITE(p) for ( ; *p == ' ' || *p == '\t'; p++)

#define VERSION "0.7"

typedef struct _modepar_tr {
    int defined;
    int blocksize;
    int density;
    int buffer_writes;
    int async_writes;
    int read_ahead;
    int two_fm;
    int compression;
    int auto_lock;
    int fast_eod;
    int can_bsr;
    int no_blklimits;
    int can_partitions;
    int scsi2logical;
    int sysv;
    int defs_for_writes;
} modepar_tr;

typedef struct _devdef_tr {
    int do_rewind;
    int drive_buffering;
    int timeout;
    int long_timeout;
    int cleaning;
    int nowait;
    modepar_tr modedefs[4];
} devdef_tr;


#define DEFMAX 2048
#define LINEMAX 256

#define MAX_TAPES 32
#define NBR_MODES 4


static int verbose = 0;

/* The device directories being searched */
typedef struct 
{
    const char *dir;
    int selective_scan;
} devdir;
static devdir devdirs[] = { {"/dev/scsi", 0}, {"/dev", 1}, {NULL, 0}};

#define DEVFS_PATH    "/dev/tapes"
#define DEVFS_TAPEFMT DEVFS_PATH "/tape%d"

/* The partial names of the tape devices being included in the
   search in selective scan */
static char *tape_name_bases[] = {
    "st", "nst", "rmt", "nrmt", "tape", NULL};

/* The list of standard definition files being searched */
static char *std_databases[] = {
    "/etc/stinit.def",
    NULL};

	static FILE *
open_database(char *base)
{
    int i;
    FILE *f;

    if (base != NULL) {
	if ((f = fopen(base, "r")) == NULL)
	    fprintf(stderr, "stinit: Can't find SCSI tape database '%s'.\n",
		    base);
	return f;
    }

    for (i=0; std_databases[i] != NULL; i++) {
	if (verbose > 1)
	    fprintf(stderr, "Trying to open database '%s'.\n", std_databases[i]);
	if ((f = fopen(std_databases[i], "r")) != NULL) {
	    if (verbose > 1)
		fprintf(stderr, "Open succeeded.\n");
	    return f;
	}
    }
    fprintf(stderr, "Can't find the tape characteristics database.\n");

    return NULL;
}


	static char *
find_string(char *s, char *target, char *buf, int buflen)
{
    int have_arg;
    char *cp, *cp2, c, *argp;

    if (buf != NULL && buflen > 0)
	*buf = '\0';

    for ( ; *s != '\0'; ) {
	SKIP_WHITE(s);
	if (isalpha(*s)) {
	    for (cp=s ; isalnum(*cp) || *cp == '-'; cp++)
		;
	    cp2 = cp;
	    SKIP_WHITE(cp);
	    if (*cp == '=') {
		cp++;
		SKIP_WHITE(cp);
		if (*cp == '"') {
		    cp++;
		    for (cp2=cp; *cp2 != '"' && *cp2 != '\0'; cp2++)
			;
		}
		else
		    for (cp2=cp+1; isalnum(*cp2) || *cp2 == '-'; cp2++)
			;
		if (cp2 == '\0')
		    return NULL;
		have_arg = TRUE;
		argp = cp;
	    }
	    else {
		have_arg = FALSE;
		argp = "1";
	    }

	    if (!strncmp(target, s, strlen(target))) {
		c = *cp2;
		*cp2 = '\0';
		if (buf == NULL)
		    buf = strdup(argp);
		else {
		    if (strlen(argp) < buflen)
			strcpy(buf, argp);
		    else {
			strncpy(buf, argp, buflen);
			buf[buflen - 1] = '\0';
		    }
		}
		if (have_arg && c == '"')
		    cp2++;
		else
		    *cp2 = c;
		if (*cp2 != '\0')
		    memmove(s, cp2, strlen(cp2) + 1);
		else
		    *s = '\0';
		return buf;
	    }
	    s = cp2;
	}
	else
	    for ( ; *s != '\0' && *s != ' ' && *s != '\t'; s++)
		;
    }
    return NULL;
}

	static int
next_block(FILE *dbf, char *buf, int buflen, int limiter)
{
    int len;
    char *cp, *bp;
    static char lbuf[LINEMAX];

    if (limiter == 0) {  /* Restart */
	rewind(dbf);
	lbuf[0] = '\0';
	return TRUE;
    }

    for (len = 0 ; ; ) {
	bp = buf + len;
	if ((cp = strchr(lbuf, limiter)) != NULL) {
	    *cp = '\0';
	    strcpy(bp, lbuf);
	    cp++;
	    SKIP_WHITE(cp);
	    memmove(lbuf, cp, strlen(cp) + 1);
	    return TRUE;
	}
	if (len + strlen(lbuf) >= DEFMAX) {
	    fprintf(stderr, "Too long definition: '%s'\n", buf);
	    return FALSE;
	}
	cp = lbuf;
	SKIP_WHITE(cp);
	strcpy(bp, cp);
	strcat(bp, " ");
	len += strlen(cp) + 1;
	if (fgets(lbuf, LINEMAX, dbf) == NULL)
	    return FALSE;
	if ((cp = strchr(lbuf, '#')) != NULL)
	    *cp = '\0';
	else
	    lbuf[strlen(lbuf) - 1] = '\0';
    }
}


	static int
find_pars(FILE *dbf, char *company, char *product, char *rev, devdef_tr *defs,
	  int parse_only)
{
    int i, mode, modes_defined, errors;
    char line[LINEMAX], defstr[DEFMAX], comdef[DEFMAX];
    char tmpcomp[LINEMAX], tmpprod[LINEMAX], tmprev[LINEMAX], *cp, c, *t;
    char *nextdef, *curdef, *comptr;
    static int call_nbr = 0;

    call_nbr++;
    defs->drive_buffering = (-1);
    defs->timeout = (-1);
    defs->long_timeout = (-1);
    defs->cleaning = (-1);
    defs->nowait = (-1);
    for (i=0; i < NBR_MODES; i++) {
	defs->modedefs[i].defined = FALSE;
	defs->modedefs[i].blocksize = (-1);
	defs->modedefs[i].density = (-1);
	defs->modedefs[i].buffer_writes = (-1);
	defs->modedefs[i].async_writes = (-1);
	defs->modedefs[i].read_ahead = (-1);
	defs->modedefs[i].two_fm = (-1);
	defs->modedefs[i].compression = (-1);
	defs->modedefs[i].auto_lock = (-1);
	defs->modedefs[i].fast_eod = (-1);
	defs->modedefs[i].can_bsr = (-1);
	defs->modedefs[i].no_blklimits = (-1);
	defs->modedefs[i].can_partitions = (-1);
	defs->modedefs[i].scsi2logical = (-1);
	defs->modedefs[i].sysv = (-1);
	defs->modedefs[i].defs_for_writes = (-1);
    }
    next_block(dbf, NULL, 0, 0);

    /* Find matching inquiry block */
    for (errors=0 ; ; ) {

	if (!next_block(dbf, defstr, DEFMAX, '{'))
	    break;

	find_string(defstr, "manuf", tmpcomp, LINEMAX);
	find_string(defstr, "model", tmpprod, LINEMAX);
	find_string(defstr, "rev", tmprev, LINEMAX);

	if (!next_block(dbf, defstr, DEFMAX, '}')) {
	    fprintf(stderr,
		    "End of definition block not found for ('%s', '%s', '%s').\n",
		    tmpcomp, tmpprod, tmprev);
	    return FALSE;
	}

	if (!parse_only) {
	    if (tmpcomp[0] != '\0' &&
		strncmp(company, tmpcomp, strlen(tmpcomp)))
		continue;
	    if (tmpprod[0] != '\0' &&
		strncmp(product, tmpprod, strlen(tmpprod)))
		continue;
	    if (tmprev[0] != '\0' &&
		strncmp(rev, tmprev, strlen(tmprev)))
		continue;
	}
	else if (verbose > 0)
	    printf("\nParsing modes for ('%s', '%s', '%s').\n",
		   tmpcomp, tmpprod, tmprev);

	/* Block found, get the characteristics */
	for (nextdef=defstr; *nextdef != '\0' &&
		 (*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
	    ;
	c = *nextdef;
	*nextdef = '\0';
	strcpy(comdef, defstr);
	*nextdef = c;
	comptr = comdef;
	SKIP_WHITE(comptr);

	for ( ; *nextdef != '\0'; ) {
	    curdef = nextdef;
	    SKIP_WHITE(curdef);
	    for (nextdef++ ; *nextdef != '\0' &&
		 (*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
		;
	    c = *nextdef;
	    *nextdef = '\0';

	    mode = strtol(curdef + 4, &cp, 0) - 1;
	    if (mode < 0 || mode >= NBR_MODES) {
		fprintf(stderr,
			"Illegal mode for ('%s', '%s', '%s'):\n'%s'\n",
			tmpcomp, tmpprod, tmprev, curdef);
		*nextdef = c;
		errors++;
		continue;
	    }

	    strcpy(defstr, comptr);
	    strcat(defstr, cp);
	    *nextdef = c;

	    if (verbose > 1)
		fprintf(stderr, "Mode %d definition: %s\n", mode + 1, defstr);

	    if ((t = find_string(defstr, "disab", line, LINEMAX)) != NULL &&
		strtol(t, NULL, 0) != 0) {
		defs->modedefs[mode].defined = FALSE;
		continue;
	    }

	    if ((t = find_string(defstr, "drive-", line, LINEMAX)) != NULL)
		defs->drive_buffering = strtol(t, NULL, 0);		
	    if ((t = find_string(defstr, "timeout", line, LINEMAX)) != NULL)
		defs->timeout = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "long-time", line, LINEMAX)) != NULL)
		defs->long_timeout = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "clean", line, LINEMAX)) != NULL)
		defs->cleaning = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "no-w", line, LINEMAX)) != NULL)
		defs->nowait = strtol(t, NULL, 0);

	    defs->modedefs[mode].defined = TRUE;
	    if ((t = find_string(defstr, "block", line, LINEMAX)) != NULL)
		defs->modedefs[mode].blocksize = strtol(t, NULL, 0);		
	    if ((t = find_string(defstr, "dens", line, LINEMAX)) != NULL)
		defs->modedefs[mode].density = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "buff", line, LINEMAX)) != NULL)
		defs->modedefs[mode].buffer_writes = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "async", line, LINEMAX)) != NULL)
		defs->modedefs[mode].async_writes = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "read", line, LINEMAX)) != NULL)
		defs->modedefs[mode].read_ahead = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "two", line, LINEMAX)) != NULL)
		defs->modedefs[mode].two_fm = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "comp", line, LINEMAX)) != NULL)
		defs->modedefs[mode].compression = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "auto", line, LINEMAX)) != NULL)
		defs->modedefs[mode].auto_lock = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "fast", line, LINEMAX)) != NULL)
		defs->modedefs[mode].fast_eod = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "can-b", line, LINEMAX)) != NULL)
		defs->modedefs[mode].can_bsr = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "noblk", line, LINEMAX)) != NULL)
		defs->modedefs[mode].no_blklimits = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "can-p", line, LINEMAX)) != NULL)
		defs->modedefs[mode].can_partitions = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "scsi2", line, LINEMAX)) != NULL)
		defs->modedefs[mode].scsi2logical = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "sysv", line, LINEMAX)) != NULL)
		defs->modedefs[mode].sysv = strtol(t, NULL, 0);
	    if ((t = find_string(defstr, "defs-for-w", line, LINEMAX)) != NULL)
		defs->modedefs[mode].defs_for_writes = strtol(t, NULL, 0);

	    for (t=defstr; *t == ' ' || *t == '\t'; t++)
		;
	    if (*t != '\0' && call_nbr <= 1) {
		fprintf(stderr,
			"Warning: errors in definition for ('%s', '%s', '%s'):\n%s\n",
			tmpcomp, tmpprod, tmprev, defstr);
		errors++;
	    }
	}
    }

    if (parse_only) {
	if (verbose > 0)
	    printf("\n");
	printf("Definition parse completed. ");
	if (errors) {
	    printf("Errors found!\n");
	    return FALSE;
	}
	else {
	    printf("No errors found.\n");
	    return TRUE;
	}
    }
    else {
	for (i=modes_defined=0; i < NBR_MODES; i++)
	    if (defs->modedefs[i].defined)
		modes_defined++;
	if (modes_defined == 0) {
	    fprintf(stderr,
		    "Warning: No modes in definition for ('%s', '%s', '%s').\n",
		    tmpcomp, tmpprod, tmprev);
	    errors++;
	}
    }

    if (modes_defined)
	return TRUE;
    return FALSE;
}


#define INQUIRY 0x12

#ifndef SCSI_IOCTL_SEND_COMMAND
#define SCSI_IOCTL_SEND_COMMAND 1
#endif
#define IOCTL_HEADER_LENGTH 8

	static int
do_inquiry(char *tname, char *company, char *product, char *rev, int print_non_found)
{
    int fn;
    int result, *ip, i;
#define BUFLEN 256
    unsigned char buffer[BUFLEN], *cmd;

    if ((fn = open(tname, O_RDONLY | O_NONBLOCK)) < 0) {
	if (print_non_found || verbose > 0) {
	    if (errno == ENXIO)
		fprintf(stderr, "Device '%s' not found by kernel.\n", tname);
	    else
		fprintf(stderr, "Can't open tape device '%s' (errno %d).\n",
			tname, errno);
	}
	return FALSE;
    }

    memset(buffer, 0, BUFLEN);
    ip = (int *)&(buffer[0]);
    *ip = 0;
    *(ip+1) = BUFLEN - 13;

    cmd = &(buffer[8]);
    cmd[0] = INQUIRY;
    cmd[4] = 200;

    result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, buffer);
    if (result) {
	close(fn);
	fprintf(stderr,
		"The SCSI INQUIRY for device '%s' failed (power off?).\n",
		tname);
	return FALSE;
    }

    memcpy(company, buffer + IOCTL_HEADER_LENGTH + 8, 8);
    for (i=8; i > 0 && company[i-1] == ' '; i--)
	;
    company[i] = '\0';
    memcpy(product, buffer + IOCTL_HEADER_LENGTH + 16, 16);
    for (i=16; i > 0 && product[i-1] == ' '; i--)
	;
    product[i] = '\0';
    memcpy(rev, buffer + IOCTL_HEADER_LENGTH + 32, 4);
    for (i=4; i > 0 && rev[i-1] == ' '; i--)
	;
    rev[i] = '\0';

    close(fn);
    return TRUE;
}


	static int
tapenum(char *name)
{
    int dev;
    struct dirent *dent;
    DIR *dirp;
    char tmpname[PATH_MAX];
    const char *dn;
    const devdir *dvd;
    struct stat statbuf;

    if (strchr(name, '/') != NULL) {  /* Complete name */
	if (stat(name, &statbuf) != 0) {
	    fprintf(stderr, "Can't stat '%s'.\n", name);
	    return (-1);
	}
	if (!S_ISCHR(statbuf.st_mode) ||
	    major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
	    return (-1);
	dev = minor(statbuf.st_rdev) & 31;
	return dev;
    }
    else { /* Search from the device directories */
	for (dvd=devdirs; dvd->dir != NULL; dvd++) {
	    dn = dvd->dir;
	    if ((dirp = opendir(dn)) == NULL)
	      continue;

	    for ( ; (dent = readdir(dirp)) != NULL; )
		if (!strcmp(dent->d_name, name)) {
		    strcpy(tmpname, dn);
		    strcat(tmpname, "/");
		    strcat(tmpname, dent->d_name);
		    if (stat(tmpname, &statbuf) != 0) {
			fprintf(stderr, "Can't stat '%s'.\n", tmpname);
			continue;
		    }
		    if (!S_ISCHR(statbuf.st_mode) ||
			major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
			continue;
		    dev = minor(statbuf.st_rdev) & 31;
		    closedir(dirp);
		    return dev;
		}
	    closedir(dirp);
	}
    }

    return (-1);
}


	static int
accept_tape_name(char *name)
{
    char **npp;

    for (npp=tape_name_bases; *npp; npp++)
	if (!strncmp(name, *npp, strlen(*npp)))
	    return TRUE;
    return FALSE;
}


	static int
find_devfiles(int tapeno, char **names)
{
    int dev, mode, found;
    int non_rew[NBR_MODES];
    struct dirent *dent;
    DIR *dirp;
    char tmpname[PATH_MAX], devname[PATH_MAX];
    const char *dn;
    const devdir *dvd;
    devdir tmpdevdirs[2];
    struct stat statbuf;

    for (found=0; found < NBR_MODES; found++) {
	*names[found] = '\0';
	non_rew[found] = FALSE;
    }

    dvd = devdirs;
    strcpy(tmpname, DEVFS_PATH);
    if (!stat(tmpname, &statbuf)) {
	if (S_ISDIR(statbuf.st_mode)) {  /* Assume devfs, one directory for each tape */
	    sprintf(devname, DEVFS_TAPEFMT, tapeno);
	    tmpdevdirs[0].dir = devname;
	    tmpdevdirs[0].selective_scan = FALSE;
	    tmpdevdirs[1].dir = NULL;
	    dvd = &tmpdevdirs[0];
	}
    }

    for (found=0; found < NBR_MODES && dvd->dir != NULL; dvd++) {
	dn = dvd->dir;
	if ((dirp = opendir(dn)) == NULL)
	    continue;

	for ( ; (dent = readdir(dirp)) != NULL; ) {
	    if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
		continue;
	    /* Ignore non-tape devices to avoid loading all the modules */
	    if (dvd->selective_scan && !accept_tape_name(dent->d_name))
		continue;
	    strcpy(tmpname, dn);
	    strcat(tmpname, "/");
	    strcat(tmpname, dent->d_name);
	    if (stat(tmpname, &statbuf) != 0) {
		fprintf(stderr, "Can't stat '%s'.\n", tmpname);
		continue;
	    }
	    if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) !=
		SCSI_TAPE_MAJOR)
		continue;
	    dev = minor(statbuf.st_rdev);
	    if ((dev & 31) != tapeno)
		continue;
	    mode = (dev & 127) >> 5;
	    if (non_rew[mode])
		continue;
	    if (*names[mode] == '\0')
		found++;
	    strcpy(names[mode], tmpname);
	    non_rew[mode] = (dev & 128) != 0;
	}
	closedir(dirp);
    }

    return (found > 0);
}


	static int
set_defs(devdef_tr *defs, char **fnames)
{
    int i, tape;
    int clear_set[2];
    struct mtop op;

    for (i=0; i < NBR_MODES; i++) {
	if (*fnames[i] == '\0' || !defs->modedefs[i].defined)
	    continue;

	if ((tape = open(fnames[i], O_RDONLY | O_NONBLOCK)) < 0) {
	    fprintf(stderr, "Can't open the tape device '%s' for mode %d.\n",
		    fnames[i], i);
	    return FALSE;
	}

	if (i == 0) {
	    if (defs->do_rewind) {
		op.mt_op = MTREW;
		op.mt_count = 1;
		ioctl(tape, MTIOCTOP, &op);  /* Don't worry about result */
	    }

	    if (defs->drive_buffering >= 0) {
		op.mt_op = MTSETDRVBUFFER;
		op.mt_count = MT_ST_DEF_DRVBUFFER | defs->drive_buffering;
		if (ioctl(tape, MTIOCTOP, &op) != 0) {
		    fprintf(stderr, "Can't set drive buffering to %d.\n",
			    defs->drive_buffering);
		}
	    }

	    if (defs->timeout >= 0) {
		op.mt_op = MTSETDRVBUFFER;
		op.mt_count = MT_ST_SET_TIMEOUT | defs->timeout;
		if (ioctl(tape, MTIOCTOP, &op) != 0) {
		    fprintf(stderr, "Can't set device timeout %d s.\n",
			    defs->timeout);
		}
	    }

	    if (defs->long_timeout >= 0) {
		op.mt_op = MTSETDRVBUFFER;
		op.mt_count = MT_ST_SET_LONG_TIMEOUT | defs->long_timeout;
		if (ioctl(tape, MTIOCTOP, &op) != 0) {
		    fprintf(stderr, "Can't set device long timeout %d s.\n",
			    defs->long_timeout);
		}
	    }

	    if (defs->cleaning >= 0) {
		op.mt_op = MTSETDRVBUFFER;
		op.mt_count = MT_ST_SET_CLN | defs->cleaning;
		if (ioctl(tape, MTIOCTOP, &op) != 0) {
		    fprintf(stderr, "Can't set cleaning request parameter to %x\n",
			    defs->cleaning);
		}
	    }
	}

	op.mt_op = MTSETDRVBUFFER;

	clear_set[0] = clear_set[1] = 0;
	if (defs->nowait >= 0)
	    clear_set[defs->nowait != 0] |= MT_ST_NOWAIT;
	if (defs->modedefs[i].buffer_writes >= 0)
	    clear_set[defs->modedefs[i].buffer_writes != 0] |= MT_ST_BUFFER_WRITES;
	if (defs->modedefs[i].async_writes >= 0)
	    clear_set[defs->modedefs[i].async_writes != 0] |= MT_ST_ASYNC_WRITES;
	if (defs->modedefs[i].read_ahead >= 0)
	    clear_set[defs->modedefs[i].read_ahead != 0] |= MT_ST_READ_AHEAD;
	if (defs->modedefs[i].two_fm >= 0)
	    clear_set[defs->modedefs[i].two_fm != 0] |= MT_ST_TWO_FM;
	if (defs->modedefs[i].fast_eod >= 0)
	    clear_set[defs->modedefs[i].fast_eod != 0] |= MT_ST_FAST_MTEOM;
	if (defs->modedefs[i].auto_lock >= 0)
	    clear_set[defs->modedefs[i].auto_lock != 0] |= MT_ST_AUTO_LOCK;
	if (defs->modedefs[i].can_bsr >= 0)
	    clear_set[defs->modedefs[i].can_bsr != 0] |= MT_ST_CAN_BSR;
	if (defs->modedefs[i].no_blklimits >= 0)
	    clear_set[defs->modedefs[i].no_blklimits != 0] |= MT_ST_NO_BLKLIMS;
	if (defs->modedefs[i].can_partitions >= 0)
	    clear_set[defs->modedefs[i].can_partitions != 0] |= MT_ST_CAN_PARTITIONS;
	if (defs->modedefs[i].scsi2logical >= 0)
	    clear_set[defs->modedefs[i].scsi2logical != 0] |= MT_ST_SCSI2LOGICAL;
	if (defs->modedefs[i].sysv >= 0)
	    clear_set[defs->modedefs[i].sysv != 0] |= MT_ST_SYSV;
	if (defs->modedefs[i].defs_for_writes >= 0)
	    clear_set[defs->modedefs[i].defs_for_writes != 0] |= MT_ST_DEF_WRITES;

	if (clear_set[0] != 0) {
	    op.mt_count = MT_ST_CLEARBOOLEANS | clear_set[0];
	    if (ioctl(tape, MTIOCTOP, &op) != 0) {
		fprintf(stderr, "Can't clear the tape options (bits 0x%x, mode %d).\n",
			clear_set[0], i);
	    }
	}
	if (clear_set[1] != 0) {
	    op.mt_count = MT_ST_SETBOOLEANS | clear_set[1];
	    if (ioctl(tape, MTIOCTOP, &op) != 0) {
		fprintf(stderr, "Can't set the tape options (bits 0x%x, mode %d).\n",
			clear_set[1], i);
	    }
	}

	if (defs->modedefs[i].blocksize >= 0) {
	    op.mt_count = MT_ST_DEF_BLKSIZE | defs->modedefs[i].blocksize;
	    if (ioctl(tape, MTIOCTOP, &op) != 0) {
		fprintf(stderr, "Can't set blocksize %d for mode %d.\n",
			defs->modedefs[i].blocksize, i);
	    }
	}
	if (defs->modedefs[i].density >= 0) {
	    op.mt_count = MT_ST_DEF_DENSITY | defs->modedefs[i].density;
	    if (ioctl(tape, MTIOCTOP, &op) != 0) {
		fprintf(stderr, "Can't set density %x for mode %d.\n",
			defs->modedefs[i].density, i);
	    }
	}
	if (defs->modedefs[i].compression >= 0) {
	    op.mt_count = MT_ST_DEF_COMPRESSION | defs->modedefs[i].compression;
	    if (ioctl(tape, MTIOCTOP, &op) != 0) {
		fprintf(stderr, "Can't set compression %d for mode %d.\n",
			defs->modedefs[i].compression, i);
	    }
	}

	close(tape);
    }
    return TRUE;
}


	static int
define_tape(int tapeno, FILE *dbf, devdef_tr *defptr, int print_non_found)
{
    int i;
    char company[10], product[20], rev[5], *tname, *fnames[NBR_MODES];

    if (verbose > 0)
	printf("\nstinit, processing tape %d\n", tapeno);

    if ((fnames[0] = calloc(NBR_MODES, PATH_MAX)) == NULL) {
	fprintf(stderr, "Can't allocate name buffers.\n");
	return FALSE;
    }
    for (i=1; i < NBR_MODES; i++)
	fnames[i] = fnames[i-1] + PATH_MAX;

    if (!find_devfiles(tapeno, fnames) ||
	*fnames[0] == '\0') {
	fprintf(stderr, "Can't find any device files for tape %d.\n", tapeno);
	free(fnames[0]);
	return FALSE;
    }
    if (verbose > 1)
	for (i=0; i < NBR_MODES; i++)
	    printf("Mode %d, name '%s'\n", i, fnames[i]);

    tname = fnames[0];
    if (!do_inquiry(tname, company, product, rev, print_non_found)) {
	free(fnames[0]);
	return FALSE;
    }
    if (verbose > 0)
	printf("The manufacturer is '%s', product is '%s', and revision '%s'.\n",
	       company, product, rev);

    if (!find_pars(dbf, company, product, rev, defptr, FALSE)) {
	fprintf(stderr, "Can't find defaults for tape number %d.\n", tapeno);
	free(fnames[0]);
	return FALSE;
    }

    if (!set_defs(defptr, fnames)) {
	free(fnames[0]);
	return FALSE;
    }

    free(fnames[0]);
    return TRUE;
}


	static char
usage(int retval)
{
    fprintf(stderr,
	    "Usage: stinit [-h] [-v] [-f dbname] [-p] [drivename_or_number ...]\n");
    exit(retval);
}


	int
main(int argc, char **argv)
{
    FILE *dbf = NULL;
    int argn;
    int tapeno, parse_only = FALSE;
    char *dbname = NULL;
    devdef_tr defs;

    defs.do_rewind = FALSE;
    for (argn=1; argn < argc && *argv[argn] == '-'; argn++) {
	if (*(argv[argn] + 1) == 'v')
	    verbose++;
	else if (*(argv[argn] + 1) == 'p')
	    parse_only = TRUE;
	else if (*(argv[argn] + 1) == 'h')
	    usage(0);
	else if (*(argv[argn] + 1) == 'r')
	    defs.do_rewind = TRUE;
	else if (*(argv[argn] + 1) == 'f') {
	    argn += 1;
	    if (argn >= argc)
		usage(1);
	    dbname = argv[argn];
	}
	else if (*(argv[argn] + 1) == '-' &&
		 *(argv[argn] + 2) == 'v') {
	    printf("stinit v. %s\n", VERSION);
	    exit(0);
	    break;
	}
	else
	    usage(1);
    }

    if ((dbf = open_database(dbname)) == NULL)
	return 1;  

    if (parse_only) {
	if (argc > argn)
	    fprintf(stderr, "Extra arguments on command line ignored.\n");
	if (!find_pars(dbf, "xyz", "xyz", "xyz", &defs, TRUE))
	    return 1;
	return 0;
    }

    if (argc > argn) {  /* Initialize specific drives */
	for ( ; argn < argc; argn++) {
	    if (*argv[argn] == '-') {
		usage(1);
		return 1; /* Never executed but makes gcc happy */
	    }
	    else if (isdigit(*argv[argn]))
		tapeno = strtol(argv[argn], NULL, 0);
	    else if ((tapeno = tapenum(argv[argn])) < 0) {
		fprintf(stderr, "Can't find tape number for name '%s'.\n",
			argv[argn]);
		continue;
	    }
	    if (!define_tape(tapeno, dbf, &defs, TRUE))
		fprintf(stderr, "Definition for '%s' failed.\n", argv[argn]);
	}
    }
    else {  /* Initialize all SCSI tapes */
	for (tapeno=0; tapeno < MAX_TAPES; tapeno++)
	    if (!define_tape(tapeno, dbf, &defs, FALSE)) {
		fprintf(stderr, "Initialized %d tape device%s.\n",
			tapeno, (tapeno != 1 ? "s" : ""));
		return 0;  /* Process tapes until failure */
	    }
    }

    return 0;
}
