Imported Upstream version 1.2.16rel upstream/1.2.16rel
authorBdale Garbee <bdale@gag.com>
Thu, 5 Jun 2008 23:26:55 +0000 (17:26 -0600)
committerBdale Garbee <bdale@gag.com>
Thu, 5 Jun 2008 23:26:55 +0000 (17:26 -0600)
61 files changed:
CHANGES [new file with mode: 0644]
COMPATABILITY [new file with mode: 0644]
FAQ [new file with mode: 0644]
LICENSE [new file with mode: 0644]
LICENSE.html [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
config.guess [new file with mode: 0755]
config.h [new file with mode: 0644]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0755]
contrib/MTX.html [new file with mode: 0644]
contrib/README [new file with mode: 0644]
contrib/TapeChanger-MTX-0.71b.tar.gz [new file with mode: 0644]
contrib/config_sgen_solaris.sh [new file with mode: 0755]
contrib/mtx-changer [new file with mode: 0644]
contrib/mtx.py [new file with mode: 0644]
contrib/mtxctl-0.0.2.tar.gz [new file with mode: 0644]
contrib/tapechanger.html [new file with mode: 0644]
contrib/tapeinfo.py [new file with mode: 0644]
contrib/tapeload.pl [new file with mode: 0644]
contrib/tapeunload.pl [new file with mode: 0644]
du/defs.h [new file with mode: 0644]
du/scsi.c [new file with mode: 0644]
install-sh [new file with mode: 0755]
loaderinfo.1 [new file with mode: 0644]
loaderinfo.c [new file with mode: 0644]
makedist [new file with mode: 0755]
mam2debug.c [new file with mode: 0644]
mam2debug2.c [new file with mode: 0644]
mtx.1 [new file with mode: 0644]
mtx.c [new file with mode: 0644]
mtx.doc [new file with mode: 0644]
mtx.h [new file with mode: 0644]
mtx.spec [new file with mode: 0644]
mtx.spec.in [new file with mode: 0644]
mtxl.README.html [new file with mode: 0644]
mtxl.c [new file with mode: 0644]
mtxl.h [new file with mode: 0644]
scsi_aix.c [new file with mode: 0644]
scsi_freebsd.c [new file with mode: 0644]
scsi_hpux.c [new file with mode: 0644]
scsi_linux.c [new file with mode: 0644]
scsi_sgi.c [new file with mode: 0644]
scsi_sun.c [new file with mode: 0644]
scsitape.1 [new file with mode: 0644]
scsitape.c [new file with mode: 0644]
sparc-patch1 [new file with mode: 0644]
tapeinfo.1 [new file with mode: 0644]
tapeinfo.c [new file with mode: 0644]
vms/000readme [new file with mode: 0644]
vms/build.com [new file with mode: 0644]
vms/defs.h [new file with mode: 0644]
vms/descrip.mms [new file with mode: 0644]
vms/ldrset.c [new file with mode: 0644]
vms/ldrset.cld [new file with mode: 0644]
vms/ldrutil.mar [new file with mode: 0644]
vms/scsi.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..8c055fd
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,285 @@
+CHANGES, mtx 1.2.16:
+- Fix a very special case that ADIC loaders presented to element status
+  parser
+- Fix a buffer overflow w/some weird hardware, courtesy of Lars
+  Fredricksen
+- Make version number include a "rel" so that the rpm will overrride  
+  a "pre" version's rpm, i.e., --version will  say "mtx 1.2.16rel". 
+- Added TapeChanger Perl package for use with Amanda to the contrib
+  directory (see http://www.killfile.org/~tskirvin/software/tapechanger.html )
+  courtesy of Tim Skirvin. 
+- Fixed spec file that was broken by 1.2.15 :-(, courtesy of Kenneth
+  Porter
+
+CHANGES, mtx 1.2.15:
+- Some Solaris fixes, courtesy of Matt Ward
+- Fix URL in .spec file
+
+CHANGES, mtx 1.2.14:
+- Fix so it'll work if 0 is result of SCSI open (e.g., in cron jobs on Linux)
+- Move changelog to end of .spec file for easier reading
+- Added a bit of text to beginning of COMPATIBILITY file
+
+CHANGES, mtx 1.2.13:
+- Fixed some autoloader bugs w/autoloaders that don't report an arm. 
+- Fixed barcode backoff.
+- Added "nobarcode" option
+- Increased timeout for 'mtx inventory' to 30 minutes
+  (note: may increase this even more if needed, please let me know!)
+- Shortened timeout for 'mtx inquiry' to 30 seconds
+- tapeinfo now prints SCSI ID/LUN info if available (only on Linux at the
+  moment, sigh). 
+- update documentation w/new email addresses, updated compile directions,
+  various fixes. 
+
+CHANGES, mtx 1.2.12:
+- Fix FreeBSD compile bugs
+- Fix SGI compile bugs
+- Add HP/UX port (I hope!), courtesy of William Smith. 
+- Re-wrote ReadElementStatus to make work for %!@# brain dead firmware that
+  reports non-existent drives (I hope!). Also has side-effect of now working
+  with multiple-arm libraries (though it only sees first arm!).
+- Cleaned up all -Wall messages.
+- Cleaned up Linux Sparc, installs loaderinfo.1, courtesy of Matt Dainty.
+- tapeinfo now reports status of CheckUnitReady.  
+- tapeinfo no longer puts out Block Position if CheckUnitReady says 'no'.
+- tapeinfo now puts out Density Code and Medium Type/Not Loaded (modification
+    of patches sent in by Bob Rahe)
+
+CHANGES, mtx 1.2.11:
+- Added a GNU autoconf Makefile.in (still provide a Makefile for your use)
+  *WARNING* autoconf is not yet working on all supported OS's! You may need
+  to do the old-fashioned 'edit Makefile' bit!
+- Changed mtx.h and mtxl.c to include and define various things based upon
+  what features autoconf detected (e.g., if 'camlib.h', use FreeBSD-style
+  'cam', if 'scsi/sg.h' use Linux-style 'sg', etc.). If I ever port to a
+  Unix that has same SCSI interface as one of the existing ports, autoconf
+  will handle it without me having to add another set of #if's or #ifdefs. 
+- Went ahead and tossed mtxctl into contrib.
+- In 'tapeinfo', skip \0 characters in serial numbers (some use \0 
+  terminator, some do not, skip it if this one does). 
+- in 'tapeinfo', dump out the block position and (if at BOP) the "BOP: Yes"
+  flag. Also dump out other info such as block limits. 
+- Put file 'sparc-patch1' contributed by Chaskiel M Grundman, and applied
+  it (sigh)
+- Added tapeinfo.py to 'contrib' directory
+- Updated mtx.py in 'contrib' directory
+- Created 'loaderinfo' program to report some misc. info about loaders.
+- Created 'scsitape' program so that I don't have to keep messing with
+  #@$%@! tape ioctls on the various Unixes that I'm porting tape software
+  to.  (But see the warnings!).
+- Applied the Solaris patch to the read_element_status command (sigh). 
+- Added timeout adjustment to the SCSI subsystem. 
+- WARNING: DIGITAL UNIX AND VMS ARE PROBABLY IRREPERABLY BROKEN, due to the
+  timeout changes to the SCSI subsystem. If anybody wishes to fix them,
+  feel free to send me patches. 
+- added contrib program "mtx-changer" (an Amanda tape changer script for
+  ?Solaris? that uses mtx rather than stc)
+- Jiggered Linux SCSI module for smarter error conditions handling (there are
+  some error conditions that are normal for READ of tape drives).
+- Added contrib program "config_sgen_solaris.sh" which should ease 
+  setting up the 'sgen' driver on Solaris 8 (still no easy Solaris 7 or
+  below config). 
+
+CHANGES, mtx 1.2.10:
+- Added FAQ and COMPATIBILITY (feel free to send me patches to these files!)
+- Added LICENSE
+- Added serial number to 'tapeinfo' output. 
+- Fixed stupid syntax error in mtx.c (compiled with gcc, not with others!)
+- Fixed spec file for building rpms (maybe).
+- Added an 'erase' command (undocumented) for use on Linux for doing
+  short erases on tapes (the Linux device driver defaults to a long erase). 
+- Made mtx inventory return an error code if the inventory
+  fails, so that we can wait for inventory to be completed at system
+  startup for libraries that auto-inventory (sigh). 
+
+CHANGES, mtx 1.2.9:
+- Added an 'eject' command that, if directed to a tape drive, will eject the
+  tape, and for some autoloaders, if directed to LUN 1, will eject the entire
+  magazine.
+- Fixed the 'transfer' command to be 1 based rather than 0 based (sigh)
+- Now properly reports bar code for the tape that's in the tape drive.
+- Added some miscellaneous Python and Perl scripts to 'contrib'. Thanks
+  to Frank Samuelson for the Perl scripts. 
+
+CHANGES, mtx 1.2.8:
+- Spec file has been changed to use the "portable" patch supplied by Red
+  Hat so it should work on Linux Alpha and Linux SPARC too... maybe...
+- Now will accept 4-byte element status for most element types, despite fact
+  that these don't comply with SCSI standards :-(. This should make many
+  older changers work, including HP optical changers. 
+- Fixed PeripheralDeviceType table, courtesy of Rob Turk.
+- Now looks for CHANGER environment variable if a device is not specified
+  on the command line. If can't find CHANGER, then tries TAPE environment
+  variable.
+- Properly sets TransportElementAddress in the CDB for the MOVE MEDIUM command
+  with what was discovered via the READ_ELEMENT_STATUS command, rather
+  than setting them to zero (SCSI spec says that zero should be the default
+  arm, but at least one changer out there didn't like it). 
+- Added a '--version' command (sigh). 
+- Added an 'inventory' command for Breece Hill libraries that don't 
+  automatically do an inventory at powerup. 
+
+CHANGES, mtx 1.2.7:
+- Fixed problem w/single drive Exabyte 220 reporting element status data for
+  both drives (sigh).
+- some general cleanup in the barcode fallback code (what a cruft!). Discovered
+  that ADIC DAT AUTOCHANGER does not work w/mtx because it produces
+  gibberish (will apparently only produce one element status page per request).
+- Fixed the RPM .spec file to have updated file locations, doc locations.
+- Fixed MoveMedium to say 'Output' for direction, to make it work with
+  Solaris 8     
+- Some changes to the Solaris low-level module to report more errors (though
+   it still doesn't work as well as the Linux low-level module). Should now
+   work properly with Solaris 2.6/7/8. (Solaris changes courtesy of Richard
+   Fish of Enhanced Software Technologies). 
+
+CHANGES, mtx 1.2.6:
+- Fixed 'eepos' stuff to use | rather than || (whoops!)
+- Accept a 4-byte element descriptor for the robot arm for certain older
+  autochangers. 
+
+CHANGES, mtx 1.2.5: 
+- Added 'noattach' command. If on command line prior to other commands, forces
+  them to use the regular SCSI changer API rather than the _ATTACHED API,
+  no matter what the _ATTACHED bit in the Inquiry page said. 
+- Created 'tapeinfo' program.
+
+CHANGES, mtx 1.2.4:
+- Major overhaul of element guts to dynamically allocate the arrays
+  using the result of a MODE_SENSE on the Element Address Assignment
+  Page. If mtx 1.2.3 works for you and mtx 1.2.4 does NOT work for you, 
+  please un-comment the '#define DEBUG_MODE_SENSE' in file 'mtxl.c' and 
+  EMAIL me the results.
+
+CHANGES, mtx 1.2.3:
+- Fixed the source storage element number stuff (again, sigh)
+- Because of above fix, 'next' etc. ought to work right again.
+
+CHANGES, mtx 1.2.2:
+- Fixed that it was saying everything was an Import/Export element (oops!)
+- Properly update the Import/Export element count.
+
+CHANGES, mtx 1.2.1:
+- Now explicitly output that a Storage element is in fact an Import/Export
+  element.
+- Added 'transfer' command to transfer between two Storage elements (so that
+  you can get a tape to an Import/Export element.
+- Added 'eepos' command for controlling tray retraction on the Breecehill
+  import/export trays. (Works with "load" and "unload" commands too, though
+  that is not documented on "mtx -h"). 
+
+CHANGES, mtx 1.2.0:
+- Re-numbered now that Leonard has asked me to take over maintenance of the
+  'mtx' program.
+- Temporarily treat Import/Export elements the same as Storage elements. Need
+  to fix this eventually so that the GUI knows what kind of element we're
+  talking about. 
+- Removed quotes from the source element # to make it easier to parse
+  from Perl or Python (just do a split on spaces). 
+- Added sample program, 'mam2debug', showing how to use mtxl library for
+  your own programs (this happens to dump the Exabyte Mammoth 2's internal 
+  debug buffer to a file, using the Mammoth2-specific SCSI commands to do so).
+
+CHANGES, mtxl 1.4.8:
+- Whoops, report logical rather than physical when I have to scan for
+  open slots :-).
+
+CHANGES, mtxl 1.4.7:
+- Update comment to reflect mtxl 1.4.6 stuff :-).
+- Fix the part of the code that scans for open slots as sources for media.
+
+CHANGES, mtxl 1.4.6:
+- Don't use _ATTACHED interface if it reports itself as a Medium Changer!
+
+CHANGES, mtxl 1.4.5:
+- Changed "whoops" compile error on Linux (teach me to release w/o testing on
+  the most popular platform!)
+- Changed declarations to remove compile-time warnings.
+
+CHANGES, mtxl 1.4.4:
+- Added support for FreeBSD. (uses pass device, NOT native FreeBSD ch device).
+- Change all 'int' DeviceFD to DEVICE_TYPE DeviceFD. Note that SGI and FreeBSD
+  use a struct * to access the CAM SCSI layer, rather than a file fd. 
+- Fixed goof where I'd hard-wired max # of elements to 127 for testing
+  purposes (it should be sum of MAX_STORAGE_ELEMENTS + MAX_TRANSFER_ELEMENTS 
+  + MAX_TRANSPORT_ELEMENTS from mtx.h -- change those if you need more
+  elements, bearing in mind that the code for ReadElementStatus in
+  mtxl.c maxes out at 255 elements unless you fix that too).
+- Cleaned some cruft out of the MOVE_MEDIUM code.
+- Must have GNU Make to process Makefile. In reality, I don't know of
+  any machine where we voluntarily use the native 'make' command, because
+  a) there's a half dozen native 'make' all with their own perverted
+  syntaxes, and b) most of them are brain dead beyond belief. 
+
+CHANGES, mtxl 1.4.3:
+- Do an INQUIRY prior to doing a MOVE_MEDIUM or READ_ELEMENT_STATUS so that I
+  can detect the MChanger bit and use MOVE_MEDIUM_ATTACHED or
+  READ_ELEMENT_STATUS_ATTACHED commands instead. 
+- Successfully tested with dual drives!
+- first, next, last now working 
+- Created a man page
+- Created a 'make install', edit Makefile to alter destinations. 
+
+CHANGES, mtxl 1.4.2:
+- Found the problem with the DAT changer! It was burping on the 
+  'bar code' bit... so I intercept that sense key, re-issue w/out the
+  'bar code' bit, and success!
+- Added a 'TODO' file...
+
+CHANGES, mtxl 1.4.1:
+- Added 'invert' qualifier to 'load' and 'unload' commands to invert
+  the media (for HP optical jukeboxes). Type './mtx' by itself to 
+  see the syntax.
+- Figured out why my code wasn't properly detecting errors --
+  turns out the 'sg' device can return ok
+  status even when there is sense data to be reported!
+- Still to fix: *still* isn't working right with my Seagate 
+  6-tape DDS-4 DAT changer... also need to put the
+  second drive into the Exabyte 220 to make sure the dual-drive stuff 
+  works properly (!). 
+
+CHANGES, mtxl 1.4:
+- Have now tested the barcode (volume tag) stuff. It works! (Well, there was
+an index-by-one problem that I had to squash, but after that...)
+- Changed to use SCSI Generic device API on Linux rather than 
+SCSI_IOCTL_SEND_COMMAND API, which cut things off at 4095 bytes on i386
+Linux. 
+- Added a bunch of debugging output that needs to be ripped out :-(. 
+Make sure you remove the -DDEBUG from the Makefile, and probably
+   -DLONG_PRINT_REQUEST_SENSE too (unless you LIKE sense results that make
+sense!) 
+- Still have annoying bug on Linux of only reading 1st 16 bytes of sense
+data. Alas, this appears to be a problem in the Linux 2.2 kernel, not in
+anything that we're doing :-(. Hmm... cdrecord has the same problem, Mr.
+Schilling says he's been saying it's a problem since 1997. Sigh. 
+- Still need to test the dual-drive stuff!
+
+CHANGES, mtxl 1.3:
+- Hacked in the barcode (volume tag) stuff. NEED SOMEONE TO TEST
+WHETHER IT WORKS!
+- started issuing redundant initial READ_ELEMENT_STATUS with Allocation Length
+of 8 in order to get a BYTE_COUNT_OF_REPORT_AVAILABLE in order to calculate a
+better Allocation Length for the "real" READ_ELEMENT_STATUS. Trying to send a
+query to a small 6-tape changer with an Allocation Length suited for a 
+200-element tape library was resulting in some errors and lockups on the
+part of the tape changer device.
+- first, last, next, previous are STILL broken. Sorry :-(. 
+
+CHANGES, mtxl 1.2:
+
+- Changed many output messages to make them more easily parsed by
+scripts written in awk/perl/python
+- Extracted out a library of SCSI tape library routines usable by "C"
+programs (must be GPL'ed). Extensive re-arranging of code.
+- Added support for multiple drives.
+- Started adding support for tape changers that use the "MCHangr" 
+bit rather than a separate ID or LUN.
+- Increased limits so we could deal with LARGE tape libraries.
+- Started adding support for barcode readers in said tape libraries
+- broke first, last, next, previous. Sorry :-(. 
+- Added ability to chain commands on command line. Thus could say 'mtx -f
+/dev/sg4 unload 4 0 load 7 0' to unload a tape from drive 0 and load the
+tape in slot 7 into drive 0. 
+
+
diff --git a/COMPATABILITY b/COMPATABILITY
new file mode 100644 (file)
index 0000000..3bc9d40
--- /dev/null
@@ -0,0 +1,203 @@
+PLEASE CONTRIBUTE YOUR HARDWARE!
+--------------------------------
+If you are currently using mtx on hardware not listed below, please
+forward me the result of 'mtx inquiry' on your hardware, plus the first
+few lines of 'mtx status', so that I can add it to the list. 
+That EMAIL address is:
+
+    eric@badtux.org
+
+Compilers:
+----------
+mtx requires the GNU 'make' command in order to build. On FreeBSD or other
+Unixes you must thus install 'gmake' and use that command for building.
+In addition, it has only been verified to compile correctly using the
+GNU "C" compiler. It may not compile with vendor "C" compilers, which
+typically tend to implement an earlier version of "C" than the version
+implemented by "gcc". 
+
+Operating Systems:
+-----------------
+
+mtx 1.2 is currently known to work flawlessly under the following
+operating systems:
+
+Linux (2.2.5 kernel and above)
+FreeBSD (tested with versions 3.2 and above, but should work for all 3.x
+         and 4.x). 
+
+Various people have reported that they have managed to make mtx work
+on various versions of Solaris. Please check the mtx list archives (
+http://mtx.sourceforge.net ) to see how they've done it.
+
+IRIX 6.5 apparently works flawlessly:
+
+(Richard.Lefebvre(@AROBAS)cerca(.POINT)umontreal(.POINT)ca):
+I just wanted to send you 1 or 2 comments on mtx. I just downloaded it
+last friday to use with and SGI Origin2000 and an 8 slot DLT autoloader.
+The whole thing compiled fine (under gcc).
+
+From Dan Wright (dtwright at uiuc dot uiuc.edu):
+  I just wanted to send an e-mail to let you know that mtx 1.2.9
+  works great for me on IRIX 6.5 with an ADIC FastStor DLT8000.
+  IRIX 6.5 has a generic scsi interface installed by default, so to
+  access the autoloader I use "mtx -f /dev/scsi/sc1d1l0" (bus 1 id 1
+  lun 0).
+  
+Vendors:
+--------
+
+'mtx' tries to stay with generic SCSI media changer commands, thus
+works with most vendors' products (see list of tested media changers
+that follows). The following vendors have been singled out for special
+mention because they provide easily-accessible SCSI documentation via
+their web site, thus making it easier for people world-wide to
+contribute to 'mtx' development:
+
+  Tandberg Data  http://www.tandberg.com
+  Exabyte/Ecrix  http://www.exabyte.com
+  SpectraLogic   http://www.spectralogic.com
+
+If contacting other companies asking them to provide easily-accessible
+SCSI documentation for their loaders, please be polite. Remember, several
+'mtx' contributors work for those "other" companies.
+
+----------------
+Changer Devices
+----------------
+The following has been directly tested:
+
+* Exabyte 220 with 1 drive   (21 slots)
+* Exabyte 220 with 2 drives  (21 slots)
+* Exabyte EZ-17  (7 slots)
+    Known quirks: Must eject tape using 'mt' or 'tapectl' prior to using
+  the 'unload' command.
+
+* Seagate DDS-4 DAT AutoLoader (1 drive, 6 slots)
+       Product ID: 'DAT    06241-XXX'  
+    Known quirks: Uses LUN 1 for robot. On Linux, must compile kernel with 
+    "Scan SCSI LUNs" or add the following line to your /etc/lilo.conf (and
+    re-run /sbin/lilo):
+         append="max_scsi_luns=2" 
+
+* DISC D-40 optical library (2 drives, 40 slots, 1 import/export): Yes,
+  it really does report that it's a Maxoptix!
+   Product Type: Medium Changer
+   Vendor ID: 'Maxoptix'
+   Product ID: 'MAXLYB          '
+   Revision: '3.04'
+
+* DISC/NSM 3000-series DVD optical library (4 drives, 240 slot,  1 mail slot)
+  Limitations: Will export to mail slot, but won't import from it (yet). 
+    'transfer' command does not work because of mechanical limitations of
+    the hardware. Initial mtx status takes a while.  
+   Vendor ID: 'NSM     '
+   Product ID: 'NSM3000         '
+   Revision: '1140'
+
+The following have been tested by others, and information may be
+incomplete or in error even.
+
+* Compaq DLT Library 20/40, 1 drive 15 slots  (antony at elizatravel dot com)
+   Vendor: HP       Model: C5173-4000
+
+* Vendor ID: 'ADIC    '       (boris dot klug at ibs dash ag dot de)   
+  Product ID: 'VLS DLT         '  7 slot 1 import/export 1 drive.
+
+* Unknown Overland changer (phreno at pacbell dot net):
+> > > Vendor ID: 'OVERLAND'
+> > > Product ID: 'LXB             '
+
+* "old Exabyte 10e tape loaders" (drew at pctc dot com)
+
+* Unknown HP DLT changer: (eric at collab dot net)
+> > Storage Changer /dev/sgd:1 Drives, 15 Slots ( 0 Import/Export )
+> > Vendor ID: 'HP      '
+> > Product ID: 'C5173-4000      '
+
+* "Lago Sys LS-380L/StorageTek 9704/Imation ITL-2225 (mine's a 
+  StorageTek labeled)" 25-slot, 2-drive, 1-arm, 1-import/export
+  (eswan at lips dot net)
+
+* HP SureStore Optical 80fx. (mmarchione at nyhomes dot org )
+  Vendor: HP       Model: C1160F           Rev: 0.40
+  Type:   Medium Changer                   ANSI SCSI revision: 02 
+
+* Breece Hill tape library (unknown configuration, unknown reporter):
+  Vendor ID: 'BHTI'
+  Product ID: 'Q2 '
+   Oddities: Must either turn on auto-inventory, or run 'mtx inventory'
+     prior to running any other mtx command. Reads bar codes by yanking
+    tapes out of slots and waving them in front of bar code reader, so
+    inventory of tapes with no bar code is VERY slow (bar code your tapes!).
+
+
+* Seagate Scorpion DDS-3 4-tape autochanger 
+     (jsled at normandy dot smarttouch dot com):
+         Vendor ID: 'ARCHIVE '
+         Product ID: 'Python 04377-XXX'
+
+* Overland 15 tape 1 drive DLT changer: (asmith at umdgrb dot umd dot edu)
+   Vendor ID: 'OVERLAND'
+   Product ID: 'LXB             '
+
+* ADIC DAT AutoChanger -- 1 drive, 12 slots, 1 import-export:
+      Vendor ID: 'ADIC    '   (andrew_gray at irobotics dot com)
+      Product ID: 'DAT AutoChanger '
+   Quirks: firmware version 0357 does not appear to work w/mtx (request
+   from ADIC that they upgrade your firmeware to 0361, which does
+   seem to work), firmware version revision `0462` does work. Use 
+   'mtx inquiry' to detirmine which firmware you have. 
+
+* HP 1/20 DLT loader: "We have a HP 1/20, but I think it's all the
+  same hardware. mtx works fine with it." (jo2y at midnightlinux dot com)
+
+* Exabyte 120 (116 slots, 4 tape drives): (wsb at paralleldata dot com)
+  Quirks: This loader does not properly support the loader slot assignment
+  page used to allocate element structures, so the #defines must be
+  changed in mtx.h for MAX_STORAGE_ELEMENTS, MAX_TRANSFER_ELEMENTS, and 
+  MAX_TRANSPORT_ELEMENTS. May also need to increase the SCSI timeout to
+  do inventories. Takes a LONG time to do inventories if you don't have
+  bar codes!
+
+* Exabyte 210 (wsb at paralleldata dot com)
+
+
+* Adic FastStor DLT4000 (7-Slot 1 drive):
+  Vendor ID: 'ADIC    '
+  Product ID: 'FastStor DLT    '
+
+* Ecrix AutoPak VXA (15-slot 1 drive) in configuration mode 0 (mark
+      at commerceengine dot com):
+  "This drive can be configured to emulate several types, but I've
+  only tested it in this one mode."
+  Vendor ID: 'SPECTRA '
+  Product ID: '215             '
+  Revision: '1008'
+  Attached Changer: No
+  SerialNumber: '023201'
+
+* ATL P1000 2 drive, 31 slot, 1 import/output: (dna plus mtx at clas.ufl.edu)
+ # ./mtx -f /dev/scsi/changer/c2t0d0 inquiry
+  Product Type: Medium Changer
+  Vendor ID: 'ATL     '
+  Product ID: 'P1000    6220070'
+  Revision: '2.01'
+  Attached Changer: No
+
+* NSM DVD Jukebox:
+ Storage Changer /dev/sg4:4 Drives, 561 Slots ( 1 Import/Export )
+Product Type: Medium Changer
+ Vendor ID: 'NSM     '
+ Product ID: 'NSM6000         '
+ Revision: '1120'
+ Attached Changer: No
+* Spectralogic Treefrog/Bullfrog:
+  Storage Changer /dev/sg0:2 Drives, 15 Slots ( 0 Import/Export )   
+Product Type: Medium Changer
+Vendor ID: 'SPECTRA '
+Product ID: '10000           '
+Revision: 'X010'
+Attached Changer: No
+
diff --git a/FAQ b/FAQ
new file mode 100644 (file)
index 0000000..3f22e08
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,145 @@
+Frequently Asked Questions List, v 1.0.1
+
+Index: 
+  I. Compiling
+  II. Finding the correct device
+  III. Operational Issues
+
+Part I: Compiling.
+
+Q: Where is the Makefile in the tarball?
+A: MTX now uses GNU Autoconf to generate the Makefile. Type "./configure"
+   while in the extracted mtx directory. 
+
+Q: Typing 'make' gives me a bunch of errors in the Makefile. Why can't
+   you provide a Makefile that works?
+A: Note that you need the GNU 'make'. The BSD 'make' won't work, and 
+  Solaris 'make' probably won't work either. If you want a better
+  configuration and makefile system, write one, then EMAIL me the results --
+  mtx is Open Source software and needs your code contributions to grow. 
+
+Q: How do I compile for operating systems other than Linux?
+A: MTX no longer needs you to edit the Makefile to compile for operating
+   systems other than Linux. Just type ./configure and go with it. 
+
+Q: How do I port it to OS's other than the supported ones?
+A: Create a new scsi_ module using one of the existing modules as an
+   example (scsi_freebsd.c might be a good model). Decide what symbol
+   you want #ifdef'ed in order to include that scsi_ module. Edit
+   mtxl.c to #include your scsi_ module. Edit the Makefile to add the
+   new target, including the -D needed to #include your new scsi_ module. 
+
+*********************************************************************
+Part II: Finding the correct device. 
+
+Q: Why does this command not work??
+    [root@Scotty mtxl-1.4.8]# ./mtx -f /dev/st0 inquiry
+   In /var/log/messages I see:
+      st0: Write not multiple of tape block size. 
+A: Note that mtx 1.2 and above use the SCSI GENERIC interface on Linux,
+  FreeBSD, and Solaris (at least). They do NOT use the tape device node. Please
+  read the man page for directions.
+
+Q: When I do 'mtx -f /dev/sga inquiry' it shows 
+      Product Type: Tape Drive
+      Vendor Id: HP       
+      Product ID: C1553A 
+   But when I do a 'mtx -f /dev/sga status' it fails. Why?!
+A: You're trying to send a robotics command to a tape drive. You need
+   to send robotics commands to robotics devices, not to tape drives. Look in
+  /proc/scsi/scsi (Linux) or camcontrol (FreeBSD) to find out what the
+  robotics device is. It will be reported as a 'Medium Changer', not a
+  'Sequential Access' or 'Tape Drive'.
+
+Q: When I do 'cat /proc/scsi/scsi' it shows only one device, the tape device!
+A: You are using a DAT autochanger that has one SCSI ID but two LUN's, LUN 0
+   and LUN 1. You need to compile a new kernel with  SCAN SCSI LUNS enabled
+   or add this line to your /etc/lilo.conf (then run /sbin/lilo and reboot): 
+       append="max_scsi_luns=2"
+
+Q: I'm tired of typing '-f /dev/sgc' all the time. How do I set a default
+  device that 'mtx' looks at?
+A: Set the CHANGER environment variable. For example, with 'bash':
+     export CHANGER=/dev/sgc
+
+Q: I get "modprobe: can't locate module char-major-21"
+   syslog messages being squirreled away into a file on our syslog host,
+   and mtx doesn't work. What's the problem?
+A: You need to compile SCSI generic support into your kernel (or as a module).
+
+Q: When I installed mtx, a message showed 
+   up on the console stating that a scsi changer was found at
+   dev sgr. However, I have no device /dev/sgr.
+A: On Linux, do 'mknod /dev/sgr c 21 19' to create a device node. By default
+  only 16 SCSI generic nodes are created, which might not be enough if
+  you have multiple SCSI controllers with lots of devices.
+
+******************************************************
+III. Operational issues:
+
+Q: I'm using Red Hat 7.0 and mtx works fine when I type ./mtx from the
+   command line, but when I use it from scripts I get the following:
+    mtx: Request Sense: Error Code=70 (Current)
+    mtx: Request Sense: Sense Key=Aborted Command
+    mtx: Request Sense: Additional Sense Code = 4E
+    mtx: Request Sense: Additional Sense Qualifier = 00
+   What's happening?
+A: Do "rpm -e mtx". Red Hat 7.0 includes a busted version of mtx. Your
+   script is apparently picking up the busted mtx in your path. Get rid
+   of the busted mtx, make sure that /usr/local/bin (or wherever you
+   put the "good" mtx) is in the path, and all should be well. 
+
+Q: I get
+   # /usr/local/bin/mtx -f /dev/sgr status
+   mtx: Request Sense: Error Code=70 (Current)
+   mtx: Request Sense: Sense Key=Not Ready
+   mtx: Request Sense: Additional Sense Code = 04
+   mtx: Request Sense: Additional Sense Qualifier = 8E
+   mtx: READ ELEMENT STATUS Command Failed
+  What gives?
+A: Make sure your loader is in random mode, not sequential mode.
+   Most "real" loaders (as vs. DAT autoloaders) will not properly report
+   status information unless they are in "random" mode. 
+
+
+Q: I issue 'mtx load 5' and it loads tape 5. But when I try to put the tape
+   back in the magazine, we hit problems:
+      mtx: MOVE MEDIUM from Element Address 82 to 5 Failed
+   What gives?
+A: Many loaders require you to first eject the tape (using 'mt' or 'tapectl')
+  before you issue an 'unload' command via 'mtx'. 
+
+Q: My Breece Hill loader does not properly report its slots.
+A: Either set the "auto-inventory" feature in the loader's control panel, 
+   or run 'mtx inventory' prior to running 'mtx status'. 
+
+Q: My Breece Hill loader takes a long time to do an inventory. mtx times
+   out and spits all over the place. Help!
+A: Many loaders that support barcodes will perform poorly if you place tapes
+  into them without bar codes. Place bar codes on all your tapes and you
+  should be able to run 'mtx inventory' without that failure.
+
+Q: How do I eject the magazine of my autoloader?
+A: Many low-end DAT autoloaders support the removable media 'EJECT' command 
+   sent to the robotics device, even though it's not documented (or required)
+   in the SCSI standards. If the loader is at /dev/sgb, simply do 
+   'mtx -f /dev/sgb eject' and see what happens. (If nothing happens, 
+    your autoloader doesn't support 'eject'). Some high-end libraries have 
+   their own proprietary way for ejecting magazine trays, generally 
+   involving abuse of the 'transfer' command and 'eepos' addendums,
+    but this is totally non-standard and undocumented. 
+
+Q: Is there a standard for cleaning tape bar codes?
+A: Many libraries, and many backup programs, expect cleaning tape bar 
+   codes to start with "CLN". 
+
+Q: How do I report a bug?
+A: First, read this FAQ. Next, check the mtx list archives at
+   http://mtx.sourceforge.net to make sure that it's not already addressed 
+   by somebody else. If your problem is still not solved, send 
+   (to the mtx list) the following information:
+        Result of 'mtx inquiry' on the loader, 
+        Result of 'mtx status' on the loader (minus a bunch of tapes if
+          it's a 50+ tape loader!), 
+        Results of the operation that isn't working correctly. 
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..c7aea18
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,280 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/LICENSE.html b/LICENSE.html
new file mode 100644 (file)
index 0000000..40bc73d
--- /dev/null
@@ -0,0 +1,515 @@
+<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<HTML>
+<HEAD>
+<TITLE>GNU General Public License - GNU Project - Free Software Foundation (FSF)</TITLE>
+<LINK REV="made" HREF="mailto:webmasters@www.gnu.org">
+</HEAD>
+<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#1F00FF" ALINK="#FF0000" VLINK="#9900DD">
+<H1>GNU General Public License</H1>
+
+<P>
+<HR>
+
+<P>
+
+<H2>Table of Contents</H2>
+<UL>
+
+  <LI><A NAME="TOC1" HREF="gpl.html#SEC1">GNU GENERAL PUBLIC LICENSE</A>
+<UL>
+<LI><A NAME="TOC2" HREF="gpl.html#SEC2">Preamble</A>
+<LI><A NAME="TOC3" HREF="gpl.html#SEC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A>
+<LI><A NAME="TOC4" HREF="gpl.html#SEC4">How to Apply These Terms to Your New Programs</A>
+
+</UL>
+</UL>
+
+<P>
+
+<HR>
+
+<P>
+
+
+
+<H2><A NAME="SEC1" HREF="gpl.html#TOC1">GNU GENERAL PUBLIC LICENSE</A></H2>
+<P>
+Version 2, June 1991
+
+</P>
+
+<PRE>
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+</PRE>
+
+
+
+<H2><A NAME="SEC2" HREF="gpl.html#TOC2">Preamble</A></H2>
+
+<P>
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+</P>
+<P>
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+</P>
+<P>
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+</P>
+<P>
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+</P>
+<P>
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+</P>
+<P>
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+</P>
+<P>
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+</P>
+<P>
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+</P>
+
+
+<H2><A NAME="SEC3" HREF="gpl.html#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A></H2>
+
+
+<P>
+
+<STRONG>0.</STRONG>
+ This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+<P>
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+<P>
+
+<STRONG>1.</STRONG>
+ You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+<P>
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+<P>
+
+<STRONG>2.</STRONG>
+ You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+<P>
+
+<UL>
+
+<LI><STRONG>a)</STRONG>
+     You must cause the modified files to carry prominent notices
+     stating that you changed the files and the date of any change.
+
+<P>
+<LI><STRONG>b)</STRONG>
+     You must cause any work that you distribute or publish, that in
+     whole or in part contains or is derived from the Program or any
+     part thereof, to be licensed as a whole at no charge to all third
+     parties under the terms of this License.
+
+<P>
+<LI><STRONG>c)</STRONG>
+     If the modified program normally reads commands interactively
+     when run, you must cause it, when started running for such
+     interactive use in the most ordinary way, to print or display an
+     announcement including an appropriate copyright notice and a
+     notice that there is no warranty (or else, saying that you provide
+     a warranty) and that users may redistribute the program under
+     these conditions, and telling the user how to view a copy of this
+     License.  (Exception: if the Program itself is interactive but
+     does not normally print such an announcement, your work based on
+     the Program is not required to print an announcement.)
+</UL>
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+<P>
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+<P>
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+<P>
+
+<STRONG>3.</STRONG>
+ You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+<!-- we use this doubled UL to get the sub-sections indented, -->
+<!-- while making the bullets as unobvious as possible. -->
+<UL>
+
+<LI><STRONG>a)</STRONG>
+     Accompany it with the complete corresponding machine-readable
+     source code, which must be distributed under the terms of Sections
+     1 and 2 above on a medium customarily used for software interchange; or,
+
+<P>
+<LI><STRONG>b)</STRONG>
+     Accompany it with a written offer, valid for at least three
+     years, to give any third party, for a charge no more than your
+     cost of physically performing source distribution, a complete
+     machine-readable copy of the corresponding source code, to be
+     distributed under the terms of Sections 1 and 2 above on a medium
+     customarily used for software interchange; or,
+
+<P>
+<LI><STRONG>c)</STRONG>
+     Accompany it with the information you received as to the offer
+     to distribute corresponding source code.  (This alternative is
+     allowed only for noncommercial distribution and only if you
+     received the program in object code or executable form with such
+     an offer, in accord with Subsection b above.)
+</UL>
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+<P>
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+<P>
+
+<STRONG>4.</STRONG>
+ You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+<P>
+
+<STRONG>5.</STRONG>
+ You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+<P>
+
+<STRONG>6.</STRONG>
+ Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+<P>
+
+<STRONG>7.</STRONG>
+ If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+<P>
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+<P>
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+<P>
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+<P>
+
+<STRONG>8.</STRONG>
+ If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+<P>
+
+<STRONG>9.</STRONG>
+ The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+<P>
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+<P>
+
+
+<STRONG>10.</STRONG>
+ If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+
+
+<P><STRONG>NO WARRANTY</STRONG></P>
+
+<P>
+
+<STRONG>11.</STRONG>
+ BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+<P>
+
+<STRONG>12.</STRONG>
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+<P>
+
+
+<H2>END OF TERMS AND CONDITIONS</H2>
+
+
+
+<H2><A NAME="SEC4" HREF="gpl.html#TOC4">How to Apply These Terms to Your New Programs</A></H2>
+
+<P>
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+</P>
+<P>
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+</P>
+
+<PRE>
+<VAR>one line to give the program's name and an idea of what it does.</VAR>
+Copyright (C) <VAR>yyyy</VAR>  <VAR>name of author</VAR>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+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 more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+</PRE>
+
+<P>
+Also add information on how to contact you by electronic and paper mail.
+
+</P>
+<P>
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+</P>
+
+<PRE>
+Gnomovision version 69, Copyright (C) <VAR>year</VAR> <VAR>name of author</VAR>
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'.  This is free software, and you are welcome
+to redistribute it under certain conditions; type `show c' 
+for details.
+</PRE>
+
+<P>
+The hypothetical commands <SAMP>`show w'</SAMP> and <SAMP>`show c'</SAMP> should show
+the appropriate parts of the General Public License.  Of course, the
+commands you use may be called something other than <SAMP>`show w'</SAMP> and
+<SAMP>`show c'</SAMP>; they could even be mouse-clicks or menu items--whatever
+suits your program.
+
+</P>
+<P>
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+</P>
+
+<PRE>
+Yoyodyne, Inc., hereby disclaims all copyright
+interest in the program `Gnomovision'
+(which makes passes at compilers) written 
+by James Hacker.
+
+<VAR>signature of Ty Coon</VAR>, 1 April 1989
+Ty Coon, President of Vice
+</PRE>
+
+<P>
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+<HR>
+
+<P>
+FSF &amp; GNU inquiries &amp; questions to
+<A HREF="mailto:gnu@gnu.org"><EM>gnu@gnu.org</EM></A>.
+send other questions to
+<A HREF="mailto:gnu@gnu.org"><EM>gnu@gnu.org</EM></A>.
+<P>
+Copyright notice above.<BR>
+Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA  02111,  USA
+<P>
+<HR>
+</BODY>
+</HTML>
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..5eae01e
--- /dev/null
@@ -0,0 +1,137 @@
+# WARNING -- THIS HAS BEEN RE-WRITTEN TO USE GNU MAKE. DO NOT
+# TRY TO PROCESS THIS WITH A NORMAL MAKE! (FREEBSD GUYS, THIS MEANS
+# USE GMAKE, NOT REGULAR BSD MAKE!)
+#
+# Valid targets:
+#  linux86 freebsd86 solarissparc sgi dec vms 
+#
+# Makefile changes by Lars Kellogg-Stedman for better integration with
+# GNU Autoconf. 
+
+# Version # for 'make dist'...
+VERSION=1.2.16rel
+BINS = mtx tapeinfo loaderinfo scsitape
+
+TARGET = @TARGET@
+CPU    = @CPU@
+CC     = @CC@
+INSTALL        = @INSTALL@
+
+CFLAGS         = @CFLAGS@
+CPPFLAGS       = @CPPFLAGS@ -DVERSION="\"$(VERSION)\""
+LDFLAGS                = @LDFLAGS@
+LIBS           = @LIBS@
+
+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@
+
+#
+# Linux on x86...
+#
+ifeq ($(TARGET),linux)
+CFLAGS += -Wall
+CPPFLAGS       += -I/usr/src/linux/include -DLONG_PRINT_REQUEST_SENSE=1
+endif
+
+#
+# FreeBSD on x86...
+#
+ifeq ($(TARGET),freebsd86)
+CFLAGS         += -m486
+CPPFLAGS       += -I/usr/src/linux/include -DLONG_PRINT_REQUEST_SENSE=1
+LIBS           += -lcam
+endif
+
+ifeq ($(TARGET),hpux)
+CFLAGS += -O -D_HPUX_SOURCE -D __hpux__ 
+endif
+
+#
+# Solaris/SPARC
+#
+ifeq ($(TARGET),solarissparc)
+CFLAGS         += -O6
+endif
+
+#
+# SGI IRIX
+#
+ifeq ($(TARGET),sgi)
+CFLAGS         += -O6
+LIBS           += -lds
+endif
+
+#
+# Digital Unix
+#
+ifeq ($(TARGET),dec)
+CFLAGS         += -O
+endif
+
+#
+# OpenVMS (see vms/000readme)
+#
+ifeq ($(TARGET),vms)
+See vms/000readme for information.
+endif
+
+all:   $(BINS)
+
+install: $(BINS)
+       for file in $(BINS); do \
+       strip $$file;   \
+       done    
+       $(INSTALL_DIR) $(sbindir)
+       $(INSTALL_BIN) $(BINS) $(sbindir)
+       $(INSTALL_DIR) $(mandir) $(mandir)/man1
+       $(INSTALL_DOC) mtx.1 tapeinfo.1 scsitape.1 loaderinfo.1 $(mandir)/man1
+
+clean:
+       rm -f *.o *~
+       rm -f $(BINS)
+       rm -f mam2debug mam2debug2
+
+distclean: clean
+       rm -f Makefile config.log config.cache config.status
+
+dist: distclean
+       ./makedist $(VERSION)   
+
+loaderinfo: loaderinfo.o mtxl.o mtxl.h mtx.h $(EXTRA)
+       $(CC) $(LDFLAGS) -o loaderinfo loaderinfo.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)
+
+mam2debug: mtxl.o mam2debug.o mtx.h $(EXTRA)   
+       $(CC) $(LDFLAGS) -o mam2debug 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)
+
+mam2debug2: mtxl.o mam2debug2.o mtx.h $(EXTRA)
+       $(CC) $(LDFLAGS) -o mam2debug2 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.o: scsitape.c mtx.h mtxl.h
+
+loaderinfo.o: loaderinfo.c mtx.h mtxl.h
+
+tapeinfo.o: tapeinfo.c mtx.h mtxl.h
+
+mam2debug.o: mam2debug.c mtx.h mtxl.h
+
+mam2debug2.o: mam2debug2.c mtx.h mtxl.h
+
+mtx.o: mtx.c mtx.h mtxl.h
+
+mtxl.o: mtxl.c mtxl.h scsi_linux.c 
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e906899
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+MTX
+
+Programs:
+  mtx is the media changer control program
+  tapeinfo dumps some interesting stuff out of tape drives' mode pages and
+sense pages. 
+  loaderinfo dumps some interesting stuff out of loaders' mode pages and
+sense pages.
+  scsitape sends raw SCSI commands to tape drives. Do not use this unless
+you know exactly what you're doing, because you can easily get into a feud
+with the system's own tape driver and end up locking up the whole system.
+
+INSTALLATION:
+
+WARNING: MUST HAVE GNU 'make' TO DO THIS! (e.g. use 'gmake' on freebsd, not
+BSD 'make'!).
+
+Type ./configure to create a Makefile. Type 'make', then 'make
+install'. Type 'man mtx' for info about mtx, and 'man tapeinfo' for
+info about tapeinfo. Enjoy.
+
+Credits:
+
+The original 'mtx' program is copyright 1996-1997 by Leonard Zubkoff
+<lnz@dandelion.com>. This version was modified for multi-drive,
+optical changer, and tape library support by Eric Lee Green
+<eric@estinc.com>. Also added FreeBSD support. Please see the man page
+for current info, and the file 'mtx.doc' for historical info.
+
+My thanks to Doug Bonnell of Breece Hill for suggestions on
+dynamically allocating element info, Tien That Ton of Tandberg for
+being the original tester of the Import/Export Element stuff, Ken
+Porter for RPM's, William D. Smith for the HP/UX port, Kai Makisara
+for the barcode backoff fix, and to all the other people out there who
+have used it, found problems with it, and let me know about it (you
+know who you are).
+  
+   -- Eric Lee Green <eric@estinc.com>
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..77cabe0
--- /dev/null
+++ b/TODO
@@ -0,0 +1,14 @@
+1 Add a 'timeout' command to adjust the timeout (timeout will be in seconds!).
+1 Adjust timeout lenghts for various operations now that the low level
+ SCSI routines now support timeouts. 
+1 Add IES command (sets CDB[5] to something in routine Inventory() ).
+1 Increase the timeout for Inventory() commands to something *BIG*.
+1 Fix 'scsitape' READ and WRITE so that block counts work.
+2 Fix ports to other Unixes/VMS.
+2 Port to AIX (no longer care about SCO Unix).
+2 Add a range to 'mtx status' so that we request status only of 
+  the elements we're interested in, rather than of all of them.
+  (nice for the very big loaders!). 
+3 Better Import/Export port support?
+3 Create a TCL/TK GUI front end (otherwise managing big libraries is HARD!).
+
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..4e5345f
--- /dev/null
@@ -0,0 +1,973 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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 more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    alpha:OSF1:*:*)
+       if test $UNAME_RELEASE = "V4.0"; then
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+       fi
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       cat <<EOF >$dummy.s
+       .globl main
+       .ent main
+main:
+       .frame \$30,0,\$26,0
+       .prologue 0
+       .long 0x47e03d80 # implver $0
+       lda \$2,259
+       .long 0x47e20c21 # amask $2,$1
+       srl \$1,8,\$2
+       sll \$2,2,\$2
+       sll \$0,3,\$0
+       addl \$1,\$0,\$0
+       addl \$2,\$0,\$0
+       ret \$31,(\$26),1
+       .end main
+EOF
+       ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+       if test "$?" = 0 ; then
+               ./$dummy
+               case "$?" in
+                       7)
+                               UNAME_MACHINE="alpha"
+                               ;;
+                       15)
+                               UNAME_MACHINE="alphaev5"
+                               ;;
+                       14)
+                               UNAME_MACHINE="alphaev56"
+                               ;;
+                       10)
+                               UNAME_MACHINE="alphapca56"
+                               ;;
+                       16)
+                               UNAME_MACHINE="alphaev6"
+                               ;;
+               esac
+       fi
+       rm -f $dummy.s $dummy
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+       exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-cbm-sysv4
+       exit 0;;
+    amiga:NetBSD:*:*)
+      echo m68k-cbm-netbsd${UNAME_RELEASE}
+      exit 0 ;;
+    amiga:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit 0 ;;
+    arc64:OpenBSD:*:*)
+       echo mips64el-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hkmips:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    pmax:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sgi:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    arm32:NetBSD:*:*)
+       echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    SR2?01:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit 0;;
+    Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    NILE:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit 0 ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit 0 ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:NetBSD:*:*)
+       echo m68k-atari-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3*:NetBSD:*:*)
+       echo m68k-sun-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:NetBSD:*:*)
+       echo m68k-apple-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    macppc:NetBSD:*:*)
+        echo powerpc-apple-netbsd${UNAME_RELEASE}
+        exit 0 ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    2020:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       sed 's/^        //' << EOF >$dummy.c
+       int main (argc, argv) int argc; char **argv; {
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       ${CC-cc} $dummy.c -o $dummy \
+         && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+         && rm $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+        if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
+       if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+            -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+       else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+       fi
+        else echo i586-dg-dgux${UNAME_RELEASE}
+        fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i?86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+               rm -f $dummy.c $dummy
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:4)
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=4.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC NetBSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/6?? | 9000/7?? | 9000/80[24] | 9000/8?[13679] | 9000/892 )
+              sed 's/^              //' << EOF >$dummy.c
+              #include <stdlib.h>
+              #include <unistd.h>
+              
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif 
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+              
+                  switch (cpu) 
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0: 
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits) 
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif 
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+       (${CC-cc} $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+       rm -f $dummy.c $dummy
+       esac
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    i?86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+       echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+       exit 0 ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY*T3E:*:*:*)
+       echo t3e-cray-unicosmk${UNAME_RELEASE}
+       exit 0 ;;
+    CRAY-2:*:*:*)
+       echo cray2-cray-unicos
+        exit 0 ;;
+    F300:UNIX_System_V:*:*)
+        FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    F301:UNIX_System_V:*:*)
+       echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+       exit 0 ;;
+    hp3[0-9][05]:NetBSD:*:*)
+       echo m68k-hp-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hp300:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    *:NetBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    *:OpenBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit 0 ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit 0 ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit 0 ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    *:GNU:*:*)
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    *:Linux:*:*)
+       # uname on the ARM produces all sorts of strangeness, and we need to
+       # filter it out.
+       case "$UNAME_MACHINE" in
+         arm* | sa110*)              UNAME_MACHINE="arm" ;;
+       esac
+
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us.
+       ld_help_string=`ld --help 2>&1`
+       ld_supported_emulations=`echo $ld_help_string \
+                        | sed -ne '/supported emulations:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported emulations: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_emulations" in
+         i?86linux)  echo "${UNAME_MACHINE}-pc-linux-gnuaout"      ; exit 0 ;;
+         i?86coff)   echo "${UNAME_MACHINE}-pc-linux-gnucoff"      ; exit 0 ;;
+         sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         armlinux)   echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         m68klinux)  echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+         elf32ppc)   echo "powerpc-unknown-linux-gnu"              ; exit 0 ;;
+       esac
+
+       if test "${UNAME_MACHINE}" = "alpha" ; then
+               sed 's/^        //'  <<EOF >$dummy.s
+               .globl main
+               .ent main
+       main:
+               .frame \$30,0,\$26,0
+               .prologue 0
+               .long 0x47e03d80 # implver $0
+               lda \$2,259
+               .long 0x47e20c21 # amask $2,$1
+               srl \$1,8,\$2
+               sll \$2,2,\$2
+               sll \$0,3,\$0
+               addl \$1,\$0,\$0
+               addl \$2,\$0,\$0
+               ret \$31,(\$26),1
+               .end main
+EOF
+               LIBC=""
+               ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+               if test "$?" = 0 ; then
+                       ./$dummy
+                       case "$?" in
+                       7)
+                               UNAME_MACHINE="alpha"
+                               ;;
+                       15)
+                               UNAME_MACHINE="alphaev5"
+                               ;;
+                       14)
+                               UNAME_MACHINE="alphaev56"
+                               ;;
+                       10)
+                               UNAME_MACHINE="alphapca56"
+                               ;;
+                       16)
+                               UNAME_MACHINE="alphaev6"
+                               ;;
+                       esac    
+
+                       objdump --private-headers $dummy | \
+                         grep ld.so.1 > /dev/null
+                       if test "$?" = 0 ; then
+                               LIBC="libc1"
+                       fi
+               fi      
+               rm -f $dummy.s $dummy
+               echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+       elif test "${UNAME_MACHINE}" = "mips" ; then
+         cat >$dummy.c <<EOF
+main(argc, argv)
+     int argc;
+     char *argv[];
+{
+#ifdef __MIPSEB__
+  printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+  printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+         ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         rm -f $dummy.c $dummy
+       else
+         # Either a pre-BFD a.out linker (linux-gnuoldld)
+         # or one that does not give us useful --help.
+         # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+         # If ld does not provide *any* "supported emulations:"
+         # that means it is gnuoldld.
+         echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+         test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+         case "${UNAME_MACHINE}" in
+         i?86)
+           VENDOR=pc;
+           ;;
+         *)
+           VENDOR=unknown;
+           ;;
+         esac
+         # Determine whether the default compiler is a.out or elf
+         cat >$dummy.c <<EOF
+#include <features.h>
+main(argc, argv)
+     int argc;
+     char *argv[];
+{
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+         ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         rm -f $dummy.c $dummy
+       fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.  earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+    i?86:DYNIX/ptx:4*:*)
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i?86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit 0 ;;
+    i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    i?86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit 0 ;;
+    i?86:UnixWare:*:*)
+       if /bin/uname -X 2>/dev/null >/dev/null ; then
+         (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+           && UNAME_MACHINE=i586
+       fi
+       echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
+       exit 0 ;;
+    pc:*:*:*)
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    M68*:*:R3V[567]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    i?86:LynxOS:2.*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+    PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                           # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit 0 ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit 0 ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit 0 ;;
+    news*:NEWS-OS:*:6*)
+       echo mips-sony-newsos6
+       exit 0 ;;
+    R3000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit 0 ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit 0 ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+  printf ("vax-dec-bsd\n"); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..3a39632
--- /dev/null
+++ b/config.h
@@ -0,0 +1,34 @@
+/* config.h.  Generated automatically 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
+ * the GNU General Public License.
+ * $Date: 2001/06/05 17:10:18 $
+ * $Revision: 1.1.1.1 $
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H 1
+
+/* autoconf changes these. */
+#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 1
+#define HAVE_SCSI_SCSI_IOCTL_H 1
+#define HAVE_SCSI_SG_H 1
+#define HAVE_CAMLIB_H 0
+#define HAVE_SYS_SCSI_IMPL_USCSI_H 0
+#define HAVE_SYS_SCSI_CTL_H 0
+#define HAVE_DSLIB_H 0
+#define HAVE_DU_DEFS_H 0
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_SYS_IOCTL_H 1
+
+#define WORDS_BIGENDIAN 0
+
+#endif
+
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..38802d2
--- /dev/null
@@ -0,0 +1,33 @@
+/* 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
+ * the GNU General Public License.
+ * $Date: 2001/06/05 17:10:18 $
+ * $Revision: 1.1.1.1 $
+ */
+
+#ifndef CONFIG_H
+#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_SCSI_SCSI_H 0
+#define HAVE_SCSI_SCSI_IOCTL_H 0
+#define HAVE_SCSI_SG_H 0
+#define HAVE_CAMLIB_H 0
+#define HAVE_SYS_SCSI_IMPL_USCSI_H 0
+#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_IOCTL_H 0
+
+#define WORDS_BIGENDIAN 0
+
+#endif
+
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..c4123f2
--- /dev/null
@@ -0,0 +1,1273 @@
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+#   Free Software Foundation, Inc.
+#
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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 more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+       echo Configuration name missing. 1>&2
+       echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+       echo "or     $0 ALIAS" 1>&2
+       echo where ALIAS is a recognized configuration type. 1>&2
+       exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+       *local*)
+               echo $1
+               exit 0
+               ;;
+       *)
+       ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+               | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+               | 580 | i960 | h8300 \
+               | x86 | ppcbe | mipsbe | mipsle | shbe | shle | armbe | armle \
+               | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+               | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+               | alphaev6[78] \
+               | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+               | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \
+               | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+               | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+               | mips64vr5000 | miprs64vr5000el | mcore \
+               | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \
+               | powerpc64 | sparcv8 | supersparc | microsparc | ultrasparc \
+               | thumb | d10v | fr30 | avr)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i[34567]86 | pentium[23] | k[56] | k6[23] | athlon)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       vax-* | tahoe-* | i[34567]86-* | pentium[23]-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+             | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+             | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+             | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+             | xmp-* | ymp-* \
+             | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* | armbe-* | armle-* \
+             | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \
+             | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+             | alphaev6[78]-* \
+             | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+             | clipper-* | orion-* \
+             | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+             | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \
+             | mips64el-* | mips64orion-* | mips64orionel-* \
+             | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+             | mipstx39-* | mipstx39el-* | mcore-* \
+             | f301-* | armv*-* | s390-* | sv1-* | t3e-* \
+             | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+             | k[56]-* | k6[23]-* | athlon-* | powerpc64-* \
+             | sparcv8-* | supersparc-* | microsparc-* | ultrasparc-* \
+             | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* )
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-cbm
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-cbm
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-cbm
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       cray2)
+               basic_machine=cray2-cray
+               os=-unicos
+               ;;
+       [ctj]90-cray)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               basic_machine=hppa2.0-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               basic_machine=hppa2.0-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i[34567]86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i[34567]86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i[34567]86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i[34567]86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       i386-go32 | go32)
+               basic_machine=i386-unknown
+               os=-go32
+               ;;
+       i386-mingw32 | mingw32)
+               basic_machine=i386-unknown
+               os=-mingw32
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       macppc*)
+               basic_machine=powerpc-apple
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mipsel*-linux*)
+               basic_machine=mipsel-unknown
+               os=-linux-gnu
+               ;;
+       mips*-linux*)
+               basic_machine=mips-unknown
+               os=-linux-gnu
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       mmix*)
+               basic_machine=mmix-knuth
+               os=-mmixware
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       msdos)
+               basic_machine=i386-unknown
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+        pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentiummmx | p55)
+               basic_machine=pentiummmx-pc
+               ;;
+       pentium | p5 | i586)
+               basic_machine=pentium-pc
+               ;;
+       pentiumpro | p6)
+               basic_machine=pentiumpro-pc
+               ;;
+       pentiummmx-* | p55-*)
+               basic_machine=pentiummmx-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium-* | p5-* | i586-*)
+               basic_machine=pentium-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-*)
+               basic_machine=pentiumpro-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       nexen)
+               # We don't have specific support for Nexgen yet, so just call it a Pentium
+               basic_machine=i586-nexgen
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=rs6000-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*)
+               basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sparclite-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=t3e-cray
+               os=-unicos
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xmp)
+               basic_machine=xmp-cray
+               os=-unicos
+               ;;
+        xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       mips)
+               if [ x$os = x-linux-gnu ]; then
+                       basic_machine=mips-unknown
+               else
+                       basic_machine=mips-mips
+               fi
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sparc | sparcv9)
+               basic_machine=sparc-sun
+               ;;
+        cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       c4x*)
+               basic_machine=c4x-none
+               os=-coff
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i[34567]86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto*)
+               os=-nto-qnx
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+        -*mint | -*MiNT)
+               os=-mint
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+        *-gould)
+               os=-sysv
+               ;;
+        *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+        *-sgi)
+               os=-irix
+               ;;
+        *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f301-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxsim* | -vxworks*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -*MiNT)
+                               vendor=atari
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..bb58481
--- /dev/null
+++ b/configure
@@ -0,0 +1,2121 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_default_prefix=/usr/local
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=mtx.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+#    configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+#    same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+#    as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:575: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+  case $nonopt in
+  NONE)
+    if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+    else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+    fi ;;
+  *) host_alias=$nonopt ;;
+  esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:596: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+  case $nonopt in
+  NONE) target_alias=$host_alias ;;
+  *) target_alias=$nonopt ;;
+  esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:614: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+  case $nonopt in
+  NONE) build_alias=$host_alias ;;
+  *) build_alias=$nonopt ;;
+  esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+
+
+
+case "$host_os" in
+  *linux*) cat >> confdefs.h <<\EOF
+#define LINUX 1
+EOF
+
+       TARGET=linux
+    ;;
+  *solaris*) cat >> confdefs.h <<\EOF
+#define SOLARIS 1
+EOF
+
+       TARGET=solarissparc
+       ;;
+  *sunos*)  TARGET=solarissparc
+        ;;
+  *freebsd*) TARGET=freebsd86
+       ;;
+  *irix*) TARGET=sgi
+      ;;
+  *hp*) TARGET=hpux
+     ;;
+  *HP*) TARGET=hpux
+     ;;
+  *sequent*) cat >> confdefs.h <<\EOF
+#define SEQUENT 1
+EOF
+       ;;
+  *) TARGET=$host_os
+     ;;
+esac
+
+case "$host_cpu" in
+  # force us down to '386 if we're on some other machine. 
+ *?86*) host_cpu='i386'
+       CPU=386
+       ;;
+  *) CPU=$host_cpu;
+       ;;
+esac
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:685: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:715: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:766: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:798: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 809 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:814: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:840: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:845: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:854: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:873: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:916: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:970: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 985 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:991: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1002 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1008: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 1019 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1025: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1050: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1055 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1063: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1080 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1098 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1119 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1130: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in \
+       unistd.h \
+       stdlib.h \
+       errno.h \
+       fcntl.h \
+       stdarg.h \
+       string.h \
+       scsi/scsi.h \
+       scsi/scsi_ioctl.h \
+       scsi/sg.h \
+       camlib.h \
+       cam/cam_ccb.h \
+       cam/scsi/scsi_message.h \
+        sys/fsid.h \
+       sys/fstyp.h \
+       sys/stat.h \
+       sys/types.h \
+       sys/mnttab.h \
+       sys/param.h \
+       sys/time.h \
+       sys/scsi/impl/uscsi.h \
+       sys/scsi.h \
+       sys/scsi_ctl.h \
+       sys/ioctl.h \
+       dslib.h \
+       du/defs.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1182: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1187 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1192: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1220: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1225 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1274: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1295: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1300 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_size_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+  cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1328: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1333 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_pid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+       
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1361: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1366 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1375: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_time=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+  cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
+echo "configure:1396: checking whether struct tm is in sys/time.h or time.h" >&5
+if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1401 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() {
+struct tm *tp; tp->tm_sec;
+; return 0; }
+EOF
+if { (eval echo configure:1409: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_struct_tm=time.h
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_struct_tm" 1>&6
+if test $ac_cv_struct_tm = sys/time.h; then
+  cat >> confdefs.h <<\EOF
+#define TM_IN_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:1430: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1438 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(int));
+  exit(0);
+}
+EOF
+if { (eval echo configure:1449: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_int=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:1469: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1477 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(long));
+  exit(0);
+}
+EOF
+if { (eval echo configure:1488: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_long=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:1508: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1515 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1526: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1530 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1541: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_bigendian=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1561 "configure"
+#include "confdefs.h"
+main () {
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1574: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_c_bigendian=no
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+  cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+
+
+
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1602: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1607 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1624: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_type_signal=void
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:1643: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1648 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vprintf(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1671: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:1695: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1700 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1723: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@TARGET@%$TARGET%g
+s%@CPU@%$CPU%g
+s%@CC@%$CC%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100755 (executable)
index 0000000..52160e9
--- /dev/null
@@ -0,0 +1,100 @@
+dnl Copyright 2001 Enhanced Software Technologies Inc.
+dnl Written Jan. 2001 Eric Lee Green
+
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(mtx.c)
+AC_CONFIG_HEADER(config.h)
+
+dnl Check system.
+AC_CANONICAL_SYSTEM
+AC_PREFIX_DEFAULT(/usr/local)
+
+case "$host_os" in
+  *linux*) AC_DEFINE(LINUX)
+       TARGET=linux
+    ;;
+  *solaris*) AC_DEFINE(SOLARIS)
+       TARGET=solarissparc
+       ;;
+  *sunos*)  TARGET=solarissparc
+        ;;
+  *freebsd*) TARGET=freebsd86
+       ;;
+  *irix*) TARGET=sgi
+      ;;
+  *hp*) TARGET=hpux
+     ;;
+  *HP*) TARGET=hpux
+     ;;
+  *sequent*) AC_DEFINE(SEQUENT) 
+       ;;
+  *) TARGET=$host_os
+     ;;
+esac
+AC_SUBST(TARGET)
+case "$host_cpu" in
+  # force us down to '386 if we're on some other machine. 
+ *?86*) host_cpu='i386'
+       CPU=386
+       ;;
+  *) CPU=$host_cpu;
+       ;;
+esac
+
+AC_SUBST(CPU)
+
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(\
+       unistd.h \
+       stdlib.h \
+       errno.h \
+       fcntl.h \
+       stdarg.h \
+       string.h \
+       scsi/scsi.h \
+       scsi/scsi_ioctl.h \
+       scsi/sg.h \
+       camlib.h \
+       cam/cam_ccb.h \
+       cam/scsi/scsi_message.h \
+        sys/fsid.h \
+       sys/fstyp.h \
+       sys/stat.h \
+       sys/types.h \
+       sys/mnttab.h \
+       sys/param.h \
+       sys/time.h \
+       sys/scsi/impl/uscsi.h \
+       sys/scsi.h \
+       sys/scsi_ctl.h \
+       sys/ioctl.h \
+       dslib.h \
+       du/defs.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_TYPE_PID_T       
+AC_HEADER_TIME
+AC_STRUCT_TM
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_C_BIGENDIAN
+
+
+
+dnl Checks for library functions.
+dnl AC_FUNC_ALLOCA
+
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+
+dnl Check for files
+
+AC_OUTPUT(Makefile)
diff --git a/contrib/MTX.html b/contrib/MTX.html
new file mode 100644 (file)
index 0000000..8ea9fce
--- /dev/null
@@ -0,0 +1,209 @@
+<HTML>
+<HEAD>
+<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE>
+<LINK REV="made" HREF="mailto:none">
+</HEAD>
+
+<BODY>
+
+<A NAME="__index__"></A>
+<!-- INDEX BEGIN -->
+
+<UL>
+
+       <LI><A HREF="#name">NAME</A></LI>
+       <LI><A HREF="#synopsis">SYNOPSIS</A></LI>
+       <LI><A HREF="#description">DESCRIPTION</A></LI>
+       <LI><A HREF="#variables">VARIABLES</A></LI>
+       <LI><A HREF="#usage">USAGE</A></LI>
+       <LI><A HREF="#notes">NOTES</A></LI>
+       <LI><A HREF="#requirements">REQUIREMENTS</A></LI>
+       <LI><A HREF="#todo">TODO</A></LI>
+       <LI><A HREF="#see also">SEE ALSO</A></LI>
+       <LI><A HREF="#author">AUTHOR</A></LI>
+       <LI><A HREF="#copyright">COPYRIGHT</A></LI>
+</UL>
+<!-- INDEX END -->
+
+<HR>
+<P>
+<H1><A NAME="name">NAME</A></H1>
+<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P>
+<P>
+<HR>
+<H1><A NAME="synopsis">SYNOPSIS</A></H1>
+<PRE>
+  use TapeChanger::MTX;</PRE>
+<PRE>
+  my $loaded = TapeChanger::MTX-&gt;loadedtape;
+  print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
+<PRE>
+  TapeChanger::MTX-&gt;loadtape('next');
+  my $nowloaded = TapeChanger::MTX-&gt;loadedtape; 
+  print &quot;Currently loaded: $nowloaded\n&quot; if ($nowloaded);
+</PRE>
+<PRE>
+
+See below for more available functions.</PRE>
+<P>
+<HR>
+<H1><A NAME="description">DESCRIPTION</A></H1>
+<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx' 
+tape library program.  It is meant to work with a simple shell/perl script
+to load and unload tapes as appropriate, and to provide a interface for
+more complicated programs to do the same.  The below functions and
+variables should do as good a job as explaining this as anything.</P>
+<P>
+<HR>
+<H1><A NAME="variables">VARIABLES</A></H1>
+<DL>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT
+=item $TapeChanger::MTX::MTX</A></STRONG><BR>
+<DD>
+What is the location of the 'mt' and 'mtx' binaries?  Can be set with
+'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and
+'/usr/local/sbin/mtx'.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR>
+<DD>
+What are the names of the tape (DRIVE) and changer (CONTROL) device
+nodes?  Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to
+'/dev/rmt/0' and '/dev/changer' respectively.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR>
+<DD>
+Does the tape drive have to eject the tape before the changer retrieves
+it?  It's okay to say 'yes' if it's not necessary, in most cases.  Can be
+set with $EJECT in ~/.mtxrc, or defaults to '1'.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR>
+<DD>
+How long should we wait to see if the drive is ready, in seconds, after
+mounting a volume?  Can be set with $READY_TIME in ~/.mtxrc, or defaults
+to 60.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR>
+<DD>
+Print debugging information?  Set to '0' for normal verbosity, '1' for
+debugging information, or '-1' for 'quiet mode' (be as quiet as possible).
+<P></P></DL>
+<P>
+<HR>
+<H1><A NAME="usage">USAGE</A></H1>
+<P>This module uses the following functions:</P>
+<DL>
+<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR>
+<DD>
+Runs 'mtx' and 'mt' as appropriate.  <CODE>COMMAND</CODE> is the command you're
+trying to send to them.  Uses 'warn()' to print the commands to the screen
+if $TapeChanger::MTX::DEBUG is set.
+<P></P>
+<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR>
+<DD>
+Returns the number of drives, number of slots, and currently loaded tape
+values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>.
+<P></P>
+<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR>
+<DD>
+Loads a tape into the tape changer, and waits until the drive is again
+ready to be written to.  <CODE>SLOT</CODE> can be any of the following (with the
+relevant function indicated):
+<PRE>
+  current       C&lt;loadedtape()&gt;
+  prev          C&lt;loadprevtape()&gt;
+  next          C&lt;loadnexttape()&gt;
+  first         C&lt;loadfirsttape()&gt;
+  last          C&lt;loadlasttape()&gt;
+  0             C&lt;_ejectdrive()&gt;
+  1..99         Loads the specified tape number, ejecting whatever is
+                currently in the drive.</PRE>
+<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0.  Returns 0 if
+successful, an error string otherwise.</P>
+<P></P>
+<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR>
+<DD>
+Loads the next, previous, first, and last tapes in the changer
+respectively.  Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>, 
+<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively.
+<P></P>
+<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR>
+<DD>
+Ejects the tape, by first ejecting the tape from the drive
+(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its
+slot (<STRONG>tape_cmd(unload)</STRONG>).  Returns 1 if successful, 0 otherwise.
+<P></P>
+<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR>
+<DD>
+Resets the changer, ejecting the tape and loading the first one from the
+changer.
+<P></P>
+<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR>
+<DD>
+Checks to see if the drive is ready or not, by waiting for up to 
+$TapeChanger::MTX::READY_TIME seconds to see if it can get status
+information using <STRONG>mt_cmd(status)</STRONG>.  Returns 1 if so, 0 otherwise.
+<P></P>
+<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR>
+<DD>
+Returns a string containing the loaded tape and the drive that it's
+mounted on.
+<P></P>
+<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR>
+<DD>
+Does some quick checks to see if you're actually capable of using this
+module, based on your user permissions.  Returns a list of problems if
+there are any, 0 otherwise.
+<P></P></DL>
+<P>
+<HR>
+<H1><A NAME="notes">NOTES</A></H1>
+<P>~/.mtxrc is automatically loaded when this module is used, if it exists,
+using do().  This could cause security problems if you're trying to use
+this with <CODE>setuid()</CODE> programs - so just don't do that.  If you want someone
+to have permission to mess with the tape drive and/or changer, let them
+have that permission directly.</P>
+<P>
+<HR>
+<H1><A NAME="requirements">REQUIREMENTS</A></H1>
+<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and
+reader connected to the system.</P>
+<P>
+<HR>
+<H1><A NAME="todo">TODO</A></H1>
+<P>Support for Input/Export slots is not included, though it may be later.
+Possibly works for multiple drives per changer, but I haven't tested it,
+so I probably missed something.  'load previous' doesn't actually work,
+because mtx doesn't support it (though the help says it does).</P>
+<P>
+<HR>
+<H1><A NAME="see also">SEE ALSO</A></H1>
+<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>.  Inspired by <STRONG>stc-changer</STRONG>, which comes
+with the AMANDA tape backup package (http://www.amanda.org), and MTX,
+available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P>
+<P>
+<HR>
+<H1><A NAME="author">AUTHOR</A></H1>
+<P>Tim Skirvin &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</P>
+<P>
+<HR>
+<H1><A NAME="copyright">COPYRIGHT</A></H1>
+<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and 
+Tim Skirvin &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</P>
+
+</BODY>
+
+</HTML>
diff --git a/contrib/README b/contrib/README
new file mode 100644 (file)
index 0000000..832ee0a
--- /dev/null
@@ -0,0 +1,3 @@
+Everything in here is copyrighted by its original copyright holder.
+The contents of this directory are not maintained as part of the 'mtx'
+project, they are maintained by their original copyright holders. 
diff --git a/contrib/TapeChanger-MTX-0.71b.tar.gz b/contrib/TapeChanger-MTX-0.71b.tar.gz
new file mode 100644 (file)
index 0000000..a42a2e0
Binary files /dev/null and b/contrib/TapeChanger-MTX-0.71b.tar.gz differ
diff --git a/contrib/config_sgen_solaris.sh b/contrib/config_sgen_solaris.sh
new file mode 100755 (executable)
index 0000000..1d8938b
--- /dev/null
@@ -0,0 +1,153 @@
+#!/bin/sh
+# Copyright 2001 Enhanced Software Technologies Inc.
+# All Rights Reserved
+#
+# This software is licensed under the terms of the Free Software Foundation's
+# General Public License, version 2. See http://www.fsf.org for more
+# inforation on the General Public License. It is released for public use in
+# the hope that others will find it useful. [NOTE FROM ERIC: Note that
+# this is now unmaintained, unless someone wishes to volunteer. In other
+# words, if you have a problem with this script, please fix it and forward
+# a new version of the script with your EMAIL address as the one to contact
+# about it :-) ]
+#
+# usage: config_sgen_solaris.sh check|[un]install
+#
+# This configures sgen under Solaris (we hope! :-). Note that this
+# *CAN* do a reboot of the system. Do NOT call this function unless
+# you are willing to let it do a reboot of the system! Also note that
+# this *must* be run as user 'root', since it does highly grokety things.
+
+
+mode="$1"
+cvs upd
+SGEN="/kernel/drv/sgen"
+SGEN_CONF="/kernel/drv/sgen.conf"
+
+do_check() {
+    if test ! -f $SGEN_CONF; then
+       # sgen.conf not installed...
+       return 1
+    fi
+
+    changer_type_count=`grep "changer" $SGEN_CONF | grep -v "^#" | wc -l`
+    target_count=`grep "target=" $SGEN_CONF | grep -v "^#" | wc -l`
+
+    if test $changer_type_count = 0 -o $target_count = 0; then
+       # sgen.conf not configured
+       return 1
+    fi
+
+    # sgen.conf installed, and configured
+    return 0
+}
+
+do_install() {
+
+    # see if already installed
+    do_check
+    if test $? = 0; then
+       echo "sgen already configured, skipping"
+       return 0 # successfully installed (?)
+    fi
+
+    if test ! -f $SGEN; then
+       echo "sgen driver not installed, aborting"
+       return 1
+    fi
+
+    echo "configuring sgen driver..."
+    
+    echo 'device-type-config-list="changer"; # BRU-PRO' >>$SGEN_CONF
+    target=0
+    while test $target -le 15; do
+       echo "name=\"sgen\" class=\"scsi\" target=$target lun=0; # BRU-PRO" >>$SGEN_CONF
+       target=`expr $target + 1`
+    done
+
+    echo "Attempting to reload driver..."
+    rem_drv sgen >/dev/null 2>&1
+    add_drv sgen
+    if test "$?" != "0"; then
+       # failed
+       touch /reconfigure
+       echo "Driver was successfully configured, but could not be re-loaded."
+       echo "The system must be rebooted for the driver changes to take effect."
+
+       ans=""
+       while test "$ans" = ""; do
+           printf "Do you want to reboot now (shutdown -g 1 -y -i 6)? [Y/n] "
+           read ans
+
+           if test "$ans" = "Y"; then
+               ans="y"
+           fi
+
+           if test "$ans" = "N"; then
+               ans="n"
+           fi
+
+           if test "$ans" != "y" -a "$ans" != "n"; then
+               echo "Please enter 'y' or 'n'"
+               ans=""
+           fi
+       done
+
+       if test "$ans" = "y"; then
+           shutdown -g 1 -y -i 6
+           # will be killed by reboot...
+           while true; do
+               echo "Waiting for reboot..."
+               sleep 300
+           done
+       fi
+
+       # not rebooted, exit with error
+       return 2
+    fi
+
+    # successful
+    return 0
+}
+
+do_uninstall() {
+    do_check
+    if test $? = 1; then
+       echo "sgen not configured, skipping"
+       return 0 # successfully uninstalled (?)
+    fi
+
+    printf "removing BRU-PRO configuration from $SGEN_CONF..."
+    grep -v "# BRU-PRO" $SGEN_CONF > ${SGEN_CONF}.$$ || return 1
+    cat ${SGEN_CONF}.$$ >${SGEN_CONF} || return 1
+    rm -f ${SGEN_CONF}.$$ >/dev/null  || return 1
+    printf "done\n"
+
+    touch /reconfigure
+    printf "Devices will be reconfigured at next reboot.\n"
+    return 0
+}
+
+uname | grep SunOS >/dev/null 2>&1
+if test $? != 0; then
+    echo "$0: not on Solaris, ABORT!"
+    exit 99
+fi
+
+case "$mode" in
+    check)
+       do_check
+       ;;
+    install)
+       do_install
+       ;;
+    uninstall)
+       do_uninstall
+       ;;
+    *)
+       echo "usage: $0 check|[un]install"
+       exit 1
+       ;;
+esac
+
+exit $?
diff --git a/contrib/mtx-changer b/contrib/mtx-changer
new file mode 100644 (file)
index 0000000..cf68ddd
--- /dev/null
@@ -0,0 +1,431 @@
+#! /bin/sh
+###############################################################################
+# AMANDA Tape Changer script for use with the MTX tape changer program
+# Version 1.0 - Tue Feb 20 13:59:39 CST 2001
+# 
+# Based on 'stc-changer' by Eric Berggren (eric@ee.pdx.edu)
+# Updated by Tim Skirvin (tskirvin@ks.uiuc.edu)
+# 
+# Given that there's no license...let's make this the Perl Artistic License.
+# Just make sure you give me and Eric credit if you modify this.  
+###############################################################################
+
+### USER CONFIGURATION
+# Name of the tape drive (takes place of "tapedev" option in amanda.conf)
+#  and default driver number in library (usu 0) that DRIVE_NAME points to
+DRIVE_NAME="/dev/rmt/0n"
+DRIVE_NUM=0
+
+# Location of "STC" command and control device
+MTX_CMD="/usr/local/sbin/mtx";
+MTX_CONTROL="/dev/scsi/changer/c4t1d0";
+
+# Whether tape drive must eject tape before changer retrieves
+#  (ie, EXB-2x0). Usually okay if set while not necessary, bad if
+#  required but not set.
+DRIVE_MUST_EJECT=1
+
+# How long to check drive readiness (in seconds) after mounting (or
+#  ejecting) a volume (on some libraries, the motion or eject command may
+#  complete before the drive has the volume fully mounted and online,
+#  or ready for retrieval, resulting in "Drive not ready"/"Media not
+#  ready" errors). Do an "mt status" command every 5 seconds upto this
+#  time.
+DRIVE_READY_TIME_MAX=120
+
+#  tape "mt" command location...
+MT_CMD="/usr/bin/mt"     # called via "MT_CMD -f DRIVE_NAME rewind" &
+                         #   "MT_CMD -f DRIVE_NAME offline" to eject
+                         # and "MT_CMD -f DRIVE_NAME status" to get ready info
+
+##############################################################################
+#
+NumDrives=-1
+NumSlots=-1
+LastSlot=-1
+LoadedTape=-1
+
+#
+# Usage information
+#
+usage()
+{
+    echo
+    echo "Usage: $Progname <command> [arg...]"
+    echo "  -info          reports capability and loaded tape"
+    echo "  -slot <slot>   loads specified tape into drive"
+    echo "        current  reports current mounted tape"
+    echo "        next     loads logically next tape (loops to top)"
+    echo "        prev     loads logically previous tape (loops to bot)"
+    echo "        first    loads first tape"
+    echo "        last     loads last tape"
+    echo "        0..99    loads tape from specified slot#"
+    echo "  -eject         uloads current mounted tape"
+    echo "  -reset         resets changer (and drive); loads first tape"
+    echo
+    exit 5
+}
+
+#
+# Perform "stc" changer command (& handle the "fatal" errors)
+#  else, set 'CommandResStr' and 'CommandRawResStr' to the result string 
+#  and 'CommandResCode' to the exit code
+#
+dotapecmd()
+{
+    cmd=$1
+    arg=$2
+
+    CommandResStr=`$MTX_CMD $MTX_CONTROL $cmd $arg 2>&1`
+    CommandRawResStr=$CommandResStr
+    CommandResCode=$?
+
+    CommandResStr=`echo $CommandResStr | head -1 | sed 's/^[^:]*: //'`
+    if [ $CommandResCode -gt 1 ]; then
+        echo "0 $Progname: returned $CommandResStr"
+        exit 2
+    fi
+}
+
+#
+# Unload tape from drive (a drive command; "ejecttape" is a changer command
+#  to actually retrieve the tape). Needed by some changers (controlled by
+#  setting "DRIVE_MUST_EJECT")
+#
+ejectdrive()
+{
+    # Tell drive to eject tape before changer retrieves; req'd by some
+    #  drives (ie, EXB-2x0). Not needed by QDLT-4x00. Do a "rewind"
+    #  command first, then "offline" to eject (instead of "rewoffl")
+    #
+    if [ "$DRIVE_MUST_EJECT" -ne 0 ]; then
+        mtresstr=`$MT_CMD -f $DRIVE_NAME rewind 2>&1`
+        mtrescode=$?
+
+        if [ $mtrescode -ne 0 ]; then
+            if echo "$mtresstr" | egrep -s 'no tape'; then
+                :;   # no tape mounted; assume okay...
+            else
+                # can't eject tape, bad; output: <tape#> reason
+                echo "0 $mtresstr"
+                exit 1
+            fi
+        else
+            mtresstr=`$MT_CMD -f $DRIVE_NAME offline 2>&1`
+            mtrescode=$?
+
+            checkdrive 1
+        fi
+    fi
+}
+
+#
+# Check drive readiness after (un)mounting a volume (which may take a while
+#  after the volume change command completes)
+#
+checkdrive()
+{
+    unmounting=$1
+
+    if [ "$DRIVE_READY_TIME_MAX" -gt 0 ]; then
+
+        # sleep time between checks
+        pausetime=5
+
+        # number of interations to check
+        numchecks=`expr $DRIVE_READY_TIME_MAX / $pausetime`
+        if [ "$numchecks" -eq 0 ]; then
+            numchecks=1
+        fi
+
+        # check until success, or out of attempts...
+        while [ "$numchecks" -gt 0 ]; do
+            mtresstr=`$MT_CMD -f $DRIVE_NAME status 2>&1`
+            mtrescode=$?
+
+            if [ $mtrescode -eq 0 ]; then
+                # Success ?
+                return 0
+            else
+                # pause, before trying again....
+                if [ "$numchecks" -gt 1 ]; then
+                    sleep $pausetime
+
+                    # if unmounting a volume, check for 'mt' command
+                    #  failure; (sleep first for additional comfort)
+                    if [ "$unmounting" -ne 0 ]; then
+                        return 0
+                    fi
+                fi
+            fi
+            numchecks=`expr $numchecks - 1`
+        done
+
+        # failed; output: -1 reason
+        echo "-1 drive won't report ready"
+        exit 1
+    fi
+}
+
+#
+# Get changer parameters
+#
+getchangerparms()
+{
+    dotapecmd status
+    if [ $CommandResCode -eq 0 ] && \
+        echo "$CommandResStr" | egrep -s '^Storage Changer'; then
+
+        NumDrives=`echo $dspec | wc -l`
+       NumDrives=`echo "$CommandRawResStr" | \
+                       grep 'Data Transfer Element' | wc -l`
+        if [ "$NumDrives" -le "$DRIVE_NUM" ]; then
+            echo "$Program: Invalid drive # specified ($DRIVE_NUM > $NumDrives)"
+            exit 3
+        fi
+                       # grep 'Data Transfer Element $DRIVE_NUM' | \
+       LoadedTape=`echo "$CommandRawResStr" | \
+                       grep 'Data Transfer Element' | \
+                       grep 'Storage Element [0-9]' | \
+                       awk '{ print $7 }'      `
+        if [ -z "$LoadedTape" -o "$LoadedTape" = "e" ]; then
+            LoadedTape=-1
+        fi
+        NumSlots=`echo "$CommandRawResStr" | \
+               grep 'Storage Element [0-9]\{1,\}:' | \
+               grep -v 'Data Element' | \
+               wc -l | sed -e 's/ //g' `
+        LastSlot=`expr $NumSlots - 1`
+    else
+        echo \
+          "$Progname: Can't get changer parameters; Result was $CommandResStr"
+        exit 3
+    fi
+}
+    
+#
+# Display changer info
+#
+changerinfo()
+{
+    getchangerparms
+
+    # output status string: currenttape numslots randomaccess?
+    echo "$LoadedTape $NumSlots 1"
+    exit 0
+}
+
+#
+# Eject current mounted tape
+#
+ejecttape()
+{
+    getchangerparms
+    ct=$LoadedTape
+
+    # If no tape reported mounted, assume success (could be bad if changer
+    #  lost track of tape)
+    #
+    if [ $ct -lt 0 ]; then
+        CommandResCode=0
+    else
+        ejectdrive
+        dotapecmd unload
+    fi
+
+    if [ $CommandResCode -ne 0 ]; then
+        # failed; output: <tape#> reason
+        echo "$ct $CommandResStr"
+        exit 1
+    else
+        # success; output: <tape#> drive
+        echo "$ct $DRIVE_NAME"
+        exit 0
+    fi
+}
+
+#
+# Move specified tape into drive (operation level)
+#
+doloadtape()
+{
+    slot=$1
+    if [ "$slot" -eq "$LoadedTape" ]; then
+        return 0
+    fi
+    ejectdrive
+    dotapecmd load $slot
+    return $CommandResCode
+}
+
+#
+# Load next available tape into drive
+#
+loadnexttape()
+{
+    curslot=$1
+    direction=$2
+
+    startslot=$curslot
+    while true; do
+        if doloadtape $curslot; then
+            return 0
+        else
+            if echo $CommandResStr | egrep -s 'Slot.*reported empty'; then
+
+                if [ "$direction" -lt 0 ]; then
+                    curslot=`expr $curslot - 1`
+                    if [ "$curslot" -lt 0 ]; then
+                        curslot=$LastSlot
+                    fi
+                else
+                    curslot=`expr $curslot + 1`
+                    if [ "$curslot" -gt "$LastSlot" ]; then
+                        curslot=0
+                    fi
+                fi
+
+                # Check if we're back to where we started...
+                if [ "$curslot" = "$startslot" ]; then
+                    if [ "$direction" -lt 0 ]; then
+                        CommandResStr="No previous volume available"
+                    else
+                        CommandResStr="No subsequent volume available"
+                    fi
+                    return 1
+                fi
+            else
+                return 1
+            fi
+        fi
+    done
+}
+
+#
+# Report loadtape() status
+#
+reportstatus()
+{
+    if [ $CommandResCode -eq 0 ]; then
+        # success; output currenttape drivename
+        echo "$LoadedTape $DRIVE_NAME"
+        exit 0
+    else
+        # failed (empty slot?); output currenttape reason
+        echo "$LoadedTape $CommandResStr"
+        exit 1
+    fi
+}
+
+
+#
+# Move specified tape into drive (command level)
+#
+loadtape()
+{
+    slot=$1
+
+    getchangerparms
+
+    case "$slot" in
+        current)
+            if [ $LoadedTape -lt 0 ]; then
+                CommandResStr="Can't determine current tape; drive empty ?"
+                CommandResCode=1
+            fi
+            ;;
+        prev)
+            if [ $LoadedTape -le 0 ]; then
+                loadnexttape $LastSlot -1
+            else
+                loadnexttape `expr $LoadedTape - 1` -1
+            fi
+            ;;
+        next)
+            if [ $LoadedTape -ge $LastSlot -o $LoadedTape -lt 0 ]; then
+                loadnexttape 0 1
+            else
+                loadnexttape `expr $LoadedTape + 1` 1
+            fi
+            ;;
+        first)
+            loadnexttape 0 1
+            ;;
+        last)
+            loadnexttape $LastSlot -1
+            ;;
+        [0-9]*)
+            doloadtape $slot
+            ;;
+        *)
+            # error; no valid slot specified
+            echo "$Progname: No valid slot specified"
+            exit 1
+            ;;
+    esac
+
+    if [ $CommandResCode -eq 0 ]; then
+        getchangerparms
+        checkdrive
+    fi
+    reportstatus
+}
+
+#
+# Reset changer to known state
+#
+resetchanger()
+{
+    ejectdrive
+    dotapecmd reset
+    if [ $CommandResCode -ne 0 ]; then
+        # failed; output: failed? reason
+        echo "-1 $CommandResStr"
+        exit 2;
+    else
+        loadtape first
+    fi
+}
+
+#############################################################################
+#
+# MAIN
+#
+Progname=`basename $0`
+
+if [ ! -x "$MTX_CMD" ]; then
+    echo "-1 $Progname: cannot run STC command ($MTX_CMD)"
+    exit 2
+fi
+if [ -n "$MTX_CONTROL" ]; then
+    if echo "$MTX_CONTROL" | egrep -s '^-f'; then
+        :;
+    else
+        MTX_CONTROL="-f $MTX_CONTROL"
+    fi
+fi
+if [ -n "$DRIVE_NUM" ]; then
+    DRIVE_NUM=0
+fi
+
+if [ $# -ge 1 ]; then command=$1; else command="-usage"; fi
+
+case "$command" in
+    -info)
+        changerinfo
+        ;;
+    -slot)
+        loadtape $2
+        ;;
+    -eject)
+        ejecttape
+        ;;
+    -reset)
+        resetchanger
+        ;;
+    *)
+        usage
+        ;;
+esac
+
+exit 0
diff --git a/contrib/mtx.py b/contrib/mtx.py
new file mode 100644 (file)
index 0000000..43564c3
--- /dev/null
@@ -0,0 +1,306 @@
+# Copyright 2000 Enhanced Software Technologies Inc.
+# Released under Free Software Foundation's General Public License,
+# Version 2 or above
+#
+# This is an example of how to parse the 'mtx' output from a scripting
+# language. 
+#
+# Routine to call 'mtx' and read status, or
+# whatever.
+#
+# We do this here rather than creating a Python "C" module because:
+#  1) Reduces complexity of compile environment
+#  2) Easier debugging.
+#  3) More in keeping with the Unix philosophy of things.
+#  4) Easier to add new functionality. 
+#
+#
+
+import string
+import os
+import time # we can do some waiting here...
+
+def readpipe(command):
+    result=""
+    
+    infile=os.popen(command,"r")
+
+    try:
+        s=infile.read()
+    except:
+        s=""
+        pass
+    if not s:
+        return None  # didn't get anything :-(. 
+    result=result+s
+    return result
+
+
+
+
+# these are returned by the mtx.status routine:
+class slotstatus:
+    def __init__(self,slotnum,middle,barcode=None):
+        middle=string.strip(middle)
+        try:
+            left,right=string.split(middle," ")
+        except:
+            left=middle
+            right=None
+            pass
+        # Note: We will not be able to test this at the moment since the
+        # 220 has no import/export port!
+        if right=="IMPORT/EXPORT":
+            self.importexport=1
+        else:
+            self.importexport=0
+            pass
+        self.slotnum=int(slotnum) # make sure it's an integer...
+        if left=="Full":
+            self.full=1
+        else:
+            self.full=None
+            pass
+        if not barcode:
+            self.voltag=None
+        else:
+            l=string.split(barcode,"=",1)
+            self.voltag=l[1]
+            pass
+        return
+    pass
+
+# Drive status lines have this format:
+#Data Transfer Element 0:Full (Storage Element 10 Loaded):VolumeTag = B0000009H
+#Data Transfer Element 1:Empty
+
+class drivestatus:
+    def __init__(self,slotnum,middle,barcode=None):
+        self.slotnum=slotnum
+        if middle=="Empty":
+            self.full=None
+            self.origin=None
+            self.voltag=None
+            return
+        else:
+            self.full=1
+            pass
+
+        # okay, we know we have a tape in the drive. 
+        # split and find out our origin: we will always have
+        # an origin, even if the #$@% mtx program had to dig one
+        # out of the air:
+
+        l=string.split(middle," ")
+        self.origin=int(l[3])
+
+        if not barcode:
+            self.voltag=None # barcode of this element.
+        else:
+            l=string.split(barcode," ")
+            self.voltag=string.strip(l[2])
+            pass
+        return
+    pass
+
+# This is the return value for mtx.status. 
+class mtxstatus:
+    def __init__(self):
+        self.numdrives=None
+        self.numslots=None
+        self.numexport=None
+        self.drives=[]  # a list of drivestatus instances.
+        self.slots=[]   # a list of slotstatus instances
+        self.export=[]  # another list of slotstatus instances
+        return
+    pass
+
+# call 'mtx' and get barcode info, if possible:
+# okay, we now have a string that consists of a number of lines.
+# we want to explode this string into its component parts.
+# Example format:
+# Storage Changer /dev/sgd:2 Drives, 21 Slots
+# Data Transfer Element 0:Full (Storage Element '5' Loaded) 
+# Data Transfer Element 1:Empty
+# Storage Element 1:Full :VolumeTag=CLNA0001
+# Storage Element 2:Full :VolumeTag=B0000009
+# Storage Element 3:Full :VolumeTag=B0000010
+# ....
+# What we want to do, then, is:
+# 1) Turn it into lines by doing a string.split on newline.
+# 2) Split the 1st line on ":" to get left and right.
+# 3) Split the right half on space to get #drives "Drives," #slots
+# 4) process the drives (Full,Empty, etc.)
+# 5) For each of the remaining lines: Split on ':'
+# 6) Full/Empty status is in 2)
+#
+configdir="/opt/brupro/bin"  # sigh. 
+
+def status(device):
+    retval=mtxstatus()
+    command="%s/mtx -f %s status" % (configdir,device)
+    result=readpipe(command)
+    if not result:
+        return None  # sorry! 
+    # now to parse:
+
+    try:
+        lines=string.split(result,"\n")
+    except:
+        return None # sorry, no status!
+    
+    # print "DEBUG:lines=",lines
+
+    try:
+        l=string.split(lines[0],":")
+    except:
+        return None # sorry, no status!
+    
+    # print "DEBUG:l=",l
+    leftside=l[0]
+    rightside=l[1]
+    if len(l) > 2:
+        barcode=l[3]
+    else:
+        barcode=None
+        pass
+    t=string.split(rightside)
+    retval.numdrives=int(t[0])
+    retval.numslots=int(t[2])
+    
+    for s in lines[1:]:
+        if not s:
+            continue # skip blank lines!
+        #print "DEBUG:s=",s
+        parts=string.split(s,":")
+        leftpart=string.split(parts[0])
+        rightpart=parts[1]
+        try:
+            barcode=parts[2]
+        except:
+            barcode=None
+            pass
+        #print "DEBUG:leftpart=",leftpart
+        if leftpart[0]=="Data" and leftpart[1]=="Transfer":
+            retval.drives.append(drivestatus(leftpart[3],rightpart,barcode))
+            pass
+        if leftpart[0]=="Storage" and leftpart[1]=="Element":
+            element=slotstatus(leftpart[2],rightpart,barcode)
+            if element.importexport:
+                retval.export.append(element)
+            else:
+                retval.slots.append(element)
+                pass
+            pass
+        continue
+
+    return retval
+
+# Output of a mtx inquiry looks like:
+#
+#Product Type: Medium Changer
+#Vendor ID: 'EXABYTE '
+#Product ID: 'Exabyte EZ17    '
+#Revision: '1.07'
+#Attached Changer: No
+#
+# We simply return a hash table with these values { left:right } format. 
+
+def mtxinquiry(device):
+    command="%s/mtx -f %s inquiry" % (configdir,device)
+    str=readpipe(command) # calls the command, returns all its data.
+
+    str=string.strip(str)
+    lines=string.split(str,"\n")
+    retval={}
+    for l in lines:
+        # DEBUG #
+        l=string.strip(l)
+        print "splitting line: '",l,"'"
+        idx,val=string.split(l,':',1)
+        val=string.strip(val)
+        if val[0]=="'":
+            val=val[1:-1] # strip off single quotes, sigh. 
+            pass 
+        retval[idx]=val
+        continue
+    return retval
+
+# Now for the easy part:
+
+def load(device,slot,drive=0):
+    command="%s/mtx -f %s load %s %s >/dev/null " % (configdir,device,slot,drive)
+    status=os.system(command)
+    return status
+
+def unload(device,slot,drive=0):
+    command="%s/mtx -f %s unload %s %s >/dev/null " % (configdir,device,slot,drive)
+    return os.system(command)
+
+def inventory(device):
+    command="%s/mtx -f %s inventory >/dev/null " % (configdir,device)
+    return os.system(command)
+
+def wait_for_inventory(device):
+    # loop while we have an error return...
+    errcount=0
+    while inventory(device):
+        if errcount==0:
+            print "Waiting for loader '%s'" % device
+            pass
+        time.sleep(1)
+        try:
+            s=status(device)
+        except:
+            s=None
+            pass
+        if s:
+            return 0 # well, whatever we're doing, we're inventoried :-(
+        errcount=errcount+1
+        if errcount==600:   # we've been waiting for 10 minutes :-(
+            return 1 # sorry!
+        continue
+    return 0  # we succeeded!
+
+# RCS REVISION LOG:
+# $Log: mtx.py,v $
+# Revision 1.1.1.1  2001/06/05 17:10:51  elgreen
+# Initial import into SourceForge
+#
+# Revision 1.2  2000/12/22 14:17:19  eric
+# mtx 1.2.11pre1
+#
+# Revision 1.14  2000/11/12 20:35:29  eric
+# do string.strip on the voltag
+#
+# Revision 1.13  2000/11/04 00:33:38  eric
+# if we can get an inventory on the loader after we send it 'mtx inventory',
+# then obviously we managed to do SOMETHING.
+#
+# Revision 1.12  2000/10/28 00:04:34  eric
+# added wait_for_inventory command
+#
+# Revision 1.11  2000/10/27 23:27:58  eric
+# Added inventory command...
+#
+# Revision 1.10  2000/10/01 01:06:29  eric
+# evening checkin
+#
+# Revision 1.9  2000/09/29 02:49:29  eric
+# evening checkin
+#
+# Revision 1.8  2000/09/02 01:05:33  eric
+# Evening Checkin
+#
+# Revision 1.7  2000/09/01 00:08:11  eric
+# strip lines in mtxinquiry
+#
+# Revision 1.6  2000/09/01 00:05:33  eric
+# debugging
+#
+# Revision 1.5  2000/08/31 23:46:01  eric
+# fix def:
+#
+# Revision 1.4  2000/08/31 23:44:06  eric
+# =->==
+#
diff --git a/contrib/mtxctl-0.0.2.tar.gz b/contrib/mtxctl-0.0.2.tar.gz
new file mode 100644 (file)
index 0000000..de41b7d
Binary files /dev/null and b/contrib/mtxctl-0.0.2.tar.gz differ
diff --git a/contrib/tapechanger.html b/contrib/tapechanger.html
new file mode 100644 (file)
index 0000000..8ea9fce
--- /dev/null
@@ -0,0 +1,209 @@
+<HTML>
+<HEAD>
+<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE>
+<LINK REV="made" HREF="mailto:none">
+</HEAD>
+
+<BODY>
+
+<A NAME="__index__"></A>
+<!-- INDEX BEGIN -->
+
+<UL>
+
+       <LI><A HREF="#name">NAME</A></LI>
+       <LI><A HREF="#synopsis">SYNOPSIS</A></LI>
+       <LI><A HREF="#description">DESCRIPTION</A></LI>
+       <LI><A HREF="#variables">VARIABLES</A></LI>
+       <LI><A HREF="#usage">USAGE</A></LI>
+       <LI><A HREF="#notes">NOTES</A></LI>
+       <LI><A HREF="#requirements">REQUIREMENTS</A></LI>
+       <LI><A HREF="#todo">TODO</A></LI>
+       <LI><A HREF="#see also">SEE ALSO</A></LI>
+       <LI><A HREF="#author">AUTHOR</A></LI>
+       <LI><A HREF="#copyright">COPYRIGHT</A></LI>
+</UL>
+<!-- INDEX END -->
+
+<HR>
+<P>
+<H1><A NAME="name">NAME</A></H1>
+<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P>
+<P>
+<HR>
+<H1><A NAME="synopsis">SYNOPSIS</A></H1>
+<PRE>
+  use TapeChanger::MTX;</PRE>
+<PRE>
+  my $loaded = TapeChanger::MTX-&gt;loadedtape;
+  print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
+<PRE>
+  TapeChanger::MTX-&gt;loadtape('next');
+  my $nowloaded = TapeChanger::MTX-&gt;loadedtape; 
+  print &quot;Currently loaded: $nowloaded\n&quot; if ($nowloaded);
+</PRE>
+<PRE>
+
+See below for more available functions.</PRE>
+<P>
+<HR>
+<H1><A NAME="description">DESCRIPTION</A></H1>
+<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx' 
+tape library program.  It is meant to work with a simple shell/perl script
+to load and unload tapes as appropriate, and to provide a interface for
+more complicated programs to do the same.  The below functions and
+variables should do as good a job as explaining this as anything.</P>
+<P>
+<HR>
+<H1><A NAME="variables">VARIABLES</A></H1>
+<DL>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT
+=item $TapeChanger::MTX::MTX</A></STRONG><BR>
+<DD>
+What is the location of the 'mt' and 'mtx' binaries?  Can be set with
+'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and
+'/usr/local/sbin/mtx'.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR>
+<DD>
+What are the names of the tape (DRIVE) and changer (CONTROL) device
+nodes?  Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to
+'/dev/rmt/0' and '/dev/changer' respectively.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR>
+<DD>
+Does the tape drive have to eject the tape before the changer retrieves
+it?  It's okay to say 'yes' if it's not necessary, in most cases.  Can be
+set with $EJECT in ~/.mtxrc, or defaults to '1'.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR>
+<DD>
+How long should we wait to see if the drive is ready, in seconds, after
+mounting a volume?  Can be set with $READY_TIME in ~/.mtxrc, or defaults
+to 60.
+<P></P>
+<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR>
+<DD>
+Print debugging information?  Set to '0' for normal verbosity, '1' for
+debugging information, or '-1' for 'quiet mode' (be as quiet as possible).
+<P></P></DL>
+<P>
+<HR>
+<H1><A NAME="usage">USAGE</A></H1>
+<P>This module uses the following functions:</P>
+<DL>
+<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR>
+<DD>
+Runs 'mtx' and 'mt' as appropriate.  <CODE>COMMAND</CODE> is the command you're
+trying to send to them.  Uses 'warn()' to print the commands to the screen
+if $TapeChanger::MTX::DEBUG is set.
+<P></P>
+<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR>
+<DD>
+Returns the number of drives, number of slots, and currently loaded tape
+values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>.
+<P></P>
+<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR>
+<DD>
+Loads a tape into the tape changer, and waits until the drive is again
+ready to be written to.  <CODE>SLOT</CODE> can be any of the following (with the
+relevant function indicated):
+<PRE>
+  current       C&lt;loadedtape()&gt;
+  prev          C&lt;loadprevtape()&gt;
+  next          C&lt;loadnexttape()&gt;
+  first         C&lt;loadfirsttape()&gt;
+  last          C&lt;loadlasttape()&gt;
+  0             C&lt;_ejectdrive()&gt;
+  1..99         Loads the specified tape number, ejecting whatever is
+                currently in the drive.</PRE>
+<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0.  Returns 0 if
+successful, an error string otherwise.</P>
+<P></P>
+<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR>
+<DD>
+<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR>
+<DD>
+Loads the next, previous, first, and last tapes in the changer
+respectively.  Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>, 
+<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively.
+<P></P>
+<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR>
+<DD>
+Ejects the tape, by first ejecting the tape from the drive
+(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its
+slot (<STRONG>tape_cmd(unload)</STRONG>).  Returns 1 if successful, 0 otherwise.
+<P></P>
+<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR>
+<DD>
+Resets the changer, ejecting the tape and loading the first one from the
+changer.
+<P></P>
+<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR>
+<DD>
+Checks to see if the drive is ready or not, by waiting for up to 
+$TapeChanger::MTX::READY_TIME seconds to see if it can get status
+information using <STRONG>mt_cmd(status)</STRONG>.  Returns 1 if so, 0 otherwise.
+<P></P>
+<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR>
+<DD>
+Returns a string containing the loaded tape and the drive that it's
+mounted on.
+<P></P>
+<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR>
+<DD>
+Does some quick checks to see if you're actually capable of using this
+module, based on your user permissions.  Returns a list of problems if
+there are any, 0 otherwise.
+<P></P></DL>
+<P>
+<HR>
+<H1><A NAME="notes">NOTES</A></H1>
+<P>~/.mtxrc is automatically loaded when this module is used, if it exists,
+using do().  This could cause security problems if you're trying to use
+this with <CODE>setuid()</CODE> programs - so just don't do that.  If you want someone
+to have permission to mess with the tape drive and/or changer, let them
+have that permission directly.</P>
+<P>
+<HR>
+<H1><A NAME="requirements">REQUIREMENTS</A></H1>
+<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and
+reader connected to the system.</P>
+<P>
+<HR>
+<H1><A NAME="todo">TODO</A></H1>
+<P>Support for Input/Export slots is not included, though it may be later.
+Possibly works for multiple drives per changer, but I haven't tested it,
+so I probably missed something.  'load previous' doesn't actually work,
+because mtx doesn't support it (though the help says it does).</P>
+<P>
+<HR>
+<H1><A NAME="see also">SEE ALSO</A></H1>
+<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>.  Inspired by <STRONG>stc-changer</STRONG>, which comes
+with the AMANDA tape backup package (http://www.amanda.org), and MTX,
+available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P>
+<P>
+<HR>
+<H1><A NAME="author">AUTHOR</A></H1>
+<P>Tim Skirvin &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</P>
+<P>
+<HR>
+<H1><A NAME="copyright">COPYRIGHT</A></H1>
+<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and 
+Tim Skirvin &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</P>
+
+</BODY>
+
+</HTML>
diff --git a/contrib/tapeinfo.py b/contrib/tapeinfo.py
new file mode 100644 (file)
index 0000000..d5df136
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright 2000 Enhanced Software Technologies Inc.
+# All Rights Reserved
+# Released under Free Software Foundation's General Public License,
+# Version 2 or above
+
+# Routine to call 'tapeinfo' and read status for a node. This is an
+# example of how to parse the 'tapeinfo' output from a scripting language.
+#
+
+import os
+import string
+import sys
+
+
+configdir="/opt/brupro/bin"  # sigh.
+
+def inquiry(device):
+    retval={}
+
+    # okay, now do the thing:
+
+    command="%s/tapeinfo -f %s" % (configdir,device)
+
+    # Now to read:
+
+    infile=os.popen(command,"r")
+
+    try:
+        s=infile.readline()
+    except:
+        s=""
+        pass
+    if not s:
+        return None # did not get anything.
+    while s:
+        s=string.strip(s)
+        idx,val=string.split(s,':',1)
+        val=string.strip(val)
+        if val[0]=="'":
+            val=val[1:-1] # strip off single quotes, sigh.
+            val=string.strip(val)
+            pass
+        while "\0" in val:
+            # zapo!
+            val=string.replace(val,"\0","")
+            pass
+        retval[idx]=val
+        try:
+            s=infile.readline()
+        except:
+            s=""
+            pass
+        continue # to top of loop!
+    return retval
+
diff --git a/contrib/tapeload.pl b/contrib/tapeload.pl
new file mode 100644 (file)
index 0000000..de1f221
--- /dev/null
@@ -0,0 +1,118 @@
+!/usr/bin/perl
+##########################  tapeload   ###########################
+# This script uses mtx 1.2.9pre2 to load a tape based 
+# on its volume tag.  You can
+# specify a tape drive by number, but if you don`t, it puts the
+# tape in the first available drive and returns the number of that drive,
+# both from the standard output and as the exit value.
+# A negative exit value indicates an error.
+# If volume tags are missing from any full slot, bar codes are rescanned
+# automatically.  
+#
+# usage: 
+# tapeload TAPE_LABEL_1    # Loads tape with label TAPE_LABEL_1 into a drive
+# or
+# tapeload TAPE_LABEL_1 1  # Loads tape with label TAPE_LABEL_1 into drive #1
+#
+
+# Set this variable to your mtx binary and proper scsi library device.
+$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ;  
+
+# Additions and corrections are welcome.
+# This software comes with absolutely no warranty and every other imaginable
+# disclaimer.
+#   -- Frank Samuelson sam@milagro.lanl.gov
+##################################################################
+
+@wt= &mdo("status");  #slurp in the list of slots
+
+# Check to be certain that every full slot has a volume tag
+for ($i=0; $i< $#wt; $i++) {  # look through every line
+    if ( $wt[$i] =~ /Full/  && $wt[$i] !~ /VolumeTag/ ) {
+        # if the element is full, but there is no volume tag, do inventory
+        @wt= &mdo("inventory status");
+        break;
+    }
+}
+
+#try to find our tape
+$slot=-1;
+for ($i=0; $i< $#wt; $i++) {  # look through every line
+    if ($wt[$i] =~ / *Storage Element (d*):Full :VolumeTag=(.*)/ ) {
+        if ($ARGV[0] eq  $2) { # We found the tape
+            $slot=$1;          # set the slot number
+            break;             # stop reading the rest of the file.
+        }
+    }
+}
+
+if ( $slot>0) {         # we found the tape you wanted.
+
+    $drivefound=-1;          # set flag to bad value
+    for ($i=0; $i< $#wt; $i++) {  # look through every line
+        # if this is a tape drive
+        if ($wt[$i] =~ / *Data Transfer Element (d*):(.*)/ ) { #parse the line
+            $drive=$1;
+            $state=$2;
+#           print STDERR "$wt[$i] $drive $state";
+            if ($state =~ /Full/) {   # This drive is full.
+                # if we are looking for a particular drive and this is it
+                if ( $#ARGV==1 && $drive == $ARGV[1]) { 
+                    print STDERR " ERROR: Specified drive $ARGV[1] is full.";
+                    print STDERR @wt;
+                    exit(-6);
+                }
+            } elsif ($state =~ /Empty/) { #This is a tape drive and it`s empty.
+                if ( $#ARGV==1 ) {          # If we want a particular drive
+                    if ($drive == $ARGV[1]) {   # and this is it,
+                        $drivefound=$drive;      # mark it so.
+                        break;
+                    }
+                } else {              # If any old drive will do
+                    $drivefound=$drive;    # Mark it.
+                    break;
+                }
+            } else {         # This is a tape drive, but what the heck is it?
+                print STDERR " Cannot assess drive status in line";
+                print STDERR $wt[$i];
+                exit(-7);
+            }
+        }
+    }
+
+    if ( $drivefound < 0 ) {  # specified drive was not found
+        print STDERR "Error: Specified drive $ARGV[1] was not found";
+        print STDERR @wt;
+        exit(-8);
+    }
+    # Now we actually load the tape.
+    @dump=&mdo(" load $slot $drivefound ");
+    print "$drivefound";
+    exit($drivefound);
+    # The end.
+
+    
+} else {
+    print STDERR " Ug. Tape $ARGV[0] is not in the library.";
+    print STDERR @wt;
+    exit(-4);
+}
+
+
+sub mdo             # a subroutine to call mtx ;
+{
+#    print STDERR "$_[0]";
+    if (!open(FD,"$MTXBIN $_[0] |")) {    #call mtx function 
+        print STDERR " ERRKK.  Could not start mtx ";
+        exit (-9);
+    }
+
+    @twt= <FD>;        # slurp in the output
+
+    if (! close(FD)) {        # if mtx exited with a nonzero value...
+        print STDERR " Mtx gave an error. Tapeload is exiting... ";
+        exit (-10);
+    }
+
+    @twt;
+}
diff --git a/contrib/tapeunload.pl b/contrib/tapeunload.pl
new file mode 100644 (file)
index 0000000..e52a4ef
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+##########################  tapeunload   ###########################
+# This script uses mtx 1.2.9pre2 to unload a tape 
+# based on its volume tag.  You can
+# specify a slot into which the tape should go, but if you don`t, it puts the
+# tape into the slot from which it was taken.  This slot number
+# is returned 
+# both from the standard output and as the exit value.
+# A negative exit value indicates an error.
+# If volume tags are missing from any full slot or drive, 
+# bar codes are rescanned automatically.
+# Note: This script assumes that the tape is ready to be removed from
+# the drive.  That means you may have to unload the tape from the drive
+# with  "mt offline" before the tape can be moved to a storage slot.
+# 
+
+# usage: 
+# tapeunload TAPE_LABEL_1  
+# Removes tape with label TAPE_LABEL_1 from a drive and puts it
+# back into its storage slot. Or, 
+# tapeunload TAPE_LABEL_1 40 
+# Removes tape with label TAPE_LABEL_1 from a drive and puts it
+# into its storage slot 40. 
+#
+
+# Set this variable to your mtx binary and proper scsi library device.
+$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ;  
+
+# Additions and corrections are welcome.
+# This software comes with absolutely no warranty and every other imaginable
+# disclaimer.
+#   --  Frank Samuelson sam@milagro.lanl.gov
+
+##################################################################
+
+@wt= &mdo("status");  #slurp in the list of slots
+
+# Check to be certain that every full slot has a volume tag
+# Rescanning probably will not help.  I haven`t seen any bar code
+# readers that read tapes that are in the drives.  But you never know...
+for ($i=0; $i< $#wt; $i++) {  # look through every line
+    if ( $wt[$i] =~ /Full/  && $wt[$i] !~ /VolumeTag/ ) {
+        # if the element is full, but there is no volume tag, do inventory
+        @wt= &mdo("inventory status");
+        break;
+    }
+}
+
+#try to find our tape
+$drivein=-1;
+for ($i=0; $i< $#wt; $i++) {  # look through every line
+                              # for a full tape drive 
+    if ($wt[$i] =~ / *Data Transfer Element (d*):Full (Storage Element
+(d*) Loaded):VolumeTag = (.*)/ ){
+        if ($ARGV[0] eq  $3) { # We found our tape
+            $drivein=$1;          # set the drive number
+            $slottogo=$2;       # set the slot number
+            break;             # stop reading the rest of the file.
+        }
+    }
+}
+
+if ( $drivein>=0) {         # we found the tape you wanted.
+    if ($#ARGV==1) {         #If an alternative slot was requested, set it.
+        $slottogo=$ARGV[1];  # and let mtx handle the errors.
+    }
+    
+    # Now we unload the tape.
+    @dump=&mdo(" unload $slottogo $drivein ");
+    print "$slottogo";
+    exit($slottogo);
+    # The end.
+
+    
+} else {
+    print STDERR " Ug. Tape $ARGV[0] is not in a tape drive.";
+    print STDERR @wt;
+    exit(-4);
+}
+
+
+sub mdo             # a subroutine to call mtx ;
+{
+#    print STDERR "$_[0]";
+    if (!open(FD,"$MTXBIN $_[0] |")) {    #call mtx function 
+        print STDERR " ERRKK.  Could not start mtx ";
+        exit (-9);
+    }
+
+    @twt= <FD>;        # slurp in the output
+
+    if (! close(FD)) {        # if mtx exited with a nonzero value...
+        print STDERR " Mtx gave an error. Tapeload is exiting... ";
+        exit (-10);
+    }
+
+    @twt;
+}
diff --git a/du/defs.h b/du/defs.h
new file mode 100644 (file)
index 0000000..2da35e7
--- /dev/null
+++ b/du/defs.h
@@ -0,0 +1,25 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef int DEVICE_TYPE;
+
+#ifdef __osf__
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <io/common/iotypes.h>
+#else /* must be ultrix */
+#include <sys/devio.h>
+#endif
+#include <io/cam/cam.h>
+#include <io/cam/uagt.h> 
+#include <io/cam/dec_cam.h>
+#include <io/cam/scsi_all.h>  
+#define DEV_CAM                "/dev/cam"      /* CAM device path */
+
+
+#define DIGITAL_UNIX
diff --git a/du/scsi.c b/du/scsi.c
new file mode 100644 (file)
index 0000000..7fc1745
--- /dev/null
+++ b/du/scsi.c
@@ -0,0 +1,206 @@
+/* SCSI.C - Digital Unix-specific SCSI routines.
+**
+** TECSys Development, Inc., April 1998
+**
+** This module began life as a part of XMCD, an X-windows CD player
+** program for many platforms. No real functionality from the original XMCD
+** is present in this module, but in the interest of making certain that
+** proper credit is given where it may be due, the copyrights and inclusions
+** from the XMCD module OS_DEC.C are included below.
+**
+** The portions of coding in this module ascribable to TECSys Development
+** are hereby also released under the terms and conditions of version 2
+** of the GNU General Public License as described below....
+*/
+
+/*
+ *   libdi - scsipt SCSI Device Interface Library
+ *
+ *   Copyright (C) 1993-1997  Ti Kan
+ *   E-mail: ti@amb.org
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Library General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2 of the License, or (at your option) any later version.
+ *
+ *   This library 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
+ *   Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free
+ *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *   Digital UNIX (OSF/1) and Ultrix support
+ *
+ *   Contributing author: Matt Thomas
+ *   E-Mail: thomas@lkg.dec.com
+ *
+ *   This software fragment contains code that interfaces the
+ *   application to the Digital UNIX and Ultrix operating systems.
+ *   The term Digital, Ultrix and OSF/1 are used here for identification
+ *   purposes only.
+ */
+
+static int             bus = -1,
+                       target = -1,
+                       lun = -1;
+
+static int SCSI_OpenDevice(char *DeviceName)
+{
+       int             fd;
+       struct stat     stbuf;
+       int             saverr;
+
+       /* Check for validity of device node */
+       if (stat(DeviceName, &stbuf) < 0) {
+               FatalError("cannot stat SCSI device '%s' - %m\n", DeviceName);
+       }
+       if (!S_ISCHR(stbuf.st_mode)) {
+               FatalError("device '%s': not appropriate device type - %m\n", DeviceName);
+       }
+
+       if ((fd = open(DeviceName, O_RDONLY | O_NDELAY, 0)) >= 0) {
+               struct devget   devget;
+
+               if (ioctl(fd, DEVIOCGET, &devget) >= 0) {
+#ifdef __osf__
+                       lun = devget.slave_num % 8;
+                       devget.slave_num /= 8;
+#else
+                       lun = 0;
+#endif
+                       target = devget.slave_num % 8;
+                       devget.slave_num /= 8;
+                       bus = devget.slave_num % 8;
+                       (void) close(fd);
+
+                       if ((fd = open(DEV_CAM, O_RDWR, 0)) >= 0 ||
+                           (fd = open(DEV_CAM, O_RDONLY, 0)) >= 0) {
+                               return (fd);
+                       }
+                       fd = bus = target = lun = -1;
+                       FatalError("error %d opening SCSI device '%s' - %m\n",
+                                        errno, DEV_CAM);
+               }
+               else {
+                       (void) close(fd);
+                       fd = bus = target = lun = -1;
+                       FatalError("error %d on DEVIOCGET ioctl for '%s' - %m\n",
+                                        errno, DeviceName);
+               }
+       }
+       else {
+               saverr = errno;
+               fd = bus = target = lun = -1;
+               FatalError("cannot open SCSI device '%s', error %d - %m\n",
+                               DeviceName, saverr);
+       }
+
+       fd = bus = target = lun = -1;
+       return -1;
+}
+
+
+static void SCSI_CloseDevice(char *DeviceName,
+                            int DeviceFD)
+{
+       (void) close(DeviceFD);
+       bus = target = lun = -1;
+}
+
+
+static int SCSI_ExecuteCommand(int DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+
+
+{
+       UAGT_CAM_CCB    uagt;
+       CCB_SCSIIO      ccb;
+
+       if (DeviceFD < 0)
+               return -1;
+
+       (void) memset(&uagt, 0, sizeof(uagt));
+       (void) memset(&ccb, 0, sizeof(ccb));
+
+       /* Setup the user agent ccb */
+       uagt.uagt_ccb = (CCB_HEADER *) &ccb;
+       uagt.uagt_ccblen = sizeof(CCB_SCSIIO);
+
+       /* Setup the scsi ccb */
+       (void) memcpy((unsigned char  *) ccb.cam_cdb_io.cam_cdb_bytes,
+                               CDB, CDB_Length);
+       ccb.cam_cdb_len = CDB_Length;
+       ccb.cam_ch.my_addr = (CCB_HEADER *) &ccb;
+       ccb.cam_ch.cam_ccb_len = sizeof(CCB_SCSIIO);
+       ccb.cam_ch.cam_func_code = XPT_SCSI_IO;
+
+       if (DataBuffer != NULL && DataBufferLength > 0) {
+               ccb.cam_ch.cam_flags |=
+                       (Direction == Input) ? CAM_DIR_IN : CAM_DIR_OUT;
+               uagt.uagt_buffer = (u_char *) DataBuffer;
+               uagt.uagt_buflen = DataBufferLength;
+       }
+       else
+               ccb.cam_ch.cam_flags |= CAM_DIR_NONE;
+       
+       ccb.cam_ch.cam_flags |= CAM_DIS_AUTOSENSE;
+       ccb.cam_data_ptr = uagt.uagt_buffer;
+       ccb.cam_dxfer_len = uagt.uagt_buflen;
+       ccb.cam_timeout = 300; /* Timeout set to 5 minutes */
+
+       ccb.cam_sense_ptr = (u_char *) RequestSense;
+       ccb.cam_sense_len = sizeof(RequestSense_T);
+
+       ccb.cam_ch.cam_path_id = bus;
+       ccb.cam_ch.cam_target_id = target;
+       ccb.cam_ch.cam_target_lun = lun;
+    
+       if (ioctl(DeviceFD, UAGT_CAM_IO, (caddr_t) &uagt) < 0) {
+               return -1;
+       }
+
+       /* Check return status */
+       if ((ccb.cam_ch.cam_status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+               if (ccb.cam_ch.cam_status & CAM_SIM_QFRZN) {
+                       (void) memset(&ccb, 0, sizeof(ccb));
+                       (void) memset(&uagt, 0, sizeof(uagt));
+
+                       /* Setup the user agent ccb */
+                       uagt.uagt_ccb = (CCB_HEADER  *) &ccb;
+                       uagt.uagt_ccblen = sizeof(CCB_RELSIM);
+
+                       /* Setup the scsi ccb */
+                       ccb.cam_ch.my_addr = (struct ccb_header *) &ccb;
+                       ccb.cam_ch.cam_ccb_len = sizeof(CCB_RELSIM);
+                       ccb.cam_ch.cam_func_code = XPT_REL_SIMQ;
+
+                       ccb.cam_ch.cam_path_id = bus;
+                       ccb.cam_ch.cam_target_id = target;
+                       ccb.cam_ch.cam_target_lun = lun;
+
+                       if (ioctl(DeviceFD, UAGT_CAM_IO, (caddr_t) &uagt) < 0)
+                               return -1;
+               }
+
+               printf("mtx: %s:\n%s=0x%x %s=0x%x\n",
+                                      "SCSI command fault",
+                                      "Opcode",
+                                      CDB[0],
+                                      "Status",
+                                      ccb.cam_scsi_status);
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..6d7f9dc
--- /dev/null
@@ -0,0 +1,253 @@
+#!/bin/sh
+# $Date: 2001/06/05 17:10:21 $
+# $Revision: 1.1.1.1 $
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/loaderinfo.1 b/loaderinfo.1
new file mode 100644 (file)
index 0000000..b25d72a
--- /dev/null
@@ -0,0 +1,90 @@
+.\" tapeinfo.1  Document copyright 2000 Eric Lee Green
+.\"  Program Copyright 2000 Eric Lee Green <eric@badtux.org>
+.\"
+.\" This is free documentation; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License as
+.\" published by the Free Software Foundation; either version 2 of
+.\" the License, or (at your option) any later version.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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 more details.
+.\"
+.\" You should have received a copy of the GNU General Public
+.\" License along with this manual; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+.\" USA.
+.\"
+.TH LOADERINFO 1 LOADERINFO1.0
+.SH NAME
+loaderinfo \- report SCSI tape device info
+.SH SYNOPSIS
+loaderinfo -f <scsi-generic-device>
+.SH DESCRIPTION
+The 
+.B loaderinfo
+command reads various information from SCSI tape loaders. Its intended
+use is for high-level programs that are trying to decide what the
+capabilities of a device are. 
+.P
+The following are printed:
+.TP 10
+.B  Element Address Assignment Page:
+This tells how many elements are in the loader, and what their raw
+hardware addresses are. 
+
+.TP 10
+.B Transport Geometry Descriptor Page:
+Will display whether media is invertible or not (usable with some
+optical jukeboxes for detirmining whether to "flip" media after writing
+to the first side). 
+
+.TP 10 
+.B Device Capabilities Page
+Currently will only display whether we can transfer between slots (i.e.
+whether 'mtx transfer' works). 
+
+.TP 10
+.B Inquiry Page
+Aside from the normal inquiry info, will also print out whether we have
+a bar code reader (for loaders that support the Exabyte extension for
+reporting presense of said reader). 
+
+
+.SH OPTIONS
+The first argument, given following
+.B -f
+, is the SCSI generic device corresponding to your tape loader. 
+Consult your operating system's documentation for more information (for
+example, under Linux these are generally start at /dev/sg0 
+under FreeBSD these start at /dev/pass0).
+.P
+Under FreeBSD, 'camcontrol devlist' will tell you what SCSI devices you
+have, along with which 'pass' device controls them. Under Linux,
+"cat /proc/scsi/scsi" will tell you what SCSI devices you have. Under
+Solaris 8, 
+.B find /devices -name '*changer*'
+will display the device names for your attached changers. Make sure
+to configure your 'sgen' driver first. 
+
+.SH BUGS AND LIMITATIONS
+.P
+This program has only been tested on Linux with a limited number of
+loaders (Ecrix Autopack, Exabyte 220). 
+.P
+.SH AVAILABILITY
+.B loaderinfo
+is currently being maintained by Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from 
+http://sourceforge.net/projects/mtx/ . 
+
+.SH SEE ALSO
+.BR mt (1), tapeinfo (1), mtx (1)
diff --git a/loaderinfo.c b/loaderinfo.c
new file mode 100644 (file)
index 0000000..1c7fcfe
--- /dev/null
@@ -0,0 +1,335 @@
+/* Copyright 2000 Enhanced Software Technologies Inc.
+ *   Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ */
+
+/* 
+* $Date: 2001/06/05 17:10:21 $
+* $Revision: 1.1.1.1 $
+*/
+
+/* What this does: Basically dumps out contents of:
+ *  Mode Sense: Element Address Assignment Page (0x1d)
+ *           1Eh (Transport Geometry Parameters) has a bit which indicates is 
+ *               a robot is  capable of rotating the media. It`s the 
+ *                `Rotate` bit, byte 2, bit 1.
+ *          Device Capabilities page (0x1f)
+ * Inquiry -- prints full inquiry info. 
+ *   DeviceType:
+ *    Manufacturer:
+ *    ProdID:  
+ *    ProdRevision:
+ *   If there is a byte 55, we use the Exabyte extension to 
+ * print out whether we have a bar code reader or not.  This is
+ * bit 0 of byte 55. 
+ *
+ * Next, we request element status on the drives. We do not
+ * request volume tags though. If Exabyte
+ * extensions are supported, we report the following information for
+ * each drive:
+ *
+ *  Drive number
+ *  EXCEPT (with ASC and ASCQ), if there is a problem. 
+ *  SCSI address and LUN
+ *  Tape drive Serial number
+ *   
+ */
+
+#include <stdio.h>
+#include "mtx.h"
+#include "mtxl.h"
+
+DEVICE_TYPE MediumChangerFD;  /* historic purposes... */
+
+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 */
+};
+
+
+/* okay, now for the structure of an Element Address Assignment Page:  */
+
+typedef struct EAAP {
+  unsigned char Page_Code;
+  unsigned char Parameter_Length;
+  unsigned char MediumTransportElementAddress[2];
+  unsigned char NumMediumTransportElements[2];
+  unsigned char FirstStorageElementAdddress[2];
+  unsigned char NumStorageElements[2];
+  unsigned char FirstImportExportElementAddress[2];
+  unsigned char NumImportExportElements[2];
+  unsigned char FirstDataTransferElementAddress[2];
+  unsigned char NumDataTransferElements[2];
+  unsigned char Reserved[2];
+} EAAP_Type;
+
+/* okay, now for the structure of a transport geometry
+ * descriptor page:
+ */
+typedef struct TGDP {
+  unsigned char Page_Code;
+  unsigned char ParameterLength;
+  unsigned char Rotate;
+  unsigned char ElementNumber;  /* we don't care about this... */
+} TGDP_Type;
+
+
+/* Structure of the Device Capabilities Page: */
+typedef struct DCP {
+  unsigned char Page_Code;
+  unsigned char ParameterLength;
+  unsigned char CanStore;  /* bits about whether elements can store carts */
+  unsigned char Reserved; 
+  unsigned char MT_Xfer;  /* bits about whether mt->xx xfers work. */
+  unsigned char ST_Xfer;  /* bits about whether st->xx xfers work. */
+  unsigned char IE_Xfer;  /* bits about whether id->xx xfers work. */
+  unsigned char DT_Xfer; /* bits about whether DT->xx xfers work. */
+  unsigned char Reserved2[12];  /* more reserved data */
+} DCP_Type ; 
+
+#define MT_BIT 0x01
+#define ST_BIT 0x02
+#define IE_BIT 0x04
+#define DT_BIT 0x08
+
+/* Okay, now for the inquiry information: */
+
+static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
+{
+  RequestSense_T RequestSense;
+  Inquiry_T *Inquiry;
+  int i;
+
+  Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
+  if (Inquiry == NULL) 
+    {
+      PrintRequestSense(&RequestSense);
+      FatalError("INQUIRY Command Failed\n");
+    }
+  
+  printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
+  printf("Vendor ID: '");
+  for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
+    printf("%c", Inquiry->VendorIdentification[i]);
+  printf("'\nProduct ID: '");
+  for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
+    printf("%c", Inquiry->ProductIdentification[i]);
+  printf("'\nRevision: '");
+  for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
+    printf("%c", Inquiry->ProductRevisionLevel[i]);
+  printf("'\n");\
+  if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
+    printf("Attached Changer: Yes\n");
+  } else {
+    printf("Attached Changer: No\n");
+  }
+  /* Now see if we have a bar code flag: */
+  if (Inquiry->AdditionalLength > 50) {  /* see if we have 56 bytes: */
+    if (Inquiry->VendorFlags & 1) {
+      printf("Bar Code Reader: Yes\n");
+    } else {
+      printf("Bar Code Reader: No\n");
+    }
+  }
+  
+  free(Inquiry);  /* well, we're about to exit, but ... */
+  return; /* done! */
+}
+
+/*********** MODE SENSE *******************/
+/* We need 3 different mode sense pages. This is a generic
+ * routine for obtaining mode sense pages. 
+ */
+
+static unsigned char
+*mode_sense(DEVICE_TYPE fd, int pagenum, int alloc_len,  RequestSense_T *RequestSense) {
+  CDB_T CDB;
+  unsigned char *input_buffer; /*the input buffer -- has junk prepended to
+                               * actual sense page. 
+                               */
+  unsigned char *tmp;
+  unsigned char *retval;  /* the return value. */
+  int i,pagelen;
+
+  if (alloc_len > 255) {
+    FatalError("mode_sense(6) can only read up to 255 characters!\n");
+  }
+
+  input_buffer=(unsigned char *)xzmalloc(256); /* overdo it, eh? */
+
+  /* clear the sense buffer: */
+  slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+  
+
+  /* returns an array of bytes in the page, or, if not possible, NULL. */
+  CDB[0]=0x1a; /* Mode Sense(6) */
+  CDB[1]=0; 
+  CDB[2]=pagenum; /* the page to read. */
+  CDB[3]=0;
+  CDB[4]=255; /* allocation length. This does max of 256 bytes! */
+  CDB[5]=0;
+  
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,
+                         input_buffer,255,RequestSense) != 0) {
+#ifdef DEBUG_MODE_SENSE
+    fprintf(stderr,"Could not execute mode sense...\n");
+    fflush(stderr);
+#endif
+    return NULL; /* sorry, couldn't do it. */
+  }
+  
+  /* First skip past any header.... */
+  tmp=input_buffer+4+input_buffer[3];
+  /* now find out real length of page... */
+  pagelen=tmp[1]+2;
+  retval=xmalloc(pagelen);
+  /* and copy our data to the new page. */
+  for (i=0;i<pagelen;i++) {
+    retval[i]=tmp[i];
+  }
+  /* okay, free our input buffer: */
+  free(input_buffer);
+  return retval;
+}
+
+/* Report the Element Address Assignment Page */
+static EAAP_Type *ReportEAAP(DEVICE_TYPE MediumChangerFD) {
+  EAAP_Type *EAAP; 
+  RequestSense_T RequestSense;
+
+  EAAP=(EAAP_Type *)mode_sense(MediumChangerFD,0x1d,sizeof(EAAP_Type),&RequestSense);
+  
+  if (EAAP==NULL) {
+    printf("EAAP: No\n");
+    return NULL;
+  }
+
+  /* we did get an EAAP, so do our thing: */
+  printf("EAAP: Yes\n");
+  printf("Number of Medium Transport Elements: %d\n", ( ((unsigned int)EAAP->NumMediumTransportElements[0]<<8) + (unsigned int)EAAP->NumMediumTransportElements[1]));
+  printf("Number of Storage Elements: %d\n", ( ((unsigned int)EAAP->NumStorageElements[0]<<8) + (unsigned int)EAAP->NumStorageElements[1]));
+  printf("Number of Import/Export Element Elements: %d\n", ( ((unsigned int)EAAP->NumImportExportElements[0]<<8) + (unsigned int)EAAP->NumImportExportElements[1]));
+  printf("Number of Data Transfer Elements: %d\n", ( ((unsigned int)EAAP->NumDataTransferElements[0]<<8) + (unsigned int)EAAP->NumDataTransferElements[1]));
+  
+  return EAAP;
+}
+
+/* See if we can get some invert information: */
+
+static void Report_TGDP(DEVICE_TYPE MediumChangerFD) {
+  TGDP_Type *result;
+  
+  RequestSense_T RequestSense;
+
+  result=(TGDP_Type *)mode_sense(MediumChangerFD,0x1e,255,&RequestSense);
+  
+  if (!result) {
+    printf("Transport Geometry Descriptor Page: No\n");
+    return;
+  }
+
+  printf("Transport Geometry Descriptor Page: Yes\n");
+  
+  /* Now print out the invert bit: */
+  if ( result->Rotate & 1 ) {
+    printf("Invertable: Yes\n");
+  } else {
+    printf("Invertable: No\n");
+  }
+
+  return;  /* done. */
+}
+
+/* Okay, let's get the Device Capabilities Page. We don't care
+ * about much here, just whether 'mtx transfer' will work (i.e., 
+ * ST->ST).
+ */
+
+static void Report_DCP(DEVICE_TYPE MediumChangerFD) {
+  DCP_Type *result;
+  RequestSense_T RequestSense;
+
+  /* Get the page. */
+  result=(DCP_Type *)mode_sense(MediumChangerFD,0x1f,sizeof(DCP_Type),&RequestSense);
+  if (!result) {
+    printf("Device Configuration Page: No\n");
+    return;
+  }
+  
+  printf("Device Configuration Page: Yes\n");
+
+  /* okay, now see if we can do xfers: */
+  if (result->ST_Xfer & ST_BIT) {
+    printf("Can Transfer: Yes\n");
+  } else {
+    printf("Can Transfer: No\n");
+  }
+  /* We don't care about anything else at the moment, eventually we
+   * do want to add the inport/export stuff here too...
+   */
+
+  return;
+}
+
+void usage(void) {
+  FatalError("Usage: loaderinfo -f <generic-device>\n");
+}
+
+
+/* we only have one argument: "-f <device>". */
+int main(int argc, char **argv) {
+  DEVICE_TYPE fd;
+  char *filename;
+
+  argv0=argv[0];
+  if (argc != 3) {
+    fprintf(stderr,"argc=%d",argc);
+    usage();
+  }
+
+  if (strcmp(argv[1],"-f")!=0) {
+    usage();
+  }
+  filename=argv[2];
+  
+  fd=SCSI_OpenDevice(filename);
+  
+  /* Now to call the various routines: */
+  ReportInquiry(fd);
+  ReportEAAP(fd);
+  Report_TGDP(fd);
+  Report_DCP(fd);
+  exit(0);
+}
diff --git a/makedist b/makedist
new file mode 100755 (executable)
index 0000000..0af64fa
--- /dev/null
+++ b/makedist
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# note -- this assumes 'bash' shell, GNU tar, 'gzip'. 
+
+# pass a version number e.g. 1.4.3 as 1st parameter...
+ME=`pwd`
+
+# okay, now to create a spec file w/the proper version number:
+sed -e "1,\$s/@@VERSION@@/${1}/g" <mtx.spec.in >mtx.spec
+
+cd ..
+if [ ! -s mtx-${1} ]
+then
+   ln -s "${ME}" "mtx-${1}"
+fi
+
+
+tar --exclude CVS -czvhf mtx-${1}.tar.gz mtx-${1}
+
diff --git a/mam2debug.c b/mam2debug.c
new file mode 100644 (file)
index 0000000..4b1efc0
--- /dev/null
@@ -0,0 +1,123 @@
+/* Mammoth 2 Debug Buffer Dumper
+   Copyright 2000 Enhanced Software Technologies Inc.
+
+$Date: 2001/06/05 17:10:21 $
+$Revision: 1.1.1.1 $
+
+   Written by Eric Lee Green <eric@estinc.com>
+   Released under the terms of the GNU General Public License v2 or
+    above.
+
+   This is an example of how to use the mtx library file 'mtxl.c' to
+   do a special-purpose task -- dump the Mammoth2 debug buffer, in this case. 
+   Note that this debug buffer is 1M-4M in size, thus may overwhelm the
+   SCSI generic subsystem on some supported platforms...
+
+   syntax:
+
+   mam2debug generic-filename output-filename.
+
+
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "mtx.h"
+#include "mtxl.h"
+
+/* This is a TOTALLY UNDOCUMENTED feature to read the debug data buffer
+ * in an Exabyte Mammoth II and dump it to a file:
+ */
+
+static RequestSense_T *DumpM2DebugBuff(DEVICE_TYPE MediumChangerFD, int outfile) 
+{
+  RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+  CDB_T CDB;
+
+  unsigned char *databuffer;
+  unsigned char buff_descriptor[4];
+  int numbytes;
+  int testbytes;
+
+  CDB[0]=0x3c; /* command. */ 
+  CDB[1]=0x03;  /* mode - read buff_descriptor! */ 
+  CDB[2]=0x01;    /* page. */ 
+  CDB[3]=0;   /* offset. */
+  CDB[4]=0;
+  CDB[5]=0;  
+  CDB[6]=0;    /* length. */
+  CDB[7]=0;
+  CDB[8]=4;   /* the descriptor is 4 long. */ 
+  CDB[9]=0;
+  
+  if ((testbytes=SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
+                         buff_descriptor, 4, RequestSense)) != 0){
+    fprintf(stderr,"mam2debug: could not read buff_descriptor. [%d]\n",testbytes);
+    return RequestSense;  /* couldn't do it. */ 
+  }
+
+  /* okay, read numbytes: */
+  numbytes=(buff_descriptor[1]<<16) + (buff_descriptor[2]<<8) + buff_descriptor[3];
+  databuffer=(unsigned char *) xmalloc(numbytes+1000000); /* see if this helps :-(. */
+  CDB[6]=buff_descriptor[1];
+  CDB[7]=buff_descriptor[2];
+  CDB[8]=buff_descriptor[3];
+
+  CDB[1]=0x02; /* mode -- read buffer! */
+  
+  if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
+                         databuffer, numbytes, RequestSense) != 0){
+    fprintf(stderr,"mam2debug: could not read buffer.\n");
+    free(databuffer);
+    return RequestSense;  /* couldn't do it. */ 
+  }
+  
+  write(outfile,databuffer,numbytes);
+  close(outfile);
+  free(databuffer);
+  free(RequestSense);
+  return NULL;  /* okay! */
+}
+
+static void usage(void) {
+  fprintf(stderr,"Usage: mam2debug scsi-generic-file output-file-name\n");
+  exit(1);
+}
+
+/* Now for the actual main() routine: */
+
+int main(int argc,char** argv) {
+  DEVICE_TYPE changer_fd;
+  static RequestSense_T *result;
+  int outfile;
+
+  if (argc != 3) {
+    usage();
+  }
+  
+  changer_fd=SCSI_OpenDevice(argv[1]);
+  
+  if (changer_fd <= 0) {
+    fprintf(stderr,"Could not open input device\n");
+    usage();
+  }
+
+  outfile=open(argv[2],O_CREAT|O_WRONLY);
+  if (outfile <=0) {
+    fprintf(stderr,"Could not open output file\n");
+    usage();
+  }
+
+  result=DumpM2DebugBuff(changer_fd, outfile);
+
+  if (result) {
+    PrintRequestSense(result);
+    exit(1);
+  }
+
+  exit(0);
+}
+
diff --git a/mam2debug2.c b/mam2debug2.c
new file mode 100644 (file)
index 0000000..9c30e4e
--- /dev/null
@@ -0,0 +1,123 @@
+/* Mammoth 2 Debug Buffer Dumper
+   Copyright 2000 Enhanced Software Technologies Inc.
+
+$Date: 2001/06/05 17:10:21 $
+$Revision: 1.1.1.1 $
+
+   Written by Eric Lee Green <eric@estinc.com>
+   Released under the terms of the GNU General Public License v2 or
+    above.
+
+   This is an example of how to use the mtx library file 'mtxl.c' to
+   do a special-purpose task -- dump the Mammoth2 debug buffer, in this case. 
+   Note that this debug buffer is 1M-4M in size, thus may overwhelm the
+   SCSI generic subsystem on some supported platforms...
+
+   syntax:
+
+   mam2debug generic-filename output-filename.
+
+
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "mtx.h"
+#include "mtxl.h"
+
+/* This is a TOTALLY UNDOCUMENTED feature to read the debug data buffer
+ * in an Exabyte Mammoth II and dump it to a file:
+ */
+
+static RequestSense_T *DumpM2DebugBuff(DEVICE_TYPE MediumChangerFD, int outfile) 
+{
+  RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+  CDB_T CDB;
+
+  unsigned char *databuffer;
+  unsigned char buff_descriptor[4];
+  int numbytes;
+  int testbytes;
+
+  CDB[0]=0x3c; /* command. */ 
+  CDB[1]=0x03;  /* mode - read buff_descriptor! */ 
+  CDB[2]=0x00;    /* page -- data. */ 
+  CDB[3]=0;   /* offset. */
+  CDB[4]=0;
+  CDB[5]=0;  
+  CDB[6]=0;    /* length. */
+  CDB[7]=0;
+  CDB[8]=4;   /* the descriptor is 4 long. */ 
+  CDB[9]=0;
+  
+  if ((testbytes=SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
+                         buff_descriptor, 4, RequestSense)) != 0){
+    fprintf(stderr,"mam2debug: could not read buff_descriptor. [%d]\n",testbytes);
+    return RequestSense;  /* couldn't do it. */ 
+  }
+
+  /* okay, read numbytes: */
+  numbytes=(buff_descriptor[1]<<16) + (buff_descriptor[2]<<8) + buff_descriptor[3];
+  databuffer=(unsigned char *) xmalloc(numbytes+1000000); /* see if this helps :-(. */
+  CDB[6]=buff_descriptor[1];
+  CDB[7]=buff_descriptor[2];
+  CDB[8]=buff_descriptor[3];
+
+  CDB[1]=0x02; /* mode -- read buffer! */
+  
+  if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
+                         databuffer, numbytes, RequestSense) != 0){
+    fprintf(stderr,"mam2debug: could not read buffer.\n");
+    free(databuffer);
+    return RequestSense;  /* couldn't do it. */ 
+  }
+  
+  write(outfile,databuffer,numbytes);
+  close(outfile);
+  free(databuffer);
+  free(RequestSense);
+  return NULL;  /* okay! */
+}
+
+static void usage(void) {
+  fprintf(stderr,"Usage: mam2debug scsi-generic-file output-file-name\n");
+  exit(1);
+}
+
+/* Now for the actual main() routine: */
+
+int main(int argc,char** argv) {
+  DEVICE_TYPE changer_fd;
+  static RequestSense_T *result;
+  int outfile;
+
+  if (argc != 3) {
+    usage();
+  }
+  
+  changer_fd=SCSI_OpenDevice(argv[1]);
+  
+  if (changer_fd <= 0) {
+    fprintf(stderr,"Could not open input device\n");
+    usage();
+  }
+
+  outfile=open(argv[2],O_CREAT|O_WRONLY);
+  if (outfile <=0) {
+    fprintf(stderr,"Could not open output file\n");
+    usage();
+  }
+
+  result=DumpM2DebugBuff(changer_fd, outfile);
+
+  if (result) {
+    PrintRequestSense(result);
+    exit(1);
+  }
+
+  exit(0);
+}
+
diff --git a/mtx.1 b/mtx.1
new file mode 100644 (file)
index 0000000..9362053
--- /dev/null
+++ b/mtx.1
@@ -0,0 +1,246 @@
+.\" mtx.1  Document copyright 2000 Eric Lee Green
+.\"  Program Copyright 1996, 1997 Leonard Zubkoff
+.\"  Extensive changes 2000 by Eric Lee Green <eric@estinc.com>
+.\"
+.\" This is free documentation; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License as
+.\" published by the Free Software Foundation; either version 2 of
+.\" the License, or (at your option) any later version.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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 more details.
+.\"
+.\" You should have received a copy of the GNU General Public
+.\" License along with this manual; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+.\" USA.
+.\"
+.TH MTX 1 MTX1.2
+.SH NAME
+mtx \- control SCSI media changer devices 
+.SH SYNOPSIS
+mtx [-f <scsi-generic-device>] [nobarcode] [invert] [noattach] command [ command ... ]
+.SH DESCRIPTION
+The 
+.B mtx
+command controls single or multi-drive SCSI media changers such as
+tape changers, autoloaders, tape libraries, or optical media jukeboxes.
+It can also be used with media changers that use the 'ATTACHED' API, 
+presuming that they properly report the MChanger bit as required
+by the SCSI T-10 SMC specification. 
+.SH OPTIONS
+The first argument, given following
+.B -f
+, is the SCSI generic device corresponding to your media changer. 
+Consult your operating system's documentation for more information (for
+example, under Linux these are generally /dev/sg0 through /dev/sg15, 
+under FreeBSD these are /dev/pass0 through /dev/passX,
+under SunOS it may be a file under /dev/rdsk).  
+.P
+The 'invert' option will invert (flip) the media (for optical jukeboxes that
+allow such) before inserting it into the drive or returning it to the
+storage slot. 
+.P
+The 'noattach' option forces the regular media changer API even if the
+media changer incorrectly reported that it uses the 'ATTACHED' API. 
+.P
+The 'nobarcode' option forces the loader to not request barcodes even if
+the loader is capable of reporting them.  
+.P
+Following these options there may follow
+one or more robotics control
+commands. Note that the 'invert' and 'noattach'
+options apply to ALL of robotics control
+commands.
+
+.SH COMMANDS
+.TP 10
+.B --version
+Report the mtx version number (e.g. mtx 1.2.8) and exit. 
+
+.TP 10
+.B inquiry
+Report the product type (Medium Changer, Tape Drive, etc.), Vendor ID,
+Product ID, Revision, and whether this uses the Attached Changer API
+(some tape drives use this rather than reporting a Medium Changer on a
+separate LUN or SCSI address). 
+.TP 10
+.B noattach
+Make further commands use the regular media changer API rather than the 
+_ATTACHED API, no matter what the "Attached" bit said in the Inquiry info.
+Needed with some brain-dead changers that report Attached bit but don't respond
+to _ATTACHED API. 
+.TP 10
+.B inventory
+Makes the robot arm go and check what elements are in the slots. This
+is needed for a few libraries like the Breece Hill ones that do not 
+automatically check the tape inventory at system startup. 
+.TP 10
+.B status
+Reports how many drives and storage elements are contained in the
+device. For each drive, reports whether it has media loaded in it, and
+if so, from which storage slot the media originated. For each storage
+slot, reports whether it is empty or full, and if the media changer
+has a bar code, MIC reader, or some other way of uniquely identifying
+media without loading it into a drive, this reports the volume tag
+and/or alternate volume tag for each piece of media.
+For historical reasons drives are numbered from 0 and storage slots are
+numbered from 1. 
+.TP 10
+.B load <slotnum> [ <drivenum> ]
+Load media from slot <slotnum> into drive <drivenum>. Drive 0 is assumed
+if the drive number is omitted.
+.TP 10
+.B unload [<slotnum>] [ <drivenum> ]
+Unloads media from drive <drivenum> into slot <slotnum>. If <drivenum> is
+omitted, defaults to drive 0 (as do all commands).
+If <slotnum> is omitted, defaults to the slot
+that the drive was loaded from. Note that there's currently no way to
+say 'unload drive 1's media to the slot it came from', other than to 
+explicitly use that slot number as the destination.
+.TP 10
+.B [eepos <operation>] transfer <slotnum> <slotnum>
+Transfers media from one slot to another, assuming that your mechanism is
+capable of doing so. Usually used to move media to/from an import/export
+port. 'eepos' is used to extend/retract the import/export 
+tray on certain mid-range to high end tape libraries (if, e.g., the tray was
+slot 32, you might say say 'eepos 1 transfer 32 32' to extend the tray). 
+Valid values for eepos <operation>
+are 0 (do nothing to the import/export tray), 1, and 2 (what 1 and 2 do varies
+depending upon the library, consult your library's SCSI-level 
+documentation). 
+.TP 10
+.B first  [<drivenum>]
+Loads drive <drivenum> from the first slot in the media changer. Unloads
+the drive if there is already media in it.  Note that
+this command may not be what you want on large tape libraries -- e.g. on Exabyte 220, the first slot is
+usually a cleaning tape. If <drivenum> is omitted, defaults to first drive.
+
+.TP 10
+.B last [<drivenum>]
+Loads drive <drivenum> from the last slot in the media changer. Unloads
+the drive if there is already a tape in it. 
+.TP 10
+.B next [<drivenum>]
+Unloads the drive and loads the next tape in sequence. If the drive was
+empty, loads the first tape into the drive.
+
+.SH AUTHORS
+The original 'mtx' program was written by Leonard Zubkoff and extensively
+revised for large multi-drive libraries with bar code readers 
+by Eric Lee Green <eric@badtux.org>, to whom all problems should
+be reported for this revision. See 'mtx.c' for other contributors. 
+.SH BUGS AND LIMITATIONS
+.P
+You may need to do a 'mt offline' on the tape drive to eject the tape
+before you can issue the 'mtx unload' command. The Exabyte EZ-17 and 220
+in particular will happily sit there snapping the robot arm's claws around
+thin air trying to grab a tape that's not there. 
+.P
+For some Linux distributions, you may need to re-compile the kernel to
+scan SCSI LUN's in order to detect the media changer. Check /proc/scsi/scsi
+to see what's going on. 
+.P
+If you try to unload a tape to its 'source' slot, and said slot is
+full, it will instead put the tape into the first empty
+slot. Unfortunately the list of empty slots is not updated between
+commands on the command line, so if you try to unload another drive to
+a full 'source' slot during the same invocation of 'mtx', it will try
+to unload to the same (no longer empty) slot and will urp with a SCSI
+error.
+.P
+
+This program reads the Mode Sense Element Address Assignment Page
+(SCSI) and requests data on all available elements. For larger
+libraries (more than a couple dozen elements)
+this sets a big Allocation_Size in the SCSI command block for the
+REQUEST_ELEMENT_STATUS command in order to be able to read the entire
+result of a big tape library. Some operating systems may not be able
+to handle this. Versions of Linux earlier than 2.2.6, in particular,
+may fail this request due to inability to find contiguous pages of
+memory for the SCSI transfer (later versions of Linux 'sg' device do
+scatter-gather so that this should no longer be a problem).
+.P
+The 
+.B eepos
+command remains in effect for all further commands on a command
+line. Thus you might want to follow 
+.B eepos 1 transfer 32 32
+with 
+.B eepos 0
+as
+the next command (which clears the 
+.B eepos
+bits). 
+.P
+Need a better name for 'eepos' command! ('eepos' is the name of the bit
+field in the actual low-level SCSI command, and has nothing to do with what
+it does). 
+.P
+
+This program has only been tested on Linux with a limited number of
+tape loaders (a dual-drive Exabyte 220 tape library, with bar-code
+reader and 21 slots, an Exabyte EZ-17 7-slot autoloader, and a Seagate
+DDS-4 autochanger with 6 slots). It may not work on other operating systems 
+with larger libraries,
+due to the big SCSI request size. 
+Report problems to Eric Lee Green <eric@badtux.org>.  
+.SH HINTS
+Under Linux, 
+.B cat /proc/scsi/scsi
+will tell you what SCSI devices you have.
+You can then refer to them as 
+.B /dev/sga,
+.B /dev/sgb, 
+etc. by the order they
+are reported.
+.P
+Under FreeBSD, 
+.B camcontrol devlist
+will tell you what SCSI devices you
+have, along with which 
+.B pass
+device controls them.
+.P
+Under Solaris, set up your 'sgen' driver so that it'll look for
+tape changers (see /kernel/drv/sgen.conf and the sgen man page), type
+.B touch /reconfigure
+then reboot. You can find your changer in /devices by typing
+.B /usr/sbin/devfsadm -C
+to clean out no-longer-extant entries in your /devices directory, then
+.B find /devices -name \*changer -print
+to find the device name. Set the symbolic link 
+.B /dev/changer 
+to point
+to that device name (if it is not doing so already).
+.P
+With BRU, set your mount and unmount commands as described on the EST
+web site at http://www.estinc.com to move to the next tape when backing up
+or restoring. With GNU 
+.B tar,
+see 
+.B mtx.doc
+for an example of how to use
+.B tar
+and 
+.B mtx
+to make multi-tape backups. 
+
+.SH AVAILABILITY
+This version of 
+.B mtx
+is currently being maintained by Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from 
+http://sourceforge.net/projects/mtx/ . 
+
+.SH SEE ALSO
+.BR mt (1), tapeinfo (1), scsitape(1), loaderinfo(1)
diff --git a/mtx.c b/mtx.c
new file mode 100644 (file)
index 0000000..7390566
--- /dev/null
+++ b/mtx.c
@@ -0,0 +1,778 @@
+/*
+
+  MTX -- SCSI Tape Attached Medium Changer Control Program
+  $Date: 2001/11/06 21:20:40 $
+  $Revision: 1.2.2.1 $
+
+  Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+  Now maintained by Eric Lee Green <eric@estinc.com>
+
+  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.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+  Thanks to Philip A. Prindeville <philipp@enteka.com> of Enteka Enterprise
+  Technology Service for porting MTX to Solaris/SPARC.
+
+  Thanks to Carsten Koch <Carsten.Koch@icem.de> for porting MTX to SGI IRIX.
+
+  Thanks to TECSys Development, Inc. for porting MTX to Digital Unix and
+  OpenVMS.
+
+  Changes Feb 2000 Eric Lee Green <eric@estinc.com> to add support for
+  multi-drive tape changers, extract out library stuff into mtxl.c,
+  and otherwise bring things up to date for dealing with LARGE tape jukeboxes
+  and other such enterprise-class storage subsystems. 
+
+*/
+
+char *argv0;
+
+#include "mtx.h"   /* various defines for bit order etc. */
+#include "mtxl.h"
+
+/* A table for printing out the peripheral device type as ASCII. */ 
+static char *PeripheralDeviceType[32] = {
+  "Disk Drive",  /* 0 */
+  "Tape Drive",   /* 1 */
+  "Printer",   /* 2 */
+  "Processor",   /* 3 */
+  "Write-once",   /* 4 */
+  "CD-ROM",       /* 5 */
+  "Scanner",      /* 6 */
+  "Optical",      /* 7 */ 
+  "Medium Changer",  /* 8 */
+  "Communications",  /* 9 */
+  "ASC IT8",           /* a */ 
+  "ASC IT8",            /* b */
+  "RAID Array",         /* c */
+  "Enclosure Services",  /* d */
+  "RBC Simplified Disk", /* e */
+  "OCR/W",           /* f */
+  "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 */
+};
+  
+static int argc;
+static char **argv;
+
+
+
+
+static char *device=NULL; /* the device name passed as argument */
+
+/* Unfortunately this must be true for SGI, because SGI does not
+   use an int :-(. 
+*/
+
+static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
+static int device_opened = 0;  /* okay, replace check here. */
+
+/* was: static int MediumChangerFD=-1; *//* open filehandle to that device */
+static int arg1=-1;       /* first arg to command */
+static int arg2=-1;       /* second arg to command */
+
+static SCSI_Flags_T SCSI_Flags = { 0, 0, 0,0 };
+  
+/* static int invert_bit=0;*/  /* we by default do not invert... */
+/* static int eepos=0;     */  /* the extend thingy for import/export. */
+static Inquiry_T *inquiry_info;  /* needed by MoveMedium etc... */
+static ElementStatus_T *ElementStatus = NULL;
+
+
+/* pre-defined commands: */
+static void ReportInquiry(void);
+static void Status(void);
+static void Load(void);
+static void Unload(void);
+static void First(void);
+static void Last(void);
+static void Next(void);
+/* static void Previous(void); */
+static void InvertCommand(void);
+static void Transfer(void);
+static void Eepos(void);
+static void NoAttach(void);
+static void Version(void);
+static void do_Inventory(void); 
+static void do_Unload(void);
+static void do_Erase(void);
+static void NoBarCode(void);
+
+struct command_table_struct {
+  int num_args;
+  char *name;
+  void (*command)(void);
+  int need_device;
+  int need_status;
+} command_table[] = {
+  { 0, "inquiry",ReportInquiry, 1,0},
+  { 0, "status", Status, 1,1 },
+  { 0, "invert", InvertCommand, 0,0},
+  { 0, "noattach",NoAttach,0,0},
+  { 1, "eepos", Eepos, 0,0},
+  { 2, "load", Load, 1,1 },
+  { 2, "unload", Unload, 1,1 },
+  { 2, "transfer", Transfer, 1,1 },
+  { 1, "first", First, 1,1 },
+  { 1, "last", Last, 1,1 },
+  { 1, "next", Next, 1,1 },
+  { 0, "--version", Version, 0,0 },
+  { 0, "inventory", do_Inventory, 1,0},
+  { 0, "eject", do_Unload, 1, 0},
+  { 0, "erase", do_Erase, 1, 0},
+  { 0, "nobarcode", NoBarCode, 0,0},
+  { 0, NULL, NULL }
+};
+
+static void Usage()
+{
+  fprintf(stderr, "Usage:\n\
+  mtx --version\n\
+  mtx [ -f <loader-dev> ] noattach <more commands>\n\
+  mtx [ -f <loader-dev> ] inquiry | inventory \n\
+  mtx [ -f <loader-dev> ] [nobarcode] status\n\
+  mtx [ -f <loader-dev> ] first [<drive#>]\n\
+  mtx [ -f <loader-dev> ] last [<drive#>]\n\
+  mtx [ -f <loader-dev> ] next [<drive#>]\n\
+  mtx [ -f <loader-dev> ] previous [<drive#>]\n\
+  mtx [ -f <loader-dev> ] [invert] load <storage-element-number> [<drive#>]\n\
+  mtx [ -f <loader-dev> ] [invert] unload [<storage-element-number>][<drive#>]\n\
+  mtx [ -f <loader-dev> ] [eepos eepos-number] transfer <storage-element-number> <storage-element-number>\n\
+  mtx [ -f <device> ] eject\n");
+
+#ifndef VMS
+  exit(1);
+#else
+  sys$exit(VMS_ExitCode);
+#endif
+}
+
+
+
+static void Version(void) {
+  fprintf(stderr,"mtx version %s\n\n",VERSION);
+  Usage(); 
+}
+
+static void NoAttach(void) {
+  SCSI_Flags.no_attached=1;
+}
+
+static void InvertCommand(void) {
+  SCSI_Flags.invert=1;
+  /* invert_bit=1;*/
+}
+
+static void NoBarCode(void) {
+  SCSI_Flags.no_barcodes=1;  /* don't request barcodes, sigh! */
+} 
+
+/* First and Last are easy. Next is the bitch. */
+static void First(void){
+  int driveno;
+  /* okay, first see if we have a drive#: */
+  if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+    driveno=arg1;
+  } else {
+    driveno = 0;
+  }
+  /* now see if there's anything *IN* that drive: */
+  if (ElementStatus->DataTransferElementFull[driveno]) {
+    /* if so, then unload it... */
+    arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+    if (arg1==1) {
+      printf("loading...done.\n");  /* it already has tape #1 in it! */ 
+      return;
+    }
+    arg2=driveno;
+    Unload();
+  }
+  /* and now to actually do the Load(): */
+  arg2=driveno;
+  arg1=1; /* first! */
+  Load(); /* and voila! */
+  
+}
+
+static void Last(void) {
+  int driveno;
+  /* okay, first see if we have a drive#: */
+  if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+    driveno=arg1;
+  } else {
+    driveno = 0;
+  }
+
+  /* now see if there's anything *IN* that drive: */
+  if (ElementStatus->DataTransferElementFull[driveno]) {
+    /* if so, then unload it... */
+    arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+    if (arg1==ElementStatus->StorageElementCount) {
+      printf("loading...done.\n");  /* it already has last tape in it! */ 
+      return;
+    }
+    arg2=driveno;
+    Unload();
+  }
+
+  arg1 = ElementStatus->StorageElementCount; /* the last slot... */
+  arg2=driveno;
+  Load(); /* and load it, sigh! */
+
+}
+
+
+static void Next(void) {
+  int driveno;
+  int current=0; 
+  /* okay, first see if we have a drive#: */
+  if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+    driveno=arg1;
+  } else {
+    driveno = 0;
+  }
+
+  /* Now to see if there's anything in that drive! */
+  if (ElementStatus->DataTransferElementFull[driveno]) {
+    /* if so, unload it! */
+    arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+    current=arg1;
+    arg2=driveno;
+    Unload();
+  }
+  
+  /* okay, now to load, if we can... */
+  current++;
+  if (current > ElementStatus->StorageElementCount) { /* the last slot... */
+    FatalError("No More Tapes\n");
+  }
+  arg1=current;
+  arg2=driveno;
+  Load();
+}
+
+static void do_Inventory(void) 
+{
+  int i;
+  i=Inventory(MediumChangerFD);
+  if (i < 0) {
+    fprintf(stderr,"mtx:inventory failed\n");
+    fflush(stderr);
+    exit(1); /*  exit with an error status. */
+  }
+  return;  /* if it failed, well, sigh.... */
+}
+
+/* For Linux, this allows us to do a short erase on a tape (sigh!).
+ * Note that you'll need to do a 'mt status' on the tape afterwards in
+ * order to get the tape driver in sync with the tape drive again. Also
+ * note that on other OS's, this might do other evil things to the tape
+ * driver. Note that to do an erase, you must first rewind using the OS's
+ * native tools!
+ */
+static void do_Erase(void) {
+  RequestSense_T *RequestSense;
+  RequestSense=Erase(MediumChangerFD);
+  if (RequestSense) {
+    PrintRequestSense(RequestSense);
+    exit(1);  /* exit with an error status. */
+  }
+  return;
+}
+  
+
+/* This should eject a tape or magazine, depending upon the device sent
+ * to.
+ */
+static void do_Unload(void)
+{
+  int i;
+  i=Eject(MediumChangerFD);
+  if (i<0) {
+    fprintf(stderr,"mtx:eject failed\n");
+    fflush(stderr);
+  }
+  return;  /* if it failed, well, sigh.... */
+}
+
+static void ReportInquiry(void)
+{
+  RequestSense_T RequestSense;
+  Inquiry_T *Inquiry;
+  int i;
+
+  Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
+  if (Inquiry == NULL) 
+    {
+      PrintRequestSense(&RequestSense);
+      FatalError("INQUIRY Command Failed\n");
+    }
+  
+  printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
+  printf("Vendor ID: '");
+  for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
+    printf("%c", Inquiry->VendorIdentification[i]);
+  printf("'\nProduct ID: '");
+  for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
+    printf("%c", Inquiry->ProductIdentification[i]);
+  printf("'\nRevision: '");
+  for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
+    printf("%c", Inquiry->ProductRevisionLevel[i]);
+  printf("'\n");\
+  if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
+    printf("Attached Changer: Yes\n");
+  } else {
+    printf("Attached Changer: No\n");
+  }
+  free(Inquiry);  /* well, we're about to exit, but ... */
+}
+
+static void Status(void)
+{
+  int StorageElementNumber;
+  int TransferElementNumber;
+  printf("  Storage Changer %s:%d Drives, %d Slots ( %d Import/Export )\n",
+        device,
+        ElementStatus->DataTransferElementCount,
+        ElementStatus->StorageElementCount,
+        ElementStatus->ImportExportCount);
+
+
+  for (TransferElementNumber=0;TransferElementNumber <
+        ElementStatus->DataTransferElementCount;TransferElementNumber++) {
+    
+    printf("Data Transfer Element %d:",TransferElementNumber);
+    if (ElementStatus->DataTransferElementFull[TransferElementNumber])
+      {
+       if (ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber] > -1) 
+         printf("Full (Storage Element %d Loaded)",
+                ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber]+1);
+       else printf("Full (Unknown Storage Element Loaded)");
+       if
+         (ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber][0])
+         printf(":VolumeTag = %s",ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber]);
+       if
+         (ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber][0])
+         
+         printf(":AlternateVolumeTag = %s",ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber]); 
+       putchar('\n');
+      }
+    else printf("Empty\n");
+  }
+
+  for (StorageElementNumber = 0;
+       StorageElementNumber < ElementStatus->StorageElementCount;
+       StorageElementNumber++) {
+    printf("      Storage Element %d%s:%s", StorageElementNumber+1, 
+          (ElementStatus->StorageElementIsImportExport[StorageElementNumber]) ? " IMPORT/EXPORT" : "",
+          (ElementStatus->StorageElementFull[StorageElementNumber] ? "Full " : "Empty"));
+    if (ElementStatus->PrimaryVolumeTag[StorageElementNumber][0]) {
+      printf(":VolumeTag=%s",ElementStatus->PrimaryVolumeTag[StorageElementNumber]);
+    }
+    if (ElementStatus->AlternateVolumeTag[StorageElementNumber][0]) {
+            printf(":AlternateVolumeTag=%s",ElementStatus->AlternateVolumeTag[StorageElementNumber]);
+    }
+    putchar('\n');
+  }
+  
+#ifdef VMS
+  VMS_DefineStatusSymbols();
+#endif
+}
+
+void Move(int src, int dest) {
+  RequestSense_T *result; /* from MoveMedium */
+  
+  result=MoveMedium(MediumChangerFD,src,dest,ElementStatus,inquiry_info,&SCSI_Flags);
+  if (result)   /* we have an error! */
+     {
+      if (result->AdditionalSenseCode == 0x3B &&
+         result->AdditionalSenseCodeQualifier == 0x0E)
+       FatalError("source Element Address %d is Empty\n", src);
+      if (result->AdditionalSenseCode == 0x3B &&
+         result->AdditionalSenseCodeQualifier == 0x0D)
+       FatalError("destination Element Address %d is Already Full\n",
+                  dest);
+      if (result->AdditionalSenseCode == 0x30 &&
+         result->AdditionalSenseCodeQualifier == 0x03)
+       FatalError("Cleaning Cartridge Installed and Ejected\n");
+      PrintRequestSense(result);
+      FatalError("MOVE MEDIUM from Element Address %d to %d Failed\n",
+                src,dest);
+    }
+}   
+
+/* okay, now for the Load, Unload, etc. logic: */
+
+static void Load(void) {
+  int src, dest;
+  /* okay, check src, dest: arg1=src, arg2=dest */
+  if (arg1 < 1) {
+    FatalError("No source specified\n");
+  }
+  if (arg2 < 0) {
+    arg2 = 0; /* default to 1st drive :-( */
+  }
+  arg1--;  /* we use zero-based arrays, sigh, not 1 base like some lusers */
+  /* check for filehandle: */
+  if (!device_opened) {
+    FatalError("No Media Changer Device Specified\n");
+  } 
+  /* okay, we should be there: */
+  if (arg1 < 0 || arg1 >= ElementStatus->StorageElementCount) {
+       FatalError(
+         "illegal <storage-element-number> argument '%s' to 'load' command\n",
+         arg1+1);
+  }
+  if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
+    FatalError(
+              "illegal <drive-number> argument '%s' to 'load' command\n",
+              arg2);
+  }
+  if (ElementStatus->DataTransferElementFull[arg2])  {
+    FatalError("Drive %d Full (Storage Element %d loaded)\n",arg2,
+              ElementStatus->DataTransferElementSourceStorageElementNumber[arg2]+1);
+  }
+  /* Now look up the actual devices: */
+  dest=ElementStatus->DataTransferElementAddress[arg2];
+  src=ElementStatus->StorageElementAddress[arg1];
+  Move(src,dest);  /* load it into the particular slot, if possible! */
+  /* now set the status for further command son this line... */
+  ElementStatus->StorageElementFull[arg1] = false;
+  ElementStatus->DataTransferElementFull[arg2] = true;
+
+  return; /* and done! */
+}
+
+static void Transfer(void) {
+  int src,dest;
+  if (arg1 < 1 ) {
+    FatalError("No source specified\n");
+  } 
+  if (arg2 < 1) {
+    FatalError("No destination specified\n");
+  }
+  if (arg1 > ElementStatus->StorageElementCount) {
+    FatalError("Invalid source\n");
+  }
+  if (arg2 > ElementStatus->StorageElementCount) {
+    FatalError("Invalid destination\n");
+  }
+  /* okay, that's done... */
+  src=ElementStatus->StorageElementAddress[arg1-1];
+  dest=ElementStatus->StorageElementAddress[arg2-1];
+  Move(src,dest);
+}
+
+static void Eepos(void) {
+  if (arg1 < 0 || arg1 > 3) {
+    FatalError("eepos equires argument between 0 and 3.\n");
+  }
+  SCSI_Flags.eepos=arg1;
+}
+
+
+static void Unload(void) {
+  int src,dest;  /* the actual SCSI-level numbers */
+  if (arg2 < 0) {
+    arg2 = 0; /* default to 1st drive :-( */
+  }
+  /* check for filehandle: */
+  if (!device_opened) {
+    FatalError("No Media Changer Device Specified\n");
+  } 
+  /* okay, we should be there: */
+  if (arg1 < 0) {
+    arg1 = ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
+    if (arg1 < 0) {
+      FatalError("No Source for tape in drive %d!\n");
+    }
+  } else {
+    arg1--; /* go from bogus 1-base to zero-base */
+  }
+
+  if (arg1 >= ElementStatus->StorageElementCount) {
+       FatalError(
+         "illegal <storage-element-number> argument '%s' to 'unload' command\n",
+         arg1+1);
+  }
+    
+  if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
+    FatalError(
+              "illegal <drive-number> argument '%s' to 'unload' command\n",
+              arg2);
+  }
+  if (ElementStatus->DataTransferElementFull[arg2] < 0 ) {
+    FatalError("Data Transfer Element %d is Empty\n", arg2);
+  }    
+  /* Now see if something already lives where  we wanna go... */
+  if (ElementStatus->StorageElementFull[arg1]) {
+    FatalError("Storage Element %d is Already Full\n", arg1+1);
+  }
+  /* okay, now to get src, dest: */
+  src=ElementStatus->DataTransferElementAddress[arg2];
+  if (arg1 >=0) {
+    dest=ElementStatus->StorageElementAddress[arg1];
+  } else {
+    dest=ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
+  }
+  if (dest < 0) { /* we STILL don't know, sigh... */
+    FatalError("Do not know which slot to unload tape into!\n");
+  }
+  fprintf(stderr, "Unloading Data Transfer Element into Storage Element %d...", arg1+1);
+  fflush(stderr); /* make it real-time :-( */ 
+  Move(src,dest);
+  fprintf(stderr, "done\n");
+  ElementStatus->StorageElementFull[arg1] = true;
+  ElementStatus->DataTransferElementFull[arg2] = false;
+}
+
+/*****************************************************************
+ ** ARGUMENT PARSING SUBROUTINES: Parse arguments, dispatch. 
+ *****************************************************************/
+
+/* ***
+ * int get_arg(idx):
+ *
+ * If we have an actual argument at the index position indicated (i.e. we
+ * have not gone off the edge of the world), we return
+ * its number. If we don't, or it's not a numeric argument, 
+ * we return -1. Note that 'get_arg' is kind of misleading, we only accept 
+ * numeric arguments, not any other kind. 
+ */
+int get_arg(int idx) {
+  char *arg;
+  int retval=-1;
+
+  if (idx >= argc) return -1;  /* sorry! */
+  arg=argv[idx];
+  if (*arg < '0' || *arg > '9') {
+    return -1;  /* sorry! */
+  }
+
+  retval=atoi(arg);
+  return retval;
+}
+
+/* open_device() -- set the 'fh' variable.... */
+void open_device(void) {
+
+
+  if (device_opened) {
+    SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
+  }
+
+  MediumChangerFD = SCSI_OpenDevice(device);
+  device_opened=1; /* SCSI_OpenDevice does an exit() if not. */
+}
+  
+
+/* we see if we've got a file open. If not, we open one :-(. Then
+ * we execute the actual command. Or not :-(. 
+ */ 
+void execute_command(struct command_table_struct *command) {
+  RequestSense_T RequestSense;
+
+
+  if ((device==NULL) && command->need_device) {
+   /* try to get it from TAPE environment variable... */
+    device=getenv("CHANGER");
+    if (device==NULL) {
+      device=getenv("TAPE");
+      if (device==NULL) {
+       device="/dev/changer"; /* Usage(); */
+      }
+    }
+    open_device();
+  }
+  if (!ElementStatus && command->need_status) {
+    inquiry_info=RequestInquiry(MediumChangerFD,&RequestSense);
+    if (!inquiry_info) {
+      PrintRequestSense(&RequestSense);
+      FatalError("INQUIRY command Failed\n");
+    }
+    ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense,inquiry_info,&SCSI_Flags);
+    if (!ElementStatus) {
+      PrintRequestSense(&RequestSense);                   
+      FatalError("READ ELEMENT STATUS Command Failed\n"); 
+    }
+  }
+
+  /* okay, now to execute the command... */
+  command->command();
+}
+
+/* parse_args():
+ *   Basically, we are parsing argv/argc. We can have multiple commands
+ * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
+ * load in another tape into drive 0, and we execute these commands one
+ * at a time as we come to them. If we don't have a -f at the start, we
+ * barf. If we leave out a drive #, we default to drive 0 (the first drive
+ * in the cabinet). 
+ */ 
+
+int parse_args(void) {
+  int i,cmd_tbl_idx;
+  struct command_table_struct *command;
+
+  i=1;
+  while (i<argc) {
+    if (strcmp(argv[i],"-f") == 0) {
+      i++;
+      if (i>=argc) {
+       Usage();
+      }
+      device=argv[i++];
+      open_device(); /* open the device and do a status scan on it... */
+    } else {
+      cmd_tbl_idx=0;
+      command=&command_table[0]; /* default to the first command... */
+      command=&command_table[cmd_tbl_idx];
+      while (command->name) {
+       if (!strcmp(command->name,argv[i])) {
+         /* we have a match... */
+         break;
+       }
+       /* otherwise we don't have a match... */
+       cmd_tbl_idx++;
+       command=&command_table[cmd_tbl_idx];
+      }
+      /* if it's not a command, exit.... */
+      if (!command->name) {
+       Usage();
+      }
+      i++;  /* go to the next argument, if possible... */
+      /* see if we need to gather arguments, though! */
+      if (command->num_args == 0) {
+       execute_command(command);  /* execute_command handles 'stuff' */
+      }
+      else {
+       arg1=get_arg(i);  /* checks i... */
+       if (arg1 != -1) {
+         i++;  /* next! */
+       }
+       if (command->num_args==2 && arg1 != -1) {
+         arg2=get_arg(i); 
+         if (arg2 != -1) {
+           i++;
+         }
+       }
+       execute_command(command);
+      }
+      arg1=-1;
+      arg2=-1;
+    }
+  }
+  return 0; /* should never get here. */
+}
+
+
+
+int main(int ArgCount,
+        char *ArgVector[],
+        char *Environment[])
+{
+
+
+#ifdef VMS
+  RequestSense_T RequestSense;
+#endif
+   
+ /* save these where we can get at them elsewhere... */
+  argc=ArgCount;
+  argv=ArgVector;
+
+  argv0=argv[0];
+
+   
+
+
+  parse_args();  /* also executes them as it sees them, sigh. */
+
+#ifndef VMS
+  if (device) 
+    SCSI_CloseDevice(device, MediumChangerFD);
+  return 0;
+#else
+  if (device) {
+    ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense);
+    if (!ElementStatus) {
+      PrintRequestSense(&RequestSense);                   
+      FatalError("READ ELEMENT STATUS Command Failed\n"); 
+    }
+    VMS_DefineStatusSymbols();
+    SCSI_CloseDevice(device, MediumChangerFD);
+  }
+  return SS$_NORMAL;
+#endif
+
+
+}
+/*
+ *$Log: mtx.c,v $
+ *Revision 1.2.2.1  2001/11/06 21:20:40  elgreen
+ *Hopefully a fix to the problem with the 0 return for open in crontabs
+ *
+ *Revision 1.2  2001/06/09 17:26:26  elgreen
+ *Added 'nobarcode' command to mtx (to skip the initial request asking for
+ *barcodes for mtx status purposes).
+ *
+ *Revision 1.1.1.1  2001/06/05 17:10:22  elgreen
+ *Initial import into SourceForge
+ *
+ *Revision 1.15  2001/04/18 16:32:59  eric
+ *Cleaned up all -Wall messages.
+ *
+ *Revision 1.14  2001/04/18 16:08:08  eric
+ *after re-arranging & fixing bugs
+ *
+ *Revision 1.13  2001/03/06 01:37:05  eric
+ *Solaris changes
+ *
+ *Revision 1.12  2001/01/24 22:04:08  eric
+ *added /dev/changer as default file name.
+ *
+ *Revision 1.11  2001/01/15 22:21:06  eric
+ *added autoconf stuff.
+ *
+ *Revision 1.10  2000/11/30 20:37:17  eric
+ *Added quicky 'erase' command because I needed blank tapes and Linux
+ *only does long erases. (Not documented on purpose, because of rather
+ *bizarre interactions with the native tape driver if you don't know
+ *what you are doing).
+ *
+ *Revision 1.9  2000/11/28 01:10:50  eric
+ *Fixed syntax error in mtx.c...
+ *
+ *Revision 1.8  2000/10/28 00:10:35  eric
+ *Fixed stupid typo
+ *
+ *Revision 1.7  2000/10/27 23:36:57  eric
+ *make mtx inventory return an error code if the inventory fails, so that
+ *we can wait for inventory to be completed at system startup
+ *
+ *
+ */
diff --git a/mtx.doc b/mtx.doc
new file mode 100644 (file)
index 0000000..ae814e8
--- /dev/null
+++ b/mtx.doc
@@ -0,0 +1,209 @@
+[WARNING: This document is of historical value only! Please read
+ 'mtxl.README.html' and 'man mtx' for current documentation! The only
+ thing useful here is examples of how to use the 'tar' command for
+ multi-tape backups.
+]
+
+               MTX - SCSI Tape Medium Changer Control Program
+
+       Version 1.1 for Linux, Solaris, IRIX, Digital Unix, and OpenVMS
+
+                                 2 June 1998
+
+                              Leonard N. Zubkoff
+                              Dandelion Digital
+                              lnz@dandelion.com
+
+        Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+                                INTRODUCTION
+
+The MTX program controls the robotic mechanism in DDS Autoloaders such as the
+Seagate 4586NP (Archive Python 28849-XXX).  This program is also reported to
+work with the Seagate 4584NP, HP Surestore 12000, Quantum DLT 2500 XT, and AIWA
+DL-210.  The 4586NP responds to both Logical Units 0 and 1 on the selected
+Target ID.  Logical Unit 0 supports commands from the SCSI-3 Sequential Stream
+Device Command Set and must be accessed via the SCSI tape devices /dev/st<N>.
+Logical Unit 1 only supports commands from the SCSI-3 Medium Changer Command
+Set and must be accessed via the SCSI Generic devices /dev/sg<N>.  In addition,
+Logical Unit 0 also acts as an Attached Medium Changer and supports the READ
+ELEMENT STATUS and MOVE MEDIUM commands.  Since using the Primary Device
+(Logical Unit 0) via the Attached Medium Changer interface is sufficient for
+the commands provided by this program, Logical Unit 1 is not normally used.
+The AIWA DL-210, by contrast, does not support the Attached Medium Changer
+commands on Logical Unit 0 and so MTX must be used with Logical Unit 1 via the
+SCSI Generic devices /dev/sg<N>.  Note that when using the SCSI Generic devices
+the Linux kernel option "max_scsi_luns=2" may be necessary.
+
+Medium Changers support four types of elements: Medium Transport Elements,
+Storage Elements, Import Export Elements, and Data Transfer Elements.  For the
+limited case of DDS Autoloaders, only the Data Transfer Element and Storage
+Element types are really applicable.  The Data Transfer Element is the primary
+device where a volume can be loaded to actually perform data transfers.  A
+Storage Element is a place a volume can be when it is waiting to be used.  For
+a DDS Autoloader, the Storage Elements are the slots in the cartridge where
+tapes may be placed, and the Data Transfer Element is the tape mechanism.
+
+Every Medium Changer Element has a unique integer Element Address that
+identifies it in the address space of all Elements known to the Medium Changer.
+For the 4586NP, the Data Transfer Element is Address 1 and the Storage Elements
+are Addresses 2-5 or 2-13.  To simplify the human interface, this program does
+not use Element Addresses directly.  Rather, it uses Storage Element Numbers
+which range from 1 to the number of Storage Elements available.
+
+The specific tape device to be operated on can either be supplied on the
+command line with the "-f <tape-device>" option, or via the TAPE environment
+variable.
+
+
+                                BUILDING MTX
+
+The Makefile contains sections for Linux, Solaris/SPARC, SGI IRIX, and Digital
+UNIX.  Comment/uncomment as necessary for the target environment.  For OpenVMS,
+see the file "vms/000readme" for information on building MTX; a VMS release
+including pre-built binaries should be available from the WKU VMS FILESERV
+ARCHIVES, which are located at URLs http://www2.wku.edu/www/fileserv/ and
+ftp://ftp.wku.edu/vms/fileserv/.
+
+
+                                  COMMANDS
+
+MTX provides the following commands:
+
+
+mtx [ -f <tape-device> ] inquiry
+
+The "inquiry" command reports the Vendor ID, Product ID, and Revision from a
+SCSI INQUIRY command.
+
+kelewan:~# setenv TAPE /dev/st0
+
+kelewan:~# mtx inquiry
+Vendor ID: 'ARCHIVE ', Product ID: 'Python 28849-XXX', Revision: '4.CM'
+
+
+mtx [ -f <tape-device> ] status
+
+The "status" command reports on the status of the DDS Autloader.  The report
+indicates the status of the Data Transfer Element and each of the Storage
+Elements.  In the first example, no tape is currently loaded.  In the second
+example, Storage Element Number 1 is loaded.  The Storage Element loaded into
+the Data Transfer Element is usually reported by the DDS Autoloader, or it can
+be inferred by MTX if there is only a single empty Storage Element.
+
+kelewan:~# mtx status
+Data Transfer Element: Empty
+Storage Element 1:     Full
+Storage Element 2:     Full
+Storage Element 3:     Full
+Storage Element 4:     Full
+
+
+kelewan:~# mtx status
+Data Transfer Element: Full (Storage Element 1 Loaded)
+Storage Element 1:     Empty
+Storage Element 2:     Full
+Storage Element 3:     Full
+Storage Element 4:     Full
+
+
+mtx [ -f <tape-device> ] load <storage-element-number>
+
+The "load" command loads the volume in Storage Element <storage-element-number>
+into the Data Transfer Element.  An error is signaled if the Data Transfer
+Element is already full.
+
+
+mtx [ -f <tape-device> ] unload [ <storage-element-number> ]
+
+The "unload" command unloads the volume in the Data Transfer Element into
+Storage Element <storage-element-number>.  If <storage-element-number> is not
+provided, then the volume is unloaded back into the Storage Element from which
+it was originally loaded, if known.  An error is signaled if the Storage
+Element is already full.
+
+
+mtx [ -f <tape-device> ] first
+
+The "first" command unloads any volume in the Data Transfer Element and then
+loads the volume in the lowest numbered non-empty Storage Element.  If the
+correct Storage Element is already loaded, the unload/load is suppressed.
+
+
+mtx [ -f <tape-device> ] next
+
+The "next" command unloads any volume in the Data Transfer Element and then
+loads the volume in the lowest numbered non-empty Storage Element above the
+Storage Element that was just unloaded.  If there is no next Storage Element,
+the unload is still performed and the Data Transfer Element will be left empty
+so that the volume is not accidentally overwritten.
+
+
+mtx [ -f <tape-device> ] last
+
+The "last" command unloads any volume in the Data Transfer Element and then
+loads the volume in the highest numbered non-empty Storage Element.  If the
+correct Storage Element is already loaded, the unload/load is suppressed.
+
+
+mtx [ -f <tape-device> ] previous
+
+The "previous" command unloads any volume in the Data Transfer Element and then
+loads the volume in the highest numbered non-empty Storage Element below the
+Storage Element that was just unloaded.  If there is no previous Storage
+Element, the unload is still performed and the Data Transfer Element will be
+left empty so that the volume is not accidentally overwritten.
+
+
+The interface is designed to allow both explicit control of precisely which
+Storage Element is loaded, as well as sequential access among only the Storage
+Elements that actually have volumes present.  Thus for example, one way of
+using MTX would be to perform Monday's incremental backups on Storage Element
+1, Tuesday's on Storage Element 2, and so on.  Multi-volume full backups are
+also conveniently supported, as in:
+
+setenv TAPE "/dev/st0"
+
+mtx status
+
+mtx first
+
+time tar --create --one-file-system --atime-preserve \
+        --listed-incremental `date +%y%m%d`.ss \
+        --multi-volume --new-volume-script "mtx next" \
+        --directory /  <wherever>
+
+mtx first
+
+time tar --compare --multi-volume --new-volume-script "mtx next" \
+        --directory /
+
+mtx unload
+
+
+                          LINUX KERNEL REQUIREMENTS
+
+Because the MOVE MEDIUM command may require 60 seconds or more to perform a
+volume load or unload request, a longer timeout must be provided.  Linux
+kernels 2.0.30 and 2.1.28 should already contain a long enough timeout.  For
+earlier kernels, edit the file "linux/drivers/scsi/scsi_ioctl.c" and add the
+following entries to the switch statement in ioctl_command:
+
+      case MOVE_MEDIUM:
+      case READ_ELEMENT_STATUS:
+       timeout =  5 * 60 * HZ; /* 5 minutes */
+       retries = 1;
+       break;
+
+For older kernels, you may also need to add the following definitions to
+"linux/include/scsi/scsi.h":
+
+#define MOVE_MEDIUM           0xa5
+#define READ_ELEMENT_STATUS   0xb8
+
+Note also that "/usr/include/scsi" should be a symbolic link to the directory
+"linux/include/scsi" from your kernel source.
+
+Finally, when using MTX you may see some console messages from the SCSI tape
+driver mentioning that there is no tape present.  These can safely be ignored.
diff --git a/mtx.h b/mtx.h
new file mode 100644 (file)
index 0000000..1144ea6
--- /dev/null
+++ b/mtx.h
@@ -0,0 +1,523 @@
+/* MTX -- SCSI Tape Attached Medium Control Program
+
+   Copyright 1997-1998 Leonard N. Zubkoff <lnz@dandelion.com>
+   Changes 1999 Eric Lee Green to add support for multi-drive tape changers.
+
+   $Date: 2001/06/19 21:51:32 $
+   $Revision: 1.3 $
+   See mtx.c for licensing information. 
+
+*/
+
+#ifndef MTX_H  /* protect against multiple includes... */
+#define MTX_H 1
+
+/* surround all the Unix-stuff w/ifndef VMS */
+#ifdef VMS
+#include "[.vms]defs.h"
+#else /* all the Unix stuff:  */
+
+#include "config.h"  /* all the autoconf stuff. */
+
+/* all the general Unix includes: */
+
+#include <stdio.h>
+#include <errno.h>
+
+#if HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+#if HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+#if HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#if HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#if HAVE_STDARG_H
+#  include <stdarg.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
+
+/* Now greately 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>
+#  include <scsi/scsi_ioctl.h>
+#  include <scsi/sg.h>
+typedef int DEVICE_TYPE; /* the sg interface uses this. */
+#  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. */
+#  include <cam/cam_ccb.h>
+#  include <cam/scsi/scsi_message.h> /* sigh sigh sigh! */
+typedef struct cam_device *DEVICE_TYPE;
+#endif
+
+
+/* the 'uscsi' interface, as used on Solaris: */
+#if HAVE_SYS_SCSI_IMPL_USCSI_H
+#include <sys/scsi/impl/uscsi.h>
+typedef int DEVICE_TYPE;
+#endif
+
+/* the scsi_ctl interface, as used on HP/UX: */
+#if HAVE_SYS_SCSI_CTL_H
+#  include <sys/wsio.h>
+#  include <sys/spinlock.h>
+#  include <sys/scsi.h>
+#  include <sys/scsi_ctl.h>
+  typedef int DEVICE_TYPE;
+#  ifndef VERSION
+#     define VERSION "1.2.12 hbb"
+#  endif
+#endif
+
+
+/* The 'tm_buf' interface, as used on AIX. */
+#ifdef HAVE_SYS_SCSI_H
+#include <sys/scsi.h>
+#include <sys/scsi_buf.h>
+#include <sys/devinfo.h> /* devinfo. */
+typedef struct tm_device_type {
+  int filenum;
+  int id;
+  int lun;
+  char *DeviceName;
+} *DEVICE_TYPE;
+    
+#endif
+
+   /* the 'dslib' interface, as used on SGI.  */
+#if HAVE_DSLIB_H
+#include <dslib.h>
+typedef dsreq_t *DEVICE_TYPE; /* 64-bit pointers/32bit int on later sgi? */
+#endif
+
+
+#if ((defined(__alpha) && defined(__osf__)) || \
+     defined(ultrix) || defined(__ultrix))
+#include "du/defs.h"
+#endif
+
+
+#endif /* VMS protect. */
+
+/* Do a test for LITTLE_ENDIAN_BITFIELDS. Use WORDS_BIGENDIAN as set
+ * by configure: 
+ */
+
+#if WORDS_BIGENDIAN
+# define BIG_ENDIAN_BITFIELDS
+#else
+# define LITTLE_ENDIAN_BITFIELDS
+#endif
+
+/* Get rid of some Hocky Pux defines: */
+#ifdef S_NO_SENSE
+#undef S_NO_SENSE
+#endif
+#ifdef S_RECOVERED_ERROR
+#undef S_RECOVERED_ERROR
+#endif
+#ifdef S_NOT_READY
+#undef S_NOT_READY
+#endif
+#ifdef S_MEDIUM_ERROR
+#undef S_MEDIUM_ERROR
+#endif
+#ifdef S_HARDWARE_ERROR
+#undef S_HARDWARE_ERROR
+#endif
+#ifdef S_UNIT_ATTENTION
+#undef S_UNIT_ATTENTION
+#endif
+#ifdef S_BLANK_CHECK
+#undef S_BLANK_CHECK
+#endif
+#ifdef S_VOLUME_OVERFLOW
+#undef S_VOLUME_OVERFLOW
+#endif
+
+/* Note: These are only used for defaults for when we don't have 
+ * the element assignment mode page to tell us real amount...
+ */
+#define MAX_STORAGE_ELEMENTS 64 /* for the BIG jukeboxes! */
+#define MAX_TRANSFER_ELEMENTS 2  /* we just do dual-drive for now :-} */
+#define MAX_TRANSPORT_ELEMENTS 1 /* we just do one arm for now... */
+
+/* These are flags used for the READ_ELEMENT_STATUS and MOVE_MEDIUM
+ * commands:
+ */
+typedef struct SCSI_Flags_Struct {
+  unsigned char eepos;
+  unsigned char invert;
+  unsigned char no_attached; /* ignore _attached bit */
+  unsigned char no_barcodes;  /* don't try to get barcodes. */
+  int numbytes;
+  int elementtype;
+  int numelements;
+  int attached;
+  int has_barcodes;
+} SCSI_Flags_T;
+
+typedef enum { false, true } boolean;
+
+
+typedef enum { Input, Output } Direction_T;
+
+
+typedef unsigned char CDB_T[12];
+
+
+typedef struct Inquiry
+{
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char PeripheralDeviceType:5;                        /* Byte 0 Bits 0-4 */
+  unsigned char PeripheralQualifier:3;                 /* Byte 0 Bits 5-7 */
+  unsigned char DeviceTypeModifier:7;                  /* Byte 1 Bits 0-6 */
+  boolean RMB:1;                                       /* Byte 1 Bit 7 */
+  unsigned char ANSI_ApprovedVersion:3;                        /* Byte 2 Bits 0-2 */
+  unsigned char ECMA_Version:3;                                /* Byte 2 Bits 3-5 */
+  unsigned char ISO_Version:2;                         /* Byte 2 Bits 6-7 */
+  unsigned char ResponseDataFormat:4;                  /* Byte 3 Bits 0-3 */
+  unsigned char :2;                                    /* Byte 3 Bits 4-5 */
+  boolean TrmIOP:1;                                    /* Byte 3 Bit 6 */
+  boolean AENC:1;                                      /* Byte 3 Bit 7 */
+#else
+  unsigned char PeripheralQualifier:3;                 /* Byte 0 Bits 5-7 */
+  unsigned char PeripheralDeviceType:5;                        /* Byte 0 Bits 0-4 */
+  boolean RMB:1;                                       /* Byte 1 Bit 7 */
+  unsigned char DeviceTypeModifier:7;                  /* Byte 1 Bits 0-6 */
+  unsigned char ISO_Version:2;                         /* Byte 2 Bits 6-7 */
+  unsigned char ECMA_Version:3;                                /* Byte 2 Bits 3-5 */
+  unsigned char ANSI_ApprovedVersion:3;                        /* Byte 2 Bits 0-2 */
+  boolean AENC:1;                                      /* Byte 3 Bit 7 */
+  boolean TrmIOP:1;                                    /* Byte 3 Bit 6 */
+  unsigned char :2;                                    /* Byte 3 Bits 4-5 */
+  unsigned char ResponseDataFormat:4;                  /* Byte 3 Bits 0-3 */
+#endif
+  unsigned char AdditionalLength;                      /* Byte 4 */
+  unsigned char :8;                                    /* Byte 5 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  boolean ADDR16:1;                                    /* Byte 6 bit 0 */
+  boolean Obs6_1:1;                                    /* Byte 6 bit 1 */
+  boolean Obs6_2:1; /* obsolete */                     /* Byte 6 bit 2 */
+  boolean MChngr:1; /* Media Changer */                /* Byte 6 bit 3 */
+  boolean MultiP:1;                                    /* Byte 6 bit 4 */
+  boolean VS:1;                                        /* Byte 6 bit 5 */
+  boolean EncServ:1;                                   /* Byte 6 bit 6 */
+  boolean BQue:1;                                      /* Byte 6 bit 7 */
+#else
+  boolean BQue:1;                                      /* Byte 6 bit 7 */
+  boolean EncServ:1;                                   /* Byte 6 bit 6 */
+  boolean VS:1;                                        /* Byte 6 bit 5 */
+  boolean MultiP:1;                                    /* Byte 6 bit 4 */
+  boolean MChngr:1; /* Media Changer */                /* Byte 6 bit 3 */
+  boolean Obs6_2:1; /* obsolete */                     /* Byte 6 bit 2 */
+  boolean Obs6_1:1;                                    /* Byte 6 bit 1 */
+  boolean ADDR16:1;                                    /* Byte 6 bit 0 */
+#endif
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  boolean SftRe:1;                                     /* Byte 7 Bit 0 */
+  boolean CmdQue:1;                                    /* Byte 7 Bit 1 */
+  boolean :1;                                          /* Byte 7 Bit 2 */
+  boolean Linked:1;                                    /* Byte 7 Bit 3 */
+  boolean Sync:1;                                      /* Byte 7 Bit 4 */
+  boolean WBus16:1;                                    /* Byte 7 Bit 5 */
+  boolean WBus32:1;                                    /* Byte 7 Bit 6 */
+  boolean RelAdr:1;                                    /* Byte 7 Bit 7 */
+#else
+  boolean RelAdr:1;                                    /* Byte 7 Bit 7 */
+  boolean WBus32:1;                                    /* Byte 7 Bit 6 */
+  boolean WBus16:1;                                    /* Byte 7 Bit 5 */
+  boolean Sync:1;                                      /* Byte 7 Bit 4 */
+  boolean Linked:1;                                    /* Byte 7 Bit 3 */
+  boolean :1;                                          /* Byte 7 Bit 2 */
+  boolean CmdQue:1;                                    /* Byte 7 Bit 1 */
+  boolean SftRe:1;                                     /* Byte 7 Bit 0 */
+#endif
+  unsigned char VendorIdentification[8];               /* Bytes 8-15 */
+  unsigned char ProductIdentification[16];             /* Bytes 16-31 */
+  unsigned char ProductRevisionLevel[4];               /* Bytes 32-35 */
+  unsigned char FullProductRevisionLevel[19];           /* bytes 36-54 */
+  unsigned char VendorFlags;                            /* byte 55 */
+}
+Inquiry_T;
+
+/* Hockey Pux may define these. If so, *UN*define them. */
+#ifdef ILI
+#undef ILI
+#endif
+
+#ifdef EOM
+#undef EOM
+#endif
+
+typedef struct RequestSense
+{
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char ErrorCode:7;                           /* Byte 0 Bits 0-6 */
+  boolean Valid:1;                                     /* Byte 0 Bit 7 */
+#else
+  boolean Valid:1;                                     /* Byte 0 Bit 7 */
+  unsigned char ErrorCode:7;                           /* Byte 0 Bits 0-6 */
+#endif
+  unsigned char SegmentNumber;                         /* Byte 1 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char SenseKey:4;                            /* Byte 2 Bits 0-3 */
+  unsigned char :1;                                    /* Byte 2 Bit 4 */
+  boolean ILI:1;                                       /* Byte 2 Bit 5 */
+  boolean EOM:1;                                       /* Byte 2 Bit 6 */
+  boolean Filemark:1;                                  /* Byte 2 Bit 7 */
+#else
+  boolean Filemark:1;                                  /* Byte 2 Bit 7 */
+  boolean EOM:1;                                       /* Byte 2 Bit 6 */
+  boolean ILI:1;                                       /* Byte 2 Bit 5 */
+  unsigned char :1;                                    /* Byte 2 Bit 4 */
+  unsigned char SenseKey:4;                            /* Byte 2 Bits 0-3 */
+#endif
+  unsigned char Information[4];                                /* Bytes 3-6 */
+  unsigned char AdditionalSenseLength;                 /* Byte 7 */
+  unsigned char CommandSpecificInformation[4];         /* Bytes 8-11 */
+  unsigned char AdditionalSenseCode;                   /* Byte 12 */
+  unsigned char AdditionalSenseCodeQualifier;          /* Byte 13 */
+  unsigned char :8;                                    /* Byte 14 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char BitPointer:3;                           /* Byte 15 */
+  boolean BPV:1;
+  unsigned char :2;
+  boolean CommandData :1;
+  boolean SKSV:1;      
+#else
+  boolean SKSV:1;      
+  boolean CommandData :1;
+  unsigned char :2;
+  boolean BPV:1;
+  unsigned char BitPointer:3;                           /* Byte 15 */
+#endif
+  unsigned char FieldData[2];                          /* Byte 16,17 */
+}
+RequestSense_T;
+
+/* Okay, now for the element status mode sense page (0x1d): */
+
+typedef struct ElementModeSensePageHeader {
+  unsigned char PageCode;  /* byte 0 */
+  unsigned char ParameterLengthList;  /* byte 1; */
+  unsigned char MediumTransportStartHi; /* byte 2,3 */
+  unsigned char MediumTransportStartLo;
+  unsigned char NumMediumTransportHi;   /* byte 4,5 */
+  unsigned char NumMediumTransportLo;   /* byte 4,5 */
+  unsigned char StorageStartHi;         /* byte 6,7 */
+  unsigned char StorageStartLo;         /* byte 6,7 */
+  unsigned char NumStorageHi;           /* byte 8,9 */
+  unsigned char NumStorageLo;           /* byte 8,9 */
+  unsigned char ImportExportStartHi;    /* byte 10,11 */
+  unsigned char ImportExportStartLo;    /* byte 10,11 */
+  unsigned char NumImportExportHi;      /* byte 12,13 */
+  unsigned char NumImportExportLo;      /* byte 12,13 */
+  unsigned char DataTransferStartHi;    /* byte 14,15 */
+  unsigned char DataTransferStartLo;    /* byte 14,15 */
+  unsigned char NumDataTransferHi;      /* byte 16,17 */
+  unsigned char NumDataTransferLo;      /* byte 16,17 */
+  unsigned char Reserved1;             /* byte 18, 19 */
+  unsigned char Reserved2;             /* byte 18, 19 */
+} ElementModeSensePage_T;
+
+typedef struct ElementModeSenseHeader {
+  int MaxReadElementStatusData; /* 'nuff for all of below. */
+  int NumElements;              /* total # of elements. */
+  int MediumTransportStart;
+  int NumMediumTransport;
+  int StorageStart;
+  int NumStorage;
+  int ImportExportStart;
+  int NumImportExport;
+  int DataTransferStart;
+  int NumDataTransfer;
+} ElementModeSense_T;
+
+
+typedef enum ElementTypeCode
+{
+  AllElementTypes =            0,
+  MediumTransportElement =     1,
+  StorageElement =             2,
+  ImportExportElement =                3,
+  DataTransferElement =                4
+}
+ElementTypeCode_T;
+
+
+typedef struct ElementStatusDataHeader
+{
+  unsigned char FirstElementAddressReported[2];                /* Bytes 0-1 */
+  unsigned char NumberOfElementsAvailable[2];          /* Bytes 2-3 */
+  unsigned char :8;                                    /* Byte 4 */
+  unsigned char ByteCountOfReportAvailable[3];         /* Bytes 5-7 */
+}
+ElementStatusDataHeader_T;
+
+
+typedef struct ElementStatusPage
+{
+  ElementTypeCode_T ElementTypeCode:8;                 /* Byte 0 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char :6;                                    /* Byte 1 Bits 0-5 */
+  boolean AVolTag:1;                                   /* Byte 1 Bit 6 */
+  boolean PVolTag:1;                                   /* Byte 1 Bit 7 */
+#else
+  boolean PVolTag:1;                                   /* Byte 1 Bit 7 */
+  boolean AVolTag:1;                                   /* Byte 1 Bit 6 */
+  unsigned char :6;                                    /* Byte 1 Bits 0-5 */
+#endif
+  unsigned char ElementDescriptorLength[2];            /* Bytes 2-3 */
+  unsigned char :8;                                    /* Byte 4 */
+  unsigned char ByteCountOfDescriptorDataAvailable[3]; /* Bytes 5-7 */
+}
+ElementStatusPage_T;
+
+typedef struct Element2StatusPage
+{
+  ElementTypeCode_T ElementTypeCode:8;                 /* Byte 0 */
+  unsigned char VolBits ; /* byte 1 */
+#define E2_PVOLTAG 0x80
+#define E2_AVOLTAG 0x40
+  unsigned char ElementDescriptorLength[2];            /* Bytes 2-3 */
+  unsigned char :8;                                    /* Byte 4 */
+  unsigned char ByteCountOfDescriptorDataAvailable[3]; /* Bytes 5-7 */
+}
+Element2StatusPage_T;
+
+
+
+typedef struct TransportElementDescriptorShort
+{
+  unsigned char ElementAddress[2];                     /* Bytes 0-1 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  boolean Full:1;                                      /* Byte 2 Bit 0 */
+  unsigned char :1;                                    /* Byte 2 Bit 1 */
+  boolean Except:1;                                    /* Byte 2 Bit 2 */
+  unsigned char :5;                                    /* Byte 2 Bits 3-7 */
+#else
+  unsigned char :5;                                    /* Byte 2 Bits 3-7 */
+  boolean Except:1;                                    /* Byte 2 Bit 2 */
+  unsigned char :1;                                    /* Byte 2 Bit 1 */
+  boolean Full:1;                                      /* Byte 2 Bit 0 */
+#endif
+  unsigned char :8;                                    /* Byte 3 */
+  unsigned char AdditionalSenseCode;                   /* Byte 4 */
+  unsigned char AdditionalSenseCodeQualifier;          /* Byte 5 */
+  unsigned char :8;                                    /* Byte 6 */
+  unsigned char :8;                                    /* Byte 7 */
+  unsigned char :8;                                    /* Byte 8 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char :6;                                    /* Byte 9 Bits 0-5 */
+  boolean SValid:1;                                    /* Byte 9 Bit 6 */
+  boolean Invert:1;                                    /* Byte 9 Bit 7 */
+#else
+  boolean Invert:1;                                    /* Byte 9 Bit 7 */
+  boolean SValid:1;                                    /* Byte 9 Bit 6 */
+  unsigned char :6;                                    /* Byte 9 Bits 0-5 */
+#endif
+  unsigned char SourceStorageElementAddress[2];                /* Bytes 10-11 */
+}
+TransportElementDescriptorShort_T;
+
+
+typedef struct TransportElementDescriptor
+{
+  unsigned char ElementAddress[2];                     /* Bytes 0-1 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  boolean Full:1;                                      /* Byte 2 Bit 0 */
+  unsigned char :1;                                    /* Byte 2 Bit 1 */
+  boolean Except:1;                                    /* Byte 2 Bit 2 */
+  unsigned char :5;                                    /* Byte 2 Bits 3-7 */
+#else
+  unsigned char :5;                                    /* Byte 2 Bits 3-7 */
+  boolean Except:1;                                    /* Byte 2 Bit 2 */
+  unsigned char :1;                                    /* Byte 2 Bit 1 */
+  boolean Full:1;                                      /* Byte 2 Bit 0 */
+#endif
+  unsigned char :8;                                    /* Byte 3 */
+  unsigned char AdditionalSenseCode;                   /* Byte 4 */
+  unsigned char AdditionalSenseCodeQualifier;          /* Byte 5 */
+  unsigned char :8;                                    /* Byte 6 */
+  unsigned char :8;                                    /* Byte 7 */
+  unsigned char :8;                                    /* Byte 8 */
+#ifdef LITTLE_ENDIAN_BITFIELDS
+  unsigned char :6;                                    /* Byte 9 Bits 0-5 */
+  boolean SValid:1;                                    /* Byte 9 Bit 6 */
+  boolean Invert:1;                                    /* Byte 9 Bit 7 */
+#else
+  boolean Invert:1;                                    /* Byte 9 Bit 7 */
+  boolean SValid:1;                                    /* Byte 9 Bit 6 */
+  unsigned char :6;                                    /* Byte 9 Bits 0-5 */
+#endif
+  unsigned char SourceStorageElementAddress[2];                /* Bytes 10-11 */
+  unsigned char PrimaryVolumeTag[36];          /* barcode */
+  unsigned char AlternateVolumeTag[36];        
+}
+TransportElementDescriptor_T;
+
+
+
+/* Now for element status data; */
+
+typedef unsigned char barcode[37];
+
+typedef struct ElementStatus {
+
+  int StorageElementCount;
+  int ImportExportCount;
+  int DataTransferElementCount;
+  int *DataTransferElementAddress;  /* array. */
+  int *DataTransferElementSourceStorageElementNumber; /* array */
+  barcode *DataTransferPrimaryVolumeTag; /* array. */
+  barcode *DataTransferAlternateVolumeTag; /* array. */
+  barcode *PrimaryVolumeTag;  /* array */
+  barcode *AlternateVolumeTag; /* array */
+  int *StorageElementAddress; /* array */
+  boolean *StorageElementIsImportExport; /* array */
+
+  int TransportElementAddress;  /* assume only one of those... */
+
+  boolean *DataTransferElementFull; /* array */
+  boolean *StorageElementFull;  /* array */
+
+} ElementStatus_T;
+
+
+/* Now for the SCSI ID and LUN information: */
+typedef struct scsi_id {
+  int id;
+  int lun;
+} scsi_id_t;
+
+#define MEDIUM_CHANGER_TYPE 8  /* what type bits are set for medium changers. */
+
+#endif  /* of multi-include protection. */
diff --git a/mtx.spec b/mtx.spec
new file mode 100644 (file)
index 0000000..e2dbb85
--- /dev/null
+++ b/mtx.spec
@@ -0,0 +1,105 @@
+Name: mtx
+Version: 1.2.16rel
+Release: 1
+Summary: SCSI media changer control program
+Copyright: Redistributable
+Group: Utilities/System
+Source0: http://prdownloads.sourceforge.net/mtx/%{name}-%{version}.tar.gz
+Url: http://%{name}.sourceforge.net
+BuildRoot: /var/tmp/%{name}-%{version}
+
+
+%description
+The MTX program controls the robotic mechanism in autoloaders and tape
+libraries such as the HP SureStore DAT 40x6, Exabyte EZ-17, and
+Exabyte 220. This program is also reported to work with a variety of other tape
+libraries and autochangers from Tandberg/Overland, Breece Hill, HP, and 
+Seagate.
+
+%prep
+%setup -q
+
+%build
+%configure
+make
+
+%install
+mkdir -p $RPM_BUILD_ROOT/sbin
+install mtx $RPM_BUILD_ROOT/sbin/mtx
+mkdir -p $RPM_BUILD_ROOT/usr/sbin
+install loaderinfo $RPM_BUILD_ROOT/usr/sbin/loaderinfo
+install scsitape $RPM_BUILD_ROOT/usr/sbin/scsitape
+install tapeinfo $RPM_BUILD_ROOT/usr/sbin/tapeinfo
+mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1
+install mtx.1 $RPM_BUILD_ROOT/%{_mandir}/man1/mtx.1
+install scsitape.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsitape.1
+install tapeinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/tapeinfo.1
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files 
+%defattr(-,root,root)
+%doc mtx.doc CHANGES README mtxl.README.html
+%doc COMPATABILITY FAQ LICENSE* TODO contrib
+%{_mandir}/man1/*
+/sbin/mtx
+/usr/sbin/*
+
+%changelog
+* Fri Nov 18 2001 Eric Green <eric@badtux.org>
+- 1.2.14
+- Moved changelog to end of spec file so it doesn't interfere
+- 
+
+* Wed Apr 18 2001 Kenneth Porter <shiva@well.com>
+- 1.2.12pre1
+- Need to create usr/sbin for install
+
+* Fri Mar 02 2001 Eric Green <eric@estinc.com>
+- 1.2.11pre6 
+- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin
+
+* Wed Feb 28 2001 Kenneth Porter <shiva@well.com>
+- 1.2.11pre5
+- Remove commented-out patch.
+- Use mandir FHS macro and configure macro.
+- Install more stuff.
+- Use build policy for stripping.
+
+* Wed Jan 17 2001 Eric Green <eric@estinc.com>
+- 1.2.11pre3
+- Removed patch, now use ./configure. 
+
+* Mon Nov 27 2000 Eric Green <eric@estinc.com>
+- 1.2.10
+- Fixed patching to use the portable.patch.
+
+* Tue Jul 25 2000 Eric Green <eric@estinc.com>
+- 1.2.8
+- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc. 
+
+* Thu Jun 6 2000 Eric Green <eric@estinc.com>
+- 1.2.7
+- Fixed single-drive Exabyte 220 special case.
+- Fixed ADIC DAT Autochanger special case.
+- Fixed mtx.spec to move the binaries to /sbin since we need root access
+
+* Fri May 12 2000 Kenneth Porter <shiva@well.com>
+- 1.2.6
+- Fixed 'eepos' stuff to use | rather than || (whoops!)
+- Accept a 4-byte element descriptor for the robot arm for certain older
+- autochangers. 
+
+* Mon May 8 2000 Kenneth Porter <shiva@well.com>
+- Spell sourceforge right so the link at rpmfind.net will work.
+
+* Thu May 4 2000 Kenneth Porter <shiva@well.com>
+- 1.2.5
+
+* Thu Oct 29 1998  Ian Macdonald <ianmacd@xs4all.nl>
+- moved mtx from /sbin to /bin, seeing as mt is also located there
+
+* Fri Oct 23 1998  Ian Macdonald <ianmacd@xs4all.nl>
+- first RPM release
diff --git a/mtx.spec.in b/mtx.spec.in
new file mode 100644 (file)
index 0000000..2f38e76
--- /dev/null
@@ -0,0 +1,105 @@
+Name: mtx
+Version: @@VERSION@@
+Release: 1
+Summary: SCSI media changer control program
+Copyright: Redistributable
+Group: Utilities/System
+Source0: http://prdownloads.sourceforge.net/mtx/%{name}-%{version}.tar.gz
+Url: http://%{name}.sourceforge.net
+BuildRoot: /var/tmp/%{name}-%{version}
+
+
+%description
+The MTX program controls the robotic mechanism in autoloaders and tape
+libraries such as the HP SureStore DAT 40x6, Exabyte EZ-17, and
+Exabyte 220. This program is also reported to work with a variety of other tape
+libraries and autochangers from Tandberg/Overland, Breece Hill, HP, and 
+Seagate.
+
+%prep
+%setup -q
+
+%build
+%configure
+make
+
+%install
+mkdir -p $RPM_BUILD_ROOT/sbin
+install mtx $RPM_BUILD_ROOT/sbin/mtx
+mkdir -p $RPM_BUILD_ROOT/usr/sbin
+install loaderinfo $RPM_BUILD_ROOT/usr/sbin/loaderinfo
+install scsitape $RPM_BUILD_ROOT/usr/sbin/scsitape
+install tapeinfo $RPM_BUILD_ROOT/usr/sbin/tapeinfo
+mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1
+install mtx.1 $RPM_BUILD_ROOT/%{_mandir}/man1/mtx.1
+install scsitape.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsitape.1
+install tapeinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/tapeinfo.1
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files 
+%defattr(-,root,root)
+%doc mtx.doc CHANGES README mtxl.README.html
+%doc COMPATABILITY FAQ LICENSE* TODO contrib
+%{_mandir}/man1/*
+/sbin/mtx
+/usr/sbin/*
+
+%changelog
+* Fri Nov 18 2001 Eric Green <eric@badtux.org>
+- 1.2.14
+- Moved changelog to end of spec file so it doesn't interfere
+- 
+
+* Wed Apr 18 2001 Kenneth Porter <shiva@well.com>
+- 1.2.12pre1
+- Need to create usr/sbin for install
+
+* Fri Mar 02 2001 Eric Green <eric@estinc.com>
+- 1.2.11pre6 
+- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin
+
+* Wed Feb 28 2001 Kenneth Porter <shiva@well.com>
+- 1.2.11pre5
+- Remove commented-out patch.
+- Use mandir FHS macro and configure macro.
+- Install more stuff.
+- Use build policy for stripping.
+
+* Wed Jan 17 2001 Eric Green <eric@estinc.com>
+- 1.2.11pre3
+- Removed patch, now use ./configure. 
+
+* Mon Nov 27 2000 Eric Green <eric@estinc.com>
+- 1.2.10
+- Fixed patching to use the portable.patch.
+
+* Tue Jul 25 2000 Eric Green <eric@estinc.com>
+- 1.2.8
+- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc. 
+
+* Thu Jun 6 2000 Eric Green <eric@estinc.com>
+- 1.2.7
+- Fixed single-drive Exabyte 220 special case.
+- Fixed ADIC DAT Autochanger special case.
+- Fixed mtx.spec to move the binaries to /sbin since we need root access
+
+* Fri May 12 2000 Kenneth Porter <shiva@well.com>
+- 1.2.6
+- Fixed 'eepos' stuff to use | rather than || (whoops!)
+- Accept a 4-byte element descriptor for the robot arm for certain older
+- autochangers. 
+
+* Mon May 8 2000 Kenneth Porter <shiva@well.com>
+- Spell sourceforge right so the link at rpmfind.net will work.
+
+* Thu May 4 2000 Kenneth Porter <shiva@well.com>
+- 1.2.5
+
+* Thu Oct 29 1998  Ian Macdonald <ianmacd@xs4all.nl>
+- moved mtx from /sbin to /bin, seeing as mt is also located there
+
+* Fri Oct 23 1998  Ian Macdonald <ianmacd@xs4all.nl>
+- first RPM release
diff --git a/mtxl.README.html b/mtxl.README.html
new file mode 100644 (file)
index 0000000..48d1537
--- /dev/null
@@ -0,0 +1,165 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+  <head>
+
+    <title>SCSI Media Changer and Backup Device Control System</title>
+
+  </head>
+
+  <body>
+<center>
+    <h1>SCSI Media Changer and Backup Device Control System</h1>
+</center>
+<p>
+[Also see the SourceForge <a href="http://sourceforge.net/projects/mtx">
+project page</a>.]
+<p>
+<i>mtx</i> is a set of low level driver programs to control features
+of SCSI backup related devices such as autoloaders, tape changers,
+media jukeboxes, and tape drives. It can also report much data,
+including serial numbers, maximum block sizes, and TapeAlert(tm)
+messages that most modern tape drives implement (to tell you the exact
+reason why a backup or restore failed), as well as do raw SCSI READ
+and WRITE commands to tape drives (not important on Linux, but
+important on Solaris due to the fact that the Solaris tape driver
+supports none of the additional features of tape drives invented after
+1988). <i>mtx</i> is designed to be a low level driver in a larger
+scripted backup solution, such as <a
+href="http://amanda.sourceforge.net">Amanda</a>.
+<i>mtx</i> is not
+supposed to itself be a high level interface to the SCSI devices that
+it controls.
+<p> 
+This version has the following features:
+
+  <ul>
+  <li> Will deal with LARGE media libraries (over a hundred elements).
+  <li> Supports multi-drive media changers such as the Exabyte 220 dual-
+drive tape library.
+  <li> Supports the 'invert' bit for optical jukeboxes that need that in
+order to flip their media.
+  <li> Supports the 'eepos' bits for libraries that need this to extend/retract
+their import/export tray. 
+  <li> Now supports import/export elements!
+   <li> Reports volume tags (bar codes) and "alternate volume tags"
+(whatever those are!) for those tape libraries
+that support them. 
+<li> Now runs under FreeBSD and at least Solaris 8. 
+
+<li> Now has a 'man' page!
+<li> The actual SCSI manipulation has been separated out into a library, so
+that you can create your own "C" programs that manipulate SCSI media changers
+directly. (Please note: this is under GPL, so any such programs will have
+to be under GPL also). 
+</ul>
+
+<p>
+ This program supposedly supports FreeBSD, Solaris, Linux, HP/UX, and
+IRIX. Tru64 Unix and VMS are probably irretrievably broken at this
+time. This program has been tested under FreeBSD, Solaris, and Linux,
+and there only with a limited set of hardware. See the COMPATIBILITY 
+list in the source code.
+</ul>
+<h2> Source Code </h2>
+The current source code is at:
+<ul>
+<li> <a href="http://sourceforge.net/project/showfiles.php?group_id=4626">Sourceforge Download Page for 'mtx'</a>
+</ul>
+RPMs may be available from the following place:
+<ul>
+<li> <a href="http://rpmfind.net/linux/RPM/mtx.html">RPMfind's 'mtx' page</a>
+</ul>
+A .spec file is now included in the 'mtx' distribution for building your
+own RPM's.
+<p>
+Note that RPMs
+are courtesy of <a href="http://www.sewingwitch.com/ken/">Kenneth Porter</a>,
+who should be contacted regarding rpm-related problems.
+<p>
+
+<h2> Known Bugs And Limitations </h2>
+<ul>
+<li> 
+       You  may  need  to  do a 'mt offline' (or equivalent for your OS)
+       on the tape drive to
+       eject the tape before you can issue the 'mtx unload'  command.
+The Exabyte EZ-17 and 220 in particular will happily
+       sit there snapping the robot arm's claws around  thin  air
+       trying to grab a tape that's not there.
+<li> 
+The 'next' command does not understand the 'invert' bit (i.e., does not
+recognize that for optical jukeboxes, the 'next' of side one is to unload,
+invert, and reload the same disk). It always advances to the next
+slot instead. 
+ <li>
+       For  some  Linux distributions, you may need to re-compile
+       the kernel to scan SCSI LUN's in order to detect the media
+       changer. Check /proc/scsi/scsi to see what's going on.
+
+<li> 
+       If you try to unload a tape to its 'source' slot, and said
+       slot is full, it will instead put the tape into the  first
+       empty  slot.  Unfortunately the list of empty slots is not
+       updated between commands on the command line,  so  if  you
+       try to unload another drive to a full 'source' slot during
+       the same invocation of 'mtx', it will try to unload to the
+       same  (no  longer  empty)  slot  and  will urp with a SCSI
+       error.
+
+<li> For big tape libraries (more than a couple dozen elements) this
+may set a big Allocation_Size in the SCSI command block for the
+REQUEST_ELEMENT_STATUS command. Some operating systems may not be able
+to handle this. Versions of Linux earlier than 2.2.6, in particular,
+may fail this request due to inability to find contiguous pages of
+memory for the SCSI transfer (later versions of Linux 'sg' device do
+scatter-gather so that this should no longer be a problem).
+
+<li> VMS and Tru64 support are probably irretrievably busted. 
+
+
+<li> This program  will only use the first arm of multiple-arm robots unless
+the robot re-maps all arms to one element ID. 
+
+<li> It has been reported that this program works on Solaris 7 using the 'sst'
+driver, and may work on Solaris 8 using the 'sgen' driver. 'sst' can
+be gotten from the Amanda contrib directory at 
+<a href="http://download.sourceforge.net/amanda/">http://download.sourceforge.net/amanda</a>.
+
+</ul>
+
+<h2> Philosophy </h2>
+The Unix philosophy is "many small tools chained together". <i>mtx</i> supplies
+those small tools so that you can create your own backup and 
+recovery tools by chaining
+<i>mtx</i> pieces together, whether it be with /bin/sh, Perl, Python, or
+CAML.
+
+
+<h2> Support </h2>
+<ul>
+<li>There is now a 'mtx' mailing list at <a href="http://sourceforge.net/projects/mtx/">http://sourceforge.net/projects/mtx/</a>. 
+<li>There is now a 'mtx' home page at <a href="http://mtx.sourceforge.net">http://mtx.sourceforge.net</a>.
+<li> There is now a FAQ that is part of the source code. Please read the
+FAQ first. 
+<li>Report problems to Eric Lee Green (<a
+href="mailto:eric@badtux.org">eric@badtux.org</a>). READ THE FAQ FIRST!
+</ul>
+
+<h2> See Also: </h2>
+<ul>
+<li>The man page for 'mtx'! (once you get it installed). 
+<li>T-10 SCSI Working Group home page at <a href="http://www.t10.org">www.t10.org</a>.
+<li>The Linux 'sg' SCSI generic driver home page at <a href="http://www.torque.net/sg/">http://www.torque.net/sg/</a>.
+<li> <a href="http://badtux.org/eric">The Home Page Of &lt;UL&gt; Tags Anonymous</a> Hi, my name is Eric, and I am addicted to the &lt;UL&gt; tag...
+</ul>
+    <hr>
+    <address>Maintained by <a href="mailto:eric@badtux.org">Eric Lee Green</a><br>
+ Hosted by <a href="http://www.valinux.com">VA Linux</a>'s <a href="http://www.sourceforge.net">SourceForge</a></address><br>
+
+
+<!-- Created: Fri Mar  3 12:19:38 MST 2000 -->
+<!-- hhmts start -->
+Last modified: Fri Nov 16 11:09:19 MST 2001
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/mtxl.c b/mtxl.c
new file mode 100644 (file)
index 0000000..1ad050d
--- /dev/null
+++ b/mtxl.c
@@ -0,0 +1,1316 @@
+/*  MTX -- SCSI Tape Attached Medium Changer Control Program
+
+  Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+$Date: 2002/02/05 16:51:11 $
+$Revision: 1.8.2.3 $
+
+  This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
+  extracted from mtx.c, plus some additional routines. 
+
+  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.
+*/
+
+
+/* FatalError: changed Feb. 2000 elg@estinc.com to eliminate a buffer
+   overflow :-(. That could be important if mtxl is SUID for some reason. 
+*/
+
+#include "mtx.h"
+#include "mtxl.h"
+
+/* #define DEBUG_MODE_SENSE 1 */
+/* #define DEBUG */
+/* #define DEBUG_SCSI */
+
+/* zap the following define when we finally add real import/export support */
+#define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
+
+/* First, do some SCSI routines: */
+
+/* the camlib is used on FreeBSD. */
+#if HAVE_CAMLIB_H
+#  include "scsi_freebsd.c"
+#endif
+
+/* the scsi_ctl interface is used on HP/UX. */
+#if HAVE_SYS_SCSI_CTL_H
+#  include "scsi_hpux.c"
+#endif
+
+/* the 'sg' interface is used on Linux. */
+#if HAVE_SCSI_SG_H
+#  include "scsi_linux.c"
+#endif
+
+/* The 'uscsi' interface is used on Solaris. */
+#if HAVE_SYS_SCSI_IMPL_USCSI_H
+#  include "scsi_sun.c"
+#endif
+
+/* The 'tm_buf' interface, as used on AIX. */
+#ifdef HAVE_SYS_SCSI_H
+#  include "scsi_aix.c"
+#endif
+
+/* The 'dslib' interface is used on SGI. */
+#if HAVE_DSLIB_H
+#  include "scsi_sgi.c"
+#endif
+
+/* Hmm, dunno what to do about Digital Unix at the moment. */
+#ifdef DIGITAL_UNIX
+#include "du/scsi.c"
+#endif
+
+
+#ifdef VMS
+#include "[.vms]scsi.c"
+#endif
+
+extern char *argv0; /* something to let us do good error messages. */
+
+/* Now for some low-level SCSI stuff: */
+
+Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense) {
+  Inquiry_T *Inquiry;
+  CDB_T CDB;
+
+  Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));  
+  
+  CDB[0] = 0x12;               /* INQUIRY */
+  CDB[1] = 0;                  /* EVPD = 0 */
+  CDB[2] = 0;                  /* Page Code */
+  CDB[3] = 0;                  /* Reserved */
+  CDB[4] = sizeof(Inquiry_T);  /* Allocation Length */
+  CDB[5] = 0;                  /* Control */
+
+  /* set us a very short timeout, sigh... */
+  SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
+
+  if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
+                         Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
+    {
+      free(Inquiry);
+      return NULL;  /* sorry! */
+    }
+  return Inquiry;
+}
+
+
+int BigEndian16(unsigned char *BigEndianData)
+{
+  return (BigEndianData[0] << 8) + BigEndianData[1];
+}
+
+
+int BigEndian24(unsigned char *BigEndianData)
+{
+  return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
+}
+
+
+
+
+
+void FatalError(char *ErrorMessage, ...)
+{
+#define FORMAT_BUF_LEN 1024
+  char FormatBuffer[FORMAT_BUF_LEN];
+  char *SourcePointer;
+  char *TargetPointer = FormatBuffer;
+  int Character, LastCharacter = '\0';
+  int numchars = 0;
+
+  va_list ArgumentPointer;
+  va_start(ArgumentPointer, ErrorMessage);
+  /*  SourcePointer = "mtx: "; */
+  sprintf(TargetPointer,"%s: ",argv0);
+  numchars=strlen(TargetPointer);
+  
+  /* while ((Character = *SourcePointer++) != '\0') { */
+  /*  *TargetPointer++ = Character;                   */
+  /*  numchars++;                                     */
+  /*   }                                              */
+  
+  while ((Character = *ErrorMessage++) != '\0')
+    if (LastCharacter == '%')
+      {
+       if (Character == 'm')
+         {
+           SourcePointer = strerror(errno);
+           while ((Character = *SourcePointer++) != '\0') {
+             *TargetPointer++ = Character;
+             numchars++;
+             if (numchars==FORMAT_BUF_LEN-1) break;  
+           }
+           if (numchars==FORMAT_BUF_LEN-1) break;  /* break outer loop... */
+         }
+       else
+         {
+           *TargetPointer++ = '%';
+           *TargetPointer++ = Character;
+           numchars++;
+           if (numchars==FORMAT_BUF_LEN-1) break;
+         }
+       LastCharacter = '\0';
+      }
+    else
+      {
+       if (Character != '%') {
+         *TargetPointer++ = Character;
+         numchars++;
+         if (numchars==FORMAT_BUF_LEN-1) break;
+       }
+       LastCharacter = Character;
+      }
+  *TargetPointer = '\0';  /* works even if we had to break from above... */
+  vfprintf(stderr, FormatBuffer, ArgumentPointer);
+  va_end(ArgumentPointer);
+#ifndef VMS
+  exit(1);
+#else
+  sys$exit(VMS_ExitCode);
+#endif
+}
+
+/* This is a really slow and stupid 'bzero' implementation'... */
+void slow_bzero(char *buffer, int numchars) {
+  while (numchars--) *buffer++ = 0;
+}
+
+
+/* malloc some memory while checking for out-of-memory conditions. */
+void *xmalloc(size_t Size)
+{
+  void *Result = (void *) malloc(Size);
+  if (Result == NULL)
+    FatalError("cannot allocate %d bytes of memory\n", Size);
+  return Result;
+}
+
+/* malloc some memory, zeroing it too: */
+void *xzmalloc(size_t Size)
+{
+  void *Result=(void *)xmalloc(Size);
+  
+  slow_bzero(Result,Size);
+  return Result;
+}
+
+
+int min(int x, int y)
+{
+  return (x < y ? x : y);
+}
+
+
+int max(int x, int y)
+{
+  return (x > y ? x : y);
+}
+
+
+/* Routine to inventory the library. Needed by, e.g., some Breece Hill
+ * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
+ * has no parameters, such as a range to scan :-(. 
+ */
+
+int Inventory(DEVICE_TYPE MediumChangerFD) {
+  CDB_T CDB;
+  RequestSense_T RequestSense;
+  /* okay, now for the command: */
+  CDB[0]=0x07; 
+  CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+  /* set us a very long timeout, sigh... */
+  SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
+  
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
+    return -1;  /* could not do! */
+  }
+  return 0; /* did do! */
+}
+
+/* Routine to send an UNLOAD from the SSC spec to a device. This can be
+ * used either to eject a tape (when sent to a tape drive), or to eject
+ * a magzine (on some Seagate changers, when sent to LUN 1 ). 
+ */
+
+int Eject(DEVICE_TYPE fd) {
+  CDB_T CDB;
+  RequestSense_T RequestSense;
+  /* okay, now for the command: */
+  
+  CDB[0]=0x1b;
+  CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+  
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
+    return -1;  /* could not do! */
+  }
+  return 0; /* did do! */
+}  
+
+/* Routine to read the Mode Sense Element Address Assignment Page */
+/* We try to read the page. If we can't read the page, we return NULL.
+ * Our caller really isn't too worried about why we could not read the
+ * page, it will simply default to some kind of default values. 
+ */ 
+ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD) {
+  CDB_T CDB;
+  ElementModeSense_T *retval;  /* processed SCSI. */
+  unsigned char input_buffer[136];
+  ElementModeSensePage_T *sense_page; /* raw SCSI. */
+  RequestSense_T RequestSense;       /* see what retval of raw SCSI was... */
+  
+  /* okay, now for the command: */
+  CDB[0]=0x1a; /* Mode Sense(6) */
+  CDB[1]=0; 
+  CDB[2]=0x1d; /* Mode Sense Element Address Assignment Page */
+  CDB[3]=0;
+  CDB[4]=136; /* allocation_length... */
+  CDB[5]=0;
+
+  /* clear the data buffer: */
+  slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
+  slow_bzero((char *)input_buffer,sizeof(input_buffer));
+
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
+                         &input_buffer,sizeof(input_buffer),&RequestSense) != 0) {
+#ifdef DEBUG_MODE_SENSE
+    fprintf(stderr,"Could not execute mode sense...\n");
+    fflush(stderr);
+#endif
+    return NULL; /* sorry, couldn't do it. */
+  }
+
+  /* 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);
+  }
+#endif
+  /* first, skip past: mode data header, and block descriptor header if any */
+  sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
+
+#ifdef DEBUG_MODE_SENSE
+  fprintf(stderr,"*sense_page=%x  %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
+  fflush(stderr);
+#endif  
+
+  retval=(ElementModeSense_T *) xzmalloc(sizeof(ElementModeSense_T));
+
+  /* Remember that all SCSI values are big-endian: */
+  retval->MediumTransportStart=(((int)sense_page->MediumTransportStartHi)<<8) +
+    sense_page->MediumTransportStartLo;
+  retval->NumMediumTransport=(((int)(sense_page->NumMediumTransportHi))<<8) +
+    sense_page->NumMediumTransportLo;
+
+  /* HACK! Some HP autochangers don't report NumMediumTransport right! */
+  /* ARG! TAKE OUT THE %#@!# HACK! */
+#ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
+  if (!retval->NumMediumTransport) {
+    retval->NumMediumTransport=1;
+  }
+#endif
+
+#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);
+  fprintf(stderr,"rawNumTransport=%d %d  rawNumDataTransfer=%d %d\n",
+         sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
+         sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
+  fflush(stderr);
+#endif
+
+  retval->StorageStart=(((int)(sense_page->StorageStartHi))<<8) +
+    sense_page->StorageStartLo;
+  retval->NumStorage=(((int)(sense_page->NumStorageHi))<<8) + 
+    sense_page->NumStorageLo;
+  retval->ImportExportStart=(((int)(sense_page->ImportExportStartHi))<<8)+
+    sense_page->ImportExportStartLo; 
+  retval->NumImportExport=(((int)(sense_page->NumImportExportHi))<<8)+
+    sense_page->NumImportExportLo;
+  retval->DataTransferStart=(((int)(sense_page->DataTransferStartHi))<<8)+
+    sense_page->DataTransferStartLo;
+  retval->NumDataTransfer=(((int)(sense_page->NumDataTransferHi))<<8)+
+    sense_page->NumDataTransferLo;
+
+  /* allocate a couple spares 'cause some HP autochangers and maybe others
+   * don't properly report the robotics arm(s) count here... 
+   */
+  retval->NumElements=retval->NumStorage+retval->NumImportExport+
+    retval->NumDataTransfer+retval->NumMediumTransport;
+
+  retval->MaxReadElementStatusData=(sizeof(ElementStatusDataHeader_T) +
+                                   4 * sizeof(ElementStatusPage_T) +
+                                   (retval->NumElements) *
+                                     sizeof(TransportElementDescriptor_T));
+
+
+#ifdef IMPORT_EXPORT_HACK
+  retval->NumStorage=retval->NumStorage+retval->NumImportExport;
+#endif
+
+#ifdef DEBUG_MODE_SENSE
+  fprintf(stderr,"NumMediumTransport=%d\n",retval->NumMediumTransport);
+  fprintf(stderr,"NumStorage=%d\n",retval->NumStorage);
+  fprintf(stderr,"NumImportExport=%d\n",retval->NumImportExport);
+  fprintf(stderr,"NumDataTransfer=%d\n",retval->NumDataTransfer);
+  fprintf(stderr,"MaxReadElementStatusData=%d\n",retval->MaxReadElementStatusData);
+  fprintf(stderr,"NumElements=%d\n",retval->NumElements);
+  fflush(stderr);
+#endif
+  
+  return retval;
+}
+
+static void FreeElementData(ElementStatus_T *data)
+{
+  free(data->DataTransferElementAddress);
+  free(data->DataTransferElementSourceStorageElementNumber);
+  free(data->DataTransferPrimaryVolumeTag);
+  free(data->DataTransferAlternateVolumeTag);
+  free(data->PrimaryVolumeTag);
+  free(data->AlternateVolumeTag);
+  free(data->StorageElementAddress);
+  free(data->StorageElementIsImportExport);
+  free(data->StorageElementFull);
+  free(data->DataTransferElementFull);
+}
+  
+       
+
+/* allocate data  */
+
+static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense) {
+  ElementStatus_T *retval;
+
+  retval=(ElementStatus_T *) xzmalloc(sizeof(ElementStatus_T));
+
+  /* now for the invidual component arrays.... */
+
+  retval->DataTransferElementAddress = (int *) xzmalloc(sizeof(int)*
+                                    (mode_sense->NumDataTransfer + 1));
+  retval->DataTransferElementSourceStorageElementNumber = 
+    (int *) xzmalloc(sizeof(int)*(mode_sense->NumDataTransfer+1));
+  retval->DataTransferPrimaryVolumeTag=
+    (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
+  retval->DataTransferAlternateVolumeTag=
+    (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
+  retval->PrimaryVolumeTag=
+    (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
+  retval->AlternateVolumeTag=
+    (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
+  retval->StorageElementAddress=
+    (int *)xzmalloc(sizeof(int)*(mode_sense->NumStorage+1));
+  retval->StorageElementIsImportExport=
+    (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));    
+  retval->StorageElementFull=
+    (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));    
+  retval->DataTransferElementFull=
+    (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumDataTransfer+1));
+  
+  return retval; /* sigh! */
+}
+
+
+void copy_barcode(unsigned char *src, unsigned char *dest) {
+  int i;
+  
+  for (i=0; i < 37; i++) {
+    *dest++ = *src++;
+  }
+  *dest=0; /* null-terminate, sigh. */ 
+}
+
+
+/* This #%!@# routine has more parameters than I can count! */
+unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
+                                       RequestSense_T *RequestSense,
+                                       Inquiry_T *inquiry_info, 
+                                       SCSI_Flags_T *flags,
+                                       int ElementStart,
+                                       int NumElements,
+                                       int NumBytes
+                                       ) {
+
+  CDB_T CDB;
+  boolean is_attached = false;
+
+  unsigned char *DataBuffer; /* size of data... */
+
+#ifdef HAVE_GET_ID_LUN
+  scsi_id_t *scsi_id;
+#endif
+  if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
+    is_attached=true;
+  }
+  if (flags->no_attached) { /* override, sigh */ 
+    is_attached=false;
+  }
+
+  DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
+
+  slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+#ifdef HAVE_GET_ID_LUN
+  scsi_id = SCSI_GetIDLun(MediumChangerFD);
+#endif
+
+
+  CDB[0] = 0xB8;               /* READ ELEMENT STATUS */
+  if (is_attached) {
+    CDB[0] = 0xB4;  /* whoops, READ_ELEMENT_STATUS_ATTACHED! */ 
+  }
+#ifdef HAVE_GET_ID_LUN
+  CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;  /* Lun + VolTag + Type code */
+  free(scsi_id);
+#else
+  CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;             /* Element Type Code = 0, VolTag = 1 */
+#endif
+  CDB[2] = (ElementStart >> 8) & 0xff; /* Starting Element Address MSB */
+  CDB[3] = ElementStart & 0xff;                /* Starting Element Address LSB */
+
+
+  CDB[4]= (NumElements >> 8) & 0xff;     /* Number Of Elements MSB */
+  CDB[5]= NumElements & 0xff ;        /* Number of elements LSB */
+
+  /*  CDB[5]=127; */ /* test */
+#ifdef DEBUG
+  fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
+#endif
+  CDB[6] = 0;                  /* Reserved */
+
+  CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
+  CDB[8]= (NumBytes >> 8) & 0xff;
+  CDB[9]= NumBytes & 0xff;   /* allocation length LSB */
+
+  CDB[10] = 0;                 /* Reserved */
+  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);
+  }
+#endif
+
+  if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+                         DataBuffer,NumBytes, RequestSense) != 0){
+    /* 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
+       w/bar code reader and did not have one, thus must re-issue the request
+       w/out barcode :-(.
+    */
+    
+    /* Okay, most autochangers and tape libraries set a sense key here if
+     * they do not have a bar code reader. For some reason, the ADIC DAT
+     * uses a different sense key? Let's retry w/o bar codes for *ALL*
+     * sense keys, sigh sigh sigh. 
+     */
+    
+    if ( RequestSense->SenseKey > 1 ) {
+      /* we issued a code requesting bar codes, there is no bar code reader? */
+      /* clear out our sense buffer first... */
+      slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+
+      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);
+      }
+#endif
+      
+      if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+                             DataBuffer, NumBytes, RequestSense) != 0){
+       free(DataBuffer);
+       return NULL;
+      }
+    } else {
+      free(DataBuffer);
+      return NULL;
+    }
+  }
+
+#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++) {
+      fprint(stderr,"%02x ",DataBuffer[i]);
+    }
+    fprintf(stderr,"\n");
+  }
+#endif  
+  return DataBuffer; /* we succeeded! */
+}
+
+/******************* ParseElementStatus ***********************************/
+/* This does the actual grunt work of parsing element status data. It fills
+ * in appropriate pieces of its input data. It may be called multiple times
+ * while we are gathering element status.
+ */
+
+static void ParseElementStatus(int *EmptyStorageElementAddress,
+                              int *EmptyStorageElementCount,
+                              unsigned char *DataBuffer,
+                              ElementStatus_T *ElementStatus,
+                              ElementModeSense_T *mode_sense
+
+                              )   {
+    
+    unsigned char *DataPointer=DataBuffer;
+    TransportElementDescriptor_T TEBuf;
+    TransportElementDescriptor_T *TransportElementDescriptor;
+    ElementStatusDataHeader_T *ElementStatusDataHeader;
+    Element2StatusPage_T *ElementStatusPage;
+    Element2StatusPage_T ESBuf;
+    int ElementCount;
+    int TransportElementDescriptorLength;
+    int BytesAvailable; 
+    
+    ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
+
+    
+
+  ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
+  DataPointer += sizeof(ElementStatusDataHeader_T);
+  ElementCount =
+    BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
+#ifdef DEBUG
+      fprintf(stderr,"ElementCount=%d\n",ElementCount);
+      fflush(stderr);
+#endif
+  while (ElementCount > 0)
+    {
+#ifdef DEBUG
+      int got_element_num=0;
+      
+      fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
+      got_element_num++;
+#endif
+
+      memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
+      ElementStatusPage = &ESBuf;
+      DataPointer += sizeof(ElementStatusPage_T);
+      TransportElementDescriptorLength =
+       BigEndian16(ElementStatusPage->ElementDescriptorLength);
+      if (TransportElementDescriptorLength <
+         sizeof(TransportElementDescriptorShort_T)) {
+
+       /* Foo, Storage Element Descriptors can be 4 bytes?! */
+       if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
+             ElementStatusPage->ElementTypeCode != StorageElement &&
+             ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
+            TransportElementDescriptorLength < 4) {
+#ifdef DEBUG
+         fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
+#endif
+         FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
+       } 
+       
+      }
+#ifdef DEBUG
+      fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
+#endif
+      BytesAvailable =
+       BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
+      if (BytesAvailable <= 0) {
+             ElementCount--;
+      }
+      while (BytesAvailable > 0)
+       {
+         /* TransportElementDescriptor =
+            (TransportElementDescriptor_T *) DataPointer; */
+          memcpy(&TEBuf, DataPointer, 
+                       (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
+                               TransportElementDescriptorLength  :
+                               sizeof(TEBuf));
+          TransportElementDescriptor = &TEBuf;
+
+         DataPointer += TransportElementDescriptorLength;
+         BytesAvailable -= TransportElementDescriptorLength;
+         ElementCount--;
+         switch (ElementStatusPage->ElementTypeCode)
+           {
+           case MediumTransportElement:
+             ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
+#ifdef DEBUG
+             fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress); 
+#endif
+             break;
+             /* we treat ImportExport elements as if they were normal
+              * storage elements now, sigh...
+              */
+           case ImportExportElement:
+             ElementStatus->ImportExportCount++;
+#ifdef DEBUG
+             fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
+#endif
+             ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
+             /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
+              * to run on into the case for a Storage Element! 
+              */
+           case StorageElement:
+#ifdef DEBUG
+             fprintf(stderr,"StorageElementCount=%d  ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
+#endif
+             ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
+               BigEndian16(TransportElementDescriptor->ElementAddress);
+             ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
+               TransportElementDescriptor->Full;
+#ifdef DEBUG
+             fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
+#endif
+             if (!TransportElementDescriptor->Full)
+               {
+                 EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
+                   ElementStatus->StorageElementCount; /* slot idx. */
+                   /*   ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
+               }
+             if ( (TransportElementDescriptorLength >  11) && 
+                  (ElementStatusPage->VolBits & E2_AVOLTAG)) {
+               copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+                            ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
+             } else {
+               ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0;  /* null string. */;
+             } 
+             if ( (TransportElementDescriptorLength > 11) && 
+                  (ElementStatusPage->VolBits & E2_PVOLTAG)) {
+               copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+                            ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
+             } else {
+               ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
+             }
+             
+             ElementStatus->StorageElementCount++;
+             /* Note that the original mtx had no check here for 
+                buffer overflow, though some drives might mistakingly
+                do one... 
+             */ 
+             if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
+               fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
+                       mode_sense->NumStorage,
+                       ElementStatus->StorageElementCount);
+               fflush(stderr);
+               return; /* we're done :-(. */
+             }
+
+
+             break;
+
+
+           case DataTransferElement:
+             /* tape drive not installed, go back to top of loop */
+
+             /* if (TransportElementDescriptor->Except) continue ; */
+
+             /* Note: This is for Exabyte tape libraries that improperly
+                report that they have a 2nd tape drive when they don't. We
+                could generalize this in an ideal world, but my attempt to
+                do so failed with dual-drive Exabyte tape libraries that
+                *DID* have the second drive. Sigh. 
+             */
+             /* No longer need this test due to code restructuring? */
+             /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 && 
+                 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04) 
+               continue;
+             */ 
+             /* generalize it. Does it work? Let's try it! */
+             /* No, dammit, following does not work on dual-drive Exabyte
+                'cause if a tape is in the drive, it sets the AdditionalSense
+                 code to something (sigh).
+             */
+             /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
+               continue;
+             */ 
+             
+
+                 
+             ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
+               BigEndian16(TransportElementDescriptor->ElementAddress);
+             ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] = 
+               TransportElementDescriptor->Full;
+             ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
+               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);
+           }
+       }
+    }
+}
+
+/********************* Real ReadElementStatus ********************* */
+
+/*
+ * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
+ * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
+ * under Linux, which gets around the @#%@ 4k buffer size in Linux. 
+ * We still have the restriction that Linux cuts off the last two
+ * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
+ * verbose widget won't work :-(. 
+ * We now look for that "attached" bit in the inquiry_info to see whether
+ * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
+ * look at the device type in the inquiry_info to see whether it is a media
+ * changer or tape device, and if it's a media changer device, we ignore the
+ * attached bit (one beta tester found an old 4-tape DAT changer that set
+ * the attached bit for both the tape device AND the media changer device). 
+
+*/
+
+ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
+  ElementStatus_T *ElementStatus;
+  
+  unsigned char *DataBuffer; /* size of data... */
+  
+  int EmptyStorageElementCount=0;
+  int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
+  
+  int empty_idx=0;
+  int invalid_sources=0;
+  boolean is_attached = false;
+  int i,j;
+  
+  ElementModeSense_T *mode_sense = NULL;
+  
+  if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
+    is_attached=true;
+  }
+  if (flags->no_attached) { /* override, sigh */ 
+    is_attached=false;
+  }
+  if (!is_attached) {
+    mode_sense=ReadAssignmentPage(MediumChangerFD);
+  }
+  if (!mode_sense) {
+    mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
+    mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
+    mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
+    mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
+    mode_sense->MaxReadElementStatusData=  (sizeof(ElementStatusDataHeader_T)
+                                           + 3 * sizeof(ElementStatusPage_T)
+                                           + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
+                                           * sizeof(TransportElementDescriptor_T));
+    
+    /* don't care about the others anyhow at the moment... */
+  }
+  
+  ElementStatus=AllocateElementData(mode_sense);
+
+  /* Now to initialize it (sigh).  */
+  ElementStatus->StorageElementCount = 0;
+  ElementStatus->DataTransferElementCount=0;
+    
+  
+  /* first, allocate some empty storage stuff: Note that we pass this
+   * down to ParseElementStatus (sigh!) 
+   */
+  
+  EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
+  for (i=0;i<mode_sense->NumStorage;i++) {
+    EmptyStorageElementAddress[i]=-1;
+  }
+  
+  /* Okay, now to send some requests for the various types of stuff: */
+
+  /* -----------STORAGE ELEMENTS---------------- */
+  /* Let's start with storage elements: */
+
+  for (i=0;i<mode_sense->NumDataTransfer;i++) {
+    /* initialize them to an illegal # so that we can fix later... */
+    ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1; 
+  }
+  
+
+  flags->elementtype=StorageElement; /* sigh! */
+  DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+                                     inquiry_info,flags,
+                                     mode_sense->StorageStart,
+                                     /* adjust for import/export. */
+                                     mode_sense->NumStorage-mode_sense->NumImportExport,
+                                     mode_sense->MaxReadElementStatusData);
+  if (!DataBuffer) {
+    /* darn. Free up stuff and return. */
+    /****FIXME**** do a free on element data! */
+    FreeElementData(ElementStatus);
+    return NULL; 
+  } 
+
+  ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+                    DataBuffer,ElementStatus,mode_sense);
+
+  free(DataBuffer); /* sigh! */
+
+  /* --------------IMPORT/EXPORT--------------- */
+  /* Next let's see if we need to do Import/Export: */
+  if (mode_sense->NumImportExport > 0) {
+    flags->elementtype=ImportExportElement;
+    DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+                                       inquiry_info,flags,
+                                       mode_sense->ImportExportStart,
+                                       mode_sense->NumImportExport,
+                                       mode_sense->MaxReadElementStatusData);
+    
+    if (!DataBuffer) {
+      /* darn. Free up stuff and return. */
+      /****FIXME**** do a free on element data! */
+      FreeElementData(ElementStatus);
+      return NULL; 
+    } 
+    ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+                    DataBuffer,ElementStatus,mode_sense);
+
+  }
+  
+
+
+  /* ----------------- DRIVES ---------------------- */
+
+
+  flags->elementtype=DataTransferElement; /* sigh! */
+  DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+                                     inquiry_info,flags,
+                                     mode_sense->DataTransferStart,
+                                     mode_sense->NumDataTransfer,
+                                     mode_sense->MaxReadElementStatusData);
+  if (!DataBuffer) {
+    /* darn. Free up stuff and return. */
+    /****FIXME**** do a free on element data! */
+    FreeElementData(ElementStatus);
+    return NULL; 
+  } 
+
+  ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+                    DataBuffer,ElementStatus,mode_sense);
+
+  free(DataBuffer); /* sigh! */
+
+
+  /* ----------------- Robot Arm(s) -------------------------- */
+
+    /* grr, damned brain dead HP doesn't report that it has any! */
+  if (!mode_sense->NumMediumTransport) { 
+    ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
+  } else {
+     flags->elementtype=MediumTransportElement; /* sigh! */
+     DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+                                     inquiry_info,flags,
+                                     mode_sense->MediumTransportStart,
+                                     1, /* only get 1, sigh. */
+                                     mode_sense->MaxReadElementStatusData);
+     if (!DataBuffer) {
+       /* darn. Free up stuff and return. */
+       /****FIXME**** do a free on element data! */
+       FreeElementData(ElementStatus);
+       return NULL; 
+     } 
+   
+     ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+                    DataBuffer,ElementStatus,mode_sense);
+
+     free(DataBuffer); /* sigh! */
+  }
+
+  /*---------------------- Sanity Checking ------------------- */
+
+  if (!ElementStatus->DataTransferElementCount)
+    FatalError("no Data Transfer Element reported\n");
+  if (ElementStatus->StorageElementCount == 0)
+    FatalError("no Storage Elements reported\n");
+
+
+  /* ---------------------- Reset SourceStorageElementNumbers ------- */
+
+  /* okay, re-write the SourceStorageElementNumber code  *AGAIN*.
+   * Pass1:
+   *   Translate from raw element # to our translated # (if possible).
+   * First, check the SourceStorageElementNumbers against the list of 
+   * filled slots. If the slots indicated are empty, we accept that list as
+   * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
+   *
+   * Pass2:
+   *
+   * If we had some invalid (or unknown) SourceStorageElementNumbers
+   * then we must search for free slots, and assign SourceStorageElementNumbers
+   * to those free slots. We happen to already built a list of free
+   * slots as part of the process of reading the storage element numbers
+   * from the tape. So that's easy enough to do! 
+   */
+
+#ifdef DEBUG_TAPELIST
+  fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
+  if (EmptyStorageElementCount) {
+     for (i=0; i<EmptyStorageElementCount; i++) {
+         fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
+     }
+  }
+#endif
+
+  /* Okay, now we re-assign origin slots if the "real" origin slot
+   * 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;
+      }
+    }
+  }
+
+#ifdef DEBUG_TAPELIST
+   fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
+#endif
+
+  /* Pass2: */
+  /* okay, we have invalid sources, so let's see what they should be: */
+  /* Note: If EmptyStorageElementCount is < # of drives, the leftover
+   * drives will be assigned a -1 (see the initialization loop for
+   * EmptyStorageElementAddress above), which will be reported as "slot 0"
+   * 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]) {
+#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);
+#endif
+              
+       ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
+         EmptyStorageElementAddress[empty_idx++];
+      }
+    }
+  }
+
+  /* and done! */      
+  free(mode_sense);
+  free(EmptyStorageElementAddress);
+  return ElementStatus;
+  
+}
+
+/*************************************************************************/
+
+
+
+/* Now the actual media movement routine! */
+RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
+                      int DestinationAddress, 
+                      ElementStatus_T *ElementStatus,
+                      Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
+{
+  RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+  CDB_T CDB;
+  if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {  /* if using the ATTACHED API */
+    CDB[0]=0xA7;   /* MOVE_MEDIUM_ATTACHED */
+  } else {
+    CDB[0] = 0xA5;             /* MOVE MEDIUM */
+  }
+  CDB[1] = 0;                  /* Reserved */
+  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[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
+  CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
+  CDB[8] = 0;                  /* Reserved */
+  CDB[9] = 0;                  /* Reserved */
+  if (flags->invert) {
+    CDB[10] = 1;                       /* Reserved */
+  } else {
+    CDB[10] = 0;
+  }
+  /* eepos controls the tray for import/export elements, sometimes. */
+  CDB[11] = 0 | (flags->eepos <<6);                    /* Control */
+  
+  if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
+                         NULL, 0, RequestSense) != 0) {
+    return RequestSense;
+  }
+  free(RequestSense);
+  return NULL; /* success! */
+}
+
+/* for Linux, this creates a way to do a short erase... the @#$%@ st.c
+ * driver defaults to doing a long erase!
+ */
+
+RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
+  RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+  CDB_T CDB;
+
+  CDB[0]=0x19;
+  CDB[1]=0;  /* Short! */
+  CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+  if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
+                         NULL, 0, RequestSense) != 0) {
+    return RequestSense;
+  }
+  free(RequestSense);
+  return NULL;  /* Success! */
+}  
+
+
+#ifdef LONG_PRINT_REQUEST_SENSE
+
+static char *sense_keys[] = {
+  "No Sense",  /* 00 */
+  "Recovered Error", /* 01 */
+  "Not Ready",  /* 02 */
+  "Medium Error",  /* 03 */
+  "Hardware Error",  /* 04 */
+  "Illegal Request",  /* 05 */
+  "Unit Attention", /* 06 */
+  "Data Protect",   /* 07 */
+  "Blank Check", /* 08 */
+  "0x09",   /* 09 */
+  "0x0a",   /* 0a */
+  "Aborted Command", /* 0b */
+  "0x0c",  /* 0c */
+  "Volume Overflow", /* 0d */
+  "Miscompare",   /* 0e */
+  "0x0f"  /* 0f */
+};
+
+static char Yes[]="yes";
+static char No[]="no";
+
+void PrintRequestSense(RequestSense_T *RequestSense)
+{
+  char *msg;
+
+  fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
+  fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
+  if (RequestSense->ErrorCode==0x70) { 
+    msg = "Current" ;
+  } else if (RequestSense->ErrorCode==0x71) { 
+    msg = "Deferred" ;
+  } else {
+    msg = "Unknown?!" ;
+  }
+  fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
+  fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
+  fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
+  fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
+  fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
+  if (RequestSense->Valid) {
+    fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
+  } 
+  fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
+  fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
+  if (RequestSense->SKSV) {
+    fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
+  }
+  fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
+  fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
+  fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
+  fflush(stderr);
+  if (RequestSense->BPV || RequestSense -> SKSV) {
+    fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
+  }
+}
+
+
+#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");
+}
+
+#endif
+
+/* $Date: 2002/02/05 16:51:11 $
+ * $Log: mtxl.c,v $
+ * Revision 1.8.2.3  2002/02/05 16:51:11  elgreen
+ * mtx 1.2.16pre3
+ *
+ * Revision 1.8.2.2  2002/01/22 16:27:47  elgreen
+ * Handle too-big transport element descriptor lengths
+ *
+ * Revision 1.8.2.1  2002/01/17 22:24:35  elgreen
+ * Handle ADIC silliness of saying that it has 1 import/export element whose
+ * descriptor is 0 bytes in length
+ *
+ * Revision 1.8  2001/06/25 23:06:22  elgreen
+ * Readying this for 1.2.13 release
+ *
+ * Revision 1.7  2001/06/25 04:56:35  elgreen
+ * Kai to the rescue *again* :-)
+ *
+ * Revision 1.6  2001/06/24 07:02:01  elgreen
+ * Kai's fix was better than mine.
+ *
+ * Revision 1.5  2001/06/24 06:59:19  elgreen
+ * Kai found bug in the barcode backoff.
+ *
+ * Revision 1.4  2001/06/15 18:56:54  elgreen
+ * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
+ *
+ * Revision 1.3  2001/06/15 14:26:09  elgreen
+ * Fixed brain-dead case of HP loaders that report they have no robot arms.
+ *
+ * Revision 1.2  2001/06/09 17:26:26  elgreen
+ * Added 'nobarcode' command to mtx (to skip the initial request asking for
+ * barcodes for mtx status purposes).
+ *
+ * Revision 1.1.1.1  2001/06/05 17:10:25  elgreen
+ * Initial import into SourceForge
+ *
+ * Revision 1.29  2001/05/01 01:39:23  eric
+ * Remove the Exabyte special case code, which seemed to be barfing me :-(.
+ *
+ * Revision 1.28  2001/04/28 00:03:10  eric
+ * update for 1.2.12.
+ *
+ * Revision 1.27  2001/04/18 19:27:39  eric
+ * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
+ *
+ * Revision 1.26  2001/04/18 18:55:44  eric
+ * turn too many storage elements into a warning rather than a fatal error (sigh!)
+ *
+ * Revision 1.25  2001/04/18 18:46:54  eric
+ * $%!@# "C" precedence rules.
+ *
+ * Revision 1.24  2001/04/18 17:58:04  eric
+ * initialize EmptyStorageElementCount (sigh)
+ *
+ * Revision 1.23  2001/04/18 16:55:03  eric
+ * hopefully get the initialization for element status straigtened out.
+ *
+ * Revision 1.22  2001/04/18 16:32:59  eric
+ * Cleaned up all -Wall messages.
+ *
+ * Revision 1.21  2001/04/18 16:08:08  eric
+ * after re-arranging & fixing bugs
+ *
+ * Revision 1.20  2001/04/17 21:28:43  eric
+ * mtx 1.2.11
+ *
+ * Revision 1.19  2001/04/02 20:12:48  eric
+ * checkin, mtx 1.2.11pre6
+ *
+ * Revision 1.18  2001/03/06 01:37:05  eric
+ * Solaris changes
+ *
+ * Revision 1.17  2001/02/26 20:56:12  eric
+ * added debugging, removed Makefile
+ *
+ * Revision 1.16  2001/02/19 23:22:28  eric
+ * Add E2 bits
+ *
+ * Revision 1.15  2001/02/19 23:02:04  eric
+ * Apply SPARC patch.
+ *
+ * Revision 1.14  2001/01/26 15:58:10  eric
+ * Make work on solaris?
+ *
+ * Revision 1.13  2001/01/17 19:39:01  eric
+ * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
+ * routines to use (this way, if we ever port to an OS that has an identical
+ * SCSI interface to one of the supported OS's, we don't have to do anything
+ * special!).
+ *
+ * Revision 1.12  2001/01/17 16:36:26  eric
+ * added date & revision strings as needed
+ *
+ * Revision 1.11  2000/11/30 20:35:18  eric
+ * Added Erase command.
+ *
+ */
diff --git a/mtxl.h b/mtxl.h
new file mode 100644 (file)
index 0000000..560c96f
--- /dev/null
+++ b/mtxl.h
@@ -0,0 +1,73 @@
+/* 
+  MTX -- SCSI Tape Attached Medium Changer Control Program
+
+  Copyright 1997-1998 Leonard N. Zubkoff <lnz@dandelion.com>
+  This file created by Eric Lee Green <eric@estinc.com>
+  
+  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.
+
+  $Date: 2001/06/19 21:51:32 $
+  $Revision: 1.2 $
+*/
+
+/* Much of the guts of mtx.c has been extracted to mtxl.c, a library file
+ * full of utility routines. This file is the header file for that library.
+ *   -E
+ */
+
+#ifndef MTXL_H
+#define MTXL_H 1
+
+#include "mtx.h"
+
+void FatalError(char *ErrorMessage, ...);
+void *xmalloc(size_t Size);
+void *xzmalloc(size_t Size);
+void slow_bzero(char *buffer, int numchars);
+
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName);
+void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD);
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense);
+
+void PrintRequestSense(RequestSense_T *RequestSense);
+
+int BigEndian16(unsigned char *BigEndianData);
+int BigEndian24(unsigned char *BigEndianData);
+int min(int x, int y);
+int max(int x, int y);
+
+ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags);
+
+Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense);
+
+RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
+                      int DestinationAddress, 
+                      ElementStatus_T *ElementStatus, 
+                          Inquiry_T *inquiry_info, SCSI_Flags_T *flags);
+
+int Inventory(DEVICE_TYPE MediumChangerFD);  /* inventory library */
+int Eject(DEVICE_TYPE fd);                  /* unload tape or magazine */
+RequestSense_T *Erase(DEVICE_TYPE fd);        /* send SHORT erase to drive */
+
+void SCSI_Set_Timeout(int secs); /* set the SCSI timeout */
+void SCSI_Default_Timeout(void);  /* go back to default timeout */
+
+/* we may not have this function :-(. */
+#ifdef HAVE_GET_ID_LUN
+   scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd);
+#endif
+
+#endif
diff --git a/scsi_aix.c b/scsi_aix.c
new file mode 100644 (file)
index 0000000..1c65b6a
--- /dev/null
@@ -0,0 +1,144 @@
+/* Copyright 2001 Enhanced Software Technologies Inc.
+ *   Written by Eric Lee Green <eric@estinc.com>
+ *
+ *$Date: 2001/06/05 17:10:25 $
+ *$Revision: 1.1.1.1 $
+
+  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 AIX. The syntax for AIX is:
+ *
+ *  /dev/scsi<n>/<id>.<lun>
+ *
+ * where <n> is the number of the scsi adapter (0..n) and
+ * <id> and <lun> are the SCSI ID and LUN of the device you wish to
+ * talk to. 
+ *
+ * AIX has a very flexible SCSI subsystem, but it is somewhat
+ * clumsy to use. 
+ */
+
+/* we do very nasty thing here -- we operate upon device name! */
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName) {
+  /* okay, we must first parse out the ID and LUN: */
+  char *rptr;
+  struct tm_device_type *retval = (struct tm_device_type *) malloc(sizeof(struct tm_device_type));
+  int id,lun,filenum,idlun;
+
+  if (retval==NULL) {
+    fprintf(stderr,"%s: Allocation error in SCSI_OpenDevice for %s. Exiting.\n",argv0,DeviceName);
+    fflush(stderr);
+    exit(1);
+  }
+
+  rptr=strrchr(DeviceName,'/');
+  
+  if (!rptr) {
+    fprintf(stderr,"%s: Illegal device name '%s'. Exiting.\n",argv0,DeviceName);
+    fflush(stderr);
+    exit(1);
+  }
+  
+  *rptr++=0;
+
+  if (sscanf(rptr,"%d.%d",&id,&lun) < 2) {
+    /* whoops, we did not get 2 items:  */ 
+    fprintf(stderr,"%s: Illegal device name '%s/%s'. Exiting.\n",argv0,DeviceName,rptr);
+    fflush(stderr);
+    exit(1);
+  }
+
+  /* Okay, now to try to open the DeviceName */
+  if ((filenum=open(DeviceName,0))<0) {
+    fprintf(stderr,"%s: Illegal device name '%s/%s'. Exiting.\n",argv0,DeviceName,rptr);
+    perror(argv0);
+    fflush(stderr);
+    exit(1);
+  }
+
+  retval->filenum=filenum;
+  retval->id=id;
+  retval->lun=lun;
+  retval->DeviceName=DeviceName;
+  return (DEVICE_TYPE) retval;
+}
+
+#define MTX_HZ 1000 
+#define MTX_DEFAULT_SCSI_TIMEOUT 60*5*MTX_HZ /* 5 minutes! */
+static int mtx_default_timeout = MTX_DEFAULT_SCSI_TIMEOUT ;
+void SCSI_Set_Timeout(int sec) {
+  mtx_default_timeout=sec*MTX_HZ;
+}
+
+void SCSI_Default_Timeout() {
+  mtx_default_timeout=MTX_DEFAULT_SCSI_TIMEOUT;
+}
+
+/* Okay , now *DO IT!* */
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                       Direction_T Direction,
+                       CDB_T *CDB,
+                       int CDB_Length,
+                       void *DataBuffer,
+                       int DataBufferLength,
+                       RequestSense_T *RequestSense) {
+
+  int id,lun;
+  struct tm_device_type *fd=(struct tm_device_type *) DeviceFD;
+  struct devinfo info;
+  struct sc_buf sb; /* the sc_buf struct needed to commicate w/adapter. */
+
+
+  id=fd->id;
+  lun=fd->lun;
+
+  /* okay, first of all, make sure we're not asking for a bigger
+   * operation than we are allowed to ask for, by going to the driver
+   * with IOCINFO. 
+   */
+  if (ioctl(filenum,IOCINFO,&info)) {
+    fprintf(stderr,"%s: Could not get info for %s. Exiting.\n",argv0,fd->DeviceName);
+    exit(1);
+  }
+    
+  /* Now check the max_transfer: */
+  if ((int)info.scsi.max_transfer < DataBufferLength) {
+    fprintf(stderr,"%s: SCSI transfer too large. %d requested, %d allowed.\n",argv0,DataBufferLength,(int)info.scsi.max_transfer);
+    fflush(stderr);
+    exit(1);
+  }
+  
+  /* okay, we have our open file, we have the other stuff: Now initialize
+     a transaction with that ID/LUN:
+  */
+
+  if (ioctl(filenum, SCIOSTART,IDLUN(id,lun))) {
+    fprintf(stderr,"%s: Could not start SCSI transaction with %s/%d.%d. Exiting.\n",argv0,fd->DeviceName,id,lun);
+    fflush(stderr);
+    exit(1);
+  }
+  
+  /* okay, now to decide command: */
+  
+  
+
+
+  /*... have finished command...*/
+  
+  /* when done w/command, get rid of the connection: */  
+  if (ioctl(filenum,SCIOSTOP,IDLUN(id,lun))) {
+    fprintf(stderr,"%s: Could not stop SCSI transaction with %s/%d.%d. Exiting.\n",argv0,fd->DeviceName,id,lun);
+    fflush(stderr);
+    exit(1);
+  }
+     
+  
diff --git a/scsi_freebsd.c b/scsi_freebsd.c
new file mode 100644 (file)
index 0000000..8c88d82
--- /dev/null
@@ -0,0 +1,115 @@
+/* Copyright 2000 Enhanced Software Technologies Inc. (http://www.estinc.com)
+   Written by Eric Lee Green <eric@estinc.com>
+
+$Date: 2001/06/05 17:10:25 $
+$Revision: 1.1.1.1 $
+
+  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.
+
+*/
+
+/* This is the SCSI commands for FreeBSD */
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+  struct cam_device *DeviceFD = cam_open_pass(DeviceName, O_RDWR | O_EXCL,
+                                             NULL);
+  if (DeviceFD == 0)
+    FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+  return (DEVICE_TYPE) DeviceFD;
+}
+
+
+void SCSI_CloseDevice(char *DeviceName,
+                            DEVICE_TYPE DeviceFD)
+{
+  cam_close_device((struct cam_device *) DeviceFD);
+}
+
+#define PASS_HZ 1000*60
+#define PASS_DEFAULT_TIMEOUT 5*PASS_HZ
+static int pass_timeout = PASS_DEFAULT_TIMEOUT;
+
+void SCSI_Set_Timeout(int secs) {
+  pass_timeout=secs*PASS_HZ;
+}
+
+void SCSI_Default_Timeout(void) {
+  pass_timeout=5*PASS_HZ;
+}
+
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+  struct cam_device *dsp = (struct cam_device *) DeviceFD;
+  int retval;
+  union ccb *ccb;
+  CDB_T *cdb;
+  int Result;
+
+  ccb=cam_getccb(dsp);
+  cdb = (CDB_T *) &ccb->csio.cdb_io.cdb_bytes; /* pointer to actual cdb. */
+
+  /* cam_getccb() zeros the CCB header only. So now clear the
+   * payload portion of the ccb.
+   */
+  bzero(&(&ccb->ccb_h)[1],
+       sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+  /* copy the CDB... */
+  memcpy(cdb,CDB,CDB_Length);
+  /* set the command control block stuff.... the rather involved
+  * conditional expression sets the direction to NONE if there is no
+  * data to go in or out, and IN or OUT if we want data. Movement
+  * commands will have no data buffer, just a CDB, while INQUIRY and
+  * READ_ELEMENT_STATUS will have input data, and we don't have any 
+  * stuff that outputs data -- yet -- but we may eventually. 
+  */
+  cam_fill_csio(&ccb->csio,
+               /* retries */ 1,
+               /* cbfcnp*/ NULL,
+               /* flags */((DataBufferLength) ?
+                           ((Direction == Input) ? CAM_DIR_IN : CAM_DIR_OUT) :
+                           CAM_DIR_NONE),  
+               /* tag action */ MSG_SIMPLE_Q_TAG,
+               /* data ptr */ DataBuffer,
+               /* xfer_len */ DataBufferLength,
+               /* sense_len */ SSD_FULL_SIZE,
+               /* cdb_len */ CDB_Length,
+               /* timeout */ pass_timeout /* should be 5 minutes or more?! */
+               ); 
+
+  pass_timeout=PASS_DEFAULT_TIMEOUT; /* make sure it gets reset. */
+  memset(RequestSense, 0, sizeof(RequestSense_T)); /* clear sense buffer... */
+
+  if (Direction == Input)
+  {
+     memset(DataBuffer, 0, DataBufferLength);
+  }
+
+  Result = cam_send_ccb(DeviceFD,ccb);
+  if ( (Result < 0) || 
+       (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+    /* copy our sense data, sigh... */
+    memcpy(RequestSense,(void *) &ccb->csio.sense_data,
+          min(sizeof(RequestSense_T), sizeof(struct scsi_sense_data)));
+    
+    cam_freeccb(ccb);
+    return -1; /* sorry!  */
+  }
+  
+  /* okay, we did good, maybe? */
+  cam_freeccb(ccb);
+  return 0; /* and done? */
+}
diff --git a/scsi_hpux.c b/scsi_hpux.c
new file mode 100644 (file)
index 0000000..66a07b9
--- /dev/null
@@ -0,0 +1,123 @@
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+   Changes copyright 2000 Eric Green <eric@estinc.com>
+
+  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.
+
+struct sctl_io {
+        unsigned flags;                 // IN: SCTL_READ
+        unsigned cdb_length;            // IN
+        unsigned char cdb[16];          // IN 
+        void *data;                     // IN 
+        unsigned data_length;           // IN 
+        unsigned max_msecs;             // IN: milli-seconds before abort 
+        unsigned data_xfer;             // OUT 
+        unsigned cdb_status;            // OUT: SCSI status 
+        unsigned char sense[256];       // OUT 
+        unsigned sense_status;          // OUT: SCSI status 
+        unsigned sense_xfer;            // OUT: bytes of sense data received 
+        unsigned reserved[16];          // IN: Must be zero; OUT: undefined 
+};
+
+*/
+
+
+/* Hockey Pux may define these. If so, *UN*define them. */
+#ifdef ILI
+#undef ILI
+#endif
+
+#ifdef EOM
+#undef EOM
+#endif
+
+/* This is the SCSI commands for HPUX. */
+
+#define LONG_PRINT_REQUEST_SENSE  /* Sigh! */
+
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+  int DeviceFD = open(DeviceName, O_RDWR | O_NDELAY);
+  if (DeviceFD < 0)
+    FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+  return (DEVICE_TYPE) DeviceFD;
+}
+
+
+void SCSI_CloseDevice(char *DeviceName,
+                            DEVICE_TYPE DeviceFD)
+{
+  if (close(DeviceFD) < 0)
+    FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
+}
+
+#define MTX_HZ 1000
+#define DEFAULT_HZ (5*60*MTX_HZ)
+
+static int sctl_io_timeout=DEFAULT_HZ;  /* default timeout is 5 minutes. */
+
+
+void SCSI_Set_Timeout(int to) {
+  sctl_io_timeout=to*60*MTX_HZ;
+}
+
+void SCSI_Default_Timeout(void) {
+  sctl_io_timeout=DEFAULT_HZ;
+}
+
+
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+    int ioctl_result;
+    struct sctl_io Command;
+
+    int i;
+
+    memset(&Command, 0, sizeof(struct sctl_io));
+    memset(RequestSense, 0, sizeof(RequestSense_T));
+
+    switch (Direction) {
+         case Input:
+              if (DataBufferLength > 0)
+                  memset(DataBuffer, 0, DataBufferLength);
+              Command.flags =  SCTL_READ | SCTL_INIT_SDTR;
+              break;
+         case Output:
+              Command.flags = SCTL_INIT_WDTR | SCTL_INIT_SDTR;
+              break;
+         }
+
+    Command.max_msecs = sctl_io_timeout;    /* Set timeout to <n> minutes. */
+    memcpy(Command.cdb, CDB, CDB_Length);
+    Command.cdb_length = CDB_Length;
+    Command.data = DataBuffer;
+    Command.data_length = DataBufferLength;
+    ioctl_result=ioctl(DeviceFD, SIOC_IO, &Command);
+    SCSI_Default_Timeout();  /* change the default back to 5 minutes */
+    if (ioctl_result < 0) {
+         perror("mtx");
+         return ioctl_result;
+         }
+
+    if (Command.sense_xfer > sizeof(RequestSense_T)) {
+      Command.sense_xfer=sizeof(RequestSense_T);
+    }
+
+    if (Command.sense_xfer) {
+      memcpy(RequestSense, Command.sense, Command.sense_xfer);
+    }
+
+    return Command.sense_status;
+}
diff --git a/scsi_linux.c b/scsi_linux.c
new file mode 100644 (file)
index 0000000..e5378d3
--- /dev/null
@@ -0,0 +1,397 @@
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+   Changes in Feb 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/06/19 21:51:32 $
+$Revision: 1.2 $
+
+  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.
+
+*/
+
+/* this is the SCSI commands for Linux. Note that <eric@estinc.com> changed 
+ * it from using SCSI_IOCTL_SEND_COMMAND to using the SCSI generic interface.
+ */
+
+#ifndef HZ
+#define HZ 100
+#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 SG_SCSI_DEFAULT_TIMEOUT HZ*60*5  /* 5 minutes? */
+
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+  int timeout=SG_SCSI_DEFAULT_TIMEOUT;      /* 5 minutes */
+  int DeviceFD = open(DeviceName, O_RDWR);
+  if (DeviceFD < 0)
+    FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+
+  if(ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout)) {
+    FatalError("failed to set sg timeout - %m\n");
+  }
+
+  return (DEVICE_TYPE) DeviceFD;
+}
+
+static int sg_timeout = SG_SCSI_DEFAULT_TIMEOUT ;
+
+void SCSI_Set_Timeout(int secs) {
+  sg_timeout=secs*HZ;
+}
+void SCSI_Default_Timeout(void) {
+  sg_timeout=SG_SCSI_DEFAULT_TIMEOUT;
+}
+
+void SCSI_CloseDevice(char *DeviceName,
+                            DEVICE_TYPE DeviceFD)
+{
+  if (close(DeviceFD) < 0)
+    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) {
+  int status;
+  scsi_id_t *retval;
+  
+  struct my_scsi_idlun {
+    int word1;
+    int word2;
+  } idlun;
+
+  status=ioctl(fd,SCSI_IOCTL_GET_IDLUN,&idlun);
+  if (status) {
+    return NULL; /* sorry! */
+  }
+  
+  retval=(scsi_id_t *)xmalloc(sizeof(scsi_id_t));
+  retval->id=idlun.word1 & 0xff;
+  retval->lun=idlun.word1 >> 8 & 0xff;
+#ifdef DEBUG
+  fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
+#endif
+  return retval;
+}
+  
+
+/* Changed February 2000 by Eric Green <eric@estinc.com> to 
+ * use the SCSI generic interface rather than SCSI_IOCTL_SEND_COMMAND
+ * so that we can get more than PAGE_SIZE data....
+ *
+ * Note that the SCSI generic interface abuses READ and WRITE calls to serve
+ * the same purpose as IOCTL calls, i.e., for "writes", the contents of the
+ * buffer that you send as the argument to the write() call are actually
+ * altered to fill in result status and sense data (if needed). 
+ * Also note that this brain-dead interface does not have any sort of 
+ * provisions for expanding the sg_header struct in a backward-compatible
+ * manner. This sucks. But sucks less than SCSI_IOCTL_SEND_COMMAND, sigh.
+ */
+
+#ifndef OLD_EXECUTE_COMMAND_STUFF
+
+static void slow_memcopy(unsigned char *src, unsigned char *dest, int numbytes)
+{
+  while (numbytes--) {
+    *dest++ = *src++;
+  }
+}
+
+
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+
+  unsigned char *Command=NULL;   /* the command data struct sent to them... */
+  unsigned char *ResultBuf=NULL; /* the data we read in return...         */
+
+  unsigned char *src;       /* for copying stuff, sigh. */
+  unsigned char *dest;      /* for copy stuff, again, sigh. */
+
+  int write_length = sizeof(struct sg_header)+CDB_Length;
+  int i;                  /* a random index...          */
+  int result;             /* the result of the write... */ 
+  
+  
+  struct sg_header *Header; /* we actually point this into Command... */
+  struct sg_header *ResultHeader; /* we point this into ResultBuf... */
+
+
+  /* First, see if we need to set our SCSI timeout to something different */
+  if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT) {
+    /* if not default, set it: */
+#ifdef DEBUG_TIMEOUT
+    fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
+    fflush(stderr);
+#endif
+    if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
+      FatalError("failed to set sg timeout - %m\n");
+    }
+  }
+
+  if (Direction == Output) {  /* if we're writing, our length is longer... */
+    write_length += DataBufferLength; 
+  }
+  
+  /* allocate some memory... enough for the command plus the header +
+   *  any other data that we may need here...
+   */
+  
+  Command=(unsigned char *)xmalloc(write_length);
+  Header = (struct sg_header *) Command;  /* make it point to start of buf */
+
+  dest=Command; /* now to copy the CDB... from start of buffer,*/
+  dest+= sizeof(struct sg_header); /* increment it past the header. */
+
+  slow_memcopy((char *)CDB,dest,CDB_Length);
+
+  /* if we are writing additional data, tack it on here! */
+  if (Direction == Output) {
+    dest += CDB_Length;
+    slow_memcopy(DataBuffer,dest,DataBufferLength); /* copy to end of command */
+  }
+  /* Now to fill in the Header struct: */
+  Header->reply_len=DataBufferLength+sizeof(struct sg_header);
+#ifdef DEBUG
+  fprintf(stderr,"sg:reply_len(sent)=%d\n",Header->reply_len);
+#endif
+  Header->twelve_byte = CDB_Length == 12; 
+  Header->result = 0;
+  Header->pack_len = write_length; /* # of bytes written... */
+  Header->pack_id = 0;             /* not used              */
+  Header->other_flags = 0;         /* not used.             */
+  Header->sense_buffer[0]=0;      /* used? */
+
+  /* Now to do the write... */
+  result=write(DeviceFD,Command,write_length);
+  
+  /* Now to check the result :-(. */
+  /* Note that we don't have any request sense here. So we have no
+   * idea what's going on. 
+   */ 
+  if ( (result < 0) || (result != write_length) || Header->result ||
+       Header->sense_buffer[0] ) {
+#ifdef DEBUG_SCSI
+    fprintf(stderr,"scsi:result=%d Header->result=%d Header->sense_buffer[0]=%d\n",
+           result,Header->result,Header->sense_buffer[0]);
+#endif  
+    /* we don't have any real sense data, sigh :-(. */
+    if (Header->sense_buffer[0]) {
+      /* well, I guess we DID have some! eep! copy the sense data! */
+      slow_memcopy((char *)Header->sense_buffer,(char *)RequestSense,
+                  sizeof(Header->sense_buffer));
+    } else {
+      dest=(unsigned char *)RequestSense;
+      *dest=(unsigned char)Header->result; /* may chop, sigh... */
+    }
+
+    /* okay, now, we may or may not need to find a non-zero value to return.
+     * For tape drives, we may get a BLANK_CHECK or MEDIUM_ERROR and find
+     * that it's *STILL* a good read! Use the STILL_A_VALID_READ macro
+     * that calls all those macros I cribbed from Richard. 
+     */
+    
+    if (!STILL_A_VALID_READ(RequestSense)) {
+      free(Command); /* zap memory leak, sigh */
+      /* okay, find us a non-zero value to return :-(. */
+      if (result) {
+       return result;
+      } else if (Header->result) {
+       return Header->result;
+      } else {
+       return -1;  /* sigh */
+      }
+    } else {
+      result=-1;
+    }
+  } else {
+    result=0; /* we're okay! */
+  }
+    
+  
+  /* now to allocate the new block.... */
+  ResultBuf=(unsigned char *)xmalloc(Header->reply_len);
+  /* now to clear ResultBuf... */
+  slow_bzero(ResultBuf,Header->reply_len); 
+
+  ResultHeader=(struct sg_header *)ResultBuf;
+  
+  /* copy the original Header... */
+  ResultHeader->result=0;
+  ResultHeader->pack_id=0;
+  ResultHeader->other_flags=0;
+  ResultHeader->reply_len=Header->reply_len;
+  ResultHeader->twelve_byte = CDB_Length == 12; 
+  ResultHeader->pack_len = write_length; /* # of bytes written... */
+  ResultHeader->sense_buffer[0]=0; /* whoops! Zero that! */
+#ifdef DEBUG
+  fprintf(stderr,"sg:Reading %d bytes from DeviceFD\n",Header->reply_len);
+  fflush(stderr);
+#endif
+  result=read(DeviceFD,ResultBuf,Header->reply_len);
+#ifdef DEBUG
+  fprintf(stderr,"sg:result=%d ResultHeader->result=%d\n",
+         result,ResultHeader->result);
+  fflush(stderr);
+#endif
+  /* New: added check to see if the result block is still all zeros! */
+  if ( (result < 0) || (result != Header->reply_len) || ResultHeader->result ||
+       ResultHeader->sense_buffer[0] ) {
+#ifdef DEBUG
+    fprintf(stderr,
+           "scsi: result=%d Header->reply_len=%d ResultHeader->result=%d ResultHeader->sense_buffer[0]=%d\n",
+           result,
+           Header->reply_len,
+           ResultHeader->result,
+           ResultHeader->sense_buffer[0]);
+#endif
+    /* eep! copy the sense data! */
+    slow_memcopy((char *)ResultHeader->sense_buffer,(char *)RequestSense,
+                sizeof(ResultHeader->sense_buffer));
+    /* sense data copied, now find us a non-zero value to return :-(. */
+    /* NOTE: Some commands return sense data even though they validly
+     * executed! We catch a few of those with the macro STILL_A_VALID_READ.
+     */
+
+    if (!STILL_A_VALID_READ(RequestSense)) {
+      free(Command);
+      if (result) {
+       free(ResultBuf);
+       return result;
+      } else if (ResultHeader->result) {
+       free(ResultBuf);
+       return ResultHeader->result;
+      } else {
+       free(ResultBuf);
+       return -1; /* sigh! */
+      } 
+    } else {
+      result=-1; /* if it was a valid read, still have -1 result. */
+    }
+  } else {
+    result=0;
+  }
+  
+  /* See if we need to reset our SCSI timeout */
+  if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT) {
+    sg_timeout = SG_SCSI_DEFAULT_TIMEOUT; /* reset it back to default */
+#ifdef DEBUG_TIMEOUT
+    fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
+    fflush(stderr);
+#endif
+    /* if not default, set it: */
+    if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout)) {
+      FatalError("failed to set sg timeout - %m\n");
+    }
+  }
+  
+  
+  /* now for the crowning moment: copying any result into the DataBuffer! */
+  /* (but only if it were an input command and not an output command :-}  */
+  if (Direction == Input) {
+#ifdef DEBUG
+    fprintf(stderr,"Header->reply_len=%d,ResultHeader->reply_len=%d\n",
+           Header->reply_len,ResultHeader->reply_len);
+#endif
+    src=ResultBuf+sizeof(struct sg_header);
+    dest=DataBuffer;
+    for (i=0;i< (ResultHeader->reply_len);i++) {
+      if (i>=DataBufferLength) break;  /* eep! */
+      *dest++=*src++;
+    }
+  }
+  
+  /* and return! */
+  free(Command);    /* clean up memory leak... */
+  free(ResultBuf);
+  return result; /* good stuff ! */
+}
+
+#endif  
+  
+
+#ifdef OLD_EXECUTE_COMMAND_STUFF
+
+int SCSI_ExecuteCommand(int DeviceFD,
+                              Direction_T Direction,
+                              CDB_T CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+  unsigned char *Command;
+  int Zero = 0, Result;
+  memset(RequestSense, 0, sizeof(RequestSense_T));
+  switch (Direction)
+    {
+    case Input:
+      Command = (unsigned char *)
+       xmalloc(8 + max(DataBufferLength, sizeof(RequestSense_T)));
+      memcpy(&Command[0], &Zero, 4);
+      memcpy(&Command[4], &DataBufferLength, 4);
+      memcpy(&Command[8], CDB, CDB_Length);
+      break;
+    case Output:
+      Command = (unsigned char *)
+       xmalloc(8 + max(CDB_Length + DataBufferLength, sizeof(RequestSense_T)));
+      memcpy(&Command[0], &DataBufferLength, 4);
+      memcpy(&Command[4], &Zero, 4);
+      memcpy(&Command[8], CDB, CDB_Length);
+      memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
+      break;
+    }
+  Result = ioctl(DeviceFD, SCSI_IOCTL_SEND_COMMAND, Command);
+  if (Result != 0)
+    memcpy(RequestSense, &Command[8], sizeof(RequestSense_T));
+  else if (Direction == Input)
+    memcpy(DataBuffer, &Command[8], DataBufferLength);
+  free(Command);
+  return Result;
+}
+
+#endif
diff --git a/scsi_sgi.c b/scsi_sgi.c
new file mode 100644 (file)
index 0000000..c215de9
--- /dev/null
@@ -0,0 +1,75 @@
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+   Changes copyright 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/06/05 17:10:26 $
+$Revision: 1.1.1.1 $
+
+  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.
+
+*/
+
+/* This is the SCSI commands for SGI Iris */
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+  dsreq_t *DeviceFD = dsopen(DeviceName, O_RDWR | O_EXCL);
+  if (DeviceFD == 0)
+    FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+  return (DEVICE_TYPE) DeviceFD;
+}
+
+
+void SCSI_CloseDevice(char *DeviceName,
+                            DEVICE_TYPE DeviceFD)
+{
+  dsclose((dsreq_t *) DeviceFD);
+}
+
+#define MTX_HZ 1000 
+#define MTX_DEFAULT_SCSI_TIMEOUT 60*5*MTX_HZ /* 5 minutes! */
+
+static int mtx_default_timeout = MTX_DEFAULT_SCSI_TIMEOUT ;
+void SCSI_Set_Timeout(int sec) {
+  mtx_default_timeout=sec*MTX_HZ;
+}
+
+void SCSI_Default_Timeout() {
+  mtx_default_timeout=MTX_DEFAULT_SCSI_TIMEOUT;
+}
+
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+  dsreq_t *dsp = (dsreq_t *) DeviceFD;
+  int Result;
+  memset(RequestSense, 0, sizeof(RequestSense_T));
+  memcpy(CMDBUF(dsp), CDB, CDB_Length);
+  if (Direction == Input)
+  {
+     memset(DataBuffer, 0, DataBufferLength);
+     filldsreq(dsp, (unsigned char *) DataBuffer, DataBufferLength,
+              DSRQ_READ | DSRQ_SENSE);
+  }
+  else filldsreq(dsp, (unsigned char *) DataBuffer, DataBufferLength,
+                DSRQ_WRITE | DSRQ_SENSE);
+  /* Set 5 minute timeout. */
+  /* TIME(dsp) = 300 * 1000; */
+  TIME(dsp) = mtx_default_timeout;
+  Result = doscsireq(getfd((dsp)), dsp);
+  if (SENSESENT(dsp) > 0)
+    memcpy(RequestSense, SENSEBUF(dsp),
+          min(sizeof(RequestSense_T), SENSESENT(dsp)));
+  SCSI_Default_Timeout(); /* reset the mtx default timeout */
+  return Result;
+}
diff --git a/scsi_sun.c b/scsi_sun.c
new file mode 100644 (file)
index 0000000..ca37a3b
--- /dev/null
@@ -0,0 +1,141 @@
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+   Changes copyright 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/12/13 16:30:11 $
+$Revision: 1.1.1.1.2.2 $
+
+  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.
+
+*/
+
+/* This is the SCSI commands for Sun Solaris. */
+
+#define LONG_PRINT_REQUEST_SENSE  /* sigh! */
+
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+  int DeviceFD = open(DeviceName, O_RDWR | O_NDELAY);
+  if (DeviceFD < 0)
+    FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+  return (DEVICE_TYPE) DeviceFD;
+}
+
+
+void SCSI_CloseDevice(char *DeviceName,
+                            DEVICE_TYPE DeviceFD)
+{
+  if (close(DeviceFD) < 0)
+    FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
+}
+
+
+#define HAS_SCSI_TIMEOUT
+
+static int uscsi_timeout=5*60; 
+
+void SCSI_Set_Timeout(int to) {
+  uscsi_timeout=to;
+}
+
+void SCSI_Default_Timeout(void) {
+  uscsi_timeout=5*60; /* the default */
+}
+
+#ifdef DEBUG
+int SCSI_DumpBuffer(int DataBufferLength, unsigned char *DataBuffer) {
+  int i,j;
+  j=0;
+  for (i=0; i < DataBufferLength; i++) {
+    if (j==25) {
+      fprintf(stderr,"\n");
+      j=0;
+    }
+    if (j==0) {
+      fprintf(stderr,"%04x:",i);
+    }
+    if (j>0) {
+      fprintf(stderr," ");
+    }
+    fprintf(stderr,"%02x",(int)DataBuffer[i]);
+    j++;
+  }
+  fprintf(stderr,"\n");
+}
+#endif
+
+
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+  int ioctl_result;
+  struct uscsi_cmd Command;
+
+#ifdef DEBUG_SCSI
+  fprintf(stderr,"------CDB--------\n");
+  SCSI_DumpBuffer(CDB_Length,(char *)CDB);
+#endif
+
+  memset(&Command, 0, sizeof(struct uscsi_cmd));
+  memset(RequestSense, 0, sizeof(RequestSense_T));
+  switch (Direction)
+    {
+    case Input:
+      Command.uscsi_flags = USCSI_DIAGNOSE | USCSI_ISOLATE | USCSI_RQENABLE;
+      if (DataBufferLength > 0) {
+          memset(DataBuffer, 0, DataBufferLength);
+          Command.uscsi_flags |= USCSI_READ;
+      }
+      break;
+    case Output:
+      Command.uscsi_flags = USCSI_DIAGNOSE | USCSI_ISOLATE
+                           | USCSI_WRITE | USCSI_RQENABLE;
+      break;
+    }
+  /* Set timeout to 5 minutes. */
+#ifdef DEBUG_TIMEOUT
+  fprintf(stderr,"uscsi_timeout=%d\n",uscsi_timeout);
+  fflush(stderr);
+#endif
+  Command.uscsi_timeout = uscsi_timeout;
+  
+  Command.uscsi_cdb = (caddr_t) CDB;
+  Command.uscsi_cdblen = CDB_Length;
+  Command.uscsi_bufaddr = DataBuffer;
+  Command.uscsi_buflen = DataBufferLength;
+  Command.uscsi_rqbuf = (caddr_t) RequestSense;
+  Command.uscsi_rqlen = sizeof(RequestSense_T);
+  ioctl_result=ioctl(DeviceFD, USCSICMD, &Command);
+
+  SCSI_Default_Timeout(); /* set it back to default, sigh. */
+
+
+  if (ioctl_result < 0) {
+#ifdef DEBUG
+    perror("mtx");
+#endif
+    return ioctl_result;
+  }
+
+  if (RequestSense -> ErrorCode > 1) {
+    return -1;
+  }
+#ifdef DEBUG_SCSI
+  if (Direction==Input) {
+    fprintf(stderr,"--------input data-----------\n");
+    SCSI_DumpBuffer(DataBufferLength,DataBuffer);
+  }
+#endif
+  return 0;
+}
diff --git a/scsitape.1 b/scsitape.1
new file mode 100644 (file)
index 0000000..2f9d7e9
--- /dev/null
@@ -0,0 +1,180 @@
+.\" scsitape.1  Document Copyright 2001 Eric Lee Green
+.\"
+.\" This is free documentation; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License as
+.\" published by the Free Software Foundation; either version 2 of
+.\" the License, or (at your option) any later version.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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 more details.
+.\"
+.\" You should have received a copy of the GNU General Public
+.\" License along with this manual; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+.\" USA.
+.\"
+.TH SCSITAPE 1 SCSITAPE1.0
+.SH NAME
+scsitape \- control SCSI tape devices 
+.SH SYNOPSIS
+scsitape [-f <scsi-generic-device>] commands
+.SH DESCRIPTION
+The 
+.B scsitape
+command controls SCSI tape drives in a platform-independent
+manner. As long as 'mtx' works on the platform, so does 'scsitape'. 
+.P
+Note that 'scsitape' and your OS's native tape driver may stomp on each
+other. In particular, if you use 'setblk' and your OS's native tape
+driver has a different notion of the block size, you may get evil results.
+It is recommended to use 'scsitape' only for software where you've written
+your own low-level READ and WRITE routines that use the SCSI command set
+to directly talk to tape drives (i.e., you do not use the OS's native tape
+driver at all). 
+.SH OPTIONS
+The first argument, given following
+.B -f
+, is the SCSI generic device corresponding to your tape drive.
+Consult your operating system's documentation for more information (for
+example, under Linux these are generally /dev/sg0 through /dev/sg15, 
+under FreeBSD these are /dev/pass0 through /dev/passX. Under Solaris
+this is usually the same as your tape drive (Solaris has a SCSI passthrough
+ioctl). You can set the STAPE or TAPE environment variable rather
+than use -f.
+.P
+.SH COMMANDS
+.TP 10
+.B setblk <n>
+Set the tape drive's SCSI block size to <n> bytes. (NOTE: if you are
+using your OS's native tape driver, THIS IS EVIL!). 
+
+.TP 10
+.B fsf <n>
+Go forward by <n> tapemarks.
+.TP 10
+.B bsf <n>
+Go to immediately previous the <n>th previous tapemark. (WARNING: This
+probably doesn't do what you expect -- e.g. if you are immediately
+after a tapemark and type 'bfs 1', it moves to immediately *before*
+that tape mark, for a sum total of zero effective movement!).
+.TP 10
+.B eod
+Go to end of data. 
+.TP 10
+.B rewind
+Rewind the tape drive.
+.TP 10
+.B eject
+Eject the tape currently in the drive.
+.TP 10
+.B erase
+Does a *short* erase (warning: does NOT work on all drives!). 
+.TP 10
+.B mark <n>
+ write <n> filemarks ( 'mark 0' flushes the drive's buffers ). 
+.TP 10
+.B seek <n>
+Seek to a logical position <n> that was reported by a previous 'tapeinfo'
+command. 
+.TP 10
+.B write <blocksize> 
+write blocks from stdin to the tape. Chunk the data into <blocksize>-sized
+chunks. *DOES NOT WRITE OUT A TAPEMARK!* (you will need to use a 
+subsequent
+.B mark 1
+command to write out a tape mark). 
+.TP 10
+.B read [<blocksize>] [ <#blocks/#bytes> ]
+read blocks from the tape, write them to stdout. If we are in variable
+block mode, <blocksize> should be zero (note: The maximum block size
+we currently support in variable block mode is 128K, MAX_READ_SIZE will
+need to be turned into a settable variable to allow bigger reads). If
+<blocksize> is ommitted, we assume that we're in variable block mode, and
+that we are going to read from tape until we hit a tapemark or end of
+partition or end of tape. 
+
+
+.SH AUTHORS
+This program was written by Eric Lee Green <eric@estinc.com>. 
+Major portions of the 'mtxl.c' library used herein were written by
+Leonard Zubkoff. 
+.P
+
+The SCSI read and write routines are based upon those that Richard
+Fish wrote for Enhanced Software Technology's BRU 16.1 product,
+substantially modified to work in our particular environment (in
+particular, all the variable block stuff is new since BRU only does
+fixed block reads and writes, and the BRU code uses bitmasks rather
+than bitfields for the various flags and such in return values, as
+well as the BRU code having a different SCSI API and having variable
+names considerably shorter than the rather sesquipedalian 'mtx'
+identifiers). As required by 'mtxl.c', these routines are licensed
+under the GNU General Public License.
+
+
+.SH HINTS
+Under Linux, 
+.B cat /proc/scsi/scsi
+will tell you what SCSI devices you have.
+You can then refer to them as 
+.B /dev/sga,
+.B /dev/sgb, 
+etc. by the order they
+are reported.
+.P
+Under FreeBSD, 
+.B camcontrol devlist
+will tell you what SCSI devices you
+have, along with which 
+.B pass
+device controls them.
+.P
+Under Solaris 7 and 8,
+.B /usr/sbin/devfsadm -C
+will clean up your /devices directory. Then
+.B find /devices -name 'st@*' -print 
+will return a list of all tape drives. /dev on Solaris is apparently only
+of historical interest. 
+
+.SH BUGS AND LIMITATIONS
+
+for
+.B scsitape read 0 <n>
+where  you are doing variable-block-size reads and wish for <n> bytes,
+it instead reads one and exactly one block from tape and prints that
+(no matter what its size). Use 'dd' on the output of scsitape if you
+want finer control. 
+.P
+.B scsitape read 0
+attempts reads of MAX_READ_SIZE, which is currently 128K. If blocks on tape
+are larger than 128K, only the first 128K will be read -- the remainder
+will be silently dumped in the toilet.
+.P
+This program does not interact well (or at all :-) with your OS's
+native tape driver.  You will likely see weird things happen if you
+attempt to intermingle scsitape commands with native tape driver
+operations. Note that BRU 16.1 for Solaris (and possibly others, but
+Solaris I know about) will have a 'scsi' keyword to bypass the 
+native tape driver and write via direct uscsi commands, so if you use
+'scsitape' to bypass the flaws of the native Solaris driver, you can use
+BRU 16.1 to write your actual tape archives. (Assuming that BRU 16.1
+has been released at the time that you read this). 
+
+.SH AVAILABILITY
+This version of 
+.B scsitape
+is currently being maintained by Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. as part of the 'mtx' suite of
+programs. The 'mtx' home page is http://mtx.sourceforge.net and the
+actual code is currently available there and via CVS from
+http://sourceforge.net/projects/mtx . 
+
+.SH SEE ALSO
+.BR tapeinfo (1), mtx (1)
diff --git a/scsitape.c b/scsitape.c
new file mode 100644 (file)
index 0000000..1ff3892
--- /dev/null
@@ -0,0 +1,851 @@
+/* Copyright 2001 Enhanced Software Technologies Inc.
+ *   Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ * $Date: 2001/06/05 17:10:27 $
+ * $Revision: 1.1.1.1 $
+ */
+
+/* This is a generic SCSI tape control program. It operates by
+ * directly sending commands to the tape drive. If you are going
+ * through your operating system's SCSI tape driver, do *NOT* use 
+ * this program! If, on the other hand, you are using raw READ and WRITE
+ * commands through your operating system's generic SCSI interface (or
+ * through our built-in 'read' and 'write'), this is the place for you. 
+ */
+
+/*#define DEBUG_PARTITION */
+/*#define DEBUG 1 */
+
+/* 
+   Commands:
+         setblk <n> -- set the block size to <n>
+         fsf <n> -- go forward by <n> filemarks
+         bsf <n> -- go backward by <n> filemarks
+         eod  -- go to end of data
+         rewind -- rewind back to start of data
+         eject  -- rewind, then eject the tape. 
+        erase  -- (short) erase the tape (we have no long erase)
+         mark <n> -- write <n> filemarks.
+         seek <n> -- seek to position <n>.
+
+        write <blksize> <-- write blocks from stdin to the tape 
+         read  [<blksize>] [<#blocks/#bytes>] -- read blocks from tape, write to stdout. 
+
+   See the 'tapeinfo' program for status info about the tape drive.
+
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mtx.h"
+#include "mtxl.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
+
+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");
+}
+
+#define arg1 (arg[0])  /* for backward compatibility, sigh */
+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 DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
+
+
+
+static int S_setblk(void);
+static int S_fsf(void);
+static int S_bsf(void);
+static int S_eod(void);
+static int S_rewind(void);
+static int S_eject(void);
+static int S_mark(void);
+static int S_seek(void);
+static int S_reten(void);
+static int S_erase(void);
+
+static int S_read(void);
+static int S_write(void);
+
+
+struct command_table_struct {
+  int num_args;
+  char *name;
+  int (*command)(void);
+} command_table[] = {
+  { 1, "setblk", S_setblk },
+  { 1, "fsf", S_fsf },
+  { 1, "bsf", S_bsf },
+  { 0, "eod", S_eod },
+  { 0, "rewind", S_rewind },
+  { 0, "eject", S_eject },
+  { 0, "reten", S_reten },
+  { 0, "erase", S_erase },
+  { 1, "mark", S_mark },
+  { 1, "seek", S_seek },
+  { 2, "read", S_read },
+  { 2, "write",S_write }
+};
+
+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) {
+
+  if (MediumChangerFD) {
+    SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
+  }
+
+  MediumChangerFD = SCSI_OpenDevice(device);
+
+}
+
+static int get_arg(char *arg) {
+  int retval=-1;
+
+  if (*arg < '0' || *arg > '9') {
+    return -1;  /* sorry! */
+  }
+
+  retval=atoi(arg);
+  return retval;
+}
+
+
+/* we see if we've got a file open. If not, we open one :-(. Then
+ * we execute the actual command. Or not :-(. 
+ */ 
+int execute_command(struct command_table_struct *command) {
+
+  /* if the device is not already open, then open it from the 
+   * environment.
+   */
+  if (!MediumChangerFD) {
+    /* try to get it from STAPE or TAPE environment variable... */
+    device=getenv("STAPE");
+    if (device==NULL) {
+      device=getenv("TAPE");
+      if (device==NULL) {
+       Usage();
+      }
+    }
+    open_device();
+  }
+
+
+  /* okay, now to execute the command... */
+  return command->command();
+}
+
+
+/* parse_args():
+ *   Basically, we are parsing argv/argc. We can have multiple commands
+ * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
+ * load in another tape into drive 0, and we execute these commands one
+ * at a time as we come to them. If we don't have a -f at the start, we
+ * barf. If we leave out a drive #, we default to drive 0 (the first drive
+ * in the cabinet). 
+ */ 
+
+int parse_args(int argc,char **argv) {
+  int i,cmd_tbl_idx,retval,arg_idx;
+  struct command_table_struct *command;
+
+  i=1;
+  arg_idx=0;
+  while (i<argc) {
+    if (strcmp(argv[i],"-f") == 0) {
+      i++;
+      if (i>=argc) {
+       Usage();
+      }
+      device=argv[i++];
+      open_device(); /* open the device and do a status scan on it... */
+    } else {
+      cmd_tbl_idx=0;
+      command=&command_table[0]; /* default to the first command... */
+      command=&command_table[cmd_tbl_idx];
+      while (command->name) {
+       if (!strcmp(command->name,argv[i])) {
+         /* we have a match... */
+         break;
+       }
+       /* otherwise we don't have a match... */
+       cmd_tbl_idx++;
+       command=&command_table[cmd_tbl_idx];
+      }
+      /* if it's not a command, exit.... */
+      if (!command->name) {
+       Usage();
+      }
+      i++;  /* go to the next argument, if possible... */
+      /* see if we need to gather arguments, though! */
+      arg1=-1; /* default it to something */
+      for (arg_idx=0;arg_idx < command->num_args ; arg_idx++) {
+       if (i < argc) {
+         arg[arg_idx]=get_arg(argv[i]);
+         if (arg[arg_idx] !=  -1) {
+           i++; /* increment i over the next cmd. */
+         }
+       } else {
+         arg[arg_idx]=0; /* default to 0 setmarks or whatever */
+       } 
+      }
+      retval=execute_command(command);  /* execute_command handles 'stuff' */
+      exit(retval);
+    }
+  }
+  return 0; /* should never get here */
+}
+
+/* For Linux, this allows us to do a short erase on a tape (sigh!).
+ * Note that you'll need to do a 'mt status' on the tape afterwards in
+ * order to get the tape driver in sync with the tape drive again. Also
+ * note that on other OS's, this might do other evil things to the tape
+ * driver. Note that to do an erase, you must first rewind!
+
+ */
+static int S_erase(void) {
+  int retval;
+  RequestSense_T *RequestSense;
+
+  retval=S_rewind();
+  if (retval) {
+    return retval;  /* we have an exit status :-(. */
+  } 
+
+  RequestSense=Erase(MediumChangerFD);
+  if (RequestSense) {
+    PrintRequestSense(RequestSense);
+    exit(1);  /* exit with an error status. */
+  }
+  return 0;
+}
+
+/* This should eject a tape or magazine, depending upon the device sent
+ * to.
+ */
+static int S_eject(void)
+{
+int i;
+  i=Eject(MediumChangerFD);
+  if (i<0) {
+    fprintf(stderr,"scsitape:eject failed\n");
+    fflush(stderr);
+  }
+  return i;  /* if it failed, well, sigh.... */
+}
+
+
+
+
+/* We write a filemarks of 0 before going to grab position, in order
+ * to insure that data in the buffer is not a problem. 
+ */
+
+static int S_mark(void) {
+  RequestSense_T RequestSense; /* for result of ReadElementStatus */
+  CDB_T CDB;
+  unsigned char buffer[6];
+  int count=arg1; /* voila! */
+
+  CDB[0]=0x10;  /* SET_MARK */
+  CDB[1]=0;
+  CDB[2]=(count >> 16) & 0xff;
+  CDB[3]=(count >>8) & 0xff;
+  CDB[4]=count & 0xff;
+  CDB[5]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&RequestSense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&RequestSense)!=0){
+    PrintRequestSense(&RequestSense);
+    return 1;
+  }
+  return 0;
+}
+/* let's rewind to bod! 
+ */
+
+static int S_rewind(void) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+  CDB[0]=0x01;  /* REWIND */
+  CDB[1]=0;
+  CDB[2]=0;
+  CDB[3]=0;
+  CDB[4]=0;
+  CDB[5]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+    PrintRequestSense(&sense);
+    return 1;
+  }
+  return 0;
+}
+
+
+
+
+/* This is used for fsf and bsf. */
+static int Space(int count,int code){
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+  CDB[0]=0x11;  /* SET_MARK */
+  CDB[1]=code;
+  CDB[2]=(count >> 16) & 0xff;
+  CDB[3]=(count >>8) & 0xff;
+  CDB[4]=count & 0xff;
+  CDB[5]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+    PrintRequestSense(&sense);
+    return 1;
+  }
+  return 0;
+  
+}
+
+
+/* Let's try a fsf: */
+/* We write a filemarks of 0 before going to grab position, in order
+ * to insure that data in the buffer is not a problem. 
+ */
+
+static int S_fsf(void) {
+  return Space(arg1,1); /* go forward! */
+}
+
+static int S_bsf(void) {
+  return Space(-arg1,1); /* go backward! */
+}
+
+static int S_eod(void) {
+  return Space(0,3); /* go to eod! */
+}
+
+/* sigh, abuse of the LOAD command...
+
+ */
+static int S_reten(void) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+  CDB[0]=0x1b;  /* START_STOP */
+  CDB[1]=0; /* wait */
+  CDB[2]=0;
+  CDB[3]=0;
+  CDB[4]=3; /* reten. */ 
+  CDB[5]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0){
+    PrintRequestSense(&sense);
+    return 1;
+  }
+  return 0;
+
+}
+
+/* seek a position on the tape (sigh!) */
+static int S_seek(void){
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+  int count =  arg1;
+
+  /* printf("count=%d\n",arg1); */
+
+  CDB[0]=0x2b;  /* LOCATE */
+  CDB[1]=0;  /* Logical */
+  CDB[2]=0; /* padding */
+  CDB[3]=(count >> 24) & 0xff;
+  CDB[4]=(count >> 16) & 0xff;
+  CDB[5]=(count >>8) & 0xff;
+  CDB[6]=count & 0xff;
+  CDB[7]=0; 
+  CDB[8]=0; 
+  CDB[9]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,10,buffer,0,&sense)!=0){
+    PrintRequestSense(&sense);
+    return 1;
+  }
+  return 0;
+}
+
+#ifdef MTSRSZ
+static int Solaris_setblk(int fh,int count) {
+  /* we get here only if we have a MTSRSZ, which means Solaris. */
+  struct mtop mt_com;  /* the struct used for the MTIOCTOP ioctl */
+  int result;
+
+  /* okay, we have fh and count.... */
+  
+  /* Now to try the ioctl: */
+  mt_com.mt_op=MTSRSZ;
+  mt_com.mt_count=count;
+
+  /* surround the actual ioctl to enable threading, since fsf/etc. can be
+   * big time consumers and we want other threads to be able to run too. 
+   */
+  result=ioctl(fh, MTIOCTOP, (char *)&mt_com);
+
+  if (result < 0) {
+    return errno;
+  }
+
+  /* okay, we did okay. Return a value of None... */
+  return 0;
+}
+#endif  
+
+
+/* okay, this is a write: we need to set the block size to something: */
+static int S_setblk(void) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[12];
+  unsigned int count = (unsigned int) arg1;
+
+  
+  CDB[0]=0x15;  /* MODE SELECT */
+  CDB[1]=0x10;  /* scsi2 */
+  CDB[2]=0; 
+  CDB[3]=0;
+  CDB[4]=12; /* length of data */
+  CDB[5]=0;
+
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  slow_bzero(buffer,12);
+
+  /* Now to set the mode page header: */
+  buffer[0]=0;
+  buffer[1]=0;
+  buffer[2]=0x10; /* we are in buffered mode now, people! */
+  buffer[3]=8; /* block descriptor length. */ 
+  buffer[4]=0; /* reset to default density, sigh. */ /* 0 */
+  buffer[5]=0; /* 1 */
+  buffer[6]=0; /* 2 */
+  buffer[7]=0; /* 3 */
+  buffer[8]=0; /* 4 */
+  buffer[9]=(count >> 16) & 0xff; /* 5 */
+  buffer[10]=(count >> 8) & 0xff; /* 6 */
+  buffer[11]= count & 0xff;  /* 7 */
+     
+  if (SCSI_ExecuteCommand(MediumChangerFD,Output,&CDB,6,buffer,12,&sense)!=0){
+    PrintRequestSense(&sense);
+    return 1;
+  }
+#ifdef MTSRSZ
+  /*   Solaris_setblk(MediumChangerFD,count);   */
+#endif
+
+  return 0;
+}
+
+/*************************************************************************/
+/* SCSI read/write calls. These are mostly pulled out of BRU 16.1, 
+ * modified to work within the mtxl.h framework rather than the
+ * scsi_lowlevel.h framework. 
+ *************************************************************************/ 
+
+#define MAX_READ_SIZE 128*1024  /* max size of a variable-block read */
+
+#define READ_OK 0
+#define READ_FILEMARK 1
+#define READ_EOD 2
+#define READ_EOP 3
+#define READ_SHORT 5
+#define READ_ERROR 255
+
+#define WRITE_OK 0
+#define WRITE_ERROR 1
+#define WRITE_EOM 2
+#define WRITE_EOV 3
+
+
+/* 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 */
+
+#define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s).Filemark && (s).Valid)
+/* Sigh, the T-10 SSC spec says all of the following is needed to
+ * detect a short read while in variable block mode. We'll see.
+ */
+#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 BECOMING_READY(s) (S_UNIT_ATTENTION((s)) && (s).AdditionalSenseCode == 0x28 && (s).AdditionalSenseCodeQualifier == 0)
+
+/* Reading is a problem. We can hit a filemark, hit an EOD, or hit an
+ * EOP. Our caller may do something about that. Note that we assume that
+ * our caller has already put us into fixed block mode. If he has not, then
+ * we are in trouble anyhow. 
+ */
+int SCSI_readt(DEVICE_TYPE fd, char * buf, unsigned int bufsize, unsigned int *len, unsigned int timeout) {
+  int rtnval;
+  CDB_T cmd;
+
+  int blockCount;
+  int info;
+  
+  RequestSense_T RequestSense;
+  
+  if (bufsize==0) { /* we are in variable block mode */
+    blockCount=MAX_READ_SIZE; /* variable block size. */
+  } else {
+    blockCount= *len / bufsize ;
+    if ((*len % bufsize) != 0) {
+      fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,bufsize);
+      exit(1); /* we're finished, sigh. */
+    }
+  }
+  
+  if (timeout == 0) {
+    timeout = 1 * 60; /* 1 minutes */
+  }
+  
+  memset(&cmd, 0, sizeof(CDB_T));
+  cmd[0] = 0x08; /* READ */
+  cmd[1] = (bufsize) ? 1 : 0; /* fixed length or var length blocks */
+  cmd[2] = (blockCount >> 16) & 0xff; /* MSB */
+  cmd[3] = (blockCount >> 8) & 0xff;
+  cmd[4] = blockCount & 0xff; /* LSB */
+  
+  /* okay, let's read, look @ the result code: */
+  rtnval=READ_OK;
+  if (SCSI_ExecuteCommand(fd,Input,&cmd,6,buf,(bufsize) ? *len : MAX_READ_SIZE,&RequestSense)) {
+    
+    rtnval=READ_ERROR;
+    if (HIT_EOP(RequestSense)) {
+      cmd[0]=0x08;
+      rtnval=READ_EOP;
+    }
+
+    if (HIT_FILEMARK(RequestSense)) {
+      rtnval=READ_FILEMARK;
+    }
+    if (HIT_EOD(RequestSense)) {
+      rtnval=READ_EOD;
+    }
+    if ( (bufsize==0) && SHORT_READ(RequestSense)) {
+      rtnval=READ_SHORT; /* we only do short reads for variable block mode */
+    }
+    if (rtnval != READ_ERROR) {
+      /* info contains number of blocks or bytes *not* read. May be 
+       negative if the block we were trying to read was too big. So
+       we will have to account for that and set it to zero if so, so that
+       we return the proper # of blocks read. 
+      */
+      info=((RequestSense.Information[0]<<24) +
+           (RequestSense.Information[1]<<16) +
+           (RequestSense.Information[2]<<8) +
+           RequestSense.Information[3]);
+      /* on 64-bit platforms, we may need to turn 'info' into a negative # */
+      if (info > 0x7fffffff) info = 0;
+      if (info < 0) info=0;  /* make sure  we don't return too big len read. */
+      /* Now set *len to # of bytes read. */
+      *len= bufsize ? (blockCount-info) * bufsize : MAX_READ_SIZE-info ;
+    } else {
+      PrintRequestSense(&RequestSense);
+      exit(1);  /* foo. */
+    }
+  }
+  
+  return(rtnval);
+}
+
+/* Low level SCSI write. Modified from BRU 16.1,  with much BRU smarts
+ * taken out and with the various types changed to mtx types rather than
+ * BRU types.
+ */ 
+int SCSI_writet(DEVICE_TYPE fd, char * buf, unsigned int blocksize,
+                unsigned int *len, 
+               unsigned int timeout) {
+  CDB_T cmd;
+
+  int blockCount;
+  int rtnval=0;
+  RequestSense_T RequestSense;
+
+  if (blocksize==0) { /* we are in variable block mode */
+    blockCount=*len; /* variable block size. */
+  } else {
+    blockCount= *len / blocksize ;
+    if ((*len % blocksize) != 0) {
+      fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,blocksize);
+      exit(1); /* we're finished, sigh. */
+    }
+  }
+
+  fprintf(stderr,"Writing %d blocks\n",blockCount);
+  
+  memset(&cmd, 0, sizeof(CDB_T));
+  cmd[0] = 0x0a; /* WRITE */
+  cmd[1] = (blocksize) ? 1 : 0; /* fixed length or var length blocks */
+  cmd[2] = (blockCount >> 16) & 0xff; /* MSB */
+  cmd[3] = (blockCount >> 8) & 0xff;
+  cmd[4] = blockCount & 0xff; /* LSB */
+  
+  
+  if (SCSI_ExecuteCommand(fd,Output,&cmd,6,buf, *len, &RequestSense)) {
+    if (HIT_EOM(RequestSense)) {
+      /* we hit end of media. Return -1. */
+      if (S_VOLUME_OVERFLOW(RequestSense)) {
+       exit(WRITE_EOV);
+      }
+      exit(WRITE_EOM); /* end of media! */
+    }
+    else { /* it was plain old write error: */
+      PrintRequestSense(&RequestSense);
+      exit(WRITE_ERROR);
+    }
+  } else {
+    rtnval = *len; /* worked! */
+  }
+  return(rtnval);
+}
+
+/* S_write is not implemented yet! */
+static int S_write(void) {
+  unsigned char *buffer; /* the buffer we're gonna read/write out of. */
+  int buffersize;
+  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 */
+  int result;
+  int eof_input;
+  int infile=fileno(stdin); /* sigh */
+
+  if (blocksize==0) {
+    varsize=1;
+    buffersize=MAX_READ_SIZE;
+    len=MAX_READ_SIZE;
+  }  else {
+    varsize=0; /* fixed block mode */
+    buffersize=blocksize;
+    len=blocksize;
+  }
+  /* sigh, make it oversized just to have some */  
+  buffer=malloc(buffersize+8); 
+
+  eof_input=0;
+  while (!eof_input) {
+    /* size_t could be 64 bit on a 32 bit platform, so do casts. */
+    len=0;
+    /* If it is a pipe, we could read 4096 bytes rather than the full
+     * 128K bytes or whatever, so we must gather multiple reads into
+     * the buffer.
+     */
+    while (len < buffersize) {
+      result=(int)read(infile,buffer+len,(size_t)(buffersize-len));
+      if (!result) {
+       eof_input=1;
+       if (!len) { /* if we have no deata in our buffer, exit */
+         return 0; /* we're at end of file! */
+       }
+       break; /* otherwise, break and write the data */
+      }
+      len+=result;  /* add the result input to our length. */
+    }
+    
+
+    result=SCSI_writet(MediumChangerFD,buffer,blocksize,&len,DEFAULT_TIMEOUT);
+    if (!result) {
+      return 1; /* at end of tape! */
+    }
+    /* Now see if we have numbytes or numblocks. If so, we may wish to exit
+       this loop.
+    */
+    if (arg[1]) {
+      if (varsize) {
+       /***BUG***/
+       return 0; /* we will only write one block in variable size mode :-( */
+      } else {
+       if (numblocks) {
+         numblocks--;
+       } else {
+         return 0; /* we're done. */
+       }
+      }
+    }
+  }
+  /* and done! */
+  return 0;
+}
+
+/* Okay, the read thingy: */
+
+/* We have a device opened (we hope!) by the parser. 
+ * we will have arg[0] and arg[1] being the blocksize and # of blocks
+ * (respectively).
+ */ 
+
+
+static int S_read(void) {
+  unsigned char *buffer; /* the buffer we're going to be reading out of */
+  int buffersize;
+  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. */
+
+  int result;
+
+  int outfile=fileno(stdout); /* sigh. */
+  
+
+  if (blocksize==0) {
+    varsize=1;
+    buffersize=MAX_READ_SIZE;
+    len=MAX_READ_SIZE;
+  }  else {
+    varsize=0; /* fixed block mode */
+    buffersize=blocksize;
+    len=blocksize;
+  }
+  /* sigh, make it oversized just to have some */  
+  buffer=malloc(buffersize+8); 
+  
+
+  while (1) {
+    if (varsize) {
+      /* it could have gotten reset by prior short read... */
+      len=MAX_READ_SIZE; 
+    }
+    result=SCSI_readt(MediumChangerFD,buffer,blocksize, &len, DEFAULT_TIMEOUT);
+    if (result==READ_FILEMARK || result==READ_EOD || result==READ_EOP) {
+      /* okay, normal end of file? */
+      if (len > 0) {
+       write(outfile,buffer,len);
+      }
+#ifdef NEED_TO_GO_PAST_FILEMARK
+      /* Now, let's try to go past the filemark if that's what we hit: */
+      if (result==READ_FILEMARK) {
+       arg1=1; /* arg for S_fsf. */
+       S_fsf(); /* and go forward 1 filemark, we hope! */
+      }
+#endif   
+      return 0; /* hit normal end of file. */
+    } else if (result==READ_SHORT) {
+      /* short reads are only valid in variable block mode. */
+      if (varsize) {
+       if (len > 0) {
+         write(outfile,buffer,len);
+       }
+      } else {
+       fprintf(stderr,"scsitape:Short Read encountered on input. Aborting.\n");
+       fflush(stderr);
+       exit(1); /* error exit! */
+      }
+    } else if (result==READ_OK) {
+      write(outfile,buffer,len);
+    } else {
+
+      fprintf(stderr,"scsitape:Read Error\n");
+      fflush(stderr);
+      exit(1);
+    }
+    /* Now see if we have numbytes or numblocks: if so, we may wish to
+     * exit this loop.
+     */
+    if (arg[1]) {
+      if (varsize) {
+       /****BUG****/ 
+       return 0; /* we're only reading one block in var size mode! */
+      } else {
+       if (numblocks) {
+         numblocks--;
+       } else {
+         return 0; /* we're done. */
+       }
+      }
+    }
+  }
+} /* got the goddam cancer of the curly braces. You can tell I used to 
+   * do Lisp.
+   */
+
+
+/* See parse_args for the scoop. parse_args does all. */
+int main(int argc, char **argv) {
+  argv0=argv[0];
+  parse_args(argc,argv);
+
+  if (device) 
+    SCSI_CloseDevice(device,MediumChangerFD);
+
+  exit(0);
+}
+      
diff --git a/sparc-patch1 b/sparc-patch1
new file mode 100644 (file)
index 0000000..3b62195
--- /dev/null
@@ -0,0 +1,35 @@
+diff -ux Makefile mtx-1.2.9/mtxl.c mtx-1.2.9.works/mtxl.c
+--- mtx-1.2.9/mtxl.c    Mon Jul 31 12:33:53 2000
++++ mtx-1.2.9.works/mtxl.c      Sat Nov 25 16:47:45 2000
+@@ -431,8 +431,10 @@
+ ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
+   ElementStatusDataHeader_T *ElementStatusDataHeader;
+   ElementStatusPage_T *ElementStatusPage;
++  ElementStatusPage_T ESBuf;
+   ElementStatus_T *ElementStatus;
+   TransportElementDescriptor_T *TransportElementDescriptor;
++  TransportElementDescriptor_T TEBuf;
+   unsigned char *DataBuffer; /* size of data... */
+   unsigned char *DataPointer; /* point into the databuffer; */
+@@ -648,7 +651,8 @@
+       got_element_num++;
+ #endif
+-      ElementStatusPage = (ElementStatusPage_T *) DataPointer;
++      memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
++      ElementStatusPage = &ESBuf;
+       DataPointer += sizeof(ElementStatusPage_T);
+       TransportElementDescriptorLength =
+        BigEndian16(ElementStatusPage->ElementDescriptorLength);
+@@ -671,8 +675,8 @@
+        BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
+       while (BytesAvailable > 0)
+        {
+-         TransportElementDescriptor =
+-           (TransportElementDescriptor_T *) DataPointer;
++         memcpy(&TEBuf, DataPointer, TransportElementDescriptorLength);
++         TransportElementDescriptor = &TEBuf;
+          DataPointer += TransportElementDescriptorLength;
+          BytesAvailable -= TransportElementDescriptorLength;
+          ElementCount--;                                                       
diff --git a/tapeinfo.1 b/tapeinfo.1
new file mode 100644 (file)
index 0000000..b53a86c
--- /dev/null
@@ -0,0 +1,72 @@
+.\" tapeinfo.1  Document copyright 2000 Eric Lee Green
+.\"  Program Copyright 2000 Eric Lee Green <eric@estinc.com>
+.\"
+.\" This is free documentation; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public License as
+.\" published by the Free Software Foundation; either version 2 of
+.\" the License, or (at your option) any later version.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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 more details.
+.\"
+.\" You should have received a copy of the GNU General Public
+.\" License along with this manual; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+.\" USA.
+.\"
+.TH TAPEINFO 1 TAPEINFO1.0
+.SH NAME
+tapeinfo \- report SCSI tape device info
+.SH SYNOPSIS
+tapeinfo -f <scsi-generic-device>
+.SH DESCRIPTION
+The 
+.B tapeinfo
+command reads various information from SCSI tape drives that is not
+generally available via most vendors' tape drivers. It issues raw
+commands directly to the tape drive, using either the operating system's
+SCSI generic device ( e.g. /dev/sg0 on Linux, /dev/pass0 on FreeBSD) or
+the raw SCSI I/O ioctl on a tape device on some operating systems. 
+.P
+One good time to use 'tapeinfo' is immediately after a tape i/o operation has
+failed. On tape drives that support HP's 'tapealert' API, 'tapeinfo' will
+report a more exact description of what went wrong. 
+.P
+Do be aware that 'tapeinfo' is not a substitute for your operating system's
+own 'mt' or similar tape driver control program. It is intended to supplement,
+not replace, programs like 'mt' that access your operating system's tape
+driver in order to report or set information. 
+.SH OPTIONS
+The first argument, given following
+.B -f
+, is the SCSI generic device corresponding to your tape drive.
+Consult your operating system's documentation for more information (for
+example, under Linux these are generally start at /dev/sg0 
+under FreeBSD these start at /dev/pass0).
+.P
+Under FreeBSD, 'camcontrol devlist' will tell you what SCSI devices you
+have, along with which 'pass' device controls them. Under Linux,
+"cat /proc/scsi/scsi" will tell you what SCSI devices you have.
+
+.SH BUGS AND LIMITATIONS
+.P
+This program has only been tested on Linux with a limited number of
+tape drives (HP DDS4, Seagate AIT). 
+.P
+.SH AVAILABILITY
+.B tapeinfo
+is currently being maintained by Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from 
+http://sourceforge.net/projects/mtx/ . 
+
+.SH SEE ALSO
+.BR mt (1), mtx (1), scsitape (1)
diff --git a/tapeinfo.c b/tapeinfo.c
new file mode 100644 (file)
index 0000000..820004e
--- /dev/null
@@ -0,0 +1,753 @@
+/* Copyright 2000 Enhanced Software Technologies Inc.
+ *   Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ * $Date: 2001/06/19 21:51:32 $
+ * $Revision: 1.2 $
+ */
+
+/*#define DEBUG_PARTITION */
+/*#define DEBUG 1 */
+
+/* What this does: This basically dumps out the contents of the following
+ * pages:
+ *
+ * Inquiry -- prints full inquiry info. If it's not a tape drive, this is
+ * the end of things.
+ *    DeviceType:
+ *    Manufacturer:
+ *    ProdID:  
+ *    ProdRevision:
+ *
+ * Log Sense: TapeAlert Page (if supported):
+ *    TapeAlert:[message#]<Message>  e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out"
+ *  
+ * Mode Sense: 
+ *  Data Compression Page:
+ *    DataCompEnabled:<yes|no>
+ *    DataCompCapable:<yes|no>
+ *    DataDeCompEnabled:<yes|no>
+ *    CompType:<number>
+ *    DeCompType:<number>
+ *
+ *  Device Configuration Page:
+ *    ActivePartition:<#>
+ *    DevConfigComp:<#>    -- the compression byte in device config page.
+ *    EarlyWarningSize:<#> -- size of early warning buffer?
+ *
+ *  Medium Partition Page:
+ *    NumPartitions:<#>
+ *    MaxPartitions:<#>
+ *    Partition[0]:<size>
+ *    Partition[1]:<size>...
+ *
+ * Read Block Limits command:
+ *    MinBlock:<#>  -- Minimum block size.
+ *    MaxBlock:<#>  -- Maximum block size. 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "mtx.h"
+#include "mtxl.h"
+
+char  *argv0;
+
+void usage(void) {
+  FatalError("Usage: tapeinfo -f <generic-device>\n");
+}
+
+/* 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 */
+};
+
+
+
+/* we call it MediumChangerFD for history reasons, sigh. */
+
+/* now to print inquiry information: Copied from other one.... */
+
+static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
+{
+  RequestSense_T RequestSense;
+  Inquiry_T *Inquiry;
+  int i;
+
+  Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
+  if (Inquiry == NULL) 
+    {
+      PrintRequestSense(&RequestSense);
+      FatalError("INQUIRY Command Failed\n");
+    }
+  
+  printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
+  printf("Vendor ID: '");
+  for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
+    printf("%c", Inquiry->VendorIdentification[i]);
+  printf("'\nProduct ID: '");
+  for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
+    printf("%c", Inquiry->ProductIdentification[i]);
+  printf("'\nRevision: '");
+  for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
+    printf("%c", Inquiry->ProductRevisionLevel[i]);
+  printf("'\n");\
+  if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
+    printf("Attached Changer: Yes\n");
+  } else {
+    printf("Attached Changer: No\n");
+  }
+  free(Inquiry);  /* well, we're about to exit, but ... */
+}
+
+
+
+
+/* Okay, now for the Log Sense Tape Alert Page (if supported): */
+#define TAPEALERT_SIZE 2048  /* max size of tapealert buffer. */ 
+#define MAX_TAPE_ALERT 0x41
+
+static char *tapealert_messages[]= {
+  "Undefined", /* 0 */
+  "         Read: Having problems reading (slowing down)", /* 1 */
+  "        Write: Having problems writing (losing capacity)", /* 2 */
+  "   Hard Error: Uncorrectable read/write error", /* 3 */
+  "        Media: Media Performance Degraded, Data Is At Risk", /* 4 */
+  " Read Failure: Tape faulty or tape drive broken", /* 5 */
+  "Write Failure: Tape faulty or tape drive broken", /* 6 */
+  "   Media Life: The tape has reached the end of its useful life", /* 7 */
+  "Not Data Grade:Replace cartridge with one  containing data grade tape",/*8*/
+  "Write Protect: Attempted to write to a write-protected cartridge",/*9 */
+  "   No Removal: Cannot unload, initiator is preventing media removal", /*a*/
+  "Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
+  "   Bad Format: The loaded tape contains data in an unsupported format", /*c */
+  " Snapped Tape: The data cartridge contains a broken tape", /* d */
+  "Undefined", /* e */
+  "Undefined", /* f */
+  "Undefined", /* 10 */
+  "Undefined", /* 11 */
+  "Undefined", /* 12 */
+  "Undefined", /* 13 */
+  "    Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
+  "Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
+  "Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
+  "Undefined", /* 0x17 */
+  "Undefined", /* 0x18 */
+  "Undefined", /* 0x19 */
+  "Undefined", /* 0x1a */
+  "Undefined", /* 0x1b */
+  "Undefined", /* 0x1c */
+  "Undefined", /* 0x1d */
+  "   Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
+  "   Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
+  "    Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
+  "  Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
+  "Download Fail: Attempt to download new firmware failed", /* 0x22 */
+  "Undefined", /* 0x23 */
+  "Undefined", /* 0x24 */
+  "Undefined", /* 0x25 */
+  "Undefined", /* 0x26 */
+  "Undefined", /* 0x27 */
+  "Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28   40 */
+  "Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
+  "Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
+  "  Loader Door: Loader door is open, please close it", /* 0x2b 43 */
+  "Undefined", /* 0x2c */
+  "Undefined", /* 0x2d */
+  "Undefined", /* 0x2e */
+  "Undefined", /* 0x2f */
+  "Undefined", /* 0x30 */
+  "Undefined", /* 0x31 */
+  "Undefined", /* 0x32 */
+  "Undefined", /* 0x33 */
+  "Undefined", /* 0x34 */
+  "Undefined", /* 0x35 */
+  "Undefined", /* 0x36 */
+  "Undefined", /* 0x37 */
+  "Undefined", /* 0x38 */
+  "Undefined", /* 0x39 */
+  "Undefined", /* 0x3a */
+  "Undefined", /* 0x3b */
+  "Undefined", /* 0x3c */
+  "Undefined", /* 0x3d */
+  "Undefined", /* 0x3e */
+  "Undefined", /* 0x3f */
+  "Undefined" /* 0x40 */
+};
+
+
+struct tapealert_struct {
+  int length;
+  unsigned char *data;
+};
+  
+  
+static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense) {
+  CDB_T CDB;
+  
+  struct tapealert_struct *result;
+  int i,tapealert_len,result_idx;
+  
+  unsigned char buffer[TAPEALERT_SIZE];
+  unsigned char *walkptr;
+
+  slow_bzero(buffer,TAPEALERT_SIZE); /*zero it... */
+
+  /* now to create the CDB block: */
+  CDB[0]=0x4d;   /* Log Sense */
+  CDB[1]=0;   
+  CDB[2]=0x2e;   /* Tape Alert Page. */
+  CDB[3]=0;
+  CDB[4]=0;
+  CDB[5]=0;
+  CDB[6]=0;
+  CDB[7]=TAPEALERT_SIZE>>8 & 0xff;  /* hi byte, allocation size */
+  CDB[8]=TAPEALERT_SIZE & 0xff;     /* lo byte, allocation size */
+  CDB[9]=0;  /* reserved */ 
+
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0){
+    return NULL;
+  }
+
+  result=xmalloc(sizeof(struct tapealert_struct));
+
+  /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
+   * byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
+   * tapealert page is. 
+   */
+  if ((buffer[0]&0x3f) != 0x2e) {
+    result->data=NULL;
+    result->length=0;
+    return result;
+  }
+
+  tapealert_len=(((int)buffer[2])<<8) + buffer[3];
+  
+
+  if (!tapealert_len) {
+    result->length=0;
+    result->data=NULL;
+    return result;
+  }
+
+  /* okay, now allocate data and move the buffer over there: */
+  result->length=MAX_TAPE_ALERT;
+  result->data=xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
+  
+  walkptr=&buffer[4];
+  i=0;
+  while (i<tapealert_len) {
+    
+
+    result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
+    if (result_idx > 0 && result_idx < MAX_TAPE_ALERT) {
+      if (walkptr[4]) {
+       result->data[result_idx]=1; 
+      } else {
+       result->data[result_idx]=0;
+      }
+#ifdef DEBUGOLD1
+      fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
+      fflush(stderr);
+#endif
+    } else {
+      FatalError("Invalid tapealert page: %d\n",result_idx);
+    }
+    i=i+4+walkptr[3]; /* length byte! */
+    walkptr=walkptr+4+walkptr[3]; /* next! */
+  }
+  return result;
+}
+
+static void ReportTapeAlert(DEVICE_TYPE fd) {
+  /* we actually ignore a bad sense reading, like might happen if the 
+   * tape drive does not support the tapealert page. 
+   */
+  
+  RequestSense_T RequestSense;
+  
+  struct tapealert_struct *result;
+  int i;
+
+  result=RequestTapeAlert(fd,&RequestSense);
+  if (!result) return; /* sorry. Don't print sense here. */
+  if (!result->length) return; /* sorry, no alerts valid. */
+  
+  for (i=0;i<result->length;i++) {
+    if (result->data[i]) {
+      printf("TapeAlert[%d]:%s.\n",i,tapealert_messages[i]);
+    }
+  }
+  free(result->data);
+  free(result);
+  return;
+}
+
+static unsigned char
+*mode_sense(DEVICE_TYPE fd, int pagenum, int alloc_len,  RequestSense_T *RequestSense) {
+  CDB_T CDB;
+
+  unsigned char *input_buffer; /*the input buffer -- has junk prepended to
+                               * actual sense page. 
+                               */
+  unsigned char *tmp;
+  unsigned char *retval;  /* the return value. */
+  int i,pagelen;
+
+  if (alloc_len > 255) {
+    FatalError("mode_sense(6) can only read up to 255 characters!\n");
+  }
+
+  input_buffer=(unsigned char *)xzmalloc(256); /* overdo it, eh? */
+
+  /* clear the sense buffer: */
+  slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+  
+
+  /* returns an array of bytes in the page, or, if not possible, NULL. */
+  CDB[0]=0x1a; /* Mode Sense(6) */
+  CDB[1]=0; 
+  CDB[2]=pagenum; /* the page to read. */
+  CDB[3]=0;
+  CDB[4]=255; /* allocation length. This does max of 256 bytes! */
+  CDB[5]=0;
+  
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,
+                         input_buffer,255,RequestSense) != 0) {
+#ifdef DEBUG_MODE_SENSE
+    fprintf(stderr,"Could not execute mode sense...\n");
+    fflush(stderr);
+#endif
+    return NULL; /* sorry, couldn't do it. */
+  }
+
+  /* Oh hell, write protect is the only thing I have: always print
+   * it if our mode page was 0x0fh, before skipping past buffer: 
+   * if the media is *NOT* write protected, just skip, sigh. 
+   *
+   * Oh poops, the blocksize is reported in the block descriptor header
+<   * too. Again, just print if our mode page was 0x0f...
+   */
+  if (pagenum == 0x0f) {
+    int blocklen;
+    if (input_buffer[2] & 0x80) {
+      printf("WriteProtect: yes\n");
+    }
+    if (input_buffer[2] & 0x70) {
+      printf("BufferedMode: yes\n");
+    }
+    if (input_buffer[1] ) {
+      printf("Medium Type: 0x%x\n", input_buffer[1]);
+    } else {
+      printf("Medium Type: Not Loaded\n");
+    }
+    printf("Density Code: 0x%x\n", input_buffer[4]);
+    /* Put out the block size: */
+    blocklen=((int)input_buffer[9]<<16)+
+      ((int)input_buffer[10]<<8)+
+      input_buffer[11];
+    printf("BlockSize: %d\n", blocklen);
+    /* This doesn't seem to do anything useful on my Tandberg so I
+     * am taking it out -- elg
+     */ 
+    /* Put out the # Blocks */
+    /* blocklen=((int)input_buffer[5]<<16)+ */
+    /*      ((int)input_buffer[6]<<8)+ */
+    /*      input_buffer[7]; */
+    /* printf("Num Blocks:  %d\n", blocklen); */
+  }
+  /* First skip past any header.... */
+  tmp=input_buffer+4+input_buffer[3];
+  /* now find out real length of page... */
+  pagelen=tmp[1]+2;
+  retval=xmalloc(pagelen);
+  /* and copy our data to the new page. */
+  for (i=0;i<pagelen;i++) {
+    retval[i]=tmp[i];
+  }
+  /* okay, free our input buffer: */
+  free(input_buffer);
+  return retval;
+}
+
+
+
+
+
+#define DCE_MASK 0x80
+#define DCC_MASK 0x40
+#define DDE_MASK 0x80
+
+static void ReportCompressionPage(DEVICE_TYPE fd) {
+  /* actually ignore a bad sense reading, like might happen if the tape
+   * drive does not support the mode sense compression page. 
+   */
+
+  RequestSense_T RequestSense;
+
+  unsigned char *compression_page;
+
+
+  compression_page=mode_sense(fd,0x0f,16,&RequestSense);
+
+  if (!compression_page) {
+    return;  /* sorry! */
+  }
+
+  /* Okay, we now have the compression page. Now print stuff from it: */
+  printf("DataCompEnabled: %s\n", (compression_page[2]&DCE_MASK)? "yes" : "no");
+  printf("DataCompCapable: %s\n", (compression_page[2]&DCC_MASK)? "yes" : "no");
+  printf("DataDeCompEnabled: %s\n", (compression_page[3]&DDE_MASK)? "yes" : "no");
+  printf("CompType: 0x%x\n",
+        (compression_page[4]<<24)+(compression_page[5]<<16)+(compression_page[6]<<8)+compression_page[7]);
+  printf("DeCompType: 0x%x\n",
+       (compression_page[8]<<24)+ (compression_page[9]<<16)+(compression_page[10]<<8)+compression_page[11]);
+  free(compression_page);
+  return;
+}
+  
+/* Now for the device configuration mode page: */
+
+static void ReportConfigPage(DEVICE_TYPE fd) {
+  RequestSense_T RequestSense;
+  unsigned char *config_page;
+  
+  config_page=mode_sense(fd,0x10,16,&RequestSense);
+  if (!config_page) return;
+
+  /* Now to print the stuff: */
+  printf("ActivePartition: %d\n",config_page[3]);
+  /* The following does NOT work accurately on any tape drive I know of... */
+  /*  printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
+  printf("EarlyWarningSize: %d\n",
+        (config_page[11]<<16)+(config_page[12]<<8)+config_page[13]);
+  return;
+}
+
+/* ***************************************
+ * Medium Partition Page:
+ * 
+ * The problem here, as we oh so graphically demonstrated during debugging
+ * of the Linux 'st' driver :-), is that there are actually *TWO* formats for
+ * the Medium Partition Page: There is the "long" format, where there is a
+ * partition size word for each partition on the page, and there is a "short"
+ * format, beloved of DAT drives, which only has a partition size word for
+ * partition #1 (and no partition size word for partition #0, and no
+ * provisions for any more partitions). So we must look at the size and
+ * # of partitions defined to know what to report as what. 
+ *
+ ********************************************/
+
+static void ReportPartitionPage(DEVICE_TYPE fd) {
+  RequestSense_T RequestSense;
+  unsigned char *partition_page;
+
+  int num_parts,max_parts;
+  int i;
+
+  partition_page=mode_sense(fd,0x11,255,&RequestSense);
+  if (!partition_page) return;
+
+  /* Okay, now we have either old format or new format: */
+  num_parts=partition_page[3];
+  max_parts=partition_page[2];
+
+  printf("NumPartitions:%d\n",num_parts);
+  printf("MaxPartitions:%d\n",max_parts);
+  if (!num_parts) {  /* if no additional partitions, then ... */ 
+    free(partition_page);
+    return;
+  }
+  
+  /* we know we have at least one partition if we got here. Check the
+   * page size field. If it is 8 or below, then we are the old format....
+   */
+
+#ifdef DEBUG_PARTITION
+  fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
+  fflush(stderr);
+#endif
+  if (partition_page[1]==8) {
+    /* old-style! */
+    printf("Partition1:%d\n",(partition_page[8]<<8)+partition_page[9]);
+  } else {
+    /* new-style! */
+    for (i=0;i<=max_parts;i++) {
+#ifdef DEBUG_PARTITION    
+      fprintf(stderr,"partition%d:[%d]%d [%d]%d\n",i,8+i*2,
+             partition_page[8+i*2]<<8, 9+i*2,partition_page[9+i*2]);
+      fflush(stderr);
+#endif
+      printf("Partition%d:%d\n",i,
+            (partition_page[8+i*2]<<8)+partition_page[9+i*2]);
+    }
+  }
+  free(partition_page);
+  return;
+}
+
+static void ReportSerialNumber(DEVICE_TYPE fd) {
+  /* actually ignore a bad sense reading, like might happen if the
+     tape drive does not support the inquiry page 0x80. 
+  */
+
+  RequestSense_T sense;
+  CDB_T CDB;
+#define WILD_SER_SIZE 30
+  unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
+
+  int i,lim;
+  char *bufptr; 
+  
+  CDB[0]=0x12;  /* INQUIRY */
+  CDB[1]= 1;     /* EVPD = 1 */
+  CDB[2]=0x80;   /* The serial # page, hopefully. */
+  CDB[3]=0  ; /* reserved */
+  CDB[4]=WILD_SER_SIZE;
+  CDB[5]=0;
+
+  if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
+                         &buffer, sizeof(buffer), &sense) != 0)  {
+    /* PrintRequestSense(&sense); */ /* zap debugging output :-) */
+    /* printf("No Serial Number: None\n"); */
+    return; 
+  }
+  
+  /* okay, we have something in our buffer. Byte 3 should be the length of
+     the sernum field, and bytes 4 onward are the serial #. */
+  
+  lim=(int)buffer[3];
+  bufptr= &(buffer[4]);
+  
+  printf("SerialNumber: '");
+  for (i=0;i<lim;i++) {
+    putchar(*bufptr++);
+  }
+  printf("'\n");
+  return ; /* done! */
+}
+    
+
+/*  Read Block Limits! */
+
+void ReportBlockLimits(DEVICE_TYPE fd) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+
+  CDB[0]=0x05;  /* READ_BLOCK_LIMITS */
+  CDB[1]=0;
+  CDB[2]=0;
+  CDB[3]=0;  /* 1-5 all unused. */
+  CDB[4]=0;
+  CDB[5]=0; 
+
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,6,&sense)!=0){
+    return;
+  }
+  
+  /* okay, but if we did get a result, print it: */
+  printf("MinBlock:%d\n",
+        (buffer[4]<<8)+buffer[5]);
+  printf("MaxBlock:%d\n",
+        (buffer[1]<<16)+(buffer[2]<<8)+buffer[3]);
+
+  return;
+}
+  
+/* Do a READ_POSITION. This may not be always valid, but (shrug). */
+void ReadPosition(DEVICE_TYPE fd) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[20];
+  unsigned int position;
+
+
+  CDB[0]=0x34;  /* READ_POSITION */
+  CDB[1]=0;
+  CDB[2]=0;
+  CDB[3]=0;  /* 1-9 all unused. */
+  CDB[4]=0;
+  CDB[5]=0; 
+  CDB[6]=0;
+  CDB[7]=0;
+  CDB[8]=0;
+  CDB[9]=0;
+
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+
+  /* set the timeout: */
+  SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
+
+  /* if we don't get a result (e.g. we issue this to a disk drive), punt. */
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,20,&sense)!=0){
+    return;
+  }
+  
+  SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
+  /* okay, but if we did get a result, print it: */
+#define RBL_BOP 0x80
+#define RBL_EOP 0x40
+#define RBL_BCU 0x20
+#define RBL_BYCU  0x10
+#define RBL_R1 0x08
+#define RBL_BPU 0x04
+#define RBL_PERR 0x02
+
+  /* If we have BOP, go ahead and print that. */
+  if (buffer[0]&RBL_BOP) {
+    printf("BOP: yes\n");
+  }
+  /* if we have valid data, print it: */
+  if (buffer[0]&RBL_BPU) {
+    printf("Block Position: -1");
+  } else {
+    
+    position= (unsigned int) (((unsigned int)buffer[4]<<24) +
+                             ((unsigned int)buffer[5]<<16) + 
+                             ((unsigned int)buffer[6]<<8) + buffer[7]);
+    printf("Block Position: %d\n",position);
+  }
+  return;
+}
+
+/* Test unit ready: This will tell us whether the tape drive
+ * is currently ready to read or write.
+ */
+
+int TestUnitReady(DEVICE_TYPE fd) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+
+  CDB[0]=0x00;  /* TEST_UNIT_READY */
+  CDB[1]=0;
+  CDB[2]=0;
+  CDB[3]=0;  /* 1-5 all unused. */
+  CDB[4]=0;
+  CDB[5]=0; 
+
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
+    printf("Ready: no\n");
+    return 0;
+  }
+  
+  printf("Ready: yes\n");
+  return 1;
+}
+
+/* We write a filemarks of 0 before going to grab position, in order
+ * to insure that data in the buffer is not a problem. 
+ */
+
+int WriteFileMarks(DEVICE_TYPE fd,int count) {
+  RequestSense_T sense;
+  CDB_T CDB;
+  unsigned char buffer[6];
+
+
+  CDB[0]=0x10;  /* WRITE_FILE_MARKS */
+  CDB[1]=0;
+  CDB[2]=(count >> 16) & 0xff;
+  CDB[3]=(count >>8) & 0xff;
+  CDB[4]=count & 0xff;
+  CDB[5]=0; 
+
+  /* we really don't care if this command works or not, sigh.  */
+  slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
+  if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
+    return 1;
+  }
+  return 0;
+}
+
+
+/* This will get the SCSI ID and LUN of the target device, if such
+ * is available from the OS. Currently only Linux supports this,
+ * but other drivers could, if someone wants to write a 
+ * SCSI_GetIDLun function for them. 
+ */
+#ifdef HAVE_GET_ID_LUN
+
+static void ReportIDLun(DEVICE_TYPE fd) {
+  scsi_id_t *scsi_id;
+
+  scsi_id=SCSI_GetIDLun(fd);
+  printf("SCSI ID: %d\nSCSI LUN: %d\n",scsi_id->id,scsi_id->lun);
+}
+
+#endif
+
+/* we only have one argument: "-f <device>". */
+int main(int argc, char **argv) {
+  DEVICE_TYPE fd;
+  char *filename;
+
+  argv0=argv[0];
+  
+  if (argc != 3) {
+    fprintf(stderr,"argc=%d",argc);
+    usage();
+  }
+
+  if (strcmp(argv[1],"-f")!=0) {
+    usage();
+  }
+  filename=argv[2];
+  
+  fd=SCSI_OpenDevice(filename);
+  
+  /* Now to call the various routines: */
+  ReportInquiry(fd);
+  ReportSerialNumber(fd);
+  ReportTapeAlert(fd);
+  /* ReportConfigPage(fd);  */
+  /* ReportPartitionPage(fd); */
+  ReportBlockLimits(fd); 
+#ifdef HAVE_GET_ID_LUN
+  ReportIDLun(fd);
+#endif
+
+  /* okay, we should only report position if the unit is ready :-(. */
+  if (TestUnitReady(fd)) {
+    ReportCompressionPage(fd); 
+    ReadPosition(fd); 
+  }
+
+
+  exit(0);
+}
+      
diff --git a/vms/000readme b/vms/000readme
new file mode 100644 (file)
index 0000000..854d58a
--- /dev/null
@@ -0,0 +1,175 @@
+MTX -- SCSI Tape Attached Medium Changer Control Program
+
+Copyright 1997 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+VMS port, April 1998, by TECSys Development, Inc.
+
+SECTION I - Disclaimer
+
+  These programs / ports are distributed in the hopes that they
+  will be useful, but WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  While a significant amount of testing has been performed, and while
+  TDI believes that this utility is safe, ONLY YOU can make that
+  determination with respect to your environment. There are NO
+  GUARANTEES WHATSOEVER that use of this program will not CRASH
+  YOUR SYSTEM or worse. TDI disclaims any responsibility for any
+  losses or damages from the use of this program.
+
+SECTION II - MTX program
+
+***CAUTIONS***
+  * Attempting to check the status of the drive with 1 tape manually
+    inserted in the drive, and no magazine results in an unexpected
+    SCSI status message. During porting of this program on a VS4000/90a,
+    it is believed that the handling of this status message caused
+    some sort of scsi state problem wherein subsequent accesses to
+    the changer from normal MKDRIVER activity resulted in SCSI bus
+    phase errors and an ultimate system failure. Power cycles of both
+    the drive and the host were required to rectify the problem.
+
+Instructions:
+  Read instructions (particularly section III below on the LDRSET utility).
+  Compile (DEC C) and link image, copy to an appropriate install location
+  Define a foreign symbol to access the program
+  Use UNIX syntax to operate program per documentation (MTX.DOC)
+
+  A descrip.mms file is provided if you have MMS or MMK installed.
+  You can build everything from scratch by doing a MMK/FROM_SOURCE. This
+  includes the LDRSET utility described below. You should be in the mtx
+  root directory before you do this, NOT [.vms].
+
+  If you do not have or use MMS or MMK, I can highly recommend MMK as
+  a great tool. It is available from www.madgoat.com. If you still do
+  not have mmk or mms, a build.com is provided. Again, you should be in
+  the mtx root directory before you do this, NOT [.vms].
+
+  Note: If you are on an alpha, the mms[k]-built exe files will be called
+  .alpha_exe, not .exe. Adjust following instructions accordingly.
+
+Example:
+  $ MMS/DESCRIP=[.VMS]   !Or MMK, or @[.VMS]BUILD
+  $ copy mtx.exe DISK$USERDISK:[USERS.FRED.UTILS]MTX
+  $ MTX:==$DISK$USERDISK:[USERS.FRED.UTILS]MTX.EXE
+  $ MTX -f MKA500 status
+    --or--
+  $ DEFINE TAPE MKA500
+  $ MTX status
+
+Notes:
+  * This code does NOT compile under VAX C... the only issue is VAX C's
+    incorrect assertion that a 'boolean' is not an acceptable "expression"
+    on either side of a || operator. If you are on VAX C - upgrade. If you
+    really must... then use a ((int)...) around the left-hand-side
+    of the lines where the compiler complains.... like so:
+        if (((int)StorageElementFull[FirstStorageElementNumber]) ||
+
+  * The following symbols result from 'status' or element movement commands:
+      MTX_DTE   (Data Transfer Element)
+        FULL:n            (Full, with element 'n'... if n=0, then unknown ele)
+        EMPTY
+      MTX_MSZ   (Magazine SiZe)
+        n                 (Number of magazine slots)
+      MTX_STE01 thru MTX_STExx (STorage Element, Max is MTX_MSZ)
+        FULL
+        EMPTY  
+      These will allow a DCL program to process the results
+      of the status command
+
+  * PHY_IO and DIAGNOSE are required to run the program as it stands
+    today. Yes, the GKdriver dox say PHY_IO or LOG_IO.... MKdriver
+    thinks otherwise. It wins.
+
+  * The program IS equipped to handle being installed with DIAGNOSE
+    and PHY_IO. A certain level of checking is done to see that the
+    right type of device is being targeted for the autoloader manipulation
+    SCSI commands. [see next item]
+
+  * There is an indicator bit in DEVCHAR2 that indicates the presence of
+    a loader on a tape. Unfortunately, it appears that the "knowledge" of
+    whether a loader is present or not is hard-coded into mkdriver. This
+    means that if you don't have the magic DEC rom's in your Archive
+    loader, then the LDR bit is not set. This makes the LDR bit effectively
+    useless unless you want to write a kernel dinker program you could
+    run from startup to "fix" the DEVCHAR2 longword.  (Incidentally,
+    if you ship the autoloader commands at a TZ30 (a non-loader drive),
+    the tz30 blows it off w/o any adverse side-effects - I would hope
+    that other drives are as tolerant.)
+
+    Because of this misfeature, the code to check the LDR flag in DEVCHAR2
+    is commented out.
+
+    *See section below on LDRSET utility if you want to use this
+    feature anyway (recommended).
+
+  * This program has been tested under:
+      VAX C V2.2, VAX/VMS 6.1, VS 4000/90A  **with code modifications
+      DEC C V5.2, VAX/VMS 6.1, VS 4000/90A
+      DEC C V5.3, AXP/VMS 6.2, DEC3000-300X
+
+
+SECTION III - LDRSET Utility
+
+Description:
+  This is a small KERNEL MODE utility program to go set or reset
+  the DEV$M_LDR flag in UCB$L_DEVCHAR2 for a specified device. While
+  a certain amount of checking is performed, and while the author
+  believes that this utility is safe, ONLY YOU can make that
+  determination with respect to your environment. There are NO
+  GUARANTEES WHATSOEVER that use of this program will not CRASH
+  YOUR SYSTEM or worse. TDI disclaims any responsibility for any
+  losses or damages from the use of this program.
+
+  With all that said... this utility can be used [example: system
+  startup] to set the LDR flag for the autoloader tape device.
+  With the loader flag properly set, you can re-enable the LDR
+  check section in MTX and be fairly well certain that the MTX
+  utility will not be used against a drive that is not a SCSI
+  MAGTAPE with a LOADER attached.
+
+Instructions:
+  Compile (DEC C ***ONLY***) the LDRSET C program
+
+  Alpha:
+    MACRO/MIGRATE the LDRUTIL.MAR program
+    LINK LDRSET,LDRUTIL/SYSEXE to generate the image
+
+  Vax
+    MACRO the LDRUTIL.MAR program
+    LINK LDRSET,LDRUTIL to generate the image
+
+  [NOTE!!! ---> DO NOT USE RESULTING IMAGE IF THERE ARE ANY COMPILE OR LINK
+                ERRORS!!!]
+
+  Copy to an appropriate install location.
+
+  Edit provided CLD file to reflect the installed location
+
+  Use the following syntax:
+      $ SET COMMAND LDRSET
+      $ LDRSET MKA500: /SET    !Sets the loader present flag on MKA500
+      $ LDRSET MKA500: /RESET  !Clears the loader present flag on MKA500
+
+Alpha example:
+  $ CC/DECC/DEBUG/NOOPT LDRSET
+  $ MACRO/MIGRATE LDRUTIL
+  $ LINK LDRSET,LDRUTIL/SYSEXE
+
+Vax example:
+  $ CC/DECC/DEBUG/NOOPT LDRSET
+  $ MACRO LDRUTIL
+  $ LINK LDRSET,LDRUTIL
+
+Common:
+  $ copy LDRSET.EXE DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.EXE
+  $ copy LDRSET.CLD DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.CLD
+  $ EDIT DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.CLD
+     ...change image sys$disk:[]ldrset line to :
+           image DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.EXE
+  $ LDRSET MKA500 /SET
+
+  * This program has been tested under:
+      DEC C V5.2, VAX/VMS 6.1, VS 4000/90A
+      DEC C V5.6, AXP/VMS 7.1, AS 1000A
+
diff --git a/vms/build.com b/vms/build.com
new file mode 100644 (file)
index 0000000..2f6308d
--- /dev/null
@@ -0,0 +1,34 @@
+$!x='f$ver(0)
+$ if f$parse("[.VMS]A.A").eqs.""
+$ then 
+$   write sys$output "?Error: Use $ @[.VMS]BUILD from the mtx directory"
+$   exit 44
+$ endif
+$ alpha = f$getsyi("hw_model").ge.1024
+$ vax = .not.alpha
+$ exe = "EXE"
+$ obj = "OBJ"
+$ sysexe=""
+$ migrate=""
+$ if alpha then exe="ALPHA_EXE"
+$ if alpha then obj="ALPHA_OBJ"
+$ if alpha then sysexe="/SYSEXE"
+$ if alpha then migrate="/MIGRATION/NOOPT"
+$ set verify
+$ if "''p1'".eqs."LINK" then goto do_link
+$ CC /DECC/DEB/NOOP MTX.C/DEB/NOOP/OBJECT=MTX.'obj'
+$ if f$search("MTX.''obj';-1").nes."" then -
+     purge/log MTX.'obj'
+$ CC /DECC/DEB/NOOP [.VMS]LDRSET.C/DEB/NOOP/OBJECT=[.VMS]LDRSET.'obj'
+$ if f$search("[.VMS]LDRSET.''obj';-1").nes."" then -
+     purge/log [.VMS]LDRSET.'obj'
+$ MACRO'migrate' /DEB [.VMS]LDRUTIL.MAR -
+       /OBJECT=[.VMS]LDRUTIL.'obj'
+$ if f$search("[.VMS]LDRUTIL.''obj';-1").nes."" then -
+     purge/log [.VMS]LDRUTIL.'obj'
+$!
+$ do_link:
+$ link/notrace mtx.'obj'/exe=mtx.'exe'
+$ link [.vms]ldrset.'obj',[.vms]ldrutil.'obj' -
+     /exe=ldrset.'exe' 'sysexe'
+$ exit
diff --git a/vms/defs.h b/vms/defs.h
new file mode 100644 (file)
index 0000000..68dd7d7
--- /dev/null
@@ -0,0 +1,42 @@
+#ifdef __DECC
+#pragma module MTX "V01-00"
+#else
+#module MTX "V01-00"
+#endif
+
+typedef int DEVICE_TYPE;
+
+#include <ssdef.h>
+#include <lib$routines.h>
+#include <ots$routines.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <iodef.h>
+#include <descrip.h>
+#include <dcdef.h>
+#include <devdef.h>
+#include <dvidef.h>
+#include <starlet.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <prvdef.h>
+
+#if defined(__DECC)
+#pragma member_alignment save
+#pragma nomember_alignment
+#endif
+
+/*
+  Define either of the following symbols as 1 to enable checking of
+  the LDR flag for specified devices.  DO NOT set these bits if you
+  do not 1) have a DEC-recognized autoloader, or 2) use the LDRSET
+  utility to set the LDR flag for the target devices.
+*/
+
+#define USING_DEC_DRIVE                0
+#define USING_LDRSET           0
+
+static unsigned long VMS_ExitCode = SS$_ABORT;
diff --git a/vms/descrip.mms b/vms/descrip.mms
new file mode 100644 (file)
index 0000000..fb08467
--- /dev/null
@@ -0,0 +1,77 @@
+!
+! MMS System build for MTX and LDRSET utility
+!
+!Global build flag macros
+!
+CDEBUG = /DEB/NOOP
+MDEBUG = /DEB
+
+CFLAGS = /DECC$(CDEBUG)
+MFLAGS = $(MDEBUG)
+
+.IFDEF __AXP__
+.SUFFIXES .ALPHA_OBJ
+MFLAGS = /MIGRATE$(MFLAGS)/NOOP
+DBG = .ALPHA_DBG
+EXE = .ALPHA_EXE
+OBJ = .ALPHA_OBJ
+OPT = .ALPHA_OPT
+SYSEXE=/SYSEXE
+
+.ELSE
+DBG = .DBG
+EXE = .EXE
+OPT = .OPT
+OBJ = .OBJ
+SYSEXE=
+
+.ENDIF
+
+PURGEOBJ = if f$search("$(MMS$TARGET_NAME)$(OBJ);-1").nes."" then purge/log $(MMS$TARGET_NAME)$(OBJ)
+
+!
+!Bend the default build rules for C, MACRO, and MESSAGE
+!
+.C$(OBJ) :
+       $(CC) $(CFLAGS) $(MMS$SOURCE)$(CDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
+       $(PURGEOBJ)
+.MAR$(OBJ) :
+       $(MACRO) $(MFLAGS) $(MMS$SOURCE)$(MDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
+       $(PURGEOBJ)
+.CLD$(OBJ) :
+       SET COMMAND/OBJECT=$(MMS$TARGET_NAME)$(OBJ)  $(MMS$SOURCE)
+       $(PURGEOBJ)
+.MSG$(OBJ) :
+       MESSAGE $(MMS$SOURCE)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
+       $(PURGEOBJ)
+
+
+DEFAULT                :       ERROR,-
+                       MTX,-
+                       LDRSET
+       @ !
+
+ERROR          :
+       @ if f$parse("[.VMS]A.A").eqs."" then write sys$output "?Error: Use $ MMS/DESCRIP=[.VMS] from the mtx directory"
+
+MTX            :       mtx$(EXE)
+       @ !
+
+mtx$(EXE)      :       mtx$(OBJ)
+       $ link/notrace mtx$(OBJ)/exe=mtx$(EXE)
+
+mtx$(OBJ)      :       mtx.c,[.vms]scsi.c,[.vms]defs.h
+
+LDRSET         :       ldrset$(EXE),ldrset.cld
+       @ !
+
+ldrset.cld     :       [.vms]ldrset.cld
+       $ copy [.vms]ldrset.cld []/log
+
+ldrset$(EXE)   :       [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ)
+       $ link [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ)/exe=ldrset$(EXE)$(SYSEXE)
+
+[.vms]ldrset$(OBJ)     :       [.vms]ldrset.c
+
+[.vms]ldrutil$(OBJ)    :       [.vms]ldrutil.mar
+
diff --git a/vms/ldrset.c b/vms/ldrset.c
new file mode 100644 (file)
index 0000000..c0efd0c
--- /dev/null
@@ -0,0 +1,183 @@
+/* LDRSET - Set the state of the LDR flag in UCB$L_DEVCHAR2 for a
+**     SCSI magtape.  REQUIRES CMKRNL privilege.
+**
+**  Copyright 1999 by TECSys Development, Inc. http://www.tditx.com
+**
+**  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.
+**
+** Description:
+**   This is a small KERNEL MODE utility program to go set or reset
+**   the DEV$M_LDR flag in UCB$L_DEVCHAR2 for a specified device. While
+**   a certain amount of checking is performed, and while the author
+**   believes that this utility is safe, ONLY YOU can make that
+**   determination with respect to your environment. There are NO
+**   GUARANTEES WHATSOEVER that use of this program will not CRASH
+**   YOUR SYSTEM or worse. TDI disclaims any responsibility for any
+**   losses or damages from the use of this program.
+**
+**   With all that said... this utility can be used [example: system
+**   startup] to set the LDR flag for the autoloader tape device.
+**   With the loader flag properly set, you can re-enable the LDR
+**   check section in MTX and be fairly well certain that the MTX
+**   utility will not be used against a drive that is not a SCSI
+**   MAGTAPE with a LOADER attached.
+**
+**
+** LDRSET.CLD:
+**     define verb LDRSET
+**             image           sys$disk:[]ldrset.exe
+**             parameter       p1,             label=device,
+**                                             prompt="Device",
+**                                             value(required,type=$FILE)
+**             qualifier       SET, nonnegatable
+**             qualifier       RESET, nonnegatable
+**             disallow        any2 (SET, RESET)
+**             disallow        NOT SET AND NOT RESET
+*/
+#ifdef __DECC
+#pragma module LDRSET "V01-00"
+#else
+#module LDRSET "V01-00"
+#endif
+
+#include <ssdef.h>
+#include <dcdef.h>
+#include <devdef.h>
+#include <dvidef.h>
+#include <descrip.h>
+#include <stdio.h>
+#include <string.h>
+#include <lib$routines.h>
+#include <starlet.h>
+
+#ifndef DESCR_CNT
+#define DESCR_CNT 16
+/* MUST BE of the form 2^N!, big enough for max concurrent usage */
+#endif
+
+static struct dsc$descriptor_s *
+descr(
+  char *str)
+{
+static struct dsc$descriptor_s d_descrtbl[DESCR_CNT];  /* MAX usage! */
+static unsigned short int descridx=0;
+  struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx];
+
+  descridx = (descridx+1)&(DESCR_CNT-1);
+
+  d_ret->dsc$w_length = strlen((const char *)str);
+  d_ret->dsc$a_pointer = (char *)str;
+
+  d_ret->dsc$b_class =
+  d_ret->dsc$b_dtype = 0;
+  return(d_ret);
+}
+
+extern unsigned long int finducb();
+extern unsigned long int _setldr();
+extern unsigned long int _clrldr();
+
+unsigned long int
+set_ldrstate(
+  int devch,
+  int setstate)
+{
+  unsigned long int ret;
+  struct ucbdef *ucb;
+
+  if (~(ret=finducb(devch,&ucb))&1)
+    return(ret);
+
+  if (setstate)
+    return(_setldr(ucb));
+  else
+    return(_clrldr(ucb));
+}
+
+extern unsigned long int CLI$PRESENT();
+extern unsigned long int CLI$GET_VALUE();
+
+static unsigned long int
+cld_special(
+  char *kwd_name)
+{
+  lib$establish(lib$sig_to_ret);
+  return(CLI$PRESENT(descr(kwd_name)));
+}
+
+int
+main(){
+  unsigned long int ret;
+  unsigned long int ismnt = 0;
+  unsigned long int dvcls = 0;
+  unsigned long int dchr2 = 0;
+  struct itmlst_3 {
+    unsigned short int ilen;
+    unsigned short int code;
+    unsigned long int *returnP;
+    unsigned long int ignored;
+  } dvi_itmlst[] = {
+    {4, DVI$_MNT, 0/*&ismnt*/, 0},
+    {4, DVI$_DEVCLASS, 0/*&dvcls*/, 0},
+    {4, DVI$_DEVCHAR2, 0/*&dchr2*/, 0},
+    {0,0,0,0} };
+  unsigned long int iosb[2];
+  struct dsc$descriptor_s *dp_tmp;
+  struct dsc$descriptor_d dy_devn = { 0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0 };
+  unsigned long int devch=0;
+
+  dvi_itmlst[0].returnP = &ismnt;
+  dvi_itmlst[1].returnP = &dvcls;
+  dvi_itmlst[2].returnP = &dchr2;
+
+  if (~(ret=CLI$PRESENT(dp_tmp=descr("DEVICE")))&1) {
+    printf("?Error obtaining CLD DEVICE parameter\n");
+    return(ret); }
+  if (~(ret=CLI$GET_VALUE(dp_tmp,&dy_devn,0))&1) {
+    printf("?Error obtaining CLD DEVICE value\n");
+    return(ret); }
+
+  if (~(ret=sys$alloc(&dy_devn,0,0,0,0))&1) {
+    printf("?Error allocating specified device\n");
+    return(ret); }
+
+  if (~(ret=sys$assign(&dy_devn,&devch,0,0))&1) {
+    printf("?Error assigning a channel to specified device\n");
+    return(ret); }
+
+  if (~(ret=sys$getdviw(0,devch,0,&dvi_itmlst,&iosb,0,0,0))&1) {
+    printf("?Error obtaining device information(1)\n");
+    return(ret); }
+  if (~(ret=iosb[0])&1) {
+    printf("?Error obtaining device information(2)\n");
+    return(ret); }
+
+  if (dvcls != DC$_TAPE) {
+    printf("?Device is not a tape drive\n");
+    return(SS$_IVDEVNAM); }
+
+  if (~dchr2 & DEV$M_SCSI) {
+    printf("?Device is not a SCSI device\n");
+    return(SS$_IVDEVNAM); }
+
+  if (ismnt) {
+    printf("?Device is mounted\n");
+    return(SS$_DEVMOUNT); }
+
+  if (cld_special("SET")&1)
+    return(set_ldrstate(devch,1));
+
+  if (cld_special("RESET")&1)
+    return(set_ldrstate(devch,0));
+
+  /* Either SET or RESET above must be present to win */
+  printf("?CLD structural error - see source\n");
+  return(SS$_BADPARAM);
+}
diff --git a/vms/ldrset.cld b/vms/ldrset.cld
new file mode 100644 (file)
index 0000000..0dfc543
--- /dev/null
@@ -0,0 +1,9 @@
+define verb LDRSET
+       image           sys$disk:[]ldrset.exe
+       parameter       p1,             label=device,
+                                       prompt="Device",
+                                       value(required,type=$FILE)
+       qualifier       SET, nonnegatable
+       qualifier       RESET, nonnegatable
+       disallow        any2 (SET, RESET)
+       disallow        NOT SET AND NOT RESET
diff --git a/vms/ldrutil.mar b/vms/ldrutil.mar
new file mode 100644 (file)
index 0000000..9d0ea31
--- /dev/null
@@ -0,0 +1,104 @@
+       .title  LDRUTIL - Obtain ucb for assigned channel
+       .ident  /LDRUTIL V1.0/
+; LDRUTIL - VMS UCB LDR bit utility library
+;
+; TECSys Development, Inc., April 1998
+;
+; This file may be copied under the terms and conditions of version 2
+; of the GNU General Public License, as published by the Free
+; Software Foundation (Cambridge, Massachusetts).
+;
+; 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 more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, write to the Free Software
+; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+;
+;
+       .link   "sys$system:sys.stb"/selective_search
+       .library /sys$share:lib/
+
+       .NTYPE  ...IS_IT_ALPHA,R22              ;Get the type of R22
+       ...IS_IT_ALPHA = <...IS_IT_ALPHA@-4&^XF>-5
+       .IIF EQ,...IS_IT_ALPHA, ALPHA=1
+
+       $ssdef
+       $ucbdef
+       $ccbdef
+       $chfdef
+       $dcdef
+       $devdef
+       $pcbdef
+
+       .psect  $$code,exe,rd,nowrt,shr
+.IF NDF,ALPHA
+.entry finducb,^m<r2,r3,r4,r5,r6,r7,r8,r9>     ;Find UCB address from channel
+.IFF
+.call_entry,   2,home_args=TRUE,-
+               preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
+               output=<r0,r1>,-
+               label=finducb
+.endc
+       movzwl  4(AP),r0                        ;prep to find UCB
+       jsb     g^IOC$VERIFYCHAN                ;callable from user mode!
+       blbc    r0,20$
+       movl    CCB$L_UCB(r1),@8(AP)            ;save UCB address
+       movzbl  #1,r0
+20$:   ret
+
+.IF NDF,ALPHA
+.entry __setldr,^m<r2,r3,r4,r5,r6,r7,r8,r9>    ;Find UCB address from channel
+.IFF
+.call_entry,   2,home_args=TRUE,-
+               preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
+               output=<r0,r1>,-
+               label=__setldr
+.endc
+       movl    4(AP),r1
+       bisl    #DEV$M_LDR,UCB$L_DEVCHAR2(r1)
+       movzbl  #1,r0
+       ret
+
+.IF NDF,ALPHA
+.entry _setldr,^m<r2,r3,r4,r5,r6,r7,r8,r9>     ;Find UCB address from channel
+.IFF
+.call_entry,   2,home_args=TRUE,-
+               preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
+               output=<r0,r1>,-
+               label=_setldr
+.endc
+       $cmkrnl_s       -
+               routin  =       __setldr,-
+               arglst  =       (AP)
+       ret
+
+.IF NDF,ALPHA
+.entry __clrldr,^m<r2,r3,r4,r5,r6,r7,r8,r9>    ;Find UCB address from channel
+.IFF
+.call_entry,   2,home_args=TRUE,-
+               preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
+               output=<r0,r1>,-
+               label=__clrldr
+.endc
+       movl    4(AP),r1
+       bicl    #DEV$M_LDR,UCB$L_DEVCHAR2(r1)
+       movzbl  #1,r0
+       ret
+
+.IF NDF,ALPHA
+.entry _clrldr,^m<r2,r3,r4,r5,r6,r7,r8,r9>     ;Find UCB address from channel
+.IFF
+.call_entry,   2,home_args=TRUE,-
+               preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
+               output=<r0,r1>,-
+               label=_clrldr
+.endc
+       $cmkrnl_s       -
+               routin  =       __clrldr,-
+               arglst  =       (AP)
+       ret
+
+       .end
diff --git a/vms/scsi.c b/vms/scsi.c
new file mode 100644 (file)
index 0000000..6cd7685
--- /dev/null
@@ -0,0 +1,372 @@
+/* SCSI.C - VMS-specific SCSI routines.
+**
+** TECSys Development, Inc., April 1998
+**
+** This module began life as a program called CDWRITE20, a CD-R control
+** program for VMS. No real functionality from the original CDWRITE20
+** is present in this module, but in the interest of making certain that
+** proper credit is given where it may be due, the copyrights and inclusions
+** from the CDWRITE20 program are included below.
+**
+** The portions of coding in this module ascribable to TECSys Development
+** are hereby also released under the terms and conditions of version 2
+** of the GNU General Public License as described below....
+*/
+
+/* The remainder of these credits are included directly from the CDWRITE20
+** sources. */
+
+/* Copyright 1994 Yggdrasil Computing, Inc. */
+/* Written by Adam J. Richter (adam@yggdrasil.com) */
+
+/* Rewritten February 1997 by Eberhard Heuser-Hofmann*/
+/* using the OpenVMS generic scsi-interface */
+/* see the README-file, how to setup your machine */
+
+/*
+**  Modified March 1997 by John Vottero to use overlapped, async I/O
+**  and lots of buffers to help prevent buffer underruns.
+**  Also improved error reporting.
+*/
+
+/* This file may be copied under the terms and conditions of version 2
+   of the GNU General Public License, as published by the Free
+   Software Foundation (Cambridge, Massachusetts).
+
+   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 more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* The second notice comes from sys$examples:gktest.c (OpenVMS 7.0)*/
+
+/*
+** COPYRIGHT (c) 1993 BY
+** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
+** ALL RIGHTS RESERVED.
+**
+** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
+** ONLY  IN  ACCORDANCE  OF  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
+** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER
+** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
+** OTHER PERSON.  NO TITLE TO AND  OWNERSHIP OF THE  SOFTWARE IS  HEREBY
+** TRANSFERRED.
+**
+** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
+** AND SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
+** CORPORATION.
+**
+** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS
+** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
+*/
+
+/*
+  Define the Generic SCSI Command Descriptor.
+*/
+
+typedef struct scsi$desc
+{
+  unsigned int SCSI$L_OPCODE;      /* SCSI Operation Code */
+  unsigned int SCSI$L_FLAGS;       /* SCSI Flags Bit Map */
+  unsigned char *SCSI$A_CMD_ADDR;   /* ->SCSI Command Buffer */
+  unsigned int SCSI$L_CMD_LEN;     /* SCSI Command Length (bytes) */
+  unsigned char *SCSI$A_DATA_ADDR;  /* ->SCSI Data Buffer */
+  unsigned int SCSI$L_DATA_LEN;            /* SCSI Data Length (bytes) */
+  unsigned int SCSI$L_PAD_LEN;     /* SCSI Pad Length (bytes) */
+  unsigned int SCSI$L_PH_CH_TMOUT;  /* SCSI Phase Change Timeout (seconds) */
+  unsigned int SCSI$L_DISCON_TMOUT; /* SCSI Disconnect Timeout (seconds) */
+  unsigned int SCSI$L_RES_1;       /* Reserved */
+  unsigned int SCSI$L_RES_2;       /* Reserved */
+  unsigned int SCSI$L_RES_3;       /* Reserved */
+  unsigned int SCSI$L_RES_4;       /* Reserved */
+  unsigned int SCSI$L_RES_5;       /* Reserved */
+  unsigned int SCSI$L_RES_6;       /* Reserved */
+}
+SCSI$DESC;
+
+
+/*
+  Define the SCSI Input/Output Status Block.
+*/
+
+typedef struct scsi$iosb
+{
+  unsigned short SCSI$W_VMS_STAT;   /* VMS Status Code */
+  unsigned long SCSI$L_IOSB_TFR_CNT; /* Actual Byte Count Transferred */
+  unsigned char SCSI$B_IOSB_FILL_1; /* Unused */
+  unsigned char SCSI$B_IOSB_STS;    /* SCSI Device Status */
+}
+SCSI$IOSB;
+
+
+/*
+  Define the VMS symbolic representation for a successful SCSI command.
+*/
+
+#define SCSI$K_GOOD            0
+
+
+/*
+  Define the SCSI Flag Field Constants.
+*/
+
+#define SCSI$K_WRITE           0x0 /* Data Transfer Direction: Write */
+#define SCSI$K_READ            0x1 /* Data Transfer Direction: Read */
+#define SCSI$K_FL_ENAB_DIS     0x2 /* Enable Disconnect/Reconnect */
+
+
+/*
+  Define DESCR_CNT.  It must be a power of two and large enough
+  for the maximum concurrent usage of descriptors.
+*/
+
+#define DESCR_CNT 16
+
+
+#define MK_EFN                 0   /* Event Flag Number */
+#define FailureStatusP(Status) (~(Status) & 1)
+
+
+static struct dsc$descriptor_s *descr(char *String)
+{
+  static struct dsc$descriptor_s d_descrtbl[DESCR_CNT];
+  static unsigned short descridx = 0;
+  struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx];
+  descridx = (descridx + 1) & (DESCR_CNT - 1);
+  d_ret->dsc$w_length = strlen((const char *) String);
+  d_ret->dsc$a_pointer = String;
+  d_ret->dsc$b_class = 0;
+  d_ret->dsc$b_dtype = 0;
+  return d_ret;
+}
+
+
+static int SCSI_OpenDevice(char *DeviceName)
+{
+  unsigned long d_dev[2], iosb[2], Status;
+  union prvdef setprivs, newprivs;
+  unsigned long ismnt = 0;
+  unsigned long dvcls = 0;
+  unsigned long dchr2 = 0;
+  int DeviceFD = 0;
+  struct itmlst_3 {
+    unsigned short ilen;
+    unsigned short code;
+    unsigned long *returnP;
+    unsigned long ignored;
+  } dvi_itmlst[] = { { 4, DVI$_MNT, 0 /*&ismnt*/, 0 },
+                    { 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 },
+                    { 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 },
+                    { 0, 0, 0, 0 } };
+  dvi_itmlst[0].returnP = &ismnt;
+  dvi_itmlst[1].returnP = &dvcls;
+  dvi_itmlst[2].returnP = &dchr2;
+  Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0);
+  if (FailureStatusP(Status))
+    {
+      VMS_ExitCode = Status;
+      FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
+    }
+  Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
+  if (FailureStatusP(Status))
+    {
+      VMS_ExitCode = Status;
+      FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
+    }
+  Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
+  if (FailureStatusP(Status))
+    {
+      VMS_ExitCode = Status;
+      FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
+    }
+  if (FailureStatusP(Status = iosb[0]))
+    {
+      VMS_ExitCode = Status;
+      FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
+    }
+  if (dvcls != DC$_TAPE)
+    {
+      VMS_ExitCode = SS$_IVDEVNAM;
+      FatalError("specified device is NOT a magtape: operation denied\n");
+    }
+#ifndef __DECC
+#ifndef DEV$M_SCSI
+#define DEV$M_SCSI 0x1000000
+#endif
+#endif
+  if (~dchr2 & DEV$M_SCSI)
+    {
+      VMS_ExitCode = SS$_IVDEVNAM;
+      FatalError("specified magtape is NOT a SCSI device: operation denied\n");
+    }
+#if USING_DEC_DRIVE | USING_LDRSET
+#ifndef __DECC
+#ifndef DEV$M_LDR
+#define DEV$M_LDR 0x100000
+#endif
+#endif
+  if (~dchr2 & DEV$M_LDR)
+    {
+      VMS_ExitCode = SS$_IVDEVNAM;
+      FatalError(
+       "specified SCSI magtape does not have a loader: operation denied\n");
+    }
+#endif
+  if (ismnt)
+    {
+      VMS_ExitCode = SS$_DEVMOUNT;
+      FatalError("specified device is mounted: operation denied\n");
+    }
+  ots$move5(0, 0, 0, sizeof(newprivs), &newprivs);
+  newprivs.prv$v_diagnose = 1;
+  newprivs.prv$v_log_io = 1;
+  newprivs.prv$v_phy_io = 1;
+  Status = sys$setprv(1, &newprivs, 0, 0);
+  if (FailureStatusP(Status))
+    {
+      VMS_ExitCode = Status;
+      FatalError(
+       "error enabling privs (diagnose,log_io,phy_io): operation denied\n");
+    }
+  Status = sys$setprv(1, 0, 0, &setprivs);
+  if (FailureStatusP(Status))
+    {
+      VMS_ExitCode = Status;
+      FatalError("error retrieving current privs: operation denied\n");
+    }
+  if (!setprivs.prv$v_diagnose)
+    {
+      VMS_ExitCode = SS$_NODIAGNOSE;
+      FatalError("DIAGNOSE privilege is required: operation denied\n");
+    }
+  if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
+    {
+      VMS_ExitCode = SS$_NOPHY_IO;
+      FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
+    }
+  return DeviceFD;
+}
+
+
+static void SCSI_CloseDevice(char *DeviceName,
+                            int DeviceFD)
+{
+  unsigned long Status;
+  Status = sys$dassgn(DeviceFD);
+  if (FailureStatusP(Status))
+    FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
+}
+
+
+static int SCSI_ExecuteCommand(int DeviceFD,
+                              Direction_T Direction,
+                              CDB_T *CDB,
+                              int CDB_Length,
+                              void *DataBuffer,
+                              int DataBufferLength,
+                              RequestSense_T *RequestSense)
+{
+  SCSI$DESC cmd_desc;
+  SCSI$IOSB cmd_iosb;
+  unsigned long Status;
+  int Result;
+  memset(RequestSense, 0, sizeof(RequestSense_T));
+  /* Issue the QIO to send the SCSI Command. */
+  ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
+  cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
+  cmd_desc.SCSI$A_CMD_ADDR = CDB;
+  cmd_desc.SCSI$L_CMD_LEN = CDB_Length;
+  cmd_desc.SCSI$A_DATA_ADDR = DataBuffer;
+  cmd_desc.SCSI$L_DATA_LEN = DataBufferLength;
+  cmd_desc.SCSI$L_PAD_LEN = 0;
+  cmd_desc.SCSI$L_PH_CH_TMOUT = 180; /* SCSI Phase Change Timeout (seconds) */
+  cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* SCSI Disconnect Timeout (seconds) */
+  switch (Direction)
+    {
+      /*
+       NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
+             It does NOT work for this case.
+      */
+    case Input:
+      cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
+      break;
+    case Output:
+      cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
+      break;
+    }
+  /* Issue the SCSI Command. */
+  Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
+                    &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
+  Result = SCSI$K_GOOD;
+  if (Status & 1)
+    Status = cmd_iosb.SCSI$W_VMS_STAT;
+  if (Status & 1)
+    Result = cmd_iosb.SCSI$B_IOSB_STS;
+  if (Result != SCSI$K_GOOD)
+    {
+      unsigned char RequestSenseCDB[6] =
+       { 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 };
+      printf("SCSI command error: %d - requesting sense data\n", Result);
+      /* Execute Request Sense to determine the failure reason. */
+      ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
+      cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
+      cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
+      cmd_desc.SCSI$A_CMD_ADDR = RequestSenseCDB;
+      cmd_desc.SCSI$L_CMD_LEN = 6;
+      cmd_desc.SCSI$A_DATA_ADDR = RequestSense;
+      cmd_desc.SCSI$L_DATA_LEN = sizeof(RequestSense_T);
+      cmd_desc.SCSI$L_PH_CH_TMOUT = 180;
+      cmd_desc.SCSI$L_DISCON_TMOUT = 180;
+      /* Issue the QIO to send the Request Sense Command. */
+      Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
+                       &cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
+      if (FailureStatusP(Status))
+       {
+         printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
+         sys$exit(Status);
+       }
+      /* Check the VMS Status from QIO. */
+      Status = cmd_iosb.SCSI$W_VMS_STAT;
+      if (FailureStatusP(Status))
+       {
+         printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
+         sys$exit(Status);
+       }
+    }
+  return Result;
+}
+
+
+static void VMS_DefineStatusSymbols(void)
+{
+  char SymbolName[32], SymbolValue[32];
+  int StorageElementNumber;
+  if (DataTransferElementFull)
+    {
+      /* Define MTX_DTE Symbol (environment variable) as 'FULL:n'. */
+      sprintf(SymbolValue, "FULL:%d",
+             DataTransferElementSourceStorageElementNumber);
+      lib$set_symbol(descr("MTX_DTE"), descr(SymbolValue), &2);
+    }
+  else
+    {
+      /* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
+      lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
+    }
+  /* Define MTX_MSZ Symbol (environment variable) "Magazine SiZe" as 'n'. */
+  sprintf(SymbolValue, "%d", StorageElementCount);
+  lib$set_symbol(descr("MTX_MSZ"), descr(SymbolValue), &2);
+  for (StorageElementNumber = 1;
+       StorageElementNumber <= StorageElementCount;
+       StorageElementNumber++)
+    {
+      sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
+      strcpy(SymbolValue,
+            (StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
+      lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);
+    }
+}