]> git.sur5r.net Git - bacula/bacula/commitdiff
Add Microsoft Windows port of MT and MTX to depkgs.
authorRobert Nelson <robertn@the-nelsons.org>
Sun, 30 Jul 2006 13:32:36 +0000 (13:32 +0000)
committerRobert Nelson <robertn@the-nelsons.org>
Sun, 30 Jul 2006 13:32:36 +0000 (13:32 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@3200 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/src/win32/README.mingw32
bacula/src/win32/build-dependencies
bacula/src/win32/patches/mingw-utils.patch
bacula/src/win32/patches/mt.patch [new file with mode: 0644]
bacula/src/win32/patches/mtx.patch [new file with mode: 0644]

index 8acccdb4057c95aac154085c5b200537ad7b4930..354f37ef1cdc23e2366d72451a28204828065e8f 100644 (file)
@@ -99,7 +99,7 @@ to ensure that you pick up all the new Win32 changes:
 
 If one of the patches change in src/win32/patches, you shouldn't
 need to update the cross-tools or dependencies for it to build.
-You can new patches by doing the following (we assume the patch
+You can install new patches by doing the following (we assume the patch
 in question is for openssl):
 
      cd <top-level>
index 226905945aa7977225446822805b07ed70803a6c..1dbca6e7af8dff7ee084dc8a4c9f5425d543f888 100755 (executable)
@@ -24,6 +24,10 @@ URL_NSIS_BIN=http://superb-west.dl.sourceforge.net/sourceforge/nsis/nsis-2.17.zi
 DIR_NSIS_BIN=
 URL_NSIS_SRC=http://superb-west.dl.sourceforge.net/sourceforge/nsis/nsis-2.17-src.tar.bz2
 DIR_NSIS_SRC=
+URL_MTX=http://superb-west.dl.sourceforge.net/sourceforge/mtx/mtx-1.3.9.tar.gz
+DIR_MTX=
+URL_MT=ftp://ftp.ibiblio.org/pub/linux/system/backup/mt-st-0.9b.tar.gz
+DIR_MT=
 
 cwd=`pwd`
 cd `dirname $0`
@@ -338,6 +342,37 @@ process_nsis()
        cp -p build/release/makensis/makensis ../../nsis
 }
 
+process_mtx()
+{
+       if get_source "${URL_MTX}" "${DIR_MTX}"
+       then
+               echo Patching mtx
+               # We can't run configure in a cross-compile environment so we
+               # patch the files to the correct values
+               cp -f config.h.in config.h
+               cp -f Makefile.in Makefile
+               rm -f configure
+               do_patch mtx.patch
+       fi
+       echo Building mtx
+       do_make Makefile prefix=${DEPPKG_DIR} all
+       echo Installing mtx
+       do_make Makefile prefix=${DEPPKG_DIR} install
+}
+
+process_mt()
+{
+       if get_source "${URL_MT}" "${DIR_MT}"
+       then
+               echo Patching mt
+               do_patch mt.patch
+       fi
+       echo Building mt
+       do_make Makefile prefix=${DEPPKG_DIR} all
+       echo Installing mt
+       do_make Makefile PREFIX=${DEPPKG_DIR} install
+}
+
 if [ "$#" -eq 0 ]
 then
        process_zlib
@@ -350,6 +385,7 @@ then
        process_wxWidgets
        process_scons
        process_nsis
+       process_mtx
 else
        for dependency in "$@"
        do
index fc0d6494023020762cf4d5c4e47f0775f61e9849..48bb6acb525756dc1853d73caded7c21f1a71ecb 100644 (file)
@@ -25,21 +25,6 @@ diff -u -r1.3 Makefile.am
  
  instdir = /tmp/$(PACKAGE)-$(VERSION)
  
-Index: autogen.sh
-===================================================================
-RCS file: /cvsroot/mingw/utils/autogen.sh,v
-retrieving revision 1.3
-diff -u -r1.3 autogen.sh
---- autogen.sh 6 Oct 2002 09:55:42 -0000       1.3
-+++ autogen.sh 24 Jun 2006 00:31:11 -0000
-@@ -1,6 +1,6 @@
- #! /bin/sh
--export WANT_AUTOMAKE_1_6=1
-+export WANT_AUTOMAKE_1_7=1
- aclocal \
- && autoheader \
 Index: configure.ac
 ===================================================================
 RCS file: /cvsroot/mingw/utils/configure.ac,v
diff --git a/bacula/src/win32/patches/mt.patch b/bacula/src/win32/patches/mt.patch
new file mode 100644 (file)
index 0000000..63a6a51
--- /dev/null
@@ -0,0 +1,1661 @@
+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;
++}
diff --git a/bacula/src/win32/patches/mtx.patch b/bacula/src/win32/patches/mtx.patch
new file mode 100644 (file)
index 0000000..35e39f6
--- /dev/null
@@ -0,0 +1,1576 @@
+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