--- /dev/null
+Only in .: .svn
+diff -ru ../../ibiblio/mt-st-0.9b/Makefile ./Makefile
+--- ../../ibiblio/mt-st-0.9b/Makefile Tue Aug 16 12:16:28 2005
++++ ./Makefile Sun Jul 30 05:59:14 2006
+@@ -1,29 +1,27 @@
++CC= mingw32-gcc
+ CFLAGS= -Wall -O2
+-SBINDIR= /sbin
+-BINDIR= /bin
+-MANDIR= /usr/share/man
++PREFIX=
++SBINDIR= $(PREFIX)/sbin
++BINDIR= $(PREFIX)/bin
++MANDIR= $(PREFIX)/man
+
+-all: mt stinit
++all: mt.exe
+
+-mt: mt.c
+- $(CC) $(CFLAGS) -o mt mt.c
++mt.exe: mt.c
++ $(CC) $(CFLAGS) -o mt.exe mt.c mtops.c
+
+-stinit: stinit.c
++stinit.exe: stinit.c
+ $(CC) $(CFLAGS) -o stinit stinit.c
+
+-install: mt stinit
+- install -s mt $(BINDIR)
++install: mt.exe
++ install -s mt.exe $(BINDIR)
+ install -c -m 444 mt.1 $(MANDIR)/man1
+ (if [ -f $(MANDIR)/man1/mt.1.gz ] ; then \
+ rm -f $(MANDIR)/man1/mt.1.gz; gzip $(MANDIR)/man1/mt.1; fi)
+- install -s stinit $(SBINDIR)
+- install -c -m 444 stinit.8 $(MANDIR)/man8
+- (if [ -f $(MANDIR)/man8/stinit.8.gz ] ; then \
+- rm -f $(MANDIR)/man8/stinit.8.gz; gzip $(MANDIR)/man8/stinit.8; fi)
+
+ dist: clean
+ (mydir=`basename \`pwd\``;\
+ cd .. && tar cvvf - $$mydir | gzip -9 > $${mydir}.tar.gz)
+
+ clean:
+- rm -f *~ \#*\# *.o mt stinit
++ rm -f *~ \#*\# *.o mt.exe stinit.exe
+Only in .: compat.h
+Only in .: mt
+diff -ru ../../ibiblio/mt-st-0.9b/mt.1 ./mt.1
+--- ../../ibiblio/mt-st-0.9b/mt.1 Sun Aug 21 11:53:50 2005
++++ ./mt.1 Sun Jul 30 05:16:07 2006
+@@ -48,20 +48,22 @@
+ files.
+ The tape is positioned on the first block of the next file.
+ .IP fsfm
+-Forward space
++Forward space past
+ .I count
+-files.
+-The tape is positioned on the last block of the previous file.
++file marks, then backward space one file record.
++This leaves the tape positioned on the last block of the file that is count-1
++files past the current file.
+ .IP bsf
+ Backward space
+ .I count
+ files.
+ The tape is positioned on the last block of the previous file.
+ .IP bsfm
+-Backward space
++Backward space past
+ .I count
+-files.
+-The tape is positioned on the first block of the next file.
++file marks, then forward space one file record.
++This leaves the tape positioned on the first block of the file that is count-1
++files before the current file.
+ .IP asf
+ The tape is positioned at the beginning of the
+ .I count
+diff -ru ../../ibiblio/mt-st-0.9b/mt.c ./mt.c
+--- ../../ibiblio/mt-st-0.9b/mt.c Sun Aug 21 11:48:06 2005
++++ ./mt.c Sun Jul 30 06:04:13 2006
+@@ -11,6 +11,12 @@
+ Last Modified: Sun Aug 21 21:48:06 2005 by kai.makisara
+ */
+
++#include <windows.h>
++#include <winioctl.h>
++#include <winsock.h>
++
++#define O_NONBLOCK 0
++
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+@@ -19,15 +25,17 @@
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <sys/types.h>
+-#include <sys/ioctl.h>
+
++#include "mtops.h"
+ #include "mtio.h"
+
++#define ioctl tape_ioctl
++
+ #ifndef DEFTAPE
+-#define DEFTAPE "/dev/tape" /* default tape device */
++#define DEFTAPE "Tape0" /* default tape device */
+ #endif /* DEFTAPE */
+
+-#define VERSION "0.9b"
++#define VERSION "0.9b-bacula"
+
+ typedef int (* cmdfunc)(/* int, struct cmdef_tr *, int, char ** */);
+
+@@ -143,12 +151,14 @@
+ FD_RDONLY, ONE_ARG, 0},
+ { "defcompression", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_COMPRESSION,
+ FD_RDONLY, ONE_ARG, 0},
++#if 0
+ { "stsetcln", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_CLN,
+ FD_RDONLY, ONE_ARG, 0},
+ { "sttimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_TIMEOUT,
+ FD_RDONLY, ONE_ARG, 0},
+ { "stlongtimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_LONG_TIMEOUT,
+ FD_RDONLY, ONE_ARG, 0},
++#endif
+ { "densities", 0, print_densities, 0, NO_FD, NO_ARGS,
+ 0 },
+ { "setpartition", MTSETPART, do_standard, 0, FD_RDONLY, ONE_ARG,
+@@ -211,13 +221,19 @@
+ {0x30, "AIT-1 or MLR3"},
+ {0x31, "AIT-2"},
+ {0x32, "AIT-3"},
+- {0x33, "SLR6"},
++ {0x33, "AIT-4 or SLR6"},
+ {0x34, "SLR100"},
++ {0x38, "AIT-E Turbo"},
++ {0x39, "AIT-1 Turbo"},
++ {0x3A, "AIT-2 Turbo"},
++ {0x3B, "AIT-3Ex"},
+ {0x40, "DLT1 40 GB, or Ultrium"},
+ {0x41, "DLT 40GB, or Ultrium2"},
+ {0x42, "LTO-2"},
+ {0x45, "QIC-3095-MC (TR-4)"},
+ {0x47, "TR-5"},
++ {0x48, "Quantum SDLT220"},
++ {0x49, "Quantum SDLT320"},
+ {0x80, "DLT 15GB uncomp. or Ecrix"},
+ {0x81, "DLT 15GB compressed"},
+ {0x82, "DLT 20GB uncompressed"},
+@@ -254,20 +270,25 @@
+ {"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"},
++#if 0
+ {"no-wait", MT_ST_NOWAIT, "immediate mode for rewind, etc."},
++#endif
+ #ifdef MT_ST_SYSV
+ {"sysv", MT_ST_SYSV, "enable the SystemV semantics"},
+ #endif
++#if 0
+ {"cleaning", MT_ST_SET_CLN, "set the cleaning bit location and mask"},
++#endif
+ {NULL, 0}};
+
+ static char *tape_name; /* The tape name for messages */
+
+
+-\f int
++int
+ main(int argc, char **argv)
+ {
+- int mtfd, cmd_code, i, argn, len, oflags;
++ int mtfd, cmd_code, i, argn, oflags;
++ unsigned int len;
+ char *cmdstr;
+ cmdef_tr *comp, *comp2;
+
+@@ -344,7 +365,7 @@
+ oflags = comp->cmd_fdtype == FD_RDONLY ? O_RDONLY : O_RDWR;
+ if ((comp->error_tests & ET_ONLINE) == 0)
+ oflags |= O_NONBLOCK;
+- if ((mtfd = open(tape_name, oflags)) < 0) {
++ if ((mtfd = tape_open(tape_name, oflags, 0)) < 0) {
+ perror(tape_name);
+ exit(1);
+ }
+@@ -368,7 +389,7 @@
+ }
+
+ if (mtfd >= 0)
+- close(mtfd);
++ tape_close(mtfd);
+ return i;
+ }
+
+@@ -409,9 +430,9 @@
+ do_standard(int mtfd, cmdef_tr *cmd, int argc, char **argv)
+ {
+ struct mtop mt_com;
+- char *endp;
++ char *endp = NULL;
+
+- mt_com.mt_op = cmd->cmd_code;
++ mt_com.mt_op = (short)cmd->cmd_code;
+ mt_com.mt_count = (argc > 0 ? strtol(*argv, &endp, 0) : 1);
+ if (argc > 0 && endp != *argv) {
+ if (*endp == 'k')
+@@ -464,7 +485,8 @@
+ static int
+ do_options(int mtfd, cmdef_tr *cmd, int argc, char **argv)
+ {
+- int i, an, len;
++ int i, an;
++ unsigned int len;
+ struct mtop mt_com;
+
+ mt_com.mt_op = MTSETDRVBUFFER;
+@@ -596,8 +618,10 @@
+ type = "SCSI 1";
+ else if (status.mt_type == MT_ISSCSI2)
+ type = "SCSI 2";
++#if 0
+ else if (status.mt_type == MT_ISONSTREAM_SC)
+ type = "OnStream SC-, DI-, DP-, or USB";
++#endif
+ else
+ type = NULL;
+ if (type == NULL) {
+@@ -607,7 +631,7 @@
+ printf("IDE-Tape (type code 0) ?\n");
+ else
+ printf("Unknown tape drive type (type code %ld)\n", status.mt_type);
+- printf("File number=%d, block number=%d.\n",
++ printf("File number=%ld, block number=%ld.\n",
+ status.mt_fileno, status.mt_blkno);
+ printf("mt_resid: %ld, mt_erreg: 0x%lx\n",
+ status.mt_resid, status.mt_erreg);
+@@ -617,14 +641,17 @@
+ else {
+ printf("%s tape drive:\n", type);
+ if (status.mt_type == MT_ISSCSI2)
+- printf("File number=%d, block number=%d, partition=%ld.\n",
++ printf("File number=%ld, block number=%ld, partition=%ld.\n",
+ status.mt_fileno, status.mt_blkno, (status.mt_resid & 0xff));
+ else
+- printf("File number=%d, block number=%d.\n",
++ printf("File number=%ld, block number=%ld.\n",
+ status.mt_fileno, status.mt_blkno);
+- if (status.mt_type == MT_ISSCSI1 ||
+- status.mt_type == MT_ISSCSI2 ||
+- status.mt_type == MT_ISONSTREAM_SC) {
++ if (status.mt_type == MT_ISSCSI1
++ || status.mt_type == MT_ISSCSI2
++#if 0
++ || status.mt_type == MT_ISONSTREAM_SC
++#endif
++ ) {
+ dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT;
+ density = "no translation";
+ for (i=0; i < NBR_DENSITIES; i++)
+@@ -666,8 +693,10 @@
+ printf(" DR_OPEN");
+ if (GMT_IM_REP_EN(status.mt_gstat))
+ printf(" IM_REP_EN");
++#if 0
+ if (GMT_CLN(status.mt_gstat))
+ printf(" CLN");
++#endif
+ printf("\n");
+ return 0;
+ }
+Only in .: mt.exe
+Only in .: mt.patch
+diff -ru ../../ibiblio/mt-st-0.9b/mtio.h ./mtio.h
+--- ../../ibiblio/mt-st-0.9b/mtio.h Tue Aug 16 12:16:28 2005
++++ ./mtio.h Sun Jul 30 06:06:13 2006
+@@ -8,9 +8,7 @@
+ #ifndef _LINUX_MTIO_H
+ #define _LINUX_MTIO_H
+
+-#include <linux/types.h>
+-#include <linux/ioctl.h>
+-#include <linux/qic117.h>
++#include <sys/types.h>
+
+ /*
+ * Structures and definitions for mag tape io control commands
+@@ -150,6 +148,7 @@
+ };
+
+
++#ifdef USE_QIC02
+ /* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
+ * as an interim solution for QIC-02 until DDI is fully implemented.
+ */
+@@ -281,6 +280,7 @@
+ * command
+ */
+ };
++#endif
+
+ /* mag tape io control commands */
+ #define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */
+Only in .: mtops.c
+Only in .: mtops.h
+diff -ru ../../ibiblio/mt-st-0.9b/stinit.def.examples ./stinit.def.examples
+--- ../../ibiblio/mt-st-0.9b/stinit.def.examples Tue Aug 16 12:16:28 2005
++++ ./stinit.def.examples Sun Jul 30 05:15:43 2006
+@@ -56,3 +56,169 @@
+ mode3 blocksize=0 density=1 # 800 bpi
+ }
+
++# DLT2000 / 2000XT
++manufacturer="QUANTUM" model = "DLT2000" {
++scsi2logical=1
++can-bsr
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++mode1 blocksize=0 density=0x81 # 10GB + compression on DLTtape III, 15+ with DLTtape IIIXT in 2000XT
++mode2 blocksize=0 density=0x80 # 10GB, no compression on DLTtape III, 15 with DLTtape IIIXT in 2000XT
++mode3 blocksize=0 density=0x18 # 6GB, compression not available, on DLTtape III
++mode4 blocksize=0 density=0x17 #2.6GB, compression not available, on DLTtape III
++}
++
++# DLT4000
++manufacturer="QUANTUM" model = "DLT4000" {
++scsi2logical=1
++can-bsr
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards compatible, use older modes (e.g. from above) as required
++mode1 blocksize=0 density=0x83 # 20GB + compression
++mode2 blocksize=0 density=0x82 # 20GB, no compression
++mode3 blocksize=0 density=0x81 # 10GB + compression (DLT2000 mode) with DLTtape III, 15+ with DLTtape IIIXT in 2000XT
++mode4 blocksize=0 density=0x80 # 10GB, no compression (DLT2000 mode) with DLTtape III, 15 with DLTtape IIIXT in 2000XT
++}
++
++# DLT7000
++manufacturer="QUANTUM" model = "DLT7000" {
++scsi2logical=1
++can-bsr
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards compatible, use older modes (e.g. from above) as required.
++mode1 blocksize=0 density=0x85 # 35GB + compression
++mode2 blocksize=0 density=0x84 # 35GB, no compression
++mode3 blocksize=0 density=0x83 # 20GB + compression (DLT4000 mode)
++mode4 blocksize=0 density=0x82 # 20GB, no compression (DLT4000 mode)
++}
++
++# DLT8000
++manufacturer="QUANTUM" model = "DLT8000" {
++scsi2logical=1
++can-bsr=1
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards compatible to DLT7000, use older modes (e.g. from above) as required. Modes <10GB (<0x19) not supported!
++mode1 blocksize=0 density=0x89 # 40GB + compression
++mode2 blocksize=0 density=0x88 # 40GB, no compression
++mode3 blocksize=0 density=0x85 # 35GB + compression (DLT7000 mode)
++mode4 blocksize=0 density=0x84 # 35GB, no compression (DLT7000 mode)
++}
++
++
++# SDLT220
++manufacturer="QUANTUM" model = "SuperDLT1" {
++scsi2logical=1
++can-bsr=1
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so no need to define any other modes here.
++mode1 blocksize=0 density=0x48 compression=1 # 110 GB + compression
++mode2 blocksize=0 density=0x48 compression=0 # 110 GB, no ompression
++}
++
++# SDLT320
++manufacturer="QUANTUM" model = "SDLT320" {
++scsi2logical=1
++can-bsr=1
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards write compatible to SDLT220 and read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so we need only the SDL220/320 modes here
++mode1 blocksize=0 density=0x49 compression=1 # 160 GB + compression
++mode2 blocksize=0 density=0x49 compression=0 # 160 GB, no ompression
++mode3 blocksize=0 density=0x48 compression=1 # 110 GB + compression
++mode4 blocksize=0 density=0x48 compression=0 # 110 GB, no ompression
++}
++
++# SDLT600
++manufacturer="QUANTUM" model = "SDLT600" {
++scsi2logical=1
++can-bsr=1
++auto-lock=0
++two-fms=0
++drive-buffering=1
++buffer-writes
++read-ahead=1
++async-writes=1
++can-partitions=0
++fast-mteom=1
++#
++# If your stinit supports the timeouts:
++timeout=3600 # 1 hour
++long-timeout=14400 # 4 hours
++#
++# Drive is backwards read compatible to SDLT220/320 and VS160. Mode settings are only required for writing, so we need only the native SDLT600 mode here.
++mode1 blocksize=0 density=0x4a compression=1 # 300 GB + compression
++mode2 blocksize=0 density=0x4a compression=0 # 300 GB, no ompression
++mode3 blocksize=0 density=0x4a compression=1 # 300 GB + compression
++mode4 blocksize=0 density=0x4a compression=0 # 300 GB, no ompression
++}
++
+--- /dev/null Sun Jul 30 06:23:05 2006
++++ mtops.h Sun Jul 30 05:50:17 2006
+@@ -0,0 +1,15 @@
++int tape_open(const char *file, int flags, int mode);
++int tape_read(int fd, void *buffer, unsigned int count);
++int tape_write(int fd, const void *buffer, unsigned int count);
++int tape_ioctl(int fd, unsigned long int request, ...);
++int tape_close(int fd);
++
++typedef unsigned long __kernel_daddr_t;
++
++#ifndef ENOMEDIUM
++#define ENOMEDIUM 123
++#endif
++
++#ifndef PATH_MAX
++#define PATH_MAX 1024
++#endif
+--- /dev/null Sun Jul 30 06:23:10 2006
++++ mtops.c Sun Jul 30 05:46:08 2006
+@@ -0,0 +1,1158 @@
++/*
++ * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows.
++ *
++ * Author: Robert Nelson, May, 2006 <robertn@the-nelsons.org>
++ *
++ * Version $Id$
++ *
++ * Copyright (C) 2006 Kern Sibbald
++ *
++ * This file was contributed to the Bacula project by Robert Nelson.
++ *
++ * Robert Nelson has been granted a perpetual, worldwide,
++ * non-exclusive, no-charge, royalty-free, irrevocable copyright
++ * license to reproduce, prepare derivative works of, publicly
++ * display, publicly perform, sublicense, and distribute the original
++ * work contributed by Robert Nelson to the Bacula project in source
++ * or object form.
++ *
++ * If you wish to license contributions from Robert Nelson
++ * under an alternate open source license please contact
++ * Robert Nelson <robertn@the-nelsons.org>.
++ */
++/*
++ Copyright (C) 2006 Kern Sibbald
++
++ This program is free software; you can redistribute it and/or
++ modify it under the terms of the GNU General Public License
++ version 2 as amended with additional clauses defined in the
++ file LICENSE in the main source directory.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ the file LICENSE for additional details.
++
++ */
++
++#include <stdarg.h>
++#include <stddef.h>
++
++#include <windows.h>
++#include <errno.h>
++
++#include "mtops.h"
++#include "mtio.h"
++#include <ddk/ntddstor.h>
++#include <ddk/ntddscsi.h>
++
++#ifndef __CPLUS_PLUS
++typedef char bool;
++#define true 1
++#define false 0
++#endif
++
++//
++// SCSI bus status codes.
++//
++
++#define SCSISTAT_GOOD 0x00
++#define SCSISTAT_CHECK_CONDITION 0x02
++#define SCSISTAT_CONDITION_MET 0x04
++#define SCSISTAT_BUSY 0x08
++#define SCSISTAT_INTERMEDIATE 0x10
++#define SCSISTAT_INTERMEDIATE_COND_MET 0x14
++#define SCSISTAT_RESERVATION_CONFLICT 0x18
++#define SCSISTAT_COMMAND_TERMINATED 0x22
++#define SCSISTAT_QUEUE_FULL 0x28
++
++/* Forward referenced functions */
++
++extern char my_name[];
++extern int debug_level;
++
++inline SHORT Read16BitSigned(const unsigned char *pValue)
++{
++ return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
++}
++
++inline USHORT Read16BitUnsigned(const unsigned char *pValue)
++{
++ return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]);
++}
++
++inline LONG Read24BitSigned(const unsigned char *pValue)
++{
++ return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) |
++ (ULONG)pValue[2])) << 8 >> 8;
++}
++
++inline ULONG Read24BitUnsigned(const unsigned char *pValue)
++{
++ return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2];
++}
++
++inline LONG Read32BitSigned(const unsigned char *pValue)
++{
++ return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
++ ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
++}
++
++inline ULONG Read32BitUnsigned(const unsigned char *pValue)
++{
++ return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) |
++ ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]);
++}
++
++inline LONGLONG Read64BitSigned(const unsigned char *pValue)
++{
++ return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
++ ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
++ ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
++ ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
++}
++
++inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue)
++{
++ return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) |
++ ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) |
++ ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) |
++ ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]);
++}
++
++typedef struct _TAPE_POSITION_INFO
++{
++ UCHAR AtPartitionStart:1;
++ UCHAR AtPartitionEnd:1;
++ UCHAR PartitionBlockValid:1;
++ UCHAR FileSetValid:1;
++ UCHAR :4;
++ UCHAR Reserved1[3];
++ ULONG Partition;
++ ULONGLONG BlockNumber;
++ ULONGLONG FileNumber;
++ ULONGLONG SetNumber;
++} TAPE_POSITION_INFO, *PTAPE_POSITION_INFO;
++
++typedef struct _TAPE_HANDLE_INFO
++{
++ HANDLE OSHandle;
++ bool bEOD;
++ bool bEOF;
++ bool bEOT;
++ bool bBlockValid;
++ ULONG FeaturesLow;
++ ULONG FeaturesHigh;
++ ULONG ulFile;
++ ULONGLONG ullFileStart;
++
++} TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO;
++
++TAPE_HANDLE_INFO TapeHandleTable[] =
++{
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE },
++ { INVALID_HANDLE_VALUE }
++};
++
++#define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0]))
++
++DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo);
++DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize);
++
++int tape_get(int fd, struct mtget *mt_get);
++int tape_op(int fd, struct mtop *mt_com);
++int tape_pos(int fd, struct mtpos *mt_pos);
++
++int
++tape_open(const char *file, int flags, int mode)
++{
++ HANDLE hDevice = INVALID_HANDLE_VALUE;
++ char szDeviceName[256] = "\\\\.\\";
++ int idxFile;
++ DWORD dwResult;
++
++ for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) {
++ if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) {
++ break;
++ }
++ }
++
++ if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) {
++ return EMFILE;
++ }
++
++ memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile]));
++
++ if (file[0] != '\\' && file[0] != '/') {
++ strncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4);
++ } else {
++ strncpy(&szDeviceName[0], file, sizeof(szDeviceName));
++ }
++
++ szDeviceName[sizeof(szDeviceName) - 1] = '\0';
++
++ hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL);
++
++ if (hDevice != INVALID_HANDLE_VALUE) {
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile];
++
++ memset(pHandleInfo, 0, sizeof(*pHandleInfo));
++
++ pHandleInfo->OSHandle = hDevice;
++
++ TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters;
++ DWORD dwSize = sizeof(TapeDriveParameters);
++
++ dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters);
++ if (dwResult == NO_ERROR) {
++ pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow;
++ pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh;
++ }
++
++ TAPE_POSITION_INFO TapePositionInfo;
++
++ dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
++
++ if (dwResult == NO_ERROR) {
++ if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd ||
++ (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) {
++ pHandleInfo->ulFile = 0;
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = 0;
++ } else if (TapePositionInfo.FileSetValid) {
++ pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
++ }
++ }
++ } else {
++ DWORD dwError = GetLastError();
++
++ switch (dwError) {
++ case ERROR_FILE_NOT_FOUND:
++ case ERROR_PATH_NOT_FOUND:
++ errno = ENOENT;
++ break;
++
++ case ERROR_TOO_MANY_OPEN_FILES:
++ errno = EMFILE;
++ break;
++
++ default:
++ case ERROR_ACCESS_DENIED:
++ case ERROR_SHARING_VIOLATION:
++ case ERROR_LOCK_VIOLATION:
++ case ERROR_INVALID_NAME:
++ errno = EACCES;
++ break;
++
++ case ERROR_FILE_EXISTS:
++ errno = EEXIST;
++ break;
++
++ case ERROR_INVALID_PARAMETER:
++ errno = EINVAL;
++ break;
++ }
++
++ return(int) -1;
++ }
++
++ return (int)idxFile + 3;
++}
++
++int
++tape_read(int fd, void *buffer, unsigned int count)
++{
++ if (buffer == NULL) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
++ TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ DWORD bytes_read;
++ BOOL bResult;
++
++ bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL);
++
++ if (bResult) {
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ return bytes_read;
++ } else {
++ int iReturnValue = 0;
++ DWORD last_error = GetLastError();
++
++ switch (last_error) {
++
++ case ERROR_FILEMARK_DETECTED:
++ pHandleInfo->bEOF = true;
++ break;
++
++ case ERROR_END_OF_MEDIA:
++ pHandleInfo->bEOT = true;
++ break;
++
++ case ERROR_NO_MEDIA_IN_DRIVE:
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ errno = ENOMEDIUM;
++ iReturnValue = -1;
++ break;
++
++ case ERROR_NO_DATA_DETECTED:
++ pHandleInfo->bEOD = true;
++ break;
++
++ case ERROR_INVALID_HANDLE:
++ case ERROR_ACCESS_DENIED:
++ case ERROR_LOCK_VIOLATION:
++ errno = EBADF;
++ iReturnValue = -1;
++ break;
++
++ default:
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ errno = EIO;
++ iReturnValue = -1;
++ }
++
++ return iReturnValue;
++ }
++}
++
++int
++tape_write(int fd, const void *buffer, unsigned int count)
++{
++ if (buffer == NULL) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ DWORD bytes_written;
++ BOOL bResult;
++
++ bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL);
++
++ if (bResult) {
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ return bytes_written;
++ } else {
++ DWORD last_error = GetLastError();
++
++ switch (last_error) {
++ case ERROR_END_OF_MEDIA:
++ case ERROR_DISK_FULL:
++ pHandleInfo->bEOT = true;
++ errno = ENOSPC;
++ break;
++
++ case ERROR_NO_MEDIA_IN_DRIVE:
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ errno = ENOMEDIUM;
++ break;
++
++ case ERROR_INVALID_HANDLE:
++ case ERROR_ACCESS_DENIED:
++ errno = EBADF;
++ break;
++
++ default:
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ errno = EIO;
++ break;
++ }
++ return -1;
++ }
++}
++
++int
++tape_close(int fd)
++{
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
++ TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ if (!CloseHandle(pHandleInfo->OSHandle)) {
++ pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
++ errno = EBADF;
++ return -1;
++ }
++
++ pHandleInfo->OSHandle = INVALID_HANDLE_VALUE;
++
++ return 0;
++}
++
++int
++tape_ioctl(int fd, unsigned long int request, ...)
++{
++ va_list argp;
++ int result;
++
++ va_start(argp, request);
++
++ switch (request) {
++ case MTIOCTOP:
++ result = tape_op(fd, va_arg(argp, struct mtop *));
++ break;
++
++ case MTIOCGET:
++ result = tape_get(fd, va_arg(argp, struct mtget *));
++ break;
++
++ case MTIOCPOS:
++ result = tape_pos(fd, va_arg(argp, struct mtpos *));
++ break;
++
++ default:
++ errno = ENOTTY;
++ result = -1;
++ break;
++ }
++
++ va_end(argp);
++
++ return result;
++}
++
++int tape_op(int fd, struct mtop *mt_com)
++{
++ DWORD result = NO_ERROR;
++ int index;
++
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
++ TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ switch (mt_com->mt_op)
++ {
++ case MTRESET:
++ case MTNOP:
++ case MTSETDRVBUFFER:
++ break;
++
++ default:
++ case MTRAS1:
++ case MTRAS2:
++ case MTRAS3:
++ case MTSETDENSITY:
++ errno = ENOTTY;
++ result = (DWORD)-1;
++ break;
++
++ case MTFSF:
++ for (index = 0; index < mt_com->mt_count; index++) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->ulFile++;
++ pHandleInfo->bEOF = true;
++ pHandleInfo->bEOT = false;
++ }
++ }
++ break;
++
++ case MTBSF:
++ for (index = 0; index < mt_com->mt_count; index++) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->ulFile--;
++ pHandleInfo->bBlockValid = false;
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ }
++ }
++ break;
++
++ case MTFSR:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ } else if (result == ERROR_FILEMARK_DETECTED) {
++ pHandleInfo->bEOF = true;
++ }
++ break;
++
++ case MTBSR:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ } else if (result == ERROR_FILEMARK_DETECTED) {
++ pHandleInfo->ulFile--;
++ pHandleInfo->bBlockValid = false;
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ }
++ break;
++
++ case MTWEOF:
++ result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOF = true;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->ulFile += mt_com->mt_count;
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = 0;
++ }
++ break;
++
++ case MTREW:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->ulFile = 0;
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = 0;
++ }
++ break;
++
++ case MTOFFL:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->ulFile = 0;
++ pHandleInfo->ullFileStart = 0;
++ }
++ break;
++
++ case MTRETEN:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->ulFile = 0;
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = 0;
++ }
++ break;
++
++ case MTBSFM:
++ for (index = 0; index < mt_com->mt_count; index++) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
++ if (result == NO_ERROR) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ }
++ }
++ break;
++
++ case MTFSFM:
++ for (index = 0; index < mt_com->mt_count; index++) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE);
++ if (result == NO_ERROR) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE);
++ pHandleInfo->bEOD = false;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ }
++ }
++ break;
++
++ case MTEOM:
++ for ( ; ; ) {
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE);
++ if (result != NO_ERROR) {
++ pHandleInfo->bEOF = false;
++
++ if (result == ERROR_END_OF_MEDIA) {
++ pHandleInfo->bEOD = true;
++ pHandleInfo->bEOT = true;
++ return 0;
++ }
++ if (result == ERROR_NO_DATA_DETECTED) {
++ pHandleInfo->bEOD = true;
++ pHandleInfo->bEOT = false;
++ return 0;
++ }
++ break;
++ } else {
++ pHandleInfo->bEOF = true;
++ pHandleInfo->ulFile++;
++ }
++ }
++ break;
++
++ case MTERASE:
++ result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE);
++ if (result == NO_ERROR) {
++ pHandleInfo->bEOD = true;
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->ulFile = 0;
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = 0;
++ }
++ break;
++
++ case MTSETBLK:
++ {
++ TAPE_SET_MEDIA_PARAMETERS SetMediaParameters;
++
++ SetMediaParameters.BlockSize = mt_com->mt_count;
++ result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters);
++ }
++ break;
++
++ case MTSEEK:
++ {
++ TAPE_POSITION_INFO TapePositionInfo;
++
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE);
++
++ memset(&TapePositionInfo, 0, sizeof(TapePositionInfo));
++ DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo);
++ if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) {
++ pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber;
++ } else {
++ pHandleInfo->ulFile = ~0U;
++ }
++ }
++ break;
++
++ case MTTELL:
++ {
++ DWORD partition;
++ DWORD offset;
++ DWORD offsetHi;
++
++ result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
++ if (result == NO_ERROR) {
++ return offset;
++ }
++ }
++ break;
++
++ case MTFSS:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE);
++ break;
++
++ case MTBSS:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE);
++ break;
++
++ case MTWSM:
++ result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE);
++ break;
++
++ case MTLOCK:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE);
++ break;
++
++ case MTUNLOCK:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE);
++ break;
++
++ case MTLOAD:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE);
++ break;
++
++ case MTUNLOAD:
++ result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE);
++ break;
++
++ case MTCOMPRESSION:
++ {
++ TAPE_GET_DRIVE_PARAMETERS GetDriveParameters;
++ TAPE_SET_DRIVE_PARAMETERS SetDriveParameters;
++ DWORD size;
++
++ size = sizeof(GetDriveParameters);
++
++ result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters);
++
++ if (result == NO_ERROR)
++ {
++ SetDriveParameters.ECC = GetDriveParameters.ECC;
++ SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count;
++ SetDriveParameters.DataPadding = GetDriveParameters.DataPadding;
++ SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks;
++ SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize;
++
++ result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters);
++ }
++ }
++ break;
++
++ case MTSETPART:
++ result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE);
++ break;
++
++ case MTMKPART:
++ if (mt_com->mt_count == 0)
++ {
++ result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0);
++ }
++ else
++ {
++ result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count);
++ }
++ break;
++ }
++
++ if ((result == NO_ERROR && pHandleInfo->bEOF) ||
++ (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) {
++
++ TAPE_POSITION_INFO TapePositionInfo;
++
++ if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) {
++ pHandleInfo->bBlockValid = true;
++ pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber;
++ }
++ }
++
++ switch (result) {
++ case NO_ERROR:
++ case (DWORD)-1: /* Error has already been translated into errno */
++ break;
++
++ default:
++ case ERROR_FILEMARK_DETECTED:
++ errno = EIO;
++ break;
++
++ case ERROR_END_OF_MEDIA:
++ pHandleInfo->bEOT = true;
++ errno = EIO;
++ break;
++
++ case ERROR_NO_DATA_DETECTED:
++ pHandleInfo->bEOD = true;
++ errno = EIO;
++ break;
++
++ case ERROR_NO_MEDIA_IN_DRIVE:
++ pHandleInfo->bEOF = false;
++ pHandleInfo->bEOT = false;
++ pHandleInfo->bEOD = false;
++ errno = ENOMEDIUM;
++ break;
++
++ case ERROR_INVALID_HANDLE:
++ case ERROR_ACCESS_DENIED:
++ case ERROR_LOCK_VIOLATION:
++ errno = EBADF;
++ break;
++ }
++
++ return result == NO_ERROR ? 0 : -1;
++}
++
++int tape_get(int fd, struct mtget *mt_get)
++{
++ TAPE_POSITION_INFO pos_info;
++ BOOL result;
++
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
++ TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) {
++ return -1;
++ }
++
++ DWORD density = 0;
++ DWORD blocksize = 0;
++
++ result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize);
++
++ if (result != NO_ERROR) {
++ TAPE_GET_DRIVE_PARAMETERS drive_params;
++ DWORD size;
++
++ size = sizeof(drive_params);
++
++ result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params);
++
++ if (result == NO_ERROR) {
++ blocksize = drive_params.DefaultBlockSize;
++ }
++ }
++
++ mt_get->mt_type = MT_ISSCSI2;
++
++ // Partition #
++ mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1;
++
++ // Density / Block Size
++ mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) |
++ ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK);
++
++ mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/
++
++ if (pHandleInfo->bEOF) {
++ mt_get->mt_gstat |= 0x80000000; // GMT_EOF
++ }
++
++ if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) {
++ mt_get->mt_gstat |= 0x40000000; // GMT_BOT
++ }
++
++ if (pHandleInfo->bEOT) {
++ mt_get->mt_gstat |= 0x20000000; // GMT_EOT
++ }
++
++ if (pHandleInfo->bEOD) {
++ mt_get->mt_gstat |= 0x08000000; // GMT_EOD
++ }
++
++ TAPE_GET_MEDIA_PARAMETERS media_params;
++ DWORD size = sizeof(media_params);
++
++ result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params);
++
++ if (result == NO_ERROR && media_params.WriteProtected) {
++ mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT
++ }
++
++ result = GetTapeStatus(pHandleInfo->OSHandle);
++
++ if (result != NO_ERROR) {
++ if (result == ERROR_NO_MEDIA_IN_DRIVE) {
++ mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN
++ }
++ } else {
++ mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE
++ }
++
++ // Recovered Error Count
++ mt_get->mt_erreg = 0;
++
++ // File Number
++ mt_get->mt_fileno = (__kernel_daddr_t)pHandleInfo->ulFile;
++
++ // Block Number
++ mt_get->mt_blkno = (__kernel_daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1);
++
++ return 0;
++}
++
++#define SERVICEACTION_SHORT_FORM_BLOCKID 0
++#define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1
++#define SERVICEACTION_LONG_FORM 6
++#define SERVICEACTION_EXTENDED_FORM 8
++
++
++typedef struct _SCSI_READ_POSITION_SHORT_BUFFER
++{
++ UCHAR :1;
++ UCHAR PERR:1;
++ UCHAR BPU:1;
++ UCHAR :1;
++ UCHAR BYCU:1;
++ UCHAR BCU:1;
++ UCHAR EOP:1;
++ UCHAR BOP:1;
++ UCHAR Partition;
++ UCHAR Reserved1[2];
++ UCHAR FirstBlock[4];
++ UCHAR LastBlock[4];
++ UCHAR Reserved2;
++ UCHAR NumberBufferBlocks[3];
++ UCHAR NumberBufferBytes[4];
++} SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER;
++
++typedef struct _SCSI_READ_POSITION_LONG_BUFFER
++{
++ UCHAR :2;
++ UCHAR BPU:1;
++ UCHAR MPU:1;
++ UCHAR :2;
++ UCHAR EOP:1;
++ UCHAR BOP:1;
++ UCHAR Reserved3[3];
++ UCHAR Partition[4];
++ UCHAR BlockNumber[8];
++ UCHAR FileNumber[8];
++ UCHAR SetNumber[8];
++} SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER;
++
++typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER
++{
++ UCHAR :1;
++ UCHAR PERR:1;
++ UCHAR LOPU:1;
++ UCHAR :1;
++ UCHAR BYCU:1;
++ UCHAR LOCU:1;
++ UCHAR EOP:1;
++ UCHAR BOP:1;
++ UCHAR Partition;
++ UCHAR AdditionalLength[2];
++ UCHAR Reserved1;
++ UCHAR NumberBufferObjects[3];
++ UCHAR FirstLogicalObject[8];
++ UCHAR LastLogicalObject[8];
++ UCHAR NumberBufferObjectBytes[8];
++} SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER;
++
++typedef union _READ_POSITION_RESULT {
++ SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer;
++ SCSI_READ_POSITION_LONG_BUFFER LongBuffer;
++ SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer;
++} READ_POSITION_RESULT, *PREAD_POSITION_RESULT;
++
++DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo)
++{
++ PSCSI_PASS_THROUGH ScsiPassThrough;
++ BOOL bResult;
++ DWORD dwBytesReturned;
++ int pass;
++
++ const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28;
++
++ memset(TapePositionInfo, 0, sizeof(*TapePositionInfo));
++
++ ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
++
++ for (pass = 0; pass < 2; pass++)
++ {
++ memset(ScsiPassThrough, 0, dwBufferSize);
++
++ ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
++
++ ScsiPassThrough->CdbLength = 10;
++ ScsiPassThrough->SenseInfoLength = 28;
++ ScsiPassThrough->DataIn = 1;
++ ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER);
++ ScsiPassThrough->TimeOutValue = 1000;
++ ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28;
++ ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
++
++ ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION
++
++ switch (pass)
++ {
++ case 0:
++ ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM;
++ break;
++
++ case 1:
++ ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID;
++ break;
++ }
++
++ bResult = DeviceIoControl( hDevice,
++ IOCTL_SCSI_PASS_THROUGH,
++ ScsiPassThrough, sizeof(SCSI_PASS_THROUGH),
++ ScsiPassThrough, dwBufferSize,
++ &dwBytesReturned,
++ NULL);
++
++ if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) {
++ if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) {
++ PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset);
++
++ switch (pass)
++ {
++ case 0: // SERVICEACTION_LONG_FORM
++ {
++ TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP;
++ TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP;
++
++ if (!TapePositionInfo->PartitionBlockValid) {
++ TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU;
++
++ if (TapePositionInfo->PartitionBlockValid) {
++ TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition);
++ TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber);
++ }
++ }
++
++ TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU;
++ if (TapePositionInfo->FileSetValid) {
++ TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber);
++ TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber);
++ }
++ }
++ break;
++
++ case 1: // SERVICEACTION_SHORT_FORM_BLOCKID
++ {
++ // pPosResult->ShortBuffer.PERR;
++ // pPosResult->ShortBuffer.BYCU;
++ // pPosResult->ShortBuffer.BCU;
++ TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP;
++ TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP;
++
++ if (!TapePositionInfo->PartitionBlockValid) {
++ TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU;
++
++ if (TapePositionInfo->PartitionBlockValid) {
++ TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition;
++ TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock);
++ }
++ }
++ // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock);
++ // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks);
++ // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes);
++ }
++ break;
++ }
++ }
++ }
++ }
++ free(ScsiPassThrough);
++
++ return NO_ERROR;
++}
++
++DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize)
++{
++ DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO);
++ GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize);
++ BOOL bResult;
++ DWORD dwResult;
++ DWORD idxMedia;
++
++ if (pGetMediaTypes == NULL) {
++ return ERROR_OUTOFMEMORY;
++ }
++
++ do {
++ DWORD dwBytesReturned;
++
++ bResult = DeviceIoControl( hDevice,
++ IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
++ NULL, 0,
++ (LPVOID)pGetMediaTypes, dwBufferSize,
++ &dwBytesReturned,
++ NULL);
++
++ if (!bResult) {
++ dwResult = GetLastError();
++
++ if (dwResult != ERROR_INSUFFICIENT_BUFFER) {
++ free(pGetMediaTypes);
++ return dwResult;
++ }
++
++ dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO);
++
++ GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize);
++
++ if (pNewBuffer != pGetMediaTypes) {
++ free(pGetMediaTypes);
++
++ if (pNewBuffer == NULL) {
++ return ERROR_OUTOFMEMORY;
++ }
++
++ pGetMediaTypes = pNewBuffer;
++ }
++ }
++ } while (!bResult);
++
++ if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) {
++ free(pGetMediaTypes);
++ return ERROR_BAD_DEVICE;
++ }
++
++ for (idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) {
++
++ if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) {
++
++ if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) {
++ *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode;
++ } else {
++ *pdwDensity = 0;
++ }
++
++ *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize;
++
++ free(pGetMediaTypes);
++
++ return NO_ERROR;
++ }
++ }
++
++ free(pGetMediaTypes);
++
++ return ERROR_NO_MEDIA_IN_DRIVE;
++}
++
++int tape_pos(int fd, struct mtpos *mt_pos)
++{
++ DWORD partition;
++ DWORD offset;
++ DWORD offsetHi;
++ BOOL result;
++
++ if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) ||
++ TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) {
++ errno = EBADF;
++ return -1;
++ }
++
++ PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3];
++
++ result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi);
++ if (result == NO_ERROR) {
++ mt_pos->mt_blkno = offset;
++ return 0;
++ }
++
++ return -1;
++}
--- /dev/null
+Index: README.win32
+===================================================================
+--- /dev/null Sat Jul 29 14:54:52 2006
++++ README.win32 Sat Jul 29 14:46:45 2006
+@@ -0,0 +1,22 @@
++CHANGES FROM UNIX
++=================
++
++The only difference is in the naming of devices. On Linux the changer is
++accessed using /dev/sg<N>, on Windows you use Changer<N>.
++
++On Linux the tape drive is referenced using /dev/nst<N>, on Windows you use
++Tape<N>.
++
++There is one exception in the case where there isn't a driver loaded for the
++device. This is usually only the case on Windows 2000 or if the Windows XP or
++Windows Server 2003 system supplied driver has been disabled.
++
++In the case where there is no driver loaded you can access the device directly
++through the SCSI driver using the following notation:
++
++ <port>:<bus>:<target>:<lun>
++
++ Port is the adapter number
++ Bus is the SCSI bus number relative to the adapter
++ Target is the SCSI device's target ID
++ LUN is the SCSI device's logical unit number
+Index: scsi_win32.c
+===================================================================
+--- /dev/null Sat Jul 29 14:55:00 2006
++++ scsi_win32.c Sat Jul 29 14:54:08 2006
+@@ -0,0 +1,353 @@
++/* Copyright 2006 Robert Nelson <robertn@the-nelsons.org>
++
++$Date$
++$Revision$
++
++ This program is free software; you may redistribute and/or modify it under
++ the terms of the GNU General Public License Version 2 as published by the
++ Free Software Foundation.
++
++ This program is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
++ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ for complete details.
++
++*/
++
++/* These are the SCSI commands for Microsoft Windows. This is derived from
++ * the file scsi_linux.c substituting Windows specific emulation of the Linux
++ * behaviour.
++ */
++
++#include <stdio.h>
++#include <windows.h>
++
++#ifdef _MSC_VER
++#include <ntddscsi.h>
++#else
++#include <ddk/ntddscsi.h>
++#endif
++
++#ifndef HZ
++#define HZ 1000
++#endif
++
++/* These are copied out of BRU 16.1, with all the boolean masks changed
++ * to our bitmasks.
++*/
++#define S_NO_SENSE(s) ((s)->SenseKey == 0x0)
++#define S_RECOVERED_ERROR(s) ((s)->SenseKey == 0x1)
++
++#define S_NOT_READY(s) ((s)->SenseKey == 0x2)
++#define S_MEDIUM_ERROR(s) ((s)->SenseKey == 0x3)
++#define S_HARDWARE_ERROR(s) ((s)->SenseKey == 0x4)
++#define S_UNIT_ATTENTION(s) ((s)->SenseKey == 0x6)
++#define S_BLANK_CHECK(s) ((s)->SenseKey == 0x8)
++#define S_VOLUME_OVERFLOW(s) ((s)->SenseKey == 0xd)
++
++#define DEFAULT_TIMEOUT 3 * 60 /* 3 minutes here */
++
++/* Sigh, the T-10 SSC spec says all of the following is needed to
++ * detect a short read while in variable block mode, and that even
++ * though we got a BLANK_CHECK or MEDIUM_ERROR, it's still a valid read.
++ */
++
++#define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s)->Filemark && (s)->Valid)
++#define SHORT_READ(s) (S_NO_SENSE((s)) && (s)->ILI && (s)->Valid && (s)->AdditionalSenseCode==0 && (s)->AdditionalSenseCodeQualifier==0)
++#define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s)->Valid)
++#define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s)->EOM && (s)->Valid)
++#define HIT_EOM(s) ((s)->EOM && (s)->Valid)
++
++#define STILL_A_VALID_READ(s) (HIT_FILEMARK(s) || SHORT_READ(s) || HIT_EOD(s) || HIT_EOP(s) || HIT_EOM(s))
++
++#define SCSI_DEFAULT_TIMEOUT 60 /* 1 minute */
++#define SCSI_MAX_TIMEOUT 108 /* 1 minute 48 seconds */
++
++typedef struct _HANDLE_ENTRY {
++ HANDLE hDevice;
++ UCHAR PortId;
++ UCHAR PathId;
++ UCHAR TargetId;
++ UCHAR Lun;
++} HANDLE_ENTRY, *PHANDLE_ENTRY;
++
++PHANDLE_ENTRY HandleTable = NULL;
++int nEntries = 0;
++
++DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
++{
++ int DeviceIndex;
++ TCHAR szDevicePath[256];
++
++ int nColons = 0;
++ int index;
++
++ int port, path, target, lun;
++
++ for (DeviceIndex = 0; DeviceIndex < nEntries; DeviceIndex++)
++ {
++ if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
++ break;
++ }
++
++ if (DeviceIndex >= nEntries)
++ {
++ PHANDLE_ENTRY pNewTable;
++
++ nEntries += 4;
++
++ if (HandleTable == NULL)
++ {
++ pNewTable = (PHANDLE_ENTRY)malloc(nEntries * sizeof(HANDLE_ENTRY));
++ }
++ else
++ {
++ pNewTable = (PHANDLE_ENTRY)realloc(HandleTable, nEntries * sizeof(HANDLE_ENTRY));
++ }
++
++ if (pNewTable == NULL)
++ {
++ FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
++ }
++
++ HandleTable = pNewTable;
++ }
++
++ for (index = 0; DeviceName[index] != '\0'; index++)
++ {
++ if (DeviceName[index] == ':')
++ nColons++;
++ else if (DeviceName[index] < '0' || DeviceName[index] > '9')
++ break;
++ }
++
++ if (DeviceName[index] == '\0' && nColons == 3 &&
++ sscanf(DeviceName, "%d:%d:%d:%d", &port, &path, &target, &lun) == 4) {
++
++ HandleTable[DeviceIndex].PortId = (UCHAR)port;
++ HandleTable[DeviceIndex].PathId = (UCHAR)path;
++ HandleTable[DeviceIndex].TargetId = (UCHAR)target;
++ HandleTable[DeviceIndex].Lun = (UCHAR)lun;
++
++ sprintf(szDevicePath, "\\\\.\\scsi%d:", port);
++ }
++ else
++ {
++ int nPrefixLength = 0;
++
++ if (DeviceName[0] != '\\') {
++ memcpy(szDevicePath, "\\\\.\\", 4 * sizeof(TCHAR));
++ nPrefixLength = 4;
++ }
++
++ HandleTable[DeviceIndex].PortId = 0;
++ HandleTable[DeviceIndex].PathId = 0;
++ HandleTable[DeviceIndex].TargetId = 0;
++ HandleTable[DeviceIndex].Lun = 0;
++
++ strncpy( &szDevicePath[nPrefixLength],
++ DeviceName,
++ sizeof(szDevicePath) / sizeof(TCHAR) - nPrefixLength - 1);
++
++ szDevicePath[sizeof(szDevicePath) / sizeof(TCHAR) - 1] = '\0';
++ }
++
++ HandleTable[DeviceIndex].hDevice = CreateFile(szDevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
++
++ if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
++ {
++ DWORD dwError = GetLastError();
++
++#if DEBUG
++ LPSTR lpszMessage;
++
++ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
++ fputs(lpszMessage, stderr);
++#endif
++
++ switch (dwError) {
++ case ERROR_FILE_NOT_FOUND:
++ case ERROR_PATH_NOT_FOUND:
++ errno = ENOENT;
++ break;
++
++ case ERROR_TOO_MANY_OPEN_FILES:
++ errno = EMFILE;
++ break;
++
++ default:
++ case ERROR_ACCESS_DENIED:
++ case ERROR_SHARING_VIOLATION:
++ case ERROR_LOCK_VIOLATION:
++ case ERROR_INVALID_NAME:
++ errno = EACCES;
++ break;
++
++ case ERROR_FILE_EXISTS:
++ errno = EEXIST;
++ break;
++
++ case ERROR_INVALID_PARAMETER:
++ errno = EINVAL;
++ break;
++ }
++
++ FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
++ }
++
++ return DeviceIndex;
++}
++
++static int scsi_timeout = SCSI_DEFAULT_TIMEOUT;
++
++void SCSI_Set_Timeout(int secs)
++{
++ if (secs > SCSI_MAX_TIMEOUT) {
++ secs = SCSI_MAX_TIMEOUT;
++ }
++ scsi_timeout = secs * HZ;
++}
++
++void SCSI_Default_Timeout(void)
++{
++ scsi_timeout = SCSI_DEFAULT_TIMEOUT * HZ;
++}
++
++void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
++{
++ if (DeviceFD < nEntries)
++ {
++ CloseHandle(HandleTable[DeviceFD].hDevice);
++ HandleTable[DeviceFD].hDevice = INVALID_HANDLE_VALUE;
++ }
++ else
++ {
++ errno = EBADF;
++ FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
++ }
++}
++
++
++/* Added by Eric Green <eric@estinc.com> to deal with burping
++ * Seagate autoloader (hopefully!).
++ */
++/* Get the SCSI ID and LUN... */
++scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd) {
++ scsi_id_t * retval;
++
++ SCSI_ADDRESS ScsiAddress;
++ BOOL bResult;
++ DWORD dwBytesReturned;
++
++ if (fd < nEntries) {
++ retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
++ retval->id = HandleTable[fd].TargetId;
++ retval->lun = HandleTable[fd].Lun;
++
++#ifdef DEBUG
++ fprintf(stderr,"SCSI:ID=%d LUN=%d\n", retval->id, retval->lun);
++#endif
++ return retval;
++ } else {
++ errno = EBADF;
++ FatalError("cannot close SCSI device - %m\n");
++ }
++
++ memset(&ScsiAddress, 0, sizeof(ScsiAddress));
++
++ ScsiAddress.Length = sizeof(ScsiAddress);
++
++ bResult = DeviceIoControl(HandleTable[fd].hDevice,
++ IOCTL_SCSI_GET_ADDRESS,
++ &ScsiAddress, sizeof(ScsiAddress),
++ &ScsiAddress, sizeof(ScsiAddress),
++ &dwBytesReturned,
++ NULL);
++
++ if (!bResult) {
++ return NULL;
++ }
++
++ retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
++ retval->id = ScsiAddress.TargetId;
++ retval->lun = ScsiAddress.Lun;
++
++#ifdef DEBUG
++ fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
++#endif
++ return retval;
++}
++
++int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
++ Direction_T Direction,
++ CDB_T *CDB,
++ int CDB_Length,
++ void *DataBuffer,
++ int DataBufferLength,
++ RequestSense_T *RequestSense)
++{
++ PSCSI_PASS_THROUGH_DIRECT ScsiPassThrough;
++
++ const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH_DIRECT) + sizeof(RequestSense_T);
++ BOOL bResult;
++ DWORD dwBytesReturned;
++
++ if (DeviceFD >= nEntries || HandleTable[DeviceFD].hDevice == INVALID_HANDLE_VALUE)
++ {
++ errno = EBADF;
++ return -1;
++ }
++
++ ScsiPassThrough = (PSCSI_PASS_THROUGH_DIRECT)malloc(dwBufferSize);
++
++ memset(ScsiPassThrough, 0, dwBufferSize);
++
++ ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
++
++ ScsiPassThrough->PathId = HandleTable[DeviceFD].PathId;
++ ScsiPassThrough->TargetId = HandleTable[DeviceFD].TargetId;
++ ScsiPassThrough->Lun = HandleTable[DeviceFD].Lun;
++ ScsiPassThrough->CdbLength = (UCHAR)CDB_Length;
++ ScsiPassThrough->SenseInfoLength = sizeof(RequestSense_T);
++ ScsiPassThrough->DataIn = Direction == Input;
++ ScsiPassThrough->DataTransferLength = DataBufferLength;
++ ScsiPassThrough->TimeOutValue = scsi_timeout;
++ ScsiPassThrough->DataBuffer = DataBuffer;
++ ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
++
++ memcpy(ScsiPassThrough->Cdb, CDB, CDB_Length);
++ dwBytesReturned = 0;
++
++ bResult = DeviceIoControl(HandleTable[DeviceFD].hDevice,
++ IOCTL_SCSI_PASS_THROUGH_DIRECT,
++ ScsiPassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT),
++ ScsiPassThrough, dwBufferSize,
++ &dwBytesReturned,
++ NULL);
++ if (bResult) {
++ if (ScsiPassThrough->ScsiStatus != 0) {
++ memcpy(RequestSense, &ScsiPassThrough[1], sizeof(RequestSense_T));
++#if DEBUG
++ fprintf(stderr, "Command failed - ScsiStatus = %d\n", ScsiPassThrough->ScsiStatus);
++ PrintRequestSense(RequestSense);
++#endif
++ bResult = false;
++ }
++ }
++ else
++ {
++#if DEBUG
++ DWORD dwError = GetLastError();
++ LPSTR lpszMessage;
++
++ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
++ fputs(lpszMessage, stderr);
++#endif
++
++ memset(RequestSense, 0, sizeof(RequestSense_T));
++ }
++
++ free(ScsiPassThrough);
++
++ return bResult ? 0 : -1;
++}
+Index: tapeinfo.c
+===================================================================
+--- tapeinfo.c (revision 139)
++++ tapeinfo.c (revision 147)
+@@ -211,25 +211,18 @@
+ unsigned int partition1_size;
+ } TapeCapacity;
+
++#if defined(DEBUG)
+ /* DEBUG */
+ static void dump_data(unsigned char *data, int len) {
+- int i;
+ if (!len) {
+ fprintf(stderr,"**NO DATA**\n");
+ return;
+ }
+
+- for (i=0;i<len;i++) {
+- if ((i % 10) == 0) {
+- if (i) {
+- fprintf(stderr,"\n");
+- }
+- fprintf(stderr,"DATA:");
+- }
+- fprintf(stderr,"%02x ",(unsigned int)*data++);
+- }
+- fprintf(stderr,"\n");
++ fprintf(stderr,"DATA:");
++ PrintHex(1, data, len);
+ }
++#endif
+
+
+
+@@ -243,7 +236,7 @@
+
+ unsigned char buffer[TAPEALERT_SIZE]; /* Overkill, but ... */
+
+- slow_bzero(buffer,TAPEALERT_SIZE); /*zero it... */
++ slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
+
+ /* now to create the CDB block: */
+ CDB[0]=0x4d; /* Log Sense */
+@@ -325,7 +318,7 @@
+ unsigned char buffer[TAPEALERT_SIZE];
+ unsigned char *walkptr;
+
+- slow_bzero(buffer,TAPEALERT_SIZE); /*zero it... */
++ slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
+
+ /* now to create the CDB block: */
+ CDB[0]=0x4d; /* Log Sense */
+@@ -676,7 +669,7 @@
+ the sernum field, and bytes 4 onward are the serial #. */
+
+ lim=(int)buffer[3];
+- bufptr= &(buffer[4]);
++ bufptr=(char *)&(buffer[4]);
+
+ printf("SerialNumber: '");
+ for (i=0;i<lim;i++) {
+@@ -702,7 +695,7 @@
+ CDB[4]=0;
+ CDB[5]=0;
+
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,6,&sense)!=0){
+ return;
+ }
+@@ -735,7 +728,7 @@
+ CDB[8]=0;
+ CDB[9]=0;
+
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+
+ /* set the timeout: */
+ SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
+@@ -789,7 +782,7 @@
+ CDB[4]=0;
+ CDB[5]=0;
+
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
+ printf("Ready: no\n");
+ return 0;
+@@ -817,7 +810,7 @@
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
+ return 1;
+ }
+Index: mtx.c
+===================================================================
+--- mtx.c (revision 139)
++++ mtx.c (revision 147)
+@@ -623,7 +623,7 @@
+ "illegal <drive-number> argument '%d' to 'unload' command\n",
+ arg2);
+ }
+- if (ElementStatus->DataTransferElementFull[arg2] < 0 ) {
++ if (!ElementStatus->DataTransferElementFull[arg2]) {
+ FatalError("Data Transfer Element %d is Empty\n", arg2);
+ }
+ /* Now see if something already lives where we wanna go... */
+@@ -715,7 +715,7 @@
+ }
+ ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense,inquiry_info,&SCSI_Flags);
+ if (!ElementStatus) {
+- PrintRequestSense(&RequestSense);
++ PrintRequestSense(&RequestSense);
+ FatalError("READ ELEMENT STATUS Command Failed\n");
+ }
+ }
+@@ -813,9 +813,6 @@
+
+ argv0=argv[0];
+
+-
+-
+-
+ parse_args(); /* also executes them as it sees them, sigh. */
+
+ #ifndef VMS
+Index: scsitape.c
+===================================================================
+--- scsitape.c (revision 139)
++++ scsitape.c (revision 147)
+@@ -41,11 +41,26 @@
+ #include "mtx.h"
+ #include "mtxl.h"
+
++#if HAVE_UNISTD_H
+ #include <unistd.h>
++#endif
++
++#if HAVE_SYS_TYPES_H
+ #include <sys/types.h>
++#endif
++
++#if HAVE_SYS_IOCTL_H
+ #include <sys/ioctl.h>
++#endif
++
++#if HAVE_SYS_MTIO_H
+ #include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
++#endif
+
++#ifdef _MSC_VER
++#include <io.h>
++#endif
++
+ void Usage(void) {
+ FatalError("Usage: scsitape -f <generic-device> <command> where <command> is:\n setblk <n> | fsf <n> | bsf <n> | eod | rewind | eject | mark <n> |\n seek <n> | read [<blksize> [<numblocks]] | write [<blocksize>] \n");
+ }
+@@ -54,7 +69,7 @@
+ static int arg[4]; /* the argument for the command, sigh. */
+
+ /* the device handle we're operating upon, sigh. */
+-static unsigned char *device; /* the text of the device thingy. */
++static char *device; /* the text of the device thingy. */
+ static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
+
+
+@@ -96,43 +111,7 @@
+
+ char *argv0;
+
+-/* A table for printing out the peripheral device type as ASCII. */
+-static char *PeripheralDeviceType[32] = {
+- "Disk Drive",
+- "Tape Drive",
+- "Printer",
+- "Processor",
+- "Write-once",
+- "CD-ROM",
+- "Scanner",
+- "Optical",
+- "Medium Changer",
+- "Communications",
+- "ASC IT8",
+- "ASC IT8",
+- "RAID Array",
+- "Enclosure Services",
+- "OCR/W",
+- "Bridging Expander", /* 0x10 */
+- "Reserved", /* 0x11 */
+- "Reserved", /* 0x12 */
+- "Reserved", /* 0x13 */
+- "Reserved", /* 0x14 */
+- "Reserved", /* 0x15 */
+- "Reserved", /* 0x16 */
+- "Reserved", /* 0x17 */
+- "Reserved", /* 0x18 */
+- "Reserved", /* 0x19 */
+- "Reserved", /* 0x1a */
+- "Reserved", /* 0x1b */
+- "Reserved", /* 0x1c */
+- "Reserved", /* 0x1d */
+- "Reserved", /* 0x1e */
+- "Unknown" /* 0x1f */
+-};
+
+-
+-
+ /* open_device() -- set the 'fh' variable.... */
+ void open_device(void) {
+
+@@ -301,7 +280,7 @@
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&RequestSense,sizeof(RequestSense_T));
++ slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&RequestSense)!=0){
+ PrintRequestSense(&RequestSense);
+ return 1;
+@@ -324,7 +303,7 @@
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+ PrintRequestSense(&sense);
+ return 1;
+@@ -349,7 +328,7 @@
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+ PrintRequestSense(&sense);
+ return 1;
+@@ -392,7 +371,7 @@
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+ PrintRequestSense(&sense);
+ return 1;
+@@ -422,7 +401,7 @@
+ CDB[9]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,10,buffer,0,&sense)!=0){
+ PrintRequestSense(&sense);
+ return 1;
+@@ -462,7 +441,7 @@
+ static int S_setblk(void) {
+ RequestSense_T sense;
+ CDB_T CDB;
+- unsigned char buffer[12];
++ char buffer[12];
+ unsigned int count = (unsigned int) arg1;
+
+
+@@ -473,7 +452,7 @@
+ CDB[4]=12; /* length of data */
+ CDB[5]=0;
+
+- slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
++ slow_bzero((char *)&sense,sizeof(RequestSense_T));
+ slow_bzero(buffer,12);
+
+ /* Now to set the mode page header: */
+@@ -679,9 +658,9 @@
+
+ /* S_write is not implemented yet! */
+ static int S_write(void) {
+- unsigned char *buffer; /* the buffer we're gonna read/write out of. */
++ char *buffer; /* the buffer we're gonna read/write out of. */
+ int buffersize;
+- int len; /* the length of the data in the buffer */
++ unsigned int len; /* the length of the data in the buffer */
+ int blocksize=arg[0];
+ int numblocks=arg[1];
+ int varsize=0; /* variable size block flag */
+@@ -755,9 +734,9 @@
+
+
+ static int S_read(void) {
+- unsigned char *buffer; /* the buffer we're going to be reading out of */
++ char *buffer; /* the buffer we're going to be reading out of */
+ int buffersize;
+- int len; /* the length of the data in the buffer */
++ unsigned int len; /* the length of the data in the buffer */
+ int blocksize=arg[0];
+ int numblocks=arg[1];
+ int varsize=0; /* variable size block flag. */
+Index: mtx.h
+===================================================================
+--- mtx.h (revision 139)
++++ mtx.h (working copy)
+@@ -18,7 +18,11 @@
+ #include "[.vms]defs.h"
+ #else /* all the Unix stuff: */
+
++#ifdef _MSC_VER
++#include "msvc/config.h" /* all the autoconf stuff. */
++#else
+ #include "config.h" /* all the autoconf stuff. */
++#endif
+
+ /* all the general Unix includes: */
+
+@@ -59,7 +63,7 @@
+ # include <sys/ioctl.h>
+ #endif
+
+-/* Now greately modified to use GNU Autoconf stuff: */
++/* Now greatly modified to use GNU Autoconf stuff: */
+ /* If we use the 'sg' interface, like Linux, do this: */
+ #if HAVE_SCSI_SG_H
+ # include <scsi/scsi.h>
+@@ -69,6 +73,27 @@
+ # define HAVE_GET_ID_LUN 1 /* signal that we have it... */
+ #endif
+
++/* Windows Native programs built using MinGW */
++#if HAVE_DDK_NTDDSCSI_H
++# include <windows.h>
++# include <ddk/ntddscsi.h>
++# undef DEVICE_TYPE
++
++typedef int DEVICE_TYPE;
++# define HAVE_GET_ID_LUN 1 /* signal that we have it... */
++#endif
++
++/* Windows Native programs built using Microsoft Visual C */
++#ifdef _MSC_VER
++# include <windows.h>
++# include <winioctl.h>
++# include <ntddscsi.h>
++# undef DEVICE_TYPE
++
++typedef int DEVICE_TYPE;
++# define HAVE_GET_ID_LUN 1 /* signal that we have it... */
++#endif
++
+ /* The 'cam' interface, like FreeBSD: */
+ #if HAVE_CAMLIB_H
+ # include <camlib.h> /* easy (?) access to the CAM user library. */
+@@ -176,10 +201,23 @@
+ unsigned char invert2; /* used for EXCHANGE command, sigh. */
+ } SCSI_Flags_T;
+
++#ifdef _MSC_VER
++typedef unsigned char boolean;
++
++#define false 0
++#define true 1
++
++
++typedef unsigned char Direction_T;
++
++#define Input 0
++#define Output 1
++#else
+ typedef enum { false, true } boolean;
+
+
+ typedef enum { Input, Output } Direction_T;
++#endif
+
+
+ typedef unsigned char CDB_T[12];
+@@ -354,6 +392,15 @@
+ } ElementModeSense_T;
+
+
++#ifdef _MSC_VER
++typedef char ElementTypeCode_T;
++
++#define AllElementTypes 0
++#define MediumTransportElement 1
++#define StorageElement 2
++#define ImportExportElement 3
++#define DataTransferElement 4
++#else
+ typedef enum ElementTypeCode
+ {
+ AllElementTypes = 0,
+@@ -363,6 +410,7 @@
+ DataTransferElement = 4
+ }
+ ElementTypeCode_T;
++#endif
+
+
+ typedef struct ElementStatusDataHeader
+Index: nsmhack.c
+===================================================================
+--- nsmhack.c (revision 139)
++++ nsmhack.c (revision 147)
+@@ -33,13 +33,13 @@
+
+ #include "mtxl.h" /* get the SCSI routines out of the main file */
+
+-/*****************************************************************
++/****************************************************************/
+ /* Variables: */
+ /****************************************************************/
+
+ /* the device handle we're operating upon, sigh. */
+-static unsigned char *device; /* the text of the device thingy. */
+-static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
++static char *device; /* the text of the device thingy. */
++static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) -1;
+ char *argv0;
+ int arg[4]; /* arguments for the command. */
+ #define arg1 (arg[0]) /* for backward compatibility, sigh */
+@@ -74,7 +74,7 @@
+ /* open_device() -- set the 'fh' variable.... */
+ void open_device(void) {
+
+- if (MediumChangerFD) {
++ if (MediumChangerFD != -1) {
+ SCSI_CloseDevice("Unknown",MediumChangerFD); /* close it, sigh... new device now! */
+ }
+
+@@ -101,7 +101,7 @@
+ /* if the device is not already open, then open it from the
+ * environment.
+ */
+- if (!MediumChangerFD) {
++ if (MediumChangerFD == -1) {
+ /* try to get it from STAPE or TAPE environment variable... */
+ device=getenv("STAPE");
+ if (device==NULL) {
+@@ -302,7 +302,7 @@
+ }
+
+ static int S_tongue_in(void) {
+-
++ return 0;
+ }
+
+ /* okay, stick our tongue out. We need a slot ID to grab a caddy from. */
+@@ -326,6 +326,7 @@
+ }
+
+ /* Okay, we have element status, so now let's assume that */
++ return 0;
+ }
+
+ /* See parse_args for the scoop. parse_args does all. */
+Index: mtxl.h
+===================================================================
+--- mtxl.h (revision 139)
++++ mtxl.h (revision 147)
+@@ -27,6 +27,9 @@
+
+ #include "mtx.h"
+
++#undef min
++#undef max
++
+ void FatalError(char *ErrorMessage, ...);
+ void *xmalloc(size_t Size);
+ void *xzmalloc(size_t Size);
+Index: config.h
+===================================================================
+--- ../release/mtx-1.3.9/config.h.in 2003-09-29 19:43:20.000000000 -0700
++++ config.h 2006-07-30 00:42:37.000000000 -0700
+@@ -1,3 +1,4 @@
++/* config.h. Generated by configure. */
+ /* Copyright 2001 Enhanced Software Technologies Inc.
+ * Released under GNU General Public License V2 or Above
+ * See http://www.gnu.org for more information about the terms of
+@@ -10,10 +11,10 @@
+ #define CONFIG_H 1
+
+ /* autoconf changes these. */
+-#define HAVE_STRING_H 0
+-#define HAVE_UNISTD_H 0
+-#define HAVE_STDLIB_H 0
+-#define HAVE_STDARG_H 0
++#define HAVE_STRING_H 1
++#define HAVE_UNISTD_H 1
++#define HAVE_STDLIB_H 1
++#define HAVE_STDARG_H 1
+ #define HAVE_SCSI_SCSI_H 0
+ #define HAVE_SCSI_SCSI_IOCTL_H 0
+ #define HAVE_SCSI_SG_H 0
+@@ -23,10 +24,12 @@
+ #define HAVE_SYS_SCSI_CTL_H 0
+ #define HAVE_DSLIB_H 0
+ #define HAVE_DU_DEFS_H 0
+-#define HAVE_SYS_STAT_H 0
+-#define HAVE_SYS_TYPES_H 0
+-#define HAVE_FCNTL_H 0
++#define HAVE_SYS_STAT_H 1
++#define HAVE_SYS_TYPES_H 1
++#define HAVE_FCNTL_H 1
+ #define HAVE_SYS_IOCTL_H 0
++#define HAVE_SYS_MTIO_H 0
++#define HAVE_DDK_NTDDSCSI_H 1
+
+ #define WORDS_BIGENDIAN 0
+
+Index: mtxl.c
+===================================================================
+--- ../release/mtx-1.3.9/mtxl.c 2003-10-02 23:03:20.000000000 -0700
++++ mtxl.c 2006-07-30 00:49:31.000000000 -0700
+@@ -53,6 +53,11 @@
+ # include "scsi_linux.c"
+ #endif
+
++/* the IOCTL_SCSI_PASSTHROUGH interface is used on Windows. */
++#if HAVE_DDK_NTDDSCSI_H || defined(_MSC_VER)
++# include "scsi_win32.c"
++#endif
++
+ /* The 'uscsi' interface is used on Solaris. */
+ #if HAVE_SYS_SCSI_IMPL_USCSI_H
+ # include "scsi_sun.c"
+@@ -78,6 +83,7 @@
+ #include "[.vms]scsi.c"
+ #endif
+
++void PrintHex(int Indent, unsigned char *Buffer, int Length);
+ extern char *argv0; /* something to let us do good error messages. */
+
+ /* create a global RequestSenseT value. */
+@@ -104,6 +110,9 @@
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
+ Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
+ {
++#ifdef DEBUG
++ fprintf(stderr, "SCSI Inquiry Command failed\n");
++#endif
+ free(Inquiry);
+ return NULL; /* sorry! */
+ }
+@@ -111,36 +120,27 @@
+ }
+
+
++#if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE)
+ /* DEBUG */
+ static void dump_cdb(unsigned char *CDB, int len) {
+- int i;
+ fprintf(stderr,"CDB:");
+- for (i=0;i<len;i++) {
+- fprintf(stderr,"%02x ",CDB[i]);
+- }
+- fprintf(stderr,"\n");
++ PrintHex(1, CDB, len);
+ }
++#endif
+
+
++#if defined(DEBUG_NSM) || defined(DEBUG_ADIC)
+ /* DEBUG */
+ static void dump_data(unsigned char *data, int len) {
+- int i;
+ if (!len) {
+ fprintf(stderr,"**NO DATA**\n");
+ return;
+ }
+
+- for (i=0;i<len;i++) {
+- if ((i % 10) == 0) {
+- if (i) {
+- fprintf(stderr,"\n");
+- }
+- fprintf(stderr,"DATA:");
+- }
+- fprintf(stderr,"%02x ",(unsigned int)*data++);
+- }
+- fprintf(stderr,"\n");
++ fprintf(stderr,"DATA:");
++ PrintHex(1, data, len);
+ }
++#endif
+
+
+ int BigEndian16(unsigned char *BigEndianData)
+@@ -255,7 +255,7 @@
+
+
+ /* Okay, this is a hack for the NSM modular jukebox series, which
+- * uses the "SEND DIAGNOSTIC" command do to shit.
++ * uses the "SEND DIAGNOSTIC" command to do shit.
+ */
+
+ int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command,
+@@ -346,6 +346,10 @@
+ SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0) {
++#ifdef DEBUG
++ PrintRequestSense(&scsi_error_sense);
++ fprintf(stderr, "Initialize Element Status (0x07) failed\n");
++#endif
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+@@ -364,6 +368,10 @@
+ CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+ if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0) {
++#ifdef DEBUG_MODE_SENSE
++ PrintRequestSense(&scsi_error_sense);
++ fprintf(stderr, "Eject (0x1B) failed\n");
++#endif
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+@@ -396,7 +404,8 @@
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
+ &input_buffer,sizeof(input_buffer),&scsi_error_sense) != 0) {
+ #ifdef DEBUG_MODE_SENSE
+- fprintf(stderr,"Could not execute mode sense...\n");
++ PrintRequestSense(&scsi_error_sense);
++ fprintf(stderr,"Mode sense (0x1A) for Page 0x1D failed\n");
+ fflush(stderr);
+ #endif
+ return NULL; /* sorry, couldn't do it. */
+@@ -405,18 +414,7 @@
+ /* Could do it, now build return value: */
+
+ #ifdef DEBUG_MODE_SENSE
+- {
+- int i;
+- for (i=0;i<30;i+=3) {
+- fprintf(stderr,"ib[%d]=%d ib[%d]=%d ib[%d]=%d\n",
+- i,input_buffer[i],i+1,input_buffer[i+1],
+- i+2,input_buffer[i+2]);
+- /* fprintf(stderr,"input_buffer[0]=%d input_buffer[3]=%d\n",
+- * input_buffer[0], input_buffer[3]);
+- */
+- }
+- fflush(stderr);
+- }
++ PrintHex(0, input_buffer, 30);
+ #endif
+ /* first, skip past: mode data header, and block descriptor header if any */
+ sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
+@@ -444,11 +442,11 @@
+
+ #ifdef DEBUG_MODE_SENSE
+ fprintf(stderr,"rawNumStorage= %d %d rawNumImportExport= %d %d\n",
+- sense_page->NumStorageHi,sense_page->NumStorageLo,
+- sense_page->NumImportExportHi, sense_page->NumImportExportLo);
++ sense_page->NumStorageHi,sense_page->NumStorageLo,
++ sense_page->NumImportExportHi, sense_page->NumImportExportLo);
+ fprintf(stderr,"rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
+- sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
+- sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
++ sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
++ sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
+ fflush(stderr);
+ #endif
+
+@@ -620,19 +618,16 @@
+ CDB[11] = 0; /* Control */
+
+ #ifdef DEBUG_BARCODE
+- {
+- int i;
+- fprintf(stderr,"CDB= ");
+- for (i=0;i<12;i++) {
+- fprintf(stderr,"%x ",CDB[i]);
+- }
+- fprintf(stderr,"\n");
+- fflush(stderr);
+- }
++ fprintf(stderr,"CDB:\n");
++ PrintHex(2, CDB, 12);
+ #endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+ DataBuffer,NumBytes, RequestSense) != 0){
++
++#ifdef DEBUG
++ fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]);
++#endif
+ /* okay, first see if we have sense key of 'illegal request',
+ additional sense code of '24', additional sense qualfier of
+ '0', and field in error of '4'. This means that we issued a request
+@@ -654,15 +649,8 @@
+ CDB[1] &= ~0x10; /* clear bar code flag! */
+
+ #ifdef DEBUG_BARCODE
+- {
+- int i;
+- fprintf(stderr,"CDB= ");
+- for (i=0;i<12;i++) {
+- fprintf(stderr,"%x ",CDB[i]);
+- }
+- fprintf(stderr,"\n");
+- fflush(stderr);
+- }
++ fprintf(stderr,"CDB:\n");
++ PrintHex(2, CDB, 12);
+ #endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+@@ -679,14 +667,8 @@
+ #ifdef DEBUG_BARCODE
+ /* print a bunch of extra debug data :-(. */
+ PrintRequestSense(RequestSense); /* see what it sez :-(. */
+- {
+- int i;
+- fprintf(stderr,"Data:");
+- for (i=0;i<40;i++) {
+- fprintf(stderr,"%02x ",DataBuffer[i]);
+- }
+- fprintf(stderr,"\n");
+- }
++ fprintf(stderr,"Data:\n");
++ PrintHex(2, DataBuffer, 40);
+ #endif
+ return DataBuffer; /* we succeeded! */
+ }
+@@ -703,7 +685,7 @@
+ ) {
+
+ unsigned char *DataBuffer; /* size of data... */
+- unsigned int real_numbytes;
++ int real_numbytes;
+
+
+ DataBuffer=SendElementStatusRequestActual(MediumChangerFD,
+@@ -950,34 +932,42 @@
+ BigEndian16(TransportElementDescriptor
+ ->SourceStorageElementAddress);
+
+- if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
+- FatalError("Too many Data Transfer Elements Reported\n");
+- }
+- if (ElementStatusPage->VolBits & E2_PVOLTAG) {
+- copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+- ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
+- } else {
+- ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+- }
+- if (ElementStatusPage->VolBits & E2_AVOLTAG) {
+- copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+- ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
+- } else {
+- ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+- }
+- ElementStatus->DataTransferElementCount++;
+- /* 0 actually is a usable element address */
+- /* if (DataTransferElementAddress == 0) */
+- /* FatalError( */
+- /* "illegal Data Transfer Element Address %d reported\n", */
+- /* DataTransferElementAddress); */
+- break;
+- default:
+- FatalError("illegal Element Type Code %d reported\n",
+- ElementStatusPage->ElementTypeCode);
+- }
+- }
++#if DEBUG
++ fprintf(stderr, "%d: ElementAddress = %d, Full = %d, SourceElement = %d\n",
++ ElementStatus->DataTransferElementCount,
++ ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount],
++ ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount],
++ ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount]);
++#endif
++ if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
++ FatalError("Too many Data Transfer Elements Reported\n");
++ }
++ if (ElementStatusPage->VolBits & E2_PVOLTAG) {
++ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
++ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
++ } else {
++ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
++ }
++ if (ElementStatusPage->VolBits & E2_AVOLTAG) {
++ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
++ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
++ } else {
++ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
++ }
++ ElementStatus->DataTransferElementCount++;
++
++ /* 0 actually is a usable element address */
++ /* if (DataTransferElementAddress == 0) */
++ /* FatalError( */
++ /* "illegal Data Transfer Element Address %d reported\n", */
++ /* DataTransferElementAddress); */
++ break;
++ default:
++ FatalError("illegal Element Type Code %d reported\n",
++ ElementStatusPage->ElementTypeCode);
++ }
+ }
++ }
+ }
+
+ /********************* Real ReadElementStatus ********************* */
+@@ -1008,7 +998,6 @@
+ int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
+
+ int empty_idx=0;
+- int invalid_sources=0;
+ boolean is_attached = false;
+ int i,j;
+
+@@ -1049,7 +1038,7 @@
+
+ EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
+ for (i=0;i<mode_sense->NumStorage;i++) {
+- EmptyStorageElementAddress[i]=-1;
++ EmptyStorageElementAddress[i] = -1;
+ }
+
+ /* Okay, now to send some requests for the various types of stuff: */
+@@ -1076,6 +1065,9 @@
+ #endif
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
++#ifdef DEBUG_MODE_SENSE
++ PrintRequestSense(RequestSense);
++#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+@@ -1107,6 +1099,9 @@
+ #endif
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
++#ifdef DEBUG_MODE_SENSE
++ PrintRequestSense(RequestSense);
++#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+@@ -1138,6 +1133,9 @@
+ #endif
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
++#ifdef DEBUG_MODE_SENSE
++ PrintRequestSense(RequestSense);
++#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+@@ -1172,6 +1170,9 @@
+ #endif
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
++#ifdef DEBUG_MODE_SENSE
++ PrintRequestSense(RequestSense);
++#endif
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+@@ -1223,34 +1224,24 @@
+ * is obviously defective:
+ */
+ /* pass one: */
+- invalid_sources=0; /* no invalid sources yet! */
+ for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
+ int elnum;
+- int translated_elnum = -1;
+ /* if we have an element, then ... */
+ if (ElementStatus->DataTransferElementFull[i]) {
+ elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
+ /* if we have an element number, then ... */
+ if (elnum >= 0) {
+- /* Now to translate the elnum: */
+- for (j=0; j<ElementStatus->StorageElementCount; j++) {
+- if (elnum == ElementStatus->StorageElementAddress[j]) {
+- translated_elnum=j;
+- }
+- }
+- /* now see if the element # is already occupied: */
+- if (ElementStatus->StorageElementFull[translated_elnum]) {
+- invalid_sources=1;
+- break; /* break out of the loop! */
+- } else {
+- /* properly set the source... */
+- ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
+- translated_elnum;
+- }
+-
+- } else {
+- /* if element # was not >=0, then we had an invalid source anyhow! */
+- invalid_sources=1;
++ /* Now to translate the elnum: */
++ ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
++ for (j=0; j<ElementStatus->StorageElementCount; j++) {
++ if (elnum == ElementStatus->StorageElementAddress[j]) {
++ /* now see if the element # is already occupied:*/
++ if (!ElementStatus->StorageElementFull[j]) {
++ /* properly set the source... */
++ ElementStatus->DataTransferElementSourceStorageElementNumber[i]= j;
++ }
++ }
++ }
+ }
+ }
+ }
+@@ -1267,21 +1258,19 @@
+ * by the user interface. This is an invalid value, but more useful for us
+ * to have than just crapping out here :-(.
+ */
+- if (invalid_sources) {
+- empty_idx=0;
+- for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
+- if (ElementStatus->DataTransferElementFull[i]) {
++ empty_idx=0;
++ for (i = 0; i < ElementStatus->DataTransferElementCount; i++) {
++ if (ElementStatus->DataTransferElementFull[i] &&
++ ElementStatus->DataTransferElementSourceStorageElementNumber[i] < 0) {
+ #ifdef DEBUG_TAPELIST
+- fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
+- i,
+- ElementStatus->DataTransferElementSourceStorageElementNumber[i],
+- EmptyStorageElementAddress[empty_idx],
+- empty_idx);
++ fprintf(stderr,"for drive %d, changing to %d (empty slot #%d)\n",
++ i,
++ EmptyStorageElementAddress[empty_idx],
++ empty_idx);
+ #endif
+
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
+ EmptyStorageElementAddress[empty_idx++];
+- }
+ }
+ }
+
+@@ -1337,9 +1326,9 @@
+ CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
+ CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
+ CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
+- CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
++ CDB[5] = SourceAddress & 0xFF; /* Source Address LSB */
+ CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
+- CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
++ CDB[7] = DestinationAddress & 0xFF; /* Destination Address LSB */
+ CDB[8] = 0; /* Reserved */
+ CDB[9] = 0; /* Reserved */
+ if (flags->invert) {
+@@ -1351,7 +1340,11 @@
+ CDB[11] = 0 | (flags->eepos <<6); /* Control */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
+- NULL, 0, RequestSense) != 0) {
++ NULL, 0, RequestSense) != 0) {
++
++#ifdef DEBUG
++ fprintf(stderr, "Move Medium (0x%02X) failed\n", CDB[0]);
++#endif
+ return RequestSense;
+ }
+ free(RequestSense);
+@@ -1372,9 +1365,9 @@
+ CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
+ CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
+ CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
+- CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
++ CDB[5] = SourceAddress & 0xFF; /* Source Address LSB */
+ CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
+- CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
++ CDB[7] = DestinationAddress & 0xFF; /* Destination Address LSB */
+ CDB[8] = (Dest2Address>>8) & 0xFF; /* move destination back to source? */
+ CDB[9] = Dest2Address & 0xFF; /* move destination back to source? */
+ CDB[10]=0;
+@@ -1418,12 +1411,53 @@
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
+ NULL, 0, RequestSense) != 0) {
++#ifdef DEBUG
++ fprintf(stderr, "Erase (0x19) failed\n");
++#endif
+ return RequestSense;
+ }
+ free(RequestSense);
+ return NULL; /* Success! */
+ }
+
++static char Spaces[] = " ";
++
++void PrintHex(int Indent, unsigned char *Buffer, int Length)
++{
++ int idxBuffer;
++ int idxAscii;
++ int PadLength;
++ char cAscii;
++
++ for (idxBuffer = 0; idxBuffer < Length; idxBuffer++) {
++ if ((idxBuffer % 16) == 0) {
++ if (idxBuffer > 0) {
++ fputc('\'', stderr);
++
++ for (idxAscii = idxBuffer - 16; idxAscii < idxBuffer; idxAscii++) {
++ cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x7F ? Buffer[idxAscii] : '.';
++ fputc(cAscii, stderr);
++ }
++ fputs("'\n", stderr);
++ }
++ fprintf(stderr, "%.*s%04X: ", Indent, Spaces, idxBuffer);
++ }
++ fprintf(stderr, "%02X ", (unsigned char)Buffer[idxBuffer]);
++ }
++
++ PadLength = 16 - (Length % 16);
++
++ if (PadLength > 0) {
++ fprintf(stderr, "%.*s'", 3 * PadLength, Spaces);
++
++ for (idxAscii = idxBuffer - (16 - PadLength); idxAscii < idxBuffer; idxAscii++) {
++ cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x80 ? Buffer[idxAscii] : '.';
++ fputc(cAscii, stderr);
++ }
++ fputs("'\n", stderr);
++ }
++ fflush(stderr);
++}
+
+ #ifdef LONG_PRINT_REQUEST_SENSE
+
+@@ -1488,12 +1522,9 @@
+ #else
+ void PrintRequestSense(RequestSense_T *RequestSense)
+ {
+- int i;
+- fprintf(stderr, "mtx: Request Sense: %02X",
+- ((unsigned char *) RequestSense)[0]);
+- for (i = 1; i < sizeof(RequestSense_T); i++)
+- fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
+- fprintf(stderr, "\n");
++ fprintf(stderr, "mtx: Request Sense: %02X\n",
++ ((unsigned char *) RequestSense)[0]);
++ PrintHex(2, (char *)RequestSense, sizeof(RequestSense_T));
+ }
+
+ #endif
+Index: Makefile
+===================================================================
+--- ../release/mtx-1.3.9/Makefile.in 2006-02-20 13:42:10.000000000 -0800
++++ Makefile 2006-07-30 01:22:00.000000000 -0700
+@@ -11,26 +11,28 @@
+ # Version # for 'make dist'...
+ VERSION=1.3.9
+
+-BINS = mtx tapeinfo loaderinfo scsitape nsmhack
++BINS = mtx.exe tapeinfo.exe loaderinfo.exe scsitape.exe nsmhack.exe
++DBGS := $(BINS:%.exe=%.dbg)
+
+-TARGET = @TARGET@
+-CPU = @CPU@
+-CC = @CC@
+-INSTALL = @INSTALL@
+-
+-CFLAGS = @CFLAGS@
+-CPPFLAGS = @CPPFLAGS@ -DVERSION="\"$(VERSION)\""
+-LDFLAGS = @LDFLAGS@
+-LIBS = @LIBS@
++TARGET = mingw
++CPU = 386
++CC = mingw32-gcc
++INSTALL = install -c
++
++CFLAGS = -g -O2
++CPPFLAGS = -DVERSION="\"$(VERSION)\""
++LDFLAGS =
++LIBS =
++USE_OBJCOPY = yes
+
+ INSTALL_DOC = $(INSTALL) -m 644
+ INSTALL_BIN = $(INSTALL) -m 755
+ INSTALL_DIR = $(INSTALL) -m 755 -d
+
+-prefix = @prefix@
+-exec_prefix = @exec_prefix@
+-sbindir = @sbindir@
+-mandir = @mandir@
++prefix = dummy
++exec_prefix = ${prefix}
++sbindir = ${exec_prefix}/bin
++mandir = ${prefix}/man
+
+ #
+ # Linux on x86...
+@@ -40,6 +42,11 @@
+ CPPFLAGS += -I/usr/src/linux/include -DLONG_PRINT_REQUEST_SENSE=1
+ endif
+
++ifeq ($(TARGET),mingw)
++CFLAGS += -Wall
++CPPFLAGS += -DLONG_PRINT_REQUEST_SENSE=1
++endif
++
+ #
+ # FreeBSD on x86...
+ #
+@@ -82,12 +89,22 @@
+ See vms/000readme for information.
+ endif
+
++%.dbg : %.exe
++ifeq ($(USE_OBJCOPY),yes)
++ mingw32-objcopy --only-keep-debug $< $@
++ mingw32-objcopy --strip-debug $<
++ mingw32-objcopy --add-gnu-debuglink=$@ $<
++else
++ strip $< -o $@
++endif
++
+ all: $(BINS)
+
+-install: $(BINS)
++dbgs: $(DBGS)
++
++install: $(BINS) $(DBGS)
+ $(INSTALL_DIR) $(sbindir)
+ for file in $(BINS); do \
+- strip "$$file" ; \
+ $(INSTALL_BIN) "$$file" $(sbindir) ; \
+ done
+ $(INSTALL_DIR) $(mandir) $(mandir)/man1
+@@ -98,7 +115,9 @@
+ clean:
+ rm -f *.o *~
+ rm -f $(BINS)
+- rm -f mam2debug mam2debug2
++ rm -f $(DBGS)
++ rm -f mam2debug.exe mam2debug2.exe
++ rm -rf autom4te.cache
+
+ distclean: clean
+ rm -f Makefile config.log config.cache config.status
+@@ -106,27 +125,26 @@
+ dist: distclean
+ ./makedist $(VERSION)
+
+-loaderinfo: loaderinfo.o mtxl.o mtxl.h mtx.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o loaderinfo loaderinfo.o mtxl.o $(EXTRA) $(LIBS)
+-
++loaderinfo.exe: loaderinfo.o mtxl.o mtxl.h mtx.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ loaderinfo.o mtxl.o $(EXTRA) $(LIBS)
+
+-nsmhack: nsmhack.o mtxl.o $(EXTRA)
+- $(CC) $(LDFLAGS) -o nsmhack nsmhack.o mtxl.o $(EXTRA) $(LIBS)
++nsmhack.exe: nsmhack.o mtxl.o $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ nsmhack.o mtxl.o $(EXTRA) $(LIBS)
+
+-mtx: mtx.o mtxl.o mtxl.h mtx.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o mtx mtx.o mtxl.o $(EXTRA) $(LIBS)
++mtx.exe: mtx.o mtxl.o mtxl.h mtx.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ mtx.o mtxl.o $(EXTRA) $(LIBS)
+
+-mam2debug: mtxl.o mam2debug.o mtx.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o mam2debug mtxl.o mam2debug.o $(EXTRA) $(LIBS)
++mam2debug.exe: mtxl.o mam2debug.o mtx.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ mtxl.o mam2debug.o $(EXTRA) $(LIBS)
+
+-tapeinfo: tapeinfo.o mtxl.o mtx.h mtxl.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o tapeinfo tapeinfo.o mtxl.o $(EXTRA) $(LIBS)
++tapeinfo.exe: tapeinfo.o mtxl.o mtx.h mtxl.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ tapeinfo.o mtxl.o $(EXTRA) $(LIBS)
+
+-mam2debug2: mtxl.o mam2debug2.o mtx.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o mam2debug2 mtxl.o mam2debug2.o $(EXTRA) $(LIBS)
++mam2debug2.exe: mtxl.o mam2debug2.o mtx.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ mtxl.o mam2debug2.o $(EXTRA) $(LIBS)
+
+-scsitape: scsitape.o mtxl.o mtxl.h mtx.h $(EXTRA)
+- $(CC) $(LDFLAGS) -o scsitape scsitape.o mtxl.o $(EXTRA) $(LIBS)
++scsitape.exe: scsitape.o mtxl.o mtxl.h mtx.h $(EXTRA)
++ $(CC) $(LDFLAGS) -o $@ scsitape.o mtxl.o $(EXTRA) $(LIBS)
+
+ scsitape.o: scsitape.c mtx.h mtxl.h
+
+@@ -140,6 +158,6 @@
+
+ mtx.o: mtx.c mtx.h mtxl.h
+
+-mtxl.o: mtxl.c mtx.h mtxl.h scsi_linux.c
++mtxl.o: mtxl.c mtx.h mtxl.h scsi_linux.c scsi_win32.c
+
+ nsmhack.o: nsmhack.c mtxl.h mtx.h